From 067c054fb9c90811ae38898ce649f8dafa13354c Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sun, 14 Jun 2020 19:04:42 -0400 Subject: [PATCH 0001/1476] dlmfs: clean up dlmfs_file_{read,write}() a bit The damn file is constant-sized - 64 bytes. IOW, * i_size_read() is pointless * so's dynamic allocation * so's the 'size' argument of user_dlm_read_lvb() * ... and so's open-coding simple_read_from_buffer(), while we are at it. Signed-off-by: Al Viro --- fs/ocfs2/dlmfs/dlmfs.c | 52 +++++++--------------------------------- fs/ocfs2/dlmfs/userdlm.c | 12 ++++------ fs/ocfs2/dlmfs/userdlm.h | 4 +--- 3 files changed, 13 insertions(+), 55 deletions(-) diff --git a/fs/ocfs2/dlmfs/dlmfs.c b/fs/ocfs2/dlmfs/dlmfs.c index ea868c6f98005..583820ec63e2d 100644 --- a/fs/ocfs2/dlmfs/dlmfs.c +++ b/fs/ocfs2/dlmfs/dlmfs.c @@ -221,47 +221,17 @@ static __poll_t dlmfs_file_poll(struct file *file, poll_table *wait) return event; } -static ssize_t dlmfs_file_read(struct file *filp, +static ssize_t dlmfs_file_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) { - int bytes_left; - ssize_t got; - char *lvb_buf; - struct inode *inode = file_inode(filp); - - mlog(0, "inode %lu, count = %zu, *ppos = %llu\n", - inode->i_ino, count, *ppos); + char lvb[DLM_LVB_LEN]; - if (*ppos >= i_size_read(inode)) + if (!user_dlm_read_lvb(file_inode(file), lvb)) return 0; - /* don't read past the lvb */ - if (count > i_size_read(inode) - *ppos) - count = i_size_read(inode) - *ppos; - - if (!count) - return 0; - - lvb_buf = kmalloc(count, GFP_NOFS); - if (!lvb_buf) - return -ENOMEM; - - got = user_dlm_read_lvb(inode, lvb_buf, count); - if (got) { - BUG_ON(got != count); - bytes_left = copy_to_user(buf, lvb_buf, count); - count -= bytes_left; - } else - count = 0; - - kfree(lvb_buf); - - *ppos = *ppos + count; - - mlog(0, "read %zu bytes\n", count); - return count; + return simple_read_from_buffer(buf, count, ppos, lvb, sizeof(lvb)); } static ssize_t dlmfs_file_write(struct file *filp, @@ -269,34 +239,28 @@ static ssize_t dlmfs_file_write(struct file *filp, size_t count, loff_t *ppos) { + char lvb_buf[DLM_LVB_LEN]; int bytes_left; - char *lvb_buf; struct inode *inode = file_inode(filp); mlog(0, "inode %lu, count = %zu, *ppos = %llu\n", inode->i_ino, count, *ppos); - if (*ppos >= i_size_read(inode)) + if (*ppos >= DLM_LVB_LEN) return -ENOSPC; /* don't write past the lvb */ - if (count > i_size_read(inode) - *ppos) - count = i_size_read(inode) - *ppos; + if (count > DLM_LVB_LEN - *ppos) + count = DLM_LVB_LEN - *ppos; if (!count) return 0; - lvb_buf = kmalloc(count, GFP_NOFS); - if (!lvb_buf) - return -ENOMEM; - bytes_left = copy_from_user(lvb_buf, buf, count); count -= bytes_left; if (count) user_dlm_write_lvb(inode, lvb_buf, count); - kfree(lvb_buf); - *ppos = *ppos + count; mlog(0, "wrote %zu bytes\n", count); return count; diff --git a/fs/ocfs2/dlmfs/userdlm.c b/fs/ocfs2/dlmfs/userdlm.c index 3df5be25bfb1f..339f098d9592c 100644 --- a/fs/ocfs2/dlmfs/userdlm.c +++ b/fs/ocfs2/dlmfs/userdlm.c @@ -547,24 +547,20 @@ void user_dlm_write_lvb(struct inode *inode, spin_unlock(&lockres->l_lock); } -ssize_t user_dlm_read_lvb(struct inode *inode, - char *val, - unsigned int len) +bool user_dlm_read_lvb(struct inode *inode, char *val) { struct user_lock_res *lockres = &DLMFS_I(inode)->ip_lockres; char *lvb; - ssize_t ret = len; - - BUG_ON(len > DLM_LVB_LEN); + bool ret = true; spin_lock(&lockres->l_lock); BUG_ON(lockres->l_level < DLM_LOCK_PR); if (ocfs2_dlm_lvb_valid(&lockres->l_lksb)) { lvb = ocfs2_dlm_lvb(&lockres->l_lksb); - memcpy(val, lvb, len); + memcpy(val, lvb, DLM_LVB_LEN); } else - ret = 0; + ret = false; spin_unlock(&lockres->l_lock); return ret; diff --git a/fs/ocfs2/dlmfs/userdlm.h b/fs/ocfs2/dlmfs/userdlm.h index 4bef7cdef8598..0558ae7682008 100644 --- a/fs/ocfs2/dlmfs/userdlm.h +++ b/fs/ocfs2/dlmfs/userdlm.h @@ -66,9 +66,7 @@ void user_dlm_cluster_unlock(struct user_lock_res *lockres, void user_dlm_write_lvb(struct inode *inode, const char *val, unsigned int len); -ssize_t user_dlm_read_lvb(struct inode *inode, - char *val, - unsigned int len); +bool user_dlm_read_lvb(struct inode *inode, char *val); struct ocfs2_cluster_connection *user_dlm_register(const struct qstr *name); void user_dlm_unregister(struct ocfs2_cluster_connection *conn); void user_dlm_set_locking_protocol(void); -- GitLab From d9563c972c167e6e8b40c840d476d30af8e5f667 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Mon, 18 May 2020 10:16:44 +0200 Subject: [PATCH 0002/1476] dt-bindings: clock: renesas: cpg: Convert to json-schema Convert the Renesas Clock Pulse Generator (CPG) Device Tree binding documentation to json-schema, combining support for: - R-Mobile APE6 (R8A73A4) and A1 (R8A7740), - R-Car M1 (R8A7778) and H1 (R8A7779), - RZ/A1 (R7S72100), - SH-Mobile AG5 (SH73A0). Keep the example for R-Mobile A1, which shows most properties. Drop the consumer examples, as they do not belong here. Signed-off-by: Geert Uytterhoeven Reviewed-by: Rob Herring Acked-by: Stephen Boyd Link: https://lore.kernel.org/r/20200518081644.23683-1-geert+renesas@glider.be --- .../bindings/clock/renesas,cpg-clocks.yaml | 241 ++++++++++++++++++ .../clock/renesas,r8a73a4-cpg-clocks.txt | 33 --- .../clock/renesas,r8a7740-cpg-clocks.txt | 41 --- .../clock/renesas,r8a7778-cpg-clocks.txt | 47 ---- .../clock/renesas,r8a7779-cpg-clocks.txt | 49 ---- .../bindings/clock/renesas,rz-cpg-clocks.txt | 53 ---- .../clock/renesas,sh73a0-cpg-clocks.txt | 35 --- 7 files changed, 241 insertions(+), 258 deletions(-) create mode 100644 Documentation/devicetree/bindings/clock/renesas,cpg-clocks.yaml delete mode 100644 Documentation/devicetree/bindings/clock/renesas,r8a73a4-cpg-clocks.txt delete mode 100644 Documentation/devicetree/bindings/clock/renesas,r8a7740-cpg-clocks.txt delete mode 100644 Documentation/devicetree/bindings/clock/renesas,r8a7778-cpg-clocks.txt delete mode 100644 Documentation/devicetree/bindings/clock/renesas,r8a7779-cpg-clocks.txt delete mode 100644 Documentation/devicetree/bindings/clock/renesas,rz-cpg-clocks.txt delete mode 100644 Documentation/devicetree/bindings/clock/renesas,sh73a0-cpg-clocks.txt diff --git a/Documentation/devicetree/bindings/clock/renesas,cpg-clocks.yaml b/Documentation/devicetree/bindings/clock/renesas,cpg-clocks.yaml new file mode 100644 index 0000000000000..b83f4138f2f8b --- /dev/null +++ b/Documentation/devicetree/bindings/clock/renesas,cpg-clocks.yaml @@ -0,0 +1,241 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/clock/renesas,cpg-clocks.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Renesas Clock Pulse Generator (CPG) + +maintainers: + - Geert Uytterhoeven + +description: + The Clock Pulse Generator (CPG) generates core clocks for the SoC. It + includes PLLs, and fixed and variable ratio dividers. + + The CPG may also provide a Clock Domain for SoC devices, in combination with + the CPG Module Stop (MSTP) Clocks. + +properties: + compatible: + oneOf: + - const: renesas,r8a73a4-cpg-clocks # R-Mobile APE6 + - const: renesas,r8a7740-cpg-clocks # R-Mobile A1 + - const: renesas,r8a7778-cpg-clocks # R-Car M1 + - const: renesas,r8a7779-cpg-clocks # R-Car H1 + - items: + - enum: + - renesas,r7s72100-cpg-clocks # RZ/A1H + - const: renesas,rz-cpg-clocks # RZ/A1 + - const: renesas,sh73a0-cpg-clocks # SH-Mobile AG5 + + reg: + maxItems: 1 + + clocks: true + + '#clock-cells': + const: 1 + + clock-output-names: true + + renesas,mode: + description: Board-specific settings of the MD_CK* bits on R-Mobile A1 + $ref: /schemas/types.yaml#/definitions/uint32 + minimum: 0 + maximum: 7 + + '#power-domain-cells': + const: 0 + +required: + - compatible + - reg + - clocks + - '#clock-cells' + - clock-output-names + +allOf: + - if: + properties: + compatible: + contains: + const: renesas,r8a73a4-cpg-clocks + then: + properties: + clocks: + items: + - description: extal1 + - description: extal2 + + clock-output-names: + items: + - const: main + - const: pll0 + - const: pll1 + - const: pll2 + - const: pll2s + - const: pll2h + - const: z + - const: z2 + - const: i + - const: m3 + - const: b + - const: m1 + - const: m2 + - const: zx + - const: zs + - const: hp + + - if: + properties: + compatible: + contains: + const: renesas,r8a7740-cpg-clocks + then: + properties: + clocks: + items: + - description: extal1 + - description: extal2 + - description: extalr + + clock-output-names: + items: + - const: system + - const: pllc0 + - const: pllc1 + - const: pllc2 + - const: r + - const: usb24s + - const: i + - const: zg + - const: b + - const: m1 + - const: hp + - const: hpp + - const: usbp + - const: s + - const: zb + - const: m3 + - const: cp + + required: + - renesas,mode + + - if: + properties: + compatible: + contains: + const: renesas,r8a7778-cpg-clocks + then: + properties: + clocks: + maxItems: 1 + + clock-output-names: + items: + - const: plla + - const: pllb + - const: b + - const: out + - const: p + - const: s + - const: s1 + + - if: + properties: + compatible: + contains: + const: renesas,r8a7779-cpg-clocks + then: + properties: + clocks: + maxItems: 1 + + clock-output-names: + items: + - const: plla + - const: z + - const: zs + - const: s + - const: s1 + - const: p + - const: b + - const: out + + - if: + properties: + compatible: + contains: + const: renesas,r7s72100-cpg-clocks + then: + properties: + clocks: + items: + - description: extal1 + - description: usb_x1 + + clock-output-names: + items: + - const: pll + - const: i + - const: g + + - if: + properties: + compatible: + contains: + const: renesas,sh73a0-cpg-clocks + then: + properties: + clocks: + items: + - description: extal1 + - description: extal2 + + clock-output-names: + items: + - const: main + - const: pll0 + - const: pll1 + - const: pll2 + - const: pll3 + - const: dsi0phy + - const: dsi1phy + - const: zg + - const: m3 + - const: b + - const: m1 + - const: m2 + - const: z + - const: zx + - const: hp + + - if: + properties: + compatible: + contains: + enum: + - renesas,r8a7778-cpg-clocks + - renesas,r8a7779-cpg-clocks + - renesas,rz-cpg-clocks + then: + required: + - '#power-domain-cells' + +additionalProperties: false + +examples: + - | + #include + cpg_clocks: cpg_clocks@e6150000 { + compatible = "renesas,r8a7740-cpg-clocks"; + reg = <0xe6150000 0x10000>; + clocks = <&extal1_clk>, <&extal2_clk>, <&extalr_clk>; + #clock-cells = <1>; + clock-output-names = "system", "pllc0", "pllc1", "pllc2", "r", + "usb24s", "i", "zg", "b", "m1", "hp", "hpp", + "usbp", "s", "zb", "m3", "cp"; + renesas,mode = <0x05>; + }; diff --git a/Documentation/devicetree/bindings/clock/renesas,r8a73a4-cpg-clocks.txt b/Documentation/devicetree/bindings/clock/renesas,r8a73a4-cpg-clocks.txt deleted file mode 100644 index ece92393e80d4..0000000000000 --- a/Documentation/devicetree/bindings/clock/renesas,r8a73a4-cpg-clocks.txt +++ /dev/null @@ -1,33 +0,0 @@ -* Renesas R8A73A4 Clock Pulse Generator (CPG) - -The CPG generates core clocks for the R8A73A4 SoC. It includes five PLLs -and several fixed ratio dividers. - -Required Properties: - - - compatible: Must be "renesas,r8a73a4-cpg-clocks" - - - reg: Base address and length of the memory resource used by the CPG - - - clocks: Reference to the parent clocks ("extal1" and "extal2") - - - #clock-cells: Must be 1 - - - clock-output-names: The names of the clocks. Supported clocks are "main", - "pll0", "pll1", "pll2", "pll2s", "pll2h", "z", "z2", "i", "m3", "b", - "m1", "m2", "zx", "zs", and "hp". - - -Example -------- - - cpg_clocks: cpg_clocks@e6150000 { - compatible = "renesas,r8a73a4-cpg-clocks"; - reg = <0 0xe6150000 0 0x10000>; - clocks = <&extal1_clk>, <&extal2_clk>; - #clock-cells = <1>; - clock-output-names = "main", "pll0", "pll1", "pll2", - "pll2s", "pll2h", "z", "z2", - "i", "m3", "b", "m1", "m2", - "zx", "zs", "hp"; - }; diff --git a/Documentation/devicetree/bindings/clock/renesas,r8a7740-cpg-clocks.txt b/Documentation/devicetree/bindings/clock/renesas,r8a7740-cpg-clocks.txt deleted file mode 100644 index 2c03302f86edf..0000000000000 --- a/Documentation/devicetree/bindings/clock/renesas,r8a7740-cpg-clocks.txt +++ /dev/null @@ -1,41 +0,0 @@ -These bindings should be considered EXPERIMENTAL for now. - -* Renesas R8A7740 Clock Pulse Generator (CPG) - -The CPG generates core clocks for the R8A7740 SoC. It includes three PLLs -and several fixed ratio and variable ratio dividers. - -Required Properties: - - - compatible: Must be "renesas,r8a7740-cpg-clocks" - - - reg: Base address and length of the memory resource used by the CPG - - - clocks: Reference to the three parent clocks - - #clock-cells: Must be 1 - - clock-output-names: The names of the clocks. Supported clocks are - "system", "pllc0", "pllc1", "pllc2", "r", "usb24s", "i", "zg", "b", - "m1", "hp", "hpp", "usbp", "s", "zb", "m3", and "cp". - - - renesas,mode: board-specific settings of the MD_CK* bits - - -Example -------- - -cpg_clocks: cpg_clocks@e6150000 { - compatible = "renesas,r8a7740-cpg-clocks"; - reg = <0xe6150000 0x10000>; - clocks = <&extal1_clk>, <&extal2_clk>, <&extalr_clk>; - #clock-cells = <1>; - clock-output-names = "system", "pllc0", "pllc1", - "pllc2", "r", - "usb24s", - "i", "zg", "b", "m1", "hp", - "hpp", "usbp", "s", "zb", "m3", - "cp"; -}; - -&cpg_clocks { - renesas,mode = <0x05>; -}; diff --git a/Documentation/devicetree/bindings/clock/renesas,r8a7778-cpg-clocks.txt b/Documentation/devicetree/bindings/clock/renesas,r8a7778-cpg-clocks.txt deleted file mode 100644 index 7cc4c0330b534..0000000000000 --- a/Documentation/devicetree/bindings/clock/renesas,r8a7778-cpg-clocks.txt +++ /dev/null @@ -1,47 +0,0 @@ -* Renesas R8A7778 Clock Pulse Generator (CPG) - -The CPG generates core clocks for the R8A7778. It includes two PLLs and -several fixed ratio dividers. -The CPG also provides a Clock Domain for SoC devices, in combination with the -CPG Module Stop (MSTP) Clocks. - -Required Properties: - - - compatible: Must be "renesas,r8a7778-cpg-clocks" - - reg: Base address and length of the memory resource used by the CPG - - #clock-cells: Must be 1 - - clock-output-names: The names of the clocks. Supported clocks are - "plla", "pllb", "b", "out", "p", "s", and "s1". - - #power-domain-cells: Must be 0 - -SoC devices that are part of the CPG/MSTP Clock Domain and can be power-managed -through an MSTP clock should refer to the CPG device node in their -"power-domains" property, as documented by the generic PM domain bindings in -Documentation/devicetree/bindings/power/power_domain.txt. - - -Examples --------- - - - CPG device node: - - cpg_clocks: cpg_clocks@ffc80000 { - compatible = "renesas,r8a7778-cpg-clocks"; - reg = <0xffc80000 0x80>; - #clock-cells = <1>; - clocks = <&extal_clk>; - clock-output-names = "plla", "pllb", "b", - "out", "p", "s", "s1"; - #power-domain-cells = <0>; - }; - - - - CPG/MSTP Clock Domain member device node: - - sdhi0: sd@ffe4c000 { - compatible = "renesas,sdhi-r8a7778"; - reg = <0xffe4c000 0x100>; - interrupts = <0 87 IRQ_TYPE_LEVEL_HIGH>; - clocks = <&mstp3_clks R8A7778_CLK_SDHI0>; - power-domains = <&cpg_clocks>; - }; diff --git a/Documentation/devicetree/bindings/clock/renesas,r8a7779-cpg-clocks.txt b/Documentation/devicetree/bindings/clock/renesas,r8a7779-cpg-clocks.txt deleted file mode 100644 index 8c81547c29f56..0000000000000 --- a/Documentation/devicetree/bindings/clock/renesas,r8a7779-cpg-clocks.txt +++ /dev/null @@ -1,49 +0,0 @@ -* Renesas R8A7779 Clock Pulse Generator (CPG) - -The CPG generates core clocks for the R8A7779. It includes one PLL and -several fixed ratio dividers. -The CPG also provides a Clock Domain for SoC devices, in combination with the -CPG Module Stop (MSTP) Clocks. - -Required Properties: - - - compatible: Must be "renesas,r8a7779-cpg-clocks" - - reg: Base address and length of the memory resource used by the CPG - - - clocks: Reference to the parent clock - - #clock-cells: Must be 1 - - clock-output-names: The names of the clocks. Supported clocks are "plla", - "z", "zs", "s", "s1", "p", "b", "out". - - #power-domain-cells: Must be 0 - -SoC devices that are part of the CPG/MSTP Clock Domain and can be power-managed -through an MSTP clock should refer to the CPG device node in their -"power-domains" property, as documented by the generic PM domain bindings in -Documentation/devicetree/bindings/power/power_domain.txt. - - -Examples --------- - - - CPG device node: - - cpg_clocks: cpg_clocks@ffc80000 { - compatible = "renesas,r8a7779-cpg-clocks"; - reg = <0xffc80000 0x30>; - clocks = <&extal_clk>; - #clock-cells = <1>; - clock-output-names = "plla", "z", "zs", "s", "s1", "p", - "b", "out"; - #power-domain-cells = <0>; - }; - - - - CPG/MSTP Clock Domain member device node: - - sata: sata@fc600000 { - compatible = "renesas,sata-r8a7779", "renesas,rcar-sata"; - reg = <0xfc600000 0x2000>; - interrupts = <0 100 IRQ_TYPE_LEVEL_HIGH>; - clocks = <&mstp1_clks R8A7779_CLK_SATA>; - power-domains = <&cpg_clocks>; - }; diff --git a/Documentation/devicetree/bindings/clock/renesas,rz-cpg-clocks.txt b/Documentation/devicetree/bindings/clock/renesas,rz-cpg-clocks.txt deleted file mode 100644 index 8ff3e2774ed8d..0000000000000 --- a/Documentation/devicetree/bindings/clock/renesas,rz-cpg-clocks.txt +++ /dev/null @@ -1,53 +0,0 @@ -* Renesas RZ/A1 Clock Pulse Generator (CPG) - -The CPG generates core clocks for the RZ/A1 SoCs. It includes the PLL, variable -CPU and GPU clocks, and several fixed ratio dividers. -The CPG also provides a Clock Domain for SoC devices, in combination with the -CPG Module Stop (MSTP) Clocks. - -Required Properties: - - - compatible: Must be one of - - "renesas,r7s72100-cpg-clocks" for the r7s72100 CPG - and "renesas,rz-cpg-clocks" as a fallback. - - reg: Base address and length of the memory resource used by the CPG - - clocks: References to possible parent clocks. Order must match clock modes - in the datasheet. For the r7s72100, this is extal, usb_x1. - - #clock-cells: Must be 1 - - clock-output-names: The names of the clocks. Supported clocks are "pll", - "i", and "g" - - #power-domain-cells: Must be 0 - -SoC devices that are part of the CPG/MSTP Clock Domain and can be power-managed -through an MSTP clock should refer to the CPG device node in their -"power-domains" property, as documented by the generic PM domain bindings in -Documentation/devicetree/bindings/power/power_domain.txt. - - -Examples --------- - - - CPG device node: - - cpg_clocks: cpg_clocks@fcfe0000 { - #clock-cells = <1>; - compatible = "renesas,r7s72100-cpg-clocks", - "renesas,rz-cpg-clocks"; - reg = <0xfcfe0000 0x18>; - clocks = <&extal_clk>, <&usb_x1_clk>; - clock-output-names = "pll", "i", "g"; - #power-domain-cells = <0>; - }; - - - - CPG/MSTP Clock Domain member device node: - - mtu2: timer@fcff0000 { - compatible = "renesas,mtu2-r7s72100", "renesas,mtu2"; - reg = <0xfcff0000 0x400>; - interrupts = <0 107 IRQ_TYPE_LEVEL_HIGH>; - interrupt-names = "tgi0a"; - clocks = <&mstp3_clks R7S72100_CLK_MTU2>; - clock-names = "fck"; - power-domains = <&cpg_clocks>; - }; diff --git a/Documentation/devicetree/bindings/clock/renesas,sh73a0-cpg-clocks.txt b/Documentation/devicetree/bindings/clock/renesas,sh73a0-cpg-clocks.txt deleted file mode 100644 index a8978ec94831f..0000000000000 --- a/Documentation/devicetree/bindings/clock/renesas,sh73a0-cpg-clocks.txt +++ /dev/null @@ -1,35 +0,0 @@ -These bindings should be considered EXPERIMENTAL for now. - -* Renesas SH73A0 Clock Pulse Generator (CPG) - -The CPG generates core clocks for the SH73A0 SoC. It includes four PLLs -and several fixed ratio dividers. - -Required Properties: - - - compatible: Must be "renesas,sh73a0-cpg-clocks" - - - reg: Base address and length of the memory resource used by the CPG - - - clocks: Reference to the parent clocks ("extal1" and "extal2") - - - #clock-cells: Must be 1 - - - clock-output-names: The names of the clocks. Supported clocks are "main", - "pll0", "pll1", "pll2", "pll3", "dsi0phy", "dsi1phy", "zg", "m3", "b", - "m1", "m2", "z", "zx", and "hp". - - -Example -------- - - cpg_clocks: cpg_clocks@e6150000 { - compatible = "renesas,sh73a0-cpg-clocks"; - reg = <0 0xe6150000 0 0x10000>; - clocks = <&extal1_clk>, <&extal2_clk>; - #clock-cells = <1>; - clock-output-names = "main", "pll0", "pll1", "pll2", - "pll3", "dsi0phy", "dsi1phy", - "zg", "m3", "b", "m1", "m2", - "z", "zx", "hp"; - }; -- GitLab From 432e8130af88e486c9419fe18fbff2edd64db374 Mon Sep 17 00:00:00 2001 From: Lad Prabhakar Date: Sun, 24 May 2020 22:37:53 +0100 Subject: [PATCH 0003/1476] dt-bindings: dmaengine: renesas,usb-dmac: Add binding for r8a7742 Document RZ/G1H (R8A7742) SoC bindings. Signed-off-by: Lad Prabhakar Reviewed-by: Marian-Cristian Rotariu Reviewed-by: Geert Uytterhoeven Acked-by: Rob Herring Link: https://lore.kernel.org/r/1590356277-19993-5-git-send-email-prabhakar.mahadev-lad.rj@bp.renesas.com Signed-off-by: Vinod Koul --- Documentation/devicetree/bindings/dma/renesas,usb-dmac.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/dma/renesas,usb-dmac.yaml b/Documentation/devicetree/bindings/dma/renesas,usb-dmac.yaml index 9ca6d8ddf2320..03aea6ae651f2 100644 --- a/Documentation/devicetree/bindings/dma/renesas,usb-dmac.yaml +++ b/Documentation/devicetree/bindings/dma/renesas,usb-dmac.yaml @@ -16,6 +16,7 @@ properties: compatible: items: - enum: + - renesas,r8a7742-usb-dmac # RZ/G1H - renesas,r8a7743-usb-dmac # RZ/G1M - renesas,r8a7744-usb-dmac # RZ/G1N - renesas,r8a7745-usb-dmac # RZ/G1E -- GitLab From 38e4fb66728f01821f5ac0ed2adb4a3d7f40329c Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Tue, 26 May 2020 21:24:15 +0300 Subject: [PATCH 0004/1476] dmaengine: dw: Register ACPI DMA controller for PCI that has companion If PCI enumerated controller has a companion device, register it in the ACPI DMA controllers as well. Fixes: f7c799e950f9 ("dmaengine: dw: we do support Merrifield SoC in PCI mode") Depends-on: b685fe26e9af ("dmaengine: dw: platform: Split ACPI helpers to separate module") Signed-off-by: Andy Shevchenko Link: https://lore.kernel.org/r/20200526182416.52805-1-andriy.shevchenko@linux.intel.com Signed-off-by: Vinod Koul --- drivers/dma/dw/Makefile | 2 +- drivers/dma/dw/acpi.c | 2 ++ drivers/dma/dw/pci.c | 4 ++++ 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/drivers/dma/dw/Makefile b/drivers/dma/dw/Makefile index b6f06699e91a9..9e949f13e1b52 100644 --- a/drivers/dma/dw/Makefile +++ b/drivers/dma/dw/Makefile @@ -1,10 +1,10 @@ # SPDX-License-Identifier: GPL-2.0 obj-$(CONFIG_DW_DMAC_CORE) += dw_dmac_core.o dw_dmac_core-objs := core.o dw.o idma32.o +dw_dmac_core-$(CONFIG_ACPI) += acpi.o obj-$(CONFIG_DW_DMAC) += dw_dmac.o dw_dmac-y := platform.o -dw_dmac-$(CONFIG_ACPI) += acpi.o dw_dmac-$(CONFIG_OF) += of.o obj-$(CONFIG_DW_DMAC_PCI) += dw_dmac_pci.o diff --git a/drivers/dma/dw/acpi.c b/drivers/dma/dw/acpi.c index f6e8d55b4f6e9..c510c109d2c3a 100644 --- a/drivers/dma/dw/acpi.c +++ b/drivers/dma/dw/acpi.c @@ -41,6 +41,7 @@ void dw_dma_acpi_controller_register(struct dw_dma *dw) if (ret) dev_err(dev, "could not register acpi_dma_controller\n"); } +EXPORT_SYMBOL_GPL(dw_dma_acpi_controller_register); void dw_dma_acpi_controller_free(struct dw_dma *dw) { @@ -51,3 +52,4 @@ void dw_dma_acpi_controller_free(struct dw_dma *dw) acpi_dma_controller_free(dev); } +EXPORT_SYMBOL_GPL(dw_dma_acpi_controller_free); diff --git a/drivers/dma/dw/pci.c b/drivers/dma/dw/pci.c index cf6e8ec4c0ff4..1142aa6f8c4ac 100644 --- a/drivers/dma/dw/pci.c +++ b/drivers/dma/dw/pci.c @@ -60,6 +60,8 @@ static int dw_pci_probe(struct pci_dev *pdev, const struct pci_device_id *pid) if (ret) return ret; + dw_dma_acpi_controller_register(chip->dw); + pci_set_drvdata(pdev, data); return 0; @@ -71,6 +73,8 @@ static void dw_pci_remove(struct pci_dev *pdev) struct dw_dma_chip *chip = data->chip; int ret; + dw_dma_acpi_controller_free(chip->dw); + ret = data->remove(chip); if (ret) dev_warn(&pdev->dev, "can't remove device properly: %d\n", ret); -- GitLab From 0658e5a83a71c7a0c06c92b2259bc62088bf8472 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Tue, 26 May 2020 21:24:16 +0300 Subject: [PATCH 0005/1476] dmaengine: dw: Replace 'objs' by 'y' `-objs` is fitted for building host programs, change to `-y`, more straightforward for device drivers. Signed-off-by: Andy Shevchenko Link: https://lore.kernel.org/r/20200526182416.52805-2-andriy.shevchenko@linux.intel.com Signed-off-by: Vinod Koul --- drivers/dma/dw/Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/dma/dw/Makefile b/drivers/dma/dw/Makefile index 9e949f13e1b52..a6f358ad85919 100644 --- a/drivers/dma/dw/Makefile +++ b/drivers/dma/dw/Makefile @@ -1,6 +1,6 @@ # SPDX-License-Identifier: GPL-2.0 obj-$(CONFIG_DW_DMAC_CORE) += dw_dmac_core.o -dw_dmac_core-objs := core.o dw.o idma32.o +dw_dmac_core-y := core.o dw.o idma32.o dw_dmac_core-$(CONFIG_ACPI) += acpi.o obj-$(CONFIG_DW_DMAC) += dw_dmac.o @@ -8,4 +8,4 @@ dw_dmac-y := platform.o dw_dmac-$(CONFIG_OF) += of.o obj-$(CONFIG_DW_DMAC_PCI) += dw_dmac_pci.o -dw_dmac_pci-objs := pci.o +dw_dmac_pci-y := pci.o -- GitLab From 5bc382ec2f94a7c1b2a8938c6ca99c26a6fe5f58 Mon Sep 17 00:00:00 2001 From: Lubomir Rintel Date: Mon, 1 Jun 2020 21:23:37 +0200 Subject: [PATCH 0006/1476] dmaengine: mmp_pdma: Do not warn when IRQ is shared by all chans When there's a single interrupt for all the DMA channels, the unsuccessful attempt to request separate IRQs emits useless warnings: [ 1.370381] mmp-pdma d4000000.dma: IRQ index 1 not found ... [ 1.412398] mmp-pdma d4000000.dma: IRQ index 15 not found [ 1.418308] mmp-pdma d4000000.dma: initialized 16 channels Avoid that, treating the IRQs as optional. Signed-off-by: Lubomir Rintel Link: https://lore.kernel.org/r/20200601192337.172869-1-lkundrak@v3.sk Signed-off-by: Vinod Koul --- drivers/dma/mmp_pdma.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/dma/mmp_pdma.c b/drivers/dma/mmp_pdma.c index ad06f260e907d..41c542eaa23a5 100644 --- a/drivers/dma/mmp_pdma.c +++ b/drivers/dma/mmp_pdma.c @@ -1060,7 +1060,7 @@ static int mmp_pdma_probe(struct platform_device *op) pdev->dma_channels = dma_channels; for (i = 0; i < dma_channels; i++) { - if (platform_get_irq(op, i) > 0) + if (platform_get_irq_optional(op, i) > 0) irq_num++; } -- GitLab From c09a7ce668a666f1e044214647cb594940ba927c Mon Sep 17 00:00:00 2001 From: Lubomir Rintel Date: Mon, 1 Jun 2020 21:22:52 +0200 Subject: [PATCH 0007/1476] dmaengine: mmp_tdma: share the IRQ line On a MMP2, the DMA interrupt is shared by all channels of the peripheral DMA controller and the audio DMA controller. Both drivers can identify their interrupts, but only the PDMA driver marks the line shared: [ 1.185782] mmp-pdma d4000000.dma: initialized 16 channels [ 1.186808] mmp-tdma d42a0800.adma: IRQ index 1 not found [ 1.194317] genirq: Flags mismatch irq 64. 00000000 (tdma) vs. 00000080 (pdma) [ 1.197894] mmp-tdma: probe of d42a0800.adma failed with error -16 Let's turn on IRQF_SHARED in the ADMA driver as well. Signed-off-by: Lubomir Rintel Link: https://lore.kernel.org/r/20200601192252.172773-1-lkundrak@v3.sk Signed-off-by: Vinod Koul --- drivers/dma/mmp_tdma.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/dma/mmp_tdma.c b/drivers/dma/mmp_tdma.c index dbc6a48424fa1..960c7c40aef70 100644 --- a/drivers/dma/mmp_tdma.c +++ b/drivers/dma/mmp_tdma.c @@ -682,7 +682,7 @@ static int mmp_tdma_probe(struct platform_device *pdev) if (irq_num != chan_num) { irq = platform_get_irq(pdev, 0); ret = devm_request_irq(&pdev->dev, irq, - mmp_tdma_int_handler, 0, "tdma", tdev); + mmp_tdma_int_handler, IRQF_SHARED, "tdma", tdev); if (ret) return ret; } -- GitLab From 47ec7f09bc107720905c96bc37771e4ed1ff0aed Mon Sep 17 00:00:00 2001 From: Dave Jiang Date: Wed, 13 May 2020 11:47:49 -0700 Subject: [PATCH 0008/1476] dmaengine: cookie bypass for out of order completion The cookie tracking in dmaengine expects all submissions completed in order. Some DMA devices like Intel DSA can complete submissions out of order, especially if configured with a work queue sharing multiple DMA engines. Add a status DMA_OUT_OF_ORDER that tx_status can be returned for those DMA devices. The user should use callbacks to track the completion rather than the DMA cookie. This would address the issue of dmatest complaining that descriptors are "busy" when the cookie count goes backwards due to out of order completion. Add DMA_COMPLETION_NO_ORDER DMA capability to allow the driver to flag the device's ability to complete operations out of order. Reported-by: Swathi Kovvuri Signed-off-by: Dave Jiang Tested-by: Swathi Kovvuri Link: https://lore.kernel.org/r/158939557151.20335.12404113976045569870.stgit@djiang5-desk3.ch.intel.com Signed-off-by: Vinod Koul --- .../driver-api/dmaengine/provider.rst | 19 +++++++++++++++++++ drivers/dma/dmatest.c | 11 ++++++++++- drivers/dma/idxd/dma.c | 3 ++- include/linux/dmaengine.h | 2 ++ 4 files changed, 33 insertions(+), 2 deletions(-) diff --git a/Documentation/driver-api/dmaengine/provider.rst b/Documentation/driver-api/dmaengine/provider.rst index 56e5833e8a07b..ce68315482b1f 100644 --- a/Documentation/driver-api/dmaengine/provider.rst +++ b/Documentation/driver-api/dmaengine/provider.rst @@ -239,6 +239,22 @@ Currently, the types available are: want to transfer a portion of uncompressed data directly to the display to print it +- DMA_COMPLETION_NO_ORDER + + - The device does not support in order completion. + + - The driver should return DMA_OUT_OF_ORDER for device_tx_status if + the device is setting this capability. + + - All cookie tracking and checking API should be treated as invalid if + the device exports this capability. + + - At this point, this is incompatible with polling option for dmatest. + + - If this cap is set, the user is recommended to provide an unique + identifier for each descriptor sent to the DMA device in order to + properly track the completion. + These various types will also affect how the source and destination addresses change over time. @@ -399,6 +415,9 @@ supported. - In the case of a cyclic transfer, it should only take into account the current period. + - Should return DMA_OUT_OF_ORDER if the device does not support in order + completion and is completing the operation out of order. + - This function can be called in an interrupt context. - device_config diff --git a/drivers/dma/dmatest.c b/drivers/dma/dmatest.c index b175229a4b01d..18f10154ba191 100644 --- a/drivers/dma/dmatest.c +++ b/drivers/dma/dmatest.c @@ -829,7 +829,10 @@ static int dmatest_func(void *data) result("test timed out", total_tests, src->off, dst->off, len, 0); goto error_unmap_continue; - } else if (status != DMA_COMPLETE) { + } else if (status != DMA_COMPLETE && + !(dma_has_cap(DMA_COMPLETION_NO_ORDER, + dev->cap_mask) && + status == DMA_OUT_OF_ORDER)) { result(status == DMA_ERROR ? "completion error status" : "completion busy status", total_tests, src->off, @@ -1007,6 +1010,12 @@ static int dmatest_add_channel(struct dmatest_info *info, dtc->chan = chan; INIT_LIST_HEAD(&dtc->threads); + if (dma_has_cap(DMA_COMPLETION_NO_ORDER, dma_dev->cap_mask) && + info->params.polled) { + info->params.polled = false; + pr_warn("DMA_COMPLETION_NO_ORDER, polled disabled\n"); + } + if (dma_has_cap(DMA_MEMCPY, dma_dev->cap_mask)) { if (dmatest == 0) { cnt = dmatest_add_threads(info, dtc, DMA_MEMCPY); diff --git a/drivers/dma/idxd/dma.c b/drivers/dma/idxd/dma.c index c64c1429d1606..0c892cbd72e01 100644 --- a/drivers/dma/idxd/dma.c +++ b/drivers/dma/idxd/dma.c @@ -133,7 +133,7 @@ static enum dma_status idxd_dma_tx_status(struct dma_chan *dma_chan, dma_cookie_t cookie, struct dma_tx_state *txstate) { - return dma_cookie_status(dma_chan, cookie, txstate); + return DMA_OUT_OF_ORDER; } /* @@ -174,6 +174,7 @@ int idxd_register_dma_device(struct idxd_device *idxd) INIT_LIST_HEAD(&dma->channels); dma->dev = &idxd->pdev->dev; + dma_cap_set(DMA_COMPLETION_NO_ORDER, dma->cap_mask); dma->device_release = idxd_dma_release; if (idxd->hw.opcap.bits[0] & IDXD_OPCAP_MEMMOVE) { diff --git a/include/linux/dmaengine.h b/include/linux/dmaengine.h index e1c03339918f9..9f9a13a2c01f8 100644 --- a/include/linux/dmaengine.h +++ b/include/linux/dmaengine.h @@ -39,6 +39,7 @@ enum dma_status { DMA_IN_PROGRESS, DMA_PAUSED, DMA_ERROR, + DMA_OUT_OF_ORDER, }; /** @@ -61,6 +62,7 @@ enum dma_transaction_type { DMA_SLAVE, DMA_CYCLIC, DMA_INTERLEAVE, + DMA_COMPLETION_NO_ORDER, /* last transaction type for creation of the capabilities mask */ DMA_TX_TYPE_END, }; -- GitLab From 307b0d631d133318b9af9a43ea748e46a1a3b1ed Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Wed, 17 Jun 2020 12:23:04 +0200 Subject: [PATCH 0009/1476] dt-bindings: power: supply: bq25890: Indent example with tabs Fix example indentation to tabs to follow generic Linux coding style. This avoids copying the space indentation to DTS when re-using the example. Signed-off-by: Krzysztof Kozlowski Signed-off-by: Sebastian Reichel --- .../bindings/power/supply/bq25890.txt | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/Documentation/devicetree/bindings/power/supply/bq25890.txt b/Documentation/devicetree/bindings/power/supply/bq25890.txt index dc9c8f76e06cd..51ecc756521f0 100644 --- a/Documentation/devicetree/bindings/power/supply/bq25890.txt +++ b/Documentation/devicetree/bindings/power/supply/bq25890.txt @@ -36,17 +36,17 @@ Optional properties: Example: bq25890 { - compatible = "ti,bq25890"; - reg = <0x6a>; + compatible = "ti,bq25890"; + reg = <0x6a>; - ti,battery-regulation-voltage = <4200000>; - ti,charge-current = <1000000>; - ti,termination-current = <50000>; - ti,precharge-current = <128000>; - ti,minimum-sys-voltage = <3600000>; - ti,boost-voltage = <5000000>; - ti,boost-max-current = <1000000>; + ti,battery-regulation-voltage = <4200000>; + ti,charge-current = <1000000>; + ti,termination-current = <50000>; + ti,precharge-current = <128000>; + ti,minimum-sys-voltage = <3600000>; + ti,boost-voltage = <5000000>; + ti,boost-max-current = <1000000>; - ti,use-ilim-pin; - ti,thermal-regulation-threshold = <120>; + ti,use-ilim-pin; + ti,thermal-regulation-threshold = <120>; }; -- GitLab From 0768e6e4934e239f1a7f8ba83150a7c46765bb3e Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Wed, 17 Jun 2020 12:23:05 +0200 Subject: [PATCH 0010/1476] dt-bindings: power: supply: bq25890: Document required interrupt The driver requires interrupts (fails probe if it is not provided) so document this requirement in bindings. Fixes: 4aeae9cb0dad ("power_supply: Add support for TI BQ25890 charger chip") Cc: Signed-off-by: Krzysztof Kozlowski Signed-off-by: Sebastian Reichel --- Documentation/devicetree/bindings/power/supply/bq25890.txt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Documentation/devicetree/bindings/power/supply/bq25890.txt b/Documentation/devicetree/bindings/power/supply/bq25890.txt index 51ecc756521f0..3b4c69a7fa705 100644 --- a/Documentation/devicetree/bindings/power/supply/bq25890.txt +++ b/Documentation/devicetree/bindings/power/supply/bq25890.txt @@ -10,6 +10,7 @@ Required properties: * "ti,bq25895" * "ti,bq25896" - reg: integer, i2c address of the device. +- interrupts: interrupt line; - ti,battery-regulation-voltage: integer, maximum charging voltage (in uV); - ti,charge-current: integer, maximum charging current (in uA); - ti,termination-current: integer, charge will be terminated when current in @@ -39,6 +40,9 @@ bq25890 { compatible = "ti,bq25890"; reg = <0x6a>; + interrupt-parent = <&gpio1>; + interrupts = <16 IRQ_TYPE_EDGE_FALLING>; + ti,battery-regulation-voltage = <4200000>; ti,charge-current = <1000000>; ti,termination-current = <50000>; -- GitLab From 224941c9424fd840bd54d2df8dbe84d1e4aa35af Mon Sep 17 00:00:00 2001 From: Wang Qing Date: Fri, 12 Jun 2020 14:55:54 +0800 Subject: [PATCH 0011/1476] power: supply: use kobj_to_dev Use kobj_to_dev() API instead of open-coded container_of(). Signed-off-by: Wang Qing Signed-off-by: Sebastian Reichel --- drivers/power/supply/power_supply_sysfs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/power/supply/power_supply_sysfs.c b/drivers/power/supply/power_supply_sysfs.c index bc79560229b51..af0cad253f521 100644 --- a/drivers/power/supply/power_supply_sysfs.c +++ b/drivers/power/supply/power_supply_sysfs.c @@ -343,7 +343,7 @@ static umode_t power_supply_attr_is_visible(struct kobject *kobj, struct attribute *attr, int attrno) { - struct device *dev = container_of(kobj, struct device, kobj); + struct device *dev = kobj_to_dev(kobj); struct power_supply *psy = dev_get_drvdata(dev); umode_t mode = S_IRUSR | S_IRGRP | S_IROTH; int i; -- GitLab From 596f4785f498baf28be2d5dec067695d85457b09 Mon Sep 17 00:00:00 2001 From: Keyur Patel Date: Tue, 9 Jun 2020 18:50:35 -0400 Subject: [PATCH 0012/1476] power: supply: axp20x_usb_power: fix spelling mistake Fix typo: "triger" --> "trigger" Signed-off-by: Keyur Patel Acked-by: Chen-Yu Tsai Signed-off-by: Sebastian Reichel --- drivers/power/supply/axp20x_usb_power.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/power/supply/axp20x_usb_power.c b/drivers/power/supply/axp20x_usb_power.c index 4fde24b5f35a1..d01dc0332edc7 100644 --- a/drivers/power/supply/axp20x_usb_power.c +++ b/drivers/power/supply/axp20x_usb_power.c @@ -78,7 +78,7 @@ static bool axp20x_usb_vbus_needs_polling(struct axp20x_usb_power *power) /* * Polling is only necessary while VBUS is offline. While online, a * present->absent transition implies an online->offline transition - * and will triger the VBUS_REMOVAL IRQ. + * and will trigger the VBUS_REMOVAL IRQ. */ if (power->axp20x_id >= AXP221_ID && !power->online) return true; -- GitLab From df06230106e95347ec613f1707a704b04737c59b Mon Sep 17 00:00:00 2001 From: Dmitry Shmidt Date: Wed, 10 Jun 2020 10:30:11 +0200 Subject: [PATCH 0013/1476] dt-bindings: clk: g12a-clkc: Add NNA CLK Source clock IDs This adds the Neural Network Accelerator IP source clocks. Signed-off-by: Dmitry Shmidt Signed-off-by: Neil Armstrong Signed-off-by: Jerome Brunet Acked-by: Rob Herring Link: https://lore.kernel.org/r/20200610083012.5024-2-narmstrong@baylibre.com --- include/dt-bindings/clock/g12a-clkc.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/include/dt-bindings/clock/g12a-clkc.h b/include/dt-bindings/clock/g12a-clkc.h index b0d65d73db960..40d49940d8a8d 100644 --- a/include/dt-bindings/clock/g12a-clkc.h +++ b/include/dt-bindings/clock/g12a-clkc.h @@ -145,5 +145,7 @@ #define CLKID_CPU3_CLK 255 #define CLKID_SPICC0_SCLK 258 #define CLKID_SPICC1_SCLK 261 +#define CLKID_NNA_AXI_CLK 264 +#define CLKID_NNA_CORE_CLK 267 #endif /* __G12A_CLKC_H */ -- GitLab From 2f1efa5340eff9af36c9a7347bb97abd726128a0 Mon Sep 17 00:00:00 2001 From: Dmitry Shmidt Date: Wed, 10 Jun 2020 10:30:12 +0200 Subject: [PATCH 0014/1476] clk: meson: g12a: Add support for NNA CLK source clocks This adds the Neural Network Accelerator source clocks hierarchy, it's 2 simple composite clocks to feed the AXI interface and the Core of the Neural Network Accelerator IP. This IP is only present on the Amlogic SM1 SoCs family. Signed-off-by: Dmitry Shmidt Signed-off-by: Neil Armstrong Signed-off-by: Jerome Brunet Link: https://lore.kernel.org/r/20200610083012.5024-3-narmstrong@baylibre.com --- drivers/clk/meson/g12a.c | 119 +++++++++++++++++++++++++++++++++++++++ drivers/clk/meson/g12a.h | 7 ++- 2 files changed, 125 insertions(+), 1 deletion(-) diff --git a/drivers/clk/meson/g12a.c b/drivers/clk/meson/g12a.c index 30c15766ebb16..9803d44bb1578 100644 --- a/drivers/clk/meson/g12a.c +++ b/drivers/clk/meson/g12a.c @@ -3981,6 +3981,113 @@ static struct clk_regmap g12a_spicc1_sclk = { }, }; +/* Neural Network Accelerator source clock */ + +static const struct clk_parent_data nna_clk_parent_data[] = { + { .fw_name = "xtal", }, + { .hw = &g12a_gp0_pll.hw, }, + { .hw = &g12a_hifi_pll.hw, }, + { .hw = &g12a_fclk_div2p5.hw, }, + { .hw = &g12a_fclk_div3.hw, }, + { .hw = &g12a_fclk_div4.hw, }, + { .hw = &g12a_fclk_div5.hw, }, + { .hw = &g12a_fclk_div7.hw }, +}; + +static struct clk_regmap sm1_nna_axi_clk_sel = { + .data = &(struct clk_regmap_mux_data){ + .offset = HHI_NNA_CLK_CNTL, + .mask = 7, + .shift = 9, + }, + .hw.init = &(struct clk_init_data){ + .name = "nna_axi_clk_sel", + .ops = &clk_regmap_mux_ops, + .parent_data = nna_clk_parent_data, + .num_parents = ARRAY_SIZE(nna_clk_parent_data), + }, +}; + +static struct clk_regmap sm1_nna_axi_clk_div = { + .data = &(struct clk_regmap_div_data){ + .offset = HHI_NNA_CLK_CNTL, + .shift = 0, + .width = 7, + }, + .hw.init = &(struct clk_init_data){ + .name = "nna_axi_clk_div", + .ops = &clk_regmap_divider_ops, + .parent_hws = (const struct clk_hw *[]) { + &sm1_nna_axi_clk_sel.hw + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + }, +}; + +static struct clk_regmap sm1_nna_axi_clk = { + .data = &(struct clk_regmap_gate_data){ + .offset = HHI_NNA_CLK_CNTL, + .bit_idx = 8, + }, + .hw.init = &(struct clk_init_data){ + .name = "nna_axi_clk", + .ops = &clk_regmap_gate_ops, + .parent_hws = (const struct clk_hw *[]) { + &sm1_nna_axi_clk_div.hw + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + }, +}; + +static struct clk_regmap sm1_nna_core_clk_sel = { + .data = &(struct clk_regmap_mux_data){ + .offset = HHI_NNA_CLK_CNTL, + .mask = 7, + .shift = 25, + }, + .hw.init = &(struct clk_init_data){ + .name = "nna_core_clk_sel", + .ops = &clk_regmap_mux_ops, + .parent_data = nna_clk_parent_data, + .num_parents = ARRAY_SIZE(nna_clk_parent_data), + }, +}; + +static struct clk_regmap sm1_nna_core_clk_div = { + .data = &(struct clk_regmap_div_data){ + .offset = HHI_NNA_CLK_CNTL, + .shift = 16, + .width = 7, + }, + .hw.init = &(struct clk_init_data){ + .name = "nna_core_clk_div", + .ops = &clk_regmap_divider_ops, + .parent_hws = (const struct clk_hw *[]) { + &sm1_nna_core_clk_sel.hw + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + }, +}; + +static struct clk_regmap sm1_nna_core_clk = { + .data = &(struct clk_regmap_gate_data){ + .offset = HHI_NNA_CLK_CNTL, + .bit_idx = 24, + }, + .hw.init = &(struct clk_init_data){ + .name = "nna_core_clk", + .ops = &clk_regmap_gate_ops, + .parent_hws = (const struct clk_hw *[]) { + &sm1_nna_core_clk_div.hw + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + }, +}; + #define MESON_GATE(_name, _reg, _bit) \ MESON_PCLK(_name, _reg, _bit, &g12a_clk81.hw) @@ -4779,6 +4886,12 @@ static struct clk_hw_onecell_data sm1_hw_onecell_data = { [CLKID_SPICC1_SCLK_SEL] = &g12a_spicc1_sclk_sel.hw, [CLKID_SPICC1_SCLK_DIV] = &g12a_spicc1_sclk_div.hw, [CLKID_SPICC1_SCLK] = &g12a_spicc1_sclk.hw, + [CLKID_NNA_AXI_CLK_SEL] = &sm1_nna_axi_clk_sel.hw, + [CLKID_NNA_AXI_CLK_DIV] = &sm1_nna_axi_clk_div.hw, + [CLKID_NNA_AXI_CLK] = &sm1_nna_axi_clk.hw, + [CLKID_NNA_CORE_CLK_SEL] = &sm1_nna_core_clk_sel.hw, + [CLKID_NNA_CORE_CLK_DIV] = &sm1_nna_core_clk_div.hw, + [CLKID_NNA_CORE_CLK] = &sm1_nna_core_clk.hw, [NR_CLKS] = NULL, }, .num = NR_CLKS, @@ -5020,6 +5133,12 @@ static struct clk_regmap *const g12a_clk_regmaps[] = { &g12a_spicc1_sclk_sel, &g12a_spicc1_sclk_div, &g12a_spicc1_sclk, + &sm1_nna_axi_clk_sel, + &sm1_nna_axi_clk_div, + &sm1_nna_axi_clk, + &sm1_nna_core_clk_sel, + &sm1_nna_core_clk_div, + &sm1_nna_core_clk, }; static const struct reg_sequence g12a_init_regs[] = { diff --git a/drivers/clk/meson/g12a.h b/drivers/clk/meson/g12a.h index a8852556836ee..69b6a69549c77 100644 --- a/drivers/clk/meson/g12a.h +++ b/drivers/clk/meson/g12a.h @@ -70,6 +70,7 @@ #define HHI_MALI_CLK_CNTL 0x1b0 #define HHI_VPU_CLKC_CNTL 0x1b4 #define HHI_VPU_CLK_CNTL 0x1bC +#define HHI_NNA_CLK_CNTL 0x1C8 #define HHI_HDMI_CLK_CNTL 0x1CC #define HHI_VDEC_CLK_CNTL 0x1E0 #define HHI_VDEC2_CLK_CNTL 0x1E4 @@ -259,8 +260,12 @@ #define CLKID_SPICC0_SCLK_DIV 257 #define CLKID_SPICC1_SCLK_SEL 259 #define CLKID_SPICC1_SCLK_DIV 260 +#define CLKID_NNA_AXI_CLK_SEL 262 +#define CLKID_NNA_AXI_CLK_DIV 263 +#define CLKID_NNA_CORE_CLK_SEL 265 +#define CLKID_NNA_CORE_CLK_DIV 266 -#define NR_CLKS 262 +#define NR_CLKS 268 /* include the CLKIDs that have been made part of the DT binding */ #include -- GitLab From dad980f13243281b8231a1a3365ce380f312f825 Mon Sep 17 00:00:00 2001 From: Sebastian Reichel Date: Sat, 6 Jun 2020 00:43:58 +0200 Subject: [PATCH 0015/1476] dt-bindings: power: supply: gpio-charger: convert to yaml Convert the gpio-charger bindings from text format to new YAML based representation. Reviewed-by: Linus Walleij Reviewed-by: Rob Herring Signed-off-by: Sebastian Reichel --- .../bindings/power/supply/gpio-charger.txt | 31 ---------- .../bindings/power/supply/gpio-charger.yaml | 58 +++++++++++++++++++ 2 files changed, 58 insertions(+), 31 deletions(-) delete mode 100644 Documentation/devicetree/bindings/power/supply/gpio-charger.txt create mode 100644 Documentation/devicetree/bindings/power/supply/gpio-charger.yaml diff --git a/Documentation/devicetree/bindings/power/supply/gpio-charger.txt b/Documentation/devicetree/bindings/power/supply/gpio-charger.txt deleted file mode 100644 index 0fb33b2c62a60..0000000000000 --- a/Documentation/devicetree/bindings/power/supply/gpio-charger.txt +++ /dev/null @@ -1,31 +0,0 @@ -gpio-charger - -Required properties : - - compatible : "gpio-charger" - - gpios : GPIO indicating the charger presence. - See GPIO binding in bindings/gpio/gpio.txt . - - charger-type : power supply type, one of - unknown - battery - ups - mains - usb-sdp (USB standard downstream port) - usb-dcp (USB dedicated charging port) - usb-cdp (USB charging downstream port) - usb-aca (USB accessory charger adapter) - -Optional properties: - - charge-status-gpios: GPIO indicating whether a battery is charging. - -Example: - - usb_charger: charger { - compatible = "gpio-charger"; - charger-type = "usb-sdp"; - gpios = <&gpd 28 GPIO_ACTIVE_LOW>; - charge-status-gpios = <&gpc 27 GPIO_ACTIVE_LOW>; - }; - - battery { - power-supplies = <&usb_charger>; - }; diff --git a/Documentation/devicetree/bindings/power/supply/gpio-charger.yaml b/Documentation/devicetree/bindings/power/supply/gpio-charger.yaml new file mode 100644 index 0000000000000..78b167c62ab15 --- /dev/null +++ b/Documentation/devicetree/bindings/power/supply/gpio-charger.yaml @@ -0,0 +1,58 @@ +# SPDX-License-Identifier: GPL-2.0 +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/power/supply/gpio-charger.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: simple battery chargers only communicating through GPIOs + +maintainers: + - Sebastian Reichel + +description: + This binding is for all chargers, which are working more or less + autonomously, only providing some status GPIOs and possibly some + GPIOs for limited control over the charging process. + +properties: + compatible: + const: gpio-charger + + charger-type: + enum: + - unknown + - battery + - ups + - mains + - usb-sdp # USB standard downstream port + - usb-dcp # USB dedicated charging port + - usb-cdp # USB charging downstream port + - usb-aca # USB accessory charger adapter + description: + Type of the charger, e.g. "mains" for a wall charger. + + gpios: + maxItems: 1 + description: GPIO indicating the charger presence + + charge-status-gpios: + maxItems: 1 + description: GPIO indicating the charging status + +required: + - compatible + - gpios + +additionalProperties: false + +examples: + - | + #include + + charger { + compatible = "gpio-charger"; + charger-type = "usb-sdp"; + + gpios = <&gpd 28 GPIO_ACTIVE_LOW>; + charge-status-gpios = <&gpc 27 GPIO_ACTIVE_LOW>; + }; -- GitLab From 1d85f6d1b0c11f15ec2b1a9719da8118f3f94e64 Mon Sep 17 00:00:00 2001 From: Sebastian Reichel Date: Sat, 6 Jun 2020 00:43:59 +0200 Subject: [PATCH 0016/1476] power: supply: gpio-charger: Make gpios optional While strongly recommended, not all devices have a gpio to detect if the charger is connected. This moves the 'gpios' from required to optional section. This also modifies error handling for the GPIO a bit: We no longer fallback to pdata, if a GPIO is specified using GPIO descriptor tables. This is a bit cleaner and does not have any real impact: There are only two mainline pdata users (arm/mach-sa1100/collie.c, arm/mach-pxa/tosa.c) and none of them specify the GPIO via gpiod descriptor tables. Once both have been converted the driver's support for specifying GPIOs numbers in pdata will be dropped. Reviewed-by: Linus Walleij Acked-by: Rob Herring Signed-off-by: Sebastian Reichel --- .../bindings/power/supply/gpio-charger.yaml | 7 +++- drivers/power/supply/gpio-charger.c | 38 ++++++++++++------- 2 files changed, 31 insertions(+), 14 deletions(-) diff --git a/Documentation/devicetree/bindings/power/supply/gpio-charger.yaml b/Documentation/devicetree/bindings/power/supply/gpio-charger.yaml index 78b167c62ab15..30eabbb14ef33 100644 --- a/Documentation/devicetree/bindings/power/supply/gpio-charger.yaml +++ b/Documentation/devicetree/bindings/power/supply/gpio-charger.yaml @@ -41,7 +41,12 @@ properties: required: - compatible - - gpios + +anyOf: + - required: + - gpios + - required: + - charge-status-gpios additionalProperties: false diff --git a/drivers/power/supply/gpio-charger.c b/drivers/power/supply/gpio-charger.c index 1b959c7f8b0e6..875735d50716b 100644 --- a/drivers/power/supply/gpio-charger.c +++ b/drivers/power/supply/gpio-charger.c @@ -112,9 +112,14 @@ static int gpio_charger_get_irq(struct device *dev, void *dev_id, return irq; } +/* + * The entries will be overwritten by driver's probe routine depending + * on the available features. This list ensures, that the array is big + * enough for all optional features. + */ static enum power_supply_property gpio_charger_properties[] = { POWER_SUPPLY_PROP_ONLINE, - POWER_SUPPLY_PROP_STATUS /* Must always be last in the array. */ + POWER_SUPPLY_PROP_STATUS, }; static int gpio_charger_probe(struct platform_device *pdev) @@ -128,6 +133,7 @@ static int gpio_charger_probe(struct platform_device *pdev) int charge_status_irq; unsigned long flags; int ret; + int num_props = 0; if (!pdata && !dev->of_node) { dev_err(dev, "No platform data\n"); @@ -142,13 +148,13 @@ static int gpio_charger_probe(struct platform_device *pdev) * This will fetch a GPIO descriptor from device tree, ACPI or * boardfile descriptor tables. It's good to try this first. */ - gpio_charger->gpiod = devm_gpiod_get(dev, NULL, GPIOD_IN); + gpio_charger->gpiod = devm_gpiod_get_optional(dev, NULL, GPIOD_IN); /* - * If this fails and we're not using device tree, try the - * legacy platform data method. + * Fallback to legacy platform data method, if no GPIO is specified + * using boardfile descriptor tables. */ - if (IS_ERR(gpio_charger->gpiod) && !dev->of_node) { + if (!gpio_charger->gpiod && pdata) { /* Non-DT: use legacy GPIO numbers */ if (!gpio_is_valid(pdata->gpio)) { dev_err(dev, "Invalid gpio pin in pdata\n"); @@ -173,17 +179,23 @@ static int gpio_charger_probe(struct platform_device *pdev) return PTR_ERR(gpio_charger->gpiod); } + if (gpio_charger->gpiod) { + gpio_charger_properties[num_props] = POWER_SUPPLY_PROP_ONLINE; + num_props++; + } + charge_status = devm_gpiod_get_optional(dev, "charge-status", GPIOD_IN); - gpio_charger->charge_status = charge_status; - if (IS_ERR(gpio_charger->charge_status)) - return PTR_ERR(gpio_charger->charge_status); + if (IS_ERR(charge_status)) + return PTR_ERR(charge_status); + if (charge_status) { + gpio_charger->charge_status = charge_status; + gpio_charger_properties[num_props] = POWER_SUPPLY_PROP_STATUS; + num_props++; + } charger_desc = &gpio_charger->charger_desc; charger_desc->properties = gpio_charger_properties; - charger_desc->num_properties = ARRAY_SIZE(gpio_charger_properties); - /* Remove POWER_SUPPLY_PROP_STATUS from the supported properties. */ - if (!gpio_charger->charge_status) - charger_desc->num_properties -= 1; + charger_desc->num_properties = num_props; charger_desc->get_property = gpio_charger_get_property; psy_cfg.of_node = dev->of_node; @@ -269,6 +281,6 @@ static struct platform_driver gpio_charger_driver = { module_platform_driver(gpio_charger_driver); MODULE_AUTHOR("Lars-Peter Clausen "); -MODULE_DESCRIPTION("Driver for chargers which report their online status through a GPIO"); +MODULE_DESCRIPTION("Driver for chargers only communicating via GPIO(s)"); MODULE_LICENSE("GPL"); MODULE_ALIAS("platform:gpio-charger"); -- GitLab From 05e0430971543e6aeb328df1226dc52930171445 Mon Sep 17 00:00:00 2001 From: Sebastian Reichel Date: Sat, 6 Jun 2020 01:06:24 +0200 Subject: [PATCH 0017/1476] power: supply: sbs-battery: use i2c_smbus_read_block_data() The SBS battery implements SMBus block reads. Currently the driver "emulates" this by doing an I2C byte read for the length followed by an I2C block read. The I2C subsystem actually provides a proper API for doing SMBus block reads, which can and should be used instead. The current implementation does not properly handle packet error checking (PEC). Not all upstream systems using sbs-battery have I2C bus drivers supporting I2C_M_RECV_LEN, so old implementation is kept as fallback to keep things working. But this prints a warning, which hopefully results in people implementing support for it. Tested-by: Marek Szyprowski Signed-off-by: Sebastian Reichel --- drivers/power/supply/sbs-battery.c | 31 ++++++++++++++++++++++++++++-- 1 file changed, 29 insertions(+), 2 deletions(-) diff --git a/drivers/power/supply/sbs-battery.c b/drivers/power/supply/sbs-battery.c index 83b9924033bd5..74221b9279a92 100644 --- a/drivers/power/supply/sbs-battery.c +++ b/drivers/power/supply/sbs-battery.c @@ -263,8 +263,7 @@ static int sbs_read_word_data(struct i2c_client *client, u8 address) return ret; } -static int sbs_read_string_data(struct i2c_client *client, u8 address, - char *values) +static int sbs_read_string_data_fallback(struct i2c_client *client, u8 address, char *values) { struct sbs_info *chip = i2c_get_clientdata(client); s32 ret = 0, block_length = 0; @@ -274,6 +273,8 @@ static int sbs_read_string_data(struct i2c_client *client, u8 address, retries_length = chip->i2c_retry_count; retries_block = chip->i2c_retry_count; + dev_warn_once(&client->dev, "I2C adapter does not support I2C_FUNC_SMBUS_READ_BLOCK_DATA.\n"); + /* Adapter needs to support these two functions */ if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA | @@ -329,6 +330,32 @@ static int sbs_read_string_data(struct i2c_client *client, u8 address, return ret; } +static int sbs_read_string_data(struct i2c_client *client, u8 address, char *values) +{ + struct sbs_info *chip = i2c_get_clientdata(client); + int retries = chip->i2c_retry_count; + int ret = 0; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_READ_BLOCK_DATA)) + return sbs_read_string_data_fallback(client, address, values); + + while (retries > 0) { + ret = i2c_smbus_read_block_data(client, address, values); + if (ret >= 0) + break; + retries--; + } + + if (ret < 0) { + dev_dbg(&client->dev, "failed to read block 0x%x: %d\n", address, ret); + return ret; + } + + /* add string termination */ + values[ret] = '\0'; + return ret; +} + static int sbs_write_word_data(struct i2c_client *client, u8 address, u16 value) { -- GitLab From 7222bd603dd2fe607794acd0c53025da1dbde21f Mon Sep 17 00:00:00 2001 From: Sebastian Reichel Date: Sat, 6 Jun 2020 01:06:25 +0200 Subject: [PATCH 0018/1476] power: supply: sbs-battery: add PEC support SBS batteries optionally have support for PEC. This enables PEC handling based on the implemented SBS version as suggested by the standard. The support for PEC is re-evaluated when the battery is hotplugged into the system, since there might be systems supporting batteries from different SBS generations. Tested-by: Marek Szyprowski Signed-off-by: Sebastian Reichel --- drivers/power/supply/sbs-battery.c | 64 ++++++++++++++++++++++++++++-- 1 file changed, 61 insertions(+), 3 deletions(-) diff --git a/drivers/power/supply/sbs-battery.c b/drivers/power/supply/sbs-battery.c index 74221b9279a92..49c3508a6b799 100644 --- a/drivers/power/supply/sbs-battery.c +++ b/drivers/power/supply/sbs-battery.c @@ -51,6 +51,14 @@ enum { REG_CHARGE_VOLTAGE, }; +#define REG_ADDR_SPEC_INFO 0x1A +#define SPEC_INFO_VERSION_MASK GENMASK(7, 4) +#define SPEC_INFO_VERSION_SHIFT 4 + +#define SBS_VERSION_1_0 1 +#define SBS_VERSION_1_1 2 +#define SBS_VERSION_1_1_WITH_PEC 3 + #define REG_ADDR_MANUFACTURE_DATE 0x1B /* Battery Mode defines */ @@ -224,14 +232,57 @@ exit: static int sbs_update_presence(struct sbs_info *chip, bool is_present) { + struct i2c_client *client = chip->client; + int retries = chip->i2c_retry_count; + s32 ret = 0; + u8 version; + if (chip->is_present == is_present) return 0; if (!is_present) { chip->is_present = false; + /* Disable PEC when no device is present */ + client->flags &= ~I2C_CLIENT_PEC; return 0; } + /* Check if device supports packet error checking and use it */ + while (retries > 0) { + ret = i2c_smbus_read_word_data(client, REG_ADDR_SPEC_INFO); + if (ret >= 0) + break; + + /* + * Some batteries trigger the detection pin before the + * I2C bus is properly connected. This works around the + * issue. + */ + msleep(100); + + retries--; + } + + if (ret < 0) { + dev_dbg(&client->dev, "failed to read spec info: %d\n", ret); + + /* fallback to old behaviour */ + client->flags &= ~I2C_CLIENT_PEC; + chip->is_present = true; + + return ret; + } + + version = (ret & SPEC_INFO_VERSION_MASK) >> SPEC_INFO_VERSION_SHIFT; + + if (version == SBS_VERSION_1_1_WITH_PEC) + client->flags |= I2C_CLIENT_PEC; + else + client->flags &= ~I2C_CLIENT_PEC; + + dev_dbg(&client->dev, "PEC: %s\n", (client->flags & I2C_CLIENT_PEC) ? + "enabled" : "disabled"); + if (!chip->is_present && is_present && !chip->charger_broadcasts) sbs_disable_charger_broadcasts(chip); @@ -273,7 +324,8 @@ static int sbs_read_string_data_fallback(struct i2c_client *client, u8 address, retries_length = chip->i2c_retry_count; retries_block = chip->i2c_retry_count; - dev_warn_once(&client->dev, "I2C adapter does not support I2C_FUNC_SMBUS_READ_BLOCK_DATA.\n"); + dev_warn_once(&client->dev, "I2C adapter does not support I2C_FUNC_SMBUS_READ_BLOCK_DATA.\n" + "Fallback method does not support PEC.\n"); /* Adapter needs to support these two functions */ if (!i2c_check_functionality(client->adapter, @@ -336,8 +388,14 @@ static int sbs_read_string_data(struct i2c_client *client, u8 address, char *val int retries = chip->i2c_retry_count; int ret = 0; - if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_READ_BLOCK_DATA)) - return sbs_read_string_data_fallback(client, address, values); + if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_READ_BLOCK_DATA)) { + bool pec = client->flags & I2C_CLIENT_PEC; + client->flags &= ~I2C_CLIENT_PEC; + ret = sbs_read_string_data_fallback(client, address, values); + if (pec) + client->flags |= I2C_CLIENT_PEC; + return ret; + } while (retries > 0) { ret = i2c_smbus_read_block_data(client, address, values); -- GitLab From 1a37a039711610dd53ec03d8cab9e81875338225 Mon Sep 17 00:00:00 2001 From: Dinghao Liu Date: Mon, 25 May 2020 19:05:40 +0800 Subject: [PATCH 0019/1476] power: supply: bq24190_charger: Fix runtime PM imbalance on error pm_runtime_get_sync() increments the runtime PM usage counter even it returns an error code. Thus a pairing decrement is needed on the error handling path to keep the counter balanced. Signed-off-by: Dinghao Liu Reviewed-by: Rafael J. Wysocki Signed-off-by: Sebastian Reichel --- drivers/power/supply/bq24190_charger.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/power/supply/bq24190_charger.c b/drivers/power/supply/bq24190_charger.c index 4540e913057f1..d14186525e1e9 100644 --- a/drivers/power/supply/bq24190_charger.c +++ b/drivers/power/supply/bq24190_charger.c @@ -481,8 +481,10 @@ static ssize_t bq24190_sysfs_store(struct device *dev, return ret; ret = pm_runtime_get_sync(bdi->dev); - if (ret < 0) + if (ret < 0) { + pm_runtime_put_noidle(bdi->dev); return ret; + } ret = bq24190_write_mask(bdi, info->reg, info->mask, info->shift, v); if (ret) -- GitLab From 5d809cb28056221e1b13459f7a2ddfa28305b74f Mon Sep 17 00:00:00 2001 From: Jonathan Bakker Date: Sat, 16 May 2020 15:35:44 -0700 Subject: [PATCH 0020/1476] power: supply: max8998_charger: Correct ONLINE and add STATUS props The ONLINE prop should be when the charger is present (ie able to charge), however, it was for when it was actually charging or not. Instead, add the STATUS prop to show whether charging is actually going on or not. The magic numbers have been ported from a downstream kernel for the SGH-T959V. Signed-off-by: Jonathan Bakker Signed-off-by: Sebastian Reichel --- drivers/power/supply/max8998_charger.c | 25 ++++++++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/drivers/power/supply/max8998_charger.c b/drivers/power/supply/max8998_charger.c index 9a926c7c0f22a..c26023b19f267 100644 --- a/drivers/power/supply/max8998_charger.c +++ b/drivers/power/supply/max8998_charger.c @@ -23,6 +23,7 @@ struct max8998_battery_data { static enum power_supply_property max8998_battery_props[] = { POWER_SUPPLY_PROP_PRESENT, /* the presence of battery */ POWER_SUPPLY_PROP_ONLINE, /* charger is active or not */ + POWER_SUPPLY_PROP_STATUS, /* charger is charging/discharging/full */ }; /* Note that the charger control is done by a current regulator "CHARGER" */ @@ -49,10 +50,28 @@ static int max8998_battery_get_property(struct power_supply *psy, ret = max8998_read_reg(i2c, MAX8998_REG_STATUS2, ®); if (ret) return ret; - if (reg & (1 << 3)) - val->intval = 0; - else + + if (reg & (1 << 5)) val->intval = 1; + else + val->intval = 0; + + break; + case POWER_SUPPLY_PROP_STATUS: + ret = max8998_read_reg(i2c, MAX8998_REG_STATUS2, ®); + if (ret) + return ret; + + if (!(reg & (1 << 5))) { + val->intval = POWER_SUPPLY_STATUS_DISCHARGING; + } else { + if (reg & (1 << 6)) + val->intval = POWER_SUPPLY_STATUS_FULL; + else if (reg & (1 << 3)) + val->intval = POWER_SUPPLY_STATUS_CHARGING; + else + val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING; + } break; default: return -EINVAL; -- GitLab From cf8030d7035bd3e89c9e66f7193a7fc8057a9b9a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81lvaro=20Fern=C3=A1ndez=20Rojas?= Date: Tue, 9 Jun 2020 13:08:46 +0200 Subject: [PATCH 0021/1476] clk: bcm63xx-gate: fix last clock availability MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In order to make the last clock available, maxbit has to be set to the highest bit value plus 1. Fixes: 1c099779c1e2 ("clk: add BCM63XX gated clock controller driver") Signed-off-by: Álvaro Fernández Rojas Link: https://lore.kernel.org/r/20200609110846.4029620-1-noltari@gmail.com Reviewed-by: Florian Fainelli Signed-off-by: Stephen Boyd --- drivers/clk/bcm/clk-bcm63xx-gate.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/clk/bcm/clk-bcm63xx-gate.c b/drivers/clk/bcm/clk-bcm63xx-gate.c index 98e884957db87..911a29bd744ef 100644 --- a/drivers/clk/bcm/clk-bcm63xx-gate.c +++ b/drivers/clk/bcm/clk-bcm63xx-gate.c @@ -155,6 +155,7 @@ static int clk_bcm63xx_probe(struct platform_device *pdev) for (entry = table; entry->name; entry++) maxbit = max_t(u8, maxbit, entry->bit); + maxbit++; hw = devm_kzalloc(&pdev->dev, struct_size(hw, data.hws, maxbit), GFP_KERNEL); -- GitLab From 8fc4f427b84b3949db0be2f66955f91a0c29ba66 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81lvaro=20Fern=C3=A1ndez=20Rojas?= Date: Wed, 10 Jun 2020 16:08:57 +0200 Subject: [PATCH 0022/1476] dt-bindings: clock: bcm63xx: add 6318 gated clock bindings MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add BCM6318 to the binding documentation for the gated clock controllers found on BCM63xx SoCs. Signed-off-by: Álvaro Fernández Rojas Reviewed-by: Florian Fainelli Link: https://lore.kernel.org/r/20200610140858.207329-2-noltari@gmail.com Acked-by: Rob Herring Signed-off-by: Stephen Boyd --- Documentation/devicetree/bindings/clock/brcm,bcm63xx-clocks.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Documentation/devicetree/bindings/clock/brcm,bcm63xx-clocks.txt b/Documentation/devicetree/bindings/clock/brcm,bcm63xx-clocks.txt index 3041657e2f96e..3e7ca55307753 100644 --- a/Documentation/devicetree/bindings/clock/brcm,bcm63xx-clocks.txt +++ b/Documentation/devicetree/bindings/clock/brcm,bcm63xx-clocks.txt @@ -3,6 +3,8 @@ Gated Clock Controller Bindings for MIPS based BCM63XX SoCs Required properties: - compatible: must be one of: "brcm,bcm3368-clocks" + "brcm,bcm6318-clocks" + "brcm,bcm6318-ubus-clocks" "brcm,bcm6328-clocks" "brcm,bcm6358-clocks" "brcm,bcm6362-clocks" -- GitLab From 90741a7268dfe4d4f159c8e10a1c1cb9d4e31dd9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81lvaro=20Fern=C3=A1ndez=20Rojas?= Date: Wed, 10 Jun 2020 16:08:58 +0200 Subject: [PATCH 0023/1476] clk: bcm63xx-gate: add BCM6318 support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add support for the gated clock controllers found on the BCM6318. Signed-off-by: Álvaro Fernández Rojas Reviewed-by: Florian Fainelli Link: https://lore.kernel.org/r/20200610140858.207329-3-noltari@gmail.com Signed-off-by: Stephen Boyd --- drivers/clk/bcm/clk-bcm63xx-gate.c | 44 ++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/drivers/clk/bcm/clk-bcm63xx-gate.c b/drivers/clk/bcm/clk-bcm63xx-gate.c index 911a29bd744ef..43853add44cb0 100644 --- a/drivers/clk/bcm/clk-bcm63xx-gate.c +++ b/drivers/clk/bcm/clk-bcm63xx-gate.c @@ -40,6 +40,48 @@ static const struct clk_bcm63xx_table_entry bcm3368_clocks[] = { { }, }; +static const struct clk_bcm63xx_table_entry bcm6318_clocks[] = { + { .name = "adsl_asb", .bit = 0, }, + { .name = "usb_asb", .bit = 1, }, + { .name = "mips_asb", .bit = 2, }, + { .name = "pcie_asb", .bit = 3, }, + { .name = "phymips_asb", .bit = 4, }, + { .name = "robosw_asb", .bit = 5, }, + { .name = "sar_asb", .bit = 6, }, + { .name = "sdr_asb", .bit = 7, }, + { .name = "swreg_asb", .bit = 8, }, + { .name = "periph_asb", .bit = 9, }, + { .name = "cpubus160", .bit = 10, }, + { .name = "adsl", .bit = 11, }, + { .name = "sar125", .bit = 12, }, + { .name = "mips", .bit = 13, .flags = CLK_IS_CRITICAL, }, + { .name = "pcie", .bit = 14, }, + { .name = "robosw250", .bit = 16, }, + { .name = "robosw025", .bit = 17, }, + { .name = "sdr", .bit = 19, .flags = CLK_IS_CRITICAL, }, + { .name = "usbd", .bit = 20, }, + { .name = "hsspi", .bit = 25, }, + { .name = "pcie25", .bit = 27, }, + { .name = "phymips", .bit = 28, }, + { .name = "afe", .bit = 29, }, + { .name = "qproc", .bit = 30, }, + { }, +}; + +static const struct clk_bcm63xx_table_entry bcm6318_ubus_clocks[] = { + { .name = "adsl-ubus", .bit = 0, }, + { .name = "arb-ubus", .bit = 1, .flags = CLK_IS_CRITICAL, }, + { .name = "mips-ubus", .bit = 2, .flags = CLK_IS_CRITICAL, }, + { .name = "pcie-ubus", .bit = 3, }, + { .name = "periph-ubus", .bit = 4, .flags = CLK_IS_CRITICAL, }, + { .name = "phymips-ubus", .bit = 5, }, + { .name = "robosw-ubus", .bit = 6, }, + { .name = "sar-ubus", .bit = 7, }, + { .name = "sdr-ubus", .bit = 8, }, + { .name = "usb-ubus", .bit = 9, }, + { }, +}; + static const struct clk_bcm63xx_table_entry bcm6328_clocks[] = { { .name = "phy_mips", .bit = 0, }, { .name = "adsl_qproc", .bit = 1, }, @@ -218,6 +260,8 @@ static int clk_bcm63xx_remove(struct platform_device *pdev) static const struct of_device_id clk_bcm63xx_dt_ids[] = { { .compatible = "brcm,bcm3368-clocks", .data = &bcm3368_clocks, }, + { .compatible = "brcm,bcm6318-clocks", .data = &bcm6318_clocks, }, + { .compatible = "brcm,bcm6318-ubus-clocks", .data = &bcm6318_ubus_clocks, }, { .compatible = "brcm,bcm6328-clocks", .data = &bcm6328_clocks, }, { .compatible = "brcm,bcm6358-clocks", .data = &bcm6358_clocks, }, { .compatible = "brcm,bcm6362-clocks", .data = &bcm6362_clocks, }, -- GitLab From c7f03eea07682639ef320aab348b706c330941dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81lvaro=20Fern=C3=A1ndez=20Rojas?= Date: Mon, 15 Jun 2020 11:02:24 +0200 Subject: [PATCH 0024/1476] mips: bmips: add BCM3368 clock definitions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add header with BCM3368 definitions in order to be able to include it from device tree files. Signed-off-by: Álvaro Fernández Rojas Link: https://lore.kernel.org/r/20200615090231.2932696-2-noltari@gmail.com Acked-by: Florian Fainelli Signed-off-by: Stephen Boyd --- include/dt-bindings/clock/bcm3368-clock.h | 24 +++++++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 include/dt-bindings/clock/bcm3368-clock.h diff --git a/include/dt-bindings/clock/bcm3368-clock.h b/include/dt-bindings/clock/bcm3368-clock.h new file mode 100644 index 0000000000000..74a7382f77b8b --- /dev/null +++ b/include/dt-bindings/clock/bcm3368-clock.h @@ -0,0 +1,24 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ + +#ifndef __DT_BINDINGS_CLOCK_BCM3368_H +#define __DT_BINDINGS_CLOCK_BCM3368_H + +#define BCM3368_CLK_MAC 3 +#define BCM3368_CLK_TC 5 +#define BCM3368_CLK_US_TOP 6 +#define BCM3368_CLK_DS_TOP 7 +#define BCM3368_CLK_ACM 8 +#define BCM3368_CLK_SPI 9 +#define BCM3368_CLK_USBS 10 +#define BCM3368_CLK_BMU 11 +#define BCM3368_CLK_PCM 12 +#define BCM3368_CLK_NTP 13 +#define BCM3368_CLK_ACP_B 14 +#define BCM3368_CLK_ACP_A 15 +#define BCM3368_CLK_EMUSB 17 +#define BCM3368_CLK_ENET0 18 +#define BCM3368_CLK_ENET1 19 +#define BCM3368_CLK_USBSU 20 +#define BCM3368_CLK_EPHY 21 + +#endif /* __DT_BINDINGS_CLOCK_BCM3368_H */ -- GitLab From 020c89c5a981cb6d0424aadab8ae067a3b6bd8e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81lvaro=20Fern=C3=A1ndez=20Rojas?= Date: Mon, 15 Jun 2020 11:02:25 +0200 Subject: [PATCH 0025/1476] mips: bmips: add BCM6318 clock definitions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add header with BCM6318 definitions in order to be able to include it from device tree files. Signed-off-by: Álvaro Fernández Rojas Link: https://lore.kernel.org/r/20200615090231.2932696-3-noltari@gmail.com Acked-by: Florian Fainelli Signed-off-by: Stephen Boyd --- include/dt-bindings/clock/bcm6318-clock.h | 42 +++++++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 include/dt-bindings/clock/bcm6318-clock.h diff --git a/include/dt-bindings/clock/bcm6318-clock.h b/include/dt-bindings/clock/bcm6318-clock.h new file mode 100644 index 0000000000000..c4417f8983ab6 --- /dev/null +++ b/include/dt-bindings/clock/bcm6318-clock.h @@ -0,0 +1,42 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ + +#ifndef __DT_BINDINGS_CLOCK_BCM6318_H +#define __DT_BINDINGS_CLOCK_BCM6318_H + +#define BCM6318_CLK_ADSL_ASB 0 +#define BCM6318_CLK_USB_ASB 1 +#define BCM6318_CLK_MIPS_ASB 2 +#define BCM6318_CLK_PCIE_ASB 3 +#define BCM6318_CLK_PHYMIPS_ASB 4 +#define BCM6318_CLK_ROBOSW_ASB 5 +#define BCM6318_CLK_SAR_ASB 6 +#define BCM6318_CLK_SDR_ASB 7 +#define BCM6318_CLK_SWREG_ASB 8 +#define BCM6318_CLK_PERIPH_ASB 9 +#define BCM6318_CLK_CPUBUS160 10 +#define BCM6318_CLK_ADSL 11 +#define BCM6318_CLK_SAR125 12 +#define BCM6318_CLK_MIPS 13 +#define BCM6318_CLK_PCIE 14 +#define BCM6318_CLK_ROBOSW250 16 +#define BCM6318_CLK_ROBOSW025 17 +#define BCM6318_CLK_SDR 19 +#define BCM6318_CLK_USBD 20 +#define BCM6318_CLK_HSSPI 25 +#define BCM6318_CLK_PCIE25 27 +#define BCM6318_CLK_PHYMIPS 28 +#define BCM6318_CLK_AFE 29 +#define BCM6318_CLK_QPROC 30 + +#define BCM6318_UCLK_ADSL 0 +#define BCM6318_UCLK_ARB 1 +#define BCM6318_UCLK_MIPS 2 +#define BCM6318_UCLK_PCIE 3 +#define BCM6318_UCLK_PERIPH 4 +#define BCM6318_UCLK_PHYMIPS 5 +#define BCM6318_UCLK_ROBOSW 6 +#define BCM6318_UCLK_SAR 7 +#define BCM6318_UCLK_SDR 8 +#define BCM6318_UCLK_USB 9 + +#endif /* __DT_BINDINGS_CLOCK_BCM6318_H */ -- GitLab From 92cd8bb27a692d93ba7442ec123e96528f5e992c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81lvaro=20Fern=C3=A1ndez=20Rojas?= Date: Mon, 15 Jun 2020 11:02:26 +0200 Subject: [PATCH 0026/1476] mips: bmips: add BCM6328 clock definitions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add header with BCM6328 definitions in order to be able to include it from device tree files. Signed-off-by: Álvaro Fernández Rojas Link: https://lore.kernel.org/r/20200615090231.2932696-4-noltari@gmail.com Acked-by: Florian Fainelli Signed-off-by: Stephen Boyd --- include/dt-bindings/clock/bcm6328-clock.h | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 include/dt-bindings/clock/bcm6328-clock.h diff --git a/include/dt-bindings/clock/bcm6328-clock.h b/include/dt-bindings/clock/bcm6328-clock.h new file mode 100644 index 0000000000000..1f6a3103f3dcb --- /dev/null +++ b/include/dt-bindings/clock/bcm6328-clock.h @@ -0,0 +1,19 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ + +#ifndef __DT_BINDINGS_CLOCK_BCM6328_H +#define __DT_BINDINGS_CLOCK_BCM6328_H + +#define BCM6328_CLK_PHYMIPS 0 +#define BCM6328_CLK_ADSL_QPROC 1 +#define BCM6328_CLK_ADSL_AFE 2 +#define BCM6328_CLK_ADSL 3 +#define BCM6328_CLK_MIPS 4 +#define BCM6328_CLK_SAR 5 +#define BCM6328_CLK_PCM 6 +#define BCM6328_CLK_USBD 7 +#define BCM6328_CLK_USBH 8 +#define BCM6328_CLK_HSSPI 9 +#define BCM6328_CLK_PCIE 10 +#define BCM6328_CLK_ROBOSW 11 + +#endif /* __DT_BINDINGS_CLOCK_BCM6328_H */ -- GitLab From d3499bda4e176de6853c24e5243f3906d9390d54 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81lvaro=20Fern=C3=A1ndez=20Rojas?= Date: Mon, 15 Jun 2020 11:02:27 +0200 Subject: [PATCH 0027/1476] mips: bmips: add BCM6358 clock definitions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add header with BCM6358 definitions in order to be able to include it from device tree files. Signed-off-by: Álvaro Fernández Rojas Link: https://lore.kernel.org/r/20200615090231.2932696-5-noltari@gmail.com Acked-by: Florian Fainelli Signed-off-by: Stephen Boyd --- include/dt-bindings/clock/bcm6358-clock.h | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 include/dt-bindings/clock/bcm6358-clock.h diff --git a/include/dt-bindings/clock/bcm6358-clock.h b/include/dt-bindings/clock/bcm6358-clock.h new file mode 100644 index 0000000000000..980c9cac47658 --- /dev/null +++ b/include/dt-bindings/clock/bcm6358-clock.h @@ -0,0 +1,18 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ + +#ifndef __DT_BINDINGS_CLOCK_BCM6358_H +#define __DT_BINDINGS_CLOCK_BCM6358_H + +#define BCM6358_CLK_ENET 4 +#define BCM6358_CLK_ADSLPHY 5 +#define BCM6358_CLK_PCM 8 +#define BCM6358_CLK_SPI 9 +#define BCM6358_CLK_USBS 10 +#define BCM6358_CLK_SAR 11 +#define BCM6358_CLK_EMUSB 17 +#define BCM6358_CLK_ENET0 18 +#define BCM6358_CLK_ENET1 19 +#define BCM6358_CLK_USBSU 20 +#define BCM6358_CLK_EPHY 21 + +#endif /* __DT_BINDINGS_CLOCK_BCM6358_H */ -- GitLab From fb8fb3f13f86fda0af72c02691333fdba5164c20 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81lvaro=20Fern=C3=A1ndez=20Rojas?= Date: Mon, 15 Jun 2020 11:02:28 +0200 Subject: [PATCH 0028/1476] mips: bmips: add BCM6362 clock definitions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add header with BCM6362 definitions in order to be able to include it from device tree files. Signed-off-by: Álvaro Fernández Rojas Link: https://lore.kernel.org/r/20200615090231.2932696-6-noltari@gmail.com Acked-by: Florian Fainelli Signed-off-by: Stephen Boyd --- include/dt-bindings/clock/bcm6362-clock.h | 26 +++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 include/dt-bindings/clock/bcm6362-clock.h diff --git a/include/dt-bindings/clock/bcm6362-clock.h b/include/dt-bindings/clock/bcm6362-clock.h new file mode 100644 index 0000000000000..17655cd5bf25a --- /dev/null +++ b/include/dt-bindings/clock/bcm6362-clock.h @@ -0,0 +1,26 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ + +#ifndef __DT_BINDINGS_CLOCK_BCM6362_H +#define __DT_BINDINGS_CLOCK_BCM6362_H + +#define BCM6362_CLK_ADSL_QPROC 1 +#define BCM6362_CLK_ADSL_AFE 2 +#define BCM6362_CLK_ADSL 3 +#define BCM6362_CLK_MIPS 4 +#define BCM6362_CLK_WLAN_OCP 5 +#define BCM6362_CLK_SWPKT_USB 7 +#define BCM6362_CLK_SWPKT_SAR 8 +#define BCM6362_CLK_SAR 9 +#define BCM6362_CLK_ROBOSW 10 +#define BCM6362_CLK_PCM 11 +#define BCM6362_CLK_USBD 12 +#define BCM6362_CLK_USBH 13 +#define BCM6362_CLK_IPSEC 14 +#define BCM6362_CLK_SPI 15 +#define BCM6362_CLK_HSSPI 16 +#define BCM6362_CLK_PCIE 17 +#define BCM6362_CLK_FAP 18 +#define BCM6362_CLK_PHYMIPS 19 +#define BCM6362_CLK_NAND 20 + +#endif /* __DT_BINDINGS_CLOCK_BCM6362_H */ -- GitLab From ad31e793f246d5276bc24829cb3d1ca95c3c92ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81lvaro=20Fern=C3=A1ndez=20Rojas?= Date: Mon, 15 Jun 2020 11:02:29 +0200 Subject: [PATCH 0029/1476] mips: bmips: add BCM6368 clock definitions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add header with BCM6368 definitions in order to be able to include it from device tree files. Signed-off-by: Álvaro Fernández Rojas Link: https://lore.kernel.org/r/20200615090231.2932696-7-noltari@gmail.com Acked-by: Florian Fainelli Signed-off-by: Stephen Boyd --- include/dt-bindings/clock/bcm6368-clock.h | 24 +++++++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 include/dt-bindings/clock/bcm6368-clock.h diff --git a/include/dt-bindings/clock/bcm6368-clock.h b/include/dt-bindings/clock/bcm6368-clock.h new file mode 100644 index 0000000000000..f161d5333883a --- /dev/null +++ b/include/dt-bindings/clock/bcm6368-clock.h @@ -0,0 +1,24 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ + +#ifndef __DT_BINDINGS_CLOCK_BCM6368_H +#define __DT_BINDINGS_CLOCK_BCM6368_H + +#define BCM6368_CLK_VDSL_QPROC 2 +#define BCM6368_CLK_VDSL_AFE 3 +#define BCM6368_CLK_VDSL_BONDING 4 +#define BCM6368_CLK_VDSL 5 +#define BCM6368_CLK_PHYMIPS 6 +#define BCM6368_CLK_SWPKT_USB 7 +#define BCM6368_CLK_SWPKT_SAR 8 +#define BCM6368_CLK_SPI 9 +#define BCM6368_CLK_USBD 10 +#define BCM6368_CLK_SAR 11 +#define BCM6368_CLK_ROBOSW 12 +#define BCM6368_CLK_UTOPIA 13 +#define BCM6368_CLK_PCM 14 +#define BCM6368_CLK_USBH 15 +#define BCM6368_CLK_DIS_GLESS 16 +#define BCM6368_CLK_NAND 17 +#define BCM6368_CLK_IPSEC 18 + +#endif /* __DT_BINDINGS_CLOCK_BCM6368_H */ -- GitLab From f3cd8c96a97ca970a116af092555778f792d0abf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81lvaro=20Fern=C3=A1ndez=20Rojas?= Date: Mon, 15 Jun 2020 11:02:30 +0200 Subject: [PATCH 0030/1476] mips: bmips: add BCM63268 clock definitions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add header with BCM63268 definitions in order to be able to include it from device tree files. Signed-off-by: Álvaro Fernández Rojas Link: https://lore.kernel.org/r/20200615090231.2932696-8-noltari@gmail.com Acked-by: Florian Fainelli Signed-off-by: Stephen Boyd --- include/dt-bindings/clock/bcm63268-clock.h | 30 ++++++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 include/dt-bindings/clock/bcm63268-clock.h diff --git a/include/dt-bindings/clock/bcm63268-clock.h b/include/dt-bindings/clock/bcm63268-clock.h new file mode 100644 index 0000000000000..da23e691d359b --- /dev/null +++ b/include/dt-bindings/clock/bcm63268-clock.h @@ -0,0 +1,30 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ + +#ifndef __DT_BINDINGS_CLOCK_BCM63268_H +#define __DT_BINDINGS_CLOCK_BCM63268_H + +#define BCM63268_CLK_DIS_GLESS 0 +#define BCM63268_CLK_VDSL_QPROC 1 +#define BCM63268_CLK_VDSL_AFE 2 +#define BCM63268_CLK_VDSL 3 +#define BCM63268_CLK_MIPS 4 +#define BCM63268_CLK_WLAN_OCP 5 +#define BCM63268_CLK_DECT 6 +#define BCM63268_CLK_FAP0 7 +#define BCM63268_CLK_FAP1 8 +#define BCM63268_CLK_SAR 9 +#define BCM63268_CLK_ROBOSW 10 +#define BCM63268_CLK_PCM 11 +#define BCM63268_CLK_USBD 12 +#define BCM63268_CLK_USBH 13 +#define BCM63268_CLK_IPSEC 14 +#define BCM63268_CLK_SPI 15 +#define BCM63268_CLK_HSSPI 16 +#define BCM63268_CLK_PCIE 17 +#define BCM63268_CLK_PHYMIPS 18 +#define BCM63268_CLK_GMAC 19 +#define BCM63268_CLK_NAND 20 +#define BCM63268_CLK_TBUS 27 +#define BCM63268_CLK_ROBOSW250 31 + +#endif /* __DT_BINDINGS_CLOCK_BCM63268_H */ -- GitLab From e244d2058dcdff6c7ac21efefed75eba43525140 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81lvaro=20Fern=C3=A1ndez=20Rojas?= Date: Mon, 15 Jun 2020 11:02:31 +0200 Subject: [PATCH 0031/1476] clk: bcm63xx-gate: switch to dt-bindings definitions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Now that there are header files for each SoC, let's use them in the bcm63xx-gate controller driver. Signed-off-by: Álvaro Fernández Rojas Link: https://lore.kernel.org/r/20200615090231.2932696-9-noltari@gmail.com Acked-by: Florian Fainelli Signed-off-by: Stephen Boyd --- drivers/clk/bcm/clk-bcm63xx-gate.c | 580 ++++++++++++++++++++++------- 1 file changed, 439 insertions(+), 141 deletions(-) diff --git a/drivers/clk/bcm/clk-bcm63xx-gate.c b/drivers/clk/bcm/clk-bcm63xx-gate.c index 43853add44cb0..89297c57881e2 100644 --- a/drivers/clk/bcm/clk-bcm63xx-gate.c +++ b/drivers/clk/bcm/clk-bcm63xx-gate.c @@ -6,6 +6,14 @@ #include #include +#include +#include +#include +#include +#include +#include +#include + struct clk_bcm63xx_table_entry { const char * const name; u8 bit; @@ -20,168 +28,458 @@ struct clk_bcm63xx_hw { }; static const struct clk_bcm63xx_table_entry bcm3368_clocks[] = { - { .name = "mac", .bit = 3, }, - { .name = "tc", .bit = 5, }, - { .name = "us_top", .bit = 6, }, - { .name = "ds_top", .bit = 7, }, - { .name = "acm", .bit = 8, }, - { .name = "spi", .bit = 9, }, - { .name = "usbs", .bit = 10, }, - { .name = "bmu", .bit = 11, }, - { .name = "pcm", .bit = 12, }, - { .name = "ntp", .bit = 13, }, - { .name = "acp_b", .bit = 14, }, - { .name = "acp_a", .bit = 15, }, - { .name = "emusb", .bit = 17, }, - { .name = "enet0", .bit = 18, }, - { .name = "enet1", .bit = 19, }, - { .name = "usbsu", .bit = 20, }, - { .name = "ephy", .bit = 21, }, - { }, + { + .name = "mac", + .bit = BCM3368_CLK_MAC, + }, { + .name = "tc", + .bit = BCM3368_CLK_TC, + }, { + .name = "us_top", + .bit = BCM3368_CLK_US_TOP, + }, { + .name = "ds_top", + .bit = BCM3368_CLK_DS_TOP, + }, { + .name = "acm", + .bit = BCM3368_CLK_ACM, + }, { + .name = "spi", + .bit = BCM3368_CLK_SPI, + }, { + .name = "usbs", + .bit = BCM3368_CLK_USBS, + }, { + .name = "bmu", + .bit = BCM3368_CLK_BMU, + }, { + .name = "pcm", + .bit = BCM3368_CLK_PCM, + }, { + .name = "ntp", + .bit = BCM3368_CLK_NTP, + }, { + .name = "acp_b", + .bit = BCM3368_CLK_ACP_B, + }, { + .name = "acp_a", + .bit = BCM3368_CLK_ACP_A, + }, { + .name = "emusb", + .bit = BCM3368_CLK_EMUSB, + }, { + .name = "enet0", + .bit = BCM3368_CLK_ENET0, + }, { + .name = "enet1", + .bit = BCM3368_CLK_ENET1, + }, { + .name = "usbsu", + .bit = BCM3368_CLK_USBSU, + }, { + .name = "ephy", + .bit = BCM3368_CLK_EPHY, + }, { + /* sentinel */ + }, }; static const struct clk_bcm63xx_table_entry bcm6318_clocks[] = { - { .name = "adsl_asb", .bit = 0, }, - { .name = "usb_asb", .bit = 1, }, - { .name = "mips_asb", .bit = 2, }, - { .name = "pcie_asb", .bit = 3, }, - { .name = "phymips_asb", .bit = 4, }, - { .name = "robosw_asb", .bit = 5, }, - { .name = "sar_asb", .bit = 6, }, - { .name = "sdr_asb", .bit = 7, }, - { .name = "swreg_asb", .bit = 8, }, - { .name = "periph_asb", .bit = 9, }, - { .name = "cpubus160", .bit = 10, }, - { .name = "adsl", .bit = 11, }, - { .name = "sar125", .bit = 12, }, - { .name = "mips", .bit = 13, .flags = CLK_IS_CRITICAL, }, - { .name = "pcie", .bit = 14, }, - { .name = "robosw250", .bit = 16, }, - { .name = "robosw025", .bit = 17, }, - { .name = "sdr", .bit = 19, .flags = CLK_IS_CRITICAL, }, - { .name = "usbd", .bit = 20, }, - { .name = "hsspi", .bit = 25, }, - { .name = "pcie25", .bit = 27, }, - { .name = "phymips", .bit = 28, }, - { .name = "afe", .bit = 29, }, - { .name = "qproc", .bit = 30, }, - { }, + { + .name = "adsl_asb", + .bit = BCM6318_CLK_ADSL_ASB, + }, { + .name = "usb_asb", + .bit = BCM6318_CLK_USB_ASB, + }, { + .name = "mips_asb", + .bit = BCM6318_CLK_MIPS_ASB, + }, { + .name = "pcie_asb", + .bit = BCM6318_CLK_PCIE_ASB, + }, { + .name = "phymips_asb", + .bit = BCM6318_CLK_PHYMIPS_ASB, + }, { + .name = "robosw_asb", + .bit = BCM6318_CLK_ROBOSW_ASB, + }, { + .name = "sar_asb", + .bit = BCM6318_CLK_SAR_ASB, + }, { + .name = "sdr_asb", + .bit = BCM6318_CLK_SDR_ASB, + }, { + .name = "swreg_asb", + .bit = BCM6318_CLK_SWREG_ASB, + }, { + .name = "periph_asb", + .bit = BCM6318_CLK_PERIPH_ASB, + }, { + .name = "cpubus160", + .bit = BCM6318_CLK_CPUBUS160, + }, { + .name = "adsl", + .bit = BCM6318_CLK_ADSL, + }, { + .name = "sar125", + .bit = BCM6318_CLK_SAR125, + }, { + .name = "mips", + .bit = BCM6318_CLK_MIPS, + .flags = CLK_IS_CRITICAL, + }, { + .name = "pcie", + .bit = BCM6318_CLK_PCIE, + }, { + .name = "robosw250", + .bit = BCM6318_CLK_ROBOSW250, + }, { + .name = "robosw025", + .bit = BCM6318_CLK_ROBOSW025, + }, { + .name = "sdr", + .bit = BCM6318_CLK_SDR, + .flags = CLK_IS_CRITICAL, + }, { + .name = "usbd", + .bit = BCM6318_CLK_USBD, + }, { + .name = "hsspi", + .bit = BCM6318_CLK_HSSPI, + }, { + .name = "pcie25", + .bit = BCM6318_CLK_PCIE25, + }, { + .name = "phymips", + .bit = BCM6318_CLK_PHYMIPS, + }, { + .name = "afe", + .bit = BCM6318_CLK_AFE, + }, { + .name = "qproc", + .bit = BCM6318_CLK_QPROC, + }, { + /* sentinel */ + }, }; static const struct clk_bcm63xx_table_entry bcm6318_ubus_clocks[] = { - { .name = "adsl-ubus", .bit = 0, }, - { .name = "arb-ubus", .bit = 1, .flags = CLK_IS_CRITICAL, }, - { .name = "mips-ubus", .bit = 2, .flags = CLK_IS_CRITICAL, }, - { .name = "pcie-ubus", .bit = 3, }, - { .name = "periph-ubus", .bit = 4, .flags = CLK_IS_CRITICAL, }, - { .name = "phymips-ubus", .bit = 5, }, - { .name = "robosw-ubus", .bit = 6, }, - { .name = "sar-ubus", .bit = 7, }, - { .name = "sdr-ubus", .bit = 8, }, - { .name = "usb-ubus", .bit = 9, }, - { }, + { + .name = "adsl-ubus", + .bit = BCM6318_UCLK_ADSL, + }, { + .name = "arb-ubus", + .bit = BCM6318_UCLK_ARB, + .flags = CLK_IS_CRITICAL, + }, { + .name = "mips-ubus", + .bit = BCM6318_UCLK_MIPS, + .flags = CLK_IS_CRITICAL, + }, { + .name = "pcie-ubus", + .bit = BCM6318_UCLK_PCIE, + }, { + .name = "periph-ubus", + .bit = BCM6318_UCLK_PERIPH, + .flags = CLK_IS_CRITICAL, + }, { + .name = "phymips-ubus", + .bit = BCM6318_UCLK_PHYMIPS, + }, { + .name = "robosw-ubus", + .bit = BCM6318_UCLK_ROBOSW, + }, { + .name = "sar-ubus", + .bit = BCM6318_UCLK_SAR, + }, { + .name = "sdr-ubus", + .bit = BCM6318_UCLK_SDR, + }, { + .name = "usb-ubus", + .bit = BCM6318_UCLK_USB, + }, { + /* sentinel */ + }, }; static const struct clk_bcm63xx_table_entry bcm6328_clocks[] = { - { .name = "phy_mips", .bit = 0, }, - { .name = "adsl_qproc", .bit = 1, }, - { .name = "adsl_afe", .bit = 2, }, - { .name = "adsl", .bit = 3, }, - { .name = "mips", .bit = 4, .flags = CLK_IS_CRITICAL, }, - { .name = "sar", .bit = 5, }, - { .name = "pcm", .bit = 6, }, - { .name = "usbd", .bit = 7, }, - { .name = "usbh", .bit = 8, }, - { .name = "hsspi", .bit = 9, }, - { .name = "pcie", .bit = 10, }, - { .name = "robosw", .bit = 11, }, - { }, + { + .name = "phy_mips", + .bit = BCM6328_CLK_PHYMIPS, + }, { + .name = "adsl_qproc", + .bit = BCM6328_CLK_ADSL_QPROC, + }, { + .name = "adsl_afe", + .bit = BCM6328_CLK_ADSL_AFE, + }, { + .name = "adsl", + .bit = BCM6328_CLK_ADSL, + }, { + .name = "mips", + .bit = BCM6328_CLK_MIPS, + .flags = CLK_IS_CRITICAL, + }, { + .name = "sar", + .bit = BCM6328_CLK_SAR, + }, { + .name = "pcm", + .bit = BCM6328_CLK_PCM, + }, { + .name = "usbd", + .bit = BCM6328_CLK_USBD, + }, { + .name = "usbh", + .bit = BCM6328_CLK_USBH, + }, { + .name = "hsspi", + .bit = BCM6328_CLK_HSSPI, + }, { + .name = "pcie", + .bit = BCM6328_CLK_PCIE, + }, { + .name = "robosw", + .bit = BCM6328_CLK_ROBOSW, + }, { + /* sentinel */ + }, }; static const struct clk_bcm63xx_table_entry bcm6358_clocks[] = { - { .name = "enet", .bit = 4, }, - { .name = "adslphy", .bit = 5, }, - { .name = "pcm", .bit = 8, }, - { .name = "spi", .bit = 9, }, - { .name = "usbs", .bit = 10, }, - { .name = "sar", .bit = 11, }, - { .name = "emusb", .bit = 17, }, - { .name = "enet0", .bit = 18, }, - { .name = "enet1", .bit = 19, }, - { .name = "usbsu", .bit = 20, }, - { .name = "ephy", .bit = 21, }, - { }, + { + .name = "enet", + .bit = BCM6358_CLK_ENET, + }, { + .name = "adslphy", + .bit = BCM6358_CLK_ADSLPHY, + }, { + .name = "pcm", + .bit = BCM6358_CLK_PCM, + }, { + .name = "spi", + .bit = BCM6358_CLK_SPI, + }, { + .name = "usbs", + .bit = BCM6358_CLK_USBS, + }, { + .name = "sar", + .bit = BCM6358_CLK_SAR, + }, { + .name = "emusb", + .bit = BCM6358_CLK_EMUSB, + }, { + .name = "enet0", + .bit = BCM6358_CLK_ENET0, + }, { + .name = "enet1", + .bit = BCM6358_CLK_ENET1, + }, { + .name = "usbsu", + .bit = BCM6358_CLK_USBSU, + }, { + .name = "ephy", + .bit = BCM6358_CLK_EPHY, + }, { + /* sentinel */ + }, }; static const struct clk_bcm63xx_table_entry bcm6362_clocks[] = { - { .name = "adsl_qproc", .bit = 1, }, - { .name = "adsl_afe", .bit = 2, }, - { .name = "adsl", .bit = 3, }, - { .name = "mips", .bit = 4, .flags = CLK_IS_CRITICAL, }, - { .name = "wlan_ocp", .bit = 5, }, - { .name = "swpkt_usb", .bit = 7, }, - { .name = "swpkt_sar", .bit = 8, }, - { .name = "sar", .bit = 9, }, - { .name = "robosw", .bit = 10, }, - { .name = "pcm", .bit = 11, }, - { .name = "usbd", .bit = 12, }, - { .name = "usbh", .bit = 13, }, - { .name = "ipsec", .bit = 14, }, - { .name = "spi", .bit = 15, }, - { .name = "hsspi", .bit = 16, }, - { .name = "pcie", .bit = 17, }, - { .name = "fap", .bit = 18, }, - { .name = "phymips", .bit = 19, }, - { .name = "nand", .bit = 20, }, - { }, + { + .name = "adsl_qproc", + .bit = BCM6362_CLK_ADSL_QPROC, + }, { + .name = "adsl_afe", + .bit = BCM6362_CLK_ADSL_AFE, + }, { + .name = "adsl", + .bit = BCM6362_CLK_ADSL, + }, { + .name = "mips", + .bit = BCM6362_CLK_MIPS, + .flags = CLK_IS_CRITICAL, + }, { + .name = "wlan_ocp", + .bit = BCM6362_CLK_WLAN_OCP, + }, { + .name = "swpkt_usb", + .bit = BCM6362_CLK_SWPKT_USB, + }, { + .name = "swpkt_sar", + .bit = BCM6362_CLK_SWPKT_SAR, + }, { + .name = "sar", + .bit = BCM6362_CLK_SAR, + }, { + .name = "robosw", + .bit = BCM6362_CLK_ROBOSW, + }, { + .name = "pcm", + .bit = BCM6362_CLK_PCM, + }, { + .name = "usbd", + .bit = BCM6362_CLK_USBD, + }, { + .name = "usbh", + .bit = BCM6362_CLK_USBH, + }, { + .name = "ipsec", + .bit = BCM6362_CLK_IPSEC, + }, { + .name = "spi", + .bit = BCM6362_CLK_SPI, + }, { + .name = "hsspi", + .bit = BCM6362_CLK_HSSPI, + }, { + .name = "pcie", + .bit = BCM6362_CLK_PCIE, + }, { + .name = "fap", + .bit = BCM6362_CLK_FAP, + }, { + .name = "phymips", + .bit = BCM6362_CLK_PHYMIPS, + }, { + .name = "nand", + .bit = BCM6362_CLK_NAND, + }, { + /* sentinel */ + }, }; static const struct clk_bcm63xx_table_entry bcm6368_clocks[] = { - { .name = "vdsl_qproc", .bit = 2, }, - { .name = "vdsl_afe", .bit = 3, }, - { .name = "vdsl_bonding", .bit = 4, }, - { .name = "vdsl", .bit = 5, }, - { .name = "phymips", .bit = 6, }, - { .name = "swpkt_usb", .bit = 7, }, - { .name = "swpkt_sar", .bit = 8, }, - { .name = "spi", .bit = 9, }, - { .name = "usbd", .bit = 10, }, - { .name = "sar", .bit = 11, }, - { .name = "robosw", .bit = 12, }, - { .name = "utopia", .bit = 13, }, - { .name = "pcm", .bit = 14, }, - { .name = "usbh", .bit = 15, }, - { .name = "disable_gless", .bit = 16, }, - { .name = "nand", .bit = 17, }, - { .name = "ipsec", .bit = 18, }, - { }, + { + .name = "vdsl_qproc", + .bit = BCM6368_CLK_VDSL_QPROC, + }, { + .name = "vdsl_afe", + .bit = BCM6368_CLK_VDSL_AFE, + }, { + .name = "vdsl_bonding", + .bit = BCM6368_CLK_VDSL_BONDING, + }, { + .name = "vdsl", + .bit = BCM6368_CLK_VDSL, + }, { + .name = "phymips", + .bit = BCM6368_CLK_PHYMIPS, + }, { + .name = "swpkt_usb", + .bit = BCM6368_CLK_SWPKT_USB, + }, { + .name = "swpkt_sar", + .bit = BCM6368_CLK_SWPKT_SAR, + }, { + .name = "spi", + .bit = BCM6368_CLK_SPI, + }, { + .name = "usbd", + .bit = BCM6368_CLK_USBD, + }, { + .name = "sar", + .bit = BCM6368_CLK_SAR, + }, { + .name = "robosw", + .bit = BCM6368_CLK_ROBOSW, + }, { + .name = "utopia", + .bit = BCM6368_CLK_UTOPIA, + }, { + .name = "pcm", + .bit = BCM6368_CLK_PCM, + }, { + .name = "usbh", + .bit = BCM6368_CLK_USBH, + }, { + .name = "disable_gless", + .bit = BCM6368_CLK_DIS_GLESS, + }, { + .name = "nand", + .bit = BCM6368_CLK_NAND, + }, { + .name = "ipsec", + .bit = BCM6368_CLK_IPSEC, + }, { + /* sentinel */ + }, }; static const struct clk_bcm63xx_table_entry bcm63268_clocks[] = { - { .name = "disable_gless", .bit = 0, }, - { .name = "vdsl_qproc", .bit = 1, }, - { .name = "vdsl_afe", .bit = 2, }, - { .name = "vdsl", .bit = 3, }, - { .name = "mips", .bit = 4, .flags = CLK_IS_CRITICAL, }, - { .name = "wlan_ocp", .bit = 5, }, - { .name = "dect", .bit = 6, }, - { .name = "fap0", .bit = 7, }, - { .name = "fap1", .bit = 8, }, - { .name = "sar", .bit = 9, }, - { .name = "robosw", .bit = 10, }, - { .name = "pcm", .bit = 11, }, - { .name = "usbd", .bit = 12, }, - { .name = "usbh", .bit = 13, }, - { .name = "ipsec", .bit = 14, }, - { .name = "spi", .bit = 15, }, - { .name = "hsspi", .bit = 16, }, - { .name = "pcie", .bit = 17, }, - { .name = "phymips", .bit = 18, }, - { .name = "gmac", .bit = 19, }, - { .name = "nand", .bit = 20, }, - { .name = "tbus", .bit = 27, }, - { .name = "robosw250", .bit = 31, }, - { }, + { + .name = "disable_gless", + .bit = BCM63268_CLK_DIS_GLESS, + }, { + .name = "vdsl_qproc", + .bit = BCM63268_CLK_VDSL_QPROC, + }, { + .name = "vdsl_afe", + .bit = BCM63268_CLK_VDSL_AFE, + }, { + .name = "vdsl", + .bit = BCM63268_CLK_VDSL, + }, { + .name = "mips", + .bit = BCM63268_CLK_MIPS, + .flags = CLK_IS_CRITICAL, + }, { + .name = "wlan_ocp", + .bit = BCM63268_CLK_WLAN_OCP, + }, { + .name = "dect", + .bit = BCM63268_CLK_DECT, + }, { + .name = "fap0", + .bit = BCM63268_CLK_FAP0, + }, { + .name = "fap1", + .bit = BCM63268_CLK_FAP1, + }, { + .name = "sar", + .bit = BCM63268_CLK_SAR, + }, { + .name = "robosw", + .bit = BCM63268_CLK_ROBOSW, + }, { + .name = "pcm", + .bit = BCM63268_CLK_PCM, + }, { + .name = "usbd", + .bit = BCM63268_CLK_USBD, + }, { + .name = "usbh", + .bit = BCM63268_CLK_USBH, + }, { + .name = "ipsec", + .bit = BCM63268_CLK_IPSEC, + }, { + .name = "spi", + .bit = BCM63268_CLK_SPI, + }, { + .name = "hsspi", + .bit = BCM63268_CLK_HSSPI, + }, { + .name = "pcie", + .bit = BCM63268_CLK_PCIE, + }, { + .name = "phymips", + .bit = BCM63268_CLK_PHYMIPS, + }, { + .name = "gmac", + .bit = BCM63268_CLK_GMAC, + }, { + .name = "nand", + .bit = BCM63268_CLK_NAND, + }, { + .name = "tbus", + .bit = BCM63268_CLK_TBUS, + }, { + .name = "robosw250", + .bit = BCM63268_CLK_ROBOSW250, + }, { + /* sentinel */ + }, }; static int clk_bcm63xx_probe(struct platform_device *pdev) -- GitLab From 8dda00049093786c9ff8cf9474b96aa60ed961cd Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Thu, 11 Jun 2020 11:23:15 +0200 Subject: [PATCH 0032/1476] dt-bindings: clock: Add BCM2711 DVP binding The BCM2711 has a unit controlling the HDMI0 and HDMI1 clock and reset signals. Let's add a binding for it. Cc: Philipp Zabel Cc: Rob Herring Cc: devicetree@vger.kernel.org Reviewed-by: Rob Herring Signed-off-by: Maxime Ripard Link: https://lore.kernel.org/r/0b8f09baff1ff3c471631e6f523e2b2cd773ec47.1591867332.git-series.maxime@cerno.tech Acked-by: Stefan Wahren Signed-off-by: Stephen Boyd --- .../bindings/clock/brcm,bcm2711-dvp.yaml | 47 +++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100644 Documentation/devicetree/bindings/clock/brcm,bcm2711-dvp.yaml diff --git a/Documentation/devicetree/bindings/clock/brcm,bcm2711-dvp.yaml b/Documentation/devicetree/bindings/clock/brcm,bcm2711-dvp.yaml new file mode 100644 index 0000000000000..08543ecbe35b2 --- /dev/null +++ b/Documentation/devicetree/bindings/clock/brcm,bcm2711-dvp.yaml @@ -0,0 +1,47 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/clock/brcm,bcm2711-dvp.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Broadcom BCM2711 HDMI DVP Device Tree Bindings + +maintainers: + - Maxime Ripard + +properties: + "#clock-cells": + const: 1 + + "#reset-cells": + const: 1 + + compatible: + const: brcm,brcm2711-dvp + + reg: + maxItems: 1 + + clocks: + maxItems: 1 + +required: + - "#clock-cells" + - "#reset-cells" + - compatible + - reg + - clocks + +additionalProperties: false + +examples: + - | + dvp: clock@7ef00000 { + compatible = "brcm,brcm2711-dvp"; + reg = <0x7ef00000 0x10>; + clocks = <&clk_108MHz>; + #clock-cells = <1>; + #reset-cells = <1>; + }; + +... -- GitLab From 1bc95972715ab81fd3fa9f5b45ace5bb607af1b5 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Thu, 11 Jun 2020 11:23:16 +0200 Subject: [PATCH 0033/1476] clk: bcm: Add BCM2711 DVP driver The HDMI block has a block that controls clocks and reset signals to the HDMI0 and HDMI1 controllers. Let's expose that through a clock driver implementing a clock and reset provider. Cc: Michael Turquette Cc: Stephen Boyd Cc: Rob Herring Cc: linux-clk@vger.kernel.org Cc: devicetree@vger.kernel.org Reviewed-by: Stephen Boyd Signed-off-by: Maxime Ripard Link: https://lore.kernel.org/r/bb60d97fc76b61c2eabef5a02ebd664c0f57ede0.1591867332.git-series.maxime@cerno.tech Acked-by: Stefan Wahren Reviewed-by: Nicolas Saenz Julienne Signed-off-by: Stephen Boyd --- drivers/clk/bcm/Kconfig | 11 +++ drivers/clk/bcm/Makefile | 1 + drivers/clk/bcm/clk-bcm2711-dvp.c | 120 ++++++++++++++++++++++++++++++ 3 files changed, 132 insertions(+) create mode 100644 drivers/clk/bcm/clk-bcm2711-dvp.c diff --git a/drivers/clk/bcm/Kconfig b/drivers/clk/bcm/Kconfig index 8c83977a7dc42..784f12c72365b 100644 --- a/drivers/clk/bcm/Kconfig +++ b/drivers/clk/bcm/Kconfig @@ -1,4 +1,15 @@ # SPDX-License-Identifier: GPL-2.0-only + +config CLK_BCM2711_DVP + tristate "Broadcom BCM2711 DVP support" + depends on ARCH_BCM2835 ||COMPILE_TEST + depends on COMMON_CLK + default ARCH_BCM2835 + select RESET_SIMPLE + help + Enable common clock framework support for the Broadcom BCM2711 + DVP Controller. + config CLK_BCM2835 bool "Broadcom BCM2835 clock support" depends on ARCH_BCM2835 || ARCH_BRCMSTB || COMPILE_TEST diff --git a/drivers/clk/bcm/Makefile b/drivers/clk/bcm/Makefile index 0070ddf6cdd24..edb66b44cb273 100644 --- a/drivers/clk/bcm/Makefile +++ b/drivers/clk/bcm/Makefile @@ -6,6 +6,7 @@ obj-$(CONFIG_CLK_BCM_KONA) += clk-kona-setup.o obj-$(CONFIG_CLK_BCM_KONA) += clk-bcm281xx.o obj-$(CONFIG_CLK_BCM_KONA) += clk-bcm21664.o obj-$(CONFIG_COMMON_CLK_IPROC) += clk-iproc-armpll.o clk-iproc-pll.o clk-iproc-asiu.o +obj-$(CONFIG_CLK_BCM2711_DVP) += clk-bcm2711-dvp.o obj-$(CONFIG_CLK_BCM2835) += clk-bcm2835.o obj-$(CONFIG_CLK_BCM2835) += clk-bcm2835-aux.o obj-$(CONFIG_CLK_RASPBERRYPI) += clk-raspberrypi.o diff --git a/drivers/clk/bcm/clk-bcm2711-dvp.c b/drivers/clk/bcm/clk-bcm2711-dvp.c new file mode 100644 index 0000000000000..84dbc886e3034 --- /dev/null +++ b/drivers/clk/bcm/clk-bcm2711-dvp.c @@ -0,0 +1,120 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +// Copyright 2020 Cerno + +#include +#include +#include +#include +#include + +#define DVP_HT_RPI_SW_INIT 0x04 +#define DVP_HT_RPI_MISC_CONFIG 0x08 + +#define NR_CLOCKS 2 +#define NR_RESETS 6 + +struct clk_dvp { + struct clk_hw_onecell_data *data; + struct reset_simple_data reset; +}; + +static const struct clk_parent_data clk_dvp_parent = { + .index = 0, +}; + +static int clk_dvp_probe(struct platform_device *pdev) +{ + struct clk_hw_onecell_data *data; + struct resource *res; + struct clk_dvp *dvp; + void __iomem *base; + int ret; + + dvp = devm_kzalloc(&pdev->dev, sizeof(*dvp), GFP_KERNEL); + if (!dvp) + return -ENOMEM; + platform_set_drvdata(pdev, dvp); + + dvp->data = devm_kzalloc(&pdev->dev, + struct_size(dvp->data, hws, NR_CLOCKS), + GFP_KERNEL); + if (!dvp->data) + return -ENOMEM; + data = dvp->data; + + base = devm_platform_get_and_ioremap_resource(pdev, 0, &res); + if (IS_ERR(base)) + return PTR_ERR(base); + + dvp->reset.rcdev.owner = THIS_MODULE; + dvp->reset.rcdev.nr_resets = NR_RESETS; + dvp->reset.rcdev.ops = &reset_simple_ops; + dvp->reset.rcdev.of_node = pdev->dev.of_node; + dvp->reset.membase = base + DVP_HT_RPI_SW_INIT; + spin_lock_init(&dvp->reset.lock); + + ret = devm_reset_controller_register(&pdev->dev, &dvp->reset.rcdev); + if (ret) + return ret; + + data->hws[0] = clk_hw_register_gate_parent_data(&pdev->dev, + "hdmi0-108MHz", + &clk_dvp_parent, 0, + base + DVP_HT_RPI_MISC_CONFIG, 3, + CLK_GATE_SET_TO_DISABLE, + &dvp->reset.lock); + if (IS_ERR(data->hws[0])) + return PTR_ERR(data->hws[0]); + + data->hws[1] = clk_hw_register_gate_parent_data(&pdev->dev, + "hdmi1-108MHz", + &clk_dvp_parent, 0, + base + DVP_HT_RPI_MISC_CONFIG, 4, + CLK_GATE_SET_TO_DISABLE, + &dvp->reset.lock); + if (IS_ERR(data->hws[1])) { + ret = PTR_ERR(data->hws[1]); + goto unregister_clk0; + } + + data->num = NR_CLOCKS; + ret = of_clk_add_hw_provider(pdev->dev.of_node, of_clk_hw_onecell_get, + data); + if (ret) + goto unregister_clk1; + + return 0; + +unregister_clk1: + clk_hw_unregister_gate(data->hws[1]); + +unregister_clk0: + clk_hw_unregister_gate(data->hws[0]); + return ret; +}; + +static int clk_dvp_remove(struct platform_device *pdev) +{ + struct clk_dvp *dvp = platform_get_drvdata(pdev); + struct clk_hw_onecell_data *data = dvp->data; + + clk_hw_unregister_gate(data->hws[1]); + clk_hw_unregister_gate(data->hws[0]); + + return 0; +} + +static const struct of_device_id clk_dvp_dt_ids[] = { + { .compatible = "brcm,brcm2711-dvp", }, + { /* sentinel */ } +}; + +static struct platform_driver clk_dvp_driver = { + .probe = clk_dvp_probe, + .remove = clk_dvp_remove, + .driver = { + .name = "brcm2711-dvp", + .of_match_table = clk_dvp_dt_ids, + }, +}; +module_platform_driver(clk_dvp_driver); -- GitLab From d4c708c032df3d95de297232e20041737e99d126 Mon Sep 17 00:00:00 2001 From: Florian Fainelli Date: Mon, 15 Jun 2020 10:40:41 +0200 Subject: [PATCH 0034/1476] dt-bindings: arm: bcm: Convert BCM2835 firmware binding to YAML Convert the Raspberry Pi BCM2835 firmware binding document to YAML. Verified with dt_binding_check and dtbs_check. Signed-off-by: Florian Fainelli Tested-by: Nicolas Saenz Julienne Signed-off-by: Maxime Ripard Link: https://lore.kernel.org/r/5bc0b9be8544b07300fccab4d4f26e5e5d8e62b2.1592210452.git-series.maxime@cerno.tech Signed-off-by: Stephen Boyd --- .../arm/bcm/raspberrypi,bcm2835-firmware.txt | 14 -------- .../arm/bcm/raspberrypi,bcm2835-firmware.yaml | 35 +++++++++++++++++++ 2 files changed, 35 insertions(+), 14 deletions(-) delete mode 100644 Documentation/devicetree/bindings/arm/bcm/raspberrypi,bcm2835-firmware.txt create mode 100644 Documentation/devicetree/bindings/arm/bcm/raspberrypi,bcm2835-firmware.yaml diff --git a/Documentation/devicetree/bindings/arm/bcm/raspberrypi,bcm2835-firmware.txt b/Documentation/devicetree/bindings/arm/bcm/raspberrypi,bcm2835-firmware.txt deleted file mode 100644 index 6824b3180ffb7..0000000000000 --- a/Documentation/devicetree/bindings/arm/bcm/raspberrypi,bcm2835-firmware.txt +++ /dev/null @@ -1,14 +0,0 @@ -Raspberry Pi VideoCore firmware driver - -Required properties: - -- compatible: Should be "raspberrypi,bcm2835-firmware" -- mboxes: Phandle to the firmware device's Mailbox. - (See: ../mailbox/mailbox.txt for more information) - -Example: - -firmware { - compatible = "raspberrypi,bcm2835-firmware"; - mboxes = <&mailbox>; -}; diff --git a/Documentation/devicetree/bindings/arm/bcm/raspberrypi,bcm2835-firmware.yaml b/Documentation/devicetree/bindings/arm/bcm/raspberrypi,bcm2835-firmware.yaml new file mode 100644 index 0000000000000..cec540c052b6a --- /dev/null +++ b/Documentation/devicetree/bindings/arm/bcm/raspberrypi,bcm2835-firmware.yaml @@ -0,0 +1,35 @@ +# SPDX-License-Identifier: GPL-2.0 +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/arm/bcm/raspberrypi,bcm2835-firmware.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Raspberry Pi VideoCore firmware driver + +maintainers: + - Eric Anholt + - Stefan Wahren + +properties: + compatible: + items: + - const: raspberrypi,bcm2835-firmware + - const: simple-bus + + mboxes: + $ref: '/schemas/types.yaml#/definitions/phandle' + description: | + Phandle to the firmware device's Mailbox. + (See: ../mailbox/mailbox.txt for more information) + +required: + - compatible + - mboxes + +examples: + - | + firmware { + compatible = "raspberrypi,bcm2835-firmware", "simple-bus"; + mboxes = <&mailbox>; + }; +... -- GitLab From 74a0caa5ef1c25905c4409b350ed9a8e0d9a3b74 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Mon, 15 Jun 2020 10:40:42 +0200 Subject: [PATCH 0035/1476] dt-bindings: clock: Add a binding for the RPi Firmware clocks The firmware running on the RPi VideoCore can be used to discover and change the various clocks running in the BCM2711. Since devices will need to use them through the DT, let's add a pretty simple binding. Cc: Michael Turquette Cc: linux-clk@vger.kernel.org Cc: devicetree@vger.kernel.org Reviewed-by: Stephen Boyd Reviewed-by: Rob Herring Tested-by: Nicolas Saenz Julienne Signed-off-by: Maxime Ripard Link: https://lore.kernel.org/r/f6105207e7ef5a5ea8d7a1774faf989d341a25f5.1592210452.git-series.maxime@cerno.tech Signed-off-by: Stephen Boyd --- .../arm/bcm/raspberrypi,bcm2835-firmware.yaml | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/Documentation/devicetree/bindings/arm/bcm/raspberrypi,bcm2835-firmware.yaml b/Documentation/devicetree/bindings/arm/bcm/raspberrypi,bcm2835-firmware.yaml index cec540c052b6a..b48ed875eb8e4 100644 --- a/Documentation/devicetree/bindings/arm/bcm/raspberrypi,bcm2835-firmware.yaml +++ b/Documentation/devicetree/bindings/arm/bcm/raspberrypi,bcm2835-firmware.yaml @@ -22,6 +22,25 @@ properties: Phandle to the firmware device's Mailbox. (See: ../mailbox/mailbox.txt for more information) + clocks: + type: object + + properties: + compatible: + const: raspberrypi,firmware-clocks + + "#clock-cells": + const: 1 + description: > + The argument is the ID of the clocks contained by the + firmware messages. + + required: + - compatible + - "#clock-cells" + + additionalProperties: false + required: - compatible - mboxes @@ -31,5 +50,10 @@ examples: firmware { compatible = "raspberrypi,bcm2835-firmware", "simple-bus"; mboxes = <&mailbox>; + + firmware_clocks: clocks { + compatible = "raspberrypi,firmware-clocks"; + #clock-cells = <1>; + }; }; ... -- GitLab From 511aba099cd4c44cb8ad4ca7e2d6ffc034eed743 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Mon, 15 Jun 2020 10:40:43 +0200 Subject: [PATCH 0036/1476] firmware: rpi: Only create clocks device if we don't have a node for it The firmware clocks driver was previously probed through a platform_device created by the firmware driver. Since we will now have a node for that clocks driver, we need to create the device only in the case where there's no node for it already. Reviewed-by: Nicolas Saenz Julienne Tested-by: Nicolas Saenz Julienne Signed-off-by: Maxime Ripard Link: https://lore.kernel.org/r/72114c4287ebda2dbd952ea238d4489d359897e5.1592210452.git-series.maxime@cerno.tech Signed-off-by: Stephen Boyd --- drivers/firmware/raspberrypi.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/drivers/firmware/raspberrypi.c b/drivers/firmware/raspberrypi.c index ef8098856a47d..b25901a77c091 100644 --- a/drivers/firmware/raspberrypi.c +++ b/drivers/firmware/raspberrypi.c @@ -208,6 +208,20 @@ rpi_register_hwmon_driver(struct device *dev, struct rpi_firmware *fw) static void rpi_register_clk_driver(struct device *dev) { + struct device_node *firmware; + + /* + * Earlier DTs don't have a node for the firmware clocks but + * rely on us creating a platform device by hand. If we do + * have a node for the firmware clocks, just bail out here. + */ + firmware = of_get_compatible_child(dev->of_node, + "raspberrypi,firmware-clocks"); + if (firmware) { + of_node_put(firmware); + return; + } + rpi_clk = platform_device_register_data(dev, "raspberrypi-clk", -1, NULL, 0); } -- GitLab From fbac2e7787ac6fd2226a5935c80c0ff80d607bce Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Mon, 15 Jun 2020 10:40:44 +0200 Subject: [PATCH 0037/1476] clk: bcm: rpi: Allow the driver to be probed by DT The current firmware clock driver for the RaspberryPi can only be probed by manually registering an associated platform_device. While this works fine for cpufreq where the device gets attached a clkdev lookup, it would be tedious to maintain a table of all the devices using one of the clocks exposed by the firmware. Since the DT on the other hand is the perfect place to store those associations, make the firmware clocks driver probe-able through the device tree so that we can represent it as a node. Cc: Michael Turquette Cc: linux-clk@vger.kernel.org Reviewed-by: Nicolas Saenz Julienne Reviewed-by: Stephen Boyd Tested-by: Nicolas Saenz Julienne Signed-off-by: Maxime Ripard Link: https://lore.kernel.org/r/cb8203b862e386ac6c3df3eff0bb5a238b6ec97a.1592210452.git-series.maxime@cerno.tech Signed-off-by: Stephen Boyd --- drivers/clk/bcm/clk-raspberrypi.c | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/drivers/clk/bcm/clk-raspberrypi.c b/drivers/clk/bcm/clk-raspberrypi.c index 1654fd0eedc94..8610355bda477 100644 --- a/drivers/clk/bcm/clk-raspberrypi.c +++ b/drivers/clk/bcm/clk-raspberrypi.c @@ -255,8 +255,16 @@ static int raspberrypi_clk_probe(struct platform_device *pdev) struct raspberrypi_clk *rpi; int ret; - firmware_node = of_find_compatible_node(NULL, NULL, - "raspberrypi,bcm2835-firmware"); + /* + * We can be probed either through the an old-fashioned + * platform device registration or through a DT node that is a + * child of the firmware node. Handle both cases. + */ + if (dev->of_node) + firmware_node = of_get_parent(dev->of_node); + else + firmware_node = of_find_compatible_node(NULL, NULL, + "raspberrypi,bcm2835-firmware"); if (!firmware_node) { dev_err(dev, "Missing firmware node\n"); return -ENOENT; @@ -300,9 +308,16 @@ static int raspberrypi_clk_remove(struct platform_device *pdev) return 0; } +static const struct of_device_id raspberrypi_clk_match[] = { + { .compatible = "raspberrypi,firmware-clocks" }, + { }, +}; +MODULE_DEVICE_TABLE(of, raspberrypi_clk_match); + static struct platform_driver raspberrypi_clk_driver = { .driver = { .name = "raspberrypi-clk", + .of_match_table = raspberrypi_clk_match, }, .probe = raspberrypi_clk_probe, .remove = raspberrypi_clk_remove, -- GitLab From 869bd27541fe4eb1468dd4f0ddf76af6dd9b35ac Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Mon, 15 Jun 2020 10:40:45 +0200 Subject: [PATCH 0038/1476] clk: bcm: rpi: Statically init clk_init_data Instead of declaring the clk_init_data and then calling memset on it, just initialise properly. Cc: Michael Turquette Cc: Stephen Boyd Cc: linux-clk@vger.kernel.org Acked-by: Nicolas Saenz Julienne Reviewed-by: Stephen Boyd Tested-by: Nicolas Saenz Julienne Signed-off-by: Maxime Ripard Link: https://lore.kernel.org/r/0342572daa561dc1bb4c9fd10641b2016493e32b.1592210452.git-series.maxime@cerno.tech Signed-off-by: Stephen Boyd --- drivers/clk/bcm/clk-raspberrypi.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/clk/bcm/clk-raspberrypi.c b/drivers/clk/bcm/clk-raspberrypi.c index 8610355bda477..ddc72207212e9 100644 --- a/drivers/clk/bcm/clk-raspberrypi.c +++ b/drivers/clk/bcm/clk-raspberrypi.c @@ -175,11 +175,10 @@ static const struct clk_ops raspberrypi_firmware_pll_clk_ops = { static int raspberrypi_register_pllb(struct raspberrypi_clk *rpi) { + struct clk_init_data init = {}; u32 min_rate = 0, max_rate = 0; - struct clk_init_data init; int ret; - memset(&init, 0, sizeof(init)); /* All of the PLLs derive from the external oscillator. */ init.parent_names = (const char *[]){ "osc" }; -- GitLab From 72856a4ed555892c0c793becebba93fc43c50856 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Mon, 15 Jun 2020 10:40:46 +0200 Subject: [PATCH 0039/1476] clk: bcm: rpi: Use clk_hw_register for pllb_arm The pllb_arm clock is defined as a fixed factor clock with the pllb clock as a parent. However, all its configuration is entirely static, and thus we don't really need to call clk_hw_register_fixed_factor() but can simply call clk_hw_register() with a static clk_fixed_factor structure. Cc: Michael Turquette Cc: linux-clk@vger.kernel.org Acked-by: Nicolas Saenz Julienne Reviewed-by: Stephen Boyd Tested-by: Nicolas Saenz Julienne Signed-off-by: Maxime Ripard Link: https://lore.kernel.org/r/1146177664999eeda65856d28ce94025021dd85e.1592210452.git-series.maxime@cerno.tech Signed-off-by: Stephen Boyd --- drivers/clk/bcm/clk-raspberrypi.c | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/drivers/clk/bcm/clk-raspberrypi.c b/drivers/clk/bcm/clk-raspberrypi.c index ddc72207212e9..5f0d4875e1457 100644 --- a/drivers/clk/bcm/clk-raspberrypi.c +++ b/drivers/clk/bcm/clk-raspberrypi.c @@ -225,16 +225,28 @@ static int raspberrypi_register_pllb(struct raspberrypi_clk *rpi) return devm_clk_hw_register(rpi->dev, &rpi->pllb); } +static struct clk_fixed_factor raspberrypi_clk_pllb_arm = { + .mult = 1, + .div = 2, + .hw.init = &(struct clk_init_data) { + .name = "pllb_arm", + .parent_names = (const char *[]){ "pllb" }, + .num_parents = 1, + .ops = &clk_fixed_factor_ops, + .flags = CLK_SET_RATE_PARENT | CLK_GET_RATE_NOCACHE, + }, +}; + static int raspberrypi_register_pllb_arm(struct raspberrypi_clk *rpi) { - rpi->pllb_arm = clk_hw_register_fixed_factor(rpi->dev, - "pllb_arm", "pllb", - CLK_SET_RATE_PARENT | CLK_GET_RATE_NOCACHE, - 1, 2); - if (IS_ERR(rpi->pllb_arm)) { + int ret; + + ret = clk_hw_register(rpi->dev, &raspberrypi_clk_pllb_arm.hw); + if (ret) { dev_err(rpi->dev, "Failed to initialize pllb_arm\n"); - return PTR_ERR(rpi->pllb_arm); + return ret; } + rpi->pllb_arm = &raspberrypi_clk_pllb_arm.hw; rpi->pllb_arm_lookup = clkdev_hw_create(rpi->pllb_arm, NULL, "cpu0"); if (!rpi->pllb_arm_lookup) { -- GitLab From c70011a985001e4dec4ce10154b9ff0f00f4140f Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Mon, 15 Jun 2020 10:40:47 +0200 Subject: [PATCH 0040/1476] clk: bcm: rpi: Remove global pllb_arm clock pointer The pllb_arm clk_hw pointer in the raspberry_clk structure isn't used anywhere but in the raspberrypi_register_pllb_arm. Let's remove it, this will make our lives easier in future patches. Cc: Michael Turquette Cc: Stephen Boyd Cc: linux-clk@vger.kernel.org Acked-by: Nicolas Saenz Julienne Reviewed-by: Stephen Boyd Tested-by: Nicolas Saenz Julienne Signed-off-by: Maxime Ripard Link: https://lore.kernel.org/r/842859cf1a77478620f45049178a588448202858.1592210452.git-series.maxime@cerno.tech Signed-off-by: Stephen Boyd --- drivers/clk/bcm/clk-raspberrypi.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/drivers/clk/bcm/clk-raspberrypi.c b/drivers/clk/bcm/clk-raspberrypi.c index 5f0d4875e1457..b21dd6ddc4fe8 100644 --- a/drivers/clk/bcm/clk-raspberrypi.c +++ b/drivers/clk/bcm/clk-raspberrypi.c @@ -40,7 +40,6 @@ struct raspberrypi_clk { unsigned long max_rate; struct clk_hw pllb; - struct clk_hw *pllb_arm; struct clk_lookup *pllb_arm_lookup; }; @@ -246,12 +245,12 @@ static int raspberrypi_register_pllb_arm(struct raspberrypi_clk *rpi) dev_err(rpi->dev, "Failed to initialize pllb_arm\n"); return ret; } - rpi->pllb_arm = &raspberrypi_clk_pllb_arm.hw; - rpi->pllb_arm_lookup = clkdev_hw_create(rpi->pllb_arm, NULL, "cpu0"); + rpi->pllb_arm_lookup = clkdev_hw_create(&raspberrypi_clk_pllb_arm.hw, + NULL, "cpu0"); if (!rpi->pllb_arm_lookup) { dev_err(rpi->dev, "Failed to initialize pllb_arm_lookup\n"); - clk_hw_unregister_fixed_factor(rpi->pllb_arm); + clk_hw_unregister_fixed_factor(&raspberrypi_clk_pllb_arm.hw); return -ENOMEM; } -- GitLab From 683de1868164ec8d252ec9261a5dfd0f9edd7f52 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Mon, 15 Jun 2020 10:40:48 +0200 Subject: [PATCH 0041/1476] clk: bcm: rpi: Make sure pllb_arm is removed The pllb_arm clock was created at probe time, but was never removed if something went wrong later in probe, or if the driver was ever removed from the system. Now that we are using clk_hw_register(), we can just use its managed variant to take care of that for us. Cc: Michael Turquette Cc: Stephen Boyd Cc: linux-clk@vger.kernel.org Acked-by: Nicolas Saenz Julienne Reviewed-by: Stephen Boyd Tested-by: Nicolas Saenz Julienne Signed-off-by: Maxime Ripard Link: https://lore.kernel.org/r/34254ed1556614658e5dad5cca4cf4fe617df7fc.1592210452.git-series.maxime@cerno.tech Signed-off-by: Stephen Boyd --- drivers/clk/bcm/clk-raspberrypi.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/clk/bcm/clk-raspberrypi.c b/drivers/clk/bcm/clk-raspberrypi.c index b21dd6ddc4fe8..d62605861028c 100644 --- a/drivers/clk/bcm/clk-raspberrypi.c +++ b/drivers/clk/bcm/clk-raspberrypi.c @@ -240,7 +240,7 @@ static int raspberrypi_register_pllb_arm(struct raspberrypi_clk *rpi) { int ret; - ret = clk_hw_register(rpi->dev, &raspberrypi_clk_pllb_arm.hw); + ret = devm_clk_hw_register(rpi->dev, &raspberrypi_clk_pllb_arm.hw); if (ret) { dev_err(rpi->dev, "Failed to initialize pllb_arm\n"); return ret; @@ -250,7 +250,6 @@ static int raspberrypi_register_pllb_arm(struct raspberrypi_clk *rpi) NULL, "cpu0"); if (!rpi->pllb_arm_lookup) { dev_err(rpi->dev, "Failed to initialize pllb_arm_lookup\n"); - clk_hw_unregister_fixed_factor(&raspberrypi_clk_pllb_arm.hw); return -ENOMEM; } -- GitLab From 8f60112f66bf3a17ef3206e716064aa76011b94d Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Mon, 15 Jun 2020 10:40:49 +0200 Subject: [PATCH 0042/1476] clk: bcm: rpi: Remove pllb_arm_lookup global pointer The pllb_arm_lookup pointer in the struct raspberrypi_clk is not used for anything but to store the returned pointer to clkdev_hw_create, and is not used anywhere else in the driver. Let's remove that global pointer from the structure. Cc: Michael Turquette Cc: linux-clk@vger.kernel.org Acked-by: Nicolas Saenz Julienne Reviewed-by: Stephen Boyd Tested-by: Nicolas Saenz Julienne Signed-off-by: Maxime Ripard Link: https://lore.kernel.org/r/189407f54906d2b07c91de7a4eeb6d8c8934280f.1592210452.git-series.maxime@cerno.tech Signed-off-by: Stephen Boyd --- drivers/clk/bcm/clk-raspberrypi.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/clk/bcm/clk-raspberrypi.c b/drivers/clk/bcm/clk-raspberrypi.c index d62605861028c..5a06c4991c7f9 100644 --- a/drivers/clk/bcm/clk-raspberrypi.c +++ b/drivers/clk/bcm/clk-raspberrypi.c @@ -40,7 +40,6 @@ struct raspberrypi_clk { unsigned long max_rate; struct clk_hw pllb; - struct clk_lookup *pllb_arm_lookup; }; /* @@ -238,6 +237,7 @@ static struct clk_fixed_factor raspberrypi_clk_pllb_arm = { static int raspberrypi_register_pllb_arm(struct raspberrypi_clk *rpi) { + struct clk_lookup *pllb_arm_lookup; int ret; ret = devm_clk_hw_register(rpi->dev, &raspberrypi_clk_pllb_arm.hw); @@ -246,9 +246,9 @@ static int raspberrypi_register_pllb_arm(struct raspberrypi_clk *rpi) return ret; } - rpi->pllb_arm_lookup = clkdev_hw_create(&raspberrypi_clk_pllb_arm.hw, - NULL, "cpu0"); - if (!rpi->pllb_arm_lookup) { + pllb_arm_lookup = clkdev_hw_create(&raspberrypi_clk_pllb_arm.hw, + NULL, "cpu0"); + if (!pllb_arm_lookup) { dev_err(rpi->dev, "Failed to initialize pllb_arm_lookup\n"); return -ENOMEM; } -- GitLab From 55ee6a992dfe9c477da629b1ddf33a0e1bbd83bc Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Mon, 15 Jun 2020 10:40:50 +0200 Subject: [PATCH 0043/1476] clk: bcm: rpi: Switch to clk_hw_register_clkdev Since we don't care about retrieving the clk_lookup structure pointer returned by clkdev_hw_create, we can just use the clk_hw_register_clkdev function. Cc: Michael Turquette Cc: linux-clk@vger.kernel.org Acked-by: Nicolas Saenz Julienne Reviewed-by: Stephen Boyd Tested-by: Nicolas Saenz Julienne Signed-off-by: Maxime Ripard Link: https://lore.kernel.org/r/59f6208b6fe3367e735b0cca4f65c2c937639af9.1592210452.git-series.maxime@cerno.tech Signed-off-by: Stephen Boyd --- drivers/clk/bcm/clk-raspberrypi.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/drivers/clk/bcm/clk-raspberrypi.c b/drivers/clk/bcm/clk-raspberrypi.c index 5a06c4991c7f9..23f06618a3567 100644 --- a/drivers/clk/bcm/clk-raspberrypi.c +++ b/drivers/clk/bcm/clk-raspberrypi.c @@ -237,7 +237,6 @@ static struct clk_fixed_factor raspberrypi_clk_pllb_arm = { static int raspberrypi_register_pllb_arm(struct raspberrypi_clk *rpi) { - struct clk_lookup *pllb_arm_lookup; int ret; ret = devm_clk_hw_register(rpi->dev, &raspberrypi_clk_pllb_arm.hw); @@ -246,11 +245,11 @@ static int raspberrypi_register_pllb_arm(struct raspberrypi_clk *rpi) return ret; } - pllb_arm_lookup = clkdev_hw_create(&raspberrypi_clk_pllb_arm.hw, - NULL, "cpu0"); - if (!pllb_arm_lookup) { - dev_err(rpi->dev, "Failed to initialize pllb_arm_lookup\n"); - return -ENOMEM; + ret = clk_hw_register_clkdev(&raspberrypi_clk_pllb_arm.hw, + NULL, "cpu0"); + if (ret) { + dev_err(rpi->dev, "Failed to initialize clkdev\n"); + return ret; } return 0; -- GitLab From 9bd43a6184c2559032a2a8675c135abf7777a736 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Mon, 15 Jun 2020 10:40:51 +0200 Subject: [PATCH 0044/1476] clk: bcm: rpi: Make sure the clkdev lookup is removed The clkdev lookup created for the cpufreq device is never removed if there's an issue later in probe or at module removal time. Let's convert to the managed variant of the clk_hw_register_clkdev function to make sure it happens. Cc: Michael Turquette Cc: linux-clk@vger.kernel.org Acked-by: Nicolas Saenz Julienne Reviewed-by: Stephen Boyd Tested-by: Nicolas Saenz Julienne Signed-off-by: Maxime Ripard Link: https://lore.kernel.org/r/075e2c6d315eccdaf8fb72b320712b86e6c25b22.1592210452.git-series.maxime@cerno.tech Signed-off-by: Stephen Boyd --- drivers/clk/bcm/clk-raspberrypi.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/clk/bcm/clk-raspberrypi.c b/drivers/clk/bcm/clk-raspberrypi.c index 23f06618a3567..a20492fade6a9 100644 --- a/drivers/clk/bcm/clk-raspberrypi.c +++ b/drivers/clk/bcm/clk-raspberrypi.c @@ -245,8 +245,9 @@ static int raspberrypi_register_pllb_arm(struct raspberrypi_clk *rpi) return ret; } - ret = clk_hw_register_clkdev(&raspberrypi_clk_pllb_arm.hw, - NULL, "cpu0"); + ret = devm_clk_hw_register_clkdev(rpi->dev, + &raspberrypi_clk_pllb_arm.hw, + NULL, "cpu0"); if (ret) { dev_err(rpi->dev, "Failed to initialize clkdev\n"); return ret; -- GitLab From df4b6a4c3bf5b0bc0fb5c35af9ca6da9c78922ee Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Mon, 15 Jun 2020 10:40:52 +0200 Subject: [PATCH 0045/1476] clk: bcm: rpi: Use CCF boundaries instead of rolling our own The raspberrypi firmware clock driver has a min_rate / max_rate clamping by storing the info it needs in a private structure. However, the CCF already provides such a facility, so we can switch to it to remove the boilerplate. Reviewed-by: Nicolas Saenz Julienne Tested-by: Nicolas Saenz Julienne Signed-off-by: Maxime Ripard Link: https://lore.kernel.org/r/d4c53dab6de5d5f70743d9c139d0117589530e62.1592210452.git-series.maxime@cerno.tech Signed-off-by: Stephen Boyd --- drivers/clk/bcm/clk-raspberrypi.c | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/drivers/clk/bcm/clk-raspberrypi.c b/drivers/clk/bcm/clk-raspberrypi.c index a20492fade6a9..e135ad28d38db 100644 --- a/drivers/clk/bcm/clk-raspberrypi.c +++ b/drivers/clk/bcm/clk-raspberrypi.c @@ -36,9 +36,6 @@ struct raspberrypi_clk { struct rpi_firmware *firmware; struct platform_device *cpufreq; - unsigned long min_rate; - unsigned long max_rate; - struct clk_hw pllb; }; @@ -142,13 +139,11 @@ static int raspberrypi_fw_pll_set_rate(struct clk_hw *hw, unsigned long rate, static int raspberrypi_pll_determine_rate(struct clk_hw *hw, struct clk_rate_request *req) { - struct raspberrypi_clk *rpi = container_of(hw, struct raspberrypi_clk, - pllb); u64 div, final_rate; u32 ndiv, fdiv; /* We can't use req->rate directly as it would overflow */ - final_rate = clamp(req->rate, rpi->min_rate, rpi->max_rate); + final_rate = clamp(req->rate, req->min_rate, req->max_rate); div = (u64)final_rate << A2W_PLL_FRAC_BITS; do_div(div, req->best_parent_rate); @@ -215,12 +210,15 @@ static int raspberrypi_register_pllb(struct raspberrypi_clk *rpi) dev_info(rpi->dev, "CPU frequency range: min %u, max %u\n", min_rate, max_rate); - rpi->min_rate = min_rate * RPI_FIRMWARE_PLLB_ARM_DIV_RATE; - rpi->max_rate = max_rate * RPI_FIRMWARE_PLLB_ARM_DIV_RATE; - rpi->pllb.init = &init; - return devm_clk_hw_register(rpi->dev, &rpi->pllb); + ret = devm_clk_hw_register(rpi->dev, &rpi->pllb); + if (!ret) + clk_hw_set_rate_range(&rpi->pllb, + min_rate * RPI_FIRMWARE_PLLB_ARM_DIV_RATE, + max_rate * RPI_FIRMWARE_PLLB_ARM_DIV_RATE); + + return ret; } static struct clk_fixed_factor raspberrypi_clk_pllb_arm = { -- GitLab From f922c560c95613f9aa45e135380bb78d3abbdb35 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Mon, 15 Jun 2020 10:40:53 +0200 Subject: [PATCH 0046/1476] clk: bcm: rpi: Create a data structure for the clocks So far the driver has really only been providing a single clock, and stored both the data associated to that clock in particular with the data associated to the "controller". Since we will change that in the future, let's decouple the clock data from the provider data. Cc: Michael Turquette Cc: linux-clk@vger.kernel.org Acked-by: Nicolas Saenz Julienne Reviewed-by: Stephen Boyd Tested-by: Nicolas Saenz Julienne Signed-off-by: Maxime Ripard Link: https://lore.kernel.org/r/ee7f508db226214fab4add7f93a351f4137c86a1.1592210452.git-series.maxime@cerno.tech Signed-off-by: Stephen Boyd --- drivers/clk/bcm/clk-raspberrypi.c | 31 +++++++++++++++++++++---------- 1 file changed, 21 insertions(+), 10 deletions(-) diff --git a/drivers/clk/bcm/clk-raspberrypi.c b/drivers/clk/bcm/clk-raspberrypi.c index e135ad28d38db..00735704eabc0 100644 --- a/drivers/clk/bcm/clk-raspberrypi.c +++ b/drivers/clk/bcm/clk-raspberrypi.c @@ -35,8 +35,11 @@ struct raspberrypi_clk { struct device *dev; struct rpi_firmware *firmware; struct platform_device *cpufreq; +}; - struct clk_hw pllb; +struct raspberrypi_clk_data { + struct clk_hw hw; + struct raspberrypi_clk *rpi; }; /* @@ -80,8 +83,9 @@ static int raspberrypi_clock_property(struct rpi_firmware *firmware, u32 tag, static int raspberrypi_fw_pll_is_on(struct clk_hw *hw) { - struct raspberrypi_clk *rpi = container_of(hw, struct raspberrypi_clk, - pllb); + struct raspberrypi_clk_data *data = + container_of(hw, struct raspberrypi_clk_data, hw); + struct raspberrypi_clk *rpi = data->rpi; u32 val = 0; int ret; @@ -98,8 +102,9 @@ static int raspberrypi_fw_pll_is_on(struct clk_hw *hw) static unsigned long raspberrypi_fw_pll_get_rate(struct clk_hw *hw, unsigned long parent_rate) { - struct raspberrypi_clk *rpi = container_of(hw, struct raspberrypi_clk, - pllb); + struct raspberrypi_clk_data *data = + container_of(hw, struct raspberrypi_clk_data, hw); + struct raspberrypi_clk *rpi = data->rpi; u32 val = 0; int ret; @@ -116,8 +121,9 @@ static unsigned long raspberrypi_fw_pll_get_rate(struct clk_hw *hw, static int raspberrypi_fw_pll_set_rate(struct clk_hw *hw, unsigned long rate, unsigned long parent_rate) { - struct raspberrypi_clk *rpi = container_of(hw, struct raspberrypi_clk, - pllb); + struct raspberrypi_clk_data *data = + container_of(hw, struct raspberrypi_clk_data, hw); + struct raspberrypi_clk *rpi = data->rpi; u32 new_rate = rate / RPI_FIRMWARE_PLLB_ARM_DIV_RATE; int ret; @@ -168,10 +174,15 @@ static const struct clk_ops raspberrypi_firmware_pll_clk_ops = { static int raspberrypi_register_pllb(struct raspberrypi_clk *rpi) { + struct raspberrypi_clk_data *data; struct clk_init_data init = {}; u32 min_rate = 0, max_rate = 0; int ret; + data = devm_kzalloc(rpi->dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + data->rpi = rpi; /* All of the PLLs derive from the external oscillator. */ init.parent_names = (const char *[]){ "osc" }; @@ -210,11 +221,11 @@ static int raspberrypi_register_pllb(struct raspberrypi_clk *rpi) dev_info(rpi->dev, "CPU frequency range: min %u, max %u\n", min_rate, max_rate); - rpi->pllb.init = &init; + data->hw.init = &init; - ret = devm_clk_hw_register(rpi->dev, &rpi->pllb); + ret = devm_clk_hw_register(rpi->dev, &data->hw); if (!ret) - clk_hw_set_rate_range(&rpi->pllb, + clk_hw_set_rate_range(&data->hw, min_rate * RPI_FIRMWARE_PLLB_ARM_DIV_RATE, max_rate * RPI_FIRMWARE_PLLB_ARM_DIV_RATE); -- GitLab From 8a1f3ebc38fca949ad69feb1e536b71360e8cbb4 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Mon, 15 Jun 2020 10:40:54 +0200 Subject: [PATCH 0047/1476] clk: bcm: rpi: Add clock id to data The driver has really only supported one clock so far and has hardcoded the ID used in communications with the firmware in all the functions implementing the clock framework hooks. Let's store that in the clock data structure so that we can support more clocks later on. Cc: Michael Turquette Cc: linux-clk@vger.kernel.org Acked-by: Nicolas Saenz Julienne Reviewed-by: Stephen Boyd Tested-by: Nicolas Saenz Julienne Signed-off-by: Maxime Ripard Link: https://lore.kernel.org/r/e23c37961b97b027e21efa3b818578970f88527a.1592210452.git-series.maxime@cerno.tech Signed-off-by: Stephen Boyd --- drivers/clk/bcm/clk-raspberrypi.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/drivers/clk/bcm/clk-raspberrypi.c b/drivers/clk/bcm/clk-raspberrypi.c index 00735704eabc0..97ac04604b0a1 100644 --- a/drivers/clk/bcm/clk-raspberrypi.c +++ b/drivers/clk/bcm/clk-raspberrypi.c @@ -39,6 +39,9 @@ struct raspberrypi_clk { struct raspberrypi_clk_data { struct clk_hw hw; + + unsigned int id; + struct raspberrypi_clk *rpi; }; @@ -91,7 +94,7 @@ static int raspberrypi_fw_pll_is_on(struct clk_hw *hw) ret = raspberrypi_clock_property(rpi->firmware, RPI_FIRMWARE_GET_CLOCK_STATE, - RPI_FIRMWARE_ARM_CLK_ID, &val); + data->id, &val); if (ret) return 0; @@ -110,8 +113,7 @@ static unsigned long raspberrypi_fw_pll_get_rate(struct clk_hw *hw, ret = raspberrypi_clock_property(rpi->firmware, RPI_FIRMWARE_GET_CLOCK_RATE, - RPI_FIRMWARE_ARM_CLK_ID, - &val); + data->id, &val); if (ret) return ret; @@ -129,8 +131,7 @@ static int raspberrypi_fw_pll_set_rate(struct clk_hw *hw, unsigned long rate, ret = raspberrypi_clock_property(rpi->firmware, RPI_FIRMWARE_SET_CLOCK_RATE, - RPI_FIRMWARE_ARM_CLK_ID, - &new_rate); + data->id, &new_rate); if (ret) dev_err_ratelimited(rpi->dev, "Failed to change %s frequency: %d", clk_hw_get_name(hw), ret); @@ -183,6 +184,7 @@ static int raspberrypi_register_pllb(struct raspberrypi_clk *rpi) if (!data) return -ENOMEM; data->rpi = rpi; + data->id = RPI_FIRMWARE_ARM_CLK_ID; /* All of the PLLs derive from the external oscillator. */ init.parent_names = (const char *[]){ "osc" }; @@ -194,8 +196,7 @@ static int raspberrypi_register_pllb(struct raspberrypi_clk *rpi) /* Get min & max rates set by the firmware */ ret = raspberrypi_clock_property(rpi->firmware, RPI_FIRMWARE_GET_MIN_CLOCK_RATE, - RPI_FIRMWARE_ARM_CLK_ID, - &min_rate); + data->id, &min_rate); if (ret) { dev_err(rpi->dev, "Failed to get %s min freq: %d\n", init.name, ret); @@ -204,8 +205,7 @@ static int raspberrypi_register_pllb(struct raspberrypi_clk *rpi) ret = raspberrypi_clock_property(rpi->firmware, RPI_FIRMWARE_GET_MAX_CLOCK_RATE, - RPI_FIRMWARE_ARM_CLK_ID, - &max_rate); + data->id, &max_rate); if (ret) { dev_err(rpi->dev, "Failed to get %s max freq: %d\n", init.name, ret); -- GitLab From 81df01518879d9d0ccce40564883ac0ba3299c47 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Mon, 15 Jun 2020 10:40:55 +0200 Subject: [PATCH 0048/1476] clk: bcm: rpi: Pass the clocks data to the firmware function The raspberry_clock_property only takes the clock ID as an argument, but now that we have a clock data structure it makes more sense to just pass that structure instead. Cc: Michael Turquette Cc: Stephen Boyd Cc: linux-clk@vger.kernel.org Acked-by: Nicolas Saenz Julienne Reviewed-by: Stephen Boyd Tested-by: Nicolas Saenz Julienne Signed-off-by: Maxime Ripard Link: https://lore.kernel.org/r/d7a3b4df3ca23feb6e0d9c7ae2d232bfb913f926.1592210452.git-series.maxime@cerno.tech Signed-off-by: Stephen Boyd --- drivers/clk/bcm/clk-raspberrypi.c | 29 ++++++++++++++--------------- 1 file changed, 14 insertions(+), 15 deletions(-) diff --git a/drivers/clk/bcm/clk-raspberrypi.c b/drivers/clk/bcm/clk-raspberrypi.c index 97ac04604b0a1..3fce49a65a79e 100644 --- a/drivers/clk/bcm/clk-raspberrypi.c +++ b/drivers/clk/bcm/clk-raspberrypi.c @@ -65,11 +65,12 @@ struct raspberrypi_firmware_prop { __le32 disable_turbo; } __packed; -static int raspberrypi_clock_property(struct rpi_firmware *firmware, u32 tag, - u32 clk, u32 *val) +static int raspberrypi_clock_property(struct rpi_firmware *firmware, + const struct raspberrypi_clk_data *data, + u32 tag, u32 *val) { struct raspberrypi_firmware_prop msg = { - .id = cpu_to_le32(clk), + .id = cpu_to_le32(data->id), .val = cpu_to_le32(*val), .disable_turbo = cpu_to_le32(1), }; @@ -92,9 +93,8 @@ static int raspberrypi_fw_pll_is_on(struct clk_hw *hw) u32 val = 0; int ret; - ret = raspberrypi_clock_property(rpi->firmware, - RPI_FIRMWARE_GET_CLOCK_STATE, - data->id, &val); + ret = raspberrypi_clock_property(rpi->firmware, data, + RPI_FIRMWARE_GET_CLOCK_STATE, &val); if (ret) return 0; @@ -111,9 +111,8 @@ static unsigned long raspberrypi_fw_pll_get_rate(struct clk_hw *hw, u32 val = 0; int ret; - ret = raspberrypi_clock_property(rpi->firmware, - RPI_FIRMWARE_GET_CLOCK_RATE, - data->id, &val); + ret = raspberrypi_clock_property(rpi->firmware, data, + RPI_FIRMWARE_GET_CLOCK_RATE, &val); if (ret) return ret; @@ -129,9 +128,9 @@ static int raspberrypi_fw_pll_set_rate(struct clk_hw *hw, unsigned long rate, u32 new_rate = rate / RPI_FIRMWARE_PLLB_ARM_DIV_RATE; int ret; - ret = raspberrypi_clock_property(rpi->firmware, + ret = raspberrypi_clock_property(rpi->firmware, data, RPI_FIRMWARE_SET_CLOCK_RATE, - data->id, &new_rate); + &new_rate); if (ret) dev_err_ratelimited(rpi->dev, "Failed to change %s frequency: %d", clk_hw_get_name(hw), ret); @@ -194,18 +193,18 @@ static int raspberrypi_register_pllb(struct raspberrypi_clk *rpi) init.flags = CLK_GET_RATE_NOCACHE | CLK_IGNORE_UNUSED; /* Get min & max rates set by the firmware */ - ret = raspberrypi_clock_property(rpi->firmware, + ret = raspberrypi_clock_property(rpi->firmware, data, RPI_FIRMWARE_GET_MIN_CLOCK_RATE, - data->id, &min_rate); + &min_rate); if (ret) { dev_err(rpi->dev, "Failed to get %s min freq: %d\n", init.name, ret); return ret; } - ret = raspberrypi_clock_property(rpi->firmware, + ret = raspberrypi_clock_property(rpi->firmware, data, RPI_FIRMWARE_GET_MAX_CLOCK_RATE, - data->id, &max_rate); + &max_rate); if (ret) { dev_err(rpi->dev, "Failed to get %s max freq: %d\n", init.name, ret); -- GitLab From c1ce3509eca684e72d0910192d5549d01558b658 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Mon, 15 Jun 2020 10:40:56 +0200 Subject: [PATCH 0049/1476] clk: bcm: rpi: Rename is_prepared function The raspberrypi_fw_pll_is_on function doesn't only apply to PLL registered in the driver, but any clock exposed by the firmware. Since we also implement the is_prepared hook, make the function consistent with the other function names. Cc: Michael Turquette Cc: linux-clk@vger.kernel.org Acked-by: Nicolas Saenz Julienne Reviewed-by: Stephen Boyd Tested-by: Nicolas Saenz Julienne Signed-off-by: Maxime Ripard Link: https://lore.kernel.org/r/ac93cc4e245316bb7e7426ac5ab0de8f3d919731.1592210452.git-series.maxime@cerno.tech Signed-off-by: Stephen Boyd --- drivers/clk/bcm/clk-raspberrypi.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/clk/bcm/clk-raspberrypi.c b/drivers/clk/bcm/clk-raspberrypi.c index 3fce49a65a79e..58ac1b104429b 100644 --- a/drivers/clk/bcm/clk-raspberrypi.c +++ b/drivers/clk/bcm/clk-raspberrypi.c @@ -85,7 +85,7 @@ static int raspberrypi_clock_property(struct rpi_firmware *firmware, return 0; } -static int raspberrypi_fw_pll_is_on(struct clk_hw *hw) +static int raspberrypi_fw_is_prepared(struct clk_hw *hw) { struct raspberrypi_clk_data *data = container_of(hw, struct raspberrypi_clk_data, hw); @@ -166,7 +166,7 @@ static int raspberrypi_pll_determine_rate(struct clk_hw *hw, } static const struct clk_ops raspberrypi_firmware_pll_clk_ops = { - .is_prepared = raspberrypi_fw_pll_is_on, + .is_prepared = raspberrypi_fw_is_prepared, .recalc_rate = raspberrypi_fw_pll_get_rate, .set_rate = raspberrypi_fw_pll_set_rate, .determine_rate = raspberrypi_pll_determine_rate, -- GitLab From 3ea59ace7c0f475bf12f13b63a47a6983959d754 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Mon, 15 Jun 2020 10:40:57 +0200 Subject: [PATCH 0050/1476] clk: bcm: rpi: Split pllb clock hooks The driver only supports the pllb for now and all the clock framework hooks are a mix of the generic firmware interface and the specifics of the pllb. Since we will support more clocks in the future let's split the generic and specific hooks Cc: Michael Turquette Cc: linux-clk@vger.kernel.org Acked-by: Nicolas Saenz Julienne Reviewed-by: Stephen Boyd Tested-by: Nicolas Saenz Julienne Signed-off-by: Maxime Ripard Link: https://lore.kernel.org/r/fdc21962fdc7de5c46232f198672d5d5c868ec74.1592210452.git-series.maxime@cerno.tech Signed-off-by: Stephen Boyd --- drivers/clk/bcm/clk-raspberrypi.c | 30 ++++++++++++++++++++++-------- 1 file changed, 22 insertions(+), 8 deletions(-) diff --git a/drivers/clk/bcm/clk-raspberrypi.c b/drivers/clk/bcm/clk-raspberrypi.c index 58ac1b104429b..19571602ba64b 100644 --- a/drivers/clk/bcm/clk-raspberrypi.c +++ b/drivers/clk/bcm/clk-raspberrypi.c @@ -102,8 +102,8 @@ static int raspberrypi_fw_is_prepared(struct clk_hw *hw) } -static unsigned long raspberrypi_fw_pll_get_rate(struct clk_hw *hw, - unsigned long parent_rate) +static unsigned long raspberrypi_fw_get_rate(struct clk_hw *hw, + unsigned long parent_rate) { struct raspberrypi_clk_data *data = container_of(hw, struct raspberrypi_clk_data, hw); @@ -116,21 +116,27 @@ static unsigned long raspberrypi_fw_pll_get_rate(struct clk_hw *hw, if (ret) return ret; - return val * RPI_FIRMWARE_PLLB_ARM_DIV_RATE; + return val; } -static int raspberrypi_fw_pll_set_rate(struct clk_hw *hw, unsigned long rate, - unsigned long parent_rate) +static unsigned long raspberrypi_fw_pll_get_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + return raspberrypi_fw_get_rate(hw, parent_rate) * + RPI_FIRMWARE_PLLB_ARM_DIV_RATE; +} + +static int raspberrypi_fw_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) { struct raspberrypi_clk_data *data = container_of(hw, struct raspberrypi_clk_data, hw); struct raspberrypi_clk *rpi = data->rpi; - u32 new_rate = rate / RPI_FIRMWARE_PLLB_ARM_DIV_RATE; + u32 _rate = rate; int ret; ret = raspberrypi_clock_property(rpi->firmware, data, - RPI_FIRMWARE_SET_CLOCK_RATE, - &new_rate); + RPI_FIRMWARE_SET_CLOCK_RATE, &_rate); if (ret) dev_err_ratelimited(rpi->dev, "Failed to change %s frequency: %d", clk_hw_get_name(hw), ret); @@ -138,6 +144,14 @@ static int raspberrypi_fw_pll_set_rate(struct clk_hw *hw, unsigned long rate, return ret; } +static int raspberrypi_fw_pll_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + u32 new_rate = rate / RPI_FIRMWARE_PLLB_ARM_DIV_RATE; + + return raspberrypi_fw_set_rate(hw, new_rate, parent_rate); +} + /* * Sadly there is no firmware rate rounding interface. We borrowed it from * clk-bcm2835. -- GitLab From 23e114b6b7a03a98d27cf327f0d173ad2d219c13 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Mon, 15 Jun 2020 10:40:58 +0200 Subject: [PATCH 0051/1476] clk: bcm: rpi: Make the PLLB registration function return a clk_hw The raspberrypi_register_pllb has been returning an integer so far to notify whether the functions has exited successfully or not. However, the OF provider functions in the clock framework require access to the clk_hw structure so that we can expose those clocks to device tree consumers. Since we'll want that for the future clocks, let's return a clk_hw pointer instead of the return code. Cc: Michael Turquette Cc: linux-clk@vger.kernel.org Acked-by: Nicolas Saenz Julienne Reviewed-by: Stephen Boyd Tested-by: Nicolas Saenz Julienne Signed-off-by: Maxime Ripard Link: https://lore.kernel.org/r/97218559db643e62fdd2b5e3046a2a05b8c2e769.1592210452.git-series.maxime@cerno.tech Signed-off-by: Stephen Boyd --- drivers/clk/bcm/clk-raspberrypi.c | 46 ++++++++++++++++--------------- 1 file changed, 24 insertions(+), 22 deletions(-) diff --git a/drivers/clk/bcm/clk-raspberrypi.c b/drivers/clk/bcm/clk-raspberrypi.c index 19571602ba64b..d2cb90c086a7c 100644 --- a/drivers/clk/bcm/clk-raspberrypi.c +++ b/drivers/clk/bcm/clk-raspberrypi.c @@ -186,7 +186,7 @@ static const struct clk_ops raspberrypi_firmware_pll_clk_ops = { .determine_rate = raspberrypi_pll_determine_rate, }; -static int raspberrypi_register_pllb(struct raspberrypi_clk *rpi) +static struct clk_hw *raspberrypi_register_pllb(struct raspberrypi_clk *rpi) { struct raspberrypi_clk_data *data; struct clk_init_data init = {}; @@ -195,7 +195,7 @@ static int raspberrypi_register_pllb(struct raspberrypi_clk *rpi) data = devm_kzalloc(rpi->dev, sizeof(*data), GFP_KERNEL); if (!data) - return -ENOMEM; + return ERR_PTR(-ENOMEM); data->rpi = rpi; data->id = RPI_FIRMWARE_ARM_CLK_ID; @@ -213,7 +213,7 @@ static int raspberrypi_register_pllb(struct raspberrypi_clk *rpi) if (ret) { dev_err(rpi->dev, "Failed to get %s min freq: %d\n", init.name, ret); - return ret; + return ERR_PTR(ret); } ret = raspberrypi_clock_property(rpi->firmware, data, @@ -222,13 +222,13 @@ static int raspberrypi_register_pllb(struct raspberrypi_clk *rpi) if (ret) { dev_err(rpi->dev, "Failed to get %s max freq: %d\n", init.name, ret); - return ret; + return ERR_PTR(ret); } if (!min_rate || !max_rate) { dev_err(rpi->dev, "Unexpected frequency range: min %u, max %u\n", min_rate, max_rate); - return -EINVAL; + return ERR_PTR(-EINVAL); } dev_info(rpi->dev, "CPU frequency range: min %u, max %u\n", @@ -237,12 +237,14 @@ static int raspberrypi_register_pllb(struct raspberrypi_clk *rpi) data->hw.init = &init; ret = devm_clk_hw_register(rpi->dev, &data->hw); - if (!ret) - clk_hw_set_rate_range(&data->hw, - min_rate * RPI_FIRMWARE_PLLB_ARM_DIV_RATE, - max_rate * RPI_FIRMWARE_PLLB_ARM_DIV_RATE); + if (ret) + return ERR_PTR(ret); - return ret; + clk_hw_set_rate_range(&data->hw, + min_rate * RPI_FIRMWARE_PLLB_ARM_DIV_RATE, + max_rate * RPI_FIRMWARE_PLLB_ARM_DIV_RATE); + + return &data->hw; } static struct clk_fixed_factor raspberrypi_clk_pllb_arm = { @@ -257,14 +259,14 @@ static struct clk_fixed_factor raspberrypi_clk_pllb_arm = { }, }; -static int raspberrypi_register_pllb_arm(struct raspberrypi_clk *rpi) +static struct clk_hw *raspberrypi_register_pllb_arm(struct raspberrypi_clk *rpi) { int ret; ret = devm_clk_hw_register(rpi->dev, &raspberrypi_clk_pllb_arm.hw); if (ret) { dev_err(rpi->dev, "Failed to initialize pllb_arm\n"); - return ret; + return ERR_PTR(ret); } ret = devm_clk_hw_register_clkdev(rpi->dev, @@ -272,10 +274,10 @@ static int raspberrypi_register_pllb_arm(struct raspberrypi_clk *rpi) NULL, "cpu0"); if (ret) { dev_err(rpi->dev, "Failed to initialize clkdev\n"); - return ret; + return ERR_PTR(ret); } - return 0; + return &raspberrypi_clk_pllb_arm.hw; } static int raspberrypi_clk_probe(struct platform_device *pdev) @@ -284,7 +286,7 @@ static int raspberrypi_clk_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; struct rpi_firmware *firmware; struct raspberrypi_clk *rpi; - int ret; + struct clk_hw *hw; /* * We can be probed either through the an old-fashioned @@ -314,15 +316,15 @@ static int raspberrypi_clk_probe(struct platform_device *pdev) rpi->firmware = firmware; platform_set_drvdata(pdev, rpi); - ret = raspberrypi_register_pllb(rpi); - if (ret) { - dev_err(dev, "Failed to initialize pllb, %d\n", ret); - return ret; + hw = raspberrypi_register_pllb(rpi); + if (IS_ERR(hw)) { + dev_err(dev, "Failed to initialize pllb, %ld\n", PTR_ERR(hw)); + return PTR_ERR(hw); } - ret = raspberrypi_register_pllb_arm(rpi); - if (ret) - return ret; + hw = raspberrypi_register_pllb_arm(rpi); + if (IS_ERR(hw)) + return PTR_ERR(hw); rpi->cpufreq = platform_device_register_data(dev, "raspberrypi-cpufreq", -1, NULL, 0); -- GitLab From d4b4f1b6b97e56d49a69d6ee734f96f4d3d35762 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Mon, 15 Jun 2020 10:40:59 +0200 Subject: [PATCH 0052/1476] clk: bcm: rpi: Add DT provider for the clocks For the upcoming registration of the clocks provided by the firmware, make sure it's exposed to the device tree providers. Cc: Michael Turquette Cc: linux-clk@vger.kernel.org Acked-by: Nicolas Saenz Julienne Reviewed-by: Stephen Boyd Tested-by: Nicolas Saenz Julienne Signed-off-by: Maxime Ripard Link: https://lore.kernel.org/r/4d8dbe4aaae98b3d3812ad7c3dba53d645cadbaf.1592210452.git-series.maxime@cerno.tech Signed-off-by: Stephen Boyd --- drivers/clk/bcm/clk-raspberrypi.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/drivers/clk/bcm/clk-raspberrypi.c b/drivers/clk/bcm/clk-raspberrypi.c index d2cb90c086a7c..5f4e2d49432fe 100644 --- a/drivers/clk/bcm/clk-raspberrypi.c +++ b/drivers/clk/bcm/clk-raspberrypi.c @@ -31,6 +31,8 @@ #define A2W_PLL_FRAC_BITS 20 +#define NUM_FW_CLKS 16 + struct raspberrypi_clk { struct device *dev; struct rpi_firmware *firmware; @@ -282,11 +284,13 @@ static struct clk_hw *raspberrypi_register_pllb_arm(struct raspberrypi_clk *rpi) static int raspberrypi_clk_probe(struct platform_device *pdev) { + struct clk_hw_onecell_data *clk_data; struct device_node *firmware_node; struct device *dev = &pdev->dev; struct rpi_firmware *firmware; struct raspberrypi_clk *rpi; struct clk_hw *hw; + int ret; /* * We can be probed either through the an old-fashioned @@ -316,6 +320,11 @@ static int raspberrypi_clk_probe(struct platform_device *pdev) rpi->firmware = firmware; platform_set_drvdata(pdev, rpi); + clk_data = devm_kzalloc(dev, struct_size(clk_data, hws, NUM_FW_CLKS), + GFP_KERNEL); + if (!clk_data) + return -ENOMEM; + hw = raspberrypi_register_pllb(rpi); if (IS_ERR(hw)) { dev_err(dev, "Failed to initialize pllb, %ld\n", PTR_ERR(hw)); @@ -325,6 +334,13 @@ static int raspberrypi_clk_probe(struct platform_device *pdev) hw = raspberrypi_register_pllb_arm(rpi); if (IS_ERR(hw)) return PTR_ERR(hw); + clk_data->hws[RPI_FIRMWARE_ARM_CLK_ID] = hw; + clk_data->num = RPI_FIRMWARE_ARM_CLK_ID + 1; + + ret = devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get, + clk_data); + if (ret) + return ret; rpi->cpufreq = platform_device_register_data(dev, "raspberrypi-cpufreq", -1, NULL, 0); -- GitLab From be1559f69768da4576f5b4b9073466e82c031153 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Mon, 15 Jun 2020 10:41:00 +0200 Subject: [PATCH 0053/1476] clk: bcm: rpi: Add an enum for the firmware clocks While the firmware allows us to discover the available clocks, we need to discriminate those clocks to only register the ones meaningful to Linux. The firmware also doesn't provide a clock name, so having a list of the ID will help us to give clocks a proper name later on. Acked-by: Nicolas Saenz Julienne Tested-by: Nicolas Saenz Julienne Signed-off-by: Maxime Ripard Link: https://lore.kernel.org/r/4738f77ee7de9b48a3bb1c558ead958d0cc064d9.1592210452.git-series.maxime@cerno.tech Signed-off-by: Stephen Boyd --- drivers/clk/bcm/clk-raspberrypi.c | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/drivers/clk/bcm/clk-raspberrypi.c b/drivers/clk/bcm/clk-raspberrypi.c index 5f4e2d49432fe..eebd16040f8a9 100644 --- a/drivers/clk/bcm/clk-raspberrypi.c +++ b/drivers/clk/bcm/clk-raspberrypi.c @@ -18,7 +18,23 @@ #include -#define RPI_FIRMWARE_ARM_CLK_ID 0x00000003 +enum rpi_firmware_clk_id { + RPI_FIRMWARE_EMMC_CLK_ID = 1, + RPI_FIRMWARE_UART_CLK_ID, + RPI_FIRMWARE_ARM_CLK_ID, + RPI_FIRMWARE_CORE_CLK_ID, + RPI_FIRMWARE_V3D_CLK_ID, + RPI_FIRMWARE_H264_CLK_ID, + RPI_FIRMWARE_ISP_CLK_ID, + RPI_FIRMWARE_SDRAM_CLK_ID, + RPI_FIRMWARE_PIXEL_CLK_ID, + RPI_FIRMWARE_PWM_CLK_ID, + RPI_FIRMWARE_HEVC_CLK_ID, + RPI_FIRMWARE_EMMC2_CLK_ID, + RPI_FIRMWARE_M2MC_CLK_ID, + RPI_FIRMWARE_PIXEL_BVB_CLK_ID, + RPI_FIRMWARE_NUM_CLK_ID, +}; #define RPI_FIRMWARE_STATE_ENABLE_BIT BIT(0) #define RPI_FIRMWARE_STATE_WAIT_BIT BIT(1) @@ -31,8 +47,6 @@ #define A2W_PLL_FRAC_BITS 20 -#define NUM_FW_CLKS 16 - struct raspberrypi_clk { struct device *dev; struct rpi_firmware *firmware; @@ -320,7 +334,8 @@ static int raspberrypi_clk_probe(struct platform_device *pdev) rpi->firmware = firmware; platform_set_drvdata(pdev, rpi); - clk_data = devm_kzalloc(dev, struct_size(clk_data, hws, NUM_FW_CLKS), + clk_data = devm_kzalloc(dev, struct_size(clk_data, hws, + RPI_FIRMWARE_NUM_CLK_ID), GFP_KERNEL); if (!clk_data) return -ENOMEM; -- GitLab From 93d2725affd65686792f4b57e49ef660f3c8c0f9 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Mon, 15 Jun 2020 10:41:01 +0200 Subject: [PATCH 0054/1476] clk: bcm: rpi: Discover the firmware clocks The RaspberryPi4 firmware actually exposes more clocks than are currently handled by the driver and we will need to change some of them directly based on the pixel rate for the display related clocks, or the load for the GPU. Since the firmware implements DVFS, this rate change can have a number of side-effects, including adjusting the various PLL voltages or the PLL parents. The firmware also implements thermal throttling, so even some thermal pressure can change those parameters behind Linux back. DVFS is currently implemented on the arm, core, h264, v3d, isp and hevc clocks, so updating any of them using the MMIO driver (and thus behind the firmware's back) can lead to troubles, the arm clock obviously being the most problematic. In order to make Linux play as nice as possible with those constraints, it makes sense to rely on the firmware clocks as much as possible. However, the firmware doesn't seem to provide some equivalents to their MMIO counterparts, so we can't really replace that driver entirely. Fortunately, the firmware has an interface to discover the clocks it exposes. Let's use it to discover, register the clocks in the clocks framework and then expose them through the device tree for consumers to use them. Cc: Michael Turquette Cc: Stephen Boyd Cc: linux-clk@vger.kernel.org Acked-by: Nicolas Saenz Julienne Reviewed-by: Stephen Boyd Tested-by: Nicolas Saenz Julienne Signed-off-by: Maxime Ripard Link: https://lore.kernel.org/r/438d73962741a8c5f7c689319b7443b930a87fde.1592210452.git-series.maxime@cerno.tech Signed-off-by: Stephen Boyd --- drivers/clk/bcm/clk-raspberrypi.c | 153 +++++++++++++++++++++++++++--- 1 file changed, 141 insertions(+), 12 deletions(-) diff --git a/drivers/clk/bcm/clk-raspberrypi.c b/drivers/clk/bcm/clk-raspberrypi.c index eebd16040f8a9..11a62bde5203a 100644 --- a/drivers/clk/bcm/clk-raspberrypi.c +++ b/drivers/clk/bcm/clk-raspberrypi.c @@ -296,6 +296,144 @@ static struct clk_hw *raspberrypi_register_pllb_arm(struct raspberrypi_clk *rpi) return &raspberrypi_clk_pllb_arm.hw; } +static int raspberrypi_fw_dumb_determine_rate(struct clk_hw *hw, + struct clk_rate_request *req) +{ + /* + * The firmware will do the rounding but that isn't part of + * the interface with the firmware, so we just do our best + * here. + */ + req->rate = clamp(req->rate, req->min_rate, req->max_rate); + return 0; +} + +static const struct clk_ops raspberrypi_firmware_clk_ops = { + .is_prepared = raspberrypi_fw_is_prepared, + .recalc_rate = raspberrypi_fw_get_rate, + .determine_rate = raspberrypi_fw_dumb_determine_rate, + .set_rate = raspberrypi_fw_set_rate, +}; + +static struct clk_hw *raspberrypi_clk_register(struct raspberrypi_clk *rpi, + unsigned int parent, + unsigned int id) +{ + struct raspberrypi_clk_data *data; + struct clk_init_data init = {}; + u32 min_rate, max_rate; + int ret; + + if (id == RPI_FIRMWARE_ARM_CLK_ID) { + struct clk_hw *hw; + + hw = raspberrypi_register_pllb(rpi); + if (IS_ERR(hw)) { + dev_err(rpi->dev, "Failed to initialize pllb, %ld\n", + PTR_ERR(hw)); + return hw; + } + + return raspberrypi_register_pllb_arm(rpi); + } + + data = devm_kzalloc(rpi->dev, sizeof(*data), GFP_KERNEL); + if (!data) + return ERR_PTR(-ENOMEM); + data->rpi = rpi; + data->id = id; + + init.name = devm_kasprintf(rpi->dev, GFP_KERNEL, "fw-clk-%u", id); + init.ops = &raspberrypi_firmware_clk_ops; + init.flags = CLK_GET_RATE_NOCACHE; + + data->hw.init = &init; + + ret = raspberrypi_clock_property(rpi->firmware, data, + RPI_FIRMWARE_GET_MIN_CLOCK_RATE, + &min_rate); + if (ret) { + dev_err(rpi->dev, "Failed to get clock %d min freq: %d", + id, ret); + return ERR_PTR(ret); + } + + ret = raspberrypi_clock_property(rpi->firmware, data, + RPI_FIRMWARE_GET_MAX_CLOCK_RATE, + &max_rate); + if (ret) { + dev_err(rpi->dev, "Failed to get clock %d max freq: %d\n", + id, ret); + return ERR_PTR(ret); + } + + ret = devm_clk_hw_register(rpi->dev, &data->hw); + if (ret) + return ERR_PTR(ret); + + clk_hw_set_rate_range(&data->hw, min_rate, max_rate); + + if (id == RPI_FIRMWARE_ARM_CLK_ID) { + ret = devm_clk_hw_register_clkdev(rpi->dev, &data->hw, + NULL, "cpu0"); + if (ret) { + dev_err(rpi->dev, "Failed to initialize clkdev\n"); + return ERR_PTR(ret); + } + } + + return &data->hw; +} + +struct rpi_firmware_get_clocks_response { + u32 parent; + u32 id; +}; + +static int raspberrypi_discover_clocks(struct raspberrypi_clk *rpi, + struct clk_hw_onecell_data *data) +{ + struct rpi_firmware_get_clocks_response *clks; + int ret; + + clks = devm_kcalloc(rpi->dev, + sizeof(*clks), RPI_FIRMWARE_NUM_CLK_ID, + GFP_KERNEL); + if (!clks) + return -ENOMEM; + + ret = rpi_firmware_property(rpi->firmware, RPI_FIRMWARE_GET_CLOCKS, + clks, + sizeof(*clks) * RPI_FIRMWARE_NUM_CLK_ID); + if (ret) + return ret; + + while (clks->id) { + struct clk_hw *hw; + + switch (clks->id) { + case RPI_FIRMWARE_ARM_CLK_ID: + case RPI_FIRMWARE_CORE_CLK_ID: + case RPI_FIRMWARE_M2MC_CLK_ID: + case RPI_FIRMWARE_V3D_CLK_ID: + hw = raspberrypi_clk_register(rpi, clks->parent, + clks->id); + if (IS_ERR(hw)) + return PTR_ERR(hw); + + data->hws[clks->id] = hw; + data->num = clks->id + 1; + fallthrough; + + default: + clks++; + break; + } + } + + return 0; +} + static int raspberrypi_clk_probe(struct platform_device *pdev) { struct clk_hw_onecell_data *clk_data; @@ -303,7 +441,6 @@ static int raspberrypi_clk_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; struct rpi_firmware *firmware; struct raspberrypi_clk *rpi; - struct clk_hw *hw; int ret; /* @@ -340,17 +477,9 @@ static int raspberrypi_clk_probe(struct platform_device *pdev) if (!clk_data) return -ENOMEM; - hw = raspberrypi_register_pllb(rpi); - if (IS_ERR(hw)) { - dev_err(dev, "Failed to initialize pllb, %ld\n", PTR_ERR(hw)); - return PTR_ERR(hw); - } - - hw = raspberrypi_register_pllb_arm(rpi); - if (IS_ERR(hw)) - return PTR_ERR(hw); - clk_data->hws[RPI_FIRMWARE_ARM_CLK_ID] = hw; - clk_data->num = RPI_FIRMWARE_ARM_CLK_ID + 1; + ret = raspberrypi_discover_clocks(rpi, clk_data); + if (ret) + return ret; ret = devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get, clk_data); -- GitLab From 7dad8a613185320093de021ca8abba986bc043cb Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Mon, 15 Jun 2020 10:41:02 +0200 Subject: [PATCH 0055/1476] clk: bcm: rpi: Give firmware clocks a name We've registered the firmware clocks using their ID as name, but it's much more convenient to register them using their proper name. Since the firmware doesn't provide it, we have to duplicate it. Acked-by: Nicolas Saenz Julienne Tested-by: Nicolas Saenz Julienne Signed-off-by: Maxime Ripard Link: https://lore.kernel.org/r/a52a5f5768cd33716cdd35237c6613f26ad75013.1592210452.git-series.maxime@cerno.tech Signed-off-by: Stephen Boyd --- drivers/clk/bcm/clk-raspberrypi.c | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/drivers/clk/bcm/clk-raspberrypi.c b/drivers/clk/bcm/clk-raspberrypi.c index 11a62bde5203a..adc0bb56008a0 100644 --- a/drivers/clk/bcm/clk-raspberrypi.c +++ b/drivers/clk/bcm/clk-raspberrypi.c @@ -36,6 +36,23 @@ enum rpi_firmware_clk_id { RPI_FIRMWARE_NUM_CLK_ID, }; +static char *rpi_firmware_clk_names[] = { + [RPI_FIRMWARE_EMMC_CLK_ID] = "emmc", + [RPI_FIRMWARE_UART_CLK_ID] = "uart", + [RPI_FIRMWARE_ARM_CLK_ID] = "arm", + [RPI_FIRMWARE_CORE_CLK_ID] = "core", + [RPI_FIRMWARE_V3D_CLK_ID] = "v3d", + [RPI_FIRMWARE_H264_CLK_ID] = "h264", + [RPI_FIRMWARE_ISP_CLK_ID] = "isp", + [RPI_FIRMWARE_SDRAM_CLK_ID] = "sdram", + [RPI_FIRMWARE_PIXEL_CLK_ID] = "pixel", + [RPI_FIRMWARE_PWM_CLK_ID] = "pwm", + [RPI_FIRMWARE_HEVC_CLK_ID] = "hevc", + [RPI_FIRMWARE_EMMC2_CLK_ID] = "emmc2", + [RPI_FIRMWARE_M2MC_CLK_ID] = "m2mc", + [RPI_FIRMWARE_PIXEL_BVB_CLK_ID] = "pixel-bvb", +}; + #define RPI_FIRMWARE_STATE_ENABLE_BIT BIT(0) #define RPI_FIRMWARE_STATE_WAIT_BIT BIT(1) @@ -343,7 +360,9 @@ static struct clk_hw *raspberrypi_clk_register(struct raspberrypi_clk *rpi, data->rpi = rpi; data->id = id; - init.name = devm_kasprintf(rpi->dev, GFP_KERNEL, "fw-clk-%u", id); + init.name = devm_kasprintf(rpi->dev, GFP_KERNEL, + "fw-clk-%s", + rpi_firmware_clk_names[id]); init.ops = &raspberrypi_firmware_clk_ops; init.flags = CLK_GET_RATE_NOCACHE; -- GitLab From dbe01b4412914c7ce8fb11ea45bf67fafbffd068 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Mon, 15 Jun 2020 10:41:03 +0200 Subject: [PATCH 0056/1476] Revert "clk: bcm2835: remove pllb" This reverts commit 2256d89333bd17b8b56b42734a7e1046d52f7fc3. Since we will be expanding the firmware clock driver, we'll need to remove the quirks to deal with the PLLB. However, we still want to expose the clock tree properly, so having that clock in the MMIO driver will allow that. Acked-by: Nicolas Saenz Julienne Tested-by: Nicolas Saenz Julienne Signed-off-by: Maxime Ripard Link: https://lore.kernel.org/r/5d26a4c58248f5be7760a7f2f720a1310baea5dd.1592210452.git-series.maxime@cerno.tech Signed-off-by: Stephen Boyd --- drivers/clk/bcm/clk-bcm2835.c | 30 ++++++++++++++++++++++++++---- 1 file changed, 26 insertions(+), 4 deletions(-) diff --git a/drivers/clk/bcm/clk-bcm2835.c b/drivers/clk/bcm/clk-bcm2835.c index 6bb7efa12037b..32f5c13be9d10 100644 --- a/drivers/clk/bcm/clk-bcm2835.c +++ b/drivers/clk/bcm/clk-bcm2835.c @@ -1684,10 +1684,32 @@ static const struct bcm2835_clk_desc clk_desc_array[] = { .fixed_divider = 1, .flags = CLK_SET_RATE_PARENT), - /* - * PLLB is used for the ARM's clock. Controlled by firmware, see - * clk-raspberrypi.c. - */ + /* PLLB is used for the ARM's clock. */ + [BCM2835_PLLB] = REGISTER_PLL( + SOC_ALL, + .name = "pllb", + .cm_ctrl_reg = CM_PLLB, + .a2w_ctrl_reg = A2W_PLLB_CTRL, + .frac_reg = A2W_PLLB_FRAC, + .ana_reg_base = A2W_PLLB_ANA0, + .reference_enable_mask = A2W_XOSC_CTRL_PLLB_ENABLE, + .lock_mask = CM_LOCK_FLOCKB, + + .ana = &bcm2835_ana_default, + + .min_rate = 600000000u, + .max_rate = 3000000000u, + .max_fb_rate = BCM2835_MAX_FB_RATE), + [BCM2835_PLLB_ARM] = REGISTER_PLL_DIV( + SOC_ALL, + .name = "pllb_arm", + .source_pll = "pllb", + .cm_reg = CM_PLLB, + .a2w_reg = A2W_PLLB_ARM, + .load_mask = CM_PLLB_LOADARM, + .hold_mask = CM_PLLB_HOLDARM, + .fixed_divider = 1, + .flags = CLK_SET_RATE_PARENT), /* * PLLC is the core PLL, used to drive the core VPU clock. -- GitLab From b2683d069bcae4277332243850d07ac24be05ab2 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Mon, 15 Jun 2020 10:41:05 +0200 Subject: [PATCH 0057/1476] clk: bcm2835: Allow custom CCF flags for the PLLs While some clock types allow for each clock to specify its own custom flags, the PLLs can't. We will need this for the PLLB, so let's add it. Acked-by: Nicolas Saenz Julienne Tested-by: Nicolas Saenz Julienne Signed-off-by: Maxime Ripard Link: https://lore.kernel.org/r/ae8bd505d8851f6646e244cd76b6b289346973c8.1592210452.git-series.maxime@cerno.tech Signed-off-by: Stephen Boyd --- drivers/clk/bcm/clk-bcm2835.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/clk/bcm/clk-bcm2835.c b/drivers/clk/bcm/clk-bcm2835.c index 32f5c13be9d10..b50f00f109bfb 100644 --- a/drivers/clk/bcm/clk-bcm2835.c +++ b/drivers/clk/bcm/clk-bcm2835.c @@ -421,6 +421,7 @@ struct bcm2835_pll_data { u32 reference_enable_mask; /* Bit in CM_LOCK to indicate when the PLL has locked. */ u32 lock_mask; + u32 flags; const struct bcm2835_pll_ana_bits *ana; @@ -1310,7 +1311,7 @@ static struct clk_hw *bcm2835_register_pll(struct bcm2835_cprman *cprman, init.num_parents = 1; init.name = pll_data->name; init.ops = &bcm2835_pll_clk_ops; - init.flags = CLK_IGNORE_UNUSED; + init.flags = pll_data->flags | CLK_IGNORE_UNUSED; pll = kzalloc(sizeof(*pll), GFP_KERNEL); if (!pll) -- GitLab From 0d46fafc0364688c54b4a9d21cee0cb2eabf43bd Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Mon, 15 Jun 2020 10:41:06 +0200 Subject: [PATCH 0058/1476] clk: bcm2835: Don't cache the PLLB rate The PLLB rate will be changed through the firmware clocks drivers and will change behind this drivers' back, so we don't want to cache the rate. Acked-by: Nicolas Saenz Julienne Tested-by: Nicolas Saenz Julienne Signed-off-by: Maxime Ripard Link: https://lore.kernel.org/r/9864daba2f584ed49aee5ed1d2f4d48507c58197.1592210452.git-series.maxime@cerno.tech Signed-off-by: Stephen Boyd --- drivers/clk/bcm/clk-bcm2835.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/clk/bcm/clk-bcm2835.c b/drivers/clk/bcm/clk-bcm2835.c index b50f00f109bfb..027eba31f7931 100644 --- a/drivers/clk/bcm/clk-bcm2835.c +++ b/drivers/clk/bcm/clk-bcm2835.c @@ -1700,7 +1700,8 @@ static const struct bcm2835_clk_desc clk_desc_array[] = { .min_rate = 600000000u, .max_rate = 3000000000u, - .max_fb_rate = BCM2835_MAX_FB_RATE), + .max_fb_rate = BCM2835_MAX_FB_RATE, + .flags = CLK_GET_RATE_NOCACHE), [BCM2835_PLLB_ARM] = REGISTER_PLL_DIV( SOC_ALL, .name = "pllb_arm", @@ -1710,7 +1711,7 @@ static const struct bcm2835_clk_desc clk_desc_array[] = { .load_mask = CM_PLLB_LOADARM, .hold_mask = CM_PLLB_HOLDARM, .fixed_divider = 1, - .flags = CLK_SET_RATE_PARENT), + .flags = CLK_SET_RATE_PARENT | CLK_GET_RATE_NOCACHE), /* * PLLC is the core PLL, used to drive the core VPU clock. -- GitLab From 56643d9ad2b4933d55f872f61195c1650f9958e9 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Mon, 15 Jun 2020 10:41:07 +0200 Subject: [PATCH 0059/1476] clk: bcm: rpi: Remove the quirks for the CPU clock The CPU clock has had so far a bunch of quirks to expose the clock tree properly, but since we reverted to exposing them through the MMIO driver, we can remove that code from the firmware driver. Acked-by: Nicolas Saenz Julienne Tested-by: Nicolas Saenz Julienne Signed-off-by: Maxime Ripard Link: https://lore.kernel.org/r/acdf820c2f78a25dd7480a0c018b8b387acd013e.1592210452.git-series.maxime@cerno.tech Signed-off-by: Stephen Boyd --- drivers/clk/bcm/clk-raspberrypi.c | 164 ------------------------------ 1 file changed, 164 deletions(-) diff --git a/drivers/clk/bcm/clk-raspberrypi.c b/drivers/clk/bcm/clk-raspberrypi.c index adc0bb56008a0..5cc82954e1ce1 100644 --- a/drivers/clk/bcm/clk-raspberrypi.c +++ b/drivers/clk/bcm/clk-raspberrypi.c @@ -56,14 +56,6 @@ static char *rpi_firmware_clk_names[] = { #define RPI_FIRMWARE_STATE_ENABLE_BIT BIT(0) #define RPI_FIRMWARE_STATE_WAIT_BIT BIT(1) -/* - * Even though the firmware interface alters 'pllb' the frequencies are - * provided as per 'pllb_arm'. We need to scale before passing them trough. - */ -#define RPI_FIRMWARE_PLLB_ARM_DIV_RATE 2 - -#define A2W_PLL_FRAC_BITS 20 - struct raspberrypi_clk { struct device *dev; struct rpi_firmware *firmware; @@ -152,13 +144,6 @@ static unsigned long raspberrypi_fw_get_rate(struct clk_hw *hw, return val; } -static unsigned long raspberrypi_fw_pll_get_rate(struct clk_hw *hw, - unsigned long parent_rate) -{ - return raspberrypi_fw_get_rate(hw, parent_rate) * - RPI_FIRMWARE_PLLB_ARM_DIV_RATE; -} - static int raspberrypi_fw_set_rate(struct clk_hw *hw, unsigned long rate, unsigned long parent_rate) { @@ -177,142 +162,6 @@ static int raspberrypi_fw_set_rate(struct clk_hw *hw, unsigned long rate, return ret; } -static int raspberrypi_fw_pll_set_rate(struct clk_hw *hw, unsigned long rate, - unsigned long parent_rate) -{ - u32 new_rate = rate / RPI_FIRMWARE_PLLB_ARM_DIV_RATE; - - return raspberrypi_fw_set_rate(hw, new_rate, parent_rate); -} - -/* - * Sadly there is no firmware rate rounding interface. We borrowed it from - * clk-bcm2835. - */ -static int raspberrypi_pll_determine_rate(struct clk_hw *hw, - struct clk_rate_request *req) -{ - u64 div, final_rate; - u32 ndiv, fdiv; - - /* We can't use req->rate directly as it would overflow */ - final_rate = clamp(req->rate, req->min_rate, req->max_rate); - - div = (u64)final_rate << A2W_PLL_FRAC_BITS; - do_div(div, req->best_parent_rate); - - ndiv = div >> A2W_PLL_FRAC_BITS; - fdiv = div & ((1 << A2W_PLL_FRAC_BITS) - 1); - - final_rate = ((u64)req->best_parent_rate * - ((ndiv << A2W_PLL_FRAC_BITS) + fdiv)); - - req->rate = final_rate >> A2W_PLL_FRAC_BITS; - - return 0; -} - -static const struct clk_ops raspberrypi_firmware_pll_clk_ops = { - .is_prepared = raspberrypi_fw_is_prepared, - .recalc_rate = raspberrypi_fw_pll_get_rate, - .set_rate = raspberrypi_fw_pll_set_rate, - .determine_rate = raspberrypi_pll_determine_rate, -}; - -static struct clk_hw *raspberrypi_register_pllb(struct raspberrypi_clk *rpi) -{ - struct raspberrypi_clk_data *data; - struct clk_init_data init = {}; - u32 min_rate = 0, max_rate = 0; - int ret; - - data = devm_kzalloc(rpi->dev, sizeof(*data), GFP_KERNEL); - if (!data) - return ERR_PTR(-ENOMEM); - data->rpi = rpi; - data->id = RPI_FIRMWARE_ARM_CLK_ID; - - /* All of the PLLs derive from the external oscillator. */ - init.parent_names = (const char *[]){ "osc" }; - init.num_parents = 1; - init.name = "pllb"; - init.ops = &raspberrypi_firmware_pll_clk_ops; - init.flags = CLK_GET_RATE_NOCACHE | CLK_IGNORE_UNUSED; - - /* Get min & max rates set by the firmware */ - ret = raspberrypi_clock_property(rpi->firmware, data, - RPI_FIRMWARE_GET_MIN_CLOCK_RATE, - &min_rate); - if (ret) { - dev_err(rpi->dev, "Failed to get %s min freq: %d\n", - init.name, ret); - return ERR_PTR(ret); - } - - ret = raspberrypi_clock_property(rpi->firmware, data, - RPI_FIRMWARE_GET_MAX_CLOCK_RATE, - &max_rate); - if (ret) { - dev_err(rpi->dev, "Failed to get %s max freq: %d\n", - init.name, ret); - return ERR_PTR(ret); - } - - if (!min_rate || !max_rate) { - dev_err(rpi->dev, "Unexpected frequency range: min %u, max %u\n", - min_rate, max_rate); - return ERR_PTR(-EINVAL); - } - - dev_info(rpi->dev, "CPU frequency range: min %u, max %u\n", - min_rate, max_rate); - - data->hw.init = &init; - - ret = devm_clk_hw_register(rpi->dev, &data->hw); - if (ret) - return ERR_PTR(ret); - - clk_hw_set_rate_range(&data->hw, - min_rate * RPI_FIRMWARE_PLLB_ARM_DIV_RATE, - max_rate * RPI_FIRMWARE_PLLB_ARM_DIV_RATE); - - return &data->hw; -} - -static struct clk_fixed_factor raspberrypi_clk_pllb_arm = { - .mult = 1, - .div = 2, - .hw.init = &(struct clk_init_data) { - .name = "pllb_arm", - .parent_names = (const char *[]){ "pllb" }, - .num_parents = 1, - .ops = &clk_fixed_factor_ops, - .flags = CLK_SET_RATE_PARENT | CLK_GET_RATE_NOCACHE, - }, -}; - -static struct clk_hw *raspberrypi_register_pllb_arm(struct raspberrypi_clk *rpi) -{ - int ret; - - ret = devm_clk_hw_register(rpi->dev, &raspberrypi_clk_pllb_arm.hw); - if (ret) { - dev_err(rpi->dev, "Failed to initialize pllb_arm\n"); - return ERR_PTR(ret); - } - - ret = devm_clk_hw_register_clkdev(rpi->dev, - &raspberrypi_clk_pllb_arm.hw, - NULL, "cpu0"); - if (ret) { - dev_err(rpi->dev, "Failed to initialize clkdev\n"); - return ERR_PTR(ret); - } - - return &raspberrypi_clk_pllb_arm.hw; -} - static int raspberrypi_fw_dumb_determine_rate(struct clk_hw *hw, struct clk_rate_request *req) { @@ -341,19 +190,6 @@ static struct clk_hw *raspberrypi_clk_register(struct raspberrypi_clk *rpi, u32 min_rate, max_rate; int ret; - if (id == RPI_FIRMWARE_ARM_CLK_ID) { - struct clk_hw *hw; - - hw = raspberrypi_register_pllb(rpi); - if (IS_ERR(hw)) { - dev_err(rpi->dev, "Failed to initialize pllb, %ld\n", - PTR_ERR(hw)); - return hw; - } - - return raspberrypi_register_pllb_arm(rpi); - } - data = devm_kzalloc(rpi->dev, sizeof(*data), GFP_KERNEL); if (!data) return ERR_PTR(-ENOMEM); -- GitLab From c2710fdf935bb1286e8eb6a6b44991bab1fe87af Mon Sep 17 00:00:00 2001 From: Dinh Nguyen Date: Tue, 16 Jun 2020 15:24:15 -0500 Subject: [PATCH 0060/1476] dt-bindings: agilex: add NAND_X_CLK and NAND_ECC_CLK Add the NAND_X_CLK and NAND_ECC_CLK clocks. Signed-off-by: Dinh Nguyen Link: https://lore.kernel.org/r/20200616202417.14376-1-dinguyen@kernel.org Signed-off-by: Stephen Boyd --- include/dt-bindings/clock/agilex-clock.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/include/dt-bindings/clock/agilex-clock.h b/include/dt-bindings/clock/agilex-clock.h index f19cf8ccbdd2e..06feca07e08e1 100644 --- a/include/dt-bindings/clock/agilex-clock.h +++ b/include/dt-bindings/clock/agilex-clock.h @@ -65,6 +65,8 @@ #define AGILEX_SDMMC_CLK 50 #define AGILEX_SPI_M_CLK 51 #define AGILEX_USB_CLK 52 -#define AGILEX_NUM_CLKS 53 +#define AGILEX_NAND_X_CLK 53 +#define AGILEX_NAND_ECC_CLK 54 +#define AGILEX_NUM_CLKS 55 #endif /* __AGILEX_CLOCK_H */ -- GitLab From 6f3bcf56f8eec78e51cac8b3dd67ed7ec67e3e1e Mon Sep 17 00:00:00 2001 From: Dinh Nguyen Date: Tue, 16 Jun 2020 15:24:16 -0500 Subject: [PATCH 0061/1476] clk: socfpga: agilex: add nand_x_clk and nand_ecc_clk And the nand_x_clk and nand_ecc_clk. Make the nand_x_clk be the main clock that is feeding the NAND IP and correct it's parent to be the l4_mp_clk. Signed-off-by: Dinh Nguyen Link: https://lore.kernel.org/r/20200616202417.14376-2-dinguyen@kernel.org Signed-off-by: Stephen Boyd --- drivers/clk/socfpga/clk-agilex.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/clk/socfpga/clk-agilex.c b/drivers/clk/socfpga/clk-agilex.c index 699527f7e7643..1b2e0ad01b244 100644 --- a/drivers/clk/socfpga/clk-agilex.c +++ b/drivers/clk/socfpga/clk-agilex.c @@ -294,8 +294,12 @@ static const struct stratix10_gate_clock agilex_gate_clks[] = { 8, 0, 0, 0, 0, 0, 0}, { AGILEX_SPI_M_CLK, "spi_m_clk", "l4_mp_clk", NULL, 1, 0, 0x7C, 9, 0, 0, 0, 0, 0, 0}, - { AGILEX_NAND_CLK, "nand_clk", "l4_main_clk", NULL, 1, 0, 0x7C, + { AGILEX_NAND_X_CLK, "nand_x_clk", "l4_mp_clk", NULL, 1, 0, 0x7C, 10, 0, 0, 0, 0, 0, 0}, + { AGILEX_NAND_CLK, "nand_clk", "nand_x_clk", NULL, 1, 0, 0x7C, + 10, 0, 0, 0, 0, 0, 4}, + { AGILEX_NAND_ECC_CLK, "nand_ecc_clk", "nand_x_clk", NULL, 1, 0, 0x7C, + 10, 0, 0, 0, 0, 0, 4}, }; static int agilex_clk_register_c_perip(const struct stratix10_perip_c_clock *clks, -- GitLab From 44a7f3e8222a7345b72a83a26d6d599bba815cf9 Mon Sep 17 00:00:00 2001 From: Dinh Nguyen Date: Tue, 16 Jun 2020 15:24:17 -0500 Subject: [PATCH 0062/1476] clk: socfpga: agilex: mpu_l2ram_clk should be mpu_ccu_clk Preliminary documentation documented the mpu_l2ram_clk, but since then, the mpu_l2ram_clk is no longer documented. It's now referred to as mpu_ccu_clk. Signed-off-by: Dinh Nguyen Link: https://lore.kernel.org/r/20200616202417.14376-3-dinguyen@kernel.org Signed-off-by: Stephen Boyd --- drivers/clk/socfpga/clk-agilex.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/clk/socfpga/clk-agilex.c b/drivers/clk/socfpga/clk-agilex.c index 1b2e0ad01b244..8fb12cbe02086 100644 --- a/drivers/clk/socfpga/clk-agilex.c +++ b/drivers/clk/socfpga/clk-agilex.c @@ -252,7 +252,7 @@ static const struct stratix10_gate_clock agilex_gate_clks[] = { 0, 0, 0, 0, 0x30, 0, 0}, { AGILEX_MPU_PERIPH_CLK, "mpu_periph_clk", "mpu_clk", NULL, 1, 0, 0x24, 0, 0, 0, 0, 0, 0, 4}, - { AGILEX_MPU_L2RAM_CLK, "mpu_l2ram_clk", "mpu_clk", NULL, 1, 0, 0x24, + { AGILEX_MPU_CCU_CLK, "mpu_ccu_clk", "mpu_clk", NULL, 1, 0, 0x24, 0, 0, 0, 0, 0, 0, 2}, { AGILEX_L4_MAIN_CLK, "l4_main_clk", "noc_clk", NULL, 1, 0, 0x24, 1, 0x44, 0, 2, 0, 0, 0}, -- GitLab From 89bbe4c798bc3a43c882179adb5222c1a972ac70 Mon Sep 17 00:00:00 2001 From: Nicholas Piggin Date: Thu, 11 Jun 2020 22:11:19 +1000 Subject: [PATCH 0063/1476] powerpc/64: indirect function call use bctrl rather than blrl in ret_from_kernel_thread blrl is not recommended to use as an indirect function call, as it may corrupt the link stack predictor. This is not a performance critical path but this should be fixed for consistency. Signed-off-by: Nicholas Piggin Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20200611121119.1015740-1-npiggin@gmail.com --- arch/powerpc/kernel/entry_64.S | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/powerpc/kernel/entry_64.S b/arch/powerpc/kernel/entry_64.S index 9d49338e0c859..1013adc9acfbf 100644 --- a/arch/powerpc/kernel/entry_64.S +++ b/arch/powerpc/kernel/entry_64.S @@ -233,12 +233,12 @@ _GLOBAL(ret_from_fork) _GLOBAL(ret_from_kernel_thread) bl schedule_tail REST_NVGPRS(r1) - mtlr r14 + mtctr r14 mr r3,r15 #ifdef PPC64_ELF_ABI_v2 mr r12,r14 #endif - blrl + bctrl li r3,0 b .Lsyscall_exit -- GitLab From f39eb5d8ac707fd59029a06c3f985f29b1aaa26b Mon Sep 17 00:00:00 2001 From: Murilo Opsfelder Araujo Date: Wed, 10 Jun 2020 18:51:12 -0300 Subject: [PATCH 0064/1476] powerpc/dt_cpu_ftrs: Remove unused macro ISA_V2_07B Macro ISA_V2_07B is defined but not used anywhere else in the code. Signed-off-by: Murilo Opsfelder Araujo Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20200610215114.167544-2-muriloo@linux.ibm.com --- arch/powerpc/kernel/dt_cpu_ftrs.c | 1 - 1 file changed, 1 deletion(-) diff --git a/arch/powerpc/kernel/dt_cpu_ftrs.c b/arch/powerpc/kernel/dt_cpu_ftrs.c index 3a409517c031d..1b7da8b5ce0dc 100644 --- a/arch/powerpc/kernel/dt_cpu_ftrs.c +++ b/arch/powerpc/kernel/dt_cpu_ftrs.c @@ -24,7 +24,6 @@ /* Device-tree visible constants follow */ -#define ISA_V2_07B 2070 #define ISA_V3_0B 3000 #define ISA_V3_1 3100 -- GitLab From e781f12a60a7bddb50909d42478cca8724c8b113 Mon Sep 17 00:00:00 2001 From: Murilo Opsfelder Araujo Date: Wed, 10 Jun 2020 18:51:13 -0300 Subject: [PATCH 0065/1476] powerpc/dt_cpu_ftrs: Make use of macro ISA_V3_0B Macro ISA_V3_0B was defined but never used. Use it instead of literal. Signed-off-by: Murilo Opsfelder Araujo Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20200610215114.167544-3-muriloo@linux.ibm.com --- arch/powerpc/kernel/dt_cpu_ftrs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/powerpc/kernel/dt_cpu_ftrs.c b/arch/powerpc/kernel/dt_cpu_ftrs.c index 1b7da8b5ce0dc..9d6e833da2bdc 100644 --- a/arch/powerpc/kernel/dt_cpu_ftrs.c +++ b/arch/powerpc/kernel/dt_cpu_ftrs.c @@ -673,7 +673,7 @@ static void __init cpufeatures_setup_start(u32 isa) { pr_info("setup for ISA %d\n", isa); - if (isa >= 3000) { + if (isa >= ISA_V3_0B) { cur_cpu_spec->cpu_features |= CPU_FTR_ARCH_300; cur_cpu_spec->cpu_user_features2 |= PPC_FEATURE2_ARCH_3_00; } -- GitLab From 7714394706c0309b3f3fc474b390463d60eb6cb1 Mon Sep 17 00:00:00 2001 From: Murilo Opsfelder Araujo Date: Wed, 10 Jun 2020 18:51:14 -0300 Subject: [PATCH 0066/1476] powerpc/dt_cpu_ftrs: Make use of macro ISA_V3_1 Macro ISA_V3_1 was defined but never used. Use it instead of literal. Signed-off-by: Murilo Opsfelder Araujo Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20200610215114.167544-4-muriloo@linux.ibm.com --- arch/powerpc/kernel/dt_cpu_ftrs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/powerpc/kernel/dt_cpu_ftrs.c b/arch/powerpc/kernel/dt_cpu_ftrs.c index 9d6e833da2bdc..a0edeb391e3e4 100644 --- a/arch/powerpc/kernel/dt_cpu_ftrs.c +++ b/arch/powerpc/kernel/dt_cpu_ftrs.c @@ -678,7 +678,7 @@ static void __init cpufeatures_setup_start(u32 isa) cur_cpu_spec->cpu_user_features2 |= PPC_FEATURE2_ARCH_3_00; } - if (isa >= 3100) { + if (isa >= ISA_V3_1) { cur_cpu_spec->cpu_features |= CPU_FTR_ARCH_31; cur_cpu_spec->cpu_user_features2 |= PPC_FEATURE2_ARCH_3_1; } -- GitLab From f134a7cef1d7de45fab028a945b7f34c615718e1 Mon Sep 17 00:00:00 2001 From: Chris Packham Date: Fri, 12 Jun 2020 10:42:19 +1200 Subject: [PATCH 0067/1476] powerpc: Remove inaccessible CMDLINE default Since commit cbe46bd4f510 ("powerpc: remove CONFIG_CMDLINE #ifdef mess") CONFIG_CMDLINE has always had a value regardless of CONFIG_CMDLINE_BOOL. For example: $ make ARCH=powerpc defconfig $ cat .config # CONFIG_CMDLINE_BOOL is not set CONFIG_CMDLINE="" When enabling CONFIG_CMDLINE_BOOL this value is kept making the 'default "..." if CONFIG_CMDLINE_BOOL' ineffective. $ ./scripts/config --enable CONFIG_CMDLINE_BOOL $ cat .config CONFIG_CMDLINE_BOOL=y CONFIG_CMDLINE="" Remove CONFIG_CMDLINE_BOOL and the inaccessible default. Signed-off-by: Chris Packham Reviewed-by: Christophe Leroy Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20200611224220.25066-2-chris.packham@alliedtelesis.co.nz --- arch/powerpc/Kconfig | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/arch/powerpc/Kconfig b/arch/powerpc/Kconfig index 9fa23eb320ff5..51abc59c3334e 100644 --- a/arch/powerpc/Kconfig +++ b/arch/powerpc/Kconfig @@ -859,12 +859,8 @@ config PPC_DENORMALISATION Add support for handling denormalisation of single precision values. Useful for bare metal only. If unsure say Y here. -config CMDLINE_BOOL - bool "Default bootloader kernel arguments" - config CMDLINE - string "Initial kernel command string" if CMDLINE_BOOL - default "console=ttyS0,9600 console=tty0 root=/dev/sda2" if CMDLINE_BOOL + string "Initial kernel command string" default "" help On some platforms, there is currently no way for the boot loader to -- GitLab From 0488d32530ecff973c40f2592a6eab2907d0a5cc Mon Sep 17 00:00:00 2001 From: Chris Packham Date: Fri, 12 Jun 2020 10:42:20 +1200 Subject: [PATCH 0068/1476] powerpc/configs: Remove CMDLINE_BOOL Regenerate defconfigs to remove CONFIG_CMDLINE_BOOL and the default CONFIG_CMDLINE where applicable. Signed-off-by: Chris Packham Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20200611224220.25066-3-chris.packham@alliedtelesis.co.nz --- arch/powerpc/configs/44x/akebono_defconfig | 2 -- arch/powerpc/configs/44x/arches_defconfig | 2 -- arch/powerpc/configs/44x/bamboo_defconfig | 2 -- arch/powerpc/configs/44x/bluestone_defconfig | 2 -- arch/powerpc/configs/44x/canyonlands_defconfig | 2 -- arch/powerpc/configs/44x/currituck_defconfig | 2 -- arch/powerpc/configs/44x/eiger_defconfig | 2 -- arch/powerpc/configs/44x/fsp2_defconfig | 1 - arch/powerpc/configs/44x/icon_defconfig | 2 -- arch/powerpc/configs/44x/iss476-smp_defconfig | 1 - arch/powerpc/configs/44x/katmai_defconfig | 2 -- arch/powerpc/configs/44x/rainier_defconfig | 2 -- arch/powerpc/configs/44x/redwood_defconfig | 2 -- arch/powerpc/configs/44x/sam440ep_defconfig | 2 -- arch/powerpc/configs/44x/sequoia_defconfig | 2 -- arch/powerpc/configs/44x/taishan_defconfig | 2 -- arch/powerpc/configs/44x/warp_defconfig | 1 - arch/powerpc/configs/holly_defconfig | 1 - arch/powerpc/configs/mvme5100_defconfig | 3 +-- arch/powerpc/configs/ps3_defconfig | 2 -- arch/powerpc/configs/skiroot_defconfig | 1 - arch/powerpc/configs/storcenter_defconfig | 1 - 22 files changed, 1 insertion(+), 38 deletions(-) diff --git a/arch/powerpc/configs/44x/akebono_defconfig b/arch/powerpc/configs/44x/akebono_defconfig index 7705a5c3f4eaa..60d5fa2c3b930 100644 --- a/arch/powerpc/configs/44x/akebono_defconfig +++ b/arch/powerpc/configs/44x/akebono_defconfig @@ -19,8 +19,6 @@ CONFIG_HIGHMEM=y CONFIG_HZ_100=y CONFIG_IRQ_ALL_CPUS=y # CONFIG_COMPACTION is not set -CONFIG_CMDLINE_BOOL=y -CONFIG_CMDLINE="" # CONFIG_SUSPEND is not set CONFIG_NET=y CONFIG_PACKET=y diff --git a/arch/powerpc/configs/44x/arches_defconfig b/arch/powerpc/configs/44x/arches_defconfig index 82c6f49b8dcb1..41d04e70d4fbc 100644 --- a/arch/powerpc/configs/44x/arches_defconfig +++ b/arch/powerpc/configs/44x/arches_defconfig @@ -11,8 +11,6 @@ CONFIG_MODULE_UNLOAD=y # CONFIG_BLK_DEV_BSG is not set # CONFIG_EBONY is not set CONFIG_ARCHES=y -CONFIG_CMDLINE_BOOL=y -CONFIG_CMDLINE="" CONFIG_NET=y CONFIG_PACKET=y CONFIG_UNIX=y diff --git a/arch/powerpc/configs/44x/bamboo_defconfig b/arch/powerpc/configs/44x/bamboo_defconfig index 679213214a754..acbce718eaa80 100644 --- a/arch/powerpc/configs/44x/bamboo_defconfig +++ b/arch/powerpc/configs/44x/bamboo_defconfig @@ -9,8 +9,6 @@ CONFIG_MODULE_UNLOAD=y # CONFIG_BLK_DEV_BSG is not set CONFIG_BAMBOO=y # CONFIG_EBONY is not set -CONFIG_CMDLINE_BOOL=y -CONFIG_CMDLINE="" CONFIG_NET=y CONFIG_PACKET=y CONFIG_UNIX=y diff --git a/arch/powerpc/configs/44x/bluestone_defconfig b/arch/powerpc/configs/44x/bluestone_defconfig index 8006a5728afd0..37088f250c9e7 100644 --- a/arch/powerpc/configs/44x/bluestone_defconfig +++ b/arch/powerpc/configs/44x/bluestone_defconfig @@ -11,8 +11,6 @@ CONFIG_EXPERT=y # CONFIG_COMPAT_BRK is not set CONFIG_BLUESTONE=y # CONFIG_EBONY is not set -CONFIG_CMDLINE_BOOL=y -CONFIG_CMDLINE="" CONFIG_NET=y CONFIG_PACKET=y CONFIG_UNIX=y diff --git a/arch/powerpc/configs/44x/canyonlands_defconfig b/arch/powerpc/configs/44x/canyonlands_defconfig index ccc14eb7a2f12..61776ade572b0 100644 --- a/arch/powerpc/configs/44x/canyonlands_defconfig +++ b/arch/powerpc/configs/44x/canyonlands_defconfig @@ -11,8 +11,6 @@ CONFIG_MODULE_UNLOAD=y # CONFIG_BLK_DEV_BSG is not set # CONFIG_EBONY is not set CONFIG_CANYONLANDS=y -CONFIG_CMDLINE_BOOL=y -CONFIG_CMDLINE="" CONFIG_NET=y CONFIG_PACKET=y CONFIG_UNIX=y diff --git a/arch/powerpc/configs/44x/currituck_defconfig b/arch/powerpc/configs/44x/currituck_defconfig index be76e066df016..34c86b3abecb5 100644 --- a/arch/powerpc/configs/44x/currituck_defconfig +++ b/arch/powerpc/configs/44x/currituck_defconfig @@ -17,8 +17,6 @@ CONFIG_HIGHMEM=y CONFIG_HZ_100=y CONFIG_MATH_EMULATION=y CONFIG_IRQ_ALL_CPUS=y -CONFIG_CMDLINE_BOOL=y -CONFIG_CMDLINE="" # CONFIG_SUSPEND is not set CONFIG_NET=y CONFIG_PACKET=y diff --git a/arch/powerpc/configs/44x/eiger_defconfig b/arch/powerpc/configs/44x/eiger_defconfig index 1abaa63e067f0..509300f400e2e 100644 --- a/arch/powerpc/configs/44x/eiger_defconfig +++ b/arch/powerpc/configs/44x/eiger_defconfig @@ -10,8 +10,6 @@ CONFIG_MODULES=y CONFIG_MODULE_UNLOAD=y # CONFIG_EBONY is not set CONFIG_EIGER=y -CONFIG_CMDLINE_BOOL=y -CONFIG_CMDLINE="" CONFIG_PCIEPORTBUS=y # CONFIG_PCIEASPM is not set CONFIG_NET=y diff --git a/arch/powerpc/configs/44x/fsp2_defconfig b/arch/powerpc/configs/44x/fsp2_defconfig index e67fc041ca3e8..30845ce0885a3 100644 --- a/arch/powerpc/configs/44x/fsp2_defconfig +++ b/arch/powerpc/configs/44x/fsp2_defconfig @@ -28,7 +28,6 @@ CONFIG_476FPE_ERR46=y CONFIG_SWIOTLB=y CONFIG_KEXEC=y CONFIG_CRASH_DUMP=y -CONFIG_CMDLINE_BOOL=y CONFIG_CMDLINE="ip=on rw" # CONFIG_SUSPEND is not set # CONFIG_PCI is not set diff --git a/arch/powerpc/configs/44x/icon_defconfig b/arch/powerpc/configs/44x/icon_defconfig index 7d7ff84c82000..930948a1da762 100644 --- a/arch/powerpc/configs/44x/icon_defconfig +++ b/arch/powerpc/configs/44x/icon_defconfig @@ -9,8 +9,6 @@ CONFIG_MODULE_UNLOAD=y # CONFIG_EBONY is not set CONFIG_ICON=y CONFIG_HIGHMEM=y -CONFIG_CMDLINE_BOOL=y -CONFIG_CMDLINE="" CONFIG_PCIEPORTBUS=y # CONFIG_PCIEASPM is not set CONFIG_NET=y diff --git a/arch/powerpc/configs/44x/iss476-smp_defconfig b/arch/powerpc/configs/44x/iss476-smp_defconfig index fb5c73a29bf42..2c3834eebca3f 100644 --- a/arch/powerpc/configs/44x/iss476-smp_defconfig +++ b/arch/powerpc/configs/44x/iss476-smp_defconfig @@ -17,7 +17,6 @@ CONFIG_ISS4xx=y CONFIG_HZ_100=y CONFIG_MATH_EMULATION=y CONFIG_IRQ_ALL_CPUS=y -CONFIG_CMDLINE_BOOL=y CONFIG_CMDLINE="root=/dev/issblk0" # CONFIG_PCI is not set CONFIG_ADVANCED_OPTIONS=y diff --git a/arch/powerpc/configs/44x/katmai_defconfig b/arch/powerpc/configs/44x/katmai_defconfig index c6dc1445fc043..1a0f1c3e0ee9b 100644 --- a/arch/powerpc/configs/44x/katmai_defconfig +++ b/arch/powerpc/configs/44x/katmai_defconfig @@ -9,8 +9,6 @@ CONFIG_MODULE_UNLOAD=y # CONFIG_BLK_DEV_BSG is not set # CONFIG_EBONY is not set CONFIG_KATMAI=y -CONFIG_CMDLINE_BOOL=y -CONFIG_CMDLINE="" CONFIG_NET=y CONFIG_PACKET=y CONFIG_UNIX=y diff --git a/arch/powerpc/configs/44x/rainier_defconfig b/arch/powerpc/configs/44x/rainier_defconfig index c83ad03182df7..6dd67de06a0b5 100644 --- a/arch/powerpc/configs/44x/rainier_defconfig +++ b/arch/powerpc/configs/44x/rainier_defconfig @@ -10,8 +10,6 @@ CONFIG_MODULE_UNLOAD=y # CONFIG_EBONY is not set CONFIG_RAINIER=y CONFIG_MATH_EMULATION=y -CONFIG_CMDLINE_BOOL=y -CONFIG_CMDLINE="" CONFIG_NET=y CONFIG_PACKET=y CONFIG_UNIX=y diff --git a/arch/powerpc/configs/44x/redwood_defconfig b/arch/powerpc/configs/44x/redwood_defconfig index 640fe1d5af282..e28d764165374 100644 --- a/arch/powerpc/configs/44x/redwood_defconfig +++ b/arch/powerpc/configs/44x/redwood_defconfig @@ -10,8 +10,6 @@ CONFIG_MODULES=y CONFIG_MODULE_UNLOAD=y # CONFIG_EBONY is not set CONFIG_REDWOOD=y -CONFIG_CMDLINE_BOOL=y -CONFIG_CMDLINE="" CONFIG_PCIEPORTBUS=y # CONFIG_PCIEASPM is not set CONFIG_NET=y diff --git a/arch/powerpc/configs/44x/sam440ep_defconfig b/arch/powerpc/configs/44x/sam440ep_defconfig index 22dc0dadf5762..ef09786d49b93 100644 --- a/arch/powerpc/configs/44x/sam440ep_defconfig +++ b/arch/powerpc/configs/44x/sam440ep_defconfig @@ -12,8 +12,6 @@ CONFIG_PARTITION_ADVANCED=y CONFIG_AMIGA_PARTITION=y # CONFIG_EBONY is not set CONFIG_SAM440EP=y -CONFIG_CMDLINE_BOOL=y -CONFIG_CMDLINE="" CONFIG_NET=y CONFIG_PACKET=y CONFIG_UNIX=y diff --git a/arch/powerpc/configs/44x/sequoia_defconfig b/arch/powerpc/configs/44x/sequoia_defconfig index 2c0973db88373..b4984eab43eb3 100644 --- a/arch/powerpc/configs/44x/sequoia_defconfig +++ b/arch/powerpc/configs/44x/sequoia_defconfig @@ -11,8 +11,6 @@ CONFIG_MODULE_UNLOAD=y # CONFIG_BLK_DEV_BSG is not set # CONFIG_EBONY is not set CONFIG_SEQUOIA=y -CONFIG_CMDLINE_BOOL=y -CONFIG_CMDLINE="" CONFIG_NET=y CONFIG_PACKET=y CONFIG_UNIX=y diff --git a/arch/powerpc/configs/44x/taishan_defconfig b/arch/powerpc/configs/44x/taishan_defconfig index a2d355ca62b28..3ea5932ab852c 100644 --- a/arch/powerpc/configs/44x/taishan_defconfig +++ b/arch/powerpc/configs/44x/taishan_defconfig @@ -9,8 +9,6 @@ CONFIG_MODULE_UNLOAD=y # CONFIG_BLK_DEV_BSG is not set # CONFIG_EBONY is not set CONFIG_TAISHAN=y -CONFIG_CMDLINE_BOOL=y -CONFIG_CMDLINE="" CONFIG_NET=y CONFIG_PACKET=y CONFIG_UNIX=y diff --git a/arch/powerpc/configs/44x/warp_defconfig b/arch/powerpc/configs/44x/warp_defconfig index af66c69c49fe4..47252c2d7669a 100644 --- a/arch/powerpc/configs/44x/warp_defconfig +++ b/arch/powerpc/configs/44x/warp_defconfig @@ -14,7 +14,6 @@ CONFIG_MODULE_UNLOAD=y CONFIG_WARP=y CONFIG_PPC4xx_GPIO=y CONFIG_HZ_1000=y -CONFIG_CMDLINE_BOOL=y CONFIG_CMDLINE="ip=on" # CONFIG_PCI is not set CONFIG_NET=y diff --git a/arch/powerpc/configs/holly_defconfig b/arch/powerpc/configs/holly_defconfig index 067f433c8f5e2..271daff47d1d7 100644 --- a/arch/powerpc/configs/holly_defconfig +++ b/arch/powerpc/configs/holly_defconfig @@ -13,7 +13,6 @@ CONFIG_EMBEDDED6xx=y CONFIG_PPC_HOLLY=y CONFIG_GEN_RTC=y CONFIG_BINFMT_MISC=y -CONFIG_CMDLINE_BOOL=y CONFIG_CMDLINE="console=ttyS0,115200" # CONFIG_SECCOMP is not set CONFIG_NET=y diff --git a/arch/powerpc/configs/mvme5100_defconfig b/arch/powerpc/configs/mvme5100_defconfig index 0a0d046fc445a..3d53d69ed36c2 100644 --- a/arch/powerpc/configs/mvme5100_defconfig +++ b/arch/powerpc/configs/mvme5100_defconfig @@ -20,10 +20,9 @@ CONFIG_EMBEDDED6xx=y CONFIG_MVME5100=y CONFIG_KVM_GUEST=y CONFIG_HZ_100=y +CONFIG_CMDLINE="console=ttyS0,9600 ip=dhcp root=/dev/nfs" # CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set # CONFIG_COMPACTION is not set -CONFIG_CMDLINE_BOOL=y -CONFIG_CMDLINE="console=ttyS0,9600 ip=dhcp root=/dev/nfs" CONFIG_NET=y CONFIG_PACKET=y CONFIG_UNIX=y diff --git a/arch/powerpc/configs/ps3_defconfig b/arch/powerpc/configs/ps3_defconfig index 81b55c880fc3d..142f1321fa58b 100644 --- a/arch/powerpc/configs/ps3_defconfig +++ b/arch/powerpc/configs/ps3_defconfig @@ -34,8 +34,6 @@ CONFIG_KEXEC=y # CONFIG_SPARSEMEM_VMEMMAP is not set # CONFIG_COMPACTION is not set CONFIG_SCHED_SMT=y -CONFIG_CMDLINE_BOOL=y -CONFIG_CMDLINE="" CONFIG_PM=y CONFIG_PM_DEBUG=y # CONFIG_SECCOMP is not set diff --git a/arch/powerpc/configs/skiroot_defconfig b/arch/powerpc/configs/skiroot_defconfig index ad6739ac63dc2..b806a5d3a695d 100644 --- a/arch/powerpc/configs/skiroot_defconfig +++ b/arch/powerpc/configs/skiroot_defconfig @@ -45,7 +45,6 @@ CONFIG_IRQ_ALL_CPUS=y CONFIG_NUMA=y CONFIG_PPC_64K_PAGES=y CONFIG_SCHED_SMT=y -CONFIG_CMDLINE_BOOL=y CONFIG_CMDLINE="console=tty0 console=hvc0 ipr.fast_reboot=1 quiet" # CONFIG_SECCOMP is not set # CONFIG_PPC_MEM_KEYS is not set diff --git a/arch/powerpc/configs/storcenter_defconfig b/arch/powerpc/configs/storcenter_defconfig index b964084e40568..47dcfaddc1aca 100644 --- a/arch/powerpc/configs/storcenter_defconfig +++ b/arch/powerpc/configs/storcenter_defconfig @@ -12,7 +12,6 @@ CONFIG_EMBEDDED6xx=y CONFIG_STORCENTER=y CONFIG_HZ_100=y CONFIG_BINFMT_MISC=y -CONFIG_CMDLINE_BOOL=y CONFIG_CMDLINE="console=ttyS0,115200" # CONFIG_SECCOMP is not set CONFIG_NET=y -- GitLab From f0993c839e95dd6c7f054a1015e693c87e33e4fb Mon Sep 17 00:00:00 2001 From: Alexey Kardashevskiy Date: Fri, 12 Jun 2020 14:33:03 +1000 Subject: [PATCH 0069/1476] powerpc/xive: Ignore kmemleak false positives xive_native_provision_pages() allocates memory and passes the pointer to OPAL so kmemleak cannot find the pointer usage in the kernel memory and produces a false positive report (below) (even if the kernel did scan OPAL memory, it is unable to deal with __pa() addresses anyway). This silences the warning. unreferenced object 0xc000200350c40000 (size 65536): comm "qemu-system-ppc", pid 2725, jiffies 4294946414 (age 70776.530s) hex dump (first 32 bytes): 02 00 00 00 50 00 00 00 00 00 00 00 00 00 00 00 ....P........... 01 00 08 07 00 00 00 00 00 00 00 00 00 00 00 00 ................ backtrace: [<0000000081ff046c>] xive_native_alloc_vp_block+0x120/0x250 [<00000000d555d524>] kvmppc_xive_compute_vp_id+0x248/0x350 [kvm] [<00000000d69b9c9f>] kvmppc_xive_connect_vcpu+0xc0/0x520 [kvm] [<000000006acbc81c>] kvm_arch_vcpu_ioctl+0x308/0x580 [kvm] [<0000000089c69580>] kvm_vcpu_ioctl+0x19c/0xae0 [kvm] [<00000000902ae91e>] ksys_ioctl+0x184/0x1b0 [<00000000f3e68bd7>] sys_ioctl+0x48/0xb0 [<0000000001b2c127>] system_call_exception+0x124/0x1f0 [<00000000d2b2ee40>] system_call_common+0xe8/0x214 Signed-off-by: Alexey Kardashevskiy Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20200612043303.84894-1-aik@ozlabs.ru --- arch/powerpc/sysdev/xive/native.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/arch/powerpc/sysdev/xive/native.c b/arch/powerpc/sysdev/xive/native.c index 71b881e554fcb..cb58ec7ce77ac 100644 --- a/arch/powerpc/sysdev/xive/native.c +++ b/arch/powerpc/sysdev/xive/native.c @@ -18,6 +18,7 @@ #include #include #include +#include #include #include @@ -647,6 +648,7 @@ static bool xive_native_provision_pages(void) pr_err("Failed to allocate provisioning page\n"); return false; } + kmemleak_ignore(p); opal_xive_donate_page(chip, __pa(p)); } return true; -- GitLab From 55bd9ac468397c4f12a33b7ec714b5d0362c3aa2 Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Fri, 5 Jun 2020 07:18:06 -0700 Subject: [PATCH 0070/1476] powerpc/mm: Fix typo in IS_ENABLED() IS_ENABLED() matches names exactly, so the missing "CONFIG_" prefix means this code would never be built. Also fixes a missing newline in pr_warn(). Fixes: 970d54f99cea ("powerpc/book3s64/hash: Disable 16M linear mapping size if not aligned") Signed-off-by: Joe Perches Signed-off-by: Kees Cook Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/202006050717.A2F9809E@keescook --- arch/powerpc/mm/book3s64/hash_utils.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/arch/powerpc/mm/book3s64/hash_utils.c b/arch/powerpc/mm/book3s64/hash_utils.c index 468169e33c86f..084287fb29081 100644 --- a/arch/powerpc/mm/book3s64/hash_utils.c +++ b/arch/powerpc/mm/book3s64/hash_utils.c @@ -663,11 +663,10 @@ static void __init htab_init_page_sizes(void) * Pick a size for the linear mapping. Currently, we only * support 16M, 1M and 4K which is the default */ - if (IS_ENABLED(STRICT_KERNEL_RWX) && + if (IS_ENABLED(CONFIG_STRICT_KERNEL_RWX) && (unsigned long)_stext % 0x1000000) { if (mmu_psize_defs[MMU_PAGE_16M].shift) - pr_warn("Kernel not 16M aligned, " - "disabling 16M linear map alignment"); + pr_warn("Kernel not 16M aligned, disabling 16M linear map alignment\n"); aligned = false; } -- GitLab From 7c466b0807960edc13e4b855be85ea765df9a6cd Mon Sep 17 00:00:00 2001 From: Christophe Leroy Date: Mon, 15 Jun 2020 13:18:39 +0000 Subject: [PATCH 0071/1476] powerpc/ptdump: Fix build failure in hashpagetable.c H_SUCCESS is only defined when CONFIG_PPC_PSERIES is defined. != H_SUCCESS means != 0. Modify the test accordingly. Fixes: 65e701b2d2a8 ("powerpc/ptdump: drop non vital #ifdefs") Cc: stable@vger.kernel.org Reported-by: kernel test robot Signed-off-by: Christophe Leroy Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/795158fc1d2b3dff3bf7347881947a887ea9391a.1592227105.git.christophe.leroy@csgroup.eu --- arch/powerpc/mm/ptdump/hashpagetable.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/powerpc/mm/ptdump/hashpagetable.c b/arch/powerpc/mm/ptdump/hashpagetable.c index a2c33efc7ce8d..5b8bd34cd3a16 100644 --- a/arch/powerpc/mm/ptdump/hashpagetable.c +++ b/arch/powerpc/mm/ptdump/hashpagetable.c @@ -258,7 +258,7 @@ static int pseries_find(unsigned long ea, int psize, bool primary, u64 *v, u64 * for (i = 0; i < HPTES_PER_GROUP; i += 4, hpte_group += 4) { lpar_rc = plpar_pte_read_4(0, hpte_group, (void *)ptes); - if (lpar_rc != H_SUCCESS) + if (lpar_rc) continue; for (j = 0; j < 4; j++) { if (HPTE_V_COMPARE(ptes[j].v, want_v) && -- GitLab From 178748b6d14946f080d49bc7dcc47b7cc4437e4d Mon Sep 17 00:00:00 2001 From: Satheesh Rajendran Date: Fri, 12 Jun 2020 19:59:53 +0530 Subject: [PATCH 0072/1476] powerpc/pseries/svm: Drop unused align argument in alloc_shared_lppaca() function Argument "align" in alloc_shared_lppaca() was unused inside the function. Let's drop it and update code comment for page alignment. Signed-off-by: Satheesh Rajendran Reviewed-by: Thiago Jung Bauermann Reviewed-by: Laurent Dufour [mpe: Massage comment wording/formatting] Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20200612142953.135408-1-sathnaga@linux.vnet.ibm.com --- arch/powerpc/kernel/paca.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/arch/powerpc/kernel/paca.c b/arch/powerpc/kernel/paca.c index 2168372b792d7..5239a421b9dfb 100644 --- a/arch/powerpc/kernel/paca.c +++ b/arch/powerpc/kernel/paca.c @@ -57,8 +57,8 @@ static void *__init alloc_paca_data(unsigned long size, unsigned long align, #define LPPACA_SIZE 0x400 -static void *__init alloc_shared_lppaca(unsigned long size, unsigned long align, - unsigned long limit, int cpu) +static void *__init alloc_shared_lppaca(unsigned long size, unsigned long limit, + int cpu) { size_t shared_lppaca_total_size = PAGE_ALIGN(nr_cpu_ids * LPPACA_SIZE); static unsigned long shared_lppaca_size; @@ -68,6 +68,13 @@ static void *__init alloc_shared_lppaca(unsigned long size, unsigned long align, if (!shared_lppaca) { memblock_set_bottom_up(true); + /* + * See Documentation/powerpc/ultravisor.rst for more details. + * + * UV/HV data sharing is in PAGE_SIZE granularity. In order to + * minimize the number of pages shared, align the allocation to + * PAGE_SIZE. + */ shared_lppaca = memblock_alloc_try_nid(shared_lppaca_total_size, PAGE_SIZE, MEMBLOCK_LOW_LIMIT, @@ -122,7 +129,7 @@ static struct lppaca * __init new_lppaca(int cpu, unsigned long limit) return NULL; if (is_secure_guest()) - lp = alloc_shared_lppaca(LPPACA_SIZE, 0x400, limit, cpu); + lp = alloc_shared_lppaca(LPPACA_SIZE, limit, cpu); else lp = alloc_paca_data(LPPACA_SIZE, 0x400, limit, cpu); -- GitLab From 5f202c1a1d429bee3ddab647711f181c72d157da Mon Sep 17 00:00:00 2001 From: Alexey Kardashevskiy Date: Wed, 17 Jun 2020 10:38:35 +1000 Subject: [PATCH 0073/1476] powerpc/powernv/ioda: Return correct error if TCE level allocation failed The iommu_table_ops::xchg_no_kill() callback updates TCE. It is quite possible that not entire table is allocated if it is huge and multilevel so xchg may also allocate subtables. If failed, it returns H_HARDWARE for failed allocation and H_TOO_HARD if it needs it but cannot do because the alloc parameter is "false" (set when called with MMU=off to force retry with MMU=on). The problem is that having separate errors only matters in real mode (MMU=off) but the only caller with alloc="false" does not check the exact error code and simply returns H_TOO_HARD; and for every other mode alloc is "true". Also, the function is also called from the ioctl() handler of the VFIO SPAPR TCE IOMMU subdriver which does not expect hypervisor error codes (H_xxx) and will expose them to the userspace. This converts wrong error codes to -ENOMEM. Signed-off-by: Alexey Kardashevskiy Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20200617003835.48831-1-aik@ozlabs.ru --- arch/powerpc/platforms/powernv/pci-ioda-tce.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/powerpc/platforms/powernv/pci-ioda-tce.c b/arch/powerpc/platforms/powernv/pci-ioda-tce.c index f923359d8afc9..5218f5da27370 100644 --- a/arch/powerpc/platforms/powernv/pci-ioda-tce.c +++ b/arch/powerpc/platforms/powernv/pci-ioda-tce.c @@ -166,7 +166,7 @@ int pnv_tce_xchg(struct iommu_table *tbl, long index, if (!ptce) { ptce = pnv_tce(tbl, false, idx, alloc); if (!ptce) - return alloc ? H_HARDWARE : H_TOO_HARD; + return -ENOMEM; } if (newtce & TCE_PCI_WRITE) -- GitLab From 01bd294642841998c76d9e6929597dcb7da9466a Mon Sep 17 00:00:00 2001 From: Jordan Niethe Date: Wed, 20 May 2020 12:11:02 +1000 Subject: [PATCH 0074/1476] selftests/powerpc: Allow choice of CI memory location in alignment_handler test The alignment handler selftest needs cache-inhibited memory and currently /dev/fb0 is relied on to provided this. This prevents running the test on systems without /dev/fb0 (e.g., mambo). Read the commandline arguments for an optional path to be used instead, as well as an optional offset to be for mmaping this path. Signed-off-by: Jordan Niethe Tested-by: Alistair Popple Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20200520021103.19798-1-jniethe5@gmail.com --- .../powerpc/alignment/alignment_handler.c | 63 ++++++++++++------- 1 file changed, 42 insertions(+), 21 deletions(-) diff --git a/tools/testing/selftests/powerpc/alignment/alignment_handler.c b/tools/testing/selftests/powerpc/alignment/alignment_handler.c index 0453c50c949cb..eb6aba323f8bc 100644 --- a/tools/testing/selftests/powerpc/alignment/alignment_handler.c +++ b/tools/testing/selftests/powerpc/alignment/alignment_handler.c @@ -9,7 +9,17 @@ * This selftest exercises the powerpc alignment fault handler. * * We create two sets of source and destination buffers, one in regular memory, - * the other cache-inhibited (we use /dev/fb0 for this). + * the other cache-inhibited (by default we use /dev/fb0 for this, but an + * alterative path for cache-inhibited memory may be provided). + * + * One way to get cache-inhibited memory is to use the "mem" kernel parameter + * to limit the kernel to less memory than actually exists. Addresses above + * the limit may still be accessed but will be treated as cache-inhibited. For + * example, if there is actually 4GB of memory and the parameter "mem=3GB" is + * used, memory from address 0xC0000000 onwards is treated as cache-inhibited. + * To access this region /dev/mem is used. The kernel should be configured + * without CONFIG_STRICT_DEVMEM. In this case use: + * ./alignment_handler /dev/mem 0xc0000000 * * We initialise the source buffers, then use whichever set of load/store * instructions is under test to copy bytes from the source buffers to the @@ -53,6 +63,8 @@ int bufsize; int debug; int testing; volatile int gotsig; +char *cipath = "/dev/fb0"; +long cioffset; void sighandler(int sig, siginfo_t *info, void *ctx) { @@ -195,17 +207,18 @@ int do_test(char *test_name, void (*test_func)(char *, char *)) printf("\tDoing %s:\t", test_name); - fd = open("/dev/fb0", O_RDWR); + fd = open(cipath, O_RDWR); if (fd < 0) { printf("\n"); - perror("Can't open /dev/fb0 now?"); + perror("Can't open ci file now?"); return 1; } - ci0 = mmap(NULL, bufsize, PROT_WRITE, MAP_SHARED, - fd, 0x0); - ci1 = mmap(NULL, bufsize, PROT_WRITE, MAP_SHARED, - fd, bufsize); + ci0 = mmap(NULL, bufsize, PROT_WRITE | PROT_READ, MAP_SHARED, + fd, cioffset); + ci1 = mmap(NULL, bufsize, PROT_WRITE | PROT_READ, MAP_SHARED, + fd, cioffset + bufsize); + if ((ci0 == MAP_FAILED) || (ci1 == MAP_FAILED)) { printf("\n"); perror("mmap failed"); @@ -270,11 +283,11 @@ int do_test(char *test_name, void (*test_func)(char *, char *)) return rc; } -static bool can_open_fb0(void) +static bool can_open_cifile(void) { int fd; - fd = open("/dev/fb0", O_RDWR); + fd = open(cipath, O_RDWR); if (fd < 0) return false; @@ -286,7 +299,7 @@ int test_alignment_handler_vsx_206(void) { int rc = 0; - SKIP_IF(!can_open_fb0()); + SKIP_IF(!can_open_cifile()); SKIP_IF(!have_hwcap(PPC_FEATURE_ARCH_2_06)); printf("VSX: 2.06B\n"); @@ -304,7 +317,7 @@ int test_alignment_handler_vsx_207(void) { int rc = 0; - SKIP_IF(!can_open_fb0()); + SKIP_IF(!can_open_cifile()); SKIP_IF(!have_hwcap2(PPC_FEATURE2_ARCH_2_07)); printf("VSX: 2.07B\n"); @@ -320,7 +333,7 @@ int test_alignment_handler_vsx_300(void) { int rc = 0; - SKIP_IF(!can_open_fb0()); + SKIP_IF(!can_open_cifile()); SKIP_IF(!have_hwcap2(PPC_FEATURE2_ARCH_3_00)); printf("VSX: 3.00B\n"); @@ -352,7 +365,7 @@ int test_alignment_handler_integer(void) { int rc = 0; - SKIP_IF(!can_open_fb0()); + SKIP_IF(!can_open_cifile()); printf("Integer\n"); LOAD_DFORM_TEST(lbz); @@ -408,7 +421,7 @@ int test_alignment_handler_integer_206(void) { int rc = 0; - SKIP_IF(!can_open_fb0()); + SKIP_IF(!can_open_cifile()); SKIP_IF(!have_hwcap(PPC_FEATURE_ARCH_2_06)); printf("Integer: 2.06\n"); @@ -423,7 +436,7 @@ int test_alignment_handler_vmx(void) { int rc = 0; - SKIP_IF(!can_open_fb0()); + SKIP_IF(!can_open_cifile()); SKIP_IF(!have_hwcap(PPC_FEATURE_HAS_ALTIVEC)); printf("VMX\n"); @@ -451,7 +464,7 @@ int test_alignment_handler_fp(void) { int rc = 0; - SKIP_IF(!can_open_fb0()); + SKIP_IF(!can_open_cifile()); printf("Floating point\n"); LOAD_FLOAT_DFORM_TEST(lfd); @@ -479,7 +492,7 @@ int test_alignment_handler_fp_205(void) { int rc = 0; - SKIP_IF(!can_open_fb0()); + SKIP_IF(!can_open_cifile()); SKIP_IF(!have_hwcap(PPC_FEATURE_ARCH_2_05)); printf("Floating point: 2.05\n"); @@ -497,7 +510,7 @@ int test_alignment_handler_fp_206(void) { int rc = 0; - SKIP_IF(!can_open_fb0()); + SKIP_IF(!can_open_cifile()); SKIP_IF(!have_hwcap(PPC_FEATURE_ARCH_2_06)); printf("Floating point: 2.06\n"); @@ -509,11 +522,12 @@ int test_alignment_handler_fp_206(void) void usage(char *prog) { - printf("Usage: %s [options]\n", prog); + printf("Usage: %s [options] [path [offset]]\n", prog); printf(" -d Enable debug error output\n"); printf("\n"); - printf("This test requires a POWER8 or POWER9 CPU and a usable "); - printf("framebuffer at /dev/fb0.\n"); + printf("This test requires a POWER8 or POWER9 CPU and either a "); + printf("usable framebuffer at /dev/fb0 or the path to usable "); + printf("cache-inhibited memory and optional offset to be provided\n"); } int main(int argc, char *argv[]) @@ -533,6 +547,13 @@ int main(int argc, char *argv[]) exit(1); } } + argc -= optind; + argv += optind; + + if (argc > 0) + cipath = argv[0]; + if (argc > 1) + cioffset = strtol(argv[1], 0, 0x10); bufsize = getpagesize(); -- GitLab From 620a6473df36f8dc6f70bc85ff3465b2e21d1254 Mon Sep 17 00:00:00 2001 From: Jordan Niethe Date: Wed, 20 May 2020 12:11:03 +1000 Subject: [PATCH 0075/1476] selftests/powerpc: Add prefixed loads/stores to alignment_handler test Extend the alignment handler selftest to exercise prefixed load store instructions. Add tests for prefixed VSX, floating point and integer instructions. Skip prefix tests if ISA version does not support prefixed instructions. Signed-off-by: Jordan Niethe Tested-by: Alistair Popple [mpe: Fixup PPC_FEATURE2_ARCH_3_1 naming as noted by Alistair] Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20200520021103.19798-2-jniethe5@gmail.com --- .../powerpc/alignment/alignment_handler.c | 93 ++++++++++++++++++- .../selftests/powerpc/include/instructions.h | 77 +++++++++++++++ .../testing/selftests/powerpc/include/utils.h | 5 + 3 files changed, 172 insertions(+), 3 deletions(-) diff --git a/tools/testing/selftests/powerpc/alignment/alignment_handler.c b/tools/testing/selftests/powerpc/alignment/alignment_handler.c index eb6aba323f8bc..55ef15184057d 100644 --- a/tools/testing/selftests/powerpc/alignment/alignment_handler.c +++ b/tools/testing/selftests/powerpc/alignment/alignment_handler.c @@ -58,6 +58,7 @@ #include #include "utils.h" +#include "instructions.h" int bufsize; int debug; @@ -96,6 +97,17 @@ void sighandler(int sig, siginfo_t *info, void *ctx) } \ rc |= do_test(#name, test_##name) +#define TESTP(name, ld_op, st_op, ld_reg, st_reg) \ + void test_##name(char *s, char *d) \ + { \ + asm volatile( \ + ld_op(ld_reg, %0, 0, 0) \ + st_op(st_reg, %1, 0, 0) \ + :: "r"(s), "r"(d), "r"(0) \ + : "memory", "vs0", "vs32", "r31"); \ + } \ + rc |= do_test(#name, test_##name) + #define LOAD_VSX_XFORM_TEST(op) TEST(op, op, stxvd2x, XFORM, 32, 32) #define STORE_VSX_XFORM_TEST(op) TEST(op, lxvd2x, op, XFORM, 32, 32) #define LOAD_VSX_DFORM_TEST(op) TEST(op, op, stxv, DFORM, 32, 32) @@ -115,6 +127,17 @@ void sighandler(int sig, siginfo_t *info, void *ctx) #define LOAD_FLOAT_XFORM_TEST(op) TEST(op, op, stfdx, XFORM, 0, 0) #define STORE_FLOAT_XFORM_TEST(op) TEST(op, lfdx, op, XFORM, 0, 0) +#define LOAD_MLS_PREFIX_TEST(op) TESTP(op, op, PSTD, 31, 31) +#define STORE_MLS_PREFIX_TEST(op) TESTP(op, PLD, op, 31, 31) + +#define LOAD_8LS_PREFIX_TEST(op) TESTP(op, op, PSTD, 31, 31) +#define STORE_8LS_PREFIX_TEST(op) TESTP(op, PLD, op, 31, 31) + +#define LOAD_FLOAT_MLS_PREFIX_TEST(op) TESTP(op, op, PSTFD, 0, 0) +#define STORE_FLOAT_MLS_PREFIX_TEST(op) TESTP(op, PLFD, op, 0, 0) + +#define LOAD_VSX_8LS_PREFIX_TEST(op, tail) TESTP(op, op, PSTXV ## tail, 0, 32) +#define STORE_VSX_8LS_PREFIX_TEST(op, tail) TESTP(op, PLXV ## tail, op, 32, 0) /* FIXME: Unimplemented tests: */ // STORE_DFORM_TEST(stq) /* FIXME: need two registers for quad */ @@ -361,6 +384,25 @@ int test_alignment_handler_vsx_300(void) return rc; } +int test_alignment_handler_vsx_prefix(void) +{ + int rc = 0; + + SKIP_IF(!can_open_cifile()); + SKIP_IF(!have_hwcap2(PPC_FEATURE2_ARCH_3_1)); + + printf("VSX: PREFIX\n"); + LOAD_VSX_8LS_PREFIX_TEST(PLXSD, 0); + LOAD_VSX_8LS_PREFIX_TEST(PLXSSP, 0); + LOAD_VSX_8LS_PREFIX_TEST(PLXV0, 0); + LOAD_VSX_8LS_PREFIX_TEST(PLXV1, 1); + STORE_VSX_8LS_PREFIX_TEST(PSTXSD, 0); + STORE_VSX_8LS_PREFIX_TEST(PSTXSSP, 0); + STORE_VSX_8LS_PREFIX_TEST(PSTXV0, 0); + STORE_VSX_8LS_PREFIX_TEST(PSTXV1, 1); + return rc; +} + int test_alignment_handler_integer(void) { int rc = 0; @@ -432,6 +474,27 @@ int test_alignment_handler_integer_206(void) return rc; } +int test_alignment_handler_integer_prefix(void) +{ + int rc = 0; + + SKIP_IF(!can_open_cifile()); + SKIP_IF(!have_hwcap2(PPC_FEATURE2_ARCH_3_1)); + + printf("Integer: PREFIX\n"); + LOAD_MLS_PREFIX_TEST(PLBZ); + LOAD_MLS_PREFIX_TEST(PLHZ); + LOAD_MLS_PREFIX_TEST(PLHA); + LOAD_MLS_PREFIX_TEST(PLWZ); + LOAD_8LS_PREFIX_TEST(PLWA); + LOAD_8LS_PREFIX_TEST(PLD); + STORE_MLS_PREFIX_TEST(PSTB); + STORE_MLS_PREFIX_TEST(PSTH); + STORE_MLS_PREFIX_TEST(PSTW); + STORE_8LS_PREFIX_TEST(PSTD); + return rc; +} + int test_alignment_handler_vmx(void) { int rc = 0; @@ -520,14 +583,32 @@ int test_alignment_handler_fp_206(void) return rc; } + +int test_alignment_handler_fp_prefix(void) +{ + int rc = 0; + + SKIP_IF(!can_open_cifile()); + SKIP_IF(!have_hwcap2(PPC_FEATURE2_ARCH_3_1)); + + printf("Floating point: PREFIX\n"); + LOAD_FLOAT_DFORM_TEST(lfs); + LOAD_FLOAT_MLS_PREFIX_TEST(PLFS); + LOAD_FLOAT_MLS_PREFIX_TEST(PLFD); + STORE_FLOAT_MLS_PREFIX_TEST(PSTFS); + STORE_FLOAT_MLS_PREFIX_TEST(PSTFD); + return rc; +} + void usage(char *prog) { printf("Usage: %s [options] [path [offset]]\n", prog); printf(" -d Enable debug error output\n"); printf("\n"); - printf("This test requires a POWER8 or POWER9 CPU and either a "); - printf("usable framebuffer at /dev/fb0 or the path to usable "); - printf("cache-inhibited memory and optional offset to be provided\n"); + printf("This test requires a POWER8, POWER9 or POWER10 CPU "); + printf("and either a usable framebuffer at /dev/fb0 or "); + printf("the path to usable cache inhibited memory and optional "); + printf("offset to be provided\n"); } int main(int argc, char *argv[]) @@ -573,10 +654,14 @@ int main(int argc, char *argv[]) "test_alignment_handler_vsx_207"); rc |= test_harness(test_alignment_handler_vsx_300, "test_alignment_handler_vsx_300"); + rc |= test_harness(test_alignment_handler_vsx_prefix, + "test_alignment_handler_vsx_prefix"); rc |= test_harness(test_alignment_handler_integer, "test_alignment_handler_integer"); rc |= test_harness(test_alignment_handler_integer_206, "test_alignment_handler_integer_206"); + rc |= test_harness(test_alignment_handler_integer_prefix, + "test_alignment_handler_integer_prefix"); rc |= test_harness(test_alignment_handler_vmx, "test_alignment_handler_vmx"); rc |= test_harness(test_alignment_handler_fp, @@ -585,5 +670,7 @@ int main(int argc, char *argv[]) "test_alignment_handler_fp_205"); rc |= test_harness(test_alignment_handler_fp_206, "test_alignment_handler_fp_206"); + rc |= test_harness(test_alignment_handler_fp_prefix, + "test_alignment_handler_fp_prefix"); return rc; } diff --git a/tools/testing/selftests/powerpc/include/instructions.h b/tools/testing/selftests/powerpc/include/instructions.h index f36061eb6f0f1..4efa6314bd963 100644 --- a/tools/testing/selftests/powerpc/include/instructions.h +++ b/tools/testing/selftests/powerpc/include/instructions.h @@ -66,4 +66,81 @@ static inline int paste_last(void *i) #define PPC_INST_PASTE __PASTE(0, 0, 0, 0) #define PPC_INST_PASTE_LAST __PASTE(0, 0, 1, 1) +/* This defines the prefixed load/store instructions */ +#ifdef __ASSEMBLY__ +# define stringify_in_c(...) __VA_ARGS__ +#else +# define __stringify_in_c(...) #__VA_ARGS__ +# define stringify_in_c(...) __stringify_in_c(__VA_ARGS__) " " +#endif + +#define __PPC_RA(a) (((a) & 0x1f) << 16) +#define __PPC_RS(s) (((s) & 0x1f) << 21) +#define __PPC_RT(t) __PPC_RS(t) +#define __PPC_PREFIX_R(r) (((r) & 0x1) << 20) + +#define PPC_PREFIX_MLS 0x06000000 +#define PPC_PREFIX_8LS 0x04000000 + +#define PPC_INST_LBZ 0x88000000 +#define PPC_INST_LHZ 0xa0000000 +#define PPC_INST_LHA 0xa8000000 +#define PPC_INST_LWZ 0x80000000 +#define PPC_INST_STB 0x98000000 +#define PPC_INST_STH 0xb0000000 +#define PPC_INST_STW 0x90000000 +#define PPC_INST_STD 0xf8000000 +#define PPC_INST_LFS 0xc0000000 +#define PPC_INST_LFD 0xc8000000 +#define PPC_INST_STFS 0xd0000000 +#define PPC_INST_STFD 0xd8000000 + +#define PREFIX_MLS(instr, t, a, r, d) stringify_in_c(.balign 64, , 4;) \ + stringify_in_c(.long PPC_PREFIX_MLS | \ + __PPC_PREFIX_R(r) | \ + (((d) >> 16) & 0x3ffff);) \ + stringify_in_c(.long (instr) | \ + __PPC_RT(t) | \ + __PPC_RA(a) | \ + ((d) & 0xffff);\n) + +#define PREFIX_8LS(instr, t, a, r, d) stringify_in_c(.balign 64, , 4;) \ + stringify_in_c(.long PPC_PREFIX_8LS | \ + __PPC_PREFIX_R(r) | \ + (((d) >> 16) & 0x3ffff);) \ + stringify_in_c(.long (instr) | \ + __PPC_RT(t) | \ + __PPC_RA(a) | \ + ((d) & 0xffff);\n) + +/* Prefixed Integer Load/Store instructions */ +#define PLBZ(t, a, r, d) PREFIX_MLS(PPC_INST_LBZ, t, a, r, d) +#define PLHZ(t, a, r, d) PREFIX_MLS(PPC_INST_LHZ, t, a, r, d) +#define PLHA(t, a, r, d) PREFIX_MLS(PPC_INST_LHA, t, a, r, d) +#define PLWZ(t, a, r, d) PREFIX_MLS(PPC_INST_LWZ, t, a, r, d) +#define PLWA(t, a, r, d) PREFIX_8LS(0xa4000000, t, a, r, d) +#define PLD(t, a, r, d) PREFIX_8LS(0xe4000000, t, a, r, d) +#define PLQ(t, a, r, d) PREFIX_8LS(0xe0000000, t, a, r, d) +#define PSTB(s, a, r, d) PREFIX_MLS(PPC_INST_STB, s, a, r, d) +#define PSTH(s, a, r, d) PREFIX_MLS(PPC_INST_STH, s, a, r, d) +#define PSTW(s, a, r, d) PREFIX_MLS(PPC_INST_STW, s, a, r, d) +#define PSTD(s, a, r, d) PREFIX_8LS(0xf4000000, s, a, r, d) +#define PSTQ(s, a, r, d) PREFIX_8LS(0xf0000000, s, a, r, d) + +/* Prefixed Floating-Point Load/Store Instructions */ +#define PLFS(frt, a, r, d) PREFIX_MLS(PPC_INST_LFS, frt, a, r, d) +#define PLFD(frt, a, r, d) PREFIX_MLS(PPC_INST_LFD, frt, a, r, d) +#define PSTFS(frs, a, r, d) PREFIX_MLS(PPC_INST_STFS, frs, a, r, d) +#define PSTFD(frs, a, r, d) PREFIX_MLS(PPC_INST_STFD, frs, a, r, d) + +/* Prefixed VSX Load/Store Instructions */ +#define PLXSD(vrt, a, r, d) PREFIX_8LS(0xa8000000, vrt, a, r, d) +#define PLXSSP(vrt, a, r, d) PREFIX_8LS(0xac000000, vrt, a, r, d) +#define PLXV0(s, a, r, d) PREFIX_8LS(0xc8000000, s, a, r, d) +#define PLXV1(s, a, r, d) PREFIX_8LS(0xcc000000, s, a, r, d) +#define PSTXSD(vrs, a, r, d) PREFIX_8LS(0xb8000000, vrs, a, r, d) +#define PSTXSSP(vrs, a, r, d) PREFIX_8LS(0xbc000000, vrs, a, r, d) +#define PSTXV0(s, a, r, d) PREFIX_8LS(0xd8000000, s, a, r, d) +#define PSTXV1(s, a, r, d) PREFIX_8LS(0xdc000000, s, a, r, d) + #endif /* _SELFTESTS_POWERPC_INSTRUCTIONS_H */ diff --git a/tools/testing/selftests/powerpc/include/utils.h b/tools/testing/selftests/powerpc/include/utils.h index e089a0c30d9af..2e39b297054f3 100644 --- a/tools/testing/selftests/powerpc/include/utils.h +++ b/tools/testing/selftests/powerpc/include/utils.h @@ -101,6 +101,11 @@ do { \ #define PPC_FEATURE2_ARCH_3_00 0x00800000 #endif +/* POWER10 feature */ +#ifndef PPC_FEATURE2_ARCH_3_1 +#define PPC_FEATURE2_ARCH_3_1 0x00040000 +#endif + #if defined(__powerpc64__) #define UCONTEXT_NIA(UC) (UC)->uc_mcontext.gp_regs[PT_NIP] #define UCONTEXT_MSR(UC) (UC)->uc_mcontext.gp_regs[PT_MSR] -- GitLab From 03fd42d458fb9cb69e712600bd69ff77ff3a45a8 Mon Sep 17 00:00:00 2001 From: Christophe Leroy Date: Mon, 15 Jun 2020 07:48:25 +0000 Subject: [PATCH 0076/1476] powerpc/fixmap: Fix FIX_EARLY_DEBUG_BASE when page size is 256k FIX_EARLY_DEBUG_BASE reserves a 128k area for debuging. When page size is 256k, the calculation results in a 0 number of pages, leading to the following failure: CC arch/powerpc/kernel/asm-offsets.s In file included from ./arch/powerpc/include/asm/nohash/32/pgtable.h:77:0, from ./arch/powerpc/include/asm/nohash/pgtable.h:8, from ./arch/powerpc/include/asm/pgtable.h:20, from ./include/linux/pgtable.h:6, from ./arch/powerpc/include/asm/kup.h:42, from ./arch/powerpc/include/asm/uaccess.h:9, from ./include/linux/uaccess.h:11, from ./include/linux/crypto.h:21, from ./include/crypto/hash.h:11, from ./include/linux/uio.h:10, from ./include/linux/socket.h:8, from ./include/linux/compat.h:15, from arch/powerpc/kernel/asm-offsets.c:14: ./arch/powerpc/include/asm/fixmap.h:75:2: error: overflow in enumeration values __end_of_permanent_fixed_addresses, ^ make[2]: *** [arch/powerpc/kernel/asm-offsets.s] Error 1 Ensure the debug area is at least one page. Fixes: b8e8efaa8639 ("powerpc: reserve fixmap entries for early debug") Reported-by: kernel test robot Signed-off-by: Christophe Leroy Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/ca8c9f8249f523b1fab873e67b81b11989d46553.1592207216.git.christophe.leroy@csgroup.eu --- arch/powerpc/include/asm/fixmap.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/powerpc/include/asm/fixmap.h b/arch/powerpc/include/asm/fixmap.h index 29188810ba302..925cf89cbf4ba 100644 --- a/arch/powerpc/include/asm/fixmap.h +++ b/arch/powerpc/include/asm/fixmap.h @@ -52,7 +52,7 @@ enum fixed_addresses { FIX_HOLE, /* reserve the top 128K for early debugging purposes */ FIX_EARLY_DEBUG_TOP = FIX_HOLE, - FIX_EARLY_DEBUG_BASE = FIX_EARLY_DEBUG_TOP+((128*1024)/PAGE_SIZE)-1, + FIX_EARLY_DEBUG_BASE = FIX_EARLY_DEBUG_TOP+(ALIGN(SZ_128, PAGE_SIZE)/PAGE_SIZE)-1, #ifdef CONFIG_HIGHMEM FIX_KMAP_BEGIN, /* reserved pte's for temporary kernel mappings */ FIX_KMAP_END = FIX_KMAP_BEGIN+(KM_TYPE_NR*NR_CPUS)-1, -- GitLab From 548ad77d10f7ad6e5f84a0026978da2ed1df0dae Mon Sep 17 00:00:00 2001 From: Imre Kaloz Date: Thu, 22 Dec 2016 09:06:08 +0100 Subject: [PATCH 0077/1476] powerpc/4xx: ppc4xx compile flag optimizations This patch splits up the compile flags between ppc40x and ppc44x. Signed-off-by: John Crispin Signed-off-by: Imre Kaloz Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/1482393968-60623-1-git-send-email-john@phrozen.org --- arch/powerpc/Makefile | 3 ++- arch/powerpc/boot/Makefile | 6 +++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/arch/powerpc/Makefile b/arch/powerpc/Makefile index f310c32e88a4e..3e8da9cf2eb9d 100644 --- a/arch/powerpc/Makefile +++ b/arch/powerpc/Makefile @@ -246,7 +246,8 @@ KBUILD_CFLAGS += -fno-asynchronous-unwind-tables # often slow when they are implemented at all KBUILD_CFLAGS += $(call cc-option,-mno-string) -cpu-as-$(CONFIG_4xx) += -Wa,-m405 +cpu-as-$(CONFIG_40x) += -Wa,-m405 +cpu-as-$(CONFIG_44x) += -Wa,-m440 cpu-as-$(CONFIG_ALTIVEC) += $(call as-option,-Wa$(comma)-maltivec) cpu-as-$(CONFIG_E200) += -Wa,-me200 cpu-as-$(CONFIG_E500) += -Wa,-me500 diff --git a/arch/powerpc/boot/Makefile b/arch/powerpc/boot/Makefile index 63d7456b95180..4d43cb59b4a40 100644 --- a/arch/powerpc/boot/Makefile +++ b/arch/powerpc/boot/Makefile @@ -70,10 +70,10 @@ BOOTCFLAGS += -I$(objtree)/$(obj) -I$(srctree)/$(obj) DTC_FLAGS ?= -p 1024 $(obj)/4xx.o: BOOTCFLAGS += -mcpu=405 -$(obj)/ebony.o: BOOTCFLAGS += -mcpu=405 +$(obj)/ebony.o: BOOTCFLAGS += -mcpu=440 $(obj)/cuboot-hotfoot.o: BOOTCFLAGS += -mcpu=405 -$(obj)/cuboot-taishan.o: BOOTCFLAGS += -mcpu=405 -$(obj)/cuboot-katmai.o: BOOTCFLAGS += -mcpu=405 +$(obj)/cuboot-taishan.o: BOOTCFLAGS += -mcpu=440 +$(obj)/cuboot-katmai.o: BOOTCFLAGS += -mcpu=440 $(obj)/cuboot-acadia.o: BOOTCFLAGS += -mcpu=405 $(obj)/treeboot-iss4xx.o: BOOTCFLAGS += -mcpu=405 $(obj)/treeboot-currituck.o: BOOTCFLAGS += -mcpu=405 -- GitLab From 2afc6ec090b29c4014870b0d78334831d6163b9b Mon Sep 17 00:00:00 2001 From: Sivaprakash Murugesan Date: Mon, 22 Jun 2020 09:58:09 +0530 Subject: [PATCH 0078/1476] dt-bindings: clock: add ipq6018 a53 pll compatible cpus on ipq6018 are clocked by a53 pll, add device compatible for a53 pll found on ipq6018 devices. Reviewed-by: Rob Herring Signed-off-by: Sivaprakash Murugesan Link: https://lore.kernel.org/r/1592800092-20533-2-git-send-email-sivaprak@codeaurora.org Signed-off-by: Stephen Boyd --- .../bindings/clock/qcom,a53pll.yaml | 21 ++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/clock/qcom,a53pll.yaml b/Documentation/devicetree/bindings/clock/qcom,a53pll.yaml index 20d2638b4cd23..db3d0ea6bc7a9 100644 --- a/Documentation/devicetree/bindings/clock/qcom,a53pll.yaml +++ b/Documentation/devicetree/bindings/clock/qcom,a53pll.yaml @@ -15,7 +15,9 @@ description: properties: compatible: - const: qcom,msm8916-a53pll + enum: + - qcom,ipq6018-a53pll + - qcom,msm8916-a53pll reg: maxItems: 1 @@ -23,6 +25,14 @@ properties: '#clock-cells': const: 0 + clocks: + items: + - description: board XO clock + + clock-names: + items: + - const: xo + required: - compatible - reg @@ -38,3 +48,12 @@ examples: reg = <0xb016000 0x40>; #clock-cells = <0>; }; + #Example 2 - A53 PLL found on IPQ6018 devices + - | + a53pll_ipq: clock-controller@b116000 { + compatible = "qcom,ipq6018-a53pll"; + reg = <0x0b116000 0x40>; + #clock-cells = <0>; + clocks = <&xo>; + clock-names = "xo"; + }; -- GitLab From ecd2bacfbbc4d7a2a1beb96fa7836a8ca6e4cf71 Mon Sep 17 00:00:00 2001 From: Sivaprakash Murugesan Date: Mon, 22 Jun 2020 09:58:10 +0530 Subject: [PATCH 0079/1476] clk: qcom: Add ipq apss pll driver The CPUs on Qualcomm ipq based devices are clocked by an alpha PLL. Add support for the apss pll found on ipq based devices which can support CPU frequencies above 1Ghz. Signed-off-by: Sivaprakash Murugesan Link: https://lore.kernel.org/r/1592800092-20533-3-git-send-email-sivaprak@codeaurora.org Signed-off-by: Stephen Boyd --- drivers/clk/qcom/Kconfig | 8 +++ drivers/clk/qcom/Makefile | 1 + drivers/clk/qcom/apss-ipq-pll.c | 95 +++++++++++++++++++++++++++++++++ 3 files changed, 104 insertions(+) create mode 100644 drivers/clk/qcom/apss-ipq-pll.c diff --git a/drivers/clk/qcom/Kconfig b/drivers/clk/qcom/Kconfig index cde6ca90a06bb..49e265ddcdabe 100644 --- a/drivers/clk/qcom/Kconfig +++ b/drivers/clk/qcom/Kconfig @@ -89,6 +89,14 @@ config APQ_MMCC_8084 Say Y if you want to support multimedia devices such as display, graphics, video encode/decode, camera, etc. +config IPQ_APSS_PLL + tristate "IPQ APSS PLL" + help + Support for APSS PLL on ipq devices. The APSS PLL is the main + clock that feeds the CPUs on ipq based devices. + Say Y if you want to support CPU frequency scaling on ipq based + devices. + config IPQ_GCC_4019 tristate "IPQ4019 Global Clock Controller" help diff --git a/drivers/clk/qcom/Makefile b/drivers/clk/qcom/Makefile index 7ec8561a1270b..7942c00902ece 100644 --- a/drivers/clk/qcom/Makefile +++ b/drivers/clk/qcom/Makefile @@ -19,6 +19,7 @@ clk-qcom-$(CONFIG_QCOM_GDSC) += gdsc.o # Keep alphabetically sorted by config obj-$(CONFIG_APQ_GCC_8084) += gcc-apq8084.o obj-$(CONFIG_APQ_MMCC_8084) += mmcc-apq8084.o +obj-$(CONFIG_IPQ_APSS_PLL) += apss-ipq-pll.o obj-$(CONFIG_IPQ_GCC_4019) += gcc-ipq4019.o obj-$(CONFIG_IPQ_GCC_6018) += gcc-ipq6018.o obj-$(CONFIG_IPQ_GCC_806X) += gcc-ipq806x.o diff --git a/drivers/clk/qcom/apss-ipq-pll.c b/drivers/clk/qcom/apss-ipq-pll.c new file mode 100644 index 0000000000000..30be87fb222aa --- /dev/null +++ b/drivers/clk/qcom/apss-ipq-pll.c @@ -0,0 +1,95 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (c) 2018, The Linux Foundation. All rights reserved. +#include +#include +#include +#include + +#include "clk-alpha-pll.h" + +static const u8 ipq_pll_offsets[] = { + [PLL_OFF_L_VAL] = 0x08, + [PLL_OFF_ALPHA_VAL] = 0x10, + [PLL_OFF_USER_CTL] = 0x18, + [PLL_OFF_CONFIG_CTL] = 0x20, + [PLL_OFF_CONFIG_CTL_U] = 0x24, + [PLL_OFF_STATUS] = 0x28, + [PLL_OFF_TEST_CTL] = 0x30, + [PLL_OFF_TEST_CTL_U] = 0x34, +}; + +static struct clk_alpha_pll ipq_pll = { + .offset = 0x0, + .regs = ipq_pll_offsets, + .flags = SUPPORTS_DYNAMIC_UPDATE, + .clkr = { + .enable_reg = 0x0, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "a53pll", + .parent_data = &(const struct clk_parent_data) { + .fw_name = "xo", + }, + .num_parents = 1, + .ops = &clk_alpha_pll_huayra_ops, + }, + }, +}; + +static const struct alpha_pll_config ipq_pll_config = { + .l = 0x37, + .config_ctl_val = 0x04141200, + .config_ctl_hi_val = 0x0, + .early_output_mask = BIT(3), + .main_output_mask = BIT(0), +}; + +static const struct regmap_config ipq_pll_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x40, + .fast_io = true, +}; + +static int apss_ipq_pll_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct regmap *regmap; + void __iomem *base; + int ret; + + base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(base)) + return PTR_ERR(base); + + regmap = devm_regmap_init_mmio(dev, base, &ipq_pll_regmap_config); + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + + clk_alpha_pll_configure(&ipq_pll, regmap, &ipq_pll_config); + + ret = devm_clk_register_regmap(dev, &ipq_pll.clkr); + if (ret) + return ret; + + return devm_of_clk_add_hw_provider(dev, of_clk_hw_simple_get, + &ipq_pll.clkr.hw); +} + +static const struct of_device_id apss_ipq_pll_match_table[] = { + { .compatible = "qcom,ipq6018-a53pll" }, + { } +}; + +static struct platform_driver apss_ipq_pll_driver = { + .probe = apss_ipq_pll_probe, + .driver = { + .name = "qcom-ipq-apss-pll", + .of_match_table = apss_ipq_pll_match_table, + }, +}; +module_platform_driver(apss_ipq_pll_driver); + +MODULE_DESCRIPTION("Qualcomm technology Inc APSS ALPHA PLL Driver"); +MODULE_LICENSE("GPL v2"); -- GitLab From 49bcaef86eba1a8097980f341e243ba01177a685 Mon Sep 17 00:00:00 2001 From: Sivaprakash Murugesan Date: Mon, 22 Jun 2020 09:58:11 +0530 Subject: [PATCH 0080/1476] clk: qcom: Add DT bindings for ipq6018 apss clock controller Add dt-binding for ipq6018 apss clock controller Acked-by: Rob Herring Signed-off-by: Sivaprakash Murugesan Link: https://lore.kernel.org/r/1592800092-20533-4-git-send-email-sivaprak@codeaurora.org Signed-off-by: Stephen Boyd --- include/dt-bindings/clock/qcom,apss-ipq.h | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 include/dt-bindings/clock/qcom,apss-ipq.h diff --git a/include/dt-bindings/clock/qcom,apss-ipq.h b/include/dt-bindings/clock/qcom,apss-ipq.h new file mode 100644 index 0000000000000..77b6e05492e24 --- /dev/null +++ b/include/dt-bindings/clock/qcom,apss-ipq.h @@ -0,0 +1,12 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2018, The Linux Foundation. All rights reserved. + */ + +#ifndef _DT_BINDINGS_CLOCK_QCA_APSS_IPQ6018_H +#define _DT_BINDINGS_CLOCK_QCA_APSS_IPQ6018_H + +#define APCS_ALIAS0_CLK_SRC 0 +#define APCS_ALIAS0_CORE_CLK 1 + +#endif -- GitLab From 5e77b4ef1b19a4ce4051ff2afb706ee675e4b412 Mon Sep 17 00:00:00 2001 From: Sivaprakash Murugesan Date: Mon, 22 Jun 2020 09:58:12 +0530 Subject: [PATCH 0081/1476] clk: qcom: Add ipq6018 apss clock controller The CPU on Qualcomm ipq6018 devices are clocked primarily by a aplha PLL and xo which are connected to a mux and enable block. Add support for the mux and enable block which feeds the CPU on ipq6018 devices. Reviewed-by: Stephen Boyd Signed-off-by: Sivaprakash Murugesan Link: https://lore.kernel.org/r/1592800092-20533-5-git-send-email-sivaprak@codeaurora.org Signed-off-by: Stephen Boyd --- drivers/clk/qcom/Kconfig | 11 ++++ drivers/clk/qcom/Makefile | 1 + drivers/clk/qcom/apss-ipq6018.c | 106 ++++++++++++++++++++++++++++++++ 3 files changed, 118 insertions(+) create mode 100644 drivers/clk/qcom/apss-ipq6018.c diff --git a/drivers/clk/qcom/Kconfig b/drivers/clk/qcom/Kconfig index 49e265ddcdabe..f510ef61db697 100644 --- a/drivers/clk/qcom/Kconfig +++ b/drivers/clk/qcom/Kconfig @@ -97,6 +97,17 @@ config IPQ_APSS_PLL Say Y if you want to support CPU frequency scaling on ipq based devices. +config IPQ_APSS_6018 + tristate "IPQ APSS Clock Controller" + select IPQ_APSS_PLL + depends on QCOM_APCS_IPC || COMPILE_TEST + help + Support for APSS clock controller on IPQ platforms. The + APSS clock controller manages the Mux and enable block that feeds the + CPUs. + Say Y if you want to support CPU frequency scaling on + ipq based devices. + config IPQ_GCC_4019 tristate "IPQ4019 Global Clock Controller" help diff --git a/drivers/clk/qcom/Makefile b/drivers/clk/qcom/Makefile index 7942c00902ece..21439b94395a4 100644 --- a/drivers/clk/qcom/Makefile +++ b/drivers/clk/qcom/Makefile @@ -20,6 +20,7 @@ clk-qcom-$(CONFIG_QCOM_GDSC) += gdsc.o obj-$(CONFIG_APQ_GCC_8084) += gcc-apq8084.o obj-$(CONFIG_APQ_MMCC_8084) += mmcc-apq8084.o obj-$(CONFIG_IPQ_APSS_PLL) += apss-ipq-pll.o +obj-$(CONFIG_IPQ_APSS_6018) += apss-ipq6018.o obj-$(CONFIG_IPQ_GCC_4019) += gcc-ipq4019.o obj-$(CONFIG_IPQ_GCC_6018) += gcc-ipq6018.o obj-$(CONFIG_IPQ_GCC_806X) += gcc-ipq806x.o diff --git a/drivers/clk/qcom/apss-ipq6018.c b/drivers/clk/qcom/apss-ipq6018.c new file mode 100644 index 0000000000000..004f7e1ecdc21 --- /dev/null +++ b/drivers/clk/qcom/apss-ipq6018.c @@ -0,0 +1,106 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2018, The Linux Foundation. All rights reserved. + */ + +#include +#include +#include +#include +#include +#include + +#include + +#include "common.h" +#include "clk-regmap.h" +#include "clk-branch.h" +#include "clk-alpha-pll.h" +#include "clk-regmap-mux.h" + +enum { + P_XO, + P_APSS_PLL_EARLY, +}; + +static const struct clk_parent_data parents_apcs_alias0_clk_src[] = { + { .fw_name = "xo" }, + { .fw_name = "pll" }, +}; + +static const struct parent_map parents_apcs_alias0_clk_src_map[] = { + { P_XO, 0 }, + { P_APSS_PLL_EARLY, 5 }, +}; + +static struct clk_regmap_mux apcs_alias0_clk_src = { + .reg = 0x0050, + .width = 3, + .shift = 7, + .parent_map = parents_apcs_alias0_clk_src_map, + .clkr.hw.init = &(struct clk_init_data){ + .name = "apcs_alias0_clk_src", + .parent_data = parents_apcs_alias0_clk_src, + .num_parents = 2, + .ops = &clk_regmap_mux_closest_ops, + .flags = CLK_SET_RATE_PARENT, + }, +}; + +static struct clk_branch apcs_alias0_core_clk = { + .halt_reg = 0x0058, + .clkr = { + .enable_reg = 0x0058, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "apcs_alias0_core_clk", + .parent_hws = (const struct clk_hw *[]){ + &apcs_alias0_clk_src.clkr.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static const struct regmap_config apss_ipq6018_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x1000, + .fast_io = true, +}; + +static struct clk_regmap *apss_ipq6018_clks[] = { + [APCS_ALIAS0_CLK_SRC] = &apcs_alias0_clk_src.clkr, + [APCS_ALIAS0_CORE_CLK] = &apcs_alias0_core_clk.clkr, +}; + +static const struct qcom_cc_desc apss_ipq6018_desc = { + .config = &apss_ipq6018_regmap_config, + .clks = apss_ipq6018_clks, + .num_clks = ARRAY_SIZE(apss_ipq6018_clks), +}; + +static int apss_ipq6018_probe(struct platform_device *pdev) +{ + struct regmap *regmap; + + regmap = dev_get_regmap(pdev->dev.parent, NULL); + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + + return qcom_cc_really_probe(pdev, &apss_ipq6018_desc, regmap); +} + +static struct platform_driver apss_ipq6018_driver = { + .probe = apss_ipq6018_probe, + .driver = { + .name = "qcom,apss-ipq6018-clk", + }, +}; + +module_platform_driver(apss_ipq6018_driver); + +MODULE_DESCRIPTION("QCOM APSS IPQ 6018 CLK Driver"); +MODULE_LICENSE("GPL v2"); -- GitLab From e8c849c2a8e8d300922399ac02065bebf53393a6 Mon Sep 17 00:00:00 2001 From: Sarang Mairal Date: Wed, 10 Jun 2020 21:19:41 -0500 Subject: [PATCH 0082/1476] clk: add function documentation for clk_hw_round_rate() Information about usage and prerequisites for this API. Signed-off-by: Sarang Mairal Link: https://lore.kernel.org/r/20200611021941.786-2-sarangmairal@gmail.com Signed-off-by: Stephen Boyd --- drivers/clk/clk.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c index 3f588ed06ce31..236923b255431 100644 --- a/drivers/clk/clk.c +++ b/drivers/clk/clk.c @@ -1400,6 +1400,21 @@ int __clk_determine_rate(struct clk_hw *hw, struct clk_rate_request *req) } EXPORT_SYMBOL_GPL(__clk_determine_rate); +/** + * clk_hw_round_rate() - round the given rate for a hw clk + * @hw: the hw clk for which we are rounding a rate + * @rate: the rate which is to be rounded + * + * Takes in a rate as input and rounds it to a rate that the clk can actually + * use. + * + * Context: prepare_lock must be held. + * For clk providers to call from within clk_ops such as .round_rate, + * .determine_rate. + * + * Return: returns rounded rate of hw clk if clk supports round_rate operation + * else returns the parent rate. + */ unsigned long clk_hw_round_rate(struct clk_hw *hw, unsigned long rate) { int ret; -- GitLab From b608013ac5b55a2e42d8734f29f9757b75d26165 Mon Sep 17 00:00:00 2001 From: Konrad Dybcio Date: Mon, 22 Jun 2020 11:02:52 +0200 Subject: [PATCH 0083/1476] clk: qcom: smd: Add support for SDM660 rpm clocks Add rpm smd clocks, PMIC and bus clocks which are required on SDM630/660 (and APQ variants) for clients to vote on. Signed-off-by: Konrad Dybcio Link: https://lore.kernel.org/r/20200622090252.36568-1-konradybcio@gmail.com Signed-off-by: Stephen Boyd --- .../devicetree/bindings/clock/qcom,rpmcc.txt | 1 + drivers/clk/qcom/clk-smd-rpm.c | 76 +++++++++++++++++++ include/dt-bindings/clock/qcom,rpmcc.h | 10 +++ 3 files changed, 87 insertions(+) diff --git a/Documentation/devicetree/bindings/clock/qcom,rpmcc.txt b/Documentation/devicetree/bindings/clock/qcom,rpmcc.txt index 90a1349bc7135..86190acc71bcf 100644 --- a/Documentation/devicetree/bindings/clock/qcom,rpmcc.txt +++ b/Documentation/devicetree/bindings/clock/qcom,rpmcc.txt @@ -20,6 +20,7 @@ Required properties : "qcom,rpmcc-msm8996", "qcom,rpmcc" "qcom,rpmcc-msm8998", "qcom,rpmcc" "qcom,rpmcc-qcs404", "qcom,rpmcc" + "qcom,rpmcc-sdm660", "qcom,rpmcc" - #clock-cells : shall contain 1 diff --git a/drivers/clk/qcom/clk-smd-rpm.c b/drivers/clk/qcom/clk-smd-rpm.c index 52f63ad787ba9..643bc355df5cb 100644 --- a/drivers/clk/qcom/clk-smd-rpm.c +++ b/drivers/clk/qcom/clk-smd-rpm.c @@ -766,6 +766,81 @@ static const struct rpm_smd_clk_desc rpm_clk_msm8998 = { .num_clks = ARRAY_SIZE(msm8998_clks), }; +/* sdm660 */ +DEFINE_CLK_SMD_RPM_BRANCH(sdm660, bi_tcxo, bi_tcxo_a, QCOM_SMD_RPM_MISC_CLK, 0, + 19200000); +DEFINE_CLK_SMD_RPM(sdm660, snoc_clk, snoc_a_clk, QCOM_SMD_RPM_BUS_CLK, 1); +DEFINE_CLK_SMD_RPM(sdm660, cnoc_clk, cnoc_a_clk, QCOM_SMD_RPM_BUS_CLK, 2); +DEFINE_CLK_SMD_RPM(sdm660, cnoc_periph_clk, cnoc_periph_a_clk, + QCOM_SMD_RPM_BUS_CLK, 0); +DEFINE_CLK_SMD_RPM(sdm660, bimc_clk, bimc_a_clk, QCOM_SMD_RPM_MEM_CLK, 0); +DEFINE_CLK_SMD_RPM(sdm660, mmssnoc_axi_clk, mmssnoc_axi_a_clk, + QCOM_SMD_RPM_MMAXI_CLK, 0); +DEFINE_CLK_SMD_RPM(sdm660, ipa_clk, ipa_a_clk, QCOM_SMD_RPM_IPA_CLK, 0); +DEFINE_CLK_SMD_RPM(sdm660, ce1_clk, ce1_a_clk, QCOM_SMD_RPM_CE_CLK, 0); +DEFINE_CLK_SMD_RPM(sdm660, aggre2_noc_clk, aggre2_noc_a_clk, + QCOM_SMD_RPM_AGGR_CLK, 2); +DEFINE_CLK_SMD_RPM_QDSS(sdm660, qdss_clk, qdss_a_clk, + QCOM_SMD_RPM_MISC_CLK, 1); +DEFINE_CLK_SMD_RPM_XO_BUFFER(sdm660, rf_clk1, rf_clk1_a, 4); +DEFINE_CLK_SMD_RPM_XO_BUFFER(sdm660, div_clk1, div_clk1_a, 11); +DEFINE_CLK_SMD_RPM_XO_BUFFER(sdm660, ln_bb_clk1, ln_bb_clk1_a, 1); +DEFINE_CLK_SMD_RPM_XO_BUFFER(sdm660, ln_bb_clk2, ln_bb_clk2_a, 2); +DEFINE_CLK_SMD_RPM_XO_BUFFER(sdm660, ln_bb_clk3, ln_bb_clk3_a, 3); + +DEFINE_CLK_SMD_RPM_XO_BUFFER_PINCTRL(sdm660, rf_clk1_pin, rf_clk1_a_pin, 4); +DEFINE_CLK_SMD_RPM_XO_BUFFER_PINCTRL(sdm660, ln_bb_clk1_pin, + ln_bb_clk1_pin_a, 1); +DEFINE_CLK_SMD_RPM_XO_BUFFER_PINCTRL(sdm660, ln_bb_clk2_pin, + ln_bb_clk2_pin_a, 2); +DEFINE_CLK_SMD_RPM_XO_BUFFER_PINCTRL(sdm660, ln_bb_clk3_pin, + ln_bb_clk3_pin_a, 3); +static struct clk_smd_rpm *sdm660_clks[] = { + [RPM_SMD_XO_CLK_SRC] = &sdm660_bi_tcxo, + [RPM_SMD_XO_A_CLK_SRC] = &sdm660_bi_tcxo_a, + [RPM_SMD_SNOC_CLK] = &sdm660_snoc_clk, + [RPM_SMD_SNOC_A_CLK] = &sdm660_snoc_a_clk, + [RPM_SMD_CNOC_CLK] = &sdm660_cnoc_clk, + [RPM_SMD_CNOC_A_CLK] = &sdm660_cnoc_a_clk, + [RPM_SMD_CNOC_PERIPH_CLK] = &sdm660_cnoc_periph_clk, + [RPM_SMD_CNOC_PERIPH_A_CLK] = &sdm660_cnoc_periph_a_clk, + [RPM_SMD_BIMC_CLK] = &sdm660_bimc_clk, + [RPM_SMD_BIMC_A_CLK] = &sdm660_bimc_a_clk, + [RPM_SMD_MMSSNOC_AXI_CLK] = &sdm660_mmssnoc_axi_clk, + [RPM_SMD_MMSSNOC_AXI_CLK_A] = &sdm660_mmssnoc_axi_a_clk, + [RPM_SMD_IPA_CLK] = &sdm660_ipa_clk, + [RPM_SMD_IPA_A_CLK] = &sdm660_ipa_a_clk, + [RPM_SMD_CE1_CLK] = &sdm660_ce1_clk, + [RPM_SMD_CE1_A_CLK] = &sdm660_ce1_a_clk, + [RPM_SMD_AGGR2_NOC_CLK] = &sdm660_aggre2_noc_clk, + [RPM_SMD_AGGR2_NOC_A_CLK] = &sdm660_aggre2_noc_a_clk, + [RPM_SMD_QDSS_CLK] = &sdm660_qdss_clk, + [RPM_SMD_QDSS_A_CLK] = &sdm660_qdss_a_clk, + [RPM_SMD_RF_CLK1] = &sdm660_rf_clk1, + [RPM_SMD_RF_CLK1_A] = &sdm660_rf_clk1_a, + [RPM_SMD_DIV_CLK1] = &sdm660_div_clk1, + [RPM_SMD_DIV_A_CLK1] = &sdm660_div_clk1_a, + [RPM_SMD_LN_BB_CLK] = &sdm660_ln_bb_clk1, + [RPM_SMD_LN_BB_A_CLK] = &sdm660_ln_bb_clk1_a, + [RPM_SMD_LN_BB_CLK2] = &sdm660_ln_bb_clk2, + [RPM_SMD_LN_BB_CLK2_A] = &sdm660_ln_bb_clk2_a, + [RPM_SMD_LN_BB_CLK3] = &sdm660_ln_bb_clk3, + [RPM_SMD_LN_BB_CLK3_A] = &sdm660_ln_bb_clk3_a, + [RPM_SMD_RF_CLK1_PIN] = &sdm660_rf_clk1_pin, + [RPM_SMD_RF_CLK1_A_PIN] = &sdm660_rf_clk1_a_pin, + [RPM_SMD_LN_BB_CLK1_PIN] = &sdm660_ln_bb_clk1_pin, + [RPM_SMD_LN_BB_CLK1_A_PIN] = &sdm660_ln_bb_clk1_pin_a, + [RPM_SMD_LN_BB_CLK2_PIN] = &sdm660_ln_bb_clk2_pin, + [RPM_SMD_LN_BB_CLK2_A_PIN] = &sdm660_ln_bb_clk2_pin_a, + [RPM_SMD_LN_BB_CLK3_PIN] = &sdm660_ln_bb_clk3_pin, + [RPM_SMD_LN_BB_CLK3_A_PIN] = &sdm660_ln_bb_clk3_pin_a, +}; + +static const struct rpm_smd_clk_desc rpm_clk_sdm660 = { + .clks = sdm660_clks, + .num_clks = ARRAY_SIZE(sdm660_clks), +}; + static const struct of_device_id rpm_smd_clk_match_table[] = { { .compatible = "qcom,rpmcc-msm8916", .data = &rpm_clk_msm8916 }, { .compatible = "qcom,rpmcc-msm8974", .data = &rpm_clk_msm8974 }, @@ -773,6 +848,7 @@ static const struct of_device_id rpm_smd_clk_match_table[] = { { .compatible = "qcom,rpmcc-msm8996", .data = &rpm_clk_msm8996 }, { .compatible = "qcom,rpmcc-msm8998", .data = &rpm_clk_msm8998 }, { .compatible = "qcom,rpmcc-qcs404", .data = &rpm_clk_qcs404 }, + { .compatible = "qcom,rpmcc-sdm660", .data = &rpm_clk_sdm660 }, { } }; MODULE_DEVICE_TABLE(of, rpm_smd_clk_match_table); diff --git a/include/dt-bindings/clock/qcom,rpmcc.h b/include/dt-bindings/clock/qcom,rpmcc.h index ae74c43c485d8..d1afa634b58d2 100644 --- a/include/dt-bindings/clock/qcom,rpmcc.h +++ b/include/dt-bindings/clock/qcom,rpmcc.h @@ -133,5 +133,15 @@ #define RPM_SMD_RF_CLK3_A 87 #define RPM_SMD_RF_CLK3_PIN 88 #define RPM_SMD_RF_CLK3_A_PIN 89 +#define RPM_SMD_MMSSNOC_AXI_CLK 90 +#define RPM_SMD_MMSSNOC_AXI_CLK_A 91 +#define RPM_SMD_CNOC_PERIPH_CLK 92 +#define RPM_SMD_CNOC_PERIPH_A_CLK 93 +#define RPM_SMD_LN_BB_CLK3 94 +#define RPM_SMD_LN_BB_CLK3_A 95 +#define RPM_SMD_LN_BB_CLK1_PIN 96 +#define RPM_SMD_LN_BB_CLK1_A_PIN 97 +#define RPM_SMD_LN_BB_CLK2_PIN 98 +#define RPM_SMD_LN_BB_CLK2_A_PIN 99 #endif -- GitLab From 86590e524ee834b629afc55d8e5786091fbf84cc Mon Sep 17 00:00:00 2001 From: "Aneesh Kumar K.V" Date: Mon, 22 Jun 2020 12:10:19 +0530 Subject: [PATCH 0084/1476] powerpc/mm/book3s64: Skip 16G page reservation with radix With hash translation, the hypervisor can hint the LPAR about 16GB contiguous range via ibm,expected#pages. The kernel marks the range specified in the device tree as reserved. Avoid doing this when using radix translation. Radix translation only supports 1G gigantic hugepage and kernel can do the 1G gigantic hugepage allocation via early memblock reservation. This can be done because with radix translation pages are not required to be contiguous on the host. Signed-off-by: Aneesh Kumar K.V Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20200622064019.16682-1-aneesh.kumar@linux.ibm.com --- arch/powerpc/mm/book3s64/hash_utils.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/powerpc/mm/book3s64/hash_utils.c b/arch/powerpc/mm/book3s64/hash_utils.c index 084287fb29081..eec6f4e5e4816 100644 --- a/arch/powerpc/mm/book3s64/hash_utils.c +++ b/arch/powerpc/mm/book3s64/hash_utils.c @@ -596,7 +596,7 @@ static void __init htab_scan_page_sizes(void) } #ifdef CONFIG_HUGETLB_PAGE - if (!hugetlb_disabled) { + if (!hugetlb_disabled && !early_radix_enabled() ) { /* Reserve 16G huge page memory sections for huge pages */ of_scan_flat_dt(htab_dt_scan_hugepage_blocks, NULL); } -- GitLab From 105fb38124a490f38e9c1e23bb4c4a0b6ba12fdb Mon Sep 17 00:00:00 2001 From: Christophe Leroy Date: Thu, 18 Jun 2020 12:07:46 +0000 Subject: [PATCH 0085/1476] powerpc/8xx: Modify ptep_get() Move ptep_get() close to pte_update(), in an ifdef section already dedicated to powerpc 8xx. This section contains explanation about the layout of page table entries. Also modify it to return 4 times the pte value instead of padding with zeroes. Signed-off-by: Christophe Leroy Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/9f2df6621fcaf9eba15fadc61c169d0c8e2fb849.1592481938.git.christophe.leroy@csgroup.eu --- arch/powerpc/include/asm/nohash/32/pgtable.h | 22 +++++++++++--------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/arch/powerpc/include/asm/nohash/32/pgtable.h b/arch/powerpc/include/asm/nohash/32/pgtable.h index b0afbdd077403..b9e134d0f03ad 100644 --- a/arch/powerpc/include/asm/nohash/32/pgtable.h +++ b/arch/powerpc/include/asm/nohash/32/pgtable.h @@ -249,6 +249,18 @@ static inline pte_basic_t pte_update(struct mm_struct *mm, unsigned long addr, p return old; } + +#ifdef CONFIG_PPC_16K_PAGES +#define __HAVE_ARCH_PTEP_GET +static inline pte_t ptep_get(pte_t *ptep) +{ + pte_basic_t val = READ_ONCE(ptep->pte); + pte_t pte = {val, val, val, val}; + + return pte; +} +#endif /* CONFIG_PPC_16K_PAGES */ + #else static inline pte_basic_t pte_update(struct mm_struct *mm, unsigned long addr, pte_t *p, unsigned long clr, unsigned long set, int huge) @@ -284,16 +296,6 @@ static inline pte_t ptep_get_and_clear(struct mm_struct *mm, unsigned long addr, return __pte(pte_update(mm, addr, ptep, ~0, 0, 0)); } -#if defined(CONFIG_PPC_8xx) && defined(CONFIG_PPC_16K_PAGES) -#define __HAVE_ARCH_PTEP_GET -static inline pte_t ptep_get(pte_t *ptep) -{ - pte_t pte = {READ_ONCE(ptep->pte), 0, 0, 0}; - - return pte; -} -#endif - #define __HAVE_ARCH_PTEP_SET_WRPROTECT static inline void ptep_set_wrprotect(struct mm_struct *mm, unsigned long addr, pte_t *ptep) -- GitLab From ed4a11807d2a35ccfc0d00371f20b826f670b5f2 Mon Sep 17 00:00:00 2001 From: Ulrich Hecht Date: Tue, 16 Jun 2020 18:26:24 +0200 Subject: [PATCH 0086/1476] clk: renesas: cpg-mssr: Mark clocks as critical only if on at boot This allows us to add the RWDT clock to the list of critical clocks without keeping it enabled needlessly if not used. Changing the semantics of crit_mod_clks in this way is safe for the current user (INTC-AP) because it is never off at boot time. Signed-off-by: Ulrich Hecht Link: https://lore.kernel.org/r/20200616162626.27944-2-uli+renesas@fpond.eu Signed-off-by: Geert Uytterhoeven --- drivers/clk/renesas/renesas-cpg-mssr.c | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/drivers/clk/renesas/renesas-cpg-mssr.c b/drivers/clk/renesas/renesas-cpg-mssr.c index dcb6e2706d372..4648a829db66d 100644 --- a/drivers/clk/renesas/renesas-cpg-mssr.c +++ b/drivers/clk/renesas/renesas-cpg-mssr.c @@ -416,14 +416,6 @@ static void __init cpg_mssr_register_mod_clk(const struct mssr_mod_clk *mod, init.name = mod->name; init.ops = &cpg_mstp_clock_ops; init.flags = CLK_SET_RATE_PARENT; - for (i = 0; i < info->num_crit_mod_clks; i++) - if (id == info->crit_mod_clks[i]) { - dev_dbg(dev, "MSTP %s setting CLK_IS_CRITICAL\n", - mod->name); - init.flags |= CLK_IS_CRITICAL; - break; - } - parent_name = __clk_get_name(parent); init.parent_names = &parent_name; init.num_parents = 1; @@ -432,6 +424,15 @@ static void __init cpg_mssr_register_mod_clk(const struct mssr_mod_clk *mod, clock->priv = priv; clock->hw.init = &init; + for (i = 0; i < info->num_crit_mod_clks; i++) + if (id == info->crit_mod_clks[i] && + cpg_mstp_clock_is_enabled(&clock->hw)) { + dev_dbg(dev, "MSTP %s setting CLK_IS_CRITICAL\n", + mod->name); + init.flags |= CLK_IS_CRITICAL; + break; + } + clk = clk_register(NULL, &clock->hw); if (IS_ERR(clk)) goto fail; -- GitLab From f23f1101ad0ef1acdc219d3364522166e2c711ce Mon Sep 17 00:00:00 2001 From: Ulrich Hecht Date: Tue, 16 Jun 2020 18:26:25 +0200 Subject: [PATCH 0087/1476] clk: renesas: rcar-gen3: Mark RWDT clocks as critical Ensures RWDT remains alert throughout the boot process if enabled. This patch applies the change to the following SoCs: r8a77950, r8a77951, r8a77960, r8a77961, r8a77965, r8a77970, r8a77980, r8a77990 and r8a77995. Signed-off-by: Ulrich Hecht Link: https://lore.kernel.org/r/20200616162626.27944-3-uli+renesas@fpond.eu Signed-off-by: Geert Uytterhoeven --- drivers/clk/renesas/r8a7795-cpg-mssr.c | 2 +- drivers/clk/renesas/r8a7796-cpg-mssr.c | 2 +- drivers/clk/renesas/r8a77965-cpg-mssr.c | 1 + drivers/clk/renesas/r8a77970-cpg-mssr.c | 2 +- drivers/clk/renesas/r8a77980-cpg-mssr.c | 2 +- drivers/clk/renesas/r8a77990-cpg-mssr.c | 1 + drivers/clk/renesas/r8a77995-cpg-mssr.c | 2 +- 7 files changed, 7 insertions(+), 5 deletions(-) diff --git a/drivers/clk/renesas/r8a7795-cpg-mssr.c b/drivers/clk/renesas/r8a7795-cpg-mssr.c index ff5b3020cb03b..068018ae3c6e2 100644 --- a/drivers/clk/renesas/r8a7795-cpg-mssr.c +++ b/drivers/clk/renesas/r8a7795-cpg-mssr.c @@ -287,10 +287,10 @@ static struct mssr_mod_clk r8a7795_mod_clks[] __initdata = { }; static const unsigned int r8a7795_crit_mod_clks[] __initconst = { + MOD_CLK_ID(402), /* RWDT */ MOD_CLK_ID(408), /* INTC-AP (GIC) */ }; - /* * CPG Clock Data */ diff --git a/drivers/clk/renesas/r8a7796-cpg-mssr.c b/drivers/clk/renesas/r8a7796-cpg-mssr.c index e8d466dbc7f98..2cd6e3876fbdc 100644 --- a/drivers/clk/renesas/r8a7796-cpg-mssr.c +++ b/drivers/clk/renesas/r8a7796-cpg-mssr.c @@ -262,10 +262,10 @@ static struct mssr_mod_clk r8a7796_mod_clks[] __initdata = { }; static const unsigned int r8a7796_crit_mod_clks[] __initconst = { + MOD_CLK_ID(402), /* RWDT */ MOD_CLK_ID(408), /* INTC-AP (GIC) */ }; - /* * CPG Clock Data */ diff --git a/drivers/clk/renesas/r8a77965-cpg-mssr.c b/drivers/clk/renesas/r8a77965-cpg-mssr.c index 7a05a2fc1cc63..2b55a06ac5cf2 100644 --- a/drivers/clk/renesas/r8a77965-cpg-mssr.c +++ b/drivers/clk/renesas/r8a77965-cpg-mssr.c @@ -263,6 +263,7 @@ static const struct mssr_mod_clk r8a77965_mod_clks[] __initconst = { }; static const unsigned int r8a77965_crit_mod_clks[] __initconst = { + MOD_CLK_ID(402), /* RWDT */ MOD_CLK_ID(408), /* INTC-AP (GIC) */ }; diff --git a/drivers/clk/renesas/r8a77970-cpg-mssr.c b/drivers/clk/renesas/r8a77970-cpg-mssr.c index cbed3769a1007..0f59c84229a8b 100644 --- a/drivers/clk/renesas/r8a77970-cpg-mssr.c +++ b/drivers/clk/renesas/r8a77970-cpg-mssr.c @@ -165,10 +165,10 @@ static const struct mssr_mod_clk r8a77970_mod_clks[] __initconst = { }; static const unsigned int r8a77970_crit_mod_clks[] __initconst = { + MOD_CLK_ID(402), /* RWDT */ MOD_CLK_ID(408), /* INTC-AP (GIC) */ }; - /* * CPG Clock Data */ diff --git a/drivers/clk/renesas/r8a77980-cpg-mssr.c b/drivers/clk/renesas/r8a77980-cpg-mssr.c index 7227f675e61ff..9fe372286c1e9 100644 --- a/drivers/clk/renesas/r8a77980-cpg-mssr.c +++ b/drivers/clk/renesas/r8a77980-cpg-mssr.c @@ -180,10 +180,10 @@ static const struct mssr_mod_clk r8a77980_mod_clks[] __initconst = { }; static const unsigned int r8a77980_crit_mod_clks[] __initconst = { + MOD_CLK_ID(402), /* RWDT */ MOD_CLK_ID(408), /* INTC-AP (GIC) */ }; - /* * CPG Clock Data */ diff --git a/drivers/clk/renesas/r8a77990-cpg-mssr.c b/drivers/clk/renesas/r8a77990-cpg-mssr.c index 8eda2e3e24807..2b97ab61d044c 100644 --- a/drivers/clk/renesas/r8a77990-cpg-mssr.c +++ b/drivers/clk/renesas/r8a77990-cpg-mssr.c @@ -245,6 +245,7 @@ static const struct mssr_mod_clk r8a77990_mod_clks[] __initconst = { }; static const unsigned int r8a77990_crit_mod_clks[] __initconst = { + MOD_CLK_ID(402), /* RWDT */ MOD_CLK_ID(408), /* INTC-AP (GIC) */ }; diff --git a/drivers/clk/renesas/r8a77995-cpg-mssr.c b/drivers/clk/renesas/r8a77995-cpg-mssr.c index 056ebf3e70e2d..5b4691117b470 100644 --- a/drivers/clk/renesas/r8a77995-cpg-mssr.c +++ b/drivers/clk/renesas/r8a77995-cpg-mssr.c @@ -183,10 +183,10 @@ static const struct mssr_mod_clk r8a77995_mod_clks[] __initconst = { }; static const unsigned int r8a77995_crit_mod_clks[] __initconst = { + MOD_CLK_ID(402), /* RWDT */ MOD_CLK_ID(408), /* INTC-AP (GIC) */ }; - /* * CPG Clock Data */ -- GitLab From 52bc5ea6edde35bc65ed6ecd7639534e78002c74 Mon Sep 17 00:00:00 2001 From: Ulrich Hecht Date: Tue, 16 Jun 2020 18:26:26 +0200 Subject: [PATCH 0088/1476] clk: renesas: rzg2: Mark RWDT clocks as critical Ensures RWDT remains alert throughout the boot process if enabled. This patch applies the change to the following SoCs: r8a774a1, r8a774b1 and r8a774c0. Signed-off-by: Ulrich Hecht Link: https://lore.kernel.org/r/20200616162626.27944-4-uli+renesas@fpond.eu Signed-off-by: Geert Uytterhoeven --- drivers/clk/renesas/r8a774a1-cpg-mssr.c | 1 + drivers/clk/renesas/r8a774b1-cpg-mssr.c | 1 + drivers/clk/renesas/r8a774c0-cpg-mssr.c | 1 + 3 files changed, 3 insertions(+) diff --git a/drivers/clk/renesas/r8a774a1-cpg-mssr.c b/drivers/clk/renesas/r8a774a1-cpg-mssr.c index e05bfa2004802..fd54b9f625dad 100644 --- a/drivers/clk/renesas/r8a774a1-cpg-mssr.c +++ b/drivers/clk/renesas/r8a774a1-cpg-mssr.c @@ -237,6 +237,7 @@ static const struct mssr_mod_clk r8a774a1_mod_clks[] __initconst = { }; static const unsigned int r8a774a1_crit_mod_clks[] __initconst = { + MOD_CLK_ID(402), /* RWDT */ MOD_CLK_ID(408), /* INTC-AP (GIC) */ }; diff --git a/drivers/clk/renesas/r8a774b1-cpg-mssr.c b/drivers/clk/renesas/r8a774b1-cpg-mssr.c index c9af709173124..f436691271ece 100644 --- a/drivers/clk/renesas/r8a774b1-cpg-mssr.c +++ b/drivers/clk/renesas/r8a774b1-cpg-mssr.c @@ -233,6 +233,7 @@ static const struct mssr_mod_clk r8a774b1_mod_clks[] __initconst = { }; static const unsigned int r8a774b1_crit_mod_clks[] __initconst = { + MOD_CLK_ID(402), /* RWDT */ MOD_CLK_ID(408), /* INTC-AP (GIC) */ }; diff --git a/drivers/clk/renesas/r8a774c0-cpg-mssr.c b/drivers/clk/renesas/r8a774c0-cpg-mssr.c index f91e7a4847537..9fc9fa9e531ab 100644 --- a/drivers/clk/renesas/r8a774c0-cpg-mssr.c +++ b/drivers/clk/renesas/r8a774c0-cpg-mssr.c @@ -238,6 +238,7 @@ static const struct mssr_mod_clk r8a774c0_mod_clks[] __initconst = { }; static const unsigned int r8a774c0_crit_mod_clks[] __initconst = { + MOD_CLK_ID(402), /* RWDT */ MOD_CLK_ID(408), /* INTC-AP (GIC) */ }; -- GitLab From f2de5257dbb4a3437060546fe75551b5538d224b Mon Sep 17 00:00:00 2001 From: Vincent Knecht Date: Sat, 13 Jun 2020 09:27:43 +0200 Subject: [PATCH 0089/1476] dt-bindings: clock: rpmcc: Document MSM8936 compatible Acked-by: Rob Herring Signed-off-by: Vincent Knecht Link: https://lore.kernel.org/r/20200613072745.1249003-3-vincent.knecht@mailoo.org Signed-off-by: Stephen Boyd --- Documentation/devicetree/bindings/clock/qcom,rpmcc.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/clock/qcom,rpmcc.txt b/Documentation/devicetree/bindings/clock/qcom,rpmcc.txt index 86190acc71bcf..8786d19ffe173 100644 --- a/Documentation/devicetree/bindings/clock/qcom,rpmcc.txt +++ b/Documentation/devicetree/bindings/clock/qcom,rpmcc.txt @@ -13,6 +13,7 @@ Required properties : "qcom,rpmcc-msm8660", "qcom,rpmcc" "qcom,rpmcc-apq8060", "qcom,rpmcc" "qcom,rpmcc-msm8916", "qcom,rpmcc" + "qcom,rpmcc-msm8936", "qcom,rpmcc" "qcom,rpmcc-msm8974", "qcom,rpmcc" "qcom,rpmcc-msm8976", "qcom,rpmcc" "qcom,rpmcc-apq8064", "qcom,rpmcc" -- GitLab From 59390282b7542c6050c1deaca0b2949180903175 Mon Sep 17 00:00:00 2001 From: Vincent Knecht Date: Sat, 13 Jun 2020 09:27:42 +0200 Subject: [PATCH 0090/1476] clk: qcom: smd: Add support for MSM8936 rpm clocks Add missing definition of rpm clk for msm8936 soc (also used by msm8939) Signed-off-by: Vincent Knecht Link: https://lore.kernel.org/r/20200613072745.1249003-2-vincent.knecht@mailoo.org Signed-off-by: Stephen Boyd --- drivers/clk/qcom/clk-smd-rpm.c | 50 ++++++++++++++++++++++++++ include/dt-bindings/clock/qcom,rpmcc.h | 2 ++ 2 files changed, 52 insertions(+) diff --git a/drivers/clk/qcom/clk-smd-rpm.c b/drivers/clk/qcom/clk-smd-rpm.c index 643bc355df5cb..083399affc8e0 100644 --- a/drivers/clk/qcom/clk-smd-rpm.c +++ b/drivers/clk/qcom/clk-smd-rpm.c @@ -452,6 +452,55 @@ static const struct rpm_smd_clk_desc rpm_clk_msm8916 = { .num_clks = ARRAY_SIZE(msm8916_clks), }; +/* msm8936 */ +DEFINE_CLK_SMD_RPM(msm8936, pcnoc_clk, pcnoc_a_clk, QCOM_SMD_RPM_BUS_CLK, 0); +DEFINE_CLK_SMD_RPM(msm8936, snoc_clk, snoc_a_clk, QCOM_SMD_RPM_BUS_CLK, 1); +DEFINE_CLK_SMD_RPM(msm8936, bimc_clk, bimc_a_clk, QCOM_SMD_RPM_MEM_CLK, 0); +DEFINE_CLK_SMD_RPM(msm8936, sysmmnoc_clk, sysmmnoc_a_clk, QCOM_SMD_RPM_BUS_CLK, 2); +DEFINE_CLK_SMD_RPM_QDSS(msm8936, qdss_clk, qdss_a_clk, QCOM_SMD_RPM_MISC_CLK, 1); +DEFINE_CLK_SMD_RPM_XO_BUFFER(msm8936, bb_clk1, bb_clk1_a, 1); +DEFINE_CLK_SMD_RPM_XO_BUFFER(msm8936, bb_clk2, bb_clk2_a, 2); +DEFINE_CLK_SMD_RPM_XO_BUFFER(msm8936, rf_clk1, rf_clk1_a, 4); +DEFINE_CLK_SMD_RPM_XO_BUFFER(msm8936, rf_clk2, rf_clk2_a, 5); +DEFINE_CLK_SMD_RPM_XO_BUFFER_PINCTRL(msm8936, bb_clk1_pin, bb_clk1_a_pin, 1); +DEFINE_CLK_SMD_RPM_XO_BUFFER_PINCTRL(msm8936, bb_clk2_pin, bb_clk2_a_pin, 2); +DEFINE_CLK_SMD_RPM_XO_BUFFER_PINCTRL(msm8936, rf_clk1_pin, rf_clk1_a_pin, 4); +DEFINE_CLK_SMD_RPM_XO_BUFFER_PINCTRL(msm8936, rf_clk2_pin, rf_clk2_a_pin, 5); + +static struct clk_smd_rpm *msm8936_clks[] = { + [RPM_SMD_PCNOC_CLK] = &msm8936_pcnoc_clk, + [RPM_SMD_PCNOC_A_CLK] = &msm8936_pcnoc_a_clk, + [RPM_SMD_SNOC_CLK] = &msm8936_snoc_clk, + [RPM_SMD_SNOC_A_CLK] = &msm8936_snoc_a_clk, + [RPM_SMD_BIMC_CLK] = &msm8936_bimc_clk, + [RPM_SMD_BIMC_A_CLK] = &msm8936_bimc_a_clk, + [RPM_SMD_SYSMMNOC_CLK] = &msm8936_sysmmnoc_clk, + [RPM_SMD_SYSMMNOC_A_CLK] = &msm8936_sysmmnoc_a_clk, + [RPM_SMD_QDSS_CLK] = &msm8936_qdss_clk, + [RPM_SMD_QDSS_A_CLK] = &msm8936_qdss_a_clk, + [RPM_SMD_BB_CLK1] = &msm8936_bb_clk1, + [RPM_SMD_BB_CLK1_A] = &msm8936_bb_clk1_a, + [RPM_SMD_BB_CLK2] = &msm8936_bb_clk2, + [RPM_SMD_BB_CLK2_A] = &msm8936_bb_clk2_a, + [RPM_SMD_RF_CLK1] = &msm8936_rf_clk1, + [RPM_SMD_RF_CLK1_A] = &msm8936_rf_clk1_a, + [RPM_SMD_RF_CLK2] = &msm8936_rf_clk2, + [RPM_SMD_RF_CLK2_A] = &msm8936_rf_clk2_a, + [RPM_SMD_BB_CLK1_PIN] = &msm8936_bb_clk1_pin, + [RPM_SMD_BB_CLK1_A_PIN] = &msm8936_bb_clk1_a_pin, + [RPM_SMD_BB_CLK2_PIN] = &msm8936_bb_clk2_pin, + [RPM_SMD_BB_CLK2_A_PIN] = &msm8936_bb_clk2_a_pin, + [RPM_SMD_RF_CLK1_PIN] = &msm8936_rf_clk1_pin, + [RPM_SMD_RF_CLK1_A_PIN] = &msm8936_rf_clk1_a_pin, + [RPM_SMD_RF_CLK2_PIN] = &msm8936_rf_clk2_pin, + [RPM_SMD_RF_CLK2_A_PIN] = &msm8936_rf_clk2_a_pin, +}; + +static const struct rpm_smd_clk_desc rpm_clk_msm8936 = { + .clks = msm8936_clks, + .num_clks = ARRAY_SIZE(msm8936_clks), +}; + /* msm8974 */ DEFINE_CLK_SMD_RPM(msm8974, pnoc_clk, pnoc_a_clk, QCOM_SMD_RPM_BUS_CLK, 0); DEFINE_CLK_SMD_RPM(msm8974, snoc_clk, snoc_a_clk, QCOM_SMD_RPM_BUS_CLK, 1); @@ -843,6 +892,7 @@ static const struct rpm_smd_clk_desc rpm_clk_sdm660 = { static const struct of_device_id rpm_smd_clk_match_table[] = { { .compatible = "qcom,rpmcc-msm8916", .data = &rpm_clk_msm8916 }, + { .compatible = "qcom,rpmcc-msm8936", .data = &rpm_clk_msm8936 }, { .compatible = "qcom,rpmcc-msm8974", .data = &rpm_clk_msm8974 }, { .compatible = "qcom,rpmcc-msm8976", .data = &rpm_clk_msm8976 }, { .compatible = "qcom,rpmcc-msm8996", .data = &rpm_clk_msm8996 }, diff --git a/include/dt-bindings/clock/qcom,rpmcc.h b/include/dt-bindings/clock/qcom,rpmcc.h index d1afa634b58d2..e98ed70d91b38 100644 --- a/include/dt-bindings/clock/qcom,rpmcc.h +++ b/include/dt-bindings/clock/qcom,rpmcc.h @@ -143,5 +143,7 @@ #define RPM_SMD_LN_BB_CLK1_A_PIN 97 #define RPM_SMD_LN_BB_CLK2_PIN 98 #define RPM_SMD_LN_BB_CLK2_A_PIN 99 +#define RPM_SMD_SYSMMNOC_CLK 100 +#define RPM_SMD_SYSMMNOC_A_CLK 101 #endif -- GitLab From f491276a5168598758ea7fc381195e4ba9af39f8 Mon Sep 17 00:00:00 2001 From: Adam Ford Date: Wed, 3 Jun 2020 10:43:27 -0500 Subject: [PATCH 0091/1476] clk: vc5: Allow Versaclock driver to support multiple instances Currently, the Versaclock driver is only expecting one instance and uses hard-coded names for the various clock names. Unfortunately, this is a problem when there is more than one instance of the driver, because the subsequent instantiations of the driver use the identical name. Each clock after the fist fails to load, because the clock subsystem cannot handle two clocks with identical name. This patch removes the hard-coded name arrays and uses kasprintf to assign clock names based on names of their respective node and parent node which gives each clock a unique identifying name. For a verasaclock node with a name like: versaclock5: versaclock_som@6a The updated clock names would appear like: versaclock_som.mux versaclock_som.out0_sel_i2cb versaclock_som.pfd versaclock_som.pll versaclock_som.fod3 versaclock_som.out4 versaclock_som.fod2 versaclock_som.out3 versaclock_som.fod1 versaclock_som.out2 versaclock_som.fod0 versaclock_som.out1 Signed-off-by: Adam Ford Link: https://lore.kernel.org/r/20200603154329.31579-1-aford173@gmail.com Signed-off-by: Stephen Boyd --- drivers/clk/clk-versaclock5.c | 84 +++++++++++++++-------------------- 1 file changed, 37 insertions(+), 47 deletions(-) diff --git a/drivers/clk/clk-versaclock5.c b/drivers/clk/clk-versaclock5.c index fa96659f8023d..41e3a75963b93 100644 --- a/drivers/clk/clk-versaclock5.c +++ b/drivers/clk/clk-versaclock5.c @@ -161,30 +161,6 @@ struct vc5_driver_data { struct vc5_hw_data clk_out[VC5_MAX_CLK_OUT_NUM]; }; -static const char * const vc5_mux_names[] = { - "mux" -}; - -static const char * const vc5_dbl_names[] = { - "dbl" -}; - -static const char * const vc5_pfd_names[] = { - "pfd" -}; - -static const char * const vc5_pll_names[] = { - "pll" -}; - -static const char * const vc5_fod_names[] = { - "fod0", "fod1", "fod2", "fod3", -}; - -static const char * const vc5_clk_out_names[] = { - "out0_sel_i2cb", "out1", "out2", "out3", "out4", -}; - /* * VersaClock5 i2c regmap */ @@ -692,8 +668,7 @@ static int vc5_map_index_to_output(const enum vc5_model model, static const struct of_device_id clk_vc5_of_match[]; -static int vc5_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int vc5_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct vc5_driver_data *vc5; struct clk_init_data init; @@ -742,7 +717,7 @@ static int vc5_probe(struct i2c_client *client, if (!IS_ERR(vc5->pin_clkin)) { vc5->clk_mux_ins |= VC5_MUX_IN_CLKIN; parent_names[init.num_parents++] = - __clk_get_name(vc5->pin_clkin); + __clk_get_name(vc5->pin_clkin); } if (!init.num_parents) { @@ -750,12 +725,13 @@ static int vc5_probe(struct i2c_client *client, return -EINVAL; } - init.name = vc5_mux_names[0]; + init.name = kasprintf(GFP_KERNEL, "%pOFn.mux", client->dev.of_node); init.ops = &vc5_mux_ops; init.flags = 0; init.parent_names = parent_names; vc5->clk_mux.init = &init; ret = devm_clk_hw_register(&client->dev, &vc5->clk_mux); + kfree(init.name); /* clock framework made a copy of the name */ if (ret) { dev_err(&client->dev, "unable to register %s\n", init.name); goto err_clk; @@ -764,13 +740,16 @@ static int vc5_probe(struct i2c_client *client, if (vc5->chip_info->flags & VC5_HAS_PFD_FREQ_DBL) { /* Register frequency doubler */ memset(&init, 0, sizeof(init)); - init.name = vc5_dbl_names[0]; + init.name = kasprintf(GFP_KERNEL, "%pOFn.dbl", + client->dev.of_node); init.ops = &vc5_dbl_ops; init.flags = CLK_SET_RATE_PARENT; - init.parent_names = vc5_mux_names; + init.parent_names = parent_names; + parent_names[0] = clk_hw_get_name(&vc5->clk_mux); init.num_parents = 1; vc5->clk_mul.init = &init; ret = devm_clk_hw_register(&client->dev, &vc5->clk_mul); + kfree(init.name); /* clock framework made a copy of the name */ if (ret) { dev_err(&client->dev, "unable to register %s\n", init.name); @@ -780,16 +759,18 @@ static int vc5_probe(struct i2c_client *client, /* Register PFD */ memset(&init, 0, sizeof(init)); - init.name = vc5_pfd_names[0]; + init.name = kasprintf(GFP_KERNEL, "%pOFn.pfd", client->dev.of_node); init.ops = &vc5_pfd_ops; init.flags = CLK_SET_RATE_PARENT; + init.parent_names = parent_names; if (vc5->chip_info->flags & VC5_HAS_PFD_FREQ_DBL) - init.parent_names = vc5_dbl_names; + parent_names[0] = clk_hw_get_name(&vc5->clk_mul); else - init.parent_names = vc5_mux_names; + parent_names[0] = clk_hw_get_name(&vc5->clk_mux); init.num_parents = 1; vc5->clk_pfd.init = &init; ret = devm_clk_hw_register(&client->dev, &vc5->clk_pfd); + kfree(init.name); /* clock framework made a copy of the name */ if (ret) { dev_err(&client->dev, "unable to register %s\n", init.name); goto err_clk; @@ -797,15 +778,17 @@ static int vc5_probe(struct i2c_client *client, /* Register PLL */ memset(&init, 0, sizeof(init)); - init.name = vc5_pll_names[0]; + init.name = kasprintf(GFP_KERNEL, "%pOFn.pll", client->dev.of_node); init.ops = &vc5_pll_ops; init.flags = CLK_SET_RATE_PARENT; - init.parent_names = vc5_pfd_names; + init.parent_names = parent_names; + parent_names[0] = clk_hw_get_name(&vc5->clk_pfd); init.num_parents = 1; vc5->clk_pll.num = 0; vc5->clk_pll.vc5 = vc5; vc5->clk_pll.hw.init = &init; ret = devm_clk_hw_register(&client->dev, &vc5->clk_pll.hw); + kfree(init.name); /* clock framework made a copy of the name */ if (ret) { dev_err(&client->dev, "unable to register %s\n", init.name); goto err_clk; @@ -815,15 +798,18 @@ static int vc5_probe(struct i2c_client *client, for (n = 0; n < vc5->chip_info->clk_fod_cnt; n++) { idx = vc5_map_index_to_output(vc5->chip_info->model, n); memset(&init, 0, sizeof(init)); - init.name = vc5_fod_names[idx]; + init.name = kasprintf(GFP_KERNEL, "%pOFn.fod%d", + client->dev.of_node, idx); init.ops = &vc5_fod_ops; init.flags = CLK_SET_RATE_PARENT; - init.parent_names = vc5_pll_names; + init.parent_names = parent_names; + parent_names[0] = clk_hw_get_name(&vc5->clk_pll.hw); init.num_parents = 1; vc5->clk_fod[n].num = idx; vc5->clk_fod[n].vc5 = vc5; vc5->clk_fod[n].hw.init = &init; ret = devm_clk_hw_register(&client->dev, &vc5->clk_fod[n].hw); + kfree(init.name); /* clock framework made a copy of the name */ if (ret) { dev_err(&client->dev, "unable to register %s\n", init.name); @@ -833,32 +819,36 @@ static int vc5_probe(struct i2c_client *client, /* Register MUX-connected OUT0_I2C_SELB output */ memset(&init, 0, sizeof(init)); - init.name = vc5_clk_out_names[0]; + init.name = kasprintf(GFP_KERNEL, "%pOFn.out0_sel_i2cb", + client->dev.of_node); init.ops = &vc5_clk_out_ops; init.flags = CLK_SET_RATE_PARENT; - init.parent_names = vc5_mux_names; + init.parent_names = parent_names; + parent_names[0] = clk_hw_get_name(&vc5->clk_mux); init.num_parents = 1; vc5->clk_out[0].num = idx; vc5->clk_out[0].vc5 = vc5; vc5->clk_out[0].hw.init = &init; ret = devm_clk_hw_register(&client->dev, &vc5->clk_out[0].hw); + kfree(init.name); /* clock framework made a copy of the name */ if (ret) { - dev_err(&client->dev, "unable to register %s\n", - init.name); + dev_err(&client->dev, "unable to register %s\n", init.name); goto err_clk; } /* Register FOD-connected OUTx outputs */ for (n = 1; n < vc5->chip_info->clk_out_cnt; n++) { idx = vc5_map_index_to_output(vc5->chip_info->model, n - 1); - parent_names[0] = vc5_fod_names[idx]; + parent_names[0] = clk_hw_get_name(&vc5->clk_fod[idx].hw); if (n == 1) - parent_names[1] = vc5_mux_names[0]; + parent_names[1] = clk_hw_get_name(&vc5->clk_mux); else - parent_names[1] = vc5_clk_out_names[n - 1]; + parent_names[1] = + clk_hw_get_name(&vc5->clk_out[n - 1].hw); memset(&init, 0, sizeof(init)); - init.name = vc5_clk_out_names[idx + 1]; + init.name = kasprintf(GFP_KERNEL, "%pOFn.out%d", + client->dev.of_node, idx + 1); init.ops = &vc5_clk_out_ops; init.flags = CLK_SET_RATE_PARENT; init.parent_names = parent_names; @@ -866,8 +856,8 @@ static int vc5_probe(struct i2c_client *client, vc5->clk_out[n].num = idx; vc5->clk_out[n].vc5 = vc5; vc5->clk_out[n].hw.init = &init; - ret = devm_clk_hw_register(&client->dev, - &vc5->clk_out[n].hw); + ret = devm_clk_hw_register(&client->dev, &vc5->clk_out[n].hw); + kfree(init.name); /* clock framework made a copy of the name */ if (ret) { dev_err(&client->dev, "unable to register %s\n", init.name); -- GitLab From 34662f6e30846ae0f82bbc9605deff67781f6616 Mon Sep 17 00:00:00 2001 From: Adam Ford Date: Wed, 3 Jun 2020 10:43:28 -0500 Subject: [PATCH 0092/1476] dt: Add additional option bindings for IDT VersaClock The VersaClock driver now supports some additional bindings to support child nodes which can configure optional settings like mode, voltage and slew. This patch updates the binding document to describe what is available in the driver. Signed-off-by: Adam Ford Reviewed-by: Rob Herring Link: https://lore.kernel.org/r/20200603154329.31579-2-aford173@gmail.com Signed-off-by: Stephen Boyd --- .../bindings/clock/idt,versaclock5.txt | 33 +++++++++++++++++++ include/dt-bindings/clk/versaclock.h | 13 ++++++++ 2 files changed, 46 insertions(+) create mode 100644 include/dt-bindings/clk/versaclock.h diff --git a/Documentation/devicetree/bindings/clock/idt,versaclock5.txt b/Documentation/devicetree/bindings/clock/idt,versaclock5.txt index bcff681a4bd0b..6165b6ddb1a9c 100644 --- a/Documentation/devicetree/bindings/clock/idt,versaclock5.txt +++ b/Documentation/devicetree/bindings/clock/idt,versaclock5.txt @@ -31,6 +31,29 @@ Required properties: - 5p49v5933 and - 5p49v5935: (optional) property not present or "clkin". +For all output ports, a corresponding, optional child node named OUT1, +OUT2, etc. can represent a each output, and the node can be used to +specify the following: + +- itd,mode: can be one of the following: + - VC5_LVPECL + - VC5_CMOS + - VC5_HCSL33 + - VC5_LVDS + - VC5_CMOS2 + - VC5_CMOSD + - VC5_HCSL25 + +- idt,voltage-microvolts: can be one of the following + - 1800000 + - 2500000 + - 3300000 +- idt,slew-percent: Percent of normal, can be one of + - 80 + - 85 + - 90 + - 100 + ==Mapping between clock specifier and physical pins== When referencing the provided clock in the DT using phandle and @@ -81,6 +104,16 @@ i2c-master-node { /* Connect XIN input to 25MHz reference */ clocks = <&ref25m>; clock-names = "xin"; + + OUT1 { + itd,mode = ; + idt,voltage-microvolts = <1800000>; + idt,slew-percent = <80>; + }; + OUT2 { + ... + }; + ... }; }; diff --git a/include/dt-bindings/clk/versaclock.h b/include/dt-bindings/clk/versaclock.h new file mode 100644 index 0000000000000..c6a6a0946564c --- /dev/null +++ b/include/dt-bindings/clk/versaclock.h @@ -0,0 +1,13 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +/* This file defines field values used by the versaclock 6 family + * for defining output type + */ + +#define VC5_LVPECL 0 +#define VC5_CMOS 1 +#define VC5_HCSL33 2 +#define VC5_LVDS 3 +#define VC5_CMOS2 4 +#define VC5_CMOSD 5 +#define VC5_HCSL25 6 -- GitLab From 260249f929e81d3d5764117fdd6b9e43eb8fb1d5 Mon Sep 17 00:00:00 2001 From: Adam Ford Date: Wed, 3 Jun 2020 10:43:29 -0500 Subject: [PATCH 0093/1476] clk: vc5: Enable addition output configurations of the Versaclock MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The existing driver is expecting the Versaclock to be pre-programmed, and only sets the output frequency. Unfortunately, not all devices are pre-programmed, and the Versaclock chip has more options beyond just the frequency. This patch enables the following additional features: - Programmable voltage: 1.8V, 2.5V, or 3.3V​ - Slew Percentage of normal: 85%, 90%, or 100% - Output Type: LVPECL, CMOS, HCSL, or LVDS Signed-off-by: Adam Ford Link: https://lore.kernel.org/r/20200603154329.31579-3-aford173@gmail.com Signed-off-by: Stephen Boyd --- drivers/clk/clk-versaclock5.c | 156 ++++++++++++++++++++++++++++++++++ 1 file changed, 156 insertions(+) diff --git a/drivers/clk/clk-versaclock5.c b/drivers/clk/clk-versaclock5.c index 41e3a75963b93..9a5fb3834b9a4 100644 --- a/drivers/clk/clk-versaclock5.c +++ b/drivers/clk/clk-versaclock5.c @@ -24,6 +24,8 @@ #include #include +#include + /* VersaClock5 registers */ #define VC5_OTP_CONTROL 0x00 @@ -89,6 +91,28 @@ /* Clock control register for clock 1,2 */ #define VC5_CLK_OUTPUT_CFG(idx, n) (0x60 + ((idx) * 0x2) + (n)) +#define VC5_CLK_OUTPUT_CFG0_CFG_SHIFT 5 +#define VC5_CLK_OUTPUT_CFG0_CFG_MASK GENMASK(7, VC5_CLK_OUTPUT_CFG0_CFG_SHIFT) + +#define VC5_CLK_OUTPUT_CFG0_CFG_LVPECL (VC5_LVPECL) +#define VC5_CLK_OUTPUT_CFG0_CFG_CMOS (VC5_CMOS) +#define VC5_CLK_OUTPUT_CFG0_CFG_HCSL33 (VC5_HCSL33) +#define VC5_CLK_OUTPUT_CFG0_CFG_LVDS (VC5_LVDS) +#define VC5_CLK_OUTPUT_CFG0_CFG_CMOS2 (VC5_CMOS2) +#define VC5_CLK_OUTPUT_CFG0_CFG_CMOSD (VC5_CMOSD) +#define VC5_CLK_OUTPUT_CFG0_CFG_HCSL25 (VC5_HCSL25) + +#define VC5_CLK_OUTPUT_CFG0_PWR_SHIFT 3 +#define VC5_CLK_OUTPUT_CFG0_PWR_MASK GENMASK(4, VC5_CLK_OUTPUT_CFG0_PWR_SHIFT) +#define VC5_CLK_OUTPUT_CFG0_PWR_18 (0<regmap, VC5_CLK_OUTPUT_CFG(hwdata->num, 1), VC5_CLK_OUTPUT_CFG1_EN_CLKBUF, VC5_CLK_OUTPUT_CFG1_EN_CLKBUF); + if (hwdata->clk_output_cfg0_mask) { + dev_dbg(&vc5->client->dev, "Update output %d mask 0x%0X val 0x%0X\n", + hwdata->num, hwdata->clk_output_cfg0_mask, + hwdata->clk_output_cfg0); + + regmap_update_bits(vc5->regmap, + VC5_CLK_OUTPUT_CFG(hwdata->num, 0), + hwdata->clk_output_cfg0_mask, + hwdata->clk_output_cfg0); + } + return 0; } @@ -666,6 +703,120 @@ static int vc5_map_index_to_output(const enum vc5_model model, } } +static int vc5_update_mode(struct device_node *np_output, + struct vc5_hw_data *clk_out) +{ + u32 value; + + if (!of_property_read_u32(np_output, "idt,mode", &value)) { + clk_out->clk_output_cfg0_mask |= VC5_CLK_OUTPUT_CFG0_CFG_MASK; + switch (value) { + case VC5_CLK_OUTPUT_CFG0_CFG_LVPECL: + case VC5_CLK_OUTPUT_CFG0_CFG_CMOS: + case VC5_CLK_OUTPUT_CFG0_CFG_HCSL33: + case VC5_CLK_OUTPUT_CFG0_CFG_LVDS: + case VC5_CLK_OUTPUT_CFG0_CFG_CMOS2: + case VC5_CLK_OUTPUT_CFG0_CFG_CMOSD: + case VC5_CLK_OUTPUT_CFG0_CFG_HCSL25: + clk_out->clk_output_cfg0 |= + value << VC5_CLK_OUTPUT_CFG0_CFG_SHIFT; + break; + default: + return -EINVAL; + } + } + return 0; +} + +static int vc5_update_power(struct device_node *np_output, + struct vc5_hw_data *clk_out) +{ + u32 value; + + if (!of_property_read_u32(np_output, + "idt,voltage-microvolts", &value)) { + clk_out->clk_output_cfg0_mask |= VC5_CLK_OUTPUT_CFG0_PWR_MASK; + switch (value) { + case 1800000: + clk_out->clk_output_cfg0 |= VC5_CLK_OUTPUT_CFG0_PWR_18; + break; + case 2500000: + clk_out->clk_output_cfg0 |= VC5_CLK_OUTPUT_CFG0_PWR_25; + break; + case 3300000: + clk_out->clk_output_cfg0 |= VC5_CLK_OUTPUT_CFG0_PWR_33; + break; + default: + return -EINVAL; + } + } + return 0; +} + +static int vc5_update_slew(struct device_node *np_output, + struct vc5_hw_data *clk_out) +{ + u32 value; + + if (!of_property_read_u32(np_output, "idt,slew-percent", &value)) { + clk_out->clk_output_cfg0_mask |= VC5_CLK_OUTPUT_CFG0_SLEW_MASK; + switch (value) { + case 80: + clk_out->clk_output_cfg0 |= VC5_CLK_OUTPUT_CFG0_SLEW_80; + break; + case 85: + clk_out->clk_output_cfg0 |= VC5_CLK_OUTPUT_CFG0_SLEW_85; + break; + case 90: + clk_out->clk_output_cfg0 |= VC5_CLK_OUTPUT_CFG0_SLEW_90; + break; + case 100: + clk_out->clk_output_cfg0 |= + VC5_CLK_OUTPUT_CFG0_SLEW_100; + break; + default: + return -EINVAL; + } + } + return 0; +} + +static int vc5_get_output_config(struct i2c_client *client, + struct vc5_hw_data *clk_out) +{ + struct device_node *np_output; + char *child_name; + int ret = 0; + + child_name = kasprintf(GFP_KERNEL, "OUT%d", clk_out->num + 1); + np_output = of_get_child_by_name(client->dev.of_node, child_name); + kfree(child_name); + if (!np_output) + goto output_done; + + ret = vc5_update_mode(np_output, clk_out); + if (ret) + goto output_error; + + ret = vc5_update_power(np_output, clk_out); + if (ret) + goto output_error; + + ret = vc5_update_slew(np_output, clk_out); + +output_error: + if (ret) { + dev_err(&client->dev, + "Invalid clock output configuration OUT%d\n", + clk_out->num + 1); + } + + of_node_put(np_output); + +output_done: + return ret; +} + static const struct of_device_id clk_vc5_of_match[]; static int vc5_probe(struct i2c_client *client, const struct i2c_device_id *id) @@ -863,6 +1014,11 @@ static int vc5_probe(struct i2c_client *client, const struct i2c_device_id *id) init.name); goto err_clk; } + + /* Fetch Clock Output configuration from DT (if specified) */ + ret = vc5_get_output_config(client, &vc5->clk_out[n]); + if (ret) + goto err_clk; } ret = of_clk_add_hw_provider(client->dev.of_node, vc5_of_clk_get, vc5); -- GitLab From 49ef12215f112d442e487903d477831f38ce7cb2 Mon Sep 17 00:00:00 2001 From: Sibi Sankar Date: Mon, 22 Jun 2020 13:46:45 +0530 Subject: [PATCH 0094/1476] cpufreq: blacklist SDM845 in cpufreq-dt-platdev Add SDM845 to cpufreq-dt-platdev blacklist since the actual scaling is handled by the 'qcom-cpufreq-hw' driver. Signed-off-by: Sibi Sankar Reviewed-by: Amit Kucheria Reviewed-by: Matthias Kaehlcke Signed-off-by: Viresh Kumar --- drivers/cpufreq/cpufreq-dt-platdev.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/cpufreq/cpufreq-dt-platdev.c b/drivers/cpufreq/cpufreq-dt-platdev.c index e8e20fef400b0..be85eb494a6b3 100644 --- a/drivers/cpufreq/cpufreq-dt-platdev.c +++ b/drivers/cpufreq/cpufreq-dt-platdev.c @@ -132,6 +132,7 @@ static const struct of_device_id blacklist[] __initconst = { { .compatible = "qcom,apq8096", }, { .compatible = "qcom,msm8996", }, { .compatible = "qcom,qcs404", }, + { .compatible = "qcom,sdm845", }, { .compatible = "st,stih407", }, { .compatible = "st,stih410", }, -- GitLab From fb091802d793e87b4ed7f83c94e29aadb7fe58b7 Mon Sep 17 00:00:00 2001 From: Sibi Sankar Date: Mon, 22 Jun 2020 13:46:46 +0530 Subject: [PATCH 0095/1476] cpufreq: blacklist SC7180 in cpufreq-dt-platdev Add SC7180 to cpufreq-dt-platdev blacklist since the actual scaling is handled by the 'qcom-cpufreq-hw' driver. Signed-off-by: Sibi Sankar Reviewed-by: Amit Kucheria Reviewed-by: Matthias Kaehlcke Signed-off-by: Viresh Kumar --- drivers/cpufreq/cpufreq-dt-platdev.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/cpufreq/cpufreq-dt-platdev.c b/drivers/cpufreq/cpufreq-dt-platdev.c index be85eb494a6b3..7d01df7bfa6cd 100644 --- a/drivers/cpufreq/cpufreq-dt-platdev.c +++ b/drivers/cpufreq/cpufreq-dt-platdev.c @@ -132,6 +132,7 @@ static const struct of_device_id blacklist[] __initconst = { { .compatible = "qcom,apq8096", }, { .compatible = "qcom,msm8996", }, { .compatible = "qcom,qcs404", }, + { .compatible = "qcom,sc7180", }, { .compatible = "qcom,sdm845", }, { .compatible = "st,stih407", }, -- GitLab From 9e5eb9a451837946d93e077e0bf02bfcd203e4be Mon Sep 17 00:00:00 2001 From: Vishal Sagar Date: Wed, 27 May 2020 15:57:18 +0200 Subject: [PATCH 0096/1476] media: dt-bindings: media: xilinx: Add Xilinx MIPI CSI-2 Rx Subsystem Add bindings documentation for Xilinx MIPI CSI-2 Rx Subsystem. The Xilinx MIPI CSI-2 Rx Subsystem consists of a CSI-2 Rx controller, a D-PHY in Rx mode and a Video Format Bridge. [Fix indentation error in if...then...else] [Fix number of cells in reg property] Signed-off-by: Vishal Sagar Reviewed-by: Hyun Kwon Reviewed-by: Rob Herring Reviewed-by: Luca Ceresoli Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- .../bindings/media/xilinx/xlnx,csi2rxss.yaml | 237 ++++++++++++++++++ 1 file changed, 237 insertions(+) create mode 100644 Documentation/devicetree/bindings/media/xilinx/xlnx,csi2rxss.yaml diff --git a/Documentation/devicetree/bindings/media/xilinx/xlnx,csi2rxss.yaml b/Documentation/devicetree/bindings/media/xilinx/xlnx,csi2rxss.yaml new file mode 100644 index 0000000000000..7b9407c0520ef --- /dev/null +++ b/Documentation/devicetree/bindings/media/xilinx/xlnx,csi2rxss.yaml @@ -0,0 +1,237 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/media/xilinx/xlnx,csi2rxss.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Xilinx MIPI CSI-2 Receiver Subsystem + +maintainers: + - Vishal Sagar + +description: | + The Xilinx MIPI CSI-2 Receiver Subsystem is used to capture MIPI CSI-2 + traffic from compliant camera sensors and send the output as AXI4 Stream + video data for image processing. + The subsystem consists of a MIPI D-PHY in slave mode which captures the + data packets. This is passed along the MIPI CSI-2 Rx IP which extracts the + packet data. The optional Video Format Bridge (VFB) converts this data to + AXI4 Stream video data. + For more details, please refer to PG232 Xilinx MIPI CSI-2 Receiver Subsystem. + Please note that this bindings includes only the MIPI CSI-2 Rx controller + and Video Format Bridge and not D-PHY. + +properties: + compatible: + items: + - enum: + - xlnx,mipi-csi2-rx-subsystem-5.0 + + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + + clocks: + description: List of clock specifiers + items: + - description: AXI Lite clock + - description: Video clock + + clock-names: + items: + - const: lite_aclk + - const: video_aclk + + xlnx,csi-pxl-format: + description: | + This denotes the CSI Data type selected in hw design. + Packets other than this data type (except for RAW8 and + User defined data types) will be filtered out. + Possible values are as below - + 0x1e - YUV4228B + 0x1f - YUV42210B + 0x20 - RGB444 + 0x21 - RGB555 + 0x22 - RGB565 + 0x23 - RGB666 + 0x24 - RGB888 + 0x28 - RAW6 + 0x29 - RAW7 + 0x2a - RAW8 + 0x2b - RAW10 + 0x2c - RAW12 + 0x2d - RAW14 + 0x2e - RAW16 + 0x2f - RAW20 + allOf: + - $ref: /schemas/types.yaml#/definitions/uint32 + - anyOf: + - minimum: 0x1e + - maximum: 0x24 + - minimum: 0x28 + - maximum: 0x2f + + xlnx,vfb: + type: boolean + description: Present when Video Format Bridge is enabled in IP configuration + + xlnx,en-csi-v2-0: + type: boolean + description: Present if CSI v2 is enabled in IP configuration. + + xlnx,en-vcx: + type: boolean + description: | + When present, there are maximum 16 virtual channels, else only 4. + + xlnx,en-active-lanes: + type: boolean + description: | + Present if the number of active lanes can be re-configured at + runtime in the Protocol Configuration Register. Otherwise all lanes, + as set in IP configuration, are always active. + + video-reset-gpios: + description: Optional specifier for a GPIO that asserts video_aresetn. + maxItems: 1 + + ports: + type: object + + properties: + port@0: + type: object + description: | + Input / sink port node, single endpoint describing the + CSI-2 transmitter. + + properties: + reg: + const: 0 + + endpoint: + type: object + + properties: + + data-lanes: + description: | + This is required only in the sink port 0 endpoint which + connects to MIPI CSI-2 source like sensor. + The possible values are - + 1 - For 1 lane enabled in IP. + 1 2 - For 2 lanes enabled in IP. + 1 2 3 - For 3 lanes enabled in IP. + 1 2 3 4 - For 4 lanes enabled in IP. + items: + - const: 1 + - const: 2 + - const: 3 + - const: 4 + + remote-endpoint: true + + required: + - data-lanes + - remote-endpoint + + additionalProperties: false + + additionalProperties: false + + port@1: + type: object + description: | + Output / source port node, endpoint describing modules + connected the CSI-2 receiver. + + properties: + + reg: + const: 1 + + endpoint: + type: object + + properties: + + remote-endpoint: true + + required: + - remote-endpoint + + additionalProperties: false + + additionalProperties: false + +required: + - compatible + - reg + - interrupts + - clocks + - clock-names + - ports + +allOf: + - if: + required: + - xlnx,vfb + then: + required: + - xlnx,csi-pxl-format + else: + properties: + xlnx,csi-pxl-format: false + + - if: + not: + required: + - xlnx,en-csi-v2-0 + then: + properties: + xlnx,en-vcx: false + +additionalProperties: false + +examples: + - | + #include + xcsi2rxss_1: csi2rx@a0020000 { + compatible = "xlnx,mipi-csi2-rx-subsystem-5.0"; + reg = <0xa0020000 0x10000>; + interrupt-parent = <&gic>; + interrupts = <0 95 4>; + xlnx,csi-pxl-format = <0x2a>; + xlnx,vfb; + xlnx,en-active-lanes; + xlnx,en-csi-v2-0; + xlnx,en-vcx; + clock-names = "lite_aclk", "video_aclk"; + clocks = <&misc_clk_0>, <&misc_clk_1>; + video-reset-gpios = <&gpio 86 GPIO_ACTIVE_LOW>; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + /* Sink port */ + reg = <0>; + csiss_in: endpoint { + data-lanes = <1 2 3 4>; + /* MIPI CSI-2 Camera handle */ + remote-endpoint = <&camera_out>; + }; + }; + port@1 { + /* Source port */ + reg = <1>; + csiss_out: endpoint { + remote-endpoint = <&vproc_in>; + }; + }; + }; + }; +... -- GitLab From ba5bf51acf0075b193878a56ea3741982391da9c Mon Sep 17 00:00:00 2001 From: Vishal Sagar Date: Wed, 27 May 2020 15:57:19 +0200 Subject: [PATCH 0097/1476] media: v4l: xilinx: Add Xilinx MIPI CSI-2 Rx Subsystem driver The Xilinx MIPI CSI-2 Rx Subsystem soft IP is used to capture images from MIPI CSI-2 camera sensors and output AXI4-Stream video data ready for image processing. Please refer to PG232 for details. The CSI2 Rx controller filters out all packets except for the packets with data type fixed in hardware. RAW8 packets are always allowed to pass through. It is also used to setup and handle interrupts and enable the core. It logs all the events in respective counters between streaming on and off. The driver supports only the video format bridge enabled configuration. Some data types like YUV 422 10bpc, RAW16, RAW20 are supported when the CSI v2.0 feature is enabled in design. When the VCX feature is enabled, the maximum number of virtual channels becomes 16 from 4. Signed-off-by: Vishal Sagar Reviewed-by: Hyun Kwon Reviewed-by: Luca Ceresoli Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/xilinx/Kconfig | 7 + drivers/media/platform/xilinx/Makefile | 1 + .../media/platform/xilinx/xilinx-csi2rxss.c | 1111 +++++++++++++++++ 3 files changed, 1119 insertions(+) create mode 100644 drivers/media/platform/xilinx/xilinx-csi2rxss.c diff --git a/drivers/media/platform/xilinx/Kconfig b/drivers/media/platform/xilinx/Kconfig index 01c96fb664148..44587dccacf1b 100644 --- a/drivers/media/platform/xilinx/Kconfig +++ b/drivers/media/platform/xilinx/Kconfig @@ -12,6 +12,13 @@ config VIDEO_XILINX if VIDEO_XILINX +config VIDEO_XILINX_CSI2RXSS + tristate "Xilinx CSI-2 Rx Subsystem" + help + Driver for Xilinx MIPI CSI-2 Rx Subsystem. This is a V4L sub-device + based driver that takes input from CSI-2 Tx source and converts + it into an AXI4-Stream. + config VIDEO_XILINX_TPG tristate "Xilinx Video Test Pattern Generator" depends on VIDEO_XILINX diff --git a/drivers/media/platform/xilinx/Makefile b/drivers/media/platform/xilinx/Makefile index 4cdc0b1ec7a5d..6119a34f30437 100644 --- a/drivers/media/platform/xilinx/Makefile +++ b/drivers/media/platform/xilinx/Makefile @@ -3,5 +3,6 @@ xilinx-video-objs += xilinx-dma.o xilinx-vip.o xilinx-vipp.o obj-$(CONFIG_VIDEO_XILINX) += xilinx-video.o +obj-$(CONFIG_VIDEO_XILINX_CSI2RXSS) += xilinx-csi2rxss.o obj-$(CONFIG_VIDEO_XILINX_TPG) += xilinx-tpg.o obj-$(CONFIG_VIDEO_XILINX_VTC) += xilinx-vtc.o diff --git a/drivers/media/platform/xilinx/xilinx-csi2rxss.c b/drivers/media/platform/xilinx/xilinx-csi2rxss.c new file mode 100644 index 0000000000000..fff7ddec6745f --- /dev/null +++ b/drivers/media/platform/xilinx/xilinx-csi2rxss.c @@ -0,0 +1,1111 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Driver for Xilinx MIPI CSI-2 Rx Subsystem + * + * Copyright (C) 2016 - 2020 Xilinx, Inc. + * + * Contacts: Vishal Sagar + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "xilinx-vip.h" + +/* Register register map */ +#define XCSI_CCR_OFFSET 0x00 +#define XCSI_CCR_SOFTRESET BIT(1) +#define XCSI_CCR_ENABLE BIT(0) + +#define XCSI_PCR_OFFSET 0x04 +#define XCSI_PCR_MAXLANES_MASK GENMASK(4, 3) +#define XCSI_PCR_ACTLANES_MASK GENMASK(1, 0) + +#define XCSI_CSR_OFFSET 0x10 +#define XCSI_CSR_PKTCNT GENMASK(31, 16) +#define XCSI_CSR_SPFIFOFULL BIT(3) +#define XCSI_CSR_SPFIFONE BIT(2) +#define XCSI_CSR_SLBF BIT(1) +#define XCSI_CSR_RIPCD BIT(0) + +#define XCSI_GIER_OFFSET 0x20 +#define XCSI_GIER_GIE BIT(0) + +#define XCSI_ISR_OFFSET 0x24 +#define XCSI_IER_OFFSET 0x28 + +#define XCSI_ISR_FR BIT(31) +#define XCSI_ISR_VCXFE BIT(30) +#define XCSI_ISR_WCC BIT(22) +#define XCSI_ISR_ILC BIT(21) +#define XCSI_ISR_SPFIFOF BIT(20) +#define XCSI_ISR_SPFIFONE BIT(19) +#define XCSI_ISR_SLBF BIT(18) +#define XCSI_ISR_STOP BIT(17) +#define XCSI_ISR_SOTERR BIT(13) +#define XCSI_ISR_SOTSYNCERR BIT(12) +#define XCSI_ISR_ECC2BERR BIT(11) +#define XCSI_ISR_ECC1BERR BIT(10) +#define XCSI_ISR_CRCERR BIT(9) +#define XCSI_ISR_DATAIDERR BIT(8) +#define XCSI_ISR_VC3FSYNCERR BIT(7) +#define XCSI_ISR_VC3FLVLERR BIT(6) +#define XCSI_ISR_VC2FSYNCERR BIT(5) +#define XCSI_ISR_VC2FLVLERR BIT(4) +#define XCSI_ISR_VC1FSYNCERR BIT(3) +#define XCSI_ISR_VC1FLVLERR BIT(2) +#define XCSI_ISR_VC0FSYNCERR BIT(1) +#define XCSI_ISR_VC0FLVLERR BIT(0) + +#define XCSI_ISR_ALLINTR_MASK (0xc07e3fff) + +/* + * Removed VCXFE mask as it doesn't exist in IER + * Removed STOP state irq as this will keep driver in irq handler only + */ +#define XCSI_IER_INTR_MASK (XCSI_ISR_ALLINTR_MASK &\ + ~(XCSI_ISR_STOP | XCSI_ISR_VCXFE)) + +#define XCSI_SPKTR_OFFSET 0x30 +#define XCSI_SPKTR_DATA GENMASK(23, 8) +#define XCSI_SPKTR_VC GENMASK(7, 6) +#define XCSI_SPKTR_DT GENMASK(5, 0) +#define XCSI_SPKT_FIFO_DEPTH 31 + +#define XCSI_VCXR_OFFSET 0x34 +#define XCSI_VCXR_VCERR GENMASK(23, 0) +#define XCSI_VCXR_FSYNCERR BIT(1) +#define XCSI_VCXR_FLVLERR BIT(0) + +#define XCSI_CLKINFR_OFFSET 0x3C +#define XCSI_CLKINFR_STOP BIT(1) + +#define XCSI_DLXINFR_OFFSET 0x40 +#define XCSI_DLXINFR_STOP BIT(5) +#define XCSI_DLXINFR_SOTERR BIT(1) +#define XCSI_DLXINFR_SOTSYNCERR BIT(0) +#define XCSI_MAXDL_COUNT 0x4 + +#define XCSI_VCXINF1R_OFFSET 0x60 +#define XCSI_VCXINF1R_LINECOUNT GENMASK(31, 16) +#define XCSI_VCXINF1R_LINECOUNT_SHIFT 16 +#define XCSI_VCXINF1R_BYTECOUNT GENMASK(15, 0) + +#define XCSI_VCXINF2R_OFFSET 0x64 +#define XCSI_VCXINF2R_DT GENMASK(5, 0) +#define XCSI_MAXVCX_COUNT 16 + +/* + * Sink pad connected to sensor source pad. + * Source pad connected to next module like demosaic. + */ +#define XCSI_MEDIA_PADS 2 +#define XCSI_DEFAULT_WIDTH 1920 +#define XCSI_DEFAULT_HEIGHT 1080 + +/* MIPI CSI-2 Data Types from spec */ +#define XCSI_DT_YUV4228B 0x1e +#define XCSI_DT_YUV42210B 0x1f +#define XCSI_DT_RGB444 0x20 +#define XCSI_DT_RGB555 0x21 +#define XCSI_DT_RGB565 0x22 +#define XCSI_DT_RGB666 0x23 +#define XCSI_DT_RGB888 0x24 +#define XCSI_DT_RAW6 0x28 +#define XCSI_DT_RAW7 0x29 +#define XCSI_DT_RAW8 0x2a +#define XCSI_DT_RAW10 0x2b +#define XCSI_DT_RAW12 0x2c +#define XCSI_DT_RAW14 0x2d +#define XCSI_DT_RAW16 0x2e +#define XCSI_DT_RAW20 0x2f + +#define XCSI_VCX_START 4 +#define XCSI_MAX_VC 4 +#define XCSI_MAX_VCX 16 + +#define XCSI_NEXTREG_OFFSET 4 + +/* There are 2 events frame sync and frame level error per VC */ +#define XCSI_VCX_NUM_EVENTS ((XCSI_MAX_VCX - XCSI_MAX_VC) * 2) + +/** + * struct xcsi2rxss_event - Event log structure + * @mask: Event mask + * @name: Name of the event + */ +struct xcsi2rxss_event { + u32 mask; + const char *name; +}; + +static const struct xcsi2rxss_event xcsi2rxss_events[] = { + { XCSI_ISR_FR, "Frame Received" }, + { XCSI_ISR_VCXFE, "VCX Frame Errors" }, + { XCSI_ISR_WCC, "Word Count Errors" }, + { XCSI_ISR_ILC, "Invalid Lane Count Error" }, + { XCSI_ISR_SPFIFOF, "Short Packet FIFO OverFlow Error" }, + { XCSI_ISR_SPFIFONE, "Short Packet FIFO Not Empty" }, + { XCSI_ISR_SLBF, "Streamline Buffer Full Error" }, + { XCSI_ISR_STOP, "Lane Stop State" }, + { XCSI_ISR_SOTERR, "SOT Error" }, + { XCSI_ISR_SOTSYNCERR, "SOT Sync Error" }, + { XCSI_ISR_ECC2BERR, "2 Bit ECC Unrecoverable Error" }, + { XCSI_ISR_ECC1BERR, "1 Bit ECC Recoverable Error" }, + { XCSI_ISR_CRCERR, "CRC Error" }, + { XCSI_ISR_DATAIDERR, "Data Id Error" }, + { XCSI_ISR_VC3FSYNCERR, "Virtual Channel 3 Frame Sync Error" }, + { XCSI_ISR_VC3FLVLERR, "Virtual Channel 3 Frame Level Error" }, + { XCSI_ISR_VC2FSYNCERR, "Virtual Channel 2 Frame Sync Error" }, + { XCSI_ISR_VC2FLVLERR, "Virtual Channel 2 Frame Level Error" }, + { XCSI_ISR_VC1FSYNCERR, "Virtual Channel 1 Frame Sync Error" }, + { XCSI_ISR_VC1FLVLERR, "Virtual Channel 1 Frame Level Error" }, + { XCSI_ISR_VC0FSYNCERR, "Virtual Channel 0 Frame Sync Error" }, + { XCSI_ISR_VC0FLVLERR, "Virtual Channel 0 Frame Level Error" } +}; + +#define XCSI_NUM_EVENTS ARRAY_SIZE(xcsi2rxss_events) + +/* + * This table provides a mapping between CSI-2 Data type + * and media bus formats + */ +static const u32 xcsi2dt_mbus_lut[][2] = { + { XCSI_DT_YUV4228B, MEDIA_BUS_FMT_UYVY8_1X16 }, + { XCSI_DT_YUV42210B, MEDIA_BUS_FMT_UYVY10_1X20 }, + { XCSI_DT_RGB444, 0 }, + { XCSI_DT_RGB555, 0 }, + { XCSI_DT_RGB565, 0 }, + { XCSI_DT_RGB666, 0 }, + { XCSI_DT_RGB888, MEDIA_BUS_FMT_RBG888_1X24 }, + { XCSI_DT_RAW6, 0 }, + { XCSI_DT_RAW7, 0 }, + { XCSI_DT_RAW8, MEDIA_BUS_FMT_SRGGB8_1X8 }, + { XCSI_DT_RAW8, MEDIA_BUS_FMT_SBGGR8_1X8 }, + { XCSI_DT_RAW8, MEDIA_BUS_FMT_SGBRG8_1X8 }, + { XCSI_DT_RAW8, MEDIA_BUS_FMT_SGRBG8_1X8 }, + { XCSI_DT_RAW10, MEDIA_BUS_FMT_SRGGB10_1X10 }, + { XCSI_DT_RAW10, MEDIA_BUS_FMT_SBGGR10_1X10 }, + { XCSI_DT_RAW10, MEDIA_BUS_FMT_SGBRG10_1X10 }, + { XCSI_DT_RAW10, MEDIA_BUS_FMT_SGRBG10_1X10 }, + { XCSI_DT_RAW12, MEDIA_BUS_FMT_SRGGB12_1X12 }, + { XCSI_DT_RAW12, MEDIA_BUS_FMT_SBGGR12_1X12 }, + { XCSI_DT_RAW12, MEDIA_BUS_FMT_SGBRG12_1X12 }, + { XCSI_DT_RAW12, MEDIA_BUS_FMT_SGRBG12_1X12 }, + { XCSI_DT_RAW16, MEDIA_BUS_FMT_SRGGB16_1X16 }, + { XCSI_DT_RAW16, MEDIA_BUS_FMT_SBGGR16_1X16 }, + { XCSI_DT_RAW16, MEDIA_BUS_FMT_SGBRG16_1X16 }, + { XCSI_DT_RAW16, MEDIA_BUS_FMT_SGRBG16_1X16 }, + { XCSI_DT_RAW20, 0 }, +}; + +/** + * struct xcsi2rxss_state - CSI-2 Rx Subsystem device structure + * @subdev: The v4l2 subdev structure + * @format: Active V4L2 formats on each pad + * @default_format: Default V4L2 format + * @events: counter for events + * @vcx_events: counter for vcx_events + * @dev: Platform structure + * @rsubdev: Remote subdev connected to sink pad + * @rst_gpio: reset to video_aresetn + * @clks: array of clocks + * @iomem: Base address of subsystem + * @max_num_lanes: Maximum number of lanes present + * @datatype: Data type filter + * @lock: mutex for accessing this structure + * @pads: media pads + * @streaming: Flag for storing streaming state + * @enable_active_lanes: If number of active lanes can be modified + * @en_vcx: If more than 4 VC are enabled + * + * This structure contains the device driver related parameters + */ +struct xcsi2rxss_state { + struct v4l2_subdev subdev; + struct v4l2_mbus_framefmt format; + struct v4l2_mbus_framefmt default_format; + u32 events[XCSI_NUM_EVENTS]; + u32 vcx_events[XCSI_VCX_NUM_EVENTS]; + struct device *dev; + struct v4l2_subdev *rsubdev; + struct gpio_desc *rst_gpio; + struct clk_bulk_data *clks; + void __iomem *iomem; + u32 max_num_lanes; + u32 datatype; + /* used to protect access to this struct */ + struct mutex lock; + struct media_pad pads[XCSI_MEDIA_PADS]; + bool streaming; + bool enable_active_lanes; + bool en_vcx; +}; + +static const struct clk_bulk_data xcsi2rxss_clks[] = { + { .id = "lite_aclk" }, + { .id = "video_aclk" }, +}; + +static inline struct xcsi2rxss_state * +to_xcsi2rxssstate(struct v4l2_subdev *subdev) +{ + return container_of(subdev, struct xcsi2rxss_state, subdev); +} + +/* + * Register related operations + */ +static inline u32 xcsi2rxss_read(struct xcsi2rxss_state *xcsi2rxss, u32 addr) +{ + return ioread32(xcsi2rxss->iomem + addr); +} + +static inline void xcsi2rxss_write(struct xcsi2rxss_state *xcsi2rxss, u32 addr, + u32 value) +{ + iowrite32(value, xcsi2rxss->iomem + addr); +} + +static inline void xcsi2rxss_clr(struct xcsi2rxss_state *xcsi2rxss, u32 addr, + u32 clr) +{ + xcsi2rxss_write(xcsi2rxss, addr, + xcsi2rxss_read(xcsi2rxss, addr) & ~clr); +} + +static inline void xcsi2rxss_set(struct xcsi2rxss_state *xcsi2rxss, u32 addr, + u32 set) +{ + xcsi2rxss_write(xcsi2rxss, addr, xcsi2rxss_read(xcsi2rxss, addr) | set); +} + +/* + * This function returns the nth mbus for a data type. + * In case of error, mbus code returned is 0. + */ +static u32 xcsi2rxss_get_nth_mbus(u32 dt, u32 n) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(xcsi2dt_mbus_lut); i++) { + if (xcsi2dt_mbus_lut[i][0] == dt) { + if (n-- == 0) + return xcsi2dt_mbus_lut[i][1]; + } + } + + return 0; +} + +/* This returns the data type for a media bus format else 0 */ +static u32 xcsi2rxss_get_dt(u32 mbus) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(xcsi2dt_mbus_lut); i++) { + if (xcsi2dt_mbus_lut[i][1] == mbus) + return xcsi2dt_mbus_lut[i][0]; + } + + return 0; +} + +/** + * xcsi2rxss_soft_reset - Does a soft reset of the MIPI CSI-2 Rx Subsystem + * @state: Xilinx CSI-2 Rx Subsystem structure pointer + * + * Core takes less than 100 video clock cycles to reset. + * So a larger timeout value is chosen for margin. + * + * Return: 0 - on success OR -ETIME if reset times out + */ +static int xcsi2rxss_soft_reset(struct xcsi2rxss_state *state) +{ + u32 timeout = 1000; /* us */ + + xcsi2rxss_set(state, XCSI_CCR_OFFSET, XCSI_CCR_SOFTRESET); + + while (xcsi2rxss_read(state, XCSI_CSR_OFFSET) & XCSI_CSR_RIPCD) { + if (timeout == 0) { + dev_err(state->dev, "soft reset timed out!\n"); + return -ETIME; + } + + timeout--; + udelay(1); + } + + xcsi2rxss_clr(state, XCSI_CCR_OFFSET, XCSI_CCR_SOFTRESET); + return 0; +} + +static void xcsi2rxss_hard_reset(struct xcsi2rxss_state *state) +{ + if (!state->rst_gpio) + return; + + /* minimum of 40 dphy_clk_200M cycles */ + gpiod_set_value_cansleep(state->rst_gpio, 1); + usleep_range(1, 2); + gpiod_set_value_cansleep(state->rst_gpio, 0); +} + +static void xcsi2rxss_reset_event_counters(struct xcsi2rxss_state *state) +{ + unsigned int i; + + for (i = 0; i < XCSI_NUM_EVENTS; i++) + state->events[i] = 0; + + for (i = 0; i < XCSI_VCX_NUM_EVENTS; i++) + state->vcx_events[i] = 0; +} + +/* Print event counters */ +static void xcsi2rxss_log_counters(struct xcsi2rxss_state *state) +{ + struct device *dev = state->dev; + unsigned int i; + + for (i = 0; i < XCSI_NUM_EVENTS; i++) { + if (state->events[i] > 0) { + dev_info(dev, "%s events: %d\n", + xcsi2rxss_events[i].name, + state->events[i]); + } + } + + if (state->en_vcx) { + for (i = 0; i < XCSI_VCX_NUM_EVENTS; i++) { + if (state->vcx_events[i] > 0) { + dev_info(dev, + "VC %d Frame %s err vcx events: %d\n", + (i / 2) + XCSI_VCX_START, + i & 1 ? "Sync" : "Level", + state->vcx_events[i]); + } + } + } +} + +/** + * xcsi2rxss_log_status - Logs the status of the CSI-2 Receiver + * @sd: Pointer to V4L2 subdevice structure + * + * This function prints the current status of Xilinx MIPI CSI-2 + * + * Return: 0 on success + */ +static int xcsi2rxss_log_status(struct v4l2_subdev *sd) +{ + struct xcsi2rxss_state *xcsi2rxss = to_xcsi2rxssstate(sd); + struct device *dev = xcsi2rxss->dev; + u32 reg, data; + unsigned int i, max_vc; + + mutex_lock(&xcsi2rxss->lock); + + xcsi2rxss_log_counters(xcsi2rxss); + + dev_info(dev, "***** Core Status *****\n"); + data = xcsi2rxss_read(xcsi2rxss, XCSI_CSR_OFFSET); + dev_info(dev, "Short Packet FIFO Full = %s\n", + data & XCSI_CSR_SPFIFOFULL ? "true" : "false"); + dev_info(dev, "Short Packet FIFO Not Empty = %s\n", + data & XCSI_CSR_SPFIFONE ? "true" : "false"); + dev_info(dev, "Stream line buffer full = %s\n", + data & XCSI_CSR_SLBF ? "true" : "false"); + dev_info(dev, "Soft reset/Core disable in progress = %s\n", + data & XCSI_CSR_RIPCD ? "true" : "false"); + + /* Clk & Lane Info */ + dev_info(dev, "******** Clock Lane Info *********\n"); + data = xcsi2rxss_read(xcsi2rxss, XCSI_CLKINFR_OFFSET); + dev_info(dev, "Clock Lane in Stop State = %s\n", + data & XCSI_CLKINFR_STOP ? "true" : "false"); + + dev_info(dev, "******** Data Lane Info *********\n"); + dev_info(dev, "Lane\tSoT Error\tSoT Sync Error\tStop State\n"); + reg = XCSI_DLXINFR_OFFSET; + for (i = 0; i < XCSI_MAXDL_COUNT; i++) { + data = xcsi2rxss_read(xcsi2rxss, reg); + + dev_info(dev, "%d\t%s\t\t%s\t\t%s\n", i, + data & XCSI_DLXINFR_SOTERR ? "true" : "false", + data & XCSI_DLXINFR_SOTSYNCERR ? "true" : "false", + data & XCSI_DLXINFR_STOP ? "true" : "false"); + + reg += XCSI_NEXTREG_OFFSET; + } + + /* Virtual Channel Image Information */ + dev_info(dev, "********** Virtual Channel Info ************\n"); + dev_info(dev, "VC\tLine Count\tByte Count\tData Type\n"); + if (xcsi2rxss->en_vcx) + max_vc = XCSI_MAX_VCX; + else + max_vc = XCSI_MAX_VC; + + reg = XCSI_VCXINF1R_OFFSET; + for (i = 0; i < max_vc; i++) { + u32 line_count, byte_count, data_type; + + /* Get line and byte count from VCXINFR1 Register */ + data = xcsi2rxss_read(xcsi2rxss, reg); + byte_count = data & XCSI_VCXINF1R_BYTECOUNT; + line_count = data & XCSI_VCXINF1R_LINECOUNT; + line_count >>= XCSI_VCXINF1R_LINECOUNT_SHIFT; + + /* Get data type from VCXINFR2 Register */ + reg += XCSI_NEXTREG_OFFSET; + data = xcsi2rxss_read(xcsi2rxss, reg); + data_type = data & XCSI_VCXINF2R_DT; + + dev_info(dev, "%d\t%d\t\t%d\t\t0x%x\n", i, line_count, + byte_count, data_type); + + /* Move to next pair of VC Info registers */ + reg += XCSI_NEXTREG_OFFSET; + } + + mutex_unlock(&xcsi2rxss->lock); + + return 0; +} + +static struct v4l2_subdev *xcsi2rxss_get_remote_subdev(struct media_pad *local) +{ + struct media_pad *remote; + + remote = media_entity_remote_pad(local); + if (!remote || !is_media_entity_v4l2_subdev(remote->entity)) + return NULL; + + return media_entity_to_v4l2_subdev(remote->entity); +} + +static int xcsi2rxss_start_stream(struct xcsi2rxss_state *state) +{ + int ret = 0; + + /* enable core */ + xcsi2rxss_set(state, XCSI_CCR_OFFSET, XCSI_CCR_ENABLE); + + ret = xcsi2rxss_soft_reset(state); + if (ret) { + state->streaming = false; + return ret; + } + + /* enable interrupts */ + xcsi2rxss_clr(state, XCSI_GIER_OFFSET, XCSI_GIER_GIE); + xcsi2rxss_write(state, XCSI_IER_OFFSET, XCSI_IER_INTR_MASK); + xcsi2rxss_set(state, XCSI_GIER_OFFSET, XCSI_GIER_GIE); + + state->streaming = true; + + state->rsubdev = + xcsi2rxss_get_remote_subdev(&state->pads[XVIP_PAD_SINK]); + + ret = v4l2_subdev_call(state->rsubdev, video, s_stream, 1); + if (ret) { + /* disable interrupts */ + xcsi2rxss_clr(state, XCSI_IER_OFFSET, XCSI_IER_INTR_MASK); + xcsi2rxss_clr(state, XCSI_GIER_OFFSET, XCSI_GIER_GIE); + + /* disable core */ + xcsi2rxss_clr(state, XCSI_CCR_OFFSET, XCSI_CCR_ENABLE); + state->streaming = false; + } + + return ret; +} + +static void xcsi2rxss_stop_stream(struct xcsi2rxss_state *state) +{ + v4l2_subdev_call(state->rsubdev, video, s_stream, 0); + + /* disable interrupts */ + xcsi2rxss_clr(state, XCSI_IER_OFFSET, XCSI_IER_INTR_MASK); + xcsi2rxss_clr(state, XCSI_GIER_OFFSET, XCSI_GIER_GIE); + + /* disable core */ + xcsi2rxss_clr(state, XCSI_CCR_OFFSET, XCSI_CCR_ENABLE); + state->streaming = false; +} + +/** + * xcsi2rxss_irq_handler - Interrupt handler for CSI-2 + * @irq: IRQ number + * @data: Pointer to device state + * + * In the interrupt handler, a list of event counters are updated for + * corresponding interrupts. This is useful to get status / debug. + * + * Return: IRQ_HANDLED after handling interrupts + */ +static irqreturn_t xcsi2rxss_irq_handler(int irq, void *data) +{ + struct xcsi2rxss_state *state = (struct xcsi2rxss_state *)data; + struct device *dev = state->dev; + u32 status; + + status = xcsi2rxss_read(state, XCSI_ISR_OFFSET) & XCSI_ISR_ALLINTR_MASK; + xcsi2rxss_write(state, XCSI_ISR_OFFSET, status); + + /* Received a short packet */ + if (status & XCSI_ISR_SPFIFONE) { + u32 count = 0; + + /* + * Drain generic short packet FIFO by reading max 31 + * (fifo depth) short packets from fifo or till fifo is empty. + */ + for (count = 0; count < XCSI_SPKT_FIFO_DEPTH; ++count) { + u32 spfifostat, spkt; + + spkt = xcsi2rxss_read(state, XCSI_SPKTR_OFFSET); + dev_dbg(dev, "Short packet = 0x%08x\n", spkt); + spfifostat = xcsi2rxss_read(state, XCSI_ISR_OFFSET); + spfifostat &= XCSI_ISR_SPFIFONE; + if (!spfifostat) + break; + xcsi2rxss_write(state, XCSI_ISR_OFFSET, spfifostat); + } + } + + /* Short packet FIFO overflow */ + if (status & XCSI_ISR_SPFIFOF) + dev_dbg_ratelimited(dev, "Short packet FIFO overflowed\n"); + + /* + * Stream line buffer full + * This means there is a backpressure from downstream IP + */ + if (status & XCSI_ISR_SLBF) { + dev_alert_ratelimited(dev, "Stream Line Buffer Full!\n"); + + /* disable interrupts */ + xcsi2rxss_clr(state, XCSI_IER_OFFSET, XCSI_IER_INTR_MASK); + xcsi2rxss_clr(state, XCSI_GIER_OFFSET, XCSI_GIER_GIE); + + /* disable core */ + xcsi2rxss_clr(state, XCSI_CCR_OFFSET, XCSI_CCR_ENABLE); + + /* + * The IP needs to be hard reset before it can be used now. + * This will be done in streamoff. + */ + + /* + * TODO: Notify the whole pipeline with v4l2_subdev_notify() to + * inform userspace. + */ + } + + /* Increment event counters */ + if (status & XCSI_ISR_ALLINTR_MASK) { + unsigned int i; + + for (i = 0; i < XCSI_NUM_EVENTS; i++) { + if (!(status & xcsi2rxss_events[i].mask)) + continue; + state->events[i]++; + dev_dbg_ratelimited(dev, "%s: %u\n", + xcsi2rxss_events[i].name, + state->events[i]); + } + + if (status & XCSI_ISR_VCXFE && state->en_vcx) { + u32 vcxstatus; + + vcxstatus = xcsi2rxss_read(state, XCSI_VCXR_OFFSET); + vcxstatus &= XCSI_VCXR_VCERR; + for (i = 0; i < XCSI_VCX_NUM_EVENTS; i++) { + if (!(vcxstatus & BIT(i))) + continue; + state->vcx_events[i]++; + } + xcsi2rxss_write(state, XCSI_VCXR_OFFSET, vcxstatus); + } + } + + return IRQ_HANDLED; +} + +/** + * xcsi2rxss_s_stream - It is used to start/stop the streaming. + * @sd: V4L2 Sub device + * @enable: Flag (True / False) + * + * This function controls the start or stop of streaming for the + * Xilinx MIPI CSI-2 Rx Subsystem. + * + * Return: 0 on success, errors otherwise + */ +static int xcsi2rxss_s_stream(struct v4l2_subdev *sd, int enable) +{ + struct xcsi2rxss_state *xcsi2rxss = to_xcsi2rxssstate(sd); + int ret = 0; + + mutex_lock(&xcsi2rxss->lock); + + if (enable == xcsi2rxss->streaming) + goto stream_done; + + if (enable) { + xcsi2rxss_reset_event_counters(xcsi2rxss); + ret = xcsi2rxss_start_stream(xcsi2rxss); + } else { + xcsi2rxss_stop_stream(xcsi2rxss); + xcsi2rxss_hard_reset(xcsi2rxss); + } + +stream_done: + mutex_unlock(&xcsi2rxss->lock); + return ret; +} + +static struct v4l2_mbus_framefmt * +__xcsi2rxss_get_pad_format(struct xcsi2rxss_state *xcsi2rxss, + struct v4l2_subdev_pad_config *cfg, + unsigned int pad, u32 which) +{ + switch (which) { + case V4L2_SUBDEV_FORMAT_TRY: + return v4l2_subdev_get_try_format(&xcsi2rxss->subdev, cfg, pad); + case V4L2_SUBDEV_FORMAT_ACTIVE: + return &xcsi2rxss->format; + default: + return NULL; + } +} + +/** + * xcsi2rxss_init_cfg - Initialise the pad format config to default + * @sd: Pointer to V4L2 Sub device structure + * @cfg: Pointer to sub device pad information structure + * + * This function is used to initialize the pad format with the default + * values. + * + * Return: 0 on success + */ +static int xcsi2rxss_init_cfg(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg) +{ + struct xcsi2rxss_state *xcsi2rxss = to_xcsi2rxssstate(sd); + struct v4l2_mbus_framefmt *format; + unsigned int i; + + mutex_lock(&xcsi2rxss->lock); + for (i = 0; i < XCSI_MEDIA_PADS; i++) { + format = v4l2_subdev_get_try_format(sd, cfg, i); + *format = xcsi2rxss->default_format; + } + mutex_unlock(&xcsi2rxss->lock); + + return 0; +} + +/** + * xcsi2rxss_get_format - Get the pad format + * @sd: Pointer to V4L2 Sub device structure + * @cfg: Pointer to sub device pad information structure + * @fmt: Pointer to pad level media bus format + * + * This function is used to get the pad format information. + * + * Return: 0 on success + */ +static int xcsi2rxss_get_format(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *fmt) +{ + struct xcsi2rxss_state *xcsi2rxss = to_xcsi2rxssstate(sd); + + mutex_lock(&xcsi2rxss->lock); + fmt->format = *__xcsi2rxss_get_pad_format(xcsi2rxss, cfg, fmt->pad, + fmt->which); + mutex_unlock(&xcsi2rxss->lock); + + return 0; +} + +/** + * xcsi2rxss_set_format - This is used to set the pad format + * @sd: Pointer to V4L2 Sub device structure + * @cfg: Pointer to sub device pad information structure + * @fmt: Pointer to pad level media bus format + * + * This function is used to set the pad format. Since the pad format is fixed + * in hardware, it can't be modified on run time. So when a format set is + * requested by application, all parameters except the format type is saved + * for the pad and the original pad format is sent back to the application. + * + * Return: 0 on success + */ +static int xcsi2rxss_set_format(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *fmt) +{ + struct xcsi2rxss_state *xcsi2rxss = to_xcsi2rxssstate(sd); + struct v4l2_mbus_framefmt *__format; + u32 dt; + + mutex_lock(&xcsi2rxss->lock); + + /* + * Only the format->code parameter matters for CSI as the + * CSI format cannot be changed at runtime. + * Ensure that format to set is copied to over to CSI pad format + */ + __format = __xcsi2rxss_get_pad_format(xcsi2rxss, cfg, + fmt->pad, fmt->which); + + /* only sink pad format can be updated */ + if (fmt->pad == XVIP_PAD_SOURCE) { + fmt->format = *__format; + mutex_unlock(&xcsi2rxss->lock); + return 0; + } + + /* + * RAW8 is supported in all datatypes. So if requested media bus format + * is of RAW8 type, then allow to be set. In case core is configured to + * other RAW, YUV422 8/10 or RGB888, set appropriate media bus format. + */ + dt = xcsi2rxss_get_dt(fmt->format.code); + if (dt != xcsi2rxss->datatype && dt != XCSI_DT_RAW8) { + dev_dbg(xcsi2rxss->dev, "Unsupported media bus format"); + /* set the default format for the data type */ + fmt->format.code = xcsi2rxss_get_nth_mbus(xcsi2rxss->datatype, + 0); + } + + *__format = fmt->format; + mutex_unlock(&xcsi2rxss->lock); + + return 0; +} + +/* + * xcsi2rxss_enum_mbus_code - Handle pixel format enumeration + * @sd: pointer to v4l2 subdev structure + * @cfg: V4L2 subdev pad configuration + * @code: pointer to v4l2_subdev_mbus_code_enum structure + * + * Return: -EINVAL or zero on success + */ +static int xcsi2rxss_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_mbus_code_enum *code) +{ + struct xcsi2rxss_state *state = to_xcsi2rxssstate(sd); + u32 dt, n; + int ret = 0; + + /* RAW8 dt packets are available in all DT configurations */ + if (code->index < 4) { + n = code->index; + dt = XCSI_DT_RAW8; + } else if (state->datatype != XCSI_DT_RAW8) { + n = code->index - 4; + dt = state->datatype; + } else { + return -EINVAL; + } + + code->code = xcsi2rxss_get_nth_mbus(dt, n); + if (!code->code) + ret = -EINVAL; + + return ret; +} + +/* ----------------------------------------------------------------------------- + * Media Operations + */ + +static const struct media_entity_operations xcsi2rxss_media_ops = { + .link_validate = v4l2_subdev_link_validate +}; + +static const struct v4l2_subdev_core_ops xcsi2rxss_core_ops = { + .log_status = xcsi2rxss_log_status, +}; + +static const struct v4l2_subdev_video_ops xcsi2rxss_video_ops = { + .s_stream = xcsi2rxss_s_stream +}; + +static const struct v4l2_subdev_pad_ops xcsi2rxss_pad_ops = { + .init_cfg = xcsi2rxss_init_cfg, + .get_fmt = xcsi2rxss_get_format, + .set_fmt = xcsi2rxss_set_format, + .enum_mbus_code = xcsi2rxss_enum_mbus_code, + .link_validate = v4l2_subdev_link_validate_default, +}; + +static const struct v4l2_subdev_ops xcsi2rxss_ops = { + .core = &xcsi2rxss_core_ops, + .video = &xcsi2rxss_video_ops, + .pad = &xcsi2rxss_pad_ops +}; + +static int xcsi2rxss_parse_of(struct xcsi2rxss_state *xcsi2rxss) +{ + struct device *dev = xcsi2rxss->dev; + struct device_node *node = dev->of_node; + + struct fwnode_handle *ep; + struct v4l2_fwnode_endpoint vep = { + .bus_type = V4L2_MBUS_CSI2_DPHY + }; + bool en_csi_v20, vfb; + int ret; + + en_csi_v20 = of_property_read_bool(node, "xlnx,en-csi-v2-0"); + if (en_csi_v20) + xcsi2rxss->en_vcx = of_property_read_bool(node, "xlnx,en-vcx"); + + xcsi2rxss->enable_active_lanes = + of_property_read_bool(node, "xlnx,en-active-lanes"); + + ret = of_property_read_u32(node, "xlnx,csi-pxl-format", + &xcsi2rxss->datatype); + if (ret < 0) { + dev_err(dev, "missing xlnx,csi-pxl-format property\n"); + return ret; + } + + switch (xcsi2rxss->datatype) { + case XCSI_DT_YUV4228B: + case XCSI_DT_RGB444: + case XCSI_DT_RGB555: + case XCSI_DT_RGB565: + case XCSI_DT_RGB666: + case XCSI_DT_RGB888: + case XCSI_DT_RAW6: + case XCSI_DT_RAW7: + case XCSI_DT_RAW8: + case XCSI_DT_RAW10: + case XCSI_DT_RAW12: + case XCSI_DT_RAW14: + break; + case XCSI_DT_YUV42210B: + case XCSI_DT_RAW16: + case XCSI_DT_RAW20: + if (!en_csi_v20) { + ret = -EINVAL; + dev_dbg(dev, "enable csi v2 for this pixel format"); + } + break; + default: + ret = -EINVAL; + } + if (ret < 0) { + dev_err(dev, "invalid csi-pxl-format property!\n"); + return ret; + } + + vfb = of_property_read_bool(node, "xlnx,vfb"); + if (!vfb) { + dev_err(dev, "operation without VFB is not supported\n"); + return -EINVAL; + } + + ep = fwnode_graph_get_endpoint_by_id(dev_fwnode(dev), + XVIP_PAD_SINK, 0, + FWNODE_GRAPH_ENDPOINT_NEXT); + if (!ep) { + dev_err(dev, "no sink port found"); + return -EINVAL; + } + + ret = v4l2_fwnode_endpoint_parse(ep, &vep); + fwnode_handle_put(ep); + if (ret) { + dev_err(dev, "error parsing sink port"); + return ret; + } + + dev_dbg(dev, "mipi number lanes = %d\n", + vep.bus.mipi_csi2.num_data_lanes); + + xcsi2rxss->max_num_lanes = vep.bus.mipi_csi2.num_data_lanes; + + ep = fwnode_graph_get_endpoint_by_id(dev_fwnode(dev), + XVIP_PAD_SOURCE, 0, + FWNODE_GRAPH_ENDPOINT_NEXT); + if (!ep) { + dev_err(dev, "no source port found"); + return -EINVAL; + } + + fwnode_handle_put(ep); + + dev_dbg(dev, "vcx %s, %u data lanes (%s), data type 0x%02x\n", + xcsi2rxss->en_vcx ? "enabled" : "disabled", + xcsi2rxss->max_num_lanes, + xcsi2rxss->enable_active_lanes ? "dynamic" : "static", + xcsi2rxss->datatype); + + return 0; +} + +static int xcsi2rxss_probe(struct platform_device *pdev) +{ + struct v4l2_subdev *subdev; + struct xcsi2rxss_state *xcsi2rxss; + int num_clks = ARRAY_SIZE(xcsi2rxss_clks); + struct device *dev = &pdev->dev; + int irq, ret; + + xcsi2rxss = devm_kzalloc(dev, sizeof(*xcsi2rxss), GFP_KERNEL); + if (!xcsi2rxss) + return -ENOMEM; + + xcsi2rxss->dev = dev; + + xcsi2rxss->clks = devm_kmemdup(dev, xcsi2rxss_clks, + sizeof(xcsi2rxss_clks), GFP_KERNEL); + if (!xcsi2rxss->clks) + return -ENOMEM; + + /* Reset GPIO */ + xcsi2rxss->rst_gpio = devm_gpiod_get_optional(dev, "video-reset", + GPIOD_OUT_HIGH); + if (IS_ERR(xcsi2rxss->rst_gpio)) { + if (PTR_ERR(xcsi2rxss->rst_gpio) != -EPROBE_DEFER) + dev_err(dev, "Video Reset GPIO not setup in DT"); + return PTR_ERR(xcsi2rxss->rst_gpio); + } + + ret = xcsi2rxss_parse_of(xcsi2rxss); + if (ret < 0) + return ret; + + xcsi2rxss->iomem = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(xcsi2rxss->iomem)) + return PTR_ERR(xcsi2rxss->iomem); + + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return irq; + + ret = devm_request_threaded_irq(dev, irq, NULL, + xcsi2rxss_irq_handler, IRQF_ONESHOT, + dev_name(dev), xcsi2rxss); + if (ret) { + dev_err(dev, "Err = %d Interrupt handler reg failed!\n", ret); + return ret; + } + + ret = clk_bulk_get(dev, num_clks, xcsi2rxss->clks); + if (ret) + return ret; + + /* TODO: Enable/disable clocks at stream on/off time. */ + ret = clk_bulk_prepare_enable(num_clks, xcsi2rxss->clks); + if (ret) + goto err_clk_put; + + mutex_init(&xcsi2rxss->lock); + + xcsi2rxss_hard_reset(xcsi2rxss); + xcsi2rxss_soft_reset(xcsi2rxss); + + /* Initialize V4L2 subdevice and media entity */ + xcsi2rxss->pads[XVIP_PAD_SINK].flags = MEDIA_PAD_FL_SINK; + xcsi2rxss->pads[XVIP_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE; + + /* Initialize the default format */ + xcsi2rxss->default_format.code = + xcsi2rxss_get_nth_mbus(xcsi2rxss->datatype, 0); + xcsi2rxss->default_format.field = V4L2_FIELD_NONE; + xcsi2rxss->default_format.colorspace = V4L2_COLORSPACE_SRGB; + xcsi2rxss->default_format.width = XCSI_DEFAULT_WIDTH; + xcsi2rxss->default_format.height = XCSI_DEFAULT_HEIGHT; + xcsi2rxss->format = xcsi2rxss->default_format; + + /* Initialize V4L2 subdevice and media entity */ + subdev = &xcsi2rxss->subdev; + v4l2_subdev_init(subdev, &xcsi2rxss_ops); + subdev->dev = dev; + strscpy(subdev->name, dev_name(dev), sizeof(subdev->name)); + subdev->flags |= V4L2_SUBDEV_FL_HAS_EVENTS | V4L2_SUBDEV_FL_HAS_DEVNODE; + subdev->entity.ops = &xcsi2rxss_media_ops; + v4l2_set_subdevdata(subdev, xcsi2rxss); + + ret = media_entity_pads_init(&subdev->entity, XCSI_MEDIA_PADS, + xcsi2rxss->pads); + if (ret < 0) + goto error; + + platform_set_drvdata(pdev, xcsi2rxss); + + ret = v4l2_async_register_subdev(subdev); + if (ret < 0) { + dev_err(dev, "failed to register subdev\n"); + goto error; + } + + return 0; +error: + media_entity_cleanup(&subdev->entity); + mutex_destroy(&xcsi2rxss->lock); + clk_bulk_disable_unprepare(num_clks, xcsi2rxss->clks); +err_clk_put: + clk_bulk_put(num_clks, xcsi2rxss->clks); + return ret; +} + +static int xcsi2rxss_remove(struct platform_device *pdev) +{ + struct xcsi2rxss_state *xcsi2rxss = platform_get_drvdata(pdev); + struct v4l2_subdev *subdev = &xcsi2rxss->subdev; + int num_clks = ARRAY_SIZE(xcsi2rxss_clks); + + v4l2_async_unregister_subdev(subdev); + media_entity_cleanup(&subdev->entity); + mutex_destroy(&xcsi2rxss->lock); + clk_bulk_disable_unprepare(num_clks, xcsi2rxss->clks); + clk_bulk_put(num_clks, xcsi2rxss->clks); + + return 0; +} + +static const struct of_device_id xcsi2rxss_of_id_table[] = { + { .compatible = "xlnx,mipi-csi2-rx-subsystem-5.0", }, + { } +}; +MODULE_DEVICE_TABLE(of, xcsi2rxss_of_id_table); + +static struct platform_driver xcsi2rxss_driver = { + .driver = { + .name = "xilinx-csi2rxss", + .of_match_table = xcsi2rxss_of_id_table, + }, + .probe = xcsi2rxss_probe, + .remove = xcsi2rxss_remove, +}; + +module_platform_driver(xcsi2rxss_driver); + +MODULE_AUTHOR("Vishal Sagar "); +MODULE_DESCRIPTION("Xilinx MIPI CSI-2 Rx Subsystem Driver"); +MODULE_LICENSE("GPL v2"); -- GitLab From 445093ff76a0b01628637b5890ac332507d8a197 Mon Sep 17 00:00:00 2001 From: Dafna Hirschfeld Date: Sat, 9 May 2020 17:29:03 +0200 Subject: [PATCH 0098/1476] media: staging: rkisp1 stats: set a measure flag with '|=' instead of '=' The flag RKISP1_CIF_ISP_STAT_AFM_FIN that indicates a type of statistics is mistakenly set with '=' instead of '|=' this might set off previous flags. Signed-off-by: Dafna Hirschfeld Acked-by: Helen Koike Reviewed-by: Laurent Pinchart Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/rkisp1/rkisp1-stats.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/media/rkisp1/rkisp1-stats.c b/drivers/staging/media/rkisp1/rkisp1-stats.c index 6dfcbdc3deb88..8351bda0be039 100644 --- a/drivers/staging/media/rkisp1/rkisp1-stats.c +++ b/drivers/staging/media/rkisp1/rkisp1-stats.c @@ -253,7 +253,7 @@ static void rkisp1_stats_get_afc_meas(struct rkisp1_stats *stats, struct rkisp1_device *rkisp1 = stats->rkisp1; struct rkisp1_cif_isp_af_stat *af; - pbuf->meas_type = RKISP1_CIF_ISP_STAT_AFM_FIN; + pbuf->meas_type |= RKISP1_CIF_ISP_STAT_AFM_FIN; af = &pbuf->params.af; af->window[0].sum = rkisp1_read(rkisp1, RKISP1_CIF_ISP_AFM_SUM_A); -- GitLab From 3bee86168657d3d846a12b6687d362d361e0cd35 Mon Sep 17 00:00:00 2001 From: Dafna Hirschfeld Date: Sat, 9 May 2020 17:29:04 +0200 Subject: [PATCH 0099/1476] media: staging: rkisp1: stats: don't set stats flags in rkisp1_stats_send_measurement The flags that indicate which statistics are read are already set in the functions that read them so there is no need to set them in the function rkisp1_stats_send_measurement. Signed-off-by: Dafna Hirschfeld Acked-by: Helen Koike Reviewed-by: Laurent Pinchart Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/rkisp1/rkisp1-stats.c | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/drivers/staging/media/rkisp1/rkisp1-stats.c b/drivers/staging/media/rkisp1/rkisp1-stats.c index 8351bda0be039..0616793ae3958 100644 --- a/drivers/staging/media/rkisp1/rkisp1-stats.c +++ b/drivers/staging/media/rkisp1/rkisp1-stats.c @@ -356,26 +356,19 @@ rkisp1_stats_send_measurement(struct rkisp1_stats *stats, cur_stat_buf = (struct rkisp1_stat_buffer *)(cur_buf->vaddr[0]); - if (meas_work->isp_ris & RKISP1_CIF_ISP_AWB_DONE) { + if (meas_work->isp_ris & RKISP1_CIF_ISP_AWB_DONE) rkisp1_stats_get_awb_meas(stats, cur_stat_buf); - cur_stat_buf->meas_type |= RKISP1_CIF_ISP_STAT_AWB; - } - if (meas_work->isp_ris & RKISP1_CIF_ISP_AFM_FIN) { + if (meas_work->isp_ris & RKISP1_CIF_ISP_AFM_FIN) rkisp1_stats_get_afc_meas(stats, cur_stat_buf); - cur_stat_buf->meas_type |= RKISP1_CIF_ISP_STAT_AFM_FIN; - } if (meas_work->isp_ris & RKISP1_CIF_ISP_EXP_END) { rkisp1_stats_get_aec_meas(stats, cur_stat_buf); rkisp1_stats_get_bls_meas(stats, cur_stat_buf); - cur_stat_buf->meas_type |= RKISP1_CIF_ISP_STAT_AUTOEXP; } - if (meas_work->isp_ris & RKISP1_CIF_ISP_HIST_MEASURE_RDY) { + if (meas_work->isp_ris & RKISP1_CIF_ISP_HIST_MEASURE_RDY) rkisp1_stats_get_hst_meas(stats, cur_stat_buf); - cur_stat_buf->meas_type |= RKISP1_CIF_ISP_STAT_HIST; - } vb2_set_plane_payload(&cur_buf->vb.vb2_buf, 0, sizeof(struct rkisp1_stat_buffer)); -- GitLab From 0c8bc934ee304e43daa04492bced568dd0bde594 Mon Sep 17 00:00:00 2001 From: Dafna Hirschfeld Date: Fri, 22 May 2020 13:56:33 +0200 Subject: [PATCH 0100/1476] media: staging: rkisp1: fix dev param for dev_* debugs In some debug prints a wrong 'dev' argument is used for the dev_(dbg/err/warn), this cause a prefix "(NULL device *)" to the prints. In some prints the 'dev' of the sensor subdevice is used which is also wrong. Signed-off-by: Dafna Hirschfeld Reviewed-by: Laurent Pinchart [hverkuil-cisco@xs4all.nl: s/in subdev/in sensor subdev/ as per Laurent's comment] Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/rkisp1/rkisp1-dev.c | 2 +- drivers/staging/media/rkisp1/rkisp1-isp.c | 17 ++++++++++------- drivers/staging/media/rkisp1/rkisp1-params.c | 2 +- drivers/staging/media/rkisp1/rkisp1-resizer.c | 2 +- 4 files changed, 13 insertions(+), 10 deletions(-) diff --git a/drivers/staging/media/rkisp1/rkisp1-dev.c b/drivers/staging/media/rkisp1/rkisp1-dev.c index 9ac38bafb8399..f38801fea10d9 100644 --- a/drivers/staging/media/rkisp1/rkisp1-dev.c +++ b/drivers/staging/media/rkisp1/rkisp1-dev.c @@ -129,7 +129,7 @@ static int rkisp1_create_links(struct rkisp1_device *rkisp1) ret = media_entity_get_fwnode_pad(&sd->entity, sd->fwnode, MEDIA_PAD_FL_SOURCE); if (ret < 0) { - dev_err(sd->dev, "failed to find src pad for %s\n", + dev_err(rkisp1->dev, "failed to find src pad for %s\n", sd->name); return ret; } diff --git a/drivers/staging/media/rkisp1/rkisp1-isp.c b/drivers/staging/media/rkisp1/rkisp1-isp.c index dc2b59a0160a8..6763379d6a653 100644 --- a/drivers/staging/media/rkisp1/rkisp1-isp.c +++ b/drivers/staging/media/rkisp1/rkisp1-isp.c @@ -203,10 +203,8 @@ static struct v4l2_subdev *rkisp1_get_remote_sensor(struct v4l2_subdev *sd) local = &sd->entity.pads[RKISP1_ISP_PAD_SINK_VIDEO]; remote = media_entity_remote_pad(local); - if (!remote) { - dev_warn(sd->dev, "No link between isp and sensor\n"); + if (!remote) return NULL; - } sensor_me = remote->entity; return media_entity_to_v4l2_subdev(sensor_me); @@ -889,18 +887,20 @@ static const struct v4l2_subdev_pad_ops rkisp1_isp_pad_ops = { static int rkisp1_mipi_csi2_start(struct rkisp1_isp *isp, struct rkisp1_sensor_async *sensor) { + struct rkisp1_device *rkisp1 = + container_of(isp->sd.v4l2_dev, struct rkisp1_device, v4l2_dev); union phy_configure_opts opts; struct phy_configure_opts_mipi_dphy *cfg = &opts.mipi_dphy; s64 pixel_clock; if (!sensor->pixel_rate_ctrl) { - dev_warn(sensor->sd->dev, "No pixel rate control in subdev\n"); + dev_warn(rkisp1->dev, "No pixel rate control in sensor subdev\n"); return -EPIPE; } pixel_clock = v4l2_ctrl_g_ctrl_int64(sensor->pixel_rate_ctrl); if (!pixel_clock) { - dev_err(sensor->sd->dev, "Invalid pixel rate value\n"); + dev_err(rkisp1->dev, "Invalid pixel rate value\n"); return -EINVAL; } @@ -933,8 +933,11 @@ static int rkisp1_isp_s_stream(struct v4l2_subdev *sd, int enable) } sensor_sd = rkisp1_get_remote_sensor(sd); - if (!sensor_sd) + if (!sensor_sd) { + dev_warn(rkisp1->dev, "No link between isp and sensor\n"); return -ENODEV; + } + rkisp1->active_sensor = container_of(sensor_sd->asd, struct rkisp1_sensor_async, asd); @@ -1021,7 +1024,7 @@ int rkisp1_isp_register(struct rkisp1_device *rkisp1, ret = v4l2_device_register_subdev(v4l2_dev, sd); if (ret) { - dev_err(sd->dev, "Failed to register isp subdev\n"); + dev_err(rkisp1->dev, "Failed to register isp subdev\n"); goto err_cleanup_media_entity; } diff --git a/drivers/staging/media/rkisp1/rkisp1-params.c b/drivers/staging/media/rkisp1/rkisp1-params.c index 44d542caf32bd..797e79de659cc 100644 --- a/drivers/staging/media/rkisp1/rkisp1-params.c +++ b/drivers/staging/media/rkisp1/rkisp1-params.c @@ -1607,7 +1607,7 @@ int rkisp1_params_register(struct rkisp1_params *params, goto err_release_queue; ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1); if (ret) { - dev_err(&vdev->dev, + dev_err(rkisp1->dev, "failed to register %s, ret=%d\n", vdev->name, ret); goto err_cleanup_media_entity; } diff --git a/drivers/staging/media/rkisp1/rkisp1-resizer.c b/drivers/staging/media/rkisp1/rkisp1-resizer.c index d049374413dcf..26fb41053f56c 100644 --- a/drivers/staging/media/rkisp1/rkisp1-resizer.c +++ b/drivers/staging/media/rkisp1/rkisp1-resizer.c @@ -639,7 +639,7 @@ static int rkisp1_rsz_set_selection(struct v4l2_subdev *sd, if (sel->target != V4L2_SEL_TGT_CROP || sel->pad == RKISP1_RSZ_PAD_SRC) return -EINVAL; - dev_dbg(sd->dev, "%s: pad: %d sel(%d,%d)/%dx%d\n", __func__, + dev_dbg(rsz->rkisp1->dev, "%s: pad: %d sel(%d,%d)/%dx%d\n", __func__, sel->pad, sel->r.left, sel->r.top, sel->r.width, sel->r.height); mutex_lock(&rsz->ops_lock); -- GitLab From 7dcfb327f8fd7418124fc6d63bdce91b5844f50a Mon Sep 17 00:00:00 2001 From: Dinghao Liu Date: Thu, 21 May 2020 08:27:45 +0200 Subject: [PATCH 0101/1476] media: staging: tegra-vde: fix runtime pm imbalance on error pm_runtime_get_sync() increments the runtime PM usage counter even the call returns an error code. Thus a pairing decrement is needed on the error handling path to keep the counter balanced. Signed-off-by: Dinghao Liu Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/tegra-vde/vde.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/staging/media/tegra-vde/vde.c b/drivers/staging/media/tegra-vde/vde.c index d3e63512a765a..3fdf2cd0b99e4 100644 --- a/drivers/staging/media/tegra-vde/vde.c +++ b/drivers/staging/media/tegra-vde/vde.c @@ -777,7 +777,7 @@ static int tegra_vde_ioctl_decode_h264(struct tegra_vde *vde, ret = pm_runtime_get_sync(dev); if (ret < 0) - goto unlock; + goto put_runtime_pm; /* * We rely on the VDE registers reset value, otherwise VDE @@ -843,8 +843,6 @@ static int tegra_vde_ioctl_decode_h264(struct tegra_vde *vde, put_runtime_pm: pm_runtime_mark_last_busy(dev); pm_runtime_put_autosuspend(dev); - -unlock: mutex_unlock(&vde->lock); release_dpb_frames: -- GitLab From 157282a5c6273976b31cf4ba4b1c46663f6519d1 Mon Sep 17 00:00:00 2001 From: Dinghao Liu Date: Thu, 21 May 2020 13:07:47 +0200 Subject: [PATCH 0102/1476] media: s5k6a3: Fix runtime PM imbalance on error pm_runtime_get() increments the runtime PM usage counter even when it returns an error code. Thus a pairing decrement is needed on the error handling path to keep the counter balanced. Signed-off-by: Dinghao Liu Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/s5k6a3.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/i2c/s5k6a3.c b/drivers/media/i2c/s5k6a3.c index bc6cc5a558db0..f26c168ef942e 100644 --- a/drivers/media/i2c/s5k6a3.c +++ b/drivers/media/i2c/s5k6a3.c @@ -197,7 +197,7 @@ static int __s5k6a3_power_on(struct s5k6a3 *sensor) ret = pm_runtime_get(sensor->dev); if (ret < 0) - return ret; + goto error_rpm_put; ret = regulator_enable(sensor->supplies[i].consumer); if (ret < 0) -- GitLab From e40a8770fd147e7c0d5bd8c327d88090480242f2 Mon Sep 17 00:00:00 2001 From: Dinghao Liu Date: Thu, 21 May 2020 12:22:04 +0200 Subject: [PATCH 0103/1476] media: fimc-capture: Fix runtime PM imbalance on error pm_runtime_get_sync() increments the runtime PM usage counter even when it returns an error code. Thus a pairing decrement is needed on the error handling path to keep the counter balanced. Signed-off-by: Dinghao Liu Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/exynos4-is/fimc-capture.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/media/platform/exynos4-is/fimc-capture.c b/drivers/media/platform/exynos4-is/fimc-capture.c index 705f182330ca4..dd33b888971fb 100644 --- a/drivers/media/platform/exynos4-is/fimc-capture.c +++ b/drivers/media/platform/exynos4-is/fimc-capture.c @@ -478,8 +478,10 @@ static int fimc_capture_open(struct file *file) set_bit(ST_CAPT_BUSY, &fimc->state); ret = pm_runtime_get_sync(&fimc->pdev->dev); - if (ret < 0) + if (ret < 0) { + pm_runtime_put_sync(&fimc->pdev->dev); goto unlock; + } ret = v4l2_fh_open(file); if (ret) { -- GitLab From 90361693ad8d13a4311fc671903dcedd6ec06940 Mon Sep 17 00:00:00 2001 From: Dinghao Liu Date: Thu, 21 May 2020 12:17:35 +0200 Subject: [PATCH 0104/1476] media: coda: Fix runtime PM imbalance on error pm_runtime_get_sync() increments the runtime PM usage counter even when it returns an error code. Thus a pairing decrement is needed on the error handling path to keep the counter balanced. Signed-off-by: Dinghao Liu Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/coda/coda-common.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/media/platform/coda/coda-common.c b/drivers/media/platform/coda/coda-common.c index 6f41f74d492c6..3ab3d976d8cad 100644 --- a/drivers/media/platform/coda/coda-common.c +++ b/drivers/media/platform/coda/coda-common.c @@ -2653,7 +2653,7 @@ static int coda_open(struct file *file) ret = clk_prepare_enable(dev->clk_per); if (ret) - goto err_clk_per; + goto err_pm_get; ret = clk_prepare_enable(dev->clk_ahb); if (ret) @@ -2692,9 +2692,8 @@ err_ctx_init: clk_disable_unprepare(dev->clk_ahb); err_clk_ahb: clk_disable_unprepare(dev->clk_per); -err_clk_per: - pm_runtime_put_sync(dev->dev); err_pm_get: + pm_runtime_put_sync(dev->dev); v4l2_fh_del(&ctx->fh); v4l2_fh_exit(&ctx->fh); err_coda_name_init: -- GitLab From cf7b1c794d2f2362610143375b4156111e35aed5 Mon Sep 17 00:00:00 2001 From: Dinghao Liu Date: Sat, 23 May 2020 09:36:39 +0200 Subject: [PATCH 0105/1476] media: camss: csid: Fix runtime PM imbalance in csid_set_power pm_runtime_get_sync() increments the runtime PM usage counter even when it returns an error code. Thus a pairing decrement is needed on the error handling path to keep the counter balanced. Signed-off-by: Dinghao Liu Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/qcom/camss/camss-csid.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/media/platform/qcom/camss/camss-csid.c b/drivers/media/platform/qcom/camss/camss-csid.c index a5ae85674ffbb..8a247b6f55509 100644 --- a/drivers/media/platform/qcom/camss/camss-csid.c +++ b/drivers/media/platform/qcom/camss/camss-csid.c @@ -562,8 +562,10 @@ static int csid_set_power(struct v4l2_subdev *sd, int on) u32 hw_version; ret = pm_runtime_get_sync(dev); - if (ret < 0) + if (ret < 0) { + pm_runtime_put_sync(dev); return ret; + } ret = regulator_enable(csid->vdda); if (ret < 0) { -- GitLab From 606578801afe6ec8bb03f5d494a4bae49a684ef3 Mon Sep 17 00:00:00 2001 From: Dinghao Liu Date: Thu, 21 May 2020 14:06:21 +0200 Subject: [PATCH 0106/1476] media: camss: ispif: Fix runtime PM imbalance on error pm_runtime_get_sync() increments the runtime PM usage counter even when it returns an error code. Thus a pairing decrement is needed on the error handling path to keep the counter balanced. Signed-off-by: Dinghao Liu Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/qcom/camss/camss-ispif.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/media/platform/qcom/camss/camss-ispif.c b/drivers/media/platform/qcom/camss/camss-ispif.c index 1f33b4eb198cc..5722e971b1848 100644 --- a/drivers/media/platform/qcom/camss/camss-ispif.c +++ b/drivers/media/platform/qcom/camss/camss-ispif.c @@ -344,8 +344,10 @@ static int ispif_set_power(struct v4l2_subdev *sd, int on) } ret = pm_runtime_get_sync(dev); - if (ret < 0) + if (ret < 0) { + pm_runtime_put_sync(dev); goto exit; + } ret = camss_enable_clocks(ispif->nclocks, ispif->clock, dev); if (ret < 0) { -- GitLab From 779096916daede26699e75e51b0c3a000410d8ad Mon Sep 17 00:00:00 2001 From: Dinghao Liu Date: Thu, 21 May 2020 13:56:07 +0200 Subject: [PATCH 0107/1476] media: camss: vfe: Fix runtime PM imbalance on error pm_runtime_get_sync() increments the runtime PM usage counter even when it returns an error code. Thus a pairing decrement is needed on the error handling path to keep the counter balanced. Signed-off-by: Dinghao Liu Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/qcom/camss/camss-vfe.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/drivers/media/platform/qcom/camss/camss-vfe.c b/drivers/media/platform/qcom/camss/camss-vfe.c index a8c542fa647db..fc31c2c169cd4 100644 --- a/drivers/media/platform/qcom/camss/camss-vfe.c +++ b/drivers/media/platform/qcom/camss/camss-vfe.c @@ -1265,12 +1265,12 @@ static int vfe_get(struct vfe_device *vfe) ret = vfe_set_clock_rates(vfe); if (ret < 0) - goto error_clocks; + goto error_pm_runtime_get; ret = camss_enable_clocks(vfe->nclocks, vfe->clock, vfe->camss->dev); if (ret < 0) - goto error_clocks; + goto error_pm_runtime_get; ret = vfe_reset(vfe); if (ret < 0) @@ -1282,7 +1282,7 @@ static int vfe_get(struct vfe_device *vfe) } else { ret = vfe_check_clock_rates(vfe); if (ret < 0) - goto error_clocks; + goto error_pm_runtime_get; } vfe->power_count++; @@ -1293,10 +1293,8 @@ static int vfe_get(struct vfe_device *vfe) error_reset: camss_disable_clocks(vfe->nclocks, vfe->clock); -error_clocks: - pm_runtime_put_sync(vfe->camss->dev); - error_pm_runtime_get: + pm_runtime_put_sync(vfe->camss->dev); camss_pm_domain_off(vfe->camss, vfe->id); error_pm_domain: -- GitLab From 00b0228061fa2dc0536fbf3355fedf2d447df3bd Mon Sep 17 00:00:00 2001 From: Dinghao Liu Date: Sun, 24 May 2020 05:34:04 +0200 Subject: [PATCH 0108/1476] media: exynos4-is: Fix runtime PM imbalance in isp_video_open pm_runtime_get_sync() increments the runtime PM usage counter even when it returns an error code. Thus a pairing decrement is needed on the error handling path to keep the counter balanced. Signed-off-by: Dinghao Liu Reviewed-by: Sylwester Nawrocki Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/exynos4-is/fimc-isp-video.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/media/platform/exynos4-is/fimc-isp-video.c b/drivers/media/platform/exynos4-is/fimc-isp-video.c index 15f443fa72082..612b9872afc87 100644 --- a/drivers/media/platform/exynos4-is/fimc-isp-video.c +++ b/drivers/media/platform/exynos4-is/fimc-isp-video.c @@ -293,6 +293,7 @@ static int isp_video_open(struct file *file) if (!ret) goto unlock; rel_fh: + pm_runtime_put_noidle(&isp->pdev->dev); v4l2_fh_release(file); unlock: mutex_unlock(&isp->video_lock); -- GitLab From 4f9b5709cc087337bf02f004f0a238ec7ebda25b Mon Sep 17 00:00:00 2001 From: Dinghao Liu Date: Sun, 24 May 2020 04:59:02 +0200 Subject: [PATCH 0109/1476] media: exynos4-is: Fix runtime PM imbalance in fimc_is_probe pm_runtime_get_sync() increments the runtime PM usage counter even when it returns an error code. Thus a pairing decrement is needed on the error handling path to keep the counter balanced. For all error paths after pm_runtime_get_sync(), things are the same. Signed-off-by: Dinghao Liu Reviewed-by: Sylwester Nawrocki Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/exynos4-is/fimc-is.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/media/platform/exynos4-is/fimc-is.c b/drivers/media/platform/exynos4-is/fimc-is.c index 64148b7e0d986..a474014f0a0fa 100644 --- a/drivers/media/platform/exynos4-is/fimc-is.c +++ b/drivers/media/platform/exynos4-is/fimc-is.c @@ -871,6 +871,7 @@ err_dfs: err_sd: fimc_is_unregister_subdevs(is); err_pm: + pm_runtime_put_noidle(dev); if (!pm_runtime_enabled(dev)) fimc_is_runtime_suspend(dev); err_irq: -- GitLab From 4024d6f601e3c49bae9297930970f68d2621ea1d Mon Sep 17 00:00:00 2001 From: Dinghao Liu Date: Sat, 23 May 2020 13:49:17 +0200 Subject: [PATCH 0110/1476] media: vpif: Fix runtime PM imbalance in vpif_probe When platform_get_resource() returns an error code, a pairing runtime PM usage counter decrement is needed to keep the counter balanced. Signed-off-by: Dinghao Liu Reviewed-by: Lad Prabhakar Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/davinci/vpif.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/media/platform/davinci/vpif.c b/drivers/media/platform/davinci/vpif.c index df66461f5d4f1..f28c1b27eac69 100644 --- a/drivers/media/platform/davinci/vpif.c +++ b/drivers/media/platform/davinci/vpif.c @@ -458,6 +458,7 @@ static int vpif_probe(struct platform_device *pdev) res_irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0); if (!res_irq) { dev_warn(&pdev->dev, "Missing IRQ resource.\n"); + pm_runtime_put(&pdev->dev); return -EINVAL; } -- GitLab From e1f60f426f5f57388ddd943f8bc49babc45d760c Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Sun, 24 May 2020 18:25:03 +0200 Subject: [PATCH 0111/1476] media: gspca: remove redundant assignment to variable retval The variable retval is being initialized with a value that is never read and it is being updated later with a new value. The initialization is redundant and can be removed. Addresses-Coverity: ("Unused value") Signed-off-by: Colin Ian King Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/gspca/jl2005bcd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/usb/gspca/jl2005bcd.c b/drivers/media/usb/gspca/jl2005bcd.c index 80ce7448b3dd6..ca12f33f3e12c 100644 --- a/drivers/media/usb/gspca/jl2005bcd.c +++ b/drivers/media/usb/gspca/jl2005bcd.c @@ -165,7 +165,7 @@ static int jl2005c_get_firmware_id(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *)gspca_dev; int i = 0; - int retval = -1; + int retval; unsigned char regs_to_read[] = {0x57, 0x02, 0x03, 0x5d, 0x5e, 0x5f}; gspca_dbg(gspca_dev, D_PROBE, "Running jl2005c_get_firmware_id\n"); -- GitLab From f47a468c350a12187eed922b3310e05a307943da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niklas=20S=C3=B6derlund?= Date: Fri, 22 May 2020 02:50:07 +0200 Subject: [PATCH 0112/1476] media: doc-rst: Fix typos in packed Bayer raw14 pixel formats diagram MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There are two typos in the byte order diagram. On row 1 and 3 the low bits for the 3rd pixel B02 and B22 are labeled as R02 and R22. On row 2 the row index is 0 for all pixels where it should be 1. Signed-off-by: Niklas Söderlund Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- .../media/v4l/pixfmt-srggb14p.rst | 28 +++++++++---------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/Documentation/userspace-api/media/v4l/pixfmt-srggb14p.rst b/Documentation/userspace-api/media/v4l/pixfmt-srggb14p.rst index ec1239ada316f..e2f5a2b360921 100644 --- a/Documentation/userspace-api/media/v4l/pixfmt-srggb14p.rst +++ b/Documentation/userspace-api/media/v4l/pixfmt-srggb14p.rst @@ -69,37 +69,37 @@ Each cell is one byte. B\ :sub:`00low bits 5--0`\ (bits 5--0) - - R\ :sub:`02low bits 3--0`\ (bits 7--4) + - B\ :sub:`02low bits 3--0`\ (bits 7--4) G\ :sub:`01low bits 5--2`\ (bits 3--0) - G\ :sub:`03low bits 5--0`\ (bits 7--2) - R\ :sub:`02low bits 5--4`\ (bits 1--0) + B\ :sub:`02low bits 5--4`\ (bits 1--0) - .. row 2 - start + 7 - - G\ :sub:`00high` + - G\ :sub:`10high` - - R\ :sub:`01high` + - R\ :sub:`11high` - - G\ :sub:`02high` + - G\ :sub:`12high` - - R\ :sub:`03high` + - R\ :sub:`13high` - - R\ :sub:`01low bits 1--0`\ (bits 7--6) + - R\ :sub:`11low bits 1--0`\ (bits 7--6) - G\ :sub:`00low bits 5--0`\ (bits 5--0) + G\ :sub:`10low bits 5--0`\ (bits 5--0) - - G\ :sub:`02low bits 3--0`\ (bits 7--4) + - G\ :sub:`12low bits 3--0`\ (bits 7--4) - R\ :sub:`01low bits 5--2`\ (bits 3--0) + R\ :sub:`11low bits 5--2`\ (bits 3--0) - - R\ :sub:`03low bits 5--0`\ (bits 7--2) + - R\ :sub:`13low bits 5--0`\ (bits 7--2) - G\ :sub:`02low bits 5--4`\ (bits 1--0) + G\ :sub:`12low bits 5--4`\ (bits 1--0) - .. row 3 @@ -117,13 +117,13 @@ Each cell is one byte. B\ :sub:`20low bits 5--0`\ (bits 5--0) - - R\ :sub:`22low bits 3--0`\ (bits 7--4) + - B\ :sub:`22low bits 3--0`\ (bits 7--4) G\ :sub:`21low bits 5--2`\ (bits 3--0) - G\ :sub:`23low bits 5--0`\ (bits 7--2) - R\ :sub:`22low bits 5--4`\ (bits 1--0) + B\ :sub:`22low bits 5--4`\ (bits 1--0) - .. row 4 -- GitLab From 830e87ed15f85765bb7e57f310f95dfd87f11dfa Mon Sep 17 00:00:00 2001 From: Sergey Senozhatsky Date: Thu, 14 May 2020 18:01:40 +0200 Subject: [PATCH 0113/1476] media: videobuf2: use explicit unsigned int in vb2_queue Switch from 'unsigned' to 'unsigned int' so that checkpatch doesn't complain. Signed-off-by: Sergey Senozhatsky Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- include/media/videobuf2-core.h | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/include/media/videobuf2-core.h b/include/media/videobuf2-core.h index f11b96514cf72..9e522bd2acc7c 100644 --- a/include/media/videobuf2-core.h +++ b/include/media/videobuf2-core.h @@ -558,15 +558,15 @@ struct vb2_queue { unsigned int io_modes; struct device *dev; unsigned long dma_attrs; - unsigned bidirectional:1; - unsigned fileio_read_once:1; - unsigned fileio_write_immediately:1; - unsigned allow_zero_bytesused:1; - unsigned quirk_poll_must_check_waiting_for_buffers:1; - unsigned supports_requests:1; - unsigned requires_requests:1; - unsigned uses_qbuf:1; - unsigned uses_requests:1; + unsigned int bidirectional:1; + unsigned int fileio_read_once:1; + unsigned int fileio_write_immediately:1; + unsigned int allow_zero_bytesused:1; + unsigned int quirk_poll_must_check_waiting_for_buffers:1; + unsigned int supports_requests:1; + unsigned int requires_requests:1; + unsigned int uses_qbuf:1; + unsigned int uses_requests:1; struct mutex *lock; void *owner; -- GitLab From 6d2199868a9aede70a4ee5fa32e6ae2800b8b25a Mon Sep 17 00:00:00 2001 From: Sergey Senozhatsky Date: Thu, 14 May 2020 18:01:41 +0200 Subject: [PATCH 0114/1476] media: videobuf2: add cache management members Extend vb2_buffer and vb2_queue structs with cache management members. V4L2 UAPI already contains two buffer flags which user-space, supposedly, can use to control buffer cache sync: - V4L2_BUF_FLAG_NO_CACHE_INVALIDATE - V4L2_BUF_FLAG_NO_CACHE_CLEAN None of these, however, do anything at the moment. This patch set is intended to change it. Since user-space cache management hints are supposed to be implemented on a per-buffer basis we need to extend vb2_buffer struct with two new members ->need_cache_sync_on_prepare and ->need_cache_sync_on_finish, which will store corresponding user-space hints. In order to preserve the existing behaviour, user-space cache managements flags will be handled only by those drivers that permit user-space cache hints. That's the purpose of vb2_queue ->allow_cache_hints member. Driver must set ->allow_cache_hints during queue initialisation to enable cache management hints mechanism. Signed-off-by: Sergey Senozhatsky Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- include/media/videobuf2-core.h | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/include/media/videobuf2-core.h b/include/media/videobuf2-core.h index 9e522bd2acc7c..7f39d9fffc8c1 100644 --- a/include/media/videobuf2-core.h +++ b/include/media/videobuf2-core.h @@ -263,6 +263,10 @@ struct vb2_buffer { * after the 'buf_finish' op is called. * copied_timestamp: the timestamp of this capture buffer was copied * from an output buffer. + * need_cache_sync_on_prepare: when set buffer's ->prepare() function + * performs cache sync/invalidation. + * need_cache_sync_on_finish: when set buffer's ->finish() function + * performs cache sync/invalidation. * queued_entry: entry on the queued buffers list, which holds * all buffers queued from userspace * done_entry: entry on the list that stores all buffers ready @@ -273,6 +277,8 @@ struct vb2_buffer { unsigned int synced:1; unsigned int prepared:1; unsigned int copied_timestamp:1; + unsigned int need_cache_sync_on_prepare:1; + unsigned int need_cache_sync_on_finish:1; struct vb2_plane planes[VB2_MAX_PLANES]; struct list_head queued_entry; @@ -491,6 +497,9 @@ struct vb2_buf_ops { * @uses_requests: requests are used for this queue. Set to 1 the first time * a request is queued. Set to 0 when the queue is canceled. * If this is 1, then you cannot queue buffers directly. + * @allow_cache_hints: when set user-space can pass cache management hints in + * order to skip cache flush/invalidation on ->prepare() or/and + * ->finish(). * @lock: pointer to a mutex that protects the &struct vb2_queue. The * driver can set this to a mutex to let the v4l2 core serialize * the queuing ioctls. If the driver wants to handle locking @@ -567,6 +576,7 @@ struct vb2_queue { unsigned int requires_requests:1; unsigned int uses_qbuf:1; unsigned int uses_requests:1; + unsigned int allow_cache_hints:1; struct mutex *lock; void *owner; -- GitLab From f5f5fa73fbfb9f346f1b5f37ebf343bae6ef6361 Mon Sep 17 00:00:00 2001 From: Sergey Senozhatsky Date: Thu, 14 May 2020 18:01:42 +0200 Subject: [PATCH 0115/1476] media: videobuf2: handle V4L2 buffer cache flags Set video buffer cache management flags corresponding to V4L2 cache flags. Both ->prepare() and ->finish() cache management hints should be passed during this stage (buffer preparation), because there is no other way for user-space to tell V4L2 to avoid ->finish() cache flush. Signed-off-by: Sergey Senozhatsky Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- .../media/common/videobuf2/videobuf2-v4l2.c | 48 +++++++++++++++++++ include/media/videobuf2-core.h | 11 +++++ 2 files changed, 59 insertions(+) diff --git a/drivers/media/common/videobuf2/videobuf2-v4l2.c b/drivers/media/common/videobuf2/videobuf2-v4l2.c index eb5d5db96552f..f13851212cc88 100644 --- a/drivers/media/common/videobuf2/videobuf2-v4l2.c +++ b/drivers/media/common/videobuf2/videobuf2-v4l2.c @@ -337,6 +337,53 @@ static int vb2_fill_vb2_v4l2_buffer(struct vb2_buffer *vb, struct v4l2_buffer *b return 0; } +static void set_buffer_cache_hints(struct vb2_queue *q, + struct vb2_buffer *vb, + struct v4l2_buffer *b) +{ + /* + * DMA exporter should take care of cache syncs, so we can avoid + * explicit ->prepare()/->finish() syncs. For other ->memory types + * we always need ->prepare() or/and ->finish() cache sync. + */ + if (q->memory == VB2_MEMORY_DMABUF) { + vb->need_cache_sync_on_finish = 0; + vb->need_cache_sync_on_prepare = 0; + return; + } + + /* + * Cache sync/invalidation flags are set by default in order to + * preserve existing behaviour for old apps/drivers. + */ + vb->need_cache_sync_on_prepare = 1; + vb->need_cache_sync_on_finish = 1; + + if (!vb2_queue_allows_cache_hints(q)) { + /* + * Clear buffer cache flags if queue does not support user + * space hints. That's to indicate to userspace that these + * flags won't work. + */ + b->flags &= ~V4L2_BUF_FLAG_NO_CACHE_INVALIDATE; + b->flags &= ~V4L2_BUF_FLAG_NO_CACHE_CLEAN; + return; + } + + /* + * ->finish() cache sync can be avoided when queue direction is + * TO_DEVICE. + */ + if (q->dma_dir == DMA_TO_DEVICE) + vb->need_cache_sync_on_finish = 0; + + if (b->flags & V4L2_BUF_FLAG_NO_CACHE_INVALIDATE) + vb->need_cache_sync_on_finish = 0; + + if (b->flags & V4L2_BUF_FLAG_NO_CACHE_CLEAN) + vb->need_cache_sync_on_prepare = 0; +} + static int vb2_queue_or_prepare_buf(struct vb2_queue *q, struct media_device *mdev, struct v4l2_buffer *b, bool is_prepare, struct media_request **p_req) @@ -381,6 +428,7 @@ static int vb2_queue_or_prepare_buf(struct vb2_queue *q, struct media_device *md } if (!vb->prepared) { + set_buffer_cache_hints(q, vb, b); /* Copy relevant information provided by the userspace */ memset(vbuf->planes, 0, sizeof(vbuf->planes[0]) * vb->num_planes); diff --git a/include/media/videobuf2-core.h b/include/media/videobuf2-core.h index 7f39d9fffc8c1..ccc5c498d3e3d 100644 --- a/include/media/videobuf2-core.h +++ b/include/media/videobuf2-core.h @@ -635,6 +635,17 @@ struct vb2_queue { #endif }; +/** + * vb2_queue_allows_cache_hints() - Return true if the queue allows cache + * and memory consistency hints. + * + * @q: pointer to &struct vb2_queue with videobuf2 queue + */ +static inline bool vb2_queue_allows_cache_hints(struct vb2_queue *q) +{ + return q->allow_cache_hints && q->memory == VB2_MEMORY_MMAP; +} + /** * vb2_plane_vaddr() - Return a kernel virtual address of a given plane. * @vb: pointer to &struct vb2_buffer to which the plane in -- GitLab From ac53503ee38a1ffbc47c7cca6cbfc48ba9c65c5e Mon Sep 17 00:00:00 2001 From: Sergey Senozhatsky Date: Thu, 14 May 2020 18:01:43 +0200 Subject: [PATCH 0116/1476] media: videobuf2: add V4L2_FLAG_MEMORY_NON_CONSISTENT flag By setting or clearing V4L2_FLAG_MEMORY_NON_CONSISTENT flag user-space should be able to set or clear queue's NON_CONSISTENT ->dma_attrs. Queue's ->dma_attrs are passed to the underlying allocator in __vb2_buf_mem_alloc(), so thus user-space is able to request vb2 buffer's memory to be either consistent (coherent) or non-consistent. The patch set also adds a corresponding capability flag: fill_buf_caps() reports V4L2_BUF_CAP_SUPPORTS_MMAP_CACHE_HINTS when queue supports user-space cache management hints. Note, however, that MMAP_CACHE_HINTS capability only valid when the queue is used for memory MMAP-ed streaming I/O. Signed-off-by: Sergey Senozhatsky Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- .../userspace-api/media/v4l/buffer.rst | 40 ++++++++++++++++++- .../media/v4l/vidioc-reqbufs.rst | 10 +++++ .../media/common/videobuf2/videobuf2-v4l2.c | 2 + include/uapi/linux/videodev2.h | 3 ++ 4 files changed, 53 insertions(+), 2 deletions(-) diff --git a/Documentation/userspace-api/media/v4l/buffer.rst b/Documentation/userspace-api/media/v4l/buffer.rst index 951ae1ed485fe..5088393b5a5cf 100644 --- a/Documentation/userspace-api/media/v4l/buffer.rst +++ b/Documentation/userspace-api/media/v4l/buffer.rst @@ -577,7 +577,10 @@ Buffer Flags applications shall use this flag if the data captured in the buffer is not going to be touched by the CPU, instead the buffer will, probably, be passed on to a DMA-capable hardware unit for - further processing or output. + further processing or output. This flag is ignored unless the + queue is used for :ref:`memory mapping ` streaming I/O and + reports :ref:`V4L2_BUF_CAP_SUPPORTS_MMAP_CACHE_HINTS + ` capability. * .. _`V4L2-BUF-FLAG-NO-CACHE-CLEAN`: - ``V4L2_BUF_FLAG_NO_CACHE_CLEAN`` @@ -585,7 +588,10 @@ Buffer Flags - Caches do not have to be cleaned for this buffer. Typically applications shall use this flag for output buffers if the data in this buffer has not been created by the CPU but by some - DMA-capable unit, in which case caches have not been used. + DMA-capable unit, in which case caches have not been used. This flag + is ignored unless the queue is used for :ref:`memory mapping ` + streaming I/O and reports :ref:`V4L2_BUF_CAP_SUPPORTS_MMAP_CACHE_HINTS + ` capability. * .. _`V4L2-BUF-FLAG-M2M-HOLD-CAPTURE-BUF`: - ``V4L2_BUF_FLAG_M2M_HOLD_CAPTURE_BUF`` @@ -681,6 +687,36 @@ Buffer Flags \normalsize +.. _memory-flags: + +Memory Consistency Flags +======================== + +.. tabularcolumns:: |p{7.0cm}|p{2.2cm}|p{8.3cm}| + +.. cssclass:: longtable + +.. flat-table:: + :header-rows: 0 + :stub-columns: 0 + :widths: 3 1 4 + + * .. _`V4L2-FLAG-MEMORY-NON-CONSISTENT`: + + - ``V4L2_FLAG_MEMORY_NON_CONSISTENT`` + - 0x00000001 + - A buffer is allocated either in consistent (it will be automatically + coherent between the CPU and the bus) or non-consistent memory. The + latter can provide performance gains, for instance the CPU cache + sync/flush operations can be avoided if the buffer is accessed by the + corresponding device only and the CPU does not read/write to/from that + buffer. However, this requires extra care from the driver -- it must + guarantee memory consistency by issuing a cache flush/sync when + consistency is needed. If this flag is set V4L2 will attempt to + allocate the buffer in non-consistent memory. The flag takes effect + only if the buffer is used for :ref:`memory mapping ` I/O and the + queue reports the :ref:`V4L2_BUF_CAP_SUPPORTS_MMAP_CACHE_HINTS + ` capability. .. c:type:: v4l2_memory diff --git a/Documentation/userspace-api/media/v4l/vidioc-reqbufs.rst b/Documentation/userspace-api/media/v4l/vidioc-reqbufs.rst index b6d52083707b4..96a59793d8572 100644 --- a/Documentation/userspace-api/media/v4l/vidioc-reqbufs.rst +++ b/Documentation/userspace-api/media/v4l/vidioc-reqbufs.rst @@ -126,6 +126,7 @@ aborting or finishing any DMA in progress, an implicit .. _V4L2-BUF-CAP-SUPPORTS-REQUESTS: .. _V4L2-BUF-CAP-SUPPORTS-ORPHANED-BUFS: .. _V4L2-BUF-CAP-SUPPORTS-M2M-HOLD-CAPTURE-BUF: +.. _V4L2-BUF-CAP-SUPPORTS-MMAP-CACHE-HINTS: .. cssclass:: longtable @@ -156,6 +157,15 @@ aborting or finishing any DMA in progress, an implicit - Only valid for stateless decoders. If set, then userspace can set the ``V4L2_BUF_FLAG_M2M_HOLD_CAPTURE_BUF`` flag to hold off on returning the capture buffer until the OUTPUT timestamp changes. + * - ``V4L2_BUF_CAP_SUPPORTS_MMAP_CACHE_HINTS`` + - 0x00000040 + - This capability is set by the driver to indicate that the queue supports + cache and memory management hints. However, it's only valid when the + queue is used for :ref:`memory mapping ` streaming I/O. See + :ref:`V4L2_FLAG_MEMORY_NON_CONSISTENT `, + :ref:`V4L2_BUF_FLAG_NO_CACHE_INVALIDATE ` and + :ref:`V4L2_BUF_FLAG_NO_CACHE_CLEAN `. + Return Value ============ diff --git a/drivers/media/common/videobuf2/videobuf2-v4l2.c b/drivers/media/common/videobuf2/videobuf2-v4l2.c index f13851212cc88..e4b4354b42b83 100644 --- a/drivers/media/common/videobuf2/videobuf2-v4l2.c +++ b/drivers/media/common/videobuf2/videobuf2-v4l2.c @@ -710,6 +710,8 @@ static void fill_buf_caps(struct vb2_queue *q, u32 *caps) *caps |= V4L2_BUF_CAP_SUPPORTS_DMABUF; if (q->subsystem_flags & VB2_V4L2_FL_SUPPORTS_M2M_HOLD_CAPTURE_BUF) *caps |= V4L2_BUF_CAP_SUPPORTS_M2M_HOLD_CAPTURE_BUF; + if (q->allow_cache_hints && q->io_modes & VB2_MMAP) + *caps |= V4L2_BUF_CAP_SUPPORTS_MMAP_CACHE_HINTS; #ifdef CONFIG_MEDIA_CONTROLLER_REQUEST_API if (q->supports_requests) *caps |= V4L2_BUF_CAP_SUPPORTS_REQUESTS; diff --git a/include/uapi/linux/videodev2.h b/include/uapi/linux/videodev2.h index c3a1cf1c507f5..34ba1017b89b5 100644 --- a/include/uapi/linux/videodev2.h +++ b/include/uapi/linux/videodev2.h @@ -189,6 +189,8 @@ enum v4l2_memory { V4L2_MEMORY_DMABUF = 4, }; +#define V4L2_FLAG_MEMORY_NON_CONSISTENT (1 << 0) + /* see also http://vektor.theorem.ca/graphics/ycbcr/ */ enum v4l2_colorspace { /* @@ -954,6 +956,7 @@ struct v4l2_requestbuffers { #define V4L2_BUF_CAP_SUPPORTS_REQUESTS (1 << 3) #define V4L2_BUF_CAP_SUPPORTS_ORPHANED_BUFS (1 << 4) #define V4L2_BUF_CAP_SUPPORTS_M2M_HOLD_CAPTURE_BUF (1 << 5) +#define V4L2_BUF_CAP_SUPPORTS_MMAP_CACHE_HINTS (1 << 6) /** * struct v4l2_plane - plane info for multi-planar buffers -- GitLab From 7b4b45555c79db03dad8192e6ef85cb30236827b Mon Sep 17 00:00:00 2001 From: Sergey Senozhatsky Date: Thu, 14 May 2020 18:01:44 +0200 Subject: [PATCH 0117/1476] media: videobuf2: add queue memory consistency parameter Preparations for future V4L2_FLAG_MEMORY_NON_CONSISTENT support. Extend vb2_core_reqbufs() parameters list to accept requests' ->flags, which will be used for memory consistency configuration. An attempt to allocate a buffer with consistency requirements which don't match queue's consistency model will fail. Signed-off-by: Sergey Senozhatsky Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- .../media/common/videobuf2/videobuf2-core.c | 51 +++++++++++++++---- .../media/common/videobuf2/videobuf2-v4l2.c | 6 +-- drivers/media/dvb-core/dvb_vb2.c | 2 +- include/media/videobuf2-core.h | 8 ++- 4 files changed, 51 insertions(+), 16 deletions(-) diff --git a/drivers/media/common/videobuf2/videobuf2-core.c b/drivers/media/common/videobuf2/videobuf2-core.c index 44d65f5be8459..0fdcf90330df4 100644 --- a/drivers/media/common/videobuf2/videobuf2-core.c +++ b/drivers/media/common/videobuf2/videobuf2-core.c @@ -664,11 +664,33 @@ int vb2_verify_memory_type(struct vb2_queue *q, } EXPORT_SYMBOL(vb2_verify_memory_type); +static void set_queue_consistency(struct vb2_queue *q, bool consistent_mem) +{ + q->dma_attrs &= ~DMA_ATTR_NON_CONSISTENT; + + if (!vb2_queue_allows_cache_hints(q)) + return; + if (!consistent_mem) + q->dma_attrs |= DMA_ATTR_NON_CONSISTENT; +} + +static bool verify_consistency_attr(struct vb2_queue *q, bool consistent_mem) +{ + bool queue_is_consistent = !(q->dma_attrs & DMA_ATTR_NON_CONSISTENT); + + if (consistent_mem != queue_is_consistent) { + dprintk(1, "memory consistency model mismatch\n"); + return false; + } + return true; +} + int vb2_core_reqbufs(struct vb2_queue *q, enum vb2_memory memory, - unsigned int *count) + unsigned int flags, unsigned int *count) { unsigned int num_buffers, allocated_buffers, num_planes = 0; unsigned plane_sizes[VB2_MAX_PLANES] = { }; + bool consistent_mem = true; unsigned int i; int ret; @@ -683,7 +705,8 @@ int vb2_core_reqbufs(struct vb2_queue *q, enum vb2_memory memory, } if (*count == 0 || q->num_buffers != 0 || - (q->memory != VB2_MEMORY_UNKNOWN && q->memory != memory)) { + (q->memory != VB2_MEMORY_UNKNOWN && q->memory != memory) || + !verify_consistency_attr(q, consistent_mem)) { /* * We already have buffers allocated, so first check if they * are not in use and can be freed. @@ -720,6 +743,7 @@ int vb2_core_reqbufs(struct vb2_queue *q, enum vb2_memory memory, num_buffers = min_t(unsigned int, num_buffers, VB2_MAX_FRAME); memset(q->alloc_devs, 0, sizeof(q->alloc_devs)); q->memory = memory; + set_queue_consistency(q, consistent_mem); /* * Ask the driver how many buffers and planes per buffer it requires. @@ -804,11 +828,13 @@ int vb2_core_reqbufs(struct vb2_queue *q, enum vb2_memory memory, EXPORT_SYMBOL_GPL(vb2_core_reqbufs); int vb2_core_create_bufs(struct vb2_queue *q, enum vb2_memory memory, - unsigned int *count, unsigned requested_planes, - const unsigned requested_sizes[]) + unsigned int flags, unsigned int *count, + unsigned int requested_planes, + const unsigned int requested_sizes[]) { unsigned int num_planes = 0, num_buffers, allocated_buffers; unsigned plane_sizes[VB2_MAX_PLANES] = { }; + bool consistent_mem = true; int ret; if (q->num_buffers == VB2_MAX_FRAME) { @@ -823,10 +849,15 @@ int vb2_core_create_bufs(struct vb2_queue *q, enum vb2_memory memory, } memset(q->alloc_devs, 0, sizeof(q->alloc_devs)); q->memory = memory; + set_queue_consistency(q, consistent_mem); q->waiting_for_buffers = !q->is_output; - } else if (q->memory != memory) { - dprintk(1, "memory model mismatch\n"); - return -EINVAL; + } else { + if (q->memory != memory) { + dprintk(1, "memory model mismatch\n"); + return -EINVAL; + } + if (!verify_consistency_attr(q, consistent_mem)) + return -EINVAL; } num_buffers = min(*count, VB2_MAX_FRAME - q->num_buffers); @@ -2498,7 +2529,7 @@ static int __vb2_init_fileio(struct vb2_queue *q, int read) fileio->memory = VB2_MEMORY_MMAP; fileio->type = q->type; q->fileio = fileio; - ret = vb2_core_reqbufs(q, fileio->memory, &fileio->count); + ret = vb2_core_reqbufs(q, fileio->memory, 0, &fileio->count); if (ret) goto err_kfree; @@ -2555,7 +2586,7 @@ static int __vb2_init_fileio(struct vb2_queue *q, int read) err_reqbufs: fileio->count = 0; - vb2_core_reqbufs(q, fileio->memory, &fileio->count); + vb2_core_reqbufs(q, fileio->memory, 0, &fileio->count); err_kfree: q->fileio = NULL; @@ -2575,7 +2606,7 @@ static int __vb2_cleanup_fileio(struct vb2_queue *q) vb2_core_streamoff(q, q->type); q->fileio = NULL; fileio->count = 0; - vb2_core_reqbufs(q, fileio->memory, &fileio->count); + vb2_core_reqbufs(q, fileio->memory, 0, &fileio->count); kfree(fileio); dprintk(3, "file io emulator closed\n"); } diff --git a/drivers/media/common/videobuf2/videobuf2-v4l2.c b/drivers/media/common/videobuf2/videobuf2-v4l2.c index e4b4354b42b83..26a3ec333bb7d 100644 --- a/drivers/media/common/videobuf2/videobuf2-v4l2.c +++ b/drivers/media/common/videobuf2/videobuf2-v4l2.c @@ -723,7 +723,7 @@ int vb2_reqbufs(struct vb2_queue *q, struct v4l2_requestbuffers *req) int ret = vb2_verify_memory_type(q, req->memory, req->type); fill_buf_caps(q, &req->capabilities); - return ret ? ret : vb2_core_reqbufs(q, req->memory, &req->count); + return ret ? ret : vb2_core_reqbufs(q, req->memory, 0, &req->count); } EXPORT_SYMBOL_GPL(vb2_reqbufs); @@ -797,7 +797,7 @@ int vb2_create_bufs(struct vb2_queue *q, struct v4l2_create_buffers *create) for (i = 0; i < requested_planes; i++) if (requested_sizes[i] == 0) return -EINVAL; - return ret ? ret : vb2_core_create_bufs(q, create->memory, + return ret ? ret : vb2_core_create_bufs(q, create->memory, 0, &create->count, requested_planes, requested_sizes); } EXPORT_SYMBOL_GPL(vb2_create_bufs); @@ -973,7 +973,7 @@ int vb2_ioctl_reqbufs(struct file *file, void *priv, return res; if (vb2_queue_is_busy(vdev, file)) return -EBUSY; - res = vb2_core_reqbufs(vdev->queue, p->memory, &p->count); + res = vb2_core_reqbufs(vdev->queue, p->memory, 0, &p->count); /* If count == 0, then the owner has released all buffers and he is no longer owner of the queue. Otherwise we have a new owner. */ if (res == 0) diff --git a/drivers/media/dvb-core/dvb_vb2.c b/drivers/media/dvb-core/dvb_vb2.c index 6974f17315294..959d110407a45 100644 --- a/drivers/media/dvb-core/dvb_vb2.c +++ b/drivers/media/dvb-core/dvb_vb2.c @@ -342,7 +342,7 @@ int dvb_vb2_reqbufs(struct dvb_vb2_ctx *ctx, struct dmx_requestbuffers *req) ctx->buf_siz = req->size; ctx->buf_cnt = req->count; - ret = vb2_core_reqbufs(&ctx->vb_q, VB2_MEMORY_MMAP, &req->count); + ret = vb2_core_reqbufs(&ctx->vb_q, VB2_MEMORY_MMAP, 0, &req->count); if (ret) { ctx->state = DVB_VB2_STATE_NONE; dprintk(1, "[%s] count=%d size=%d errno=%d\n", ctx->name, diff --git a/include/media/videobuf2-core.h b/include/media/videobuf2-core.h index ccc5c498d3e3d..9e68fe043a6cb 100644 --- a/include/media/videobuf2-core.h +++ b/include/media/videobuf2-core.h @@ -740,6 +740,8 @@ void vb2_core_querybuf(struct vb2_queue *q, unsigned int index, void *pb); * vb2_core_reqbufs() - Initiate streaming. * @q: pointer to &struct vb2_queue with videobuf2 queue. * @memory: memory type, as defined by &enum vb2_memory. + * @flags: auxiliary queue/buffer management flags. Currently, the only + * used flag is %V4L2_FLAG_MEMORY_NON_CONSISTENT. * @count: requested buffer count. * * Videobuf2 core helper to implement VIDIOC_REQBUF() operation. It is called @@ -764,12 +766,13 @@ void vb2_core_querybuf(struct vb2_queue *q, unsigned int index, void *pb); * Return: returns zero on success; an error code otherwise. */ int vb2_core_reqbufs(struct vb2_queue *q, enum vb2_memory memory, - unsigned int *count); + unsigned int flags, unsigned int *count); /** * vb2_core_create_bufs() - Allocate buffers and any required auxiliary structs * @q: pointer to &struct vb2_queue with videobuf2 queue. * @memory: memory type, as defined by &enum vb2_memory. + * @flags: auxiliary queue/buffer management flags. * @count: requested buffer count. * @requested_planes: number of planes requested. * @requested_sizes: array with the size of the planes. @@ -787,7 +790,8 @@ int vb2_core_reqbufs(struct vb2_queue *q, enum vb2_memory memory, * Return: returns zero on success; an error code otherwise. */ int vb2_core_create_bufs(struct vb2_queue *q, enum vb2_memory memory, - unsigned int *count, unsigned int requested_planes, + unsigned int flags, unsigned int *count, + unsigned int requested_planes, const unsigned int requested_sizes[]); /** -- GitLab From 1e0b2318fa75d186ee0d2be31843ce867385fcc4 Mon Sep 17 00:00:00 2001 From: Sergey Senozhatsky Date: Thu, 14 May 2020 18:01:45 +0200 Subject: [PATCH 0118/1476] media: videobuf2: handle V4L2_FLAG_MEMORY_NON_CONSISTENT flag This patch lets user-space to request a non-consistent memory allocation during CREATE_BUFS and REQBUFS ioctl calls. = CREATE_BUFS struct v4l2_create_buffers has seven 4-byte reserved areas, so reserved[0] is renamed to ->flags. The struct, thus, now has six reserved 4-byte regions. = CREATE_BUFS32 struct v4l2_create_buffers32 has seven 4-byte reserved areas, so reserved[0] is renamed to ->flags. The struct, thus, now has six reserved 4-byte regions. = REQBUFS We use one bit of a ->reserved[1] member of struct v4l2_requestbuffers, which is now renamed to ->flags. Unlike v4l2_create_buffers, struct v4l2_requestbuffers does not have enough reserved room. Therefore for backward compatibility ->reserved and ->flags were put into anonymous union. Signed-off-by: Sergey Senozhatsky Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- .../media/v4l/vidioc-create-bufs.rst | 7 +++++- .../media/v4l/vidioc-reqbufs.rst | 11 +++++++-- .../media/common/videobuf2/videobuf2-core.c | 6 +++++ .../media/common/videobuf2/videobuf2-v4l2.c | 24 +++++++++++++++---- drivers/media/v4l2-core/v4l2-compat-ioctl32.c | 10 ++++++-- drivers/media/v4l2-core/v4l2-ioctl.c | 5 +--- include/uapi/linux/videodev2.h | 11 +++++++-- 7 files changed, 59 insertions(+), 15 deletions(-) diff --git a/Documentation/userspace-api/media/v4l/vidioc-create-bufs.rst b/Documentation/userspace-api/media/v4l/vidioc-create-bufs.rst index e1afc5b504c23..f2a702870fadc 100644 --- a/Documentation/userspace-api/media/v4l/vidioc-create-bufs.rst +++ b/Documentation/userspace-api/media/v4l/vidioc-create-bufs.rst @@ -121,7 +121,12 @@ than the number requested. other changes, then set ``count`` to 0, ``memory`` to ``V4L2_MEMORY_MMAP`` and ``format.type`` to the buffer type. * - __u32 - - ``reserved``\ [7] + - ``flags`` + - Specifies additional buffer management attributes. + See :ref:`memory-flags`. + + * - __u32 + - ``reserved``\ [6] - A place holder for future extensions. Drivers and applications must set the array to zero. diff --git a/Documentation/userspace-api/media/v4l/vidioc-reqbufs.rst b/Documentation/userspace-api/media/v4l/vidioc-reqbufs.rst index 96a59793d8572..75d894d9c36c4 100644 --- a/Documentation/userspace-api/media/v4l/vidioc-reqbufs.rst +++ b/Documentation/userspace-api/media/v4l/vidioc-reqbufs.rst @@ -112,10 +112,17 @@ aborting or finishing any DMA in progress, an implicit ``V4L2_MEMORY_MMAP`` and ``type`` set to the buffer type. This will free any previously allocated buffers, so this is typically something that will be done at the start of the application. + * - union { + - (anonymous) + * - __u32 + - ``flags`` + - Specifies additional buffer management attributes. + See :ref:`memory-flags`. * - __u32 - ``reserved``\ [1] - - A place holder for future extensions. Drivers and applications - must set the array to zero. + - Kept for backwards compatibility. Use ``flags`` instead. + * - } + - .. tabularcolumns:: |p{6.1cm}|p{2.2cm}|p{8.7cm}| diff --git a/drivers/media/common/videobuf2/videobuf2-core.c b/drivers/media/common/videobuf2/videobuf2-core.c index 0fdcf90330df4..626c4db5134c2 100644 --- a/drivers/media/common/videobuf2/videobuf2-core.c +++ b/drivers/media/common/videobuf2/videobuf2-core.c @@ -694,6 +694,9 @@ int vb2_core_reqbufs(struct vb2_queue *q, enum vb2_memory memory, unsigned int i; int ret; + if (flags & V4L2_FLAG_MEMORY_NON_CONSISTENT) + consistent_mem = false; + if (q->streaming) { dprintk(1, "streaming active\n"); return -EBUSY; @@ -837,6 +840,9 @@ int vb2_core_create_bufs(struct vb2_queue *q, enum vb2_memory memory, bool consistent_mem = true; int ret; + if (flags & V4L2_FLAG_MEMORY_NON_CONSISTENT) + consistent_mem = false; + if (q->num_buffers == VB2_MAX_FRAME) { dprintk(1, "maximum number of buffers already allocated\n"); return -ENOBUFS; diff --git a/drivers/media/common/videobuf2/videobuf2-v4l2.c b/drivers/media/common/videobuf2/videobuf2-v4l2.c index 26a3ec333bb7d..559a229cac414 100644 --- a/drivers/media/common/videobuf2/videobuf2-v4l2.c +++ b/drivers/media/common/videobuf2/videobuf2-v4l2.c @@ -718,12 +718,22 @@ static void fill_buf_caps(struct vb2_queue *q, u32 *caps) #endif } +static void clear_consistency_attr(struct vb2_queue *q, + int memory, + unsigned int *flags) +{ + if (!q->allow_cache_hints || memory != V4L2_MEMORY_MMAP) + *flags &= ~V4L2_FLAG_MEMORY_NON_CONSISTENT; +} + int vb2_reqbufs(struct vb2_queue *q, struct v4l2_requestbuffers *req) { int ret = vb2_verify_memory_type(q, req->memory, req->type); fill_buf_caps(q, &req->capabilities); - return ret ? ret : vb2_core_reqbufs(q, req->memory, 0, &req->count); + clear_consistency_attr(q, req->memory, &req->flags); + return ret ? ret : vb2_core_reqbufs(q, req->memory, + req->flags, &req->count); } EXPORT_SYMBOL_GPL(vb2_reqbufs); @@ -755,6 +765,7 @@ int vb2_create_bufs(struct vb2_queue *q, struct v4l2_create_buffers *create) unsigned i; fill_buf_caps(q, &create->capabilities); + clear_consistency_attr(q, create->memory, &create->flags); create->index = q->num_buffers; if (create->count == 0) return ret != -EBUSY ? ret : 0; @@ -797,8 +808,11 @@ int vb2_create_bufs(struct vb2_queue *q, struct v4l2_create_buffers *create) for (i = 0; i < requested_planes; i++) if (requested_sizes[i] == 0) return -EINVAL; - return ret ? ret : vb2_core_create_bufs(q, create->memory, 0, - &create->count, requested_planes, requested_sizes); + return ret ? ret : vb2_core_create_bufs(q, create->memory, + create->flags, + &create->count, + requested_planes, + requested_sizes); } EXPORT_SYMBOL_GPL(vb2_create_bufs); @@ -969,11 +983,12 @@ int vb2_ioctl_reqbufs(struct file *file, void *priv, int res = vb2_verify_memory_type(vdev->queue, p->memory, p->type); fill_buf_caps(vdev->queue, &p->capabilities); + clear_consistency_attr(vdev->queue, p->memory, &p->flags); if (res) return res; if (vb2_queue_is_busy(vdev, file)) return -EBUSY; - res = vb2_core_reqbufs(vdev->queue, p->memory, 0, &p->count); + res = vb2_core_reqbufs(vdev->queue, p->memory, p->flags, &p->count); /* If count == 0, then the owner has released all buffers and he is no longer owner of the queue. Otherwise we have a new owner. */ if (res == 0) @@ -991,6 +1006,7 @@ int vb2_ioctl_create_bufs(struct file *file, void *priv, p->index = vdev->queue->num_buffers; fill_buf_caps(vdev->queue, &p->capabilities); + clear_consistency_attr(vdev->queue, p->memory, &p->flags); /* * If count == 0, then just check if memory and type are valid. * Any -EBUSY result from vb2_verify_memory_type can be mapped to 0. diff --git a/drivers/media/v4l2-core/v4l2-compat-ioctl32.c b/drivers/media/v4l2-core/v4l2-compat-ioctl32.c index a99e82ec9ab60..593bcf6c37350 100644 --- a/drivers/media/v4l2-core/v4l2-compat-ioctl32.c +++ b/drivers/media/v4l2-core/v4l2-compat-ioctl32.c @@ -246,6 +246,9 @@ struct v4l2_format32 { * @memory: buffer memory type * @format: frame format, for which buffers are requested * @capabilities: capabilities of this buffer type. + * @flags: additional buffer management attributes (ignored unless the + * queue has V4L2_BUF_CAP_SUPPORTS_MMAP_CACHE_HINTS capability and + * configured for MMAP streaming I/O). * @reserved: future extensions */ struct v4l2_create_buffers32 { @@ -254,7 +257,8 @@ struct v4l2_create_buffers32 { __u32 memory; /* enum v4l2_memory */ struct v4l2_format32 format; __u32 capabilities; - __u32 reserved[7]; + __u32 flags; + __u32 reserved[6]; }; static int __bufsize_v4l2_format(struct v4l2_format32 __user *p32, u32 *size) @@ -355,7 +359,8 @@ static int get_v4l2_create32(struct v4l2_create_buffers __user *p64, { if (!access_ok(p32, sizeof(*p32)) || copy_in_user(p64, p32, - offsetof(struct v4l2_create_buffers32, format))) + offsetof(struct v4l2_create_buffers32, format)) || + assign_in_user(&p64->flags, &p32->flags)) return -EFAULT; return __get_v4l2_format32(&p64->format, &p32->format, aux_buf, aux_space); @@ -417,6 +422,7 @@ static int put_v4l2_create32(struct v4l2_create_buffers __user *p64, copy_in_user(p32, p64, offsetof(struct v4l2_create_buffers32, format)) || assign_in_user(&p32->capabilities, &p64->capabilities) || + assign_in_user(&p32->flags, &p64->flags) || copy_in_user(p32->reserved, p64->reserved, sizeof(p64->reserved))) return -EFAULT; return __put_v4l2_format32(&p64->format, &p32->format); diff --git a/drivers/media/v4l2-core/v4l2-ioctl.c b/drivers/media/v4l2-core/v4l2-ioctl.c index 2322f08a98be6..02bfef0da76da 100644 --- a/drivers/media/v4l2-core/v4l2-ioctl.c +++ b/drivers/media/v4l2-core/v4l2-ioctl.c @@ -2038,9 +2038,6 @@ static int v4l_reqbufs(const struct v4l2_ioctl_ops *ops, if (ret) return ret; - - CLEAR_AFTER_FIELD(p, capabilities); - return ops->vidioc_reqbufs(file, fh, p); } @@ -2080,7 +2077,7 @@ static int v4l_create_bufs(const struct v4l2_ioctl_ops *ops, if (ret) return ret; - CLEAR_AFTER_FIELD(create, capabilities); + CLEAR_AFTER_FIELD(create, flags); v4l_sanitize_format(&create->format); diff --git a/include/uapi/linux/videodev2.h b/include/uapi/linux/videodev2.h index 34ba1017b89b5..fec2607a07e31 100644 --- a/include/uapi/linux/videodev2.h +++ b/include/uapi/linux/videodev2.h @@ -946,7 +946,10 @@ struct v4l2_requestbuffers { __u32 type; /* enum v4l2_buf_type */ __u32 memory; /* enum v4l2_memory */ __u32 capabilities; - __u32 reserved[1]; + union { + __u32 flags; + __u32 reserved[1]; + }; }; /* capabilities for struct v4l2_requestbuffers and v4l2_create_buffers */ @@ -2450,6 +2453,9 @@ struct v4l2_dbg_chip_info { * @memory: enum v4l2_memory; buffer memory type * @format: frame format, for which buffers are requested * @capabilities: capabilities of this buffer type. + * @flags: additional buffer management attributes (ignored unless the + * queue has V4L2_BUF_CAP_SUPPORTS_MMAP_CACHE_HINTS capability + * and configured for MMAP streaming I/O). * @reserved: future extensions */ struct v4l2_create_buffers { @@ -2458,7 +2464,8 @@ struct v4l2_create_buffers { __u32 memory; struct v4l2_format format; __u32 capabilities; - __u32 reserved[7]; + __u32 flags; + __u32 reserved[6]; }; /* -- GitLab From 38a417e88aad1e5ad5ff2647d4cd26040b567fc3 Mon Sep 17 00:00:00 2001 From: Sergey Senozhatsky Date: Thu, 14 May 2020 18:01:46 +0200 Subject: [PATCH 0119/1476] media: videobuf2: factor out planes prepare/finish functions Factor out the code, no functional changes. Signed-off-by: Sergey Senozhatsky Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- .../media/common/videobuf2/videobuf2-core.c | 52 +++++++++++-------- 1 file changed, 31 insertions(+), 21 deletions(-) diff --git a/drivers/media/common/videobuf2/videobuf2-core.c b/drivers/media/common/videobuf2/videobuf2-core.c index 626c4db5134c2..1bf58e8c55e52 100644 --- a/drivers/media/common/videobuf2/videobuf2-core.c +++ b/drivers/media/common/videobuf2/videobuf2-core.c @@ -296,6 +296,32 @@ static void __vb2_buf_dmabuf_put(struct vb2_buffer *vb) __vb2_plane_dmabuf_put(vb, &vb->planes[plane]); } +/* + * __vb2_buf_mem_prepare() - call ->prepare() on buffer's private memory + * to sync caches + */ +static void __vb2_buf_mem_prepare(struct vb2_buffer *vb) +{ + unsigned int plane; + + for (plane = 0; plane < vb->num_planes; ++plane) + call_void_memop(vb, prepare, vb->planes[plane].mem_priv); + vb->synced = 1; +} + +/* + * __vb2_buf_mem_finish() - call ->finish on buffer's private memory + * to sync caches + */ +static void __vb2_buf_mem_finish(struct vb2_buffer *vb) +{ + unsigned int plane; + + for (plane = 0; plane < vb->num_planes; ++plane) + call_void_memop(vb, finish, vb->planes[plane].mem_priv); + vb->synced = 0; +} + /* * __setup_offsets() - setup unique offsets ("cookies") for every plane in * the buffer. @@ -959,7 +985,6 @@ void vb2_buffer_done(struct vb2_buffer *vb, enum vb2_buffer_state state) { struct vb2_queue *q = vb->vb2_queue; unsigned long flags; - unsigned int plane; if (WARN_ON(vb->state != VB2_BUF_STATE_ACTIVE)) return; @@ -979,12 +1004,8 @@ void vb2_buffer_done(struct vb2_buffer *vb, enum vb2_buffer_state state) dprintk(4, "done processing on buffer %d, state: %d\n", vb->index, state); - if (state != VB2_BUF_STATE_QUEUED) { - /* sync buffers */ - for (plane = 0; plane < vb->num_planes; ++plane) - call_void_memop(vb, finish, vb->planes[plane].mem_priv); - vb->synced = 0; - } + if (state != VB2_BUF_STATE_QUEUED) + __vb2_buf_mem_finish(vb); spin_lock_irqsave(&q->done_lock, flags); if (state == VB2_BUF_STATE_QUEUED) { @@ -1309,7 +1330,6 @@ static int __buf_prepare(struct vb2_buffer *vb) { struct vb2_queue *q = vb->vb2_queue; enum vb2_buffer_state orig_state = vb->state; - unsigned int plane; int ret; if (q->error) { @@ -1353,11 +1373,7 @@ static int __buf_prepare(struct vb2_buffer *vb) return ret; } - /* sync buffers */ - for (plane = 0; plane < vb->num_planes; ++plane) - call_void_memop(vb, prepare, vb->planes[plane].mem_priv); - - vb->synced = 1; + __vb2_buf_mem_prepare(vb); vb->prepared = 1; vb->state = orig_state; @@ -1977,14 +1993,8 @@ static void __vb2_queue_cancel(struct vb2_queue *q) call_void_vb_qop(vb, buf_request_complete, vb); } - if (vb->synced) { - unsigned int plane; - - for (plane = 0; plane < vb->num_planes; ++plane) - call_void_memop(vb, finish, - vb->planes[plane].mem_priv); - vb->synced = 0; - } + if (vb->synced) + __vb2_buf_mem_finish(vb); if (vb->prepared) { call_void_vb_qop(vb, buf_finish, vb); -- GitLab From dcf3bfe7c71d8da57cda2e3e06b8c28ed155741b Mon Sep 17 00:00:00 2001 From: Sergey Senozhatsky Date: Thu, 14 May 2020 18:01:47 +0200 Subject: [PATCH 0120/1476] media: videobuf2: do not sync caches when we are allowed not to Skip ->prepare() or/and ->finish() cache synchronisation if user-space requested us to do so (or when queue dma direction permits us to skip cache syncs). Signed-off-by: Sergey Senozhatsky Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/common/videobuf2/videobuf2-core.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/drivers/media/common/videobuf2/videobuf2-core.c b/drivers/media/common/videobuf2/videobuf2-core.c index 1bf58e8c55e52..b15fb11be8464 100644 --- a/drivers/media/common/videobuf2/videobuf2-core.c +++ b/drivers/media/common/videobuf2/videobuf2-core.c @@ -304,8 +304,11 @@ static void __vb2_buf_mem_prepare(struct vb2_buffer *vb) { unsigned int plane; - for (plane = 0; plane < vb->num_planes; ++plane) - call_void_memop(vb, prepare, vb->planes[plane].mem_priv); + if (vb->need_cache_sync_on_prepare) { + for (plane = 0; plane < vb->num_planes; ++plane) + call_void_memop(vb, prepare, + vb->planes[plane].mem_priv); + } vb->synced = 1; } @@ -317,8 +320,11 @@ static void __vb2_buf_mem_finish(struct vb2_buffer *vb) { unsigned int plane; - for (plane = 0; plane < vb->num_planes; ++plane) - call_void_memop(vb, finish, vb->planes[plane].mem_priv); + if (vb->need_cache_sync_on_finish) { + for (plane = 0; plane < vb->num_planes; ++plane) + call_void_memop(vb, finish, + vb->planes[plane].mem_priv); + } vb->synced = 0; } -- GitLab From 093067ed1285d027d1e4a0e3dba72160849fbb84 Mon Sep 17 00:00:00 2001 From: Sergey Senozhatsky Date: Thu, 14 May 2020 18:01:48 +0200 Subject: [PATCH 0121/1476] media: videobuf2: check ->synced flag in prepare() and finish() This simplifies the code a tiny bit and let's us to avoid unneeded ->prepare()/->finish() calls. Signed-off-by: Sergey Senozhatsky Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/common/videobuf2/videobuf2-core.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/drivers/media/common/videobuf2/videobuf2-core.c b/drivers/media/common/videobuf2/videobuf2-core.c index b15fb11be8464..37d0186ba3308 100644 --- a/drivers/media/common/videobuf2/videobuf2-core.c +++ b/drivers/media/common/videobuf2/videobuf2-core.c @@ -304,6 +304,9 @@ static void __vb2_buf_mem_prepare(struct vb2_buffer *vb) { unsigned int plane; + if (vb->synced) + return; + if (vb->need_cache_sync_on_prepare) { for (plane = 0; plane < vb->num_planes; ++plane) call_void_memop(vb, prepare, @@ -320,6 +323,9 @@ static void __vb2_buf_mem_finish(struct vb2_buffer *vb) { unsigned int plane; + if (!vb->synced) + return; + if (vb->need_cache_sync_on_finish) { for (plane = 0; plane < vb->num_planes; ++plane) call_void_memop(vb, finish, @@ -1999,8 +2005,7 @@ static void __vb2_queue_cancel(struct vb2_queue *q) call_void_vb_qop(vb, buf_request_complete, vb); } - if (vb->synced) - __vb2_buf_mem_finish(vb); + __vb2_buf_mem_finish(vb); if (vb->prepared) { call_void_vb_qop(vb, buf_finish, vb); -- GitLab From d5adf1b0c2a31f8244ded805578188e067baaf5e Mon Sep 17 00:00:00 2001 From: Sergey Senozhatsky Date: Thu, 14 May 2020 18:01:49 +0200 Subject: [PATCH 0122/1476] media: videobuf2: add begin/end cpu_access callbacks to dma-contig Provide begin_cpu_access() and end_cpu_access() callbacks for cache synchronisation on exported buffers. The patch also adds a new helper function - vb2_dc_buffer_consistent(), which returns true is if the buffer is consistent (DMA_ATTR_NON_CONSISTENT bit cleared), so then we don't need to sync anything. Signed-off-by: Sergey Senozhatsky Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- .../common/videobuf2/videobuf2-dma-contig.c | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/drivers/media/common/videobuf2/videobuf2-dma-contig.c b/drivers/media/common/videobuf2/videobuf2-dma-contig.c index f4b4a7c135ebb..4c68263c617a3 100644 --- a/drivers/media/common/videobuf2/videobuf2-dma-contig.c +++ b/drivers/media/common/videobuf2/videobuf2-dma-contig.c @@ -42,6 +42,11 @@ struct vb2_dc_buf { struct dma_buf_attachment *db_attach; }; +static inline bool vb2_dc_buffer_consistent(unsigned long attr) +{ + return !(attr & DMA_ATTR_NON_CONSISTENT); +} + /*********************************************/ /* scatterlist table functions */ /*********************************************/ @@ -335,6 +340,34 @@ static void vb2_dc_dmabuf_ops_release(struct dma_buf *dbuf) vb2_dc_put(dbuf->priv); } +static int +vb2_dc_dmabuf_ops_begin_cpu_access(struct dma_buf *dbuf, + enum dma_data_direction direction) +{ + struct vb2_dc_buf *buf = dbuf->priv; + struct sg_table *sgt = buf->dma_sgt; + + if (vb2_dc_buffer_consistent(buf->attrs)) + return 0; + + dma_sync_sg_for_cpu(buf->dev, sgt->sgl, sgt->nents, buf->dma_dir); + return 0; +} + +static int +vb2_dc_dmabuf_ops_end_cpu_access(struct dma_buf *dbuf, + enum dma_data_direction direction) +{ + struct vb2_dc_buf *buf = dbuf->priv; + struct sg_table *sgt = buf->dma_sgt; + + if (vb2_dc_buffer_consistent(buf->attrs)) + return 0; + + dma_sync_sg_for_device(buf->dev, sgt->sgl, sgt->nents, buf->dma_dir); + return 0; +} + static void *vb2_dc_dmabuf_ops_vmap(struct dma_buf *dbuf) { struct vb2_dc_buf *buf = dbuf->priv; @@ -353,6 +386,8 @@ static const struct dma_buf_ops vb2_dc_dmabuf_ops = { .detach = vb2_dc_dmabuf_ops_detach, .map_dma_buf = vb2_dc_dmabuf_ops_map, .unmap_dma_buf = vb2_dc_dmabuf_ops_unmap, + .begin_cpu_access = vb2_dc_dmabuf_ops_begin_cpu_access, + .end_cpu_access = vb2_dc_dmabuf_ops_end_cpu_access, .vmap = vb2_dc_dmabuf_ops_vmap, .mmap = vb2_dc_dmabuf_ops_mmap, .release = vb2_dc_dmabuf_ops_release, -- GitLab From d4db5eb57cab049d378fbfb7ee842857009a8679 Mon Sep 17 00:00:00 2001 From: Sergey Senozhatsky Date: Thu, 14 May 2020 18:01:50 +0200 Subject: [PATCH 0123/1476] media: videobuf2: add begin/end cpu_access callbacks to dma-sg Provide begin_cpu_access() and end_cpu_access() dma_buf_ops callbacks for cache synchronisation on exported buffers. V4L2_FLAG_MEMORY_NON_CONSISTENT has no effect on dma-sg buffers. dma-sg allocates memory using the page allocator directly, so there is no memory consistency guarantee. Signed-off-by: Sergey Senozhatsky Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- .../media/common/videobuf2/videobuf2-dma-sg.c | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/drivers/media/common/videobuf2/videobuf2-dma-sg.c b/drivers/media/common/videobuf2/videobuf2-dma-sg.c index 92072a08af255..595137e358e7c 100644 --- a/drivers/media/common/videobuf2/videobuf2-dma-sg.c +++ b/drivers/media/common/videobuf2/videobuf2-dma-sg.c @@ -120,6 +120,12 @@ static void *vb2_dma_sg_alloc(struct device *dev, unsigned long dma_attrs, buf->num_pages = size >> PAGE_SHIFT; buf->dma_sgt = &buf->sg_table; + /* + * NOTE: dma-sg allocates memory using the page allocator directly, so + * there is no memory consistency guarantee, hence dma-sg ignores DMA + * attributes passed from the upper layer. That means that + * V4L2_FLAG_MEMORY_NON_CONSISTENT has no effect on dma-sg buffers. + */ buf->pages = kvmalloc_array(buf->num_pages, sizeof(struct page *), GFP_KERNEL | __GFP_ZERO); if (!buf->pages) @@ -469,6 +475,28 @@ static void vb2_dma_sg_dmabuf_ops_release(struct dma_buf *dbuf) vb2_dma_sg_put(dbuf->priv); } +static int +vb2_dma_sg_dmabuf_ops_begin_cpu_access(struct dma_buf *dbuf, + enum dma_data_direction direction) +{ + struct vb2_dma_sg_buf *buf = dbuf->priv; + struct sg_table *sgt = buf->dma_sgt; + + dma_sync_sg_for_cpu(buf->dev, sgt->sgl, sgt->nents, buf->dma_dir); + return 0; +} + +static int +vb2_dma_sg_dmabuf_ops_end_cpu_access(struct dma_buf *dbuf, + enum dma_data_direction direction) +{ + struct vb2_dma_sg_buf *buf = dbuf->priv; + struct sg_table *sgt = buf->dma_sgt; + + dma_sync_sg_for_device(buf->dev, sgt->sgl, sgt->nents, buf->dma_dir); + return 0; +} + static void *vb2_dma_sg_dmabuf_ops_vmap(struct dma_buf *dbuf) { struct vb2_dma_sg_buf *buf = dbuf->priv; @@ -487,6 +515,8 @@ static const struct dma_buf_ops vb2_dma_sg_dmabuf_ops = { .detach = vb2_dma_sg_dmabuf_ops_detach, .map_dma_buf = vb2_dma_sg_dmabuf_ops_map, .unmap_dma_buf = vb2_dma_sg_dmabuf_ops_unmap, + .begin_cpu_access = vb2_dma_sg_dmabuf_ops_begin_cpu_access, + .end_cpu_access = vb2_dma_sg_dmabuf_ops_end_cpu_access, .vmap = vb2_dma_sg_dmabuf_ops_vmap, .mmap = vb2_dma_sg_dmabuf_ops_mmap, .release = vb2_dma_sg_dmabuf_ops_release, -- GitLab From a9a2c823601789e9f59ec75f13d0ef03138c04a4 Mon Sep 17 00:00:00 2001 From: Sergey Senozhatsky Date: Thu, 14 May 2020 18:01:51 +0200 Subject: [PATCH 0124/1476] media: videobuf2: don't test db_attach in dma-contig prepare and finish We moved cache management decision making to the upper layer and rely on buffer's need_cache_sync flags and videobuf2 core. If the upper layer (core) has decided to invoke ->prepare() or ->finish() then we must sync. For DMABUF ->need_cache_sync_on_prepare and ->need_cache_sync_on_flush are always false so videobuf core does not call ->prepare() and ->finish() on such buffers. Additionally, scratch the DMABUF comment. Signed-off-by: Sergey Senozhatsky Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/common/videobuf2/videobuf2-dma-contig.c | 6 ++---- drivers/media/common/videobuf2/videobuf2-dma-sg.c | 8 -------- 2 files changed, 2 insertions(+), 12 deletions(-) diff --git a/drivers/media/common/videobuf2/videobuf2-dma-contig.c b/drivers/media/common/videobuf2/videobuf2-dma-contig.c index 4c68263c617a3..a753a03bc48e6 100644 --- a/drivers/media/common/videobuf2/videobuf2-dma-contig.c +++ b/drivers/media/common/videobuf2/videobuf2-dma-contig.c @@ -100,8 +100,7 @@ static void vb2_dc_prepare(void *buf_priv) struct vb2_dc_buf *buf = buf_priv; struct sg_table *sgt = buf->dma_sgt; - /* DMABUF exporter will flush the cache for us */ - if (!sgt || buf->db_attach) + if (!sgt) return; dma_sync_sg_for_device(buf->dev, sgt->sgl, sgt->orig_nents, @@ -113,8 +112,7 @@ static void vb2_dc_finish(void *buf_priv) struct vb2_dc_buf *buf = buf_priv; struct sg_table *sgt = buf->dma_sgt; - /* DMABUF exporter will flush the cache for us */ - if (!sgt || buf->db_attach) + if (!sgt) return; dma_sync_sg_for_cpu(buf->dev, sgt->sgl, sgt->orig_nents, buf->dma_dir); diff --git a/drivers/media/common/videobuf2/videobuf2-dma-sg.c b/drivers/media/common/videobuf2/videobuf2-dma-sg.c index 595137e358e7c..0a40e00f0d7e5 100644 --- a/drivers/media/common/videobuf2/videobuf2-dma-sg.c +++ b/drivers/media/common/videobuf2/videobuf2-dma-sg.c @@ -204,10 +204,6 @@ static void vb2_dma_sg_prepare(void *buf_priv) struct vb2_dma_sg_buf *buf = buf_priv; struct sg_table *sgt = buf->dma_sgt; - /* DMABUF exporter will flush the cache for us */ - if (buf->db_attach) - return; - dma_sync_sg_for_device(buf->dev, sgt->sgl, sgt->orig_nents, buf->dma_dir); } @@ -217,10 +213,6 @@ static void vb2_dma_sg_finish(void *buf_priv) struct vb2_dma_sg_buf *buf = buf_priv; struct sg_table *sgt = buf->dma_sgt; - /* DMABUF exporter will flush the cache for us */ - if (buf->db_attach) - return; - dma_sync_sg_for_cpu(buf->dev, sgt->sgl, sgt->orig_nents, buf->dma_dir); } -- GitLab From 2ff99ca4bc30895f7e061f1fc0e510f19bd032bb Mon Sep 17 00:00:00 2001 From: Sergey Senozhatsky Date: Thu, 14 May 2020 18:01:52 +0200 Subject: [PATCH 0125/1476] media: videobuf2: remove redundant if-statement That if-statement seems to be unneeded. Signed-off-by: Sergey Senozhatsky Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/common/videobuf2/videobuf2-dma-contig.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/media/common/videobuf2/videobuf2-dma-contig.c b/drivers/media/common/videobuf2/videobuf2-dma-contig.c index a753a03bc48e6..ec3446cc45b8d 100644 --- a/drivers/media/common/videobuf2/videobuf2-dma-contig.c +++ b/drivers/media/common/videobuf2/videobuf2-dma-contig.c @@ -152,8 +152,7 @@ static void *vb2_dc_alloc(struct device *dev, unsigned long attrs, if (!buf) return ERR_PTR(-ENOMEM); - if (attrs) - buf->attrs = attrs; + buf->attrs = attrs; buf->cookie = dma_alloc_attrs(dev, size, &buf->dma_addr, GFP_KERNEL | gfp_flags, buf->attrs); if (!buf->cookie) { -- GitLab From 62a4cd0130f3936e431d5c8495af07edc9e1363d Mon Sep 17 00:00:00 2001 From: Sergey Senozhatsky Date: Thu, 14 May 2020 18:01:53 +0200 Subject: [PATCH 0126/1476] media: vivid: add cache_hints module param Add a cache_hints module param to control per-queue user space cache hints support. Signed-off-by: Sergey Senozhatsky Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- Documentation/admin-guide/media/vivid.rst | 9 +++++++++ drivers/media/test-drivers/vivid/vivid-core.c | 9 +++++++++ 2 files changed, 18 insertions(+) diff --git a/Documentation/admin-guide/media/vivid.rst b/Documentation/admin-guide/media/vivid.rst index 52e57b773f076..6d7175f96f74c 100644 --- a/Documentation/admin-guide/media/vivid.rst +++ b/Documentation/admin-guide/media/vivid.rst @@ -293,6 +293,15 @@ all configurable using the following module options: - 0: vmalloc - 1: dma-contig +- cache_hints: + + specifies if the device should set queues' user-space cache and memory + consistency hint capability (V4L2_BUF_CAP_SUPPORTS_MMAP_CACHE_HINTS). + The hints are valid only when using MMAP streaming I/O. Default is 0. + + - 0: forbid hints + - 1: allow hints + Taken together, all these module options allow you to precisely customize the driver behavior and test your application with all sorts of permutations. It is also very suitable to emulate hardware that is not yet available, e.g. diff --git a/drivers/media/test-drivers/vivid/vivid-core.c b/drivers/media/test-drivers/vivid/vivid-core.c index 6c740e3e6999f..5c986df4a8d4e 100644 --- a/drivers/media/test-drivers/vivid/vivid-core.c +++ b/drivers/media/test-drivers/vivid/vivid-core.c @@ -169,6 +169,14 @@ MODULE_PARM_DESC(allocators, " memory allocator selection, default is 0.\n" "\t\t 0 == vmalloc\n" "\t\t 1 == dma-contig"); +static unsigned int cache_hints[VIVID_MAX_DEVS] = { + [0 ... (VIVID_MAX_DEVS - 1)] = 0 +}; +module_param_array(cache_hints, uint, NULL, 0444); +MODULE_PARM_DESC(cache_hints, " user-space cache hints, default is 0.\n" + "\t\t 0 == forbid\n" + "\t\t 1 == allow"); + static struct vivid_dev *vivid_devs[VIVID_MAX_DEVS]; const struct v4l2_rect vivid_min_rect = { @@ -819,6 +827,7 @@ static int vivid_create_queue(struct vivid_dev *dev, q->lock = &dev->mutex; q->dev = dev->v4l2_dev.dev; q->supports_requests = true; + q->allow_cache_hints = (cache_hints[dev->inst] == 1); return vb2_queue_init(q); } -- GitLab From 9ec656cf571edb85de72c21b3ca852289c4788ae Mon Sep 17 00:00:00 2001 From: Tomasz Figa Date: Tue, 19 Nov 2019 12:34:57 +0100 Subject: [PATCH 0127/1476] media: docs-rst: Document memory-to-memory video encoder interface MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Due to complexity of the video encoding process, the V4L2 drivers of stateful encoder hardware require specific sequences of V4L2 API calls to be followed. These include capability enumeration, initialization, encoding, encode parameters change, drain and reset. Specifics of the above have been discussed during Media Workshops at LinuxCon Europe 2012 in Barcelona and then later Embedded Linux Conference Europe 2014 in Düsseldorf. The de facto Codec API that originated at those events was later implemented by the drivers we already have merged in mainline, such as s5p-mfc or coda. The only thing missing was the real specification included as a part of Linux Media documentation. Fix it now and document the encoder part of the Codec API. Signed-off-by: Tomasz Figa Signed-off-by: Hans Verkuil Reviewed-by: Michael Tretter Signed-off-by: Mauro Carvalho Chehab --- .../userspace-api/media/v4l/dev-encoder.rst | 728 ++++++++++++++++++ .../userspace-api/media/v4l/dev-mem2mem.rst | 1 + .../userspace-api/media/v4l/pixfmt-v4l2.rst | 5 + .../userspace-api/media/v4l/v4l2.rst | 2 + .../media/v4l/vidioc-encoder-cmd.rst | 51 +- 5 files changed, 767 insertions(+), 20 deletions(-) create mode 100644 Documentation/userspace-api/media/v4l/dev-encoder.rst diff --git a/Documentation/userspace-api/media/v4l/dev-encoder.rst b/Documentation/userspace-api/media/v4l/dev-encoder.rst new file mode 100644 index 0000000000000..aace7b812a9c4 --- /dev/null +++ b/Documentation/userspace-api/media/v4l/dev-encoder.rst @@ -0,0 +1,728 @@ +.. SPDX-License-Identifier: GPL-2.0 + +.. _encoder: + +************************************************* +Memory-to-Memory Stateful Video Encoder Interface +************************************************* + +A stateful video encoder takes raw video frames in display order and encodes +them into a bytestream. It generates complete chunks of the bytestream, including +all metadata, headers, etc. The resulting bytestream does not require any +further post-processing by the client. + +Performing software stream processing, header generation etc. in the driver +in order to support this interface is strongly discouraged. In case such +operations are needed, use of the Stateless Video Encoder Interface (in +development) is strongly advised. + +Conventions and Notations Used in This Document +=============================================== + +1. The general V4L2 API rules apply if not specified in this document + otherwise. + +2. The meaning of words "must", "may", "should", etc. is as per `RFC + 2119 `_. + +3. All steps not marked "optional" are required. + +4. :c:func:`VIDIOC_G_EXT_CTRLS` and :c:func:`VIDIOC_S_EXT_CTRLS` may be used + interchangeably with :c:func:`VIDIOC_G_CTRL` and :c:func:`VIDIOC_S_CTRL`, + unless specified otherwise. + +5. Single-planar API (see :ref:`planar-apis`) and applicable structures may be + used interchangeably with multi-planar API, unless specified otherwise, + depending on encoder capabilities and following the general V4L2 guidelines. + +6. i = [a..b]: sequence of integers from a to b, inclusive, i.e. i = + [0..2]: i = 0, 1, 2. + +7. Given an ``OUTPUT`` buffer A, then A' represents a buffer on the ``CAPTURE`` + queue containing data that resulted from processing buffer A. + +Glossary +======== + +Refer to :ref:`decoder-glossary`. + +State Machine +============= + +.. kernel-render:: DOT + :alt: DOT digraph of encoder state machine + :caption: Encoder State Machine + + digraph encoder_state_machine { + node [shape = doublecircle, label="Encoding"] Encoding; + + node [shape = circle, label="Initialization"] Initialization; + node [shape = circle, label="Stopped"] Stopped; + node [shape = circle, label="Drain"] Drain; + node [shape = circle, label="Reset"] Reset; + + node [shape = point]; qi + qi -> Initialization [ label = "open()" ]; + + Initialization -> Encoding [ label = "Both queues streaming" ]; + + Encoding -> Drain [ label = "V4L2_ENC_CMD_STOP" ]; + Encoding -> Reset [ label = "VIDIOC_STREAMOFF(CAPTURE)" ]; + Encoding -> Stopped [ label = "VIDIOC_STREAMOFF(OUTPUT)" ]; + Encoding -> Encoding; + + Drain -> Stopped [ label = "All CAPTURE\nbuffers dequeued\nor\nVIDIOC_STREAMOFF(OUTPUT)" ]; + Drain -> Reset [ label = "VIDIOC_STREAMOFF(CAPTURE)" ]; + + Reset -> Encoding [ label = "VIDIOC_STREAMON(CAPTURE)" ]; + Reset -> Initialization [ label = "VIDIOC_REQBUFS(OUTPUT, 0)" ]; + + Stopped -> Encoding [ label = "V4L2_ENC_CMD_START\nor\nVIDIOC_STREAMON(OUTPUT)" ]; + Stopped -> Reset [ label = "VIDIOC_STREAMOFF(CAPTURE)" ]; + } + +Querying Capabilities +===================== + +1. To enumerate the set of coded formats supported by the encoder, the + client may call :c:func:`VIDIOC_ENUM_FMT` on ``CAPTURE``. + + * The full set of supported formats will be returned, regardless of the + format set on ``OUTPUT``. + +2. To enumerate the set of supported raw formats, the client may call + :c:func:`VIDIOC_ENUM_FMT` on ``OUTPUT``. + + * Only the formats supported for the format currently active on ``CAPTURE`` + will be returned. + + * In order to enumerate raw formats supported by a given coded format, + the client must first set that coded format on ``CAPTURE`` and then + enumerate the formats on ``OUTPUT``. + +3. The client may use :c:func:`VIDIOC_ENUM_FRAMESIZES` to detect supported + resolutions for a given format, passing the desired pixel format in + :c:type:`v4l2_frmsizeenum` ``pixel_format``. + + * Values returned by :c:func:`VIDIOC_ENUM_FRAMESIZES` for a coded pixel + format will include all possible coded resolutions supported by the + encoder for the given coded pixel format. + + * Values returned by :c:func:`VIDIOC_ENUM_FRAMESIZES` for a raw pixel format + will include all possible frame buffer resolutions supported by the + encoder for the given raw pixel format and coded format currently set on + ``CAPTURE``. + +4. The client may use :c:func:`VIDIOC_ENUM_FRAMEINTERVALS` to detect supported + frame intervals for a given format and resolution, passing the desired pixel + format in :c:type:`v4l2_frmsizeenum` ``pixel_format`` and the resolution + in :c:type:`v4l2_frmsizeenum` ``width`` and :c:type:`v4l2_frmsizeenum` + ``height``. + + * Values returned by :c:func:`VIDIOC_ENUM_FRAMEINTERVALS` for a coded pixel + format and coded resolution will include all possible frame intervals + supported by the encoder for the given coded pixel format and resolution. + + * Values returned by :c:func:`VIDIOC_ENUM_FRAMEINTERVALS` for a raw pixel + format and resolution will include all possible frame intervals supported + by the encoder for the given raw pixel format and resolution and for the + coded format, coded resolution and coded frame interval currently set on + ``CAPTURE``. + + * Support for :c:func:`VIDIOC_ENUM_FRAMEINTERVALS` is optional. If it is + not implemented, then there are no special restrictions other than the + limits of the codec itself. + +5. Supported profiles and levels for the coded format currently set on + ``CAPTURE``, if applicable, may be queried using their respective controls + via :c:func:`VIDIOC_QUERYCTRL`. + +6. Any additional encoder capabilities may be discovered by querying + their respective controls. + +Initialization +============== + +1. Set the coded format on the ``CAPTURE`` queue via :c:func:`VIDIOC_S_FMT`. + + * **Required fields:** + + ``type`` + a ``V4L2_BUF_TYPE_*`` enum appropriate for ``CAPTURE``. + + ``pixelformat`` + the coded format to be produced. + + ``sizeimage`` + desired size of ``CAPTURE`` buffers; the encoder may adjust it to + match hardware requirements. + + ``width``, ``height`` + ignored (read-only). + + other fields + follow standard semantics. + + * **Return fields:** + + ``sizeimage`` + adjusted size of ``CAPTURE`` buffers. + + ``width``, ``height`` + the coded size selected by the encoder based on current state, e.g. + ``OUTPUT`` format, selection rectangles, etc. (read-only). + + .. important:: + + Changing the ``CAPTURE`` format may change the currently set ``OUTPUT`` + format. How the new ``OUTPUT`` format is determined is up to the encoder + and the client must ensure it matches its needs afterwards. + +2. **Optional.** Enumerate supported ``OUTPUT`` formats (raw formats for + source) for the selected coded format via :c:func:`VIDIOC_ENUM_FMT`. + + * **Required fields:** + + ``type`` + a ``V4L2_BUF_TYPE_*`` enum appropriate for ``OUTPUT``. + + other fields + follow standard semantics. + + * **Return fields:** + + ``pixelformat`` + raw format supported for the coded format currently selected on + the ``CAPTURE`` queue. + + other fields + follow standard semantics. + +3. Set the raw source format on the ``OUTPUT`` queue via + :c:func:`VIDIOC_S_FMT`. + + * **Required fields:** + + ``type`` + a ``V4L2_BUF_TYPE_*`` enum appropriate for ``OUTPUT``. + + ``pixelformat`` + raw format of the source. + + ``width``, ``height`` + source resolution. + + other fields + follow standard semantics. + + * **Return fields:** + + ``width``, ``height`` + may be adjusted to match encoder minimums, maximums and alignment + requirements, as required by the currently selected formats, as + reported by :c:func:`VIDIOC_ENUM_FRAMESIZES`. + + other fields + follow standard semantics. + + * Setting the ``OUTPUT`` format will reset the selection rectangles to their + default values, based on the new resolution, as described in the next + step. + +4. Set the raw frame interval on the ``OUTPUT`` queue via + :c:func:`VIDIOC_S_PARM`. This also sets the coded frame interval on the + ``CAPTURE`` queue to the same value. + + * ** Required fields:** + + ``type`` + a ``V4L2_BUF_TYPE_*`` enum appropriate for ``OUTPUT``. + + ``parm.output`` + set all fields except ``parm.output.timeperframe`` to 0. + + ``parm.output.timeperframe`` + the desired frame interval; the encoder may adjust it to + match hardware requirements. + + * **Return fields:** + + ``parm.output.timeperframe`` + the adjusted frame interval. + + .. important:: + + Changing the ``OUTPUT`` frame interval *also* sets the framerate that + the encoder uses to encode the video. So setting the frame interval + to 1/24 (or 24 frames per second) will produce a coded video stream + that can be played back at that speed. The frame interval for the + ``OUTPUT`` queue is just a hint, the application may provide raw + frames at a different rate. It can be used by the driver to help + schedule multiple encoders running in parallel. + + In the next step the ``CAPTURE`` frame interval can optionally be + changed to a different value. This is useful for off-line encoding + were the coded frame interval can be different from the rate at + which raw frames are supplied. + + .. important:: + + ``timeperframe`` deals with *frames*, not fields. So for interlaced + formats this is the time per two fields, since a frame consists of + a top and a bottom field. + + .. note:: + + It is due to historical reasons that changing the ``OUTPUT`` frame + interval also changes the coded frame interval on the ``CAPTURE`` + queue. Ideally these would be independent settings, but that would + break the existing API. + +5. **Optional** Set the coded frame interval on the ``CAPTURE`` queue via + :c:func:`VIDIOC_S_PARM`. This is only necessary if the coded frame + interval is different from the raw frame interval, which is typically + the case for off-line encoding. + + * ** Required fields:** + + ``type`` + a ``V4L2_BUF_TYPE_*`` enum appropriate for ``CAPTURE``. + + ``parm.capture`` + set all fields except ``parm.capture.timeperframe`` to 0. + + ``parm.capture.timeperframe`` + the desired coded frame interval; the encoder may adjust it to + match hardware requirements. + + * **Return fields:** + + ``parm.capture.timeperframe`` + the adjusted frame interval. + + .. important:: + + Changing the ``CAPTURE`` frame interval sets the framerate for the + coded video. It does *not* set the rate at which buffers arrive on the + ``CAPTURE`` queue, that depends on how fast the encoder is and how + fast raw frames are queued on the ``OUTPUT`` queue. + + .. important:: + + ``timeperframe`` deals with *frames*, not fields. So for interlaced + formats this is the time per two fields, since a frame consists of + a top and a bottom field. + + .. note:: + + Not all drivers support this functionality, in that case just set + the desired coded frame interval for the ``OUTPUT`` queue. + + However, drivers that can schedule multiple encoders based on the + ``OUTPUT`` frame interval must support this optional feature. + +6. **Optional.** Set the visible resolution for the stream metadata via + :c:func:`VIDIOC_S_SELECTION` on the ``OUTPUT`` queue if it is desired + to be different than the full OUTPUT resolution. + + * **Required fields:** + + ``type`` + a ``V4L2_BUF_TYPE_*`` enum appropriate for ``OUTPUT``. + + ``target`` + set to ``V4L2_SEL_TGT_CROP``. + + ``r.left``, ``r.top``, ``r.width``, ``r.height`` + visible rectangle; this must fit within the `V4L2_SEL_TGT_CROP_BOUNDS` + rectangle and may be subject to adjustment to match codec and + hardware constraints. + + * **Return fields:** + + ``r.left``, ``r.top``, ``r.width``, ``r.height`` + visible rectangle adjusted by the encoder. + + * The following selection targets are supported on ``OUTPUT``: + + ``V4L2_SEL_TGT_CROP_BOUNDS`` + equal to the full source frame, matching the active ``OUTPUT`` + format. + + ``V4L2_SEL_TGT_CROP_DEFAULT`` + equal to ``V4L2_SEL_TGT_CROP_BOUNDS``. + + ``V4L2_SEL_TGT_CROP`` + rectangle within the source buffer to be encoded into the + ``CAPTURE`` stream; defaults to ``V4L2_SEL_TGT_CROP_DEFAULT``. + + .. note:: + + A common use case for this selection target is encoding a source + video with a resolution that is not a multiple of a macroblock, + e.g. the common 1920x1080 resolution may require the source + buffers to be aligned to 1920x1088 for codecs with 16x16 macroblock + size. To avoid encoding the padding, the client needs to explicitly + configure this selection target to 1920x1080. + + .. warning:: + + The encoder may adjust the crop/compose rectangles to the nearest + supported ones to meet codec and hardware requirements. The client needs + to check the adjusted rectangle returned by :c:func:`VIDIOC_S_SELECTION`. + +7. Allocate buffers for both ``OUTPUT`` and ``CAPTURE`` via + :c:func:`VIDIOC_REQBUFS`. This may be performed in any order. + + * **Required fields:** + + ``count`` + requested number of buffers to allocate; greater than zero. + + ``type`` + a ``V4L2_BUF_TYPE_*`` enum appropriate for ``OUTPUT`` or + ``CAPTURE``. + + other fields + follow standard semantics. + + * **Return fields:** + + ``count`` + actual number of buffers allocated. + + .. warning:: + + The actual number of allocated buffers may differ from the ``count`` + given. The client must check the updated value of ``count`` after the + call returns. + + .. note:: + + To allocate more than the minimum number of OUTPUT buffers (for pipeline + depth), the client may query the ``V4L2_CID_MIN_BUFFERS_FOR_OUTPUT`` + control to get the minimum number of buffers required, and pass the + obtained value plus the number of additional buffers needed in the + ``count`` field to :c:func:`VIDIOC_REQBUFS`. + + Alternatively, :c:func:`VIDIOC_CREATE_BUFS` can be used to have more + control over buffer allocation. + + * **Required fields:** + + ``count`` + requested number of buffers to allocate; greater than zero. + + ``type`` + a ``V4L2_BUF_TYPE_*`` enum appropriate for ``OUTPUT``. + + other fields + follow standard semantics. + + * **Return fields:** + + ``count`` + adjusted to the number of allocated buffers. + +8. Begin streaming on both ``OUTPUT`` and ``CAPTURE`` queues via + :c:func:`VIDIOC_STREAMON`. This may be performed in any order. The actual + encoding process starts when both queues start streaming. + +.. note:: + + If the client stops the ``CAPTURE`` queue during the encode process and then + restarts it again, the encoder will begin generating a stream independent + from the stream generated before the stop. The exact constraints depend + on the coded format, but may include the following implications: + + * encoded frames produced after the restart must not reference any + frames produced before the stop, e.g. no long term references for + H.264/HEVC, + + * any headers that must be included in a standalone stream must be + produced again, e.g. SPS and PPS for H.264/HEVC. + +Encoding +======== + +This state is reached after the `Initialization` sequence finishes +successfully. In this state, the client queues and dequeues buffers to both +queues via :c:func:`VIDIOC_QBUF` and :c:func:`VIDIOC_DQBUF`, following the +standard semantics. + +The content of encoded ``CAPTURE`` buffers depends on the active coded pixel +format and may be affected by codec-specific extended controls, as stated +in the documentation of each format. + +Both queues operate independently, following standard behavior of V4L2 buffer +queues and memory-to-memory devices. In addition, the order of encoded frames +dequeued from the ``CAPTURE`` queue may differ from the order of queuing raw +frames to the ``OUTPUT`` queue, due to properties of the selected coded format, +e.g. frame reordering. + +The client must not assume any direct relationship between ``CAPTURE`` and +``OUTPUT`` buffers and any specific timing of buffers becoming +available to dequeue. Specifically: + +* a buffer queued to ``OUTPUT`` may result in more than one buffer produced on + ``CAPTURE`` (for example, if returning an encoded frame allowed the encoder + to return a frame that preceded it in display, but succeeded it in the decode + order; however, there may be other reasons for this as well), + +* a buffer queued to ``OUTPUT`` may result in a buffer being produced on + ``CAPTURE`` later into encode process, and/or after processing further + ``OUTPUT`` buffers, or be returned out of order, e.g. if display + reordering is used, + +* buffers may become available on the ``CAPTURE`` queue without additional + buffers queued to ``OUTPUT`` (e.g. during drain or ``EOS``), because of the + ``OUTPUT`` buffers queued in the past whose encoding results are only + available at later time, due to specifics of the encoding process, + +* buffers queued to ``OUTPUT`` may not become available to dequeue instantly + after being encoded into a corresponding ``CAPTURE`` buffer, e.g. if the + encoder needs to use the frame as a reference for encoding further frames. + +.. note:: + + To allow matching encoded ``CAPTURE`` buffers with ``OUTPUT`` buffers they + originated from, the client can set the ``timestamp`` field of the + :c:type:`v4l2_buffer` struct when queuing an ``OUTPUT`` buffer. The + ``CAPTURE`` buffer(s), which resulted from encoding that ``OUTPUT`` buffer + will have their ``timestamp`` field set to the same value when dequeued. + + In addition to the straightforward case of one ``OUTPUT`` buffer producing + one ``CAPTURE`` buffer, the following cases are defined: + + * one ``OUTPUT`` buffer generates multiple ``CAPTURE`` buffers: the same + ``OUTPUT`` timestamp will be copied to multiple ``CAPTURE`` buffers, + + * the encoding order differs from the presentation order (i.e. the + ``CAPTURE`` buffers are out-of-order compared to the ``OUTPUT`` buffers): + ``CAPTURE`` timestamps will not retain the order of ``OUTPUT`` timestamps. + +.. note:: + + To let the client distinguish between frame types (keyframes, intermediate + frames; the exact list of types depends on the coded format), the + ``CAPTURE`` buffers will have corresponding flag bits set in their + :c:type:`v4l2_buffer` struct when dequeued. See the documentation of + :c:type:`v4l2_buffer` and each coded pixel format for exact list of flags + and their meanings. + +Should an encoding error occur, it will be reported to the client with the level +of details depending on the encoder capabilities. Specifically: + +* the ``CAPTURE`` buffer (if any) that contains the results of the failed encode + operation will be returned with the ``V4L2_BUF_FLAG_ERROR`` flag set, + +* if the encoder is able to precisely report the ``OUTPUT`` buffer(s) that triggered + the error, such buffer(s) will be returned with the ``V4L2_BUF_FLAG_ERROR`` flag + set. + +.. note:: + + If a ``CAPTURE`` buffer is too small then it is just returned with the + ``V4L2_BUF_FLAG_ERROR`` flag set. More work is needed to detect that this + error occurred because the buffer was too small, and to provide support to + free existing buffers that were too small. + +In case of a fatal failure that does not allow the encoding to continue, any +further operations on corresponding encoder file handle will return the -EIO +error code. The client may close the file handle and open a new one, or +alternatively reinitialize the instance by stopping streaming on both queues, +releasing all buffers and performing the Initialization sequence again. + +Encoding Parameter Changes +========================== + +The client is allowed to use :c:func:`VIDIOC_S_CTRL` to change encoder +parameters at any time. The availability of parameters is encoder-specific +and the client must query the encoder to find the set of available controls. + +The ability to change each parameter during encoding is encoder-specific, as +per the standard semantics of the V4L2 control interface. The client may +attempt to set a control during encoding and if the operation fails with the +-EBUSY error code, the ``CAPTURE`` queue needs to be stopped for the +configuration change to be allowed. To do this, it may follow the `Drain` +sequence to avoid losing the already queued/encoded frames. + +The timing of parameter updates is encoder-specific, as per the standard +semantics of the V4L2 control interface. If the client needs to apply the +parameters exactly at specific frame, using the Request API +(:ref:`media-request-api`) should be considered, if supported by the encoder. + +Drain +===== + +To ensure that all the queued ``OUTPUT`` buffers have been processed and the +related ``CAPTURE`` buffers are given to the client, the client must follow the +drain sequence described below. After the drain sequence ends, the client has +received all encoded frames for all ``OUTPUT`` buffers queued before the +sequence was started. + +1. Begin the drain sequence by issuing :c:func:`VIDIOC_ENCODER_CMD`. + + * **Required fields:** + + ``cmd`` + set to ``V4L2_ENC_CMD_STOP``. + + ``flags`` + set to 0. + + ``pts`` + set to 0. + + .. warning:: + + The sequence can be only initiated if both ``OUTPUT`` and ``CAPTURE`` + queues are streaming. For compatibility reasons, the call to + :c:func:`VIDIOC_ENCODER_CMD` will not fail even if any of the queues is + not streaming, but at the same time it will not initiate the `Drain` + sequence and so the steps described below would not be applicable. + +2. Any ``OUTPUT`` buffers queued by the client before the + :c:func:`VIDIOC_ENCODER_CMD` was issued will be processed and encoded as + normal. The client must continue to handle both queues independently, + similarly to normal encode operation. This includes: + + * queuing and dequeuing ``CAPTURE`` buffers, until a buffer marked with the + ``V4L2_BUF_FLAG_LAST`` flag is dequeued, + + .. warning:: + + The last buffer may be empty (with :c:type:`v4l2_buffer` + ``bytesused`` = 0) and in that case it must be ignored by the client, + as it does not contain an encoded frame. + + .. note:: + + Any attempt to dequeue more ``CAPTURE`` buffers beyond the buffer + marked with ``V4L2_BUF_FLAG_LAST`` will result in a -EPIPE error from + :c:func:`VIDIOC_DQBUF`. + + * dequeuing processed ``OUTPUT`` buffers, until all the buffers queued + before the ``V4L2_ENC_CMD_STOP`` command are dequeued, + + * dequeuing the ``V4L2_EVENT_EOS`` event, if the client subscribes to it. + + .. note:: + + For backwards compatibility, the encoder will signal a ``V4L2_EVENT_EOS`` + event when the last frame has been encoded and all frames are ready to be + dequeued. It is deprecated behavior and the client must not rely on it. + The ``V4L2_BUF_FLAG_LAST`` buffer flag should be used instead. + +3. Once all ``OUTPUT`` buffers queued before the ``V4L2_ENC_CMD_STOP`` call are + dequeued and the last ``CAPTURE`` buffer is dequeued, the encoder is stopped + and it will accept, but not process any newly queued ``OUTPUT`` buffers + until the client issues any of the following operations: + + * ``V4L2_ENC_CMD_START`` - the encoder will not be reset and will resume + operation normally, with all the state from before the drain, + + * a pair of :c:func:`VIDIOC_STREAMOFF` and :c:func:`VIDIOC_STREAMON` on the + ``CAPTURE`` queue - the encoder will be reset (see the `Reset` sequence) + and then resume encoding, + + * a pair of :c:func:`VIDIOC_STREAMOFF` and :c:func:`VIDIOC_STREAMON` on the + ``OUTPUT`` queue - the encoder will resume operation normally, however any + source frames queued to the ``OUTPUT`` queue between ``V4L2_ENC_CMD_STOP`` + and :c:func:`VIDIOC_STREAMOFF` will be discarded. + +.. note:: + + Once the drain sequence is initiated, the client needs to drive it to + completion, as described by the steps above, unless it aborts the process by + issuing :c:func:`VIDIOC_STREAMOFF` on any of the ``OUTPUT`` or ``CAPTURE`` + queues. The client is not allowed to issue ``V4L2_ENC_CMD_START`` or + ``V4L2_ENC_CMD_STOP`` again while the drain sequence is in progress and they + will fail with -EBUSY error code if attempted. + + For reference, handling of various corner cases is described below: + + * In case of no buffer in the ``OUTPUT`` queue at the time the + ``V4L2_ENC_CMD_STOP`` command was issued, the drain sequence completes + immediately and the encoder returns an empty ``CAPTURE`` buffer with the + ``V4L2_BUF_FLAG_LAST`` flag set. + + * In case of no buffer in the ``CAPTURE`` queue at the time the drain + sequence completes, the next time the client queues a ``CAPTURE`` buffer + it is returned at once as an empty buffer with the ``V4L2_BUF_FLAG_LAST`` + flag set. + + * If :c:func:`VIDIOC_STREAMOFF` is called on the ``CAPTURE`` queue in the + middle of the drain sequence, the drain sequence is canceled and all + ``CAPTURE`` buffers are implicitly returned to the client. + + * If :c:func:`VIDIOC_STREAMOFF` is called on the ``OUTPUT`` queue in the + middle of the drain sequence, the drain sequence completes immediately and + next ``CAPTURE`` buffer will be returned empty with the + ``V4L2_BUF_FLAG_LAST`` flag set. + + Although not mandatory, the availability of encoder commands may be queried + using :c:func:`VIDIOC_TRY_ENCODER_CMD`. + +Reset +===== + +The client may want to request the encoder to reinitialize the encoding, so +that the following stream data becomes independent from the stream data +generated before. Depending on the coded format, that may imply that: + +* encoded frames produced after the restart must not reference any frames + produced before the stop, e.g. no long term references for H.264/HEVC, + +* any headers that must be included in a standalone stream must be produced + again, e.g. SPS and PPS for H.264/HEVC. + +This can be achieved by performing the reset sequence. + +1. Perform the `Drain` sequence to ensure all the in-flight encoding finishes + and respective buffers are dequeued. + +2. Stop streaming on the ``CAPTURE`` queue via :c:func:`VIDIOC_STREAMOFF`. This + will return all currently queued ``CAPTURE`` buffers to the client, without + valid frame data. + +3. Start streaming on the ``CAPTURE`` queue via :c:func:`VIDIOC_STREAMON` and + continue with regular encoding sequence. The encoded frames produced into + ``CAPTURE`` buffers from now on will contain a standalone stream that can be + decoded without the need for frames encoded before the reset sequence, + starting at the first ``OUTPUT`` buffer queued after issuing the + `V4L2_ENC_CMD_STOP` of the `Drain` sequence. + +This sequence may be also used to change encoding parameters for encoders +without the ability to change the parameters on the fly. + +Commit Points +============= + +Setting formats and allocating buffers triggers changes in the behavior of the +encoder. + +1. Setting the format on the ``CAPTURE`` queue may change the set of formats + supported/advertised on the ``OUTPUT`` queue. In particular, it also means + that the ``OUTPUT`` format may be reset and the client must not rely on the + previously set format being preserved. + +2. Enumerating formats on the ``OUTPUT`` queue always returns only formats + supported for the current ``CAPTURE`` format. + +3. Setting the format on the ``OUTPUT`` queue does not change the list of + formats available on the ``CAPTURE`` queue. An attempt to set the ``OUTPUT`` + format that is not supported for the currently selected ``CAPTURE`` format + will result in the encoder adjusting the requested ``OUTPUT`` format to a + supported one. + +4. Enumerating formats on the ``CAPTURE`` queue always returns the full set of + supported coded formats, irrespective of the current ``OUTPUT`` format. + +5. While buffers are allocated on any of the ``OUTPUT`` or ``CAPTURE`` queues, + the client must not change the format on the ``CAPTURE`` queue. Drivers will + return the -EBUSY error code for any such format change attempt. + +To summarize, setting formats and allocation must always start with the +``CAPTURE`` queue and the ``CAPTURE`` queue is the master that governs the +set of supported formats for the ``OUTPUT`` queue. diff --git a/Documentation/userspace-api/media/v4l/dev-mem2mem.rst b/Documentation/userspace-api/media/v4l/dev-mem2mem.rst index 9279d87c08a1a..40aff9c952670 100644 --- a/Documentation/userspace-api/media/v4l/dev-mem2mem.rst +++ b/Documentation/userspace-api/media/v4l/dev-mem2mem.rst @@ -46,4 +46,5 @@ devices are given in the following sections. :maxdepth: 1 dev-decoder + dev-encoder dev-stateless-decoder diff --git a/Documentation/userspace-api/media/v4l/pixfmt-v4l2.rst b/Documentation/userspace-api/media/v4l/pixfmt-v4l2.rst index 759420a872d60..e0ee2823ab1fd 100644 --- a/Documentation/userspace-api/media/v4l/pixfmt-v4l2.rst +++ b/Documentation/userspace-api/media/v4l/pixfmt-v4l2.rst @@ -44,6 +44,11 @@ Single-planar format structure inside the stream, when fed to a stateful mem2mem decoder, the fields may be zero to rely on the decoder to detect the right values. For more details see :ref:`decoder` and format descriptions. + + For compressed formats on the CAPTURE side of a stateful mem2mem + encoder, the fields must be zero, since the coded size is expected to + be calculated internally by the encoder itself, based on the OUTPUT + side. For more details see :ref:`encoder` and format descriptions. * - __u32 - ``pixelformat`` - The pixel format or type of compression, set by the application. diff --git a/Documentation/userspace-api/media/v4l/v4l2.rst b/Documentation/userspace-api/media/v4l/v4l2.rst index ab7c97c39b970..35796c4fbe522 100644 --- a/Documentation/userspace-api/media/v4l/v4l2.rst +++ b/Documentation/userspace-api/media/v4l/v4l2.rst @@ -63,6 +63,7 @@ Authors, in alphabetical order: - Figa, Tomasz - Documented the memory-to-memory decoder interface. + - Documented the memory-to-memory encoder interface. - H Schimek, Michael @@ -75,6 +76,7 @@ Authors, in alphabetical order: - Osciak, Pawel - Documented the memory-to-memory decoder interface. + - Documented the memory-to-memory encoder interface. - Osciak, Pawel diff --git a/Documentation/userspace-api/media/v4l/vidioc-encoder-cmd.rst b/Documentation/userspace-api/media/v4l/vidioc-encoder-cmd.rst index 16269b3b1715f..d0eacce5485e3 100644 --- a/Documentation/userspace-api/media/v4l/vidioc-encoder-cmd.rst +++ b/Documentation/userspace-api/media/v4l/vidioc-encoder-cmd.rst @@ -51,25 +51,26 @@ To send a command applications must initialize all fields of a struct ``VIDIOC_ENCODER_CMD`` or ``VIDIOC_TRY_ENCODER_CMD`` with a pointer to this structure. -The ``cmd`` field must contain the command code. The ``flags`` field is -currently only used by the STOP command and contains one bit: If the -``V4L2_ENC_CMD_STOP_AT_GOP_END`` flag is set, encoding will continue -until the end of the current *Group Of Pictures*, otherwise it will stop -immediately. +The ``cmd`` field must contain the command code. Some commands use the +``flags`` field for additional information. -A :ref:`read() ` or :ref:`VIDIOC_STREAMON ` -call sends an implicit START command to the encoder if it has not been -started yet. After a STOP command, :ref:`read() ` calls will read +After a STOP command, :ref:`read() ` calls will read the remaining data buffered by the driver. When the buffer is empty, :ref:`read() ` will return zero and the next :ref:`read() ` call will restart the encoder. +A :ref:`read() ` or :ref:`VIDIOC_STREAMON ` +call sends an implicit START command to the encoder if it has not been +started yet. Applies to both queues of mem2mem encoders. + A :ref:`close() ` or :ref:`VIDIOC_STREAMOFF ` call of a streaming file descriptor sends an implicit immediate STOP to -the encoder, and all buffered data is discarded. +the encoder, and all buffered data is discarded. Applies to both queues of +mem2mem encoders. These ioctls are optional, not all drivers may support them. They were -introduced in Linux 2.6.21. +introduced in Linux 2.6.21. They are, however, mandatory for stateful mem2mem +encoders (as further documented in :ref:`encoder`). .. tabularcolumns:: |p{4.4cm}|p{4.4cm}|p{8.7cm}| @@ -109,21 +110,24 @@ introduced in Linux 2.6.21. - 0 - Start the encoder. When the encoder is already running or paused, this command does nothing. No flags are defined for this command. + + For a device implementing the :ref:`encoder`, once the drain sequence + is initiated with the ``V4L2_ENC_CMD_STOP`` command, it must be driven + to completion before this command can be invoked. Any attempt to + invoke the command while the drain sequence is in progress will trigger + an ``EBUSY`` error code. See :ref:`encoder` for more details. * - ``V4L2_ENC_CMD_STOP`` - 1 - Stop the encoder. When the ``V4L2_ENC_CMD_STOP_AT_GOP_END`` flag is set, encoding will continue until the end of the current *Group Of Pictures*, otherwise encoding will stop immediately. When the - encoder is already stopped, this command does nothing. mem2mem - encoders will send a ``V4L2_EVENT_EOS`` event when the last frame - has been encoded and all frames are ready to be dequeued and will - set the ``V4L2_BUF_FLAG_LAST`` buffer flag on the last buffer of - the capture queue to indicate there will be no new buffers - produced to dequeue. This buffer may be empty, indicated by the - driver setting the ``bytesused`` field to 0. Once the - ``V4L2_BUF_FLAG_LAST`` flag was set, the - :ref:`VIDIOC_DQBUF ` ioctl will not block anymore, - but return an ``EPIPE`` error code. + encoder is already stopped, this command does nothing. + + For a device implementing the :ref:`encoder`, the command will initiate + the drain sequence as documented in :ref:`encoder`. No flags or other + arguments are accepted in this case. Any attempt to invoke the command + again before the sequence completes will trigger an ``EBUSY`` error + code. * - ``V4L2_ENC_CMD_PAUSE`` - 2 - Pause the encoder. When the encoder has not been started yet, the @@ -152,6 +156,8 @@ introduced in Linux 2.6.21. - Stop encoding at the end of the current *Group Of Pictures*, rather than immediately. + Does not apply to :ref:`encoder`. + Return Value ============ @@ -160,6 +166,11 @@ On success 0 is returned, on error -1 and the ``errno`` variable is set appropriately. The generic error codes are described at the :ref:`Generic Error Codes ` chapter. +EBUSY + A drain sequence of a device implementing the :ref:`encoder` is still in + progress. It is not allowed to issue another encoder command until it + completes. + EINVAL The ``cmd`` field is invalid. -- GitLab From aa2e479fd021b975b95409f8a3181b953796aa01 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Wed, 20 May 2020 11:42:23 +0200 Subject: [PATCH 0128/1476] media: vidioc-g-parm.rst: update the VIDIOC_G/S_PARM documentation This documentation is very outdated. In particular, it is not obvious at all that this is used to change the framerate of sensors. Fix it, and include references to the stateful encoder API where this works slightly different. Signed-off-by: Hans Verkuil Acked-by: Tomasz Figa Reviewed-by: Michael Tretter Signed-off-by: Mauro Carvalho Chehab --- .../userspace-api/media/v4l/vidioc-g-parm.rst | 51 ++++++++++++------- 1 file changed, 32 insertions(+), 19 deletions(-) diff --git a/Documentation/userspace-api/media/v4l/vidioc-g-parm.rst b/Documentation/userspace-api/media/v4l/vidioc-g-parm.rst index 42e9f6ee7a59e..59e02aca164c6 100644 --- a/Documentation/userspace-api/media/v4l/vidioc-g-parm.rst +++ b/Documentation/userspace-api/media/v4l/vidioc-g-parm.rst @@ -42,12 +42,13 @@ Arguments Description =========== -The current video standard determines a nominal number of frames per -second. If less than this number of frames is to be captured or output, -applications can request frame skipping or duplicating on the driver -side. This is especially useful when using the :ref:`read() ` or -:ref:`write() `, which are not augmented by timestamps or sequence -counters, and to avoid unnecessary data copying. +Applications can request a different frame interval. The capture or +output device will be reconfigured to support the requested frame +interval if possible. Optionally drivers may choose to skip or +repeat frames to achieve the requested frame interval. + +For stateful encoders (see :ref:`encoder`) this represents the +frame interval that is typically embedded in the encoded video stream. Changing the frame interval shall never change the format. Changing the format, on the other hand, may change the frame interval. @@ -57,7 +58,8 @@ internally by a driver in read/write mode. For implications see the section discussing the :ref:`read() ` function. To get and set the streaming parameters applications call the -:ref:`VIDIOC_G_PARM ` and :ref:`VIDIOC_S_PARM ` ioctl, respectively. They take a +:ref:`VIDIOC_G_PARM ` and +:ref:`VIDIOC_S_PARM ` ioctl, respectively. They take a pointer to a struct :c:type:`v4l2_streamparm` which contains a union holding separate parameters for input and output devices. @@ -113,14 +115,21 @@ union holding separate parameters for input and output devices. * - struct :c:type:`v4l2_fract` - ``timeperframe`` - This is the desired period between successive frames captured by - the driver, in seconds. The field is intended to skip frames on - the driver side, saving I/O bandwidth. + the driver, in seconds. + * - :cspan:`2` + + This will configure the speed at which the video source (e.g. a sensor) + generates video frames. If the speed is fixed, then the driver may + choose to skip or repeat frames in order to achieve the requested + frame rate. + + For stateful encoders (see :ref:`encoder`) this represents the + frame interval that is typically embedded in the encoded video stream. Applications store here the desired frame period, drivers return - the actual frame period, which must be greater or equal to the - nominal frame period determined by the current video standard - (struct :c:type:`v4l2_standard` ``frameperiod`` - field). Changing the video standard (also implicitly by switching + the actual frame period. + + Changing the video standard (also implicitly by switching the video input) may reset this parameter to the nominal frame period. To reset manually applications can just set this field to zero. @@ -173,11 +182,15 @@ union holding separate parameters for input and output devices. :ref:`write() ` mode (in streaming mode timestamps can be used to throttle the output), saving I/O bandwidth. + For stateful encoders (see :ref:`encoder`) this represents the + frame interval that is typically embedded in the encoded video stream + and it provides a hint to the encoder of the speed at which raw + frames are queued up to the encoder. + Applications store here the desired frame period, drivers return - the actual frame period, which must be greater or equal to the - nominal frame period determined by the current video standard - (struct :c:type:`v4l2_standard` ``frameperiod`` - field). Changing the video standard (also implicitly by switching + the actual frame period. + + Changing the video standard (also implicitly by switching the video output) may reset this parameter to the nominal frame period. To reset manually applications can just set this field to zero. @@ -216,8 +229,8 @@ union holding separate parameters for input and output devices. * - ``V4L2_CAP_TIMEPERFRAME`` - 0x1000 - - The frame skipping/repeating controlled by the ``timeperframe`` - field is supported. + - The frame period can be modified by setting the ``timeperframe`` + field. -- GitLab From eef7a5e7307f8f0da5254305703ac1da14250e77 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Tue, 26 May 2020 10:40:01 +0200 Subject: [PATCH 0129/1476] media: dev-decoder.rst: small fixes Add missing periods at the end of two sentences. Although mandatory -> Although not mandatory Signed-off-by: Hans Verkuil Reviewed-by: Michael Tretter Acked-by: Tomasz Figa Signed-off-by: Mauro Carvalho Chehab --- Documentation/userspace-api/media/v4l/dev-decoder.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Documentation/userspace-api/media/v4l/dev-decoder.rst b/Documentation/userspace-api/media/v4l/dev-decoder.rst index 606b54947e10b..04034dbc0b3ed 100644 --- a/Documentation/userspace-api/media/v4l/dev-decoder.rst +++ b/Documentation/userspace-api/media/v4l/dev-decoder.rst @@ -247,7 +247,7 @@ Querying Capabilities Initialization ============== -1. Set the coded format on ``OUTPUT`` via :c:func:`VIDIOC_S_FMT` +1. Set the coded format on ``OUTPUT`` via :c:func:`VIDIOC_S_FMT`. * **Required fields:** @@ -803,7 +803,7 @@ it may be affected as per normal decoder operation. * The decoder will drop all the pending ``OUTPUT`` buffers and they must be treated as returned to the client (following standard semantics). -2. Restart the ``OUTPUT`` queue via :c:func:`VIDIOC_STREAMON` +2. Restart the ``OUTPUT`` queue via :c:func:`VIDIOC_STREAMON`. * **Required fields:** @@ -1059,7 +1059,7 @@ sequence was started. ``V4L2_DEC_CMD_STOP`` again while the drain sequence is in progress and they will fail with -EBUSY error code if attempted. - Although mandatory, the availability of decoder commands may be queried + Although not mandatory, the availability of decoder commands may be queried using :c:func:`VIDIOC_TRY_DECODER_CMD`. End of Stream -- GitLab From 286cf7d3a99e1ca8c1d8e674b9a98f2dbe8520dc Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Tue, 26 May 2020 10:59:53 +0200 Subject: [PATCH 0130/1476] media: videodev2.h: add V4L2_FMT_FLAG_ENC_CAP_FRAME_INTERVAL flag Add the V4L2_FMT_FLAG_ENC_CAP_FRAME_INTERVAL flag to signal that the coded frame interval can be set separately from the raw frame interval for stateful encoders. Signed-off-by: Hans Verkuil Reviewed-by: Michael Tretter Acked-by: Tomasz Figa Signed-off-by: Mauro Carvalho Chehab --- .../media/v4l/vidioc-enum-fmt.rst | 30 +++++++++++++++---- .../media/videodev2.h.rst.exceptions | 1 + include/uapi/linux/videodev2.h | 1 + 3 files changed, 27 insertions(+), 5 deletions(-) diff --git a/Documentation/userspace-api/media/v4l/vidioc-enum-fmt.rst b/Documentation/userspace-api/media/v4l/vidioc-enum-fmt.rst index a53dd3d7f7e2a..05835e04c20b4 100644 --- a/Documentation/userspace-api/media/v4l/vidioc-enum-fmt.rst +++ b/Documentation/userspace-api/media/v4l/vidioc-enum-fmt.rst @@ -167,17 +167,37 @@ the ``mbus_code`` field is handled differently: - The hardware decoder for this compressed bytestream format (aka coded format) is capable of parsing a continuous bytestream. Applications do not need to parse the bytestream themselves to find the boundaries - between frames/fields. This flag can only be used in combination with - the ``V4L2_FMT_FLAG_COMPRESSED`` flag, since this applies to compressed + between frames/fields. + + This flag can only be used in combination with the + ``V4L2_FMT_FLAG_COMPRESSED`` flag, since this applies to compressed formats only. This flag is valid for stateful decoders only. * - ``V4L2_FMT_FLAG_DYN_RESOLUTION`` - 0x0008 - Dynamic resolution switching is supported by the device for this compressed bytestream format (aka coded format). It will notify the user via the event ``V4L2_EVENT_SOURCE_CHANGE`` when changes in the video - parameters are detected. This flag can only be used in combination - with the ``V4L2_FMT_FLAG_COMPRESSED`` flag, since this applies to - compressed formats only. It is also only applies to stateful codecs. + parameters are detected. + + This flag can only be used in combination with the + ``V4L2_FMT_FLAG_COMPRESSED`` flag, since this applies to + compressed formats only. This flag is valid for stateful codecs only. + * - ``V4L2_FMT_FLAG_ENC_CAP_FRAME_INTERVAL`` + - 0x0010 + - The hardware encoder supports setting the ``CAPTURE`` coded frame + interval separately from the ``OUTPUT`` raw frame interval. + Setting the ``OUTPUT`` raw frame interval with :ref:`VIDIOC_S_PARM ` + also sets the ``CAPTURE`` coded frame interval to the same value. + If this flag is set, then the ``CAPTURE`` coded frame interval can be + set to a different value afterwards. This is typically used for + offline encoding where the ``OUTPUT`` raw frame interval is used as + a hint for reserving hardware encoder resources and the ``CAPTURE`` coded + frame interval is the actual frame rate embedded in the encoded video + stream. + + This flag can only be used in combination with the + ``V4L2_FMT_FLAG_COMPRESSED`` flag, since this applies to + compressed formats only. This flag is valid for stateful encoders only. Return Value diff --git a/Documentation/userspace-api/media/videodev2.h.rst.exceptions b/Documentation/userspace-api/media/videodev2.h.rst.exceptions index a625fb90e3a98..ca05e4e126b2f 100644 --- a/Documentation/userspace-api/media/videodev2.h.rst.exceptions +++ b/Documentation/userspace-api/media/videodev2.h.rst.exceptions @@ -187,6 +187,7 @@ replace define V4L2_FMT_FLAG_COMPRESSED fmtdesc-flags replace define V4L2_FMT_FLAG_EMULATED fmtdesc-flags replace define V4L2_FMT_FLAG_CONTINUOUS_BYTESTREAM fmtdesc-flags replace define V4L2_FMT_FLAG_DYN_RESOLUTION fmtdesc-flags +replace define V4L2_FMT_FLAG_ENC_CAP_FRAME_INTERVAL fmtdesc-flags # V4L2 timecode types replace define V4L2_TC_TYPE_24FPS timecode-type diff --git a/include/uapi/linux/videodev2.h b/include/uapi/linux/videodev2.h index fec2607a07e31..303805438814f 100644 --- a/include/uapi/linux/videodev2.h +++ b/include/uapi/linux/videodev2.h @@ -794,6 +794,7 @@ struct v4l2_fmtdesc { #define V4L2_FMT_FLAG_EMULATED 0x0002 #define V4L2_FMT_FLAG_CONTINUOUS_BYTESTREAM 0x0004 #define V4L2_FMT_FLAG_DYN_RESOLUTION 0x0008 +#define V4L2_FMT_FLAG_ENC_CAP_FRAME_INTERVAL 0x0010 /* Frame Size and frame rate enumeration */ /* -- GitLab From d09382777e9ad3d6a1b369ff29785250f620b544 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Tue, 26 May 2020 11:00:51 +0200 Subject: [PATCH 0131/1476] media: dev-encoder.rst: add reference to V4L2_FMT_FLAG_ENC_CAP_FRAME_INTERVAL Setting the stateful encoder capture frame interval is only supported if this flag is set. Document this. Signed-off-by: Hans Verkuil Reviewed-by: Michael Tretter Acked-by: Tomasz Figa Signed-off-by: Mauro Carvalho Chehab --- Documentation/userspace-api/media/v4l/dev-encoder.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Documentation/userspace-api/media/v4l/dev-encoder.rst b/Documentation/userspace-api/media/v4l/dev-encoder.rst index aace7b812a9c4..c3a21bfe00522 100644 --- a/Documentation/userspace-api/media/v4l/dev-encoder.rst +++ b/Documentation/userspace-api/media/v4l/dev-encoder.rst @@ -281,7 +281,8 @@ Initialization 5. **Optional** Set the coded frame interval on the ``CAPTURE`` queue via :c:func:`VIDIOC_S_PARM`. This is only necessary if the coded frame interval is different from the raw frame interval, which is typically - the case for off-line encoding. + the case for off-line encoding. Support for this feature is signalled + by the :ref:`V4L2_FMT_FLAG_ENC_CAP_FRAME_INTERVAL ` format flag. * ** Required fields:** -- GitLab From 55e34d5bf9fd5ca8f1febb234276f8b6e9e46f06 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 23 Jun 2020 14:03:58 +0200 Subject: [PATCH 0132/1476] media: dev-encoder.rst: add GFDL-1-1+ to SPDX header The media documents under the uAPI should be GFDL compatible. So, make this one dual-licensed GPL-2.0 or GFDL-1.1+ with no-invariant sections. Signed-off-by: Tomasz Figa Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- .../userspace-api/media/v4l/dev-encoder.rst | 26 ++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/Documentation/userspace-api/media/v4l/dev-encoder.rst b/Documentation/userspace-api/media/v4l/dev-encoder.rst index c3a21bfe00522..fb44f20924de9 100644 --- a/Documentation/userspace-api/media/v4l/dev-encoder.rst +++ b/Documentation/userspace-api/media/v4l/dev-encoder.rst @@ -1,4 +1,28 @@ -.. SPDX-License-Identifier: GPL-2.0 +.. This file is dual-licensed: you can use it either under the terms +.. of the GPL 2.0 or the GFDL 1.1+ license, at your option. Note that this +.. dual licensing only applies to this file, and not this project as a +.. whole. +.. +.. a) This file is free software; you can redistribute it and/or +.. modify it under the terms of the GNU General Public License as +.. published by the Free Software Foundation version 2 of +.. the License. +.. +.. This file 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. +.. +.. Or, alternatively, +.. +.. b) Permission is granted to copy, distribute and/or modify this +.. document under the terms of the GNU Free Documentation License, +.. Version 1.1 or any later version published by the Free Software +.. Foundation, with no Invariant Sections, no Front-Cover Texts +.. and no Back-Cover Texts. A copy of the license is included at +.. Documentation/userspace-api/media/fdl-appendix.rst. +.. +.. TODO: replace it to GPL-2.0 OR GFDL-1.1-or-later WITH no-invariant-sections .. _encoder: -- GitLab From 261463dbc34ff0acafe4d84df04535b48a15afea Mon Sep 17 00:00:00 2001 From: Sean Young Date: Sun, 3 May 2020 11:27:55 +0200 Subject: [PATCH 0133/1476] media: rc: add support for Infrared Toy and IR Droid devices http://dangerousprototypes.com/docs/USB_Infrared_Toy https://www.irdroid.com/irdroid-usb-ir-transceiver/ Reviewed-by: Greg Kroah-Hartman Signed-off-by: Sean Young Signed-off-by: Mauro Carvalho Chehab --- drivers/media/rc/Kconfig | 11 + drivers/media/rc/Makefile | 1 + drivers/media/rc/ir_toy.c | 509 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 521 insertions(+) create mode 100644 drivers/media/rc/ir_toy.c diff --git a/drivers/media/rc/Kconfig b/drivers/media/rc/Kconfig index c18dee6482536..2c0ee2e5b446f 100644 --- a/drivers/media/rc/Kconfig +++ b/drivers/media/rc/Kconfig @@ -530,6 +530,17 @@ config IR_ZX To compile this driver as a module, choose M here: the module will be called zx-irdec. +config IR_TOY + tristate "Infrared Toy and IR Droid" + depends on RC_CORE + depends on USB_ARCH_HAS_HCD + help + Say Y here if you want to use the Infrared Toy or IR Droid, USB + versions. + + To compile this driver as a module, choose M here: the module will be + called ir_toy. + endif #RC_DEVICES endif #RC_CORE diff --git a/drivers/media/rc/Makefile b/drivers/media/rc/Makefile index 48d23433b3c06..5bb2932ab1195 100644 --- a/drivers/media/rc/Makefile +++ b/drivers/media/rc/Makefile @@ -50,3 +50,4 @@ obj-$(CONFIG_IR_MTK) += mtk-cir.o obj-$(CONFIG_IR_ZX) += zx-irdec.o obj-$(CONFIG_IR_TANGO) += tango-ir.o obj-$(CONFIG_RC_XBOX_DVD) += xbox_remote.o +obj-$(CONFIG_IR_TOY) += ir_toy.o diff --git a/drivers/media/rc/ir_toy.c b/drivers/media/rc/ir_toy.c new file mode 100644 index 0000000000000..5c7a7500a925c --- /dev/null +++ b/drivers/media/rc/ir_toy.c @@ -0,0 +1,509 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +/* + * Infrared Toy and IR Droid RC core driver + * + * Copyright (C) 2020 Sean Young + + * This driver is based on the lirc driver which can be found here: + * https://sourceforge.net/p/lirc/git/ci/master/tree/plugins/irtoy.c + * Copyright (C) 2011 Peter Kooiman + */ + +#include +#include +#include +#include +#include +#include +#include + +#include + +static const u8 COMMAND_VERSION[] = { 'v' }; +// End transmit and repeat reset command so we exit sump mode +static const u8 COMMAND_RESET[] = { 0xff, 0xff, 0, 0, 0, 0, 0 }; +static const u8 COMMAND_SMODE_ENTER[] = { 's' }; +static const u8 COMMAND_TXSTART[] = { 0x26, 0x24, 0x25, 0x03 }; + +#define REPLY_XMITCOUNT 't' +#define REPLY_XMITSUCCESS 'C' +#define REPLY_VERSION 'V' +#define REPLY_SAMPLEMODEPROTO 'S' + +#define TIMEOUT 500 + +#define LEN_XMITRES 3 +#define LEN_VERSION 4 +#define LEN_SAMPLEMODEPROTO 3 + +#define MIN_FW_VERSION 20 +#define UNIT_NS 21333 +#define MAX_TIMEOUT_NS (UNIT_NS * U16_MAX) + +#define MAX_PACKET 64 + +enum state { + STATE_IRDATA, + STATE_RESET, + STATE_COMMAND, + STATE_TX, +}; + +struct irtoy { + struct device *dev; + struct usb_device *usbdev; + + struct rc_dev *rc; + struct urb *urb_in, *urb_out; + + u8 *in; + u8 *out; + struct completion command_done; + + bool pulse; + enum state state; + + void *tx_buf; + uint tx_len; + + uint emitted; + uint hw_version; + uint sw_version; + uint proto_version; + + char phys[64]; +}; + +static void irtoy_response(struct irtoy *irtoy, u32 len) +{ + switch (irtoy->state) { + case STATE_COMMAND: + if (len == LEN_VERSION && irtoy->in[0] == REPLY_VERSION) { + uint version; + + irtoy->in[LEN_VERSION] = 0; + + if (kstrtouint(irtoy->in + 1, 10, &version)) { + dev_err(irtoy->dev, "invalid version %*phN. Please make sure you are using firmware v20 or higher", + LEN_VERSION, irtoy->in); + break; + } + + dev_dbg(irtoy->dev, "version %s\n", irtoy->in); + + irtoy->hw_version = version / 100; + irtoy->sw_version = version % 100; + + irtoy->state = STATE_IRDATA; + complete(&irtoy->command_done); + } else if (len == LEN_SAMPLEMODEPROTO && + irtoy->in[0] == REPLY_SAMPLEMODEPROTO) { + uint version; + + irtoy->in[LEN_SAMPLEMODEPROTO] = 0; + + if (kstrtouint(irtoy->in + 1, 10, &version)) { + dev_err(irtoy->dev, "invalid sample mode response %*phN", + LEN_SAMPLEMODEPROTO, irtoy->in); + return; + } + + dev_dbg(irtoy->dev, "protocol %s\n", irtoy->in); + + irtoy->proto_version = version; + + irtoy->state = STATE_IRDATA; + complete(&irtoy->command_done); + } else { + dev_err(irtoy->dev, "unexpected response to command: %*phN\n", + len, irtoy->in); + } + break; + case STATE_IRDATA: { + struct ir_raw_event rawir = { .pulse = irtoy->pulse }; + __be16 *in = (__be16 *)irtoy->in; + int i; + + for (i = 0; i < len / sizeof(__be16); i++) { + u16 v = be16_to_cpu(in[i]); + + if (v == 0xffff) { + rawir.pulse = false; + } else { + rawir.duration = v * UNIT_NS; + ir_raw_event_store_with_timeout(irtoy->rc, + &rawir); + } + + rawir.pulse = !rawir.pulse; + } + + irtoy->pulse = rawir.pulse; + + ir_raw_event_handle(irtoy->rc); + break; + } + case STATE_TX: + if (irtoy->tx_len == 0) { + if (len == LEN_XMITRES && + irtoy->in[0] == REPLY_XMITCOUNT) { + u16 emitted = get_unaligned_be16(irtoy->in + 1); + + dev_dbg(irtoy->dev, "emitted:%u\n", emitted); + + irtoy->emitted = emitted; + } else if (len == 1 && + irtoy->in[0] == REPLY_XMITSUCCESS) { + irtoy->state = STATE_IRDATA; + complete(&irtoy->command_done); + } + } else { + // send next part of tx buffer + uint space = irtoy->in[0]; + uint buf_len; + int err; + + if (len != 1 || space > MAX_PACKET || space == 0) { + dev_err(irtoy->dev, "packet length expected: %*phN\n", + len, irtoy->in); + irtoy->state = STATE_IRDATA; + complete(&irtoy->command_done); + break; + } + + buf_len = min(space, irtoy->tx_len); + + dev_dbg(irtoy->dev, "remaining:%u sending:%u\n", + irtoy->tx_len, buf_len); + + memcpy(irtoy->out, irtoy->tx_buf, buf_len); + irtoy->urb_out->transfer_buffer_length = buf_len; + err = usb_submit_urb(irtoy->urb_out, GFP_ATOMIC); + if (err != 0) { + dev_err(irtoy->dev, "fail to submit tx buf urb: %d\n", + err); + irtoy->state = STATE_IRDATA; + complete(&irtoy->command_done); + break; + } + + irtoy->tx_buf += buf_len; + irtoy->tx_len -= buf_len; + } + break; + case STATE_RESET: + dev_err(irtoy->dev, "unexpected response to reset: %*phN\n", + len, irtoy->in); + } +} + +static void irtoy_out_callback(struct urb *urb) +{ + struct irtoy *irtoy = urb->context; + + if (urb->status == 0) { + if (irtoy->state == STATE_RESET) + complete(&irtoy->command_done); + } else { + dev_warn(irtoy->dev, "out urb status: %d\n", urb->status); + } +} + +static void irtoy_in_callback(struct urb *urb) +{ + struct irtoy *irtoy = urb->context; + int ret; + + if (urb->status == 0) + irtoy_response(irtoy, urb->actual_length); + else + dev_dbg(irtoy->dev, "in urb status: %d\n", urb->status); + + ret = usb_submit_urb(urb, GFP_ATOMIC); + if (ret && ret != -ENODEV) + dev_warn(irtoy->dev, "failed to resubmit urb: %d\n", ret); +} + +static int irtoy_command(struct irtoy *irtoy, const u8 *cmd, int cmd_len, + enum state state) +{ + int err; + + init_completion(&irtoy->command_done); + + irtoy->state = state; + + memcpy(irtoy->out, cmd, cmd_len); + irtoy->urb_out->transfer_buffer_length = cmd_len; + + err = usb_submit_urb(irtoy->urb_out, GFP_KERNEL); + if (err != 0) + return err; + + if (!wait_for_completion_timeout(&irtoy->command_done, + msecs_to_jiffies(TIMEOUT))) { + usb_kill_urb(irtoy->urb_out); + return -ETIMEDOUT; + } + + return 0; +} + +static int irtoy_setup(struct irtoy *irtoy) +{ + int err; + + err = irtoy_command(irtoy, COMMAND_RESET, sizeof(COMMAND_RESET), + STATE_RESET); + if (err != 0) { + dev_err(irtoy->dev, "could not write reset command: %d\n", + err); + return err; + } + + usleep_range(50, 50); + + // get version + err = irtoy_command(irtoy, COMMAND_VERSION, sizeof(COMMAND_VERSION), + STATE_COMMAND); + if (err) { + dev_err(irtoy->dev, "could not write version command: %d\n", + err); + return err; + } + + // enter sample mode + err = irtoy_command(irtoy, COMMAND_SMODE_ENTER, + sizeof(COMMAND_SMODE_ENTER), STATE_COMMAND); + if (err) + dev_err(irtoy->dev, "could not write sample command: %d\n", + err); + + return err; +} + +/* + * When sending IR, it is imperative that we send the IR data as quickly + * as possible to the device, so it does not run out of IR data and + * introduce gaps. Allocate the buffer here, and then feed the data from + * the urb callback handler. + */ +static int irtoy_tx(struct rc_dev *rc, uint *txbuf, uint count) +{ + struct irtoy *irtoy = rc->priv; + unsigned int i, size; + __be16 *buf; + int err; + + size = sizeof(u16) * (count + 1); + buf = kmalloc(size, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + for (i = 0; i < count; i++) { + u16 v = DIV_ROUND_CLOSEST(US_TO_NS(txbuf[i]), UNIT_NS); + + if (!v) + v = 1; + buf[i] = cpu_to_be16(v); + } + + buf[count] = cpu_to_be16(0xffff); + + irtoy->tx_buf = buf; + irtoy->tx_len = size; + irtoy->emitted = 0; + + err = irtoy_command(irtoy, COMMAND_TXSTART, sizeof(COMMAND_TXSTART), + STATE_TX); + kfree(buf); + + if (err) { + dev_err(irtoy->dev, "failed to send tx start command: %d\n", + err); + // not sure what state the device is in, reset it + irtoy_setup(irtoy); + return err; + } + + if (size != irtoy->emitted) { + dev_err(irtoy->dev, "expected %u emitted, got %u\n", size, + irtoy->emitted); + // not sure what state the device is in, reset it + irtoy_setup(irtoy); + return -EINVAL; + } + + return count; +} + +static int irtoy_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + struct usb_host_interface *idesc = intf->cur_altsetting; + struct usb_device *usbdev = interface_to_usbdev(intf); + struct usb_endpoint_descriptor *ep_in = NULL; + struct usb_endpoint_descriptor *ep_out = NULL; + struct usb_endpoint_descriptor *ep = NULL; + struct irtoy *irtoy; + struct rc_dev *rc; + struct urb *urb; + int i, pipe, err = -ENOMEM; + + for (i = 0; i < idesc->desc.bNumEndpoints; i++) { + ep = &idesc->endpoint[i].desc; + + if (!ep_in && usb_endpoint_is_bulk_in(ep) && + usb_endpoint_maxp(ep) == MAX_PACKET) + ep_in = ep; + + if (!ep_out && usb_endpoint_is_bulk_out(ep) && + usb_endpoint_maxp(ep) == MAX_PACKET) + ep_out = ep; + } + + if (!ep_in || !ep_out) { + dev_err(&intf->dev, "required endpoints not found\n"); + return -ENODEV; + } + + irtoy = kzalloc(sizeof(*irtoy), GFP_KERNEL); + if (!irtoy) + return -ENOMEM; + + irtoy->in = kmalloc(MAX_PACKET, GFP_KERNEL); + if (!irtoy->in) + goto free_irtoy; + + irtoy->out = kmalloc(MAX_PACKET, GFP_KERNEL); + if (!irtoy->out) + goto free_irtoy; + + rc = rc_allocate_device(RC_DRIVER_IR_RAW); + if (!rc) + goto free_irtoy; + + urb = usb_alloc_urb(0, GFP_KERNEL); + if (!urb) + goto free_rcdev; + + pipe = usb_rcvbulkpipe(usbdev, ep_in->bEndpointAddress); + usb_fill_bulk_urb(urb, usbdev, pipe, irtoy->in, MAX_PACKET, + irtoy_in_callback, irtoy); + irtoy->urb_in = urb; + + urb = usb_alloc_urb(0, GFP_KERNEL); + if (!urb) + goto free_rcdev; + + pipe = usb_sndbulkpipe(usbdev, ep_out->bEndpointAddress); + usb_fill_bulk_urb(urb, usbdev, pipe, irtoy->out, MAX_PACKET, + irtoy_out_callback, irtoy); + + irtoy->dev = &intf->dev; + irtoy->usbdev = usbdev; + irtoy->rc = rc; + irtoy->urb_out = urb; + irtoy->pulse = true; + + err = usb_submit_urb(irtoy->urb_in, GFP_KERNEL); + if (err != 0) { + dev_err(irtoy->dev, "fail to submit in urb: %d\n", err); + return err; + } + + err = irtoy_setup(irtoy); + if (err) + goto free_rcdev; + + dev_info(irtoy->dev, "version: hardware %u, firmware %u, protocol %u", + irtoy->hw_version, irtoy->sw_version, irtoy->proto_version); + + if (irtoy->sw_version < MIN_FW_VERSION) { + dev_err(irtoy->dev, "need firmware V%02u or higher", + MIN_FW_VERSION); + err = -ENODEV; + goto free_rcdev; + } + + usb_make_path(usbdev, irtoy->phys, sizeof(irtoy->phys)); + + rc->device_name = "Infrared Toy"; + rc->driver_name = KBUILD_MODNAME; + rc->input_phys = irtoy->phys; + usb_to_input_id(usbdev, &rc->input_id); + rc->dev.parent = &intf->dev; + rc->priv = irtoy; + rc->tx_ir = irtoy_tx; + rc->allowed_protocols = RC_PROTO_BIT_ALL_IR_DECODER; + rc->map_name = RC_MAP_RC6_MCE; + rc->rx_resolution = UNIT_NS; + rc->timeout = IR_DEFAULT_TIMEOUT; + + /* + * end of transmission is detected by absence of a usb packet + * with more pulse/spaces. However, each usb packet sent can + * contain 32 pulse/spaces, which can be quite lengthy, so there + * can be a delay between usb packets. For example with nec there is a + * 17ms gap between packets. + * + * So, make timeout a largish minimum which works with most protocols. + */ + rc->min_timeout = MS_TO_NS(40); + rc->max_timeout = MAX_TIMEOUT_NS; + + err = rc_register_device(rc); + if (err) + goto free_rcdev; + + usb_set_intfdata(intf, irtoy); + + return 0; + +free_rcdev: + usb_kill_urb(irtoy->urb_out); + usb_free_urb(irtoy->urb_out); + usb_kill_urb(irtoy->urb_in); + usb_free_urb(irtoy->urb_in); + rc_free_device(rc); +free_irtoy: + kfree(irtoy->in); + kfree(irtoy->out); + kfree(irtoy); + return err; +} + +static void irtoy_disconnect(struct usb_interface *intf) +{ + struct irtoy *ir = usb_get_intfdata(intf); + + rc_unregister_device(ir->rc); + usb_set_intfdata(intf, NULL); + usb_kill_urb(ir->urb_out); + usb_free_urb(ir->urb_out); + usb_kill_urb(ir->urb_in); + usb_free_urb(ir->urb_in); + kfree(ir->in); + kfree(ir->out); + kfree(ir); +} + +static const struct usb_device_id irtoy_table[] = { + { USB_DEVICE_INTERFACE_CLASS(0x04d8, 0xfd08, USB_CLASS_CDC_DATA) }, + { } +}; + +static struct usb_driver irtoy_driver = { + .name = KBUILD_MODNAME, + .probe = irtoy_probe, + .disconnect = irtoy_disconnect, + .id_table = irtoy_table, +}; + +module_usb_driver(irtoy_driver); + +MODULE_AUTHOR("Sean Young "); +MODULE_DESCRIPTION("Infrared Toy and IR Droid driver"); +MODULE_LICENSE("GPL"); +MODULE_DEVICE_TABLE(usb, irtoy_table); -- GitLab From 6eeb775a8606a828d235221d84a15eefb05c3978 Mon Sep 17 00:00:00 2001 From: Sean Young Date: Wed, 27 May 2020 11:22:16 +0200 Subject: [PATCH 0134/1476] media: USB: cdc-acm: blacklist IR Droid / IR Toy device The IR Droid / IR Toy device used custom protocol over serial line, so it is presenting itself as CDC ACM device. Now that we have proper in-kernel driver for it we need to black-list the device in cdc-acm driver. http://dangerousprototypes.com/docs/USB_Infrared_Toy https://www.irdroid.com/irdroid-usb-ir-transceiver/ Acked-by: Greg Kroah-Hartman Signed-off-by: Sean Young Signed-off-by: Mauro Carvalho Chehab --- drivers/usb/class/cdc-acm.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c index f67088bb8218b..450824a20ac9a 100644 --- a/drivers/usb/class/cdc-acm.c +++ b/drivers/usb/class/cdc-acm.c @@ -1890,6 +1890,12 @@ static const struct usb_device_id acm_ids[] = { }, #endif +#if IS_ENABLED(CONFIG_IR_TOY) + { USB_DEVICE(0x04d8, 0xfd08), + .driver_info = IGNORE_DEVICE, + }, +#endif + /*Samsung phone in firmware update mode */ { USB_DEVICE(0x04e8, 0x685d), .driver_info = IGNORE_DEVICE, -- GitLab From e508a9f66332ffae585f68cf005a8310a9a9bc14 Mon Sep 17 00:00:00 2001 From: Sean Young Date: Wed, 27 May 2020 11:27:48 +0200 Subject: [PATCH 0135/1476] media: MAINTAINERS: rc-core and lirc maintainership This has been maintained for some time, update MAINTAINERS to reflect. Signed-off-by: Sean Young Signed-off-by: Mauro Carvalho Chehab --- MAINTAINERS | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index 68f21d46614c4..634d2c3d621aa 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -14357,6 +14357,19 @@ L: linux-wireless@vger.kernel.org S: Orphan F: drivers/net/wireless/ray* +RC-CORE / LIRC FRAMEWORK +M: Sean Young +L: linux-media@vger.kernel.org +S: Maintained +W: http://linuxtv.org +T: git git://linuxtv.org/media_tree.git +F: Documentation/driver-api/media/rc-core.rst +F: Documentation/userspace-api/media/rc/ +F: drivers/media/rc/ +F: include/media/rc-map.h +F: include/media/rc-core.h +F: include/uapi/linux/lirc.h + RCMM REMOTE CONTROLS DECODER M: Patrick Lerda S: Maintained -- GitLab From 1a55caf010c46d4f2073f9e92e97ef65358c16bf Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Thu, 7 May 2020 23:33:14 +0200 Subject: [PATCH 0136/1476] media: cxusb-analog: fix V4L2 dependency CONFIG_DVB_USB_CXUSB_ANALOG is a 'bool' symbol with a dependency on the tristate CONFIG_VIDEO_V4L2, which means it can be enabled as =y even when its dependency is =m. This leads to a link failure: drivers/media/usb/dvb-usb/cxusb-analog.o: In function `cxusb_medion_analog_init': cxusb-analog.c:(.text+0x92): undefined reference to `v4l2_subdev_call_wrappers' drivers/media/usb/dvb-usb/cxusb-analog.o: In function `cxusb_medion_register_analog': cxusb-analog.c:(.text+0x466): undefined reference to `v4l2_device_register' cxusb-analog.c:(.text+0x4c3): undefined reference to `v4l2_i2c_new_subdev' cxusb-analog.c:(.text+0x4fb): undefined reference to `v4l2_subdev_call_wrappers' ... Change the dependency only disallow the analog portion of the driver in that configuration. Fixes: e478d4054054 ("media: cxusb: add analog mode support for Medion MD95700") Signed-off-by: Arnd Bergmann Signed-off-by: Sean Young Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/dvb-usb/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/media/usb/dvb-usb/Kconfig b/drivers/media/usb/dvb-usb/Kconfig index 15d29c91662f3..25ba03edcb5c6 100644 --- a/drivers/media/usb/dvb-usb/Kconfig +++ b/drivers/media/usb/dvb-usb/Kconfig @@ -151,6 +151,7 @@ config DVB_USB_CXUSB config DVB_USB_CXUSB_ANALOG bool "Analog support for the Conexant USB2.0 hybrid reference design" depends on DVB_USB_CXUSB && VIDEO_V4L2 + depends on VIDEO_V4L2=y || VIDEO_V4L2=DVB_USB_CXUSB select VIDEO_CX25840 select VIDEOBUF2_VMALLOC help -- GitLab From bc76369b89bbbbd01b2a3852287ea8092b59d012 Mon Sep 17 00:00:00 2001 From: Jan Pieter van Woerkom Date: Wed, 3 Jun 2020 05:58:40 +0200 Subject: [PATCH 0137/1476] media: dvbsky: MyGica T230* receivers also do DVB-C and DVB-T MyGica T230* receivers also do DVB-C and DVB-T: change their names. Signed-off-by: Jan Pieter van Woerkom Signed-off-by: Sean Young Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/dvb-usb-v2/dvbsky.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/media/usb/dvb-usb-v2/dvbsky.c b/drivers/media/usb/dvb-usb-v2/dvbsky.c index 356fd8e66834e..0d9657f7f29d7 100644 --- a/drivers/media/usb/dvb-usb-v2/dvbsky.c +++ b/drivers/media/usb/dvb-usb-v2/dvbsky.c @@ -778,16 +778,16 @@ static const struct usb_device_id dvbsky_id_table[] = { &dvbsky_s960_props, "Terratec Cinergy S2 Rev.4", RC_MAP_DVBSKY) }, { DVB_USB_DEVICE(USB_VID_CONEXANT, USB_PID_MYGICA_T230, - &mygica_t230c_props, "MyGica Mini DVB-T2 USB Stick T230", + &mygica_t230c_props, "MyGica Mini DVB-(T/T2/C) USB Stick T230", RC_MAP_TOTAL_MEDIA_IN_HAND_02) }, { DVB_USB_DEVICE(USB_VID_CONEXANT, USB_PID_MYGICA_T230C, - &mygica_t230c_props, "MyGica Mini DVB-T2 USB Stick T230C", + &mygica_t230c_props, "MyGica Mini DVB-(T/T2/C) USB Stick T230C", RC_MAP_TOTAL_MEDIA_IN_HAND_02) }, { DVB_USB_DEVICE(USB_VID_CONEXANT, USB_PID_MYGICA_T230C_LITE, - &mygica_t230c_props, "MyGica Mini DVB-T2 USB Stick T230C Lite", + &mygica_t230c_props, "MyGica Mini DVB-(T/T2/C) USB Stick T230C Lite", NULL) }, { DVB_USB_DEVICE(USB_VID_CONEXANT, USB_PID_MYGICA_T230C2, - &mygica_t230c_props, "MyGica Mini DVB-T2 USB Stick T230C v2", + &mygica_t230c_props, "MyGica Mini DVB-(T/T2/C) USB Stick T230C v2", RC_MAP_TOTAL_MEDIA_IN_HAND_02) }, { } }; -- GitLab From 6499a0db9b0f1e903d52f8244eacc1d4be00eea2 Mon Sep 17 00:00:00 2001 From: Jia-Ju Bai Date: Sat, 30 May 2020 16:42:08 +0200 Subject: [PATCH 0138/1476] media: pci: ttpci: av7110: fix possible buffer overflow caused by bad DMA value in debiirq() The value av7110->debi_virt is stored in DMA memory, and it is assigned to data, and thus data[0] can be modified at any time by malicious hardware. In this case, "if (data[0] < 2)" can be passed, but then data[0] can be changed into a large number, which may cause buffer overflow when the code "av7110->ci_slot[data[0]]" is used. To fix this possible bug, data[0] is assigned to a local variable, which replaces the use of data[0]. Signed-off-by: Jia-Ju Bai Signed-off-by: Sean Young Signed-off-by: Mauro Carvalho Chehab --- drivers/media/pci/ttpci/av7110.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/media/pci/ttpci/av7110.c b/drivers/media/pci/ttpci/av7110.c index d0cdee1c6eb0b..bf36b1e22b635 100644 --- a/drivers/media/pci/ttpci/av7110.c +++ b/drivers/media/pci/ttpci/av7110.c @@ -406,14 +406,15 @@ static void debiirq(unsigned long cookie) case DATA_CI_GET: { u8 *data = av7110->debi_virt; + u8 data_0 = data[0]; - if ((data[0] < 2) && data[2] == 0xff) { + if (data_0 < 2 && data[2] == 0xff) { int flags = 0; if (data[5] > 0) flags |= CA_CI_MODULE_PRESENT; if (data[5] > 5) flags |= CA_CI_MODULE_READY; - av7110->ci_slot[data[0]].flags = flags; + av7110->ci_slot[data_0].flags = flags; } else ci_get_data(&av7110->ci_rbuffer, av7110->debi_virt, -- GitLab From fc0456458df8b3421dba2a5508cd817fbc20ea71 Mon Sep 17 00:00:00 2001 From: Chuhong Yuan Date: Fri, 5 Jun 2020 18:17:28 +0200 Subject: [PATCH 0139/1476] media: budget-core: Improve exception handling in budget_register() budget_register() has no error handling after its failure. Add the missed undo functions for error handling to fix it. Signed-off-by: Chuhong Yuan Signed-off-by: Sean Young Signed-off-by: Mauro Carvalho Chehab --- drivers/media/pci/ttpci/budget-core.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/drivers/media/pci/ttpci/budget-core.c b/drivers/media/pci/ttpci/budget-core.c index fadbdeeb44955..293867b9e7961 100644 --- a/drivers/media/pci/ttpci/budget-core.c +++ b/drivers/media/pci/ttpci/budget-core.c @@ -369,20 +369,25 @@ static int budget_register(struct budget *budget) ret = dvbdemux->dmx.add_frontend(&dvbdemux->dmx, &budget->hw_frontend); if (ret < 0) - return ret; + goto err_release_dmx; budget->mem_frontend.source = DMX_MEMORY_FE; ret = dvbdemux->dmx.add_frontend(&dvbdemux->dmx, &budget->mem_frontend); if (ret < 0) - return ret; + goto err_release_dmx; ret = dvbdemux->dmx.connect_frontend(&dvbdemux->dmx, &budget->hw_frontend); if (ret < 0) - return ret; + goto err_release_dmx; dvb_net_init(&budget->dvb_adapter, &budget->dvb_net, &dvbdemux->dmx); return 0; + +err_release_dmx: + dvb_dmxdev_release(&budget->dmxdev); + dvb_dmx_release(&budget->demux); + return ret; } static void budget_unregister(struct budget *budget) -- GitLab From b16c928c13d103c7c338ae9ff076345c82d819b3 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Sat, 20 Jun 2020 11:33:01 +0200 Subject: [PATCH 0140/1476] media: cec-ioc-adap-g-caps.rst: document available_log_addrs The CEC_ADAP_G_CAPS documentation of the cec_caps struct was missing the available_log_addrs field. Add this. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- Documentation/userspace-api/media/cec/cec-ioc-adap-g-caps.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Documentation/userspace-api/media/cec/cec-ioc-adap-g-caps.rst b/Documentation/userspace-api/media/cec/cec-ioc-adap-g-caps.rst index 94e46a11d68d9..436a882dfa317 100644 --- a/Documentation/userspace-api/media/cec/cec-ioc-adap-g-caps.rst +++ b/Documentation/userspace-api/media/cec/cec-ioc-adap-g-caps.rst @@ -57,6 +57,9 @@ returns the information to the application. The ioctl never fails. - ``name[32]`` - The name of this CEC adapter. The combination ``driver`` and ``name`` must be unique. + * - __u32 + - ``available_log_addrs`` + - The maximum number of logical addresses that can be configured. * - __u32 - ``capabilities`` - The capabilities of the CEC adapter, see -- GitLab From 8dd65ed67e1679830dc8f94169d25ec9452da99d Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Sat, 20 Jun 2020 12:16:40 +0200 Subject: [PATCH 0141/1476] media: cec: remove unused waitq and phys_addrs fields The cec_adapter struct contained a waitq field that isn't used anywhere, so drop this. It also contained a phys_addrs array to store any reported physical addresses. However, this was never actually used, so this field is removed as well. The original idea was to let the core keep track of this information, but nothing was ever done with this. Should this be needed in the future then it is easy enough to resurrect this. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/cec/core/cec-adap.c | 4 ---- drivers/media/cec/core/cec-core.c | 1 - include/media/cec.h | 2 -- 3 files changed, 7 deletions(-) diff --git a/drivers/media/cec/core/cec-adap.c b/drivers/media/cec/core/cec-adap.c index 6a04d19a96b2e..4efe8014445ea 100644 --- a/drivers/media/cec/core/cec-adap.c +++ b/drivers/media/cec/core/cec-adap.c @@ -1306,7 +1306,6 @@ static int cec_config_log_addr(struct cec_adapter *adap, las->log_addr[idx] = log_addr; las->log_addr_mask |= 1 << log_addr; - adap->phys_addrs[log_addr] = adap->phys_addr; return 1; } @@ -1324,7 +1323,6 @@ static void cec_adap_unconfigure(struct cec_adapter *adap) adap->log_addrs.log_addr_mask = 0; adap->is_configuring = false; adap->is_configured = false; - memset(adap->phys_addrs, 0xff, sizeof(adap->phys_addrs)); cec_flush(adap); wake_up_interruptible(&adap->kthread_waitq); cec_post_state_event(adap); @@ -1974,8 +1972,6 @@ static int cec_receive_notify(struct cec_adapter *adap, struct cec_msg *msg, case CEC_MSG_REPORT_PHYSICAL_ADDR: { u16 pa = (msg->msg[2] << 8) | msg->msg[3]; - if (!from_unregistered) - adap->phys_addrs[init_laddr] = pa; dprintk(1, "reported physical address %x.%x.%x.%x for logical address %d\n", cec_phys_addr_exp(pa), init_laddr); break; diff --git a/drivers/media/cec/core/cec-core.c b/drivers/media/cec/core/cec-core.c index 0c52e1bb39105..c599cd94dd621 100644 --- a/drivers/media/cec/core/cec-core.c +++ b/drivers/media/cec/core/cec-core.c @@ -265,7 +265,6 @@ struct cec_adapter *cec_allocate_adapter(const struct cec_adap_ops *ops, adap->sequence = 0; adap->ops = ops; adap->priv = priv; - memset(adap->phys_addrs, 0xff, sizeof(adap->phys_addrs)); mutex_init(&adap->lock); INIT_LIST_HEAD(&adap->transmit_queue); INIT_LIST_HEAD(&adap->wait_queue); diff --git a/include/media/cec.h b/include/media/cec.h index 972bc8cd4384e..1de44a4fc3900 100644 --- a/include/media/cec.h +++ b/include/media/cec.h @@ -162,7 +162,6 @@ struct cec_adapter { struct task_struct *kthread; wait_queue_head_t kthread_waitq; - wait_queue_head_t waitq; const struct cec_adap_ops *ops; void *priv; @@ -197,7 +196,6 @@ struct cec_adapter { struct dentry *status_file; struct dentry *error_inj_file; - u16 phys_addrs[15]; u32 sequence; char input_phys[32]; -- GitLab From e233f81cfc906e415e5784526d9a6ec7d7cf3c5c Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Sat, 20 Jun 2020 12:16:41 +0200 Subject: [PATCH 0142/1476] media: media/cec.h: document cec_adapter fields Document this core CEC structure. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- include/media/cec.h | 49 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/include/media/cec.h b/include/media/cec.h index 1de44a4fc3900..32f7c695d7b5e 100644 --- a/include/media/cec.h +++ b/include/media/cec.h @@ -144,6 +144,55 @@ struct cec_adap_ops { */ #define CEC_MAX_MSG_TX_QUEUE_SZ (18 * 1) +/** + * struct cec_adapter - cec adapter structure + * @owner: module owner + * @name: name of the CEC adapter + * @devnode: device node for the /dev/cecX device + * @lock: mutex controlling access to this structure + * @rc: remote control device + * @transmit_queue: queue of pending transmits + * @transmit_queue_sz: number of pending transmits + * @wait_queue: queue of transmits waiting for a reply + * @transmitting: CEC messages currently being transmitted + * @transmit_in_progress: true if a transmit is in progress + * @kthread_config: kthread used to configure a CEC adapter + * @config_completion: used to signal completion of the config kthread + * @kthread: main CEC processing thread + * @kthread_waitq: main CEC processing wait_queue + * @ops: cec adapter ops + * @priv: cec driver's private data + * @capabilities: cec adapter capabilities + * @available_log_addrs: maximum number of available logical addresses + * @phys_addr: the current physical address + * @needs_hpd: if true, then the HDMI HotPlug Detect pin must be high + * in order to transmit or receive CEC messages. This is usually a HW + * limitation. + * @is_configuring: the CEC adapter is configuring (i.e. claiming LAs) + * @is_configured: the CEC adapter is configured (i.e. has claimed LAs) + * @cec_pin_is_high: if true then the CEC pin is high. Only used with the + * CEC pin framework. + * @last_initiator: the initiator of the last transmitted message. + * @monitor_all_cnt: number of filehandles monitoring all msgs + * @monitor_pin_cnt: number of filehandles monitoring pin changes + * @follower_cnt: number of filehandles in follower mode + * @cec_follower: filehandle of the exclusive follower + * @cec_initiator: filehandle of the exclusive initiator + * @passthrough: if true, then the exclusive follower is in + * passthrough mode. + * @log_addrs: current logical addresses + * @conn_info: current connector info + * @tx_timeouts: number of transmit timeouts + * @notifier: CEC notifier + * @pin: CEC pin status struct + * @cec_dir: debugfs cec directory + * @status_file: debugfs cec status file + * @error_inj_file: debugfs cec error injection file + * @sequence: transmit sequence counter + * @input_phys: remote control input_phys name + * + * This structure represents a cec adapter. + */ struct cec_adapter { struct module *owner; char name[32]; -- GitLab From 52db11f38333f90dcdc9a12a09f9ec990516025d Mon Sep 17 00:00:00 2001 From: Dinghao Liu Date: Mon, 25 May 2020 10:19:58 +0200 Subject: [PATCH 0143/1476] media: smiapp: Fix runtime PM imbalance on error When v4l2_async_register_subdev_sensor_common() returns an error code, a pairing runtime PM usage counter decrement is needed to keep the counter balanced. Signed-off-by: Dinghao Liu Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/smiapp/smiapp-core.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/media/i2c/smiapp/smiapp-core.c b/drivers/media/i2c/smiapp/smiapp-core.c index 5e4f6a2ef78e5..0a8d27481bd12 100644 --- a/drivers/media/i2c/smiapp/smiapp-core.c +++ b/drivers/media/i2c/smiapp/smiapp-core.c @@ -3103,6 +3103,7 @@ static int smiapp_probe(struct i2c_client *client) return 0; out_disable_runtime_pm: + pm_runtime_put_noidle(&client->dev); pm_runtime_disable(&client->dev); out_media_entity_cleanup: -- GitLab From c1d7a2c8259c0a65189542d3e5d7a80f9ea012b6 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Wed, 16 Oct 2019 13:19:30 +0200 Subject: [PATCH 0144/1476] media: smiapp: Use pm_runtime_get_if_active Use the convenience function pm_runtime_get_if_active() instead of a number of calls to runtime PM to figure out if the device was already powered up. Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/smiapp/smiapp-core.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/media/i2c/smiapp/smiapp-core.c b/drivers/media/i2c/smiapp/smiapp-core.c index 0a8d27481bd12..8a9c7de0c056d 100644 --- a/drivers/media/i2c/smiapp/smiapp-core.c +++ b/drivers/media/i2c/smiapp/smiapp-core.c @@ -508,9 +508,7 @@ static int smiapp_set_ctrl(struct v4l2_ctrl *ctrl) break; } - pm_runtime_get_noresume(&client->dev); - pm_status = pm_runtime_get_if_in_use(&client->dev); - pm_runtime_put_noidle(&client->dev); + pm_status = pm_runtime_get_if_active(&client->dev, true); if (!pm_status) return 0; -- GitLab From 1e8d3bbcdda30973a10d34032401647fc4111747 Mon Sep 17 00:00:00 2001 From: Bingbu Cao Date: Thu, 4 Jun 2020 03:25:19 +0200 Subject: [PATCH 0145/1476] media: ov2740: make ov2740 driver only work with ACPI As the ACPI id of ov2740 camera sensor was registered as "INT3474", current ov2740 driver is supposed to be working with ACPI only. Signed-off-by: Bingbu Cao Suggested-by: Andy Shevchenko Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/Kconfig | 1 + drivers/media/i2c/ov2740.c | 4 +--- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig index da11036ad804d..6804ad57ee719 100644 --- a/drivers/media/i2c/Kconfig +++ b/drivers/media/i2c/Kconfig @@ -860,6 +860,7 @@ config VIDEO_OV2685 config VIDEO_OV2740 tristate "OmniVision OV2740 sensor support" depends on VIDEO_V4L2 && I2C + depends on ACPI || COMPILE_TEST select MEDIA_CONTROLLER select VIDEO_V4L2_SUBDEV_API select V4L2_FWNODE diff --git a/drivers/media/i2c/ov2740.c b/drivers/media/i2c/ov2740.c index 2dd2609db873a..33025c6d23ac0 100644 --- a/drivers/media/i2c/ov2740.c +++ b/drivers/media/i2c/ov2740.c @@ -988,20 +988,18 @@ static const struct dev_pm_ops ov2740_pm_ops = { SET_SYSTEM_SLEEP_PM_OPS(ov2740_suspend, ov2740_resume) }; -#ifdef CONFIG_ACPI static const struct acpi_device_id ov2740_acpi_ids[] = { {"INT3474"}, {} }; MODULE_DEVICE_TABLE(acpi, ov2740_acpi_ids); -#endif static struct i2c_driver ov2740_i2c_driver = { .driver = { .name = "ov2740", .pm = &ov2740_pm_ops, - .acpi_match_table = ACPI_PTR(ov2740_acpi_ids), + .acpi_match_table = ov2740_acpi_ids, }, .probe_new = ov2740_probe, .remove = ov2740_remove, -- GitLab From 4603a5b4a87ccd6fb90cbfa10195291cfcf6ba34 Mon Sep 17 00:00:00 2001 From: Chuhong Yuan Date: Wed, 3 Jun 2020 18:40:48 +0200 Subject: [PATCH 0146/1476] media: marvell-ccic: Add missed v4l2_async_notifier_cleanup() mccic_register() forgets to cleanup the notifier in its error handler. mccic_shutdown() also misses calling v4l2_async_notifier_cleanup(). Add the missed calls to fix them. Fixes: 3eefe36cc00c ("media: marvell-ccic: use async notifier to get the sensor") Signed-off-by: Chuhong Yuan Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/marvell-ccic/mcam-core.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/media/platform/marvell-ccic/mcam-core.c b/drivers/media/platform/marvell-ccic/mcam-core.c index 09775b6624c6b..326e79b8531c5 100644 --- a/drivers/media/platform/marvell-ccic/mcam-core.c +++ b/drivers/media/platform/marvell-ccic/mcam-core.c @@ -1940,6 +1940,7 @@ int mccic_register(struct mcam_camera *cam) out: v4l2_async_notifier_unregister(&cam->notifier); v4l2_device_unregister(&cam->v4l2_dev); + v4l2_async_notifier_cleanup(&cam->notifier); return ret; } EXPORT_SYMBOL_GPL(mccic_register); @@ -1961,6 +1962,7 @@ void mccic_shutdown(struct mcam_camera *cam) v4l2_ctrl_handler_free(&cam->ctrl_handler); v4l2_async_notifier_unregister(&cam->notifier); v4l2_device_unregister(&cam->v4l2_dev); + v4l2_async_notifier_cleanup(&cam->notifier); } EXPORT_SYMBOL_GPL(mccic_shutdown); -- GitLab From dc7690a73017e1236202022e26a6aa133f239c8c Mon Sep 17 00:00:00 2001 From: Chuhong Yuan Date: Wed, 3 Jun 2020 18:41:22 +0200 Subject: [PATCH 0147/1476] media: omap3isp: Add missed v4l2_ctrl_handler_free() for preview_init_entities() preview_init_entities() does not call v4l2_ctrl_handler_free() when it fails. Add the missed function to fix it. Fixes: de1135d44f4f ("[media] omap3isp: CCDC, preview engine and resizer") Signed-off-by: Chuhong Yuan Reviewed-by: Laurent Pinchart Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/omap3isp/isppreview.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/media/platform/omap3isp/isppreview.c b/drivers/media/platform/omap3isp/isppreview.c index 4dbdf3180d108..607b7685c982f 100644 --- a/drivers/media/platform/omap3isp/isppreview.c +++ b/drivers/media/platform/omap3isp/isppreview.c @@ -2287,7 +2287,7 @@ static int preview_init_entities(struct isp_prev_device *prev) me->ops = &preview_media_ops; ret = media_entity_pads_init(me, PREV_PADS_NUM, pads); if (ret < 0) - return ret; + goto error_handler_free; preview_init_formats(sd, NULL); @@ -2320,6 +2320,8 @@ error_video_out: omap3isp_video_cleanup(&prev->video_in); error_video_in: media_entity_cleanup(&prev->subdev.entity); +error_handler_free: + v4l2_ctrl_handler_free(&prev->ctrls); return ret; } -- GitLab From d46cfdc86c30d5ec768924f0b1e2683c8d20b671 Mon Sep 17 00:00:00 2001 From: Andrey Konovalov Date: Fri, 12 Jun 2020 15:53:46 +0200 Subject: [PATCH 0148/1476] media: i2c: imx290: set the format before VIDIOC_SUBDEV_G_FMT is called With the current driver 'media-ctl -p' issued right after the imx290 driver is loaded prints: pad0: Source [fmt:unknown/0x0] The format value of zero is due to the current_format field of the imx290 struct not being initialized yet. As imx290_entity_init_cfg() calls imx290_set_fmt(), the current_mode field is also initialized, so the line which set current_mode to a default value in driver's probe() function is no longer needed. Signed-off-by: Andrey Konovalov Reviewed-by: Manivannan Sadhasivam Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/imx290.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/media/i2c/imx290.c b/drivers/media/i2c/imx290.c index f7678e5a5d879..2d8c38ffe2f07 100644 --- a/drivers/media/i2c/imx290.c +++ b/drivers/media/i2c/imx290.c @@ -722,9 +722,6 @@ static int imx290_probe(struct i2c_client *client) goto free_err; } - /* Set default mode to max resolution */ - imx290->current_mode = &imx290_modes[0]; - /* get system clock (xclk) */ imx290->xclk = devm_clk_get(dev, "xclk"); if (IS_ERR(imx290->xclk)) { @@ -809,6 +806,9 @@ static int imx290_probe(struct i2c_client *client) goto free_ctrl; } + /* Initialize the frame format (this also sets imx290->current_mode) */ + imx290_entity_init_cfg(&imx290->sd, NULL); + ret = v4l2_async_register_subdev(&imx290->sd); if (ret < 0) { dev_err(dev, "Could not register v4l2 device\n"); -- GitLab From 8d2d1bedb1b9af3e0c039a4444858da7b6da71f8 Mon Sep 17 00:00:00 2001 From: Andrey Konovalov Date: Fri, 12 Jun 2020 15:53:47 +0200 Subject: [PATCH 0149/1476] media: i2c: imx290: fix the order of the args in SET_RUNTIME_PM_OPS() The macro is defined as SET_RUNTIME_PM_OPS(suspend_fn, resume_fn, idle_fn), so imx290_power_off must be the 1st arg, and imx290_power_on the 2nd. Signed-off-by: Andrey Konovalov Reviewed-by: Manivannan Sadhasivam Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/imx290.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/i2c/imx290.c b/drivers/media/i2c/imx290.c index 2d8c38ffe2f07..d0322f9a88562 100644 --- a/drivers/media/i2c/imx290.c +++ b/drivers/media/i2c/imx290.c @@ -648,7 +648,7 @@ static int imx290_power_off(struct device *dev) } static const struct dev_pm_ops imx290_pm_ops = { - SET_RUNTIME_PM_OPS(imx290_power_on, imx290_power_off, NULL) + SET_RUNTIME_PM_OPS(imx290_power_off, imx290_power_on, NULL) }; static const struct v4l2_subdev_video_ops imx290_video_ops = { -- GitLab From 3909a92d7df622b41b9ceeeea694e641cad7667b Mon Sep 17 00:00:00 2001 From: Andrey Konovalov Date: Fri, 12 Jun 2020 15:53:48 +0200 Subject: [PATCH 0150/1476] media: i2c: imx290: fix reset GPIO pin handling According to https://www.kernel.org/doc/Documentation/gpio/consumer.txt, - all of the gpiod_set_value_xxx() functions operate with the *logical* value. So in imx290_power_on() the reset signal should be cleared (de-asserted) with gpiod_set_value_cansleep(imx290->rst_gpio, 0), and in imx290_power_off() the value of 1 must be used to apply/assert the reset to the sensor. In the device tree the reset pin is described as GPIO_ACTIVE_LOW, and gpiod_set_value_xxx() functions take this into account, - when devm_gpiod_get_optional() is called with GPIOD_ASIS, the GPIO is not initialized, and the direction must be set later; using a GPIO without setting its direction first is illegal and will result in undefined behavior. Fix this by using GPIOD_OUT_HIGH instead of GPIOD_ASIS (this asserts the reset signal to the sensor initially). Signed-off-by: Andrey Konovalov Reviewed-by: Manivannan Sadhasivam Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/imx290.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/media/i2c/imx290.c b/drivers/media/i2c/imx290.c index d0322f9a88562..7b1de1f0c8b74 100644 --- a/drivers/media/i2c/imx290.c +++ b/drivers/media/i2c/imx290.c @@ -628,7 +628,7 @@ static int imx290_power_on(struct device *dev) } usleep_range(1, 2); - gpiod_set_value_cansleep(imx290->rst_gpio, 1); + gpiod_set_value_cansleep(imx290->rst_gpio, 0); usleep_range(30000, 31000); return 0; @@ -641,7 +641,7 @@ static int imx290_power_off(struct device *dev) struct imx290 *imx290 = to_imx290(sd); clk_disable_unprepare(imx290->xclk); - gpiod_set_value_cansleep(imx290->rst_gpio, 0); + gpiod_set_value_cansleep(imx290->rst_gpio, 1); regulator_bulk_disable(IMX290_NUM_SUPPLIES, imx290->supplies); return 0; @@ -757,7 +757,8 @@ static int imx290_probe(struct i2c_client *client) goto free_err; } - imx290->rst_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_ASIS); + imx290->rst_gpio = devm_gpiod_get_optional(dev, "reset", + GPIOD_OUT_HIGH); if (IS_ERR(imx290->rst_gpio)) { dev_err(dev, "Cannot get reset gpio\n"); ret = PTR_ERR(imx290->rst_gpio); -- GitLab From 97589ad61c730e0f486635c6c19fa25ab8e8f29d Mon Sep 17 00:00:00 2001 From: Manivannan Sadhasivam Date: Fri, 12 Jun 2020 15:53:49 +0200 Subject: [PATCH 0151/1476] media: i2c: imx290: Add support for 2 data lanes The IMX290 sensor can output frames with 2/4 CSI2 data lanes. This commit adds support for 2 lane mode in addition to the 4 lane and also configuring the data lane settings in the driver based on system configuration. Signed-off-by: Manivannan Sadhasivam Signed-off-by: Andrey Konovalov Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/imx290.c | 147 +++++++++++++++++++++++++++++++++---- 1 file changed, 133 insertions(+), 14 deletions(-) diff --git a/drivers/media/i2c/imx290.c b/drivers/media/i2c/imx290.c index 7b1de1f0c8b74..ffb393962ffc5 100644 --- a/drivers/media/i2c/imx290.c +++ b/drivers/media/i2c/imx290.c @@ -25,7 +25,12 @@ #define IMX290_STANDBY 0x3000 #define IMX290_REGHOLD 0x3001 #define IMX290_XMSTA 0x3002 +#define IMX290_FR_FDG_SEL 0x3009 #define IMX290_GAIN 0x3014 +#define IMX290_HMAX_LOW 0x301c +#define IMX290_HMAX_HIGH 0x301d +#define IMX290_PHY_LANE_NUM 0x3407 +#define IMX290_CSI_LANE_MODE 0x3443 #define IMX290_DEFAULT_LINK_FREQ 445500000 @@ -45,6 +50,7 @@ struct imx290_regval { struct imx290_mode { u32 width; u32 height; + u32 hmax; u32 pixel_rate; u32 link_freq_index; @@ -56,6 +62,7 @@ struct imx290 { struct device *dev; struct clk *xclk; struct regmap *regmap; + u8 nlanes; struct v4l2_subdev sd; struct v4l2_fwnode_endpoint ep; @@ -89,14 +96,11 @@ static const struct regmap_config imx290_regmap_config = { static const struct imx290_regval imx290_global_init_settings[] = { { 0x3007, 0x00 }, - { 0x3009, 0x00 }, { 0x3018, 0x65 }, { 0x3019, 0x04 }, { 0x301a, 0x00 }, - { 0x3443, 0x03 }, { 0x3444, 0x20 }, { 0x3445, 0x25 }, - { 0x3407, 0x03 }, { 0x303a, 0x0c }, { 0x3040, 0x00 }, { 0x3041, 0x00 }, @@ -169,7 +173,6 @@ static const struct imx290_regval imx290_1080p_settings[] = { { 0x3164, 0x1a }, { 0x3480, 0x49 }, /* data rate settings */ - { 0x3009, 0x01 }, { 0x3405, 0x10 }, { 0x3446, 0x57 }, { 0x3447, 0x00 }, @@ -187,8 +190,6 @@ static const struct imx290_regval imx290_1080p_settings[] = { { 0x3453, 0x00 }, { 0x3454, 0x17 }, { 0x3455, 0x00 }, - { 0x301c, 0x98 }, - { 0x301d, 0x08 }, }; static const struct imx290_regval imx290_720p_settings[] = { @@ -210,7 +211,6 @@ static const struct imx290_regval imx290_720p_settings[] = { { 0x3164, 0x1a }, { 0x3480, 0x49 }, /* data rate settings */ - { 0x3009, 0x01 }, { 0x3405, 0x10 }, { 0x3446, 0x4f }, { 0x3447, 0x00 }, @@ -228,8 +228,6 @@ static const struct imx290_regval imx290_720p_settings[] = { { 0x3453, 0x00 }, { 0x3454, 0x17 }, { 0x3455, 0x00 }, - { 0x301c, 0xe4 }, - { 0x301d, 0x0c }, }; static const struct imx290_regval imx290_10bit_settings[] = { @@ -250,10 +248,11 @@ static const s64 imx290_link_freq[] = { }; /* Mode configs */ -static const struct imx290_mode imx290_modes[] = { +static const struct imx290_mode imx290_modes_2lanes[] = { { .width = 1920, .height = 1080, + .hmax = 0x1130, .data = imx290_1080p_settings, .data_size = ARRAY_SIZE(imx290_1080p_settings), .pixel_rate = 178200000, @@ -262,6 +261,7 @@ static const struct imx290_mode imx290_modes[] = { { .width = 1280, .height = 720, + .hmax = 0x19c8, .data = imx290_720p_settings, .data_size = ARRAY_SIZE(imx290_720p_settings), .pixel_rate = 178200000, @@ -269,6 +269,44 @@ static const struct imx290_mode imx290_modes[] = { }, }; +static const struct imx290_mode imx290_modes_4lanes[] = { + { + .width = 1920, + .height = 1080, + .hmax = 0x0898, + .data = imx290_1080p_settings, + .data_size = ARRAY_SIZE(imx290_1080p_settings), + .pixel_rate = 178200000, + .link_freq_index = 0, + }, + { + .width = 1280, + .height = 720, + .hmax = 0x0ce4, + .data = imx290_720p_settings, + .data_size = ARRAY_SIZE(imx290_720p_settings), + .pixel_rate = 178200000, + .link_freq_index = 0, + }, +}; + +static inline const struct imx290_mode *imx290_modes_ptr(const struct imx290 *imx290) +{ + /* We rely on imx290_probe() to ensure that nlanes is either 2 or 4 */ + if (imx290->nlanes == 2) + return imx290_modes_2lanes; + else + return imx290_modes_4lanes; +} + +static inline int imx290_modes_num(const struct imx290 *imx290) +{ + if (imx290->nlanes == 2) + return ARRAY_SIZE(imx290_modes_2lanes); + else + return ARRAY_SIZE(imx290_modes_4lanes); +} + static inline struct imx290 *to_imx290(struct v4l2_subdev *_sd) { return container_of(_sd, struct imx290, sd); @@ -450,9 +488,8 @@ static int imx290_set_fmt(struct v4l2_subdev *sd, mutex_lock(&imx290->lock); - mode = v4l2_find_nearest_size(imx290_modes, - ARRAY_SIZE(imx290_modes), - width, height, + mode = v4l2_find_nearest_size(imx290_modes_ptr(imx290), + imx290_modes_num(imx290), width, height, fmt->format.width, fmt->format.height); fmt->format.width = mode->width; @@ -522,6 +559,25 @@ static int imx290_write_current_format(struct imx290 *imx290, return 0; } +static int imx290_set_hmax(struct imx290 *imx290, u32 val) +{ + int ret; + + ret = imx290_write_reg(imx290, IMX290_HMAX_LOW, (val & 0xff)); + if (ret) { + dev_err(imx290->dev, "Error setting HMAX register\n"); + return ret; + } + + ret = imx290_write_reg(imx290, IMX290_HMAX_HIGH, ((val >> 8) & 0xff)); + if (ret) { + dev_err(imx290->dev, "Error setting HMAX register\n"); + return ret; + } + + return 0; +} + /* Start streaming */ static int imx290_start_streaming(struct imx290 *imx290) { @@ -550,6 +606,9 @@ static int imx290_start_streaming(struct imx290 *imx290) dev_err(imx290->dev, "Could not set current mode\n"); return ret; } + ret = imx290_set_hmax(imx290, imx290->current_mode->hmax); + if (ret < 0) + return ret; /* Apply customized values from user */ ret = v4l2_ctrl_handler_setup(imx290->sd.ctrl_handler); @@ -607,6 +666,49 @@ static int imx290_get_regulators(struct device *dev, struct imx290 *imx290) imx290->supplies); } +static int imx290_set_data_lanes(struct imx290 *imx290) +{ + int ret = 0, laneval, frsel; + + switch (imx290->nlanes) { + case 2: + laneval = 0x01; + frsel = 0x02; + break; + case 4: + laneval = 0x03; + frsel = 0x01; + break; + default: + /* + * We should never hit this since the data lane count is + * validated in probe itself + */ + dev_err(imx290->dev, "Lane configuration not supported\n"); + ret = -EINVAL; + goto exit; + } + + ret = imx290_write_reg(imx290, IMX290_PHY_LANE_NUM, laneval); + if (ret) { + dev_err(imx290->dev, "Error setting Physical Lane number register\n"); + goto exit; + } + + ret = imx290_write_reg(imx290, IMX290_CSI_LANE_MODE, laneval); + if (ret) { + dev_err(imx290->dev, "Error setting CSI Lane mode register\n"); + goto exit; + } + + ret = imx290_write_reg(imx290, IMX290_FR_FDG_SEL, frsel); + if (ret) + dev_err(imx290->dev, "Error setting FR/FDG SEL register\n"); + +exit: + return ret; +} + static int imx290_power_on(struct device *dev) { struct i2c_client *client = to_i2c_client(dev); @@ -631,6 +733,9 @@ static int imx290_power_on(struct device *dev) gpiod_set_value_cansleep(imx290->rst_gpio, 0); usleep_range(30000, 31000); + /* Set data lane count */ + imx290_set_data_lanes(imx290); + return 0; } @@ -677,6 +782,7 @@ static int imx290_probe(struct i2c_client *client) struct fwnode_handle *endpoint; struct imx290 *imx290; u32 xclk_freq; + u32 default_pixel_rate; int ret; imx290 = devm_kzalloc(dev, sizeof(*imx290), GFP_KERNEL); @@ -703,6 +809,16 @@ static int imx290_probe(struct i2c_client *client) goto free_err; } + /* Get number of data lanes */ + imx290->nlanes = imx290->ep.bus.mipi_csi2.num_data_lanes; + if (imx290->nlanes != 2 && imx290->nlanes != 4) { + dev_err(dev, "Invalid data lanes: %d\n", imx290->nlanes); + ret = -EINVAL; + goto free_err; + } + + dev_dbg(dev, "Using %u data lanes\n", imx290->nlanes); + if (!imx290->ep.nr_of_link_frequencies) { dev_err(dev, "link-frequency property not found in DT\n"); ret = -EINVAL; @@ -780,10 +896,13 @@ static int imx290_probe(struct i2c_client *client) if (imx290->link_freq) imx290->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY; + default_pixel_rate = imx290->nlanes == 2 ? + imx290_modes_2lanes[0].pixel_rate : + imx290_modes_4lanes[0].pixel_rate; imx290->pixel_rate = v4l2_ctrl_new_std(&imx290->ctrls, &imx290_ctrl_ops, V4L2_CID_PIXEL_RATE, 1, INT_MAX, 1, - imx290_modes[0].pixel_rate); + default_pixel_rate); imx290->sd.ctrl_handler = &imx290->ctrls; -- GitLab From 98e0500eadb772e1be32d8e369fcc3b7bcac93ed Mon Sep 17 00:00:00 2001 From: Manivannan Sadhasivam Date: Fri, 12 Jun 2020 15:53:50 +0200 Subject: [PATCH 0152/1476] media: i2c: imx290: Add configurable link frequency and pixel rate IMX290 operates with multiple link frequency and pixel rate combinations. The initial driver used a single setting for both but since we now have the lane count support in place, let's add configurable link frequency and pixel rate. Signed-off-by: Manivannan Sadhasivam Signed-off-by: Andrey Konovalov Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/imx290.c | 148 +++++++++++++++++++++++++++---------- 1 file changed, 109 insertions(+), 39 deletions(-) diff --git a/drivers/media/i2c/imx290.c b/drivers/media/i2c/imx290.c index ffb393962ffc5..152b65cb7cbc1 100644 --- a/drivers/media/i2c/imx290.c +++ b/drivers/media/i2c/imx290.c @@ -32,8 +32,6 @@ #define IMX290_PHY_LANE_NUM 0x3407 #define IMX290_CSI_LANE_MODE 0x3443 -#define IMX290_DEFAULT_LINK_FREQ 445500000 - static const char * const imx290_supply_name[] = { "vdda", "vddd", @@ -51,8 +49,7 @@ struct imx290_mode { u32 width; u32 height; u32 hmax; - u32 pixel_rate; - u32 link_freq_index; + u8 link_freq_index; const struct imx290_regval *data; u32 data_size; @@ -243,29 +240,54 @@ static const struct imx290_regval imx290_10bit_settings[] = { }; /* supported link frequencies */ -static const s64 imx290_link_freq[] = { - IMX290_DEFAULT_LINK_FREQ, +#define FREQ_INDEX_1080P 0 +#define FREQ_INDEX_720P 1 +static const s64 imx290_link_freq_2lanes[] = { + [FREQ_INDEX_1080P] = 445500000, + [FREQ_INDEX_720P] = 297000000, +}; +static const s64 imx290_link_freq_4lanes[] = { + [FREQ_INDEX_1080P] = 222750000, + [FREQ_INDEX_720P] = 148500000, }; +/* + * In this function and in the similar ones below We rely on imx290_probe() + * to ensure that nlanes is either 2 or 4. + */ +static inline const s64 *imx290_link_freqs_ptr(const struct imx290 *imx290) +{ + if (imx290->nlanes == 2) + return imx290_link_freq_2lanes; + else + return imx290_link_freq_4lanes; +} + +static inline int imx290_link_freqs_num(const struct imx290 *imx290) +{ + if (imx290->nlanes == 2) + return ARRAY_SIZE(imx290_link_freq_2lanes); + else + return ARRAY_SIZE(imx290_link_freq_4lanes); +} + /* Mode configs */ static const struct imx290_mode imx290_modes_2lanes[] = { { .width = 1920, .height = 1080, .hmax = 0x1130, + .link_freq_index = FREQ_INDEX_1080P, .data = imx290_1080p_settings, .data_size = ARRAY_SIZE(imx290_1080p_settings), - .pixel_rate = 178200000, - .link_freq_index = 0, }, { .width = 1280, .height = 720, .hmax = 0x19c8, + .link_freq_index = FREQ_INDEX_720P, .data = imx290_720p_settings, .data_size = ARRAY_SIZE(imx290_720p_settings), - .pixel_rate = 178200000, - .link_freq_index = 0, }, }; @@ -274,25 +296,22 @@ static const struct imx290_mode imx290_modes_4lanes[] = { .width = 1920, .height = 1080, .hmax = 0x0898, + .link_freq_index = FREQ_INDEX_1080P, .data = imx290_1080p_settings, .data_size = ARRAY_SIZE(imx290_1080p_settings), - .pixel_rate = 178200000, - .link_freq_index = 0, }, { .width = 1280, .height = 720, .hmax = 0x0ce4, + .link_freq_index = FREQ_INDEX_720P, .data = imx290_720p_settings, .data_size = ARRAY_SIZE(imx290_720p_settings), - .pixel_rate = 178200000, - .link_freq_index = 0, }, }; static inline const struct imx290_mode *imx290_modes_ptr(const struct imx290 *imx290) { - /* We rely on imx290_probe() to ensure that nlanes is either 2 or 4 */ if (imx290->nlanes == 2) return imx290_modes_2lanes; else @@ -477,6 +496,30 @@ static int imx290_get_fmt(struct v4l2_subdev *sd, return 0; } +static inline u8 imx290_get_link_freq_index(struct imx290 *imx290) +{ + return imx290->current_mode->link_freq_index; +} + +static s64 imx290_get_link_freq(struct imx290 *imx290) +{ + u8 index = imx290_get_link_freq_index(imx290); + + return *(imx290_link_freqs_ptr(imx290) + index); +} + +static u64 imx290_calc_pixel_rate(struct imx290 *imx290) +{ + s64 link_freq = imx290_get_link_freq(imx290); + u8 nlanes = imx290->nlanes; + u64 pixel_rate; + + /* pixel rate = link_freq * 2 * nr_of_lanes / bits_per_sample */ + pixel_rate = link_freq * 2 * nlanes; + do_div(pixel_rate, 10); + return pixel_rate; +} + static int imx290_set_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_format *fmt) @@ -509,10 +552,14 @@ static int imx290_set_fmt(struct v4l2_subdev *sd, format = v4l2_subdev_get_try_format(sd, cfg, fmt->pad); } else { format = &imx290->current_format; - __v4l2_ctrl_s_ctrl(imx290->link_freq, mode->link_freq_index); - __v4l2_ctrl_s_ctrl_int64(imx290->pixel_rate, mode->pixel_rate); - imx290->current_mode = mode; + + if (imx290->link_freq) + __v4l2_ctrl_s_ctrl(imx290->link_freq, + imx290_get_link_freq_index(imx290)); + if (imx290->pixel_rate) + __v4l2_ctrl_s_ctrl_int64(imx290->pixel_rate, + imx290_calc_pixel_rate(imx290)); } *format = fmt->format; @@ -536,12 +583,11 @@ static int imx290_entity_init_cfg(struct v4l2_subdev *subdev, return 0; } -static int imx290_write_current_format(struct imx290 *imx290, - struct v4l2_mbus_framefmt *format) +static int imx290_write_current_format(struct imx290 *imx290) { int ret; - switch (format->code) { + switch (imx290->current_format.code) { case MEDIA_BUS_FMT_SRGGB10_1X10: ret = imx290_set_register_array(imx290, imx290_10bit_settings, ARRAY_SIZE( @@ -592,8 +638,8 @@ static int imx290_start_streaming(struct imx290 *imx290) return ret; } - /* Set current frame format */ - ret = imx290_write_current_format(imx290, &imx290->current_format); + /* Apply the register values related to current frame format */ + ret = imx290_write_current_format(imx290); if (ret < 0) { dev_err(imx290->dev, "Could not set frame format\n"); return ret; @@ -776,13 +822,34 @@ static const struct media_entity_operations imx290_subdev_entity_ops = { .link_validate = v4l2_subdev_link_validate, }; +/* + * Returns 0 if all link frequencies used by the driver for the given number + * of MIPI data lanes are mentioned in the device tree, or the value of the + * first missing frequency otherwise. + */ +static s64 imx290_check_link_freqs(const struct imx290 *imx290) +{ + int i, j; + const s64 *freqs = imx290_link_freqs_ptr(imx290); + int freqs_count = imx290_link_freqs_num(imx290); + + for (i = 0; i < freqs_count; i++) { + for (j = 0; j < imx290->ep.nr_of_link_frequencies; j++) + if (freqs[i] == imx290->ep.link_frequencies[j]) + break; + if (j == imx290->ep.nr_of_link_frequencies) + return freqs[i]; + } + return 0; +} + static int imx290_probe(struct i2c_client *client) { struct device *dev = &client->dev; struct fwnode_handle *endpoint; struct imx290 *imx290; u32 xclk_freq; - u32 default_pixel_rate; + s64 fq; int ret; imx290 = devm_kzalloc(dev, sizeof(*imx290), GFP_KERNEL); @@ -825,8 +892,10 @@ static int imx290_probe(struct i2c_client *client) goto free_err; } - if (imx290->ep.link_frequencies[0] != IMX290_DEFAULT_LINK_FREQ) { - dev_err(dev, "Unsupported link frequency\n"); + /* Check that link frequences for all the modes are in device tree */ + fq = imx290_check_link_freqs(imx290); + if (fq) { + dev_err(dev, "Link frequency of %lld is not supported\n", fq); ret = -EINVAL; goto free_err; } @@ -883,26 +952,30 @@ static int imx290_probe(struct i2c_client *client) mutex_init(&imx290->lock); + /* + * Initialize the frame format. In particular, imx290->current_mode + * and imx290->bpp are set to defaults: imx290_calc_pixel_rate() call + * below relies on these fields. + */ + imx290_entity_init_cfg(&imx290->sd, NULL); + v4l2_ctrl_handler_init(&imx290->ctrls, 3); v4l2_ctrl_new_std(&imx290->ctrls, &imx290_ctrl_ops, V4L2_CID_GAIN, 0, 72, 1, 0); + imx290->link_freq = - v4l2_ctrl_new_int_menu(&imx290->ctrls, - &imx290_ctrl_ops, + v4l2_ctrl_new_int_menu(&imx290->ctrls, &imx290_ctrl_ops, V4L2_CID_LINK_FREQ, - ARRAY_SIZE(imx290_link_freq) - 1, - 0, imx290_link_freq); + imx290_link_freqs_num(imx290) - 1, 0, + imx290_link_freqs_ptr(imx290)); if (imx290->link_freq) imx290->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY; - default_pixel_rate = imx290->nlanes == 2 ? - imx290_modes_2lanes[0].pixel_rate : - imx290_modes_4lanes[0].pixel_rate; imx290->pixel_rate = v4l2_ctrl_new_std(&imx290->ctrls, &imx290_ctrl_ops, - V4L2_CID_PIXEL_RATE, 1, - INT_MAX, 1, - default_pixel_rate); + V4L2_CID_PIXEL_RATE, + 1, INT_MAX, 1, + imx290_calc_pixel_rate(imx290)); imx290->sd.ctrl_handler = &imx290->ctrls; @@ -926,9 +999,6 @@ static int imx290_probe(struct i2c_client *client) goto free_ctrl; } - /* Initialize the frame format (this also sets imx290->current_mode) */ - imx290_entity_init_cfg(&imx290->sd, NULL); - ret = v4l2_async_register_subdev(&imx290->sd); if (ret < 0) { dev_err(dev, "Could not register v4l2 device\n"); -- GitLab From a58df1f9e4885eaf3d0663574a217e513821a9f0 Mon Sep 17 00:00:00 2001 From: Manivannan Sadhasivam Date: Fri, 12 Jun 2020 15:53:51 +0200 Subject: [PATCH 0153/1476] media: i2c: imx290: Add support for test pattern generation Add support for generating following test patterns by IMX290: * Sequence Pattern 1 * Horizontal Color-bar Chart * Vertical Color-bar Chart * Sequence Pattern 2 * Gradation Pattern 1 * Gradation Pattern 2 * 000/555h Toggle Pattern Signed-off-by: Manivannan Sadhasivam Signed-off-by: Andrey Konovalov Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/imx290.c | 41 +++++++++++++++++++++++++++++++++++++- 1 file changed, 40 insertions(+), 1 deletion(-) diff --git a/drivers/media/i2c/imx290.c b/drivers/media/i2c/imx290.c index 152b65cb7cbc1..67725a5aabd36 100644 --- a/drivers/media/i2c/imx290.c +++ b/drivers/media/i2c/imx290.c @@ -26,12 +26,19 @@ #define IMX290_REGHOLD 0x3001 #define IMX290_XMSTA 0x3002 #define IMX290_FR_FDG_SEL 0x3009 +#define IMX290_BLKLEVEL_LOW 0x300a +#define IMX290_BLKLEVEL_HIGH 0x300b #define IMX290_GAIN 0x3014 #define IMX290_HMAX_LOW 0x301c #define IMX290_HMAX_HIGH 0x301d +#define IMX290_PGCTRL 0x308c #define IMX290_PHY_LANE_NUM 0x3407 #define IMX290_CSI_LANE_MODE 0x3443 +#define IMX290_PGCTRL_REGEN BIT(0) +#define IMX290_PGCTRL_THRU BIT(1) +#define IMX290_PGCTRL_MODE(n) ((n) << 4) + static const char * const imx290_supply_name[] = { "vdda", "vddd", @@ -91,6 +98,17 @@ static const struct regmap_config imx290_regmap_config = { .cache_type = REGCACHE_RBTREE, }; +static const char * const imx290_test_pattern_menu[] = { + "Disabled", + "Sequence Pattern 1", + "Horizontal Color-bar Chart", + "Vertical Color-bar Chart", + "Sequence Pattern 2", + "Gradation Pattern 1", + "Gradation Pattern 2", + "000/555h Toggle Pattern", +}; + static const struct imx290_regval imx290_global_init_settings[] = { { 0x3007, 0x00 }, { 0x3018, 0x65 }, @@ -448,6 +466,22 @@ static int imx290_set_ctrl(struct v4l2_ctrl *ctrl) case V4L2_CID_GAIN: ret = imx290_set_gain(imx290, ctrl->val); break; + case V4L2_CID_TEST_PATTERN: + if (ctrl->val) { + imx290_write_reg(imx290, IMX290_BLKLEVEL_LOW, 0x00); + imx290_write_reg(imx290, IMX290_BLKLEVEL_HIGH, 0x00); + msleep(10); + imx290_write_reg(imx290, IMX290_PGCTRL, + (u8)(IMX290_PGCTRL_REGEN | + IMX290_PGCTRL_THRU | + IMX290_PGCTRL_MODE(ctrl->val))); + } else { + imx290_write_reg(imx290, IMX290_PGCTRL, 0x00); + msleep(10); + imx290_write_reg(imx290, IMX290_BLKLEVEL_LOW, 0x3c); + imx290_write_reg(imx290, IMX290_BLKLEVEL_HIGH, 0x00); + } + break; default: ret = -EINVAL; break; @@ -959,7 +993,7 @@ static int imx290_probe(struct i2c_client *client) */ imx290_entity_init_cfg(&imx290->sd, NULL); - v4l2_ctrl_handler_init(&imx290->ctrls, 3); + v4l2_ctrl_handler_init(&imx290->ctrls, 4); v4l2_ctrl_new_std(&imx290->ctrls, &imx290_ctrl_ops, V4L2_CID_GAIN, 0, 72, 1, 0); @@ -977,6 +1011,11 @@ static int imx290_probe(struct i2c_client *client) 1, INT_MAX, 1, imx290_calc_pixel_rate(imx290)); + v4l2_ctrl_new_std_menu_items(&imx290->ctrls, &imx290_ctrl_ops, + V4L2_CID_TEST_PATTERN, + ARRAY_SIZE(imx290_test_pattern_menu) - 1, + 0, 0, imx290_test_pattern_menu); + imx290->sd.ctrl_handler = &imx290->ctrls; if (imx290->ctrls.error) { -- GitLab From c566ac01ceaa02450acc155201772c0623530e76 Mon Sep 17 00:00:00 2001 From: Manivannan Sadhasivam Date: Fri, 12 Jun 2020 15:53:52 +0200 Subject: [PATCH 0154/1476] media: i2c: imx290: Add RAW12 mode support IMX290 is capable of outputting frames in both Raw Bayer (packed) 10 and 12 bit formats. Since the driver already supports RAW10 mode, let's add the missing RAW12 mode as well. Signed-off-by: Manivannan Sadhasivam Signed-off-by: Andrey Konovalov Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/imx290.c | 36 +++++++++++++++++++++++++++++++++--- 1 file changed, 33 insertions(+), 3 deletions(-) diff --git a/drivers/media/i2c/imx290.c b/drivers/media/i2c/imx290.c index 67725a5aabd36..c654a9a8fb081 100644 --- a/drivers/media/i2c/imx290.c +++ b/drivers/media/i2c/imx290.c @@ -67,6 +67,7 @@ struct imx290 { struct clk *xclk; struct regmap *regmap; u8 nlanes; + u8 bpp; struct v4l2_subdev sd; struct v4l2_fwnode_endpoint ep; @@ -86,10 +87,12 @@ struct imx290 { struct imx290_pixfmt { u32 code; + u8 bpp; }; static const struct imx290_pixfmt imx290_formats[] = { - { MEDIA_BUS_FMT_SRGGB10_1X10 }, + { MEDIA_BUS_FMT_SRGGB10_1X10, 10 }, + { MEDIA_BUS_FMT_SRGGB12_1X12, 12 }, }; static const struct regmap_config imx290_regmap_config = { @@ -257,6 +260,18 @@ static const struct imx290_regval imx290_10bit_settings[] = { { 0x300b, 0x00}, }; +static const struct imx290_regval imx290_12bit_settings[] = { + { 0x3005, 0x01 }, + { 0x3046, 0x01 }, + { 0x3129, 0x00 }, + { 0x317c, 0x00 }, + { 0x31ec, 0x0e }, + { 0x3441, 0x0c }, + { 0x3442, 0x0c }, + { 0x300a, 0xf0 }, + { 0x300b, 0x00 }, +}; + /* supported link frequencies */ #define FREQ_INDEX_1080P 0 #define FREQ_INDEX_720P 1 @@ -478,7 +493,12 @@ static int imx290_set_ctrl(struct v4l2_ctrl *ctrl) } else { imx290_write_reg(imx290, IMX290_PGCTRL, 0x00); msleep(10); - imx290_write_reg(imx290, IMX290_BLKLEVEL_LOW, 0x3c); + if (imx290->bpp == 10) + imx290_write_reg(imx290, IMX290_BLKLEVEL_LOW, + 0x3c); + else /* 12 bits per pixel */ + imx290_write_reg(imx290, IMX290_BLKLEVEL_LOW, + 0xf0); imx290_write_reg(imx290, IMX290_BLKLEVEL_HIGH, 0x00); } break; @@ -550,7 +570,7 @@ static u64 imx290_calc_pixel_rate(struct imx290 *imx290) /* pixel rate = link_freq * 2 * nr_of_lanes / bits_per_sample */ pixel_rate = link_freq * 2 * nlanes; - do_div(pixel_rate, 10); + do_div(pixel_rate, imx290->bpp); return pixel_rate; } @@ -587,6 +607,7 @@ static int imx290_set_fmt(struct v4l2_subdev *sd, } else { format = &imx290->current_format; imx290->current_mode = mode; + imx290->bpp = imx290_formats[i].bpp; if (imx290->link_freq) __v4l2_ctrl_s_ctrl(imx290->link_freq, @@ -631,6 +652,15 @@ static int imx290_write_current_format(struct imx290 *imx290) return ret; } break; + case MEDIA_BUS_FMT_SRGGB12_1X12: + ret = imx290_set_register_array(imx290, imx290_12bit_settings, + ARRAY_SIZE( + imx290_12bit_settings)); + if (ret < 0) { + dev_err(imx290->dev, "Could not set format registers\n"); + return ret; + } + break; default: dev_err(imx290->dev, "Unknown pixel format\n"); return -EINVAL; -- GitLab From 3b867fb641d884b714fba390ae866714ba475f29 Mon Sep 17 00:00:00 2001 From: Manivannan Sadhasivam Date: Fri, 12 Jun 2020 15:53:53 +0200 Subject: [PATCH 0155/1476] media: i2c: imx290: Add support to enumerate all frame sizes Add support to enumerate all frame sizes supported by IMX290. This is required for using with userspace tools such as libcamera. Signed-off-by: Manivannan Sadhasivam Signed-off-by: Andrey Konovalov Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/imx290.c | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/drivers/media/i2c/imx290.c b/drivers/media/i2c/imx290.c index c654a9a8fb081..fd147fac5ef2e 100644 --- a/drivers/media/i2c/imx290.c +++ b/drivers/media/i2c/imx290.c @@ -528,6 +528,28 @@ static int imx290_enum_mbus_code(struct v4l2_subdev *sd, return 0; } +static int imx290_enum_frame_size(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_frame_size_enum *fse) +{ + const struct imx290 *imx290 = to_imx290(sd); + const struct imx290_mode *imx290_modes = imx290_modes_ptr(imx290); + + if ((fse->code != imx290_formats[0].code) && + (fse->code != imx290_formats[1].code)) + return -EINVAL; + + if (fse->index >= imx290_modes_num(imx290)) + return -EINVAL; + + fse->min_width = imx290_modes[fse->index].width; + fse->max_width = imx290_modes[fse->index].width; + fse->min_height = imx290_modes[fse->index].height; + fse->max_height = imx290_modes[fse->index].height; + + return 0; +} + static int imx290_get_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_format *fmt) @@ -873,6 +895,7 @@ static const struct v4l2_subdev_video_ops imx290_video_ops = { static const struct v4l2_subdev_pad_ops imx290_pad_ops = { .init_cfg = imx290_entity_init_cfg, .enum_mbus_code = imx290_enum_mbus_code, + .enum_frame_size = imx290_enum_frame_size, .get_fmt = imx290_get_fmt, .set_fmt = imx290_set_fmt, }; -- GitLab From 6544af9b04b4484867c234ba0be1b5008e4a14ee Mon Sep 17 00:00:00 2001 From: Manivannan Sadhasivam Date: Fri, 12 Jun 2020 15:53:54 +0200 Subject: [PATCH 0156/1476] media: i2c: imx290: Move the settle time delay out of loop The 10ms settle time is needed only at the end of all consecutive register writes. So move the delay to outside of the for loop of imx290_set_register_array(). Signed-off-by: Manivannan Sadhasivam Signed-off-by: Andrey Konovalov Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/imx290.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/media/i2c/imx290.c b/drivers/media/i2c/imx290.c index fd147fac5ef2e..02001c1b0dfcf 100644 --- a/drivers/media/i2c/imx290.c +++ b/drivers/media/i2c/imx290.c @@ -404,11 +404,11 @@ static int imx290_set_register_array(struct imx290 *imx290, ret = imx290_write_reg(imx290, settings->reg, settings->val); if (ret < 0) return ret; - - /* Settle time is 10ms for all registers */ - msleep(10); } + /* Provide 10ms settle time */ + msleep(10); + return 0; } -- GitLab From a270675875829b6d46eb9e38960fd6019555ebb8 Mon Sep 17 00:00:00 2001 From: Andrey Konovalov Date: Fri, 12 Jun 2020 15:53:55 +0200 Subject: [PATCH 0157/1476] media: i2c: imx290: set bus_type before calling v4l2_fwnode_endpoint_alloc_parse() The bus_type field of v4l2_fwnode_endpoint structure passed as the argument to v4l2_fwnode_endpoint_alloc_parse() function must be initiaized. Set it to V4L2_MBUS_CSI2_DPHY, and check for -ENXIO which is returned when the requested media bus type doesn't match the fwnode. Also remove v4l2_fwnode_endpoint field from struct imx290 as it is only needed in the probe function: use the local variable for this purpose. Signed-off-by: Andrey Konovalov Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/imx290.c | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/drivers/media/i2c/imx290.c b/drivers/media/i2c/imx290.c index 02001c1b0dfcf..9c97830164e97 100644 --- a/drivers/media/i2c/imx290.c +++ b/drivers/media/i2c/imx290.c @@ -70,7 +70,6 @@ struct imx290 { u8 bpp; struct v4l2_subdev sd; - struct v4l2_fwnode_endpoint ep; struct media_pad pad; struct v4l2_mbus_framefmt current_format; const struct imx290_mode *current_mode; @@ -914,17 +913,18 @@ static const struct media_entity_operations imx290_subdev_entity_ops = { * of MIPI data lanes are mentioned in the device tree, or the value of the * first missing frequency otherwise. */ -static s64 imx290_check_link_freqs(const struct imx290 *imx290) +static s64 imx290_check_link_freqs(const struct imx290 *imx290, + const struct v4l2_fwnode_endpoint *ep) { int i, j; const s64 *freqs = imx290_link_freqs_ptr(imx290); int freqs_count = imx290_link_freqs_num(imx290); for (i = 0; i < freqs_count; i++) { - for (j = 0; j < imx290->ep.nr_of_link_frequencies; j++) - if (freqs[i] == imx290->ep.link_frequencies[j]) + for (j = 0; j < ep->nr_of_link_frequencies; j++) + if (freqs[i] == ep->link_frequencies[j]) break; - if (j == imx290->ep.nr_of_link_frequencies) + if (j == ep->nr_of_link_frequencies) return freqs[i]; } return 0; @@ -934,6 +934,10 @@ static int imx290_probe(struct i2c_client *client) { struct device *dev = &client->dev; struct fwnode_handle *endpoint; + /* Only CSI2 is supported for now: */ + struct v4l2_fwnode_endpoint ep = { + .bus_type = V4L2_MBUS_CSI2_DPHY + }; struct imx290 *imx290; u32 xclk_freq; s64 fq; @@ -956,15 +960,18 @@ static int imx290_probe(struct i2c_client *client) return -EINVAL; } - ret = v4l2_fwnode_endpoint_alloc_parse(endpoint, &imx290->ep); + ret = v4l2_fwnode_endpoint_alloc_parse(endpoint, &ep); fwnode_handle_put(endpoint); - if (ret) { + if (ret == -ENXIO) { + dev_err(dev, "Unsupported bus type, should be CSI2\n"); + goto free_err; + } else if (ret) { dev_err(dev, "Parsing endpoint node failed\n"); goto free_err; } /* Get number of data lanes */ - imx290->nlanes = imx290->ep.bus.mipi_csi2.num_data_lanes; + imx290->nlanes = ep.bus.mipi_csi2.num_data_lanes; if (imx290->nlanes != 2 && imx290->nlanes != 4) { dev_err(dev, "Invalid data lanes: %d\n", imx290->nlanes); ret = -EINVAL; @@ -973,27 +980,20 @@ static int imx290_probe(struct i2c_client *client) dev_dbg(dev, "Using %u data lanes\n", imx290->nlanes); - if (!imx290->ep.nr_of_link_frequencies) { + if (!ep.nr_of_link_frequencies) { dev_err(dev, "link-frequency property not found in DT\n"); ret = -EINVAL; goto free_err; } /* Check that link frequences for all the modes are in device tree */ - fq = imx290_check_link_freqs(imx290); + fq = imx290_check_link_freqs(imx290, &ep); if (fq) { dev_err(dev, "Link frequency of %lld is not supported\n", fq); ret = -EINVAL; goto free_err; } - /* Only CSI2 is supported for now */ - if (imx290->ep.bus_type != V4L2_MBUS_CSI2_DPHY) { - dev_err(dev, "Unsupported bus type, should be CSI2\n"); - ret = -EINVAL; - goto free_err; - } - /* get system clock (xclk) */ imx290->xclk = devm_clk_get(dev, "xclk"); if (IS_ERR(imx290->xclk)) { @@ -1108,7 +1108,7 @@ static int imx290_probe(struct i2c_client *client) pm_runtime_enable(dev); pm_runtime_idle(dev); - v4l2_fwnode_endpoint_free(&imx290->ep); + v4l2_fwnode_endpoint_free(&ep); return 0; @@ -1118,7 +1118,7 @@ free_ctrl: v4l2_ctrl_handler_free(&imx290->ctrls); mutex_destroy(&imx290->lock); free_err: - v4l2_fwnode_endpoint_free(&imx290->ep); + v4l2_fwnode_endpoint_free(&ep); return ret; } -- GitLab From 7b981288285f0e8b816ea7ba8f34d0973ee39e0d Mon Sep 17 00:00:00 2001 From: Qingwu Zhang Date: Fri, 12 Jun 2020 11:42:02 +0200 Subject: [PATCH 0158/1476] media: ov2740: add NVMEM interface to read customized OTP data ov2740 includes 512bytes of one-time programmable memory and 256 bytes are reserved for customers which can be used to store customized information. This patch provide an NVMEM interface to support read out the customized data in OTP. Signed-off-by: Bingbu Cao Signed-off-by: Qingwu Zhang Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/ov2740.c | 145 +++++++++++++++++++++++++++++++++++++ 1 file changed, 145 insertions(+) diff --git a/drivers/media/i2c/ov2740.c b/drivers/media/i2c/ov2740.c index 33025c6d23ac0..fd0b6a903ec1d 100644 --- a/drivers/media/i2c/ov2740.c +++ b/drivers/media/i2c/ov2740.c @@ -7,6 +7,8 @@ #include #include #include +#include +#include #include #include #include @@ -59,6 +61,21 @@ #define OV2740_TEST_PATTERN_ENABLE BIT(7) #define OV2740_TEST_PATTERN_BAR_SHIFT 2 +/* ISP CTRL00 */ +#define OV2740_REG_ISP_CTRL00 0x5000 +/* ISP CTRL01 */ +#define OV2740_REG_ISP_CTRL01 0x5001 +/* Customer Addresses: 0x7010 - 0x710F */ +#define CUSTOMER_USE_OTP_SIZE 0x100 +/* OTP registers from sensor */ +#define OV2740_REG_OTP_CUSTOMER 0x7010 + +struct nvm_data { + char *nvm_buffer; + struct nvmem_device *nvmem; + struct regmap *regmap; +}; + enum { OV2740_LINK_FREQ_360MHZ_INDEX, }; @@ -915,6 +932,130 @@ static int ov2740_remove(struct i2c_client *client) return 0; } +static int ov2740_load_otp_data(struct i2c_client *client, struct nvm_data *nvm) +{ + struct ov2740 *ov2740 = to_ov2740(i2c_get_clientdata(client)); + u32 isp_ctrl00 = 0; + u32 isp_ctrl01 = 0; + int ret; + + ret = ov2740_read_reg(ov2740, OV2740_REG_ISP_CTRL00, 1, &isp_ctrl00); + if (ret) { + dev_err(&client->dev, "failed to read ISP CTRL00\n"); + goto exit; + } + ret = ov2740_read_reg(ov2740, OV2740_REG_ISP_CTRL01, 1, &isp_ctrl01); + if (ret) { + dev_err(&client->dev, "failed to read ISP CTRL01\n"); + goto exit; + } + + /* Clear bit 5 of ISP CTRL00 */ + ret = ov2740_write_reg(ov2740, OV2740_REG_ISP_CTRL00, 1, + isp_ctrl00 & ~BIT(5)); + if (ret) { + dev_err(&client->dev, "failed to write ISP CTRL00\n"); + goto exit; + } + + /* Clear bit 7 of ISP CTRL01 */ + ret = ov2740_write_reg(ov2740, OV2740_REG_ISP_CTRL01, 1, + isp_ctrl01 & ~BIT(7)); + if (ret) { + dev_err(&client->dev, "failed to write ISP CTRL01\n"); + goto exit; + } + + ret = ov2740_write_reg(ov2740, OV2740_REG_MODE_SELECT, 1, + OV2740_MODE_STREAMING); + if (ret) { + dev_err(&client->dev, "failed to start streaming\n"); + goto exit; + } + + /* + * Users are not allowed to access OTP-related registers and memory + * during the 20 ms period after streaming starts (0x100 = 0x01). + */ + msleep(20); + + ret = regmap_bulk_read(nvm->regmap, OV2740_REG_OTP_CUSTOMER, + nvm->nvm_buffer, CUSTOMER_USE_OTP_SIZE); + if (ret) { + dev_err(&client->dev, "failed to read OTP data, ret %d\n", ret); + goto exit; + } + + ov2740_write_reg(ov2740, OV2740_REG_MODE_SELECT, 1, + OV2740_MODE_STANDBY); + ov2740_write_reg(ov2740, OV2740_REG_ISP_CTRL01, 1, isp_ctrl01); + ov2740_write_reg(ov2740, OV2740_REG_ISP_CTRL00, 1, isp_ctrl00); + +exit: + return ret; +} + +static int ov2740_nvmem_read(void *priv, unsigned int off, void *val, + size_t count) +{ + struct nvm_data *nvm = priv; + + memcpy(val, nvm->nvm_buffer + off, count); + + return 0; +} + +static int ov2740_register_nvmem(struct i2c_client *client) +{ + struct nvm_data *nvm; + struct regmap_config regmap_config = { }; + struct nvmem_config nvmem_config = { }; + struct regmap *regmap; + struct device *dev = &client->dev; + int ret = 0; + + nvm = devm_kzalloc(dev, sizeof(*nvm), GFP_KERNEL); + if (!nvm) + return -ENOMEM; + + regmap_config.val_bits = 8; + regmap_config.reg_bits = 16; + regmap_config.disable_locking = true; + regmap = devm_regmap_init_i2c(client, ®map_config); + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + + nvm->regmap = regmap; + + nvmem_config.name = dev_name(dev); + nvmem_config.dev = dev; + nvmem_config.read_only = true; + nvmem_config.root_only = true; + nvmem_config.owner = THIS_MODULE; + nvmem_config.compat = true; + nvmem_config.base_dev = dev; + nvmem_config.reg_read = ov2740_nvmem_read; + nvmem_config.reg_write = NULL; + nvmem_config.priv = nvm; + nvmem_config.stride = 1; + nvmem_config.word_size = 1; + nvmem_config.size = CUSTOMER_USE_OTP_SIZE; + + nvm->nvmem = devm_nvmem_register(dev, &nvmem_config); + if (IS_ERR(nvm->nvmem)) + return PTR_ERR(nvm->nvmem); + + nvm->nvm_buffer = devm_kzalloc(dev, CUSTOMER_USE_OTP_SIZE, GFP_KERNEL); + if (!nvm->nvm_buffer) + return -ENOMEM; + + ret = ov2740_load_otp_data(client, nvm); + if (ret) + dev_err(dev, "failed to load OTP data, ret %d\n", ret); + + return ret; +} + static int ov2740_probe(struct i2c_client *client) { struct ov2740 *ov2740; @@ -964,6 +1105,10 @@ static int ov2740_probe(struct i2c_client *client) goto probe_error_media_entity_cleanup; } + ret = ov2740_register_nvmem(client); + if (ret) + dev_err(&client->dev, "register nvmem failed, ret %d\n", ret); + /* * Device is already turned on by i2c-core with ACPI domain PM. * Enable runtime PM and turn off the device. -- GitLab From e30cc79cc80fd919b697a15c5000d9f57487de8e Mon Sep 17 00:00:00 2001 From: Tuomas Tynkkynen Date: Sun, 21 Jun 2020 13:30:40 +0200 Subject: [PATCH 0159/1476] media: media-request: Fix crash if memory allocation fails Syzbot reports a NULL-ptr deref in the kref_put() call: BUG: KASAN: null-ptr-deref in media_request_put drivers/media/mc/mc-request.c:81 [inline] kref_put include/linux/kref.h:64 [inline] media_request_put drivers/media/mc/mc-request.c:81 [inline] media_request_close+0x4d/0x170 drivers/media/mc/mc-request.c:89 __fput+0x2ed/0x750 fs/file_table.c:281 task_work_run+0x147/0x1d0 kernel/task_work.c:123 tracehook_notify_resume include/linux/tracehook.h:188 [inline] exit_to_usermode_loop arch/x86/entry/common.c:165 [inline] prepare_exit_to_usermode+0x48e/0x600 arch/x86/entry/common.c:196 What led to this crash was an injected memory allocation failure in media_request_alloc(): FAULT_INJECTION: forcing a failure. name failslab, interval 1, probability 0, space 0, times 0 should_failslab+0x5/0x20 kmem_cache_alloc_trace+0x57/0x300 ? anon_inode_getfile+0xe5/0x170 media_request_alloc+0x339/0x440 media_device_request_alloc+0x94/0xc0 media_device_ioctl+0x1fb/0x330 ? do_vfs_ioctl+0x6ea/0x1a00 ? media_ioctl+0x101/0x120 ? __media_device_usb_init+0x430/0x430 ? media_poll+0x110/0x110 __se_sys_ioctl+0xf9/0x160 do_syscall_64+0xf3/0x1b0 When that allocation fails, filp->private_data is left uninitialized which media_request_close() does not expect and crashes. To avoid this, reorder media_request_alloc() such that allocating the struct file happens as the last step thus media_request_close() will no longer get called for a partially created media request. Reported-by: syzbot+6bed2d543cf7e48b822b@syzkaller.appspotmail.com Cc: stable@vger.kernel.org Signed-off-by: Tuomas Tynkkynen Fixes: 10905d70d788 ("media: media-request: implement media requests") Reviewed-by: Hans Verkuil Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/mc/mc-request.c | 31 +++++++++++++++++-------------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/drivers/media/mc/mc-request.c b/drivers/media/mc/mc-request.c index e3fca436c75bf..c0782fd96c591 100644 --- a/drivers/media/mc/mc-request.c +++ b/drivers/media/mc/mc-request.c @@ -296,9 +296,18 @@ int media_request_alloc(struct media_device *mdev, int *alloc_fd) if (WARN_ON(!mdev->ops->req_alloc ^ !mdev->ops->req_free)) return -ENOMEM; + if (mdev->ops->req_alloc) + req = mdev->ops->req_alloc(mdev); + else + req = kzalloc(sizeof(*req), GFP_KERNEL); + if (!req) + return -ENOMEM; + fd = get_unused_fd_flags(O_CLOEXEC); - if (fd < 0) - return fd; + if (fd < 0) { + ret = fd; + goto err_free_req; + } filp = anon_inode_getfile("request", &request_fops, NULL, O_CLOEXEC); if (IS_ERR(filp)) { @@ -306,15 +315,6 @@ int media_request_alloc(struct media_device *mdev, int *alloc_fd) goto err_put_fd; } - if (mdev->ops->req_alloc) - req = mdev->ops->req_alloc(mdev); - else - req = kzalloc(sizeof(*req), GFP_KERNEL); - if (!req) { - ret = -ENOMEM; - goto err_fput; - } - filp->private_data = req; req->mdev = mdev; req->state = MEDIA_REQUEST_STATE_IDLE; @@ -336,12 +336,15 @@ int media_request_alloc(struct media_device *mdev, int *alloc_fd) return 0; -err_fput: - fput(filp); - err_put_fd: put_unused_fd(fd); +err_free_req: + if (mdev->ops->req_free) + mdev->ops->req_free(req); + else + kfree(req); + return ret; } -- GitLab From 09c16edc5a3dc2bcfa8a93ddbeec59793e3b414d Mon Sep 17 00:00:00 2001 From: Vinod Koul Date: Tue, 23 Jun 2020 19:59:15 +0530 Subject: [PATCH 0160/1476] MAINTAINERS: switch dmaengine tree to kernel.org I have switched DMAengine tree to kernel.org now, so update in MAINTAINERS file Signed-off-by: Vinod Koul --- MAINTAINERS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MAINTAINERS b/MAINTAINERS index 68f21d46614c4..49d096742d5dc 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -5111,7 +5111,7 @@ M: Vinod Koul L: dmaengine@vger.kernel.org S: Maintained Q: https://patchwork.kernel.org/project/linux-dmaengine/list/ -T: git git://git.infradead.org/users/vkoul/slave-dma.git +T: git git://git.kernel.org/pub/scm/linux/kernel/git/vkoul/dmaengine.git F: Documentation/devicetree/bindings/dma/ F: Documentation/driver-api/dmaengine/ F: drivers/dma/ -- GitLab From 999a32efed09d724c426568731c5691233d3a680 Mon Sep 17 00:00:00 2001 From: "Gustavo A. R. Silva" Date: Wed, 17 Jun 2020 16:11:35 -0500 Subject: [PATCH 0161/1476] dmaengine: hisilicon: Use struct_size() in devm_kzalloc() Make use of the struct_size() helper instead of an open-coded version in order to avoid any potential type mistakes. This code was detected with the help of Coccinelle and, audited and fixed manually. Signed-off-by: Gustavo A. R. Silva Reviewed-by: Zhou Wang Link: https://lore.kernel.org/r/20200617211135.GA8660@embeddedor Signed-off-by: Vinod Koul --- drivers/dma/hisi_dma.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/drivers/dma/hisi_dma.c b/drivers/dma/hisi_dma.c index ed3619266a482..e1a958ae79254 100644 --- a/drivers/dma/hisi_dma.c +++ b/drivers/dma/hisi_dma.c @@ -511,7 +511,6 @@ static int hisi_dma_probe(struct pci_dev *pdev, const struct pci_device_id *id) struct device *dev = &pdev->dev; struct hisi_dma_dev *hdma_dev; struct dma_device *dma_dev; - size_t dev_size; int ret; ret = pcim_enable_device(pdev); @@ -534,9 +533,7 @@ static int hisi_dma_probe(struct pci_dev *pdev, const struct pci_device_id *id) if (ret) return ret; - dev_size = sizeof(struct hisi_dma_chan) * HISI_DMA_CHAN_NUM + - sizeof(*hdma_dev); - hdma_dev = devm_kzalloc(dev, dev_size, GFP_KERNEL); + hdma_dev = devm_kzalloc(dev, struct_size(hdma_dev, chan, HISI_DMA_CHAN_NUM), GFP_KERNEL); if (!hdma_dev) return -EINVAL; -- GitLab From 6915ef1cbebb3f66e140ce6b1480bf4a800719da Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Mon, 22 Jun 2020 21:13:11 +0300 Subject: [PATCH 0162/1476] dmaengine: acpi: Drop double check for ACPI companion device acpi_dev_get_resources() does perform the NULL pointer check against ACPI companion device which is given as function parameter. Thus, there is no need to duplicate this check in the caller. Signed-off-by: Andy Shevchenko Link: https://lore.kernel.org/r/20200622181311.67649-1-andriy.shevchenko@linux.intel.com Signed-off-by: Vinod Koul --- drivers/dma/acpi-dma.c | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/drivers/dma/acpi-dma.c b/drivers/dma/acpi-dma.c index 8a05db3343d39..35f4804ea4afa 100644 --- a/drivers/dma/acpi-dma.c +++ b/drivers/dma/acpi-dma.c @@ -358,19 +358,12 @@ struct dma_chan *acpi_dma_request_slave_chan_by_index(struct device *dev, { struct acpi_dma_parser_data pdata; struct acpi_dma_spec *dma_spec = &pdata.dma_spec; + struct acpi_device *adev = ACPI_COMPANION(dev); struct list_head resource_list; - struct acpi_device *adev; struct acpi_dma *adma; struct dma_chan *chan = NULL; int found; - - /* Check if the device was enumerated by ACPI */ - if (!dev) - return ERR_PTR(-ENODEV); - - adev = ACPI_COMPANION(dev); - if (!adev) - return ERR_PTR(-ENODEV); + int ret; memset(&pdata, 0, sizeof(pdata)); pdata.index = index; @@ -380,9 +373,11 @@ struct dma_chan *acpi_dma_request_slave_chan_by_index(struct device *dev, dma_spec->slave_id = -1; INIT_LIST_HEAD(&resource_list); - acpi_dev_get_resources(adev, &resource_list, - acpi_dma_parse_fixed_dma, &pdata); + ret = acpi_dev_get_resources(adev, &resource_list, + acpi_dma_parse_fixed_dma, &pdata); acpi_dev_free_resource_list(&resource_list); + if (ret < 0) + return ERR_PTR(ret); if (dma_spec->slave_id < 0 || dma_spec->chan_id < 0) return ERR_PTR(-ENODEV); -- GitLab From 77522b2191368ea57720e960fc3c7e6fbfc04533 Mon Sep 17 00:00:00 2001 From: Dave Jiang Date: Mon, 15 Jun 2020 13:54:31 -0700 Subject: [PATCH 0163/1476] dmaengine: idxd: add leading / for sysfspath in ABI documentation Correct to standard convention. All sysfs paths seem to be missing leading /. Signed-off-by: Dave Jiang Reviewed-by: Dan Williams Link: https://lore.kernel.org/r/159225447176.68253.2922149693913698177.stgit@djiang5-desk3.ch.intel.com Signed-off-by: Vinod Koul --- .../ABI/stable/sysfs-driver-dma-idxd | 56 +++++++++---------- 1 file changed, 28 insertions(+), 28 deletions(-) diff --git a/Documentation/ABI/stable/sysfs-driver-dma-idxd b/Documentation/ABI/stable/sysfs-driver-dma-idxd index b5bebf642db69..1af9c41752135 100644 --- a/Documentation/ABI/stable/sysfs-driver-dma-idxd +++ b/Documentation/ABI/stable/sysfs-driver-dma-idxd @@ -1,47 +1,47 @@ -What: sys/bus/dsa/devices/dsa/version +What: /sys/bus/dsa/devices/dsa/version Date: Apr 15, 2020 KernelVersion: 5.8.0 Contact: dmaengine@vger.kernel.org Description: The hardware version number. -What: sys/bus/dsa/devices/dsa/cdev_major +What: /sys/bus/dsa/devices/dsa/cdev_major Date: Oct 25, 2019 -KernelVersion: 5.6.0 +KernelVersion: 5.6.0 Contact: dmaengine@vger.kernel.org Description: The major number that the character device driver assigned to this device. -What: sys/bus/dsa/devices/dsa/errors +What: /sys/bus/dsa/devices/dsa/errors Date: Oct 25, 2019 KernelVersion: 5.6.0 Contact: dmaengine@vger.kernel.org Description: The error information for this device. -What: sys/bus/dsa/devices/dsa/max_batch_size +What: /sys/bus/dsa/devices/dsa/max_batch_size Date: Oct 25, 2019 KernelVersion: 5.6.0 Contact: dmaengine@vger.kernel.org Description: The largest number of work descriptors in a batch. -What: sys/bus/dsa/devices/dsa/max_work_queues_size +What: /sys/bus/dsa/devices/dsa/max_work_queues_size Date: Oct 25, 2019 KernelVersion: 5.6.0 Contact: dmaengine@vger.kernel.org Description: The maximum work queue size supported by this device. -What: sys/bus/dsa/devices/dsa/max_engines +What: /sys/bus/dsa/devices/dsa/max_engines Date: Oct 25, 2019 KernelVersion: 5.6.0 Contact: dmaengine@vger.kernel.org Description: The maximum number of engines supported by this device. -What: sys/bus/dsa/devices/dsa/max_groups +What: /sys/bus/dsa/devices/dsa/max_groups Date: Oct 25, 2019 KernelVersion: 5.6.0 Contact: dmaengine@vger.kernel.org Description: The maximum number of groups can be created under this device. -What: sys/bus/dsa/devices/dsa/max_tokens +What: /sys/bus/dsa/devices/dsa/max_tokens Date: Oct 25, 2019 KernelVersion: 5.6.0 Contact: dmaengine@vger.kernel.org @@ -50,7 +50,7 @@ Description: The total number of bandwidth tokens supported by this device. implementation, and these resources are allocated by engines to support operations. -What: sys/bus/dsa/devices/dsa/max_transfer_size +What: /sys/bus/dsa/devices/dsa/max_transfer_size Date: Oct 25, 2019 KernelVersion: 5.6.0 Contact: dmaengine@vger.kernel.org @@ -58,57 +58,57 @@ Description: The number of bytes to be read from the source address to perform the operation. The maximum transfer size is dependent on the workqueue the descriptor was submitted to. -What: sys/bus/dsa/devices/dsa/max_work_queues +What: /sys/bus/dsa/devices/dsa/max_work_queues Date: Oct 25, 2019 KernelVersion: 5.6.0 Contact: dmaengine@vger.kernel.org Description: The maximum work queue number that this device supports. -What: sys/bus/dsa/devices/dsa/numa_node +What: /sys/bus/dsa/devices/dsa/numa_node Date: Oct 25, 2019 KernelVersion: 5.6.0 Contact: dmaengine@vger.kernel.org Description: The numa node number for this device. -What: sys/bus/dsa/devices/dsa/op_cap +What: /sys/bus/dsa/devices/dsa/op_cap Date: Oct 25, 2019 KernelVersion: 5.6.0 Contact: dmaengine@vger.kernel.org Description: The operation capability bit mask specify the operation types supported by the this device. -What: sys/bus/dsa/devices/dsa/state +What: /sys/bus/dsa/devices/dsa/state Date: Oct 25, 2019 KernelVersion: 5.6.0 Contact: dmaengine@vger.kernel.org Description: The state information of this device. It can be either enabled or disabled. -What: sys/bus/dsa/devices/dsa/group. +What: /sys/bus/dsa/devices/dsa/group. Date: Oct 25, 2019 KernelVersion: 5.6.0 Contact: dmaengine@vger.kernel.org Description: The assigned group under this device. -What: sys/bus/dsa/devices/dsa/engine. +What: /sys/bus/dsa/devices/dsa/engine. Date: Oct 25, 2019 KernelVersion: 5.6.0 Contact: dmaengine@vger.kernel.org Description: The assigned engine under this device. -What: sys/bus/dsa/devices/dsa/wq. +What: /sys/bus/dsa/devices/dsa/wq. Date: Oct 25, 2019 KernelVersion: 5.6.0 Contact: dmaengine@vger.kernel.org Description: The assigned work queue under this device. -What: sys/bus/dsa/devices/dsa/configurable +What: /sys/bus/dsa/devices/dsa/configurable Date: Oct 25, 2019 KernelVersion: 5.6.0 Contact: dmaengine@vger.kernel.org Description: To indicate if this device is configurable or not. -What: sys/bus/dsa/devices/dsa/token_limit +What: /sys/bus/dsa/devices/dsa/token_limit Date: Oct 25, 2019 KernelVersion: 5.6.0 Contact: dmaengine@vger.kernel.org @@ -116,19 +116,19 @@ Description: The maximum number of bandwidth tokens that may be in use at one time by operations that access low bandwidth memory in the device. -What: sys/bus/dsa/devices/wq./group_id +What: /sys/bus/dsa/devices/wq./group_id Date: Oct 25, 2019 KernelVersion: 5.6.0 Contact: dmaengine@vger.kernel.org Description: The group id that this work queue belongs to. -What: sys/bus/dsa/devices/wq./size +What: /sys/bus/dsa/devices/wq./size Date: Oct 25, 2019 KernelVersion: 5.6.0 Contact: dmaengine@vger.kernel.org Description: The work queue size for this work queue. -What: sys/bus/dsa/devices/wq./type +What: /sys/bus/dsa/devices/wq./type Date: Oct 25, 2019 KernelVersion: 5.6.0 Contact: dmaengine@vger.kernel.org @@ -136,20 +136,20 @@ Description: The type of this work queue, it can be "kernel" type for work queue usages in the kernel space or "user" type for work queue usages by applications in user space. -What: sys/bus/dsa/devices/wq./cdev_minor +What: /sys/bus/dsa/devices/wq./cdev_minor Date: Oct 25, 2019 KernelVersion: 5.6.0 Contact: dmaengine@vger.kernel.org Description: The minor number assigned to this work queue by the character device driver. -What: sys/bus/dsa/devices/wq./mode +What: /sys/bus/dsa/devices/wq./mode Date: Oct 25, 2019 KernelVersion: 5.6.0 Contact: dmaengine@vger.kernel.org Description: The work queue mode type for this work queue. -What: sys/bus/dsa/devices/wq./priority +What: /sys/bus/dsa/devices/wq./priority Date: Oct 25, 2019 KernelVersion: 5.6.0 Contact: dmaengine@vger.kernel.org @@ -157,20 +157,20 @@ Description: The priority value of this work queue, it is a vlue relative to other work queue in the same group to control quality of service for dispatching work from multiple workqueues in the same group. -What: sys/bus/dsa/devices/wq./state +What: /sys/bus/dsa/devices/wq./state Date: Oct 25, 2019 KernelVersion: 5.6.0 Contact: dmaengine@vger.kernel.org Description: The current state of the work queue. -What: sys/bus/dsa/devices/wq./threshold +What: /sys/bus/dsa/devices/wq./threshold Date: Oct 25, 2019 KernelVersion: 5.6.0 Contact: dmaengine@vger.kernel.org Description: The number of entries in this work queue that may be filled via a limited portal. -What: sys/bus/dsa/devices/engine./group_id +What: /sys/bus/dsa/devices/engine./group_id Date: Oct 25, 2019 KernelVersion: 5.6.0 Contact: dmaengine@vger.kernel.org -- GitLab From 0705107fcc80711680b169abc2011686dded6c21 Mon Sep 17 00:00:00 2001 From: Dave Jiang Date: Mon, 15 Jun 2020 13:54:26 -0700 Subject: [PATCH 0164/1476] dmaengine: idxd: move submission to sbitmap_queue Kill the percpu-rwsem for work submission in favor of an sbitmap_queue. Signed-off-by: Dave Jiang Reviewed-by: Tony Luck Reviewed-by: Dan Williams Link: https://lore.kernel.org/r/159225446631.68253.8860709181621260997.stgit@djiang5-desk3.ch.intel.com Signed-off-by: Vinod Koul --- drivers/dma/Kconfig | 2 +- drivers/dma/idxd/device.c | 14 ++++---- drivers/dma/idxd/idxd.h | 6 ++-- drivers/dma/idxd/init.c | 20 ----------- drivers/dma/idxd/submit.c | 74 +++++++++++++++++++-------------------- 5 files changed, 46 insertions(+), 70 deletions(-) diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig index de41d7928bff2..b70e90765ad35 100644 --- a/drivers/dma/Kconfig +++ b/drivers/dma/Kconfig @@ -285,8 +285,8 @@ config INTEL_IDMA64 config INTEL_IDXD tristate "Intel Data Accelerators support" depends on PCI && X86_64 + depends on SBITMAP select DMA_ENGINE - select SBITMAP help Enable support for the Intel(R) data accelerators present in Intel Xeon CPU. diff --git a/drivers/dma/idxd/device.c b/drivers/dma/idxd/device.c index 8d79a8787104d..8f05b29e78913 100644 --- a/drivers/dma/idxd/device.c +++ b/drivers/dma/idxd/device.c @@ -160,16 +160,14 @@ static int alloc_descs(struct idxd_wq *wq, int num) int idxd_wq_alloc_resources(struct idxd_wq *wq) { struct idxd_device *idxd = wq->idxd; - struct idxd_group *group = wq->group; struct device *dev = &idxd->pdev->dev; int rc, num_descs, i; if (wq->type != IDXD_WQT_KERNEL) return 0; - num_descs = wq->size + - idxd->hw.gen_cap.max_descs_per_engine * group->num_engines; - wq->num_descs = num_descs; + wq->num_descs = wq->size; + num_descs = wq->size; rc = alloc_hw_descs(wq, num_descs); if (rc < 0) @@ -187,8 +185,8 @@ int idxd_wq_alloc_resources(struct idxd_wq *wq) if (rc < 0) goto fail_alloc_descs; - rc = sbitmap_init_node(&wq->sbmap, num_descs, -1, GFP_KERNEL, - dev_to_node(dev)); + rc = sbitmap_queue_init_node(&wq->sbq, num_descs, -1, false, GFP_KERNEL, + dev_to_node(dev)); if (rc < 0) goto fail_sbitmap_init; @@ -201,7 +199,7 @@ int idxd_wq_alloc_resources(struct idxd_wq *wq) sizeof(struct dsa_completion_record) * i; desc->id = i; desc->wq = wq; - + desc->cpu = -1; dma_async_tx_descriptor_init(&desc->txd, &wq->dma_chan); desc->txd.tx_submit = idxd_dma_tx_submit; } @@ -227,7 +225,7 @@ void idxd_wq_free_resources(struct idxd_wq *wq) free_hw_descs(wq); free_descs(wq); dma_free_coherent(dev, wq->compls_size, wq->compls, wq->compls_addr); - sbitmap_free(&wq->sbmap); + sbitmap_queue_free(&wq->sbq); } int idxd_wq_enable(struct idxd_wq *wq) diff --git a/drivers/dma/idxd/idxd.h b/drivers/dma/idxd/idxd.h index b8f8a363b4a71..b03a754918ef4 100644 --- a/drivers/dma/idxd/idxd.h +++ b/drivers/dma/idxd/idxd.h @@ -104,7 +104,6 @@ struct idxd_wq { enum idxd_wq_state state; unsigned long flags; union wqcfg wqcfg; - atomic_t dq_count; /* dedicated queue flow control */ u32 vec_ptr; /* interrupt steering */ struct dsa_hw_desc **hw_descs; int num_descs; @@ -112,10 +111,8 @@ struct idxd_wq { dma_addr_t compls_addr; int compls_size; struct idxd_desc **descs; - struct sbitmap sbmap; + struct sbitmap_queue sbq; struct dma_chan dma_chan; - struct percpu_rw_semaphore submit_lock; - wait_queue_head_t submit_waitq; char name[WQ_NAME_SIZE + 1]; }; @@ -201,6 +198,7 @@ struct idxd_desc { struct llist_node llnode; struct list_head list; int id; + int cpu; struct idxd_wq *wq; }; diff --git a/drivers/dma/idxd/init.c b/drivers/dma/idxd/init.c index 7778c05deb5d5..b69839a8ac2c0 100644 --- a/drivers/dma/idxd/init.c +++ b/drivers/dma/idxd/init.c @@ -141,17 +141,6 @@ static int idxd_setup_interrupts(struct idxd_device *idxd) return rc; } -static void idxd_wqs_free_lock(struct idxd_device *idxd) -{ - int i; - - for (i = 0; i < idxd->max_wqs; i++) { - struct idxd_wq *wq = &idxd->wqs[i]; - - percpu_free_rwsem(&wq->submit_lock); - } -} - static int idxd_setup_internals(struct idxd_device *idxd) { struct device *dev = &idxd->pdev->dev; @@ -181,19 +170,11 @@ static int idxd_setup_internals(struct idxd_device *idxd) for (i = 0; i < idxd->max_wqs; i++) { struct idxd_wq *wq = &idxd->wqs[i]; - int rc; wq->id = i; wq->idxd = idxd; mutex_init(&wq->wq_lock); - atomic_set(&wq->dq_count, 0); - init_waitqueue_head(&wq->submit_waitq); wq->idxd_cdev.minor = -1; - rc = percpu_init_rwsem(&wq->submit_lock); - if (rc < 0) { - idxd_wqs_free_lock(idxd); - return rc; - } } for (i = 0; i < idxd->max_engines; i++) { @@ -462,7 +443,6 @@ static void idxd_remove(struct pci_dev *pdev) dev_dbg(&pdev->dev, "%s called\n", __func__); idxd_cleanup_sysfs(idxd); idxd_shutdown(pdev); - idxd_wqs_free_lock(idxd); mutex_lock(&idxd_idr_lock); idr_remove(&idxd_idrs[idxd->type], idxd->id); mutex_unlock(&idxd_idr_lock); diff --git a/drivers/dma/idxd/submit.c b/drivers/dma/idxd/submit.c index 45a0c5869a0af..156a1ee233aa5 100644 --- a/drivers/dma/idxd/submit.c +++ b/drivers/dma/idxd/submit.c @@ -8,61 +8,61 @@ #include "idxd.h" #include "registers.h" -struct idxd_desc *idxd_alloc_desc(struct idxd_wq *wq, enum idxd_op_type optype) +static struct idxd_desc *__get_desc(struct idxd_wq *wq, int idx, int cpu) { struct idxd_desc *desc; - int idx; + + desc = wq->descs[idx]; + memset(desc->hw, 0, sizeof(struct dsa_hw_desc)); + memset(desc->completion, 0, sizeof(struct dsa_completion_record)); + desc->cpu = cpu; + return desc; +} + +struct idxd_desc *idxd_alloc_desc(struct idxd_wq *wq, enum idxd_op_type optype) +{ + int cpu, idx; struct idxd_device *idxd = wq->idxd; + DEFINE_SBQ_WAIT(wait); + struct sbq_wait_state *ws; + struct sbitmap_queue *sbq; if (idxd->state != IDXD_DEV_ENABLED) return ERR_PTR(-EIO); - if (optype == IDXD_OP_BLOCK) - percpu_down_read(&wq->submit_lock); - else if (!percpu_down_read_trylock(&wq->submit_lock)) - return ERR_PTR(-EBUSY); - - if (!atomic_add_unless(&wq->dq_count, 1, wq->size)) { - int rc; - - if (optype == IDXD_OP_NONBLOCK) { - percpu_up_read(&wq->submit_lock); + sbq = &wq->sbq; + idx = sbitmap_queue_get(sbq, &cpu); + if (idx < 0) { + if (optype == IDXD_OP_NONBLOCK) return ERR_PTR(-EAGAIN); - } - - percpu_up_read(&wq->submit_lock); - percpu_down_write(&wq->submit_lock); - rc = wait_event_interruptible(wq->submit_waitq, - atomic_add_unless(&wq->dq_count, - 1, wq->size) || - idxd->state != IDXD_DEV_ENABLED); - percpu_up_write(&wq->submit_lock); - if (rc < 0) - return ERR_PTR(-EINTR); - if (idxd->state != IDXD_DEV_ENABLED) - return ERR_PTR(-EIO); } else { - percpu_up_read(&wq->submit_lock); + return __get_desc(wq, idx, cpu); } - idx = sbitmap_get(&wq->sbmap, 0, false); - if (idx < 0) { - atomic_dec(&wq->dq_count); - return ERR_PTR(-EAGAIN); + ws = &sbq->ws[0]; + for (;;) { + sbitmap_prepare_to_wait(sbq, ws, &wait, TASK_INTERRUPTIBLE); + if (signal_pending_state(TASK_INTERRUPTIBLE, current)) + break; + idx = sbitmap_queue_get(sbq, &cpu); + if (idx > 0) + break; + schedule(); } - desc = wq->descs[idx]; - memset(desc->hw, 0, sizeof(struct dsa_hw_desc)); - memset(desc->completion, 0, sizeof(struct dsa_completion_record)); - return desc; + sbitmap_finish_wait(sbq, ws, &wait); + if (idx < 0) + return ERR_PTR(-EAGAIN); + + return __get_desc(wq, idx, cpu); } void idxd_free_desc(struct idxd_wq *wq, struct idxd_desc *desc) { - atomic_dec(&wq->dq_count); + int cpu = desc->cpu; - sbitmap_clear_bit(&wq->sbmap, desc->id); - wake_up(&wq->submit_waitq); + desc->cpu = -1; + sbitmap_queue_clear(&wq->sbq, desc->id, cpu); } int idxd_submit_desc(struct idxd_wq *wq, struct idxd_desc *desc) -- GitLab From d12ea5591eddf625b7707c018b72e46e8674c3c2 Mon Sep 17 00:00:00 2001 From: Sugar Zhang Date: Thu, 4 Jun 2020 09:36:38 +0800 Subject: [PATCH 0165/1476] dmaengine: pl330: Make sure the debug is idle before doing DMAGO According to the datasheet of pl330: Example 2-1 Using DMAGO with the debug instruction registers 1. Create a program for the DMA channel 2. Store the program in a region of system memory 3. Poll the DBGSTATUS Register to ensure that the debug is idle 4. Write to the DBGINST0 Register 5. Write to the DBGINST1 Register 6. Write zero to the DBGCMD Register so, we should make sure the debug is idle before step 4/5/6, not only step 6. if not, there maybe a risk that fail to write DBGINST0/1. Signed-off-by: Sugar Zhang Link: https://lore.kernel.org/r/1591234598-78919-1-git-send-email-sugar.zhang@rock-chips.com Signed-off-by: Vinod Koul --- drivers/dma/pl330.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/dma/pl330.c b/drivers/dma/pl330.c index 88b884cbb7c1b..6a158eef6b8ad 100644 --- a/drivers/dma/pl330.c +++ b/drivers/dma/pl330.c @@ -885,6 +885,12 @@ static inline void _execute_DBGINSN(struct pl330_thread *thrd, void __iomem *regs = thrd->dmac->base; u32 val; + /* If timed out due to halted state-machine */ + if (_until_dmac_idle(thrd)) { + dev_err(thrd->dmac->ddma.dev, "DMAC halted!\n"); + return; + } + val = (insn[0] << 16) | (insn[1] << 24); if (!as_manager) { val |= (1 << 0); @@ -895,12 +901,6 @@ static inline void _execute_DBGINSN(struct pl330_thread *thrd, val = le32_to_cpu(*((__le32 *)&insn[2])); writel(val, regs + DBGINST1); - /* If timed out due to halted state-machine */ - if (_until_dmac_idle(thrd)) { - dev_err(thrd->dmac->ddma.dev, "DMAC halted!\n"); - return; - } - /* Get going */ writel(0, regs + DBGCMD); } -- GitLab From d4db5721f3c847df43b967d9f02994b15e4a48e6 Mon Sep 17 00:00:00 2001 From: Martin Blumenstingl Date: Sat, 20 Jun 2020 18:14:22 +0200 Subject: [PATCH 0166/1476] clk: meson: meson8b: Drop CLK_IS_CRITICAL from fclk_div2 Drop CLK_IS_CRITICAL from fclk_div2. This was added because we didn't know the relation between this clock and RGMII Ethernet. It turns out that fclk_div2 is used as "timing adjustment clock" to generate the RX delay on the MAC side - which was enabled by u-boot on Odriod-C1. When using the RX delay on the PHY side or not using a RX delay at all then this clock can be disabled. Signed-off-by: Martin Blumenstingl Signed-off-by: Jerome Brunet Link: https://lore.kernel.org/r/20200620161422.24114-1-martin.blumenstingl@googlemail.com --- drivers/clk/meson/meson8b.c | 7 ------- 1 file changed, 7 deletions(-) diff --git a/drivers/clk/meson/meson8b.c b/drivers/clk/meson/meson8b.c index edc09d050ecf4..3d826711c820b 100644 --- a/drivers/clk/meson/meson8b.c +++ b/drivers/clk/meson/meson8b.c @@ -293,13 +293,6 @@ static struct clk_regmap meson8b_fclk_div2 = { &meson8b_fclk_div2_div.hw }, .num_parents = 1, - /* - * FIXME: Ethernet with a RGMII PHYs is not working if - * fclk_div2 is disabled. it is currently unclear why this - * is. keep it enabled until the Ethernet driver knows how - * to manage this clock. - */ - .flags = CLK_IS_CRITICAL, }, }; -- GitLab From 36c8673f90c8e244b10d1905eb08f4f61c033a25 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Fri, 21 Feb 2020 00:11:55 -0500 Subject: [PATCH 0167/1476] x86: copy_fpstate_to_sigframe(): have fpregs_soft_get() use kernel buffer ... then copy_to_user() the results Signed-off-by: Al Viro --- arch/x86/kernel/fpu/signal.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/arch/x86/kernel/fpu/signal.c b/arch/x86/kernel/fpu/signal.c index 9393a445d73c8..e0b832df74043 100644 --- a/arch/x86/kernel/fpu/signal.c +++ b/arch/x86/kernel/fpu/signal.c @@ -170,14 +170,14 @@ int copy_fpstate_to_sigframe(void __user *buf, void __user *buf_fx, int size) ia32_fxstate &= (IS_ENABLED(CONFIG_X86_32) || IS_ENABLED(CONFIG_IA32_EMULATION)); + if (!static_cpu_has(X86_FEATURE_FPU)) { + struct user_i387_ia32_struct fp; + fpregs_soft_get(current, NULL, 0, sizeof(fp), &fp, NULL); + return copy_to_user(buf, &fp, sizeof(fp)) ? -EFAULT : 0; + } + if (!access_ok(buf, size)) return -EACCES; - - if (!static_cpu_has(X86_FEATURE_FPU)) - return fpregs_soft_get(current, NULL, 0, - sizeof(struct user_i387_ia32_struct), NULL, - (struct _fpstate_32 __user *) buf) ? -1 : 1; - retry: /* * Load the FPU registers if they are not valid for the current task. -- GitLab From 4dfa103e82aefe15ac525c0d4853653a774be494 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sun, 7 Jun 2020 11:48:25 -0400 Subject: [PATCH 0168/1476] x86: kill dump_fpu() dead since the removal of aout coredump support... Signed-off-by: Al Viro --- arch/x86/include/asm/fpu/internal.h | 1 - arch/x86/kernel/fpu/regset.c | 16 ---------------- 2 files changed, 17 deletions(-) diff --git a/arch/x86/include/asm/fpu/internal.h b/arch/x86/include/asm/fpu/internal.h index 42159f45bf9c4..0c85a08176c03 100644 --- a/arch/x86/include/asm/fpu/internal.h +++ b/arch/x86/include/asm/fpu/internal.h @@ -34,7 +34,6 @@ extern int fpu__copy(struct task_struct *dst, struct task_struct *src); extern void fpu__clear_user_states(struct fpu *fpu); extern void fpu__clear_all(struct fpu *fpu); extern int fpu__exception_code(struct fpu *fpu, int trap_nr); -extern int dump_fpu(struct pt_regs *ptregs, struct user_i387_struct *fpstate); /* * Boot time FPU initialization functions: diff --git a/arch/x86/kernel/fpu/regset.c b/arch/x86/kernel/fpu/regset.c index bd1d0649f8cef..91f80aca27fbe 100644 --- a/arch/x86/kernel/fpu/regset.c +++ b/arch/x86/kernel/fpu/regset.c @@ -356,20 +356,4 @@ int fpregs_set(struct task_struct *target, const struct user_regset *regset, return ret; } -/* - * FPU state for core dumps. - * This is only used for a.out dumps now. - * It is declared generically using elf_fpregset_t (which is - * struct user_i387_struct) but is in fact only used for 32-bit - * dumps, so on 64-bit it is really struct user_i387_ia32_struct. - */ -int dump_fpu(struct pt_regs *regs, struct user_i387_struct *ufpu) -{ - struct task_struct *tsk = current; - - return !fpregs_get(tsk, NULL, 0, sizeof(struct user_i387_ia32_struct), - ufpu, NULL); -} -EXPORT_SYMBOL(dump_fpu); - #endif /* CONFIG_X86_32 || CONFIG_IA32_EMULATION */ -- GitLab From 4c35bf3ae948d6232f3d2c6fbf2008c341490683 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sat, 6 Jun 2020 17:55:02 -0400 Subject: [PATCH 0169/1476] [ia64] sanitize elf_access_gpreg() The function takes the register number, finds the corresponding field of pt_regs for registers that are saved there or does the unwind for the registers that end up spilled on the kernel stack. Then it reads from or writes to the resulting location. Unfortunately, finding the required pt_regs field is done by rather horrible switch. It's microoptimized in all the wrong places - it even uses the knowledge that fields for r8..r11 follow each other in pt_regs layout, while r12..r13 are not adjacent to those, etc. All of that is to encode the mapping from register numbers to offsets + the information that r4..r7 are not to be found in pt_regs. It's deeply in nasal demon territory, at that - the games it plays with pointer arithmetics on addresses of structure members are undefined behaviour. Valid C ends up with better code in this case: just initialize a constant array with offsets of relevant pt_regs fields and we don't need that switch anymore. Signed-off-by: Al Viro --- arch/ia64/kernel/ptrace.c | 57 ++++++++++++++++----------------------- 1 file changed, 23 insertions(+), 34 deletions(-) diff --git a/arch/ia64/kernel/ptrace.c b/arch/ia64/kernel/ptrace.c index 82aaacf64583e..b9d068903b983 100644 --- a/arch/ia64/kernel/ptrace.c +++ b/arch/ia64/kernel/ptrace.c @@ -1273,52 +1273,41 @@ struct regset_getset { int ret; }; +static const ptrdiff_t pt_offsets[16] = +{ +#define R(n) offsetof(struct pt_regs, r##n) + [0] = -1, R(1), R(2), R(3), + [4] = -1, [5] = -1, [6] = -1, [7] = -1, + R(8), R(9), R(10), R(11), R(12), R(13), R(14), R(15), +#undef R +}; + static int access_elf_gpreg(struct task_struct *target, struct unw_frame_info *info, unsigned long addr, unsigned long *data, int write_access) { - struct pt_regs *pt; - unsigned long *ptr = NULL; - int ret; - char nat = 0; + struct pt_regs *pt = task_pt_regs(target); + unsigned reg = addr / sizeof(unsigned long); + ptrdiff_t d = pt_offsets[reg]; - pt = task_pt_regs(target); - switch (addr) { - case ELF_GR_OFFSET(1): - ptr = &pt->r1; - break; - case ELF_GR_OFFSET(2): - case ELF_GR_OFFSET(3): - ptr = (void *)&pt->r2 + (addr - ELF_GR_OFFSET(2)); - break; - case ELF_GR_OFFSET(4) ... ELF_GR_OFFSET(7): + if (d >= 0) { + unsigned long *ptr = (void *)pt + d; + if (write_access) + *ptr = *data; + else + *data = *ptr; + return 0; + } else { + char nat = 0; if (write_access) { /* read NaT bit first: */ unsigned long dummy; - - ret = unw_get_gr(info, addr/8, &dummy, &nat); + int ret = unw_get_gr(info, reg, &dummy, &nat); if (ret < 0) return ret; } - return unw_access_gr(info, addr/8, data, &nat, write_access); - case ELF_GR_OFFSET(8) ... ELF_GR_OFFSET(11): - ptr = (void *)&pt->r8 + addr - ELF_GR_OFFSET(8); - break; - case ELF_GR_OFFSET(12): - case ELF_GR_OFFSET(13): - ptr = (void *)&pt->r12 + addr - ELF_GR_OFFSET(12); - break; - case ELF_GR_OFFSET(14): - ptr = &pt->r14; - break; - case ELF_GR_OFFSET(15): - ptr = &pt->r15; + return unw_access_gr(info, reg, data, &nat, write_access); } - if (write_access) - *ptr = *data; - else - *data = *ptr; - return 0; } static int -- GitLab From e2115cf3cc57fc4b3f6b0ea4f746e5ab6dc9c2c0 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sat, 6 Jun 2020 17:55:02 -0400 Subject: [PATCH 0170/1476] [ia64] teach elf_access_reg() to handle the missing range (r16..r31) Now it's easy to make elf_access_gpreg() handle the rest of global registers (r16..r31). That gets rid of the hole in the registers elf_access_reg() can handle, which will allow to simplify its callers later in the series. Signed-off-by: Al Viro --- arch/ia64/kernel/ptrace.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/arch/ia64/kernel/ptrace.c b/arch/ia64/kernel/ptrace.c index b9d068903b983..37b5140d0dfe8 100644 --- a/arch/ia64/kernel/ptrace.c +++ b/arch/ia64/kernel/ptrace.c @@ -1273,12 +1273,14 @@ struct regset_getset { int ret; }; -static const ptrdiff_t pt_offsets[16] = +static const ptrdiff_t pt_offsets[32] = { #define R(n) offsetof(struct pt_regs, r##n) [0] = -1, R(1), R(2), R(3), [4] = -1, [5] = -1, [6] = -1, [7] = -1, R(8), R(9), R(10), R(11), R(12), R(13), R(14), R(15), + R(16), R(17), R(18), R(19), R(20), R(21), R(22), R(23), + R(24), R(25), R(26), R(27), R(28), R(29), R(30), R(31), #undef R }; @@ -1479,7 +1481,7 @@ static int access_elf_reg(struct task_struct *target, struct unw_frame_info *info, unsigned long addr, unsigned long *data, int write_access) { - if (addr >= ELF_GR_OFFSET(1) && addr <= ELF_GR_OFFSET(15)) + if (addr >= ELF_GR_OFFSET(1) && addr <= ELF_GR_OFFSET(31)) return access_elf_gpreg(target, info, addr, data, write_access); else if (addr >= ELF_BR_OFFSET(0) && addr <= ELF_BR_OFFSET(7)) return access_elf_breg(target, info, addr, data, write_access); -- GitLab From a79ca8e7b9d56c762c3cd53465fde62f8ca41acf Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sat, 6 Jun 2020 18:00:56 -0400 Subject: [PATCH 0171/1476] [ia64] regularize do_gpregs_[gs]et() now access_elf_reg() does the right thing for everything other than r0, we can simplify do_grepgs_[gs]et() Signed-off-by: Al Viro --- arch/ia64/kernel/ptrace.c | 155 ++++++++------------------------------ 1 file changed, 31 insertions(+), 124 deletions(-) diff --git a/arch/ia64/kernel/ptrace.c b/arch/ia64/kernel/ptrace.c index 37b5140d0dfe8..be635f6f93c9f 100644 --- a/arch/ia64/kernel/ptrace.c +++ b/arch/ia64/kernel/ptrace.c @@ -1491,10 +1491,7 @@ access_elf_reg(struct task_struct *target, struct unw_frame_info *info, void do_gpregs_get(struct unw_frame_info *info, void *arg) { - struct pt_regs *pt; struct regset_getset *dst = arg; - elf_greg_t tmp[16]; - unsigned int i, index, min_copy; if (unw_unwind_to_user(info) < 0) return; @@ -1517,160 +1514,70 @@ void do_gpregs_get(struct unw_frame_info *info, void *arg) &dst->u.get.kbuf, &dst->u.get.ubuf, 0, ELF_GR_OFFSET(1)); - if (dst->ret || dst->count == 0) - return; - } - - /* gr1 - gr15 */ - if (dst->count > 0 && dst->pos < ELF_GR_OFFSET(16)) { - index = (dst->pos - ELF_GR_OFFSET(1)) / sizeof(elf_greg_t); - min_copy = ELF_GR_OFFSET(16) > (dst->pos + dst->count) ? - (dst->pos + dst->count) : ELF_GR_OFFSET(16); - for (i = dst->pos; i < min_copy; i += sizeof(elf_greg_t), - index++) - if (access_elf_reg(dst->target, info, i, - &tmp[index], 0) < 0) { - dst->ret = -EIO; - return; - } - dst->ret = user_regset_copyout(&dst->pos, &dst->count, - &dst->u.get.kbuf, &dst->u.get.ubuf, tmp, - ELF_GR_OFFSET(1), ELF_GR_OFFSET(16)); - if (dst->ret || dst->count == 0) - return; - } - - /* r16-r31 */ - if (dst->count > 0 && dst->pos < ELF_NAT_OFFSET) { - pt = task_pt_regs(dst->target); - dst->ret = user_regset_copyout(&dst->pos, &dst->count, - &dst->u.get.kbuf, &dst->u.get.ubuf, &pt->r16, - ELF_GR_OFFSET(16), ELF_NAT_OFFSET); - if (dst->ret || dst->count == 0) + if (dst->ret) return; } - /* nat, pr, b0 - b7 */ - if (dst->count > 0 && dst->pos < ELF_CR_IIP_OFFSET) { - index = (dst->pos - ELF_NAT_OFFSET) / sizeof(elf_greg_t); - min_copy = ELF_CR_IIP_OFFSET > (dst->pos + dst->count) ? - (dst->pos + dst->count) : ELF_CR_IIP_OFFSET; - for (i = dst->pos; i < min_copy; i += sizeof(elf_greg_t), - index++) - if (access_elf_reg(dst->target, info, i, - &tmp[index], 0) < 0) { + while (dst->count && dst->pos < ELF_AR_END_OFFSET) { + unsigned int n, from, to; + elf_greg_t tmp[16]; + + from = dst->pos; + to = from + min(dst->count, (unsigned)sizeof(tmp)); + if (to > ELF_AR_END_OFFSET) + to = ELF_AR_END_OFFSET; + for (n = 0; from < to; from += sizeof(elf_greg_t), n++) { + if (access_elf_reg(dst->target, info, from, + &tmp[n], 0) < 0) { dst->ret = -EIO; return; } + } dst->ret = user_regset_copyout(&dst->pos, &dst->count, &dst->u.get.kbuf, &dst->u.get.ubuf, tmp, - ELF_NAT_OFFSET, ELF_CR_IIP_OFFSET); - if (dst->ret || dst->count == 0) + dst->pos, to); + if (dst->ret) return; } - - /* ip cfm psr ar.rsc ar.bsp ar.bspstore ar.rnat - * ar.ccv ar.unat ar.fpsr ar.pfs ar.lc ar.ec ar.csd ar.ssd - */ - if (dst->count > 0 && dst->pos < (ELF_AR_END_OFFSET)) { - index = (dst->pos - ELF_CR_IIP_OFFSET) / sizeof(elf_greg_t); - min_copy = ELF_AR_END_OFFSET > (dst->pos + dst->count) ? - (dst->pos + dst->count) : ELF_AR_END_OFFSET; - for (i = dst->pos; i < min_copy; i += sizeof(elf_greg_t), - index++) - if (access_elf_reg(dst->target, info, i, - &tmp[index], 0) < 0) { - dst->ret = -EIO; - return; - } - dst->ret = user_regset_copyout(&dst->pos, &dst->count, - &dst->u.get.kbuf, &dst->u.get.ubuf, tmp, - ELF_CR_IIP_OFFSET, ELF_AR_END_OFFSET); - } } void do_gpregs_set(struct unw_frame_info *info, void *arg) { - struct pt_regs *pt; struct regset_getset *dst = arg; - elf_greg_t tmp[16]; - unsigned int i, index; if (unw_unwind_to_user(info) < 0) return; + if (!dst->count) + return; /* Skip r0 */ - if (dst->count > 0 && dst->pos < ELF_GR_OFFSET(1)) { + if (dst->pos < ELF_GR_OFFSET(1)) { dst->ret = user_regset_copyin_ignore(&dst->pos, &dst->count, &dst->u.set.kbuf, &dst->u.set.ubuf, 0, ELF_GR_OFFSET(1)); - if (dst->ret || dst->count == 0) - return; - } - - /* gr1-gr15 */ - if (dst->count > 0 && dst->pos < ELF_GR_OFFSET(16)) { - i = dst->pos; - index = (dst->pos - ELF_GR_OFFSET(1)) / sizeof(elf_greg_t); - dst->ret = user_regset_copyin(&dst->pos, &dst->count, - &dst->u.set.kbuf, &dst->u.set.ubuf, tmp, - ELF_GR_OFFSET(1), ELF_GR_OFFSET(16)); if (dst->ret) return; - for ( ; i < dst->pos; i += sizeof(elf_greg_t), index++) - if (access_elf_reg(dst->target, info, i, - &tmp[index], 1) < 0) { - dst->ret = -EIO; - return; - } - if (dst->count == 0) - return; - } - - /* gr16-gr31 */ - if (dst->count > 0 && dst->pos < ELF_NAT_OFFSET) { - pt = task_pt_regs(dst->target); - dst->ret = user_regset_copyin(&dst->pos, &dst->count, - &dst->u.set.kbuf, &dst->u.set.ubuf, &pt->r16, - ELF_GR_OFFSET(16), ELF_NAT_OFFSET); - if (dst->ret || dst->count == 0) - return; } - /* nat, pr, b0 - b7 */ - if (dst->count > 0 && dst->pos < ELF_CR_IIP_OFFSET) { - i = dst->pos; - index = (dst->pos - ELF_NAT_OFFSET) / sizeof(elf_greg_t); - dst->ret = user_regset_copyin(&dst->pos, &dst->count, - &dst->u.set.kbuf, &dst->u.set.ubuf, tmp, - ELF_NAT_OFFSET, ELF_CR_IIP_OFFSET); - if (dst->ret) - return; - for (; i < dst->pos; i += sizeof(elf_greg_t), index++) - if (access_elf_reg(dst->target, info, i, - &tmp[index], 1) < 0) { - dst->ret = -EIO; - return; - } - if (dst->count == 0) - return; - } + while (dst->count && dst->pos < ELF_AR_END_OFFSET) { + unsigned int n, from, to; + elf_greg_t tmp[16]; - /* ip cfm psr ar.rsc ar.bsp ar.bspstore ar.rnat - * ar.ccv ar.unat ar.fpsr ar.pfs ar.lc ar.ec ar.csd ar.ssd - */ - if (dst->count > 0 && dst->pos < (ELF_AR_END_OFFSET)) { - i = dst->pos; - index = (dst->pos - ELF_CR_IIP_OFFSET) / sizeof(elf_greg_t); + from = dst->pos; + to = from + sizeof(tmp); + if (to > ELF_AR_END_OFFSET) + to = ELF_AR_END_OFFSET; + /* get up to 16 values */ dst->ret = user_regset_copyin(&dst->pos, &dst->count, &dst->u.set.kbuf, &dst->u.set.ubuf, tmp, - ELF_CR_IIP_OFFSET, ELF_AR_END_OFFSET); + from, to); if (dst->ret) return; - for ( ; i < dst->pos; i += sizeof(elf_greg_t), index++) - if (access_elf_reg(dst->target, info, i, - &tmp[index], 1) < 0) { + /* now copy them into registers */ + for (n = 0; from < dst->pos; from += sizeof(elf_greg_t), n++) + if (access_elf_reg(dst->target, info, from, + &tmp[n], 1) < 0) { dst->ret = -EIO; return; } -- GitLab From 6bc4f16c6c9bed6ce5c3ab77b95397c8c88bdb66 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sat, 6 Jun 2020 18:46:45 -0400 Subject: [PATCH 0172/1476] [ia64] access_uarea(): stop bothering with gpregs_[gs]et() We know this won't be called for child == current, so we don't need to bother with callbacks, etc. - just do unw_init_from_blocked_task(), unw_unwind_to_user() and do the payload of gpregs_[gs]et(). For one register. Which is to say, access_elf_reg(). Signed-off-by: Al Viro --- arch/ia64/kernel/ptrace.c | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/arch/ia64/kernel/ptrace.c b/arch/ia64/kernel/ptrace.c index be635f6f93c9f..e0813c8e4b47f 100644 --- a/arch/ia64/kernel/ptrace.c +++ b/arch/ia64/kernel/ptrace.c @@ -1936,15 +1936,14 @@ access_uarea(struct task_struct *child, unsigned long addr, } if (pos != -1) { - if (write_access) - ret = gpregs_set(child, NULL, pos, - sizeof(unsigned long), data, NULL); - else - ret = gpregs_get(child, NULL, pos, - sizeof(unsigned long), data, NULL); - if (ret != 0) - return -1; - return 0; + struct unw_frame_info info; + + memset(&info, 0, sizeof(info)); + unw_init_from_blocked_task(&info, child); + if (unw_unwind_to_user(&info) < 0) + return 0; + + return access_elf_reg(child, &info, pos, data, write_access); } /* access debug registers */ -- GitLab From e3fdfa37a3fabea38b01af05893050136a6cd136 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sat, 6 Jun 2020 20:22:32 -0400 Subject: [PATCH 0173/1476] [ia64] access_uarea(): don't bother with fpregs_[gs]et() similar to previous commit... Signed-off-by: Al Viro --- arch/ia64/kernel/ptrace.c | 42 ++++++++++++++++++++++++++++++--------- 1 file changed, 33 insertions(+), 9 deletions(-) diff --git a/arch/ia64/kernel/ptrace.c b/arch/ia64/kernel/ptrace.c index e0813c8e4b47f..eae7b094f6082 100644 --- a/arch/ia64/kernel/ptrace.c +++ b/arch/ia64/kernel/ptrace.c @@ -1811,7 +1811,6 @@ access_uarea(struct task_struct *child, unsigned long addr, unsigned long *data, int write_access) { unsigned int pos = -1; /* an invalid value */ - int ret; unsigned long *ptr, regnum; if ((addr & 0x7) != 0) { @@ -1843,14 +1842,39 @@ access_uarea(struct task_struct *child, unsigned long addr, } if (pos != -1) { - if (write_access) - ret = fpregs_set(child, NULL, pos, - sizeof(unsigned long), data, NULL); - else - ret = fpregs_get(child, NULL, pos, - sizeof(unsigned long), data, NULL); - if (ret != 0) - return -1; + unsigned reg = pos / sizeof(elf_fpreg_t); + int which_half = (pos / sizeof(unsigned long)) & 1; + + if (reg < 32) { /* fr2-fr31 */ + struct unw_frame_info info; + elf_fpreg_t fpreg; + + memset(&info, 0, sizeof(info)); + unw_init_from_blocked_task(&info, child); + if (unw_unwind_to_user(&info) < 0) + return 0; + + if (unw_get_fr(&info, reg, &fpreg)) + return -1; + if (write_access) { + fpreg.u.bits[which_half] = *data; + if (unw_set_fr(&info, reg, fpreg)) + return -1; + } else { + *data = fpreg.u.bits[which_half]; + } + } else { /* fph */ + elf_fpreg_t *p = &child->thread.fph[reg - 32]; + unsigned long *bits = &p->u.bits[which_half]; + + ia64_sync_fph(child); + if (write_access) + *bits = *data; + else if (child->thread.flags & IA64_THREAD_FPH_VALID) + *data = *bits; + else + *data = 0; + } return 0; } -- GitLab From 030754c99538ef83ba6aaf1cdfe33ec4fbbc0c0f Mon Sep 17 00:00:00 2001 From: Al Viro Date: Fri, 22 May 2020 12:51:42 -0400 Subject: [PATCH 0174/1476] sparc64: switch genregs32_get() to use of get_from_target() ... for fetching the register window from target's stack, rather than open-coding it. Signed-off-by: Al Viro --- arch/sparc/kernel/ptrace_64.c | 59 ++++++++++------------------------- 1 file changed, 16 insertions(+), 43 deletions(-) diff --git a/arch/sparc/kernel/ptrace_64.c b/arch/sparc/kernel/ptrace_64.c index 7122efb4b1ccd..f7b2ddfc81d66 100644 --- a/arch/sparc/kernel/ptrace_64.c +++ b/arch/sparc/kernel/ptrace_64.c @@ -518,10 +518,10 @@ static int genregs32_get(struct task_struct *target, void *kbuf, void __user *ubuf) { const struct pt_regs *regs = task_pt_regs(target); - compat_ulong_t __user *reg_window; compat_ulong_t *k = kbuf; compat_ulong_t __user *u = ubuf; - compat_ulong_t reg; + u32 uregs[16]; + u32 reg; if (target == current) flushw_user(); @@ -533,52 +533,25 @@ static int genregs32_get(struct task_struct *target, for (; count > 0 && pos < 16; count--) *k++ = regs->u_regs[pos++]; - reg_window = (compat_ulong_t __user *) regs->u_regs[UREG_I6]; - reg_window -= 16; - if (target == current) { - for (; count > 0 && pos < 32; count--) { - if (get_user(*k++, ®_window[pos++])) - return -EFAULT; - } - } else { - for (; count > 0 && pos < 32; count--) { - if (access_process_vm(target, - (unsigned long) - ®_window[pos], - k, sizeof(*k), - FOLL_FORCE) - != sizeof(*k)) - return -EFAULT; - k++; - pos++; - } + if (count && pos < 32) { + if (get_from_target(target, regs->u_regs[UREG_I6], + uregs, sizeof(uregs))) + return -EFAULT; + for (; count > 0 && pos < 32; count--) + *k++ = uregs[pos++ - 16]; + } } else { - for (; count > 0 && pos < 16; count--) { + for (; count > 0 && pos < 16; count--) if (put_user((compat_ulong_t) regs->u_regs[pos++], u++)) return -EFAULT; - } - - reg_window = (compat_ulong_t __user *) regs->u_regs[UREG_I6]; - reg_window -= 16; - if (target == current) { - for (; count > 0 && pos < 32; count--) { - if (get_user(reg, ®_window[pos++]) || - put_user(reg, u++)) - return -EFAULT; - } - } else { - for (; count > 0 && pos < 32; count--) { - if (access_process_vm(target, - (unsigned long) - ®_window[pos++], - ®, sizeof(reg), - FOLL_FORCE) - != sizeof(reg)) - return -EFAULT; - if (put_user(reg, u++)) + if (count && pos < 32) { + if (get_from_target(target, regs->u_regs[UREG_I6], + uregs, sizeof(uregs))) + return -EFAULT; + for (; count > 0 && pos < 32; count--) + if (put_user(uregs[pos++ - 16], u++)) return -EFAULT; - } } } while (count > 0) { -- GitLab From 87d805331a69afdd7b3ee3d5c42760b747a4904f Mon Sep 17 00:00:00 2001 From: Al Viro Date: Fri, 22 May 2020 13:19:47 -0400 Subject: [PATCH 0175/1476] sparc32: get rid of odd callers of copy_regset_to_user() the life is much simpler if copy_regset_to_user() (and ->get()) gets called only with pos == 0; sparc32 PTRACE_GETREGS and PTRACE_GETFPREGS are among the few things that use it to fetch pieces of regset _not_ starting at the beginning. It's actually easier to define a separate regset that would provide what we need, rather than trying to cobble that from the one PTRACE_GETREGSET uses. Extra ->get() instances do not amount to much code and once we get the conversion of ->get() to new API (dependent upon the lack of weird callers of ->get()) they'll shrink a lot, along with the rest of ->get() instances... Signed-off-by: Al Viro --- arch/sparc/kernel/ptrace_32.c | 117 ++++++++++++++++++++++++---------- 1 file changed, 82 insertions(+), 35 deletions(-) diff --git a/arch/sparc/kernel/ptrace_32.c b/arch/sparc/kernel/ptrace_32.c index 47eb315d411ce..f72b7d2c4716d 100644 --- a/arch/sparc/kernel/ptrace_32.c +++ b/arch/sparc/kernel/ptrace_32.c @@ -99,15 +99,13 @@ static int genregs32_get(struct task_struct *target, if (ret || !count) return ret; - if (pos < 32 * sizeof(u32)) { - if (regwindow32_get(target, regs, uregs)) - return -EFAULT; - ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, - uregs, - 16 * sizeof(u32), 32 * sizeof(u32)); - if (ret || !count) - return ret; - } + if (regwindow32_get(target, regs, uregs)) + return -EFAULT; + ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, + uregs, + 16 * sizeof(u32), 32 * sizeof(u32)); + if (ret) + return ret; uregs[0] = regs->psr; uregs[1] = regs->pc; @@ -288,6 +286,73 @@ static const struct user_regset sparc32_regsets[] = { }, }; +static int getregs_get(struct task_struct *target, + const struct user_regset *regset, + unsigned int pos, unsigned int count, + void *kbuf, void __user *ubuf) +{ + const struct pt_regs *regs = target->thread.kregs; + u32 v[4]; + int ret; + + if (target == current) + flush_user_windows(); + + v[0] = regs->psr; + v[1] = regs->pc; + v[2] = regs->npc; + v[3] = regs->y; + ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, + v, + 0 * sizeof(u32), 4 * sizeof(u32)); + if (ret) + return ret; + + return user_regset_copyout(&pos, &count, &kbuf, &ubuf, + regs->u_regs + 1, + 4 * sizeof(u32), 19 * sizeof(u32)); +} + +static int getfpregs_get(struct task_struct *target, + const struct user_regset *regset, + unsigned int pos, unsigned int count, + void *kbuf, void __user *ubuf) +{ + const unsigned long *fpregs = target->thread.float_regs; + int ret = 0; + +#if 0 + if (target == current) + save_and_clear_fpu(); +#endif + + ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, + fpregs, + 0, 32 * sizeof(u32)); + if (ret) + return ret; + ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, + &target->thread.fsr, + 32 * sizeof(u32), 33 * sizeof(u32)); + if (ret) + return ret; + return user_regset_copyout_zero(&pos, &count, &kbuf, &ubuf, + 33 * sizeof(u32), 68 * sizeof(u32)); +} + +static const struct user_regset ptrace32_regsets[] = { + [REGSET_GENERAL] = { + .n = 19, .size = sizeof(u32), .get = getregs_get, + }, + [REGSET_FP] = { + .n = 68, .size = sizeof(u32), .get = getfpregs_get, + }, +}; + +static const struct user_regset_view ptrace32_view = { + .regsets = ptrace32_regsets, .n = ARRAY_SIZE(ptrace32_regsets) +}; + static const struct user_regset_view user_sparc32_view = { .name = "sparc", .e_machine = EM_SPARC, .regsets = sparc32_regsets, .n = ARRAY_SIZE(sparc32_regsets) @@ -327,15 +392,10 @@ long arch_ptrace(struct task_struct *child, long request, switch(request) { case PTRACE_GETREGS: { - ret = copy_regset_to_user(child, view, REGSET_GENERAL, - 32 * sizeof(u32), - 4 * sizeof(u32), - &pregs->psr); - if (!ret) - copy_regset_to_user(child, view, REGSET_GENERAL, - 1 * sizeof(u32), - 15 * sizeof(u32), - &pregs->u_regs[0]); + ret = copy_regset_to_user(child, &ptrace32_view, + REGSET_GENERAL, 0, + 19 * sizeof(u32), + pregs); break; } @@ -353,23 +413,10 @@ long arch_ptrace(struct task_struct *child, long request, } case PTRACE_GETFPREGS: { - ret = copy_regset_to_user(child, view, REGSET_FP, - 0 * sizeof(u32), - 32 * sizeof(u32), - &fps->regs[0]); - if (!ret) - ret = copy_regset_to_user(child, view, REGSET_FP, - 33 * sizeof(u32), - 1 * sizeof(u32), - &fps->fsr); - - if (!ret) { - if (__put_user(0, &fps->fpqd) || - __put_user(0, &fps->flags) || - __put_user(0, &fps->extra) || - clear_user(fps->fpq, sizeof(fps->fpq))) - ret = -EFAULT; - } + ret = copy_regset_to_user(child, &ptrace32_view, + REGSET_FP, 0, + 68 * sizeof(u32), + fps); break; } -- GitLab From 8f0329211b7001a47f94702380d188295eba8ef2 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Fri, 22 May 2020 14:21:59 -0400 Subject: [PATCH 0176/1476] sparc64: get rid of odd callers of copy_regset_to_user() same as for sparc32, and that's it - no more caller of ->get() with non-zero pos. Signed-off-by: Al Viro --- arch/sparc/kernel/ptrace_64.c | 175 ++++++++++++++++++++++++++-------- 1 file changed, 136 insertions(+), 39 deletions(-) diff --git a/arch/sparc/kernel/ptrace_64.c b/arch/sparc/kernel/ptrace_64.c index f7b2ddfc81d66..1b1910b67ca4e 100644 --- a/arch/sparc/kernel/ptrace_64.c +++ b/arch/sparc/kernel/ptrace_64.c @@ -258,7 +258,7 @@ static int genregs64_get(struct task_struct *target, ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, regs->u_regs, 0, 16 * sizeof(u64)); - if (!ret && count && pos < (32 * sizeof(u64))) { + if (!ret && count) { struct reg_window window; if (regwindow64_get(target, regs, &window)) @@ -506,6 +506,57 @@ static const struct user_regset sparc64_regsets[] = { }, }; +static int getregs64_get(struct task_struct *target, + const struct user_regset *regset, + unsigned int pos, unsigned int count, + void *kbuf, void __user *ubuf) +{ + const struct pt_regs *regs = task_pt_regs(target); + int ret; + + if (target == current) + flushw_user(); + + ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, + regs->u_regs + 1, + 0, 15 * sizeof(u64)); + if (!ret) + ret = user_regset_copyout_zero(&pos, &count, &kbuf, &ubuf, + 15 * sizeof(u64), 16 * sizeof(u64)); + if (!ret) { + /* TSTATE, TPC, TNPC */ + ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, + ®s->tstate, + 16 * sizeof(u64), + 19 * sizeof(u64)); + } + if (!ret) { + unsigned long y = regs->y; + + ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, + &y, + 19 * sizeof(u64), + 20 * sizeof(u64)); + } + return ret; +} + +static const struct user_regset ptrace64_regsets[] = { + /* Format is: + * G1 --> G7 + * O0 --> O7 + * 0 + * TSTATE, TPC, TNPC, Y + */ + [REGSET_GENERAL] = { + .n = 20, .size = sizeof(u64), .get = getregs64_get, + }, +}; + +static const struct user_regset_view ptrace64_view = { + .regsets = ptrace64_regsets, .n = ARRAY_SIZE(ptrace64_regsets) +}; + static const struct user_regset_view user_sparc64_view = { .name = "sparc64", .e_machine = EM_SPARCV9, .regsets = sparc64_regsets, .n = ARRAY_SIZE(sparc64_regsets) @@ -533,7 +584,7 @@ static int genregs32_get(struct task_struct *target, for (; count > 0 && pos < 16; count--) *k++ = regs->u_regs[pos++]; - if (count && pos < 32) { + if (count) { if (get_from_target(target, regs->u_regs[UREG_I6], uregs, sizeof(uregs))) return -EFAULT; @@ -545,7 +596,7 @@ static int genregs32_get(struct task_struct *target, for (; count > 0 && pos < 16; count--) if (put_user((compat_ulong_t) regs->u_regs[pos++], u++)) return -EFAULT; - if (count && pos < 32) { + if (count) { if (get_from_target(target, regs->u_regs[UREG_I6], uregs, sizeof(uregs))) return -EFAULT; @@ -840,6 +891,76 @@ static const struct user_regset sparc32_regsets[] = { }, }; +static int getregs_get(struct task_struct *target, + const struct user_regset *regset, + unsigned int pos, unsigned int count, + void *kbuf, void __user *ubuf) +{ + const struct pt_regs *regs = task_pt_regs(target); + u32 uregs[19]; + int i; + + if (target == current) + flushw_user(); + + uregs[0] = tstate_to_psr(regs->tstate); + uregs[1] = regs->tpc; + uregs[2] = regs->tnpc; + uregs[3] = regs->y; + for (i = 1; i < 16; i++) + uregs[3 + i] = regs->u_regs[i]; + return user_regset_copyout(&pos, &count, &kbuf, &ubuf, + uregs, + 0, 19 * sizeof(u32)); +} + +static int getfpregs_get(struct task_struct *target, + const struct user_regset *regset, + unsigned int pos, unsigned int count, + void *kbuf, void __user *ubuf) +{ + const unsigned long *fpregs = task_thread_info(target)->fpregs; + unsigned long fprs; + compat_ulong_t fsr; + int ret = 0; + + if (target == current) + save_and_clear_fpu(); + + fprs = task_thread_info(target)->fpsaved[0]; + if (fprs & FPRS_FEF) { + fsr = task_thread_info(target)->xfsr[0]; + } else { + fsr = 0; + } + + ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, + fpregs, + 0, 32 * sizeof(u32)); + if (!ret) + ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, + &fsr, + 32 * sizeof(u32), + 33 * sizeof(u32)); + if (!ret) + ret = user_regset_copyout_zero(&pos, &count, &kbuf, &ubuf, + 33 * sizeof(u32), 68 * sizeof(u32)); + return ret; +} + +static const struct user_regset ptrace32_regsets[] = { + [REGSET_GENERAL] = { + .n = 19, .size = sizeof(u32), .get = getregs_get, + }, + [REGSET_FP] = { + .n = 68, .size = sizeof(u32), .get = getfpregs_get, + }, +}; + +static const struct user_regset_view ptrace32_view = { + .regsets = ptrace32_regsets, .n = ARRAY_SIZE(ptrace32_regsets) +}; + static const struct user_regset_view user_sparc32_view = { .name = "sparc", .e_machine = EM_SPARC, .regsets = sparc32_regsets, .n = ARRAY_SIZE(sparc32_regsets) @@ -889,15 +1010,10 @@ long compat_arch_ptrace(struct task_struct *child, compat_long_t request, break; case PTRACE_GETREGS: - ret = copy_regset_to_user(child, view, REGSET_GENERAL, - 32 * sizeof(u32), - 4 * sizeof(u32), - &pregs->psr); - if (!ret) - ret = copy_regset_to_user(child, view, REGSET_GENERAL, - 1 * sizeof(u32), - 15 * sizeof(u32), - &pregs->u_regs[0]); + ret = copy_regset_to_user(child, &ptrace32_view, + REGSET_GENERAL, 0, + 19 * sizeof(u32), + pregs); break; case PTRACE_SETREGS: @@ -913,22 +1029,10 @@ long compat_arch_ptrace(struct task_struct *child, compat_long_t request, break; case PTRACE_GETFPREGS: - ret = copy_regset_to_user(child, view, REGSET_FP, - 0 * sizeof(u32), - 32 * sizeof(u32), - &fps->regs[0]); - if (!ret) - ret = copy_regset_to_user(child, view, REGSET_FP, - 33 * sizeof(u32), - 1 * sizeof(u32), - &fps->fsr); - if (!ret) { - if (__put_user(0, &fps->flags) || - __put_user(0, &fps->extra) || - __put_user(0, &fps->fpqd) || - clear_user(&fps->fpq[0], 32 * sizeof(unsigned int))) - ret = -EFAULT; - } + ret = copy_regset_to_user(child, &ptrace32_view, + REGSET_FP, 0, + 68 * sizeof(u32), + fps); break; case PTRACE_SETFPREGS: @@ -999,17 +1103,10 @@ long arch_ptrace(struct task_struct *child, long request, break; case PTRACE_GETREGS64: - ret = copy_regset_to_user(child, view, REGSET_GENERAL, - 1 * sizeof(u64), - 15 * sizeof(u64), - &pregs->u_regs[0]); - if (!ret) { - /* XXX doesn't handle 'y' register correctly XXX */ - ret = copy_regset_to_user(child, view, REGSET_GENERAL, - 32 * sizeof(u64), - 4 * sizeof(u64), - &pregs->tstate); - } + ret = copy_regset_to_user(child, &ptrace64_view, + REGSET_GENERAL, 0, + 19 * sizeof(u64), + pregs); break; case PTRACE_SETREGS64: -- GitLab From a96dacf98884151f6a738a98aab50f35391455ca Mon Sep 17 00:00:00 2001 From: Al Viro Date: Fri, 15 May 2020 11:22:26 -0400 Subject: [PATCH 0177/1476] arm64: take fetching compat reg out of pt_regs into a new helper Signed-off-by: Al Viro --- arch/arm64/kernel/ptrace.c | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/arch/arm64/kernel/ptrace.c b/arch/arm64/kernel/ptrace.c index 68b7f34a08f54..5b837741ab769 100644 --- a/arch/arm64/kernel/ptrace.c +++ b/arch/arm64/kernel/ptrace.c @@ -1237,6 +1237,22 @@ enum compat_regset { REGSET_COMPAT_VFP, }; +static inline compat_ulong_t compat_get_user_reg(struct task_struct *task, int idx) +{ + struct pt_regs *regs = task_pt_regs(task); + + switch (idx) { + case 15: + return regs->pc; + case 16: + return pstate_to_compat_psr(regs->pstate); + case 17: + return regs->orig_x0; + default: + return regs->regs[idx]; + } +} + static int compat_gpr_get(struct task_struct *target, const struct user_regset *regset, unsigned int pos, unsigned int count, @@ -1255,23 +1271,7 @@ static int compat_gpr_get(struct task_struct *target, return -EIO; for (i = 0; i < num_regs; ++i) { - unsigned int idx = start + i; - compat_ulong_t reg; - - switch (idx) { - case 15: - reg = task_pt_regs(target)->pc; - break; - case 16: - reg = task_pt_regs(target)->pstate; - reg = pstate_to_compat_psr(reg); - break; - case 17: - reg = task_pt_regs(target)->orig_x0; - break; - default: - reg = task_pt_regs(target)->regs[idx]; - } + compat_ulong_t reg = compat_get_user_reg(target, start + i); if (kbuf) { memcpy(kbuf, ®, sizeof(reg)); -- GitLab From b44f384074d09d406b9223d60905e4badadc4741 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Fri, 15 May 2020 11:26:08 -0400 Subject: [PATCH 0178/1476] arm64: get rid of copy_regset_to_user() in compat_ptrace_read_user() Signed-off-by: Al Viro --- arch/arm64/kernel/ptrace.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/arch/arm64/kernel/ptrace.c b/arch/arm64/kernel/ptrace.c index 5b837741ab769..d5f3da5197a14 100644 --- a/arch/arm64/kernel/ptrace.c +++ b/arch/arm64/kernel/ptrace.c @@ -1541,9 +1541,7 @@ static int compat_ptrace_read_user(struct task_struct *tsk, compat_ulong_t off, else if (off == COMPAT_PT_TEXT_END_ADDR) tmp = tsk->mm->end_code; else if (off < sizeof(compat_elf_gregset_t)) - return copy_regset_to_user(tsk, &user_aarch32_view, - REGSET_COMPAT_GPR, off, - sizeof(compat_ulong_t), ret); + tmp = compat_get_user_reg(tsk, off >> 2); else if (off >= COMPAT_USER_SZ) return -EIO; else -- GitLab From d547175b54fa8104b0e2e5fd4b372cf04fa031ab Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sat, 6 Jun 2020 21:19:58 -0400 Subject: [PATCH 0179/1476] arm64: sanitize compat_ptrace_write_user() don't bother with copy_regset_from_user() (not to mention set_fs()) Signed-off-by: Al Viro --- arch/arm64/kernel/ptrace.c | 29 ++++++++++++++++++++--------- 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/arch/arm64/kernel/ptrace.c b/arch/arm64/kernel/ptrace.c index d5f3da5197a14..9f769e862f687 100644 --- a/arch/arm64/kernel/ptrace.c +++ b/arch/arm64/kernel/ptrace.c @@ -1553,8 +1553,8 @@ static int compat_ptrace_read_user(struct task_struct *tsk, compat_ulong_t off, static int compat_ptrace_write_user(struct task_struct *tsk, compat_ulong_t off, compat_ulong_t val) { - int ret; - mm_segment_t old_fs = get_fs(); + struct pt_regs newregs = *task_pt_regs(tsk); + unsigned int idx = off / 4; if (off & 3 || off >= COMPAT_USER_SZ) return -EIO; @@ -1562,14 +1562,25 @@ static int compat_ptrace_write_user(struct task_struct *tsk, compat_ulong_t off, if (off >= sizeof(compat_elf_gregset_t)) return 0; - set_fs(KERNEL_DS); - ret = copy_regset_from_user(tsk, &user_aarch32_view, - REGSET_COMPAT_GPR, off, - sizeof(compat_ulong_t), - &val); - set_fs(old_fs); + switch (idx) { + case 15: + newregs.pc = val; + break; + case 16: + newregs.pstate = compat_psr_to_pstate(val); + break; + case 17: + newregs.orig_x0 = val; + break; + default: + newregs.regs[idx] = val; + } + + if (!valid_user_regs(&newregs.user_regs, tsk)) + return -EINVAL; - return ret; + *task_pt_regs(tsk) = newregs; + return 0; } #ifdef CONFIG_HAVE_HW_BREAKPOINT -- GitLab From d20df83b66ccb9cc6b1ac448d4013647a0fe4952 Mon Sep 17 00:00:00 2001 From: Bolarinwa Olayemi Saheed Date: Mon, 15 Jun 2020 09:32:18 +0200 Subject: [PATCH 0180/1476] PCI: Convert PCIe capability PCIBIOS errors to errno The PCI config accessors (pci_read_config_word(), et al) return PCIBIOS_SUCCESSFUL (zero) or positive error values like PCIBIOS_FUNC_NOT_SUPPORTED. The PCIe capability accessors (pcie_capability_read_word(), et al) similarly return PCIBIOS errors, but some callers assume they return generic errno values like -EINVAL. For example, the Myri-10G probe function returns a positive PCIBIOS error if the pcie_capability_clear_and_set_word() in pcie_set_readrq() fails: myri10ge_probe status = pcie_set_readrq return pcie_capability_clear_and_set_word if (status) return status A positive return from a PCI driver probe function would cause a "Driver probe function unexpectedly returned" warning from local_pci_probe() instead of the desired probe failure. Convert PCIBIOS errors to generic errno for all callers of: pcie_capability_read_word pcie_capability_read_dword pcie_capability_write_word pcie_capability_write_dword pcie_capability_set_word pcie_capability_set_dword pcie_capability_clear_word pcie_capability_clear_dword pcie_capability_clear_and_set_word pcie_capability_clear_and_set_dword that check the return code for anything other than zero. [bhelgaas: commit log, squash together] Suggested-by: Bjorn Helgaas Link: https://lore.kernel.org/r/20200615073225.24061-1-refactormyself@gmail.com Signed-off-by: Bolarinwa Olayemi Saheed Signed-off-by: Bjorn Helgaas --- drivers/dma/ioat/init.c | 4 ++-- drivers/pci/pci.c | 10 ++++++++-- drivers/pci/pcie/aer.c | 11 ++++++++--- drivers/scsi/smartpqi/smartpqi_init.c | 6 +++++- 4 files changed, 23 insertions(+), 8 deletions(-) diff --git a/drivers/dma/ioat/init.c b/drivers/dma/ioat/init.c index 58d13564f88b4..9a6a9ec3cf48a 100644 --- a/drivers/dma/ioat/init.c +++ b/drivers/dma/ioat/init.c @@ -1195,13 +1195,13 @@ static int ioat3_dma_probe(struct ioatdma_device *ioat_dma, int dca) /* disable relaxed ordering */ err = pcie_capability_read_word(pdev, IOAT_DEVCTRL_OFFSET, &val16); if (err) - return err; + return pcibios_err_to_errno(err); /* clear relaxed ordering enable */ val16 &= ~IOAT_DEVCTRL_ROE; err = pcie_capability_write_word(pdev, IOAT_DEVCTRL_OFFSET, val16); if (err) - return err; + return pcibios_err_to_errno(err); if (ioat_dma->cap & IOAT_CAP_DPS) writeb(ioat_pending_level + 1, diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index ce096272f52b1..45c51aff9c030 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -5688,6 +5688,7 @@ EXPORT_SYMBOL(pcie_get_readrq); int pcie_set_readrq(struct pci_dev *dev, int rq) { u16 v; + int ret; if (rq < 128 || rq > 4096 || !is_power_of_2(rq)) return -EINVAL; @@ -5706,8 +5707,10 @@ int pcie_set_readrq(struct pci_dev *dev, int rq) v = (ffs(rq) - 8) << 12; - return pcie_capability_clear_and_set_word(dev, PCI_EXP_DEVCTL, + ret = pcie_capability_clear_and_set_word(dev, PCI_EXP_DEVCTL, PCI_EXP_DEVCTL_READRQ, v); + + return pcibios_err_to_errno(ret); } EXPORT_SYMBOL(pcie_set_readrq); @@ -5738,6 +5741,7 @@ EXPORT_SYMBOL(pcie_get_mps); int pcie_set_mps(struct pci_dev *dev, int mps) { u16 v; + int ret; if (mps < 128 || mps > 4096 || !is_power_of_2(mps)) return -EINVAL; @@ -5747,8 +5751,10 @@ int pcie_set_mps(struct pci_dev *dev, int mps) return -EINVAL; v <<= 5; - return pcie_capability_clear_and_set_word(dev, PCI_EXP_DEVCTL, + ret = pcie_capability_clear_and_set_word(dev, PCI_EXP_DEVCTL, PCI_EXP_DEVCTL_PAYLOAD, v); + + return pcibios_err_to_errno(ret); } EXPORT_SYMBOL(pcie_set_mps); diff --git a/drivers/pci/pcie/aer.c b/drivers/pci/pcie/aer.c index 3acf56683915e..2dbc1fd2910b2 100644 --- a/drivers/pci/pcie/aer.c +++ b/drivers/pci/pcie/aer.c @@ -224,20 +224,25 @@ int pcie_aer_is_native(struct pci_dev *dev) int pci_enable_pcie_error_reporting(struct pci_dev *dev) { + int rc; + if (!pcie_aer_is_native(dev)) return -EIO; - return pcie_capability_set_word(dev, PCI_EXP_DEVCTL, PCI_EXP_AER_FLAGS); + rc = pcie_capability_set_word(dev, PCI_EXP_DEVCTL, PCI_EXP_AER_FLAGS); + return pcibios_err_to_errno(rc); } EXPORT_SYMBOL_GPL(pci_enable_pcie_error_reporting); int pci_disable_pcie_error_reporting(struct pci_dev *dev) { + int rc; + if (!pcie_aer_is_native(dev)) return -EIO; - return pcie_capability_clear_word(dev, PCI_EXP_DEVCTL, - PCI_EXP_AER_FLAGS); + rc = pcie_capability_clear_word(dev, PCI_EXP_DEVCTL, PCI_EXP_AER_FLAGS); + return pcibios_err_to_errno(rc); } EXPORT_SYMBOL_GPL(pci_disable_pcie_error_reporting); diff --git a/drivers/scsi/smartpqi/smartpqi_init.c b/drivers/scsi/smartpqi/smartpqi_init.c index cd157f11eb222..bd38c8cea56e1 100644 --- a/drivers/scsi/smartpqi/smartpqi_init.c +++ b/drivers/scsi/smartpqi/smartpqi_init.c @@ -7423,8 +7423,12 @@ static int pqi_ctrl_init_resume(struct pqi_ctrl_info *ctrl_info) static inline int pqi_set_pcie_completion_timeout(struct pci_dev *pci_dev, u16 timeout) { - return pcie_capability_clear_and_set_word(pci_dev, PCI_EXP_DEVCTL2, + int rc; + + rc = pcie_capability_clear_and_set_word(pci_dev, PCI_EXP_DEVCTL2, PCI_EXP_DEVCTL2_COMP_TIMEOUT, timeout); + + return pcibios_err_to_errno(rc); } static int pqi_pci_init(struct pqi_ctrl_info *ctrl_info) -- GitLab From b91535812d663acc47952d05386c8a126eae8080 Mon Sep 17 00:00:00 2001 From: Bolarinwa Olayemi Saheed Date: Mon, 15 Jun 2020 09:32:25 +0200 Subject: [PATCH 0181/1476] PCI: Align PCIe capability and PCI accessor return values The PCI config accessors (pci_read_config_word(), et al) return PCIBIOS_SUCCESSFUL (zero) or positive error values like PCIBIOS_FUNC_NOT_SUPPORTED. The PCIe capability accessors similarly return PCIBIOS errors, but in addition, they can return -EINVAL. This makes it harder than it should be to check for errors. Return PCIBIOS_BAD_REGISTER_NUMBER instead of -EINVAL in all PCIe capability accessors. Suggested-by: Bjorn Helgaas Link: https://lore.kernel.org/r/20200615073225.24061-9-refactormyself@gmail.com Signed-off-by: Bolarinwa Olayemi Saheed Signed-off-by: Bjorn Helgaas --- drivers/pci/access.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/pci/access.c b/drivers/pci/access.c index 79c4a2ef269a7..cbb3804903a0e 100644 --- a/drivers/pci/access.c +++ b/drivers/pci/access.c @@ -409,7 +409,7 @@ int pcie_capability_read_word(struct pci_dev *dev, int pos, u16 *val) *val = 0; if (pos & 1) - return -EINVAL; + return PCIBIOS_BAD_REGISTER_NUMBER; if (pcie_capability_reg_implemented(dev, pos)) { ret = pci_read_config_word(dev, pci_pcie_cap(dev) + pos, val); @@ -444,7 +444,7 @@ int pcie_capability_read_dword(struct pci_dev *dev, int pos, u32 *val) *val = 0; if (pos & 3) - return -EINVAL; + return PCIBIOS_BAD_REGISTER_NUMBER; if (pcie_capability_reg_implemented(dev, pos)) { ret = pci_read_config_dword(dev, pci_pcie_cap(dev) + pos, val); @@ -469,7 +469,7 @@ EXPORT_SYMBOL(pcie_capability_read_dword); int pcie_capability_write_word(struct pci_dev *dev, int pos, u16 val) { if (pos & 1) - return -EINVAL; + return PCIBIOS_BAD_REGISTER_NUMBER; if (!pcie_capability_reg_implemented(dev, pos)) return 0; @@ -481,7 +481,7 @@ EXPORT_SYMBOL(pcie_capability_write_word); int pcie_capability_write_dword(struct pci_dev *dev, int pos, u32 val) { if (pos & 3) - return -EINVAL; + return PCIBIOS_BAD_REGISTER_NUMBER; if (!pcie_capability_reg_implemented(dev, pos)) return 0; -- GitLab From eb46f547a2fa9d5913c76066eab34efbd7f2252c Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Fri, 26 Jun 2020 13:25:13 +0200 Subject: [PATCH 0182/1476] clk: bcm: dvp: Add missing module informations The driver for the DVP controller in the BCM2711 was missing the MODULE_* macros resulting in a modpost warning at compilation. Fixes: 1bc95972715a ("clk: bcm: Add BCM2711 DVP driver") Signed-off-by: Maxime Ripard Link: https://lore.kernel.org/r/20200626112513.90816-1-maxime@cerno.tech Signed-off-by: Stephen Boyd --- drivers/clk/bcm/clk-bcm2711-dvp.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/clk/bcm/clk-bcm2711-dvp.c b/drivers/clk/bcm/clk-bcm2711-dvp.c index 84dbc886e3034..8333e20dc9d22 100644 --- a/drivers/clk/bcm/clk-bcm2711-dvp.c +++ b/drivers/clk/bcm/clk-bcm2711-dvp.c @@ -118,3 +118,7 @@ static struct platform_driver clk_dvp_driver = { }, }; module_platform_driver(clk_dvp_driver); + +MODULE_AUTHOR("Maxime Ripard "); +MODULE_DESCRIPTION("BCM2711 DVP clock driver"); +MODULE_LICENSE("GPL"); -- GitLab From c791cc4b1feb881b3644c059dba5464b076bf592 Mon Sep 17 00:00:00 2001 From: "Steven Rostedt (VMware)" Date: Tue, 16 Jun 2020 14:53:55 -0400 Subject: [PATCH 0183/1476] tracing: Only allow trace_array_printk() to be used by instances To prevent default "trace_printks()" from spamming the top level tracing ring buffer, only allow trace instances to use trace_array_printk() (which can be used without the trace_printk() start up warning). Signed-off-by: Steven Rostedt (VMware) --- kernel/trace/trace.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c index bb62269724d5f..8241d1448d708 100644 --- a/kernel/trace/trace.c +++ b/kernel/trace/trace.c @@ -3346,12 +3346,16 @@ int trace_array_printk(struct trace_array *tr, int ret; va_list ap; - if (!(global_trace.trace_flags & TRACE_ITER_PRINTK)) - return 0; - if (!tr) return -ENOENT; + /* This is only allowed for created instances */ + if (tr == &global_trace) + return 0; + + if (!(tr->trace_flags & TRACE_ITER_PRINTK)) + return 0; + va_start(ap, fmt); ret = trace_array_vprintk(tr, ip, fmt, ap); va_end(ap); -- GitLab From 0b4f8ddc0cc235f30431ed5c7d533b24e995d267 Mon Sep 17 00:00:00 2001 From: "Steven Rostedt (VMware)" Date: Wed, 22 Apr 2020 12:25:40 -0400 Subject: [PATCH 0184/1476] x86/ftrace: Make non direct case the default in ftrace_regs_caller If a direct function is hooked along with one of the ftrace registered functions, then the ftrace_regs_caller is attached to the function that shares the direct hook as well as the ftrace hook. The ftrace_regs_caller will call ftrace_ops_list_func() that iterates through all the registered ftrace callbacks, and if there's a direct callback attached to that function, the direct ftrace_ops callback is called to notify that ftrace_regs_caller to return to the direct caller instead of going back to the function that called it. But this is a very uncommon case. Currently, the code has it as the default case. Modify ftrace_regs_caller to make the default case (the non jump) to just return normally, and have the jump to the handling of the direct caller. Link: http://lkml.kernel.org/r/20200422162750.350373278@goodmis.org Cc: Peter Zijlstra Signed-off-by: Steven Rostedt (VMware) --- arch/x86/kernel/ftrace_64.S | 31 +++++++++++++++---------------- 1 file changed, 15 insertions(+), 16 deletions(-) diff --git a/arch/x86/kernel/ftrace_64.S b/arch/x86/kernel/ftrace_64.S index 083a3da7bb738..3ba32cc58f012 100644 --- a/arch/x86/kernel/ftrace_64.S +++ b/arch/x86/kernel/ftrace_64.S @@ -241,22 +241,9 @@ SYM_INNER_LABEL(ftrace_regs_call, SYM_L_GLOBAL) */ movq ORIG_RAX(%rsp), %rax testq %rax, %rax - jz 1f + jnz 1f - /* Swap the flags with orig_rax */ - movq MCOUNT_REG_SIZE(%rsp), %rdi - movq %rdi, MCOUNT_REG_SIZE-8(%rsp) - movq %rax, MCOUNT_REG_SIZE(%rsp) - - restore_mcount_regs 8 - /* Restore flags */ - popfq - -SYM_INNER_LABEL(ftrace_regs_caller_ret, SYM_L_GLOBAL); - UNWIND_HINT_RET_OFFSET - jmp ftrace_epilogue - -1: restore_mcount_regs + restore_mcount_regs /* Restore flags */ popfq @@ -266,9 +253,21 @@ SYM_INNER_LABEL(ftrace_regs_caller_ret, SYM_L_GLOBAL); * The trampoline will add the code to jump * to the return. */ -SYM_INNER_LABEL(ftrace_regs_caller_end, SYM_L_GLOBAL) +SYM_INNER_LABEL(ftrace_regs_caller_ret, SYM_L_GLOBAL) jmp ftrace_epilogue + /* Swap the flags with orig_rax */ +1: movq MCOUNT_REG_SIZE(%rsp), %rdi + movq %rdi, MCOUNT_REG_SIZE-8(%rsp) + movq %rax, MCOUNT_REG_SIZE(%rsp) + + restore_mcount_regs 8 + /* Restore flags */ + popfq +SYM_INNER_LABEL(ftrace_regs_caller_end, SYM_L_GLOBAL) + UNWIND_HINT_RET_OFFSET + jmp ftrace_epilogue + SYM_FUNC_END(ftrace_regs_caller) -- GitLab From 5da7cd11d0811c35a6988d416053b5421bc61521 Mon Sep 17 00:00:00 2001 From: "Steven Rostedt (VMware)" Date: Wed, 22 Apr 2020 12:25:41 -0400 Subject: [PATCH 0185/1476] x86/ftrace: Only have the builtin ftrace_regs_caller call direct hooks If a direct hook is attached to a function that ftrace also has a function attached to it, then it is required that the ftrace_ops_list_func() is used to iterate over the registered ftrace callbacks. This will also include the direct ftrace_ops helper, that tells ftrace_regs_caller where to return to (the direct callback and not the function that called it). As this direct helper is only to handle the case of ftrace callbacks attached to the same function as the direct callback, the ftrace callback allocated trampolines (used to only call them), should never be used to return back to a direct callback. Only copy the portion of the ftrace_regs_caller that will return back to what called it, and not the portion that returns back to the direct caller. The direct ftrace_ops must then pick the ftrace_regs_caller builtin function as its own trampoline to ensure that it will never have one allocated for it (which would not include the handling of direct callbacks). Link: http://lkml.kernel.org/r/20200422162750.495903799@goodmis.org Cc: Peter Zijlstra Signed-off-by: Steven Rostedt (VMware) --- arch/x86/kernel/ftrace.c | 7 ------- arch/x86/kernel/ftrace_64.S | 3 +-- kernel/trace/ftrace.c | 8 ++++++++ 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/arch/x86/kernel/ftrace.c b/arch/x86/kernel/ftrace.c index 51504566b3a6c..d1a0190fef5bc 100644 --- a/arch/x86/kernel/ftrace.c +++ b/arch/x86/kernel/ftrace.c @@ -367,13 +367,6 @@ create_trampoline(struct ftrace_ops *ops, unsigned int *tramp_size) if (WARN_ON(ret < 0)) goto fail; - if (ops->flags & FTRACE_OPS_FL_SAVE_REGS) { - ip = trampoline + (ftrace_regs_caller_ret - ftrace_regs_caller); - ret = copy_from_kernel_nofault(ip, (void *)retq, RET_SIZE); - if (WARN_ON(ret < 0)) - goto fail; - } - /* * The address of the ftrace_ops that is used for this trampoline * is stored at the end of the trampoline. This will be used to diff --git a/arch/x86/kernel/ftrace_64.S b/arch/x86/kernel/ftrace_64.S index 3ba32cc58f012..43cf9a2b52c74 100644 --- a/arch/x86/kernel/ftrace_64.S +++ b/arch/x86/kernel/ftrace_64.S @@ -253,7 +253,7 @@ SYM_INNER_LABEL(ftrace_regs_call, SYM_L_GLOBAL) * The trampoline will add the code to jump * to the return. */ -SYM_INNER_LABEL(ftrace_regs_caller_ret, SYM_L_GLOBAL) +SYM_INNER_LABEL(ftrace_regs_caller_end, SYM_L_GLOBAL) jmp ftrace_epilogue /* Swap the flags with orig_rax */ @@ -264,7 +264,6 @@ SYM_INNER_LABEL(ftrace_regs_caller_ret, SYM_L_GLOBAL) restore_mcount_regs 8 /* Restore flags */ popfq -SYM_INNER_LABEL(ftrace_regs_caller_end, SYM_L_GLOBAL) UNWIND_HINT_RET_OFFSET jmp ftrace_epilogue diff --git a/kernel/trace/ftrace.c b/kernel/trace/ftrace.c index 1903b80db6ebc..c141d347f71a8 100644 --- a/kernel/trace/ftrace.c +++ b/kernel/trace/ftrace.c @@ -2388,6 +2388,14 @@ struct ftrace_ops direct_ops = { .flags = FTRACE_OPS_FL_IPMODIFY | FTRACE_OPS_FL_RECURSION_SAFE | FTRACE_OPS_FL_DIRECT | FTRACE_OPS_FL_SAVE_REGS | FTRACE_OPS_FL_PERMANENT, + /* + * By declaring the main trampoline as this trampoline + * it will never have one allocated for it. Allocated + * trampolines should not call direct functions. + * The direct_ops should only be called by the builtin + * ftrace_regs_caller trampoline. + */ + .trampoline = FTRACE_REGS_ADDR, }; #endif /* CONFIG_DYNAMIC_FTRACE_WITH_DIRECT_CALLS */ -- GitLab From fe58acefd5a66d33a8fb94da53c3af4374b6d376 Mon Sep 17 00:00:00 2001 From: "Steven Rostedt (VMware)" Date: Wed, 22 Apr 2020 12:25:42 -0400 Subject: [PATCH 0186/1476] x86/ftrace: Do not jump to direct code in created trampolines When creating a trampoline based on the ftrace_regs_caller code, nop out the jnz test that would jmup to the code that would return to a direct caller (stored in the ORIG_RAX field) and not back to the function that called it. Link: http://lkml.kernel.org/r/20200422162750.638839749@goodmis.org Cc: Peter Zijlstra Signed-off-by: Steven Rostedt (VMware) --- arch/x86/kernel/ftrace.c | 15 +++++++++++++++ arch/x86/kernel/ftrace_64.S | 1 + 2 files changed, 16 insertions(+) diff --git a/arch/x86/kernel/ftrace.c b/arch/x86/kernel/ftrace.c index d1a0190fef5bc..7edbd5ee5ed43 100644 --- a/arch/x86/kernel/ftrace.c +++ b/arch/x86/kernel/ftrace.c @@ -286,6 +286,7 @@ extern void ftrace_regs_caller_ret(void); extern void ftrace_caller_end(void); extern void ftrace_caller_op_ptr(void); extern void ftrace_regs_caller_op_ptr(void); +extern void ftrace_regs_caller_jmp(void); /* movq function_trace_op(%rip), %rdx */ /* 0x48 0x8b 0x15 */ @@ -316,6 +317,7 @@ create_trampoline(struct ftrace_ops *ops, unsigned int *tramp_size) unsigned long end_offset; unsigned long op_offset; unsigned long call_offset; + unsigned long jmp_offset; unsigned long offset; unsigned long npages; unsigned long size; @@ -333,11 +335,13 @@ create_trampoline(struct ftrace_ops *ops, unsigned int *tramp_size) end_offset = (unsigned long)ftrace_regs_caller_end; op_offset = (unsigned long)ftrace_regs_caller_op_ptr; call_offset = (unsigned long)ftrace_regs_call; + jmp_offset = (unsigned long)ftrace_regs_caller_jmp; } else { start_offset = (unsigned long)ftrace_caller; end_offset = (unsigned long)ftrace_caller_end; op_offset = (unsigned long)ftrace_caller_op_ptr; call_offset = (unsigned long)ftrace_call; + jmp_offset = 0; } size = end_offset - start_offset; @@ -367,6 +371,17 @@ create_trampoline(struct ftrace_ops *ops, unsigned int *tramp_size) if (WARN_ON(ret < 0)) goto fail; + /* No need to test direct calls on created trampolines */ + if (ops->flags & FTRACE_OPS_FL_SAVE_REGS) { + /* NOP the jnz 1f; but make sure it's a 2 byte jnz */ + ip = trampoline + (jmp_offset - start_offset); + if (WARN_ON(*(char *)ip != 0x75)) + goto fail; + ret = copy_from_kernel_nofault(ip, ideal_nops[2], 2); + if (ret < 0) + goto fail; + } + /* * The address of the ftrace_ops that is used for this trampoline * is stored at the end of the trampoline. This will be used to diff --git a/arch/x86/kernel/ftrace_64.S b/arch/x86/kernel/ftrace_64.S index 43cf9a2b52c74..ac3d5f22fe64b 100644 --- a/arch/x86/kernel/ftrace_64.S +++ b/arch/x86/kernel/ftrace_64.S @@ -241,6 +241,7 @@ SYM_INNER_LABEL(ftrace_regs_call, SYM_L_GLOBAL) */ movq ORIG_RAX(%rsp), %rax testq %rax, %rax +SYM_INNER_LABEL(ftrace_regs_caller_jmp, SYM_L_GLOBAL) jnz 1f restore_mcount_regs -- GitLab From dbb988b4e7eb924ffceaa2a7da6e794c868cb2f3 Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Sun, 28 Jun 2020 02:30:55 +0000 Subject: [PATCH 0187/1476] clk: qcom: Fix return value check in apss_ipq6018_probe() In case of error, the function dev_get_regmap() returns NULL pointer not ERR_PTR(). The IS_ERR() test in the return value check should be replaced with NULL test. Fixes: 5e77b4ef1b19 ("clk: qcom: Add ipq6018 apss clock controller") Reported-by: Hulk Robot Signed-off-by: Wei Yongjun Link: https://lore.kernel.org/r/20200628023055.50608-1-weiyongjun1@huawei.com Signed-off-by: Stephen Boyd --- drivers/clk/qcom/apss-ipq6018.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/clk/qcom/apss-ipq6018.c b/drivers/clk/qcom/apss-ipq6018.c index 004f7e1ecdc21..d78ff2f310bfa 100644 --- a/drivers/clk/qcom/apss-ipq6018.c +++ b/drivers/clk/qcom/apss-ipq6018.c @@ -87,8 +87,8 @@ static int apss_ipq6018_probe(struct platform_device *pdev) struct regmap *regmap; regmap = dev_get_regmap(pdev->dev.parent, NULL); - if (IS_ERR(regmap)) - return PTR_ERR(regmap); + if (!regmap) + return -ENODEV; return qcom_cc_really_probe(pdev, &apss_ipq6018_desc, regmap); } -- GitLab From 234e9d7a6200952264002b9ba808e7dfc559b0b2 Mon Sep 17 00:00:00 2001 From: Chenxi Mao Date: Fri, 5 Jun 2020 13:15:10 +0800 Subject: [PATCH 0188/1476] riscv: Select ARCH_SUPPORTS_ATOMIC_RMW by default Select ARCH_SUPPORTS_ATOMIC_RMW by default to enable osqlocks. Signed-off-by: Chenxi Mao [Palmer: commit text] Signed-off-by: Palmer Dabbelt --- arch/riscv/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/riscv/Kconfig b/arch/riscv/Kconfig index 128192e14ff2a..089293e4ad46d 100644 --- a/arch/riscv/Kconfig +++ b/arch/riscv/Kconfig @@ -13,6 +13,7 @@ config 32BIT config RISCV def_bool y select ARCH_CLOCKSOURCE_INIT + select ARCH_SUPPORTS_ATOMIC_RMW select ARCH_HAS_BINFMT_FLAT select ARCH_HAS_DEBUG_VIRTUAL if MMU select ARCH_HAS_DEBUG_WX -- GitLab From a2693fe254e77055cb80571fa2d723021dd3aa1d Mon Sep 17 00:00:00 2001 From: Greentime Hu Date: Mon, 22 Jun 2020 16:47:25 -0700 Subject: [PATCH 0189/1476] RISC-V: Use a local variable instead of smp_processor_id() Store the smp_processor_id() in a local variable to save some pointer chasing. Signed-off-by: Greentime Hu Signed-off-by: Atish Patra Signed-off-by: Palmer Dabbelt --- arch/riscv/kernel/smpboot.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/arch/riscv/kernel/smpboot.c b/arch/riscv/kernel/smpboot.c index 4e9922790f6ea..3e033e97dd083 100644 --- a/arch/riscv/kernel/smpboot.c +++ b/arch/riscv/kernel/smpboot.c @@ -146,6 +146,7 @@ void __init smp_cpus_done(unsigned int max_cpus) asmlinkage __visible void smp_callin(void) { struct mm_struct *mm = &init_mm; + unsigned int curr_cpuid = smp_processor_id(); if (!IS_ENABLED(CONFIG_RISCV_SBI)) clint_clear_ipi(cpuid_to_hartid_map(smp_processor_id())); @@ -155,9 +156,9 @@ asmlinkage __visible void smp_callin(void) current->active_mm = mm; trap_init(); - notify_cpu_starting(smp_processor_id()); - update_siblings_masks(smp_processor_id()); - set_cpu_online(smp_processor_id(), 1); + notify_cpu_starting(curr_cpuid); + update_siblings_masks(curr_cpuid); + set_cpu_online(curr_cpuid, 1); /* * Remote TLB flushes are ignored while the CPU is offline, so emit * a local TLB flush right now just in case. -- GitLab From 828ca4320d130bbe1d12866152600c49ff6a9f79 Mon Sep 17 00:00:00 2001 From: Sandipan Das Date: Thu, 4 Jun 2020 18:26:08 +0530 Subject: [PATCH 0190/1476] selftests/powerpc: Fix pkey access right updates The Power ISA mandates that all writes to the Authority Mask Register (AMR) must always be preceded as well as succeeded by a context synchronizing instruction. This makes sure that the tests follow this requirement when attempting to update a pkey's access rights. Signed-off-by: Sandipan Das Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20200604125610.649668-2-sandipan@linux.ibm.com --- tools/testing/selftests/powerpc/include/reg.h | 6 ++++++ tools/testing/selftests/powerpc/ptrace/core-pkey.c | 2 +- tools/testing/selftests/powerpc/ptrace/ptrace-pkey.c | 2 +- 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/tools/testing/selftests/powerpc/include/reg.h b/tools/testing/selftests/powerpc/include/reg.h index 022c5076b2c56..c0f2742a3a59c 100644 --- a/tools/testing/selftests/powerpc/include/reg.h +++ b/tools/testing/selftests/powerpc/include/reg.h @@ -57,6 +57,12 @@ #define SPRN_PPR 896 /* Program Priority Register */ #define SPRN_AMR 13 /* Authority Mask Register - problem state */ +#define set_amr(v) asm volatile("isync;" \ + "mtspr " __stringify(SPRN_AMR) ",%0;" \ + "isync" : \ + : "r" ((unsigned long)(v)) \ + : "memory") + /* TEXASR register bits */ #define TEXASR_FC 0xFE00000000000000 #define TEXASR_FP 0x0100000000000000 diff --git a/tools/testing/selftests/powerpc/ptrace/core-pkey.c b/tools/testing/selftests/powerpc/ptrace/core-pkey.c index d5c64fee032dc..bbc05ffc5860a 100644 --- a/tools/testing/selftests/powerpc/ptrace/core-pkey.c +++ b/tools/testing/selftests/powerpc/ptrace/core-pkey.c @@ -150,7 +150,7 @@ static int child(struct shared_info *info) printf("%-30s AMR: %016lx pkey1: %d pkey2: %d pkey3: %d\n", user_write, info->amr, pkey1, pkey2, pkey3); - mtspr(SPRN_AMR, info->amr); + set_amr(info->amr); /* * We won't use pkey3. This tests whether the kernel restores the UAMOR diff --git a/tools/testing/selftests/powerpc/ptrace/ptrace-pkey.c b/tools/testing/selftests/powerpc/ptrace/ptrace-pkey.c index bdbbbe8431e03..904c04f8c919b 100644 --- a/tools/testing/selftests/powerpc/ptrace/ptrace-pkey.c +++ b/tools/testing/selftests/powerpc/ptrace/ptrace-pkey.c @@ -126,7 +126,7 @@ static int child(struct shared_info *info) printf("%-30s AMR: %016lx pkey1: %d pkey2: %d pkey3: %d\n", user_write, info->amr1, pkey1, pkey2, pkey3); - mtspr(SPRN_AMR, info->amr1); + set_amr(info->amr1); /* Wait for parent to read our AMR value and write a new one. */ ret = prod_parent(&info->child_sync); -- GitLab From c405b738daf9d8e8a5aedfeb6be851681e65e54b Mon Sep 17 00:00:00 2001 From: Sandipan Das Date: Thu, 4 Jun 2020 18:26:09 +0530 Subject: [PATCH 0191/1476] selftests/powerpc: Move Hash MMU check to utilities This moves a function to test if the MMU is in Hash mode under the generic test utilities. Signed-off-by: Sandipan Das Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20200604125610.649668-3-sandipan@linux.ibm.com --- .../testing/selftests/powerpc/include/utils.h | 1 + tools/testing/selftests/powerpc/mm/Makefile | 2 +- .../selftests/powerpc/mm/bad_accesses.c | 28 ------------------- tools/testing/selftests/powerpc/utils.c | 28 +++++++++++++++++++ 4 files changed, 30 insertions(+), 29 deletions(-) diff --git a/tools/testing/selftests/powerpc/include/utils.h b/tools/testing/selftests/powerpc/include/utils.h index 2e39b297054f3..9dbe607cc5ec3 100644 --- a/tools/testing/selftests/powerpc/include/utils.h +++ b/tools/testing/selftests/powerpc/include/utils.h @@ -60,6 +60,7 @@ static inline bool have_hwcap2(unsigned long ftr2) #endif bool is_ppc64le(void); +int using_hash_mmu(bool *using_hash); /* Yes, this is evil */ #define FAIL_IF(x) \ diff --git a/tools/testing/selftests/powerpc/mm/Makefile b/tools/testing/selftests/powerpc/mm/Makefile index b9103c4bb4145..2389bf791fd66 100644 --- a/tools/testing/selftests/powerpc/mm/Makefile +++ b/tools/testing/selftests/powerpc/mm/Makefile @@ -10,7 +10,7 @@ TEST_GEN_FILES := tempfile top_srcdir = ../../../../.. include ../../lib.mk -$(TEST_GEN_PROGS): ../harness.c +$(TEST_GEN_PROGS): ../harness.c ../utils.c $(OUTPUT)/prot_sao: ../utils.c diff --git a/tools/testing/selftests/powerpc/mm/bad_accesses.c b/tools/testing/selftests/powerpc/mm/bad_accesses.c index adc465f499efa..a864ed7e20081 100644 --- a/tools/testing/selftests/powerpc/mm/bad_accesses.c +++ b/tools/testing/selftests/powerpc/mm/bad_accesses.c @@ -64,34 +64,6 @@ int bad_access(char *p, bool write) return 0; } -static int using_hash_mmu(bool *using_hash) -{ - char line[128]; - FILE *f; - int rc; - - f = fopen("/proc/cpuinfo", "r"); - FAIL_IF(!f); - - rc = 0; - while (fgets(line, sizeof(line), f) != NULL) { - if (strcmp(line, "MMU : Hash\n") == 0) { - *using_hash = true; - goto out; - } - - if (strcmp(line, "MMU : Radix\n") == 0) { - *using_hash = false; - goto out; - } - } - - rc = -1; -out: - fclose(f); - return rc; -} - static int test(void) { unsigned long i, j, addr, region_shift, page_shift, page_size; diff --git a/tools/testing/selftests/powerpc/utils.c b/tools/testing/selftests/powerpc/utils.c index 5ee0e98c48967..933678f1ed0a0 100644 --- a/tools/testing/selftests/powerpc/utils.c +++ b/tools/testing/selftests/powerpc/utils.c @@ -293,3 +293,31 @@ void set_dscr(unsigned long val) asm volatile("mtspr %1,%0" : : "r" (val), "i" (SPRN_DSCR)); } + +int using_hash_mmu(bool *using_hash) +{ + char line[128]; + FILE *f; + int rc; + + f = fopen("/proc/cpuinfo", "r"); + FAIL_IF(!f); + + rc = 0; + while (fgets(line, sizeof(line), f) != NULL) { + if (strcmp(line, "MMU : Hash\n") == 0) { + *using_hash = true; + goto out; + } + + if (strcmp(line, "MMU : Radix\n") == 0) { + *using_hash = false; + goto out; + } + } + + rc = -1; +out: + fclose(f); + return rc; +} -- GitLab From 1addb6444791f9e87fce0eb9882ec96a4a76e615 Mon Sep 17 00:00:00 2001 From: Sandipan Das Date: Thu, 4 Jun 2020 18:26:10 +0530 Subject: [PATCH 0192/1476] selftests/powerpc: Add test for execute-disabled pkeys Apart from read and write access, memory protection keys can also be used for restricting execute permission of pages on powerpc. This adds a test to verify if the feature works as expected. Signed-off-by: Sandipan Das Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20200604125610.649668-4-sandipan@linux.ibm.com --- tools/testing/selftests/powerpc/mm/.gitignore | 1 + tools/testing/selftests/powerpc/mm/Makefile | 3 +- .../selftests/powerpc/mm/pkey_exec_prot.c | 388 ++++++++++++++++++ 3 files changed, 391 insertions(+), 1 deletion(-) create mode 100644 tools/testing/selftests/powerpc/mm/pkey_exec_prot.c diff --git a/tools/testing/selftests/powerpc/mm/.gitignore b/tools/testing/selftests/powerpc/mm/.gitignore index 2ca523255b1b2..8f841f925baa5 100644 --- a/tools/testing/selftests/powerpc/mm/.gitignore +++ b/tools/testing/selftests/powerpc/mm/.gitignore @@ -8,3 +8,4 @@ wild_bctr large_vm_fork_separation bad_accesses tlbie_test +pkey_exec_prot diff --git a/tools/testing/selftests/powerpc/mm/Makefile b/tools/testing/selftests/powerpc/mm/Makefile index 2389bf791fd66..f9fa0ba7435c4 100644 --- a/tools/testing/selftests/powerpc/mm/Makefile +++ b/tools/testing/selftests/powerpc/mm/Makefile @@ -3,7 +3,7 @@ noarg: $(MAKE) -C ../ TEST_GEN_PROGS := hugetlb_vs_thp_test subpage_prot prot_sao segv_errors wild_bctr \ - large_vm_fork_separation bad_accesses + large_vm_fork_separation bad_accesses pkey_exec_prot TEST_GEN_PROGS_EXTENDED := tlbie_test TEST_GEN_FILES := tempfile @@ -17,6 +17,7 @@ $(OUTPUT)/prot_sao: ../utils.c $(OUTPUT)/wild_bctr: CFLAGS += -m64 $(OUTPUT)/large_vm_fork_separation: CFLAGS += -m64 $(OUTPUT)/bad_accesses: CFLAGS += -m64 +$(OUTPUT)/pkey_exec_prot: CFLAGS += -m64 $(OUTPUT)/tempfile: dd if=/dev/zero of=$@ bs=64k count=1 diff --git a/tools/testing/selftests/powerpc/mm/pkey_exec_prot.c b/tools/testing/selftests/powerpc/mm/pkey_exec_prot.c new file mode 100644 index 0000000000000..7c7c93425c5e9 --- /dev/null +++ b/tools/testing/selftests/powerpc/mm/pkey_exec_prot.c @@ -0,0 +1,388 @@ +// SPDX-License-Identifier: GPL-2.0+ + +/* + * Copyright 2020, Sandipan Das, IBM Corp. + * + * Test if applying execute protection on pages using memory + * protection keys works as expected. + */ + +#define _GNU_SOURCE +#include +#include +#include +#include + +#include +#include + +#include "reg.h" +#include "utils.h" + +/* + * Older versions of libc use the Intel-specific access rights. + * Hence, override the definitions as they might be incorrect. + */ +#undef PKEY_DISABLE_ACCESS +#define PKEY_DISABLE_ACCESS 0x3 + +#undef PKEY_DISABLE_WRITE +#define PKEY_DISABLE_WRITE 0x2 + +#undef PKEY_DISABLE_EXECUTE +#define PKEY_DISABLE_EXECUTE 0x4 + +/* Older versions of libc do not not define this */ +#ifndef SEGV_PKUERR +#define SEGV_PKUERR 4 +#endif + +#define SI_PKEY_OFFSET 0x20 + +#define SYS_pkey_mprotect 386 +#define SYS_pkey_alloc 384 +#define SYS_pkey_free 385 + +#define PKEY_BITS_PER_PKEY 2 +#define NR_PKEYS 32 +#define PKEY_BITS_MASK ((1UL << PKEY_BITS_PER_PKEY) - 1) + +#define PPC_INST_NOP 0x60000000 +#define PPC_INST_TRAP 0x7fe00008 +#define PPC_INST_BLR 0x4e800020 + +#define sigsafe_err(msg) ({ \ + ssize_t nbytes __attribute__((unused)); \ + nbytes = write(STDERR_FILENO, msg, strlen(msg)); }) + +static inline unsigned long pkeyreg_get(void) +{ + return mfspr(SPRN_AMR); +} + +static inline void pkeyreg_set(unsigned long amr) +{ + set_amr(amr); +} + +static void pkey_set_rights(int pkey, unsigned long rights) +{ + unsigned long amr, shift; + + shift = (NR_PKEYS - pkey - 1) * PKEY_BITS_PER_PKEY; + amr = pkeyreg_get(); + amr &= ~(PKEY_BITS_MASK << shift); + amr |= (rights & PKEY_BITS_MASK) << shift; + pkeyreg_set(amr); +} + +static int sys_pkey_mprotect(void *addr, size_t len, int prot, int pkey) +{ + return syscall(SYS_pkey_mprotect, addr, len, prot, pkey); +} + +static int sys_pkey_alloc(unsigned long flags, unsigned long rights) +{ + return syscall(SYS_pkey_alloc, flags, rights); +} + +static int sys_pkey_free(int pkey) +{ + return syscall(SYS_pkey_free, pkey); +} + +static volatile sig_atomic_t fault_pkey, fault_code, fault_type; +static volatile sig_atomic_t remaining_faults; +static volatile unsigned int *fault_addr; +static unsigned long pgsize, numinsns; +static unsigned int *insns; + +static void trap_handler(int signum, siginfo_t *sinfo, void *ctx) +{ + /* Check if this fault originated from the expected address */ + if (sinfo->si_addr != (void *) fault_addr) + sigsafe_err("got a fault for an unexpected address\n"); + + _exit(1); +} + +static void segv_handler(int signum, siginfo_t *sinfo, void *ctx) +{ + int signal_pkey; + + /* + * In older versions of libc, siginfo_t does not have si_pkey as + * a member. + */ +#ifdef si_pkey + signal_pkey = sinfo->si_pkey; +#else + signal_pkey = *((int *)(((char *) sinfo) + SI_PKEY_OFFSET)); +#endif + + fault_code = sinfo->si_code; + + /* Check if this fault originated from the expected address */ + if (sinfo->si_addr != (void *) fault_addr) { + sigsafe_err("got a fault for an unexpected address\n"); + _exit(1); + } + + /* Check if too many faults have occurred for a single test case */ + if (!remaining_faults) { + sigsafe_err("got too many faults for the same address\n"); + _exit(1); + } + + + /* Restore permissions in order to continue */ + switch (fault_code) { + case SEGV_ACCERR: + if (mprotect(insns, pgsize, PROT_READ | PROT_WRITE)) { + sigsafe_err("failed to set access permissions\n"); + _exit(1); + } + break; + case SEGV_PKUERR: + if (signal_pkey != fault_pkey) { + sigsafe_err("got a fault for an unexpected pkey\n"); + _exit(1); + } + + switch (fault_type) { + case PKEY_DISABLE_ACCESS: + pkey_set_rights(fault_pkey, 0); + break; + case PKEY_DISABLE_EXECUTE: + /* + * Reassociate the exec-only pkey with the region + * to be able to continue. Unlike AMR, we cannot + * set IAMR directly from userspace to restore the + * permissions. + */ + if (mprotect(insns, pgsize, PROT_EXEC)) { + sigsafe_err("failed to set execute permissions\n"); + _exit(1); + } + break; + default: + sigsafe_err("got a fault with an unexpected type\n"); + _exit(1); + } + break; + default: + sigsafe_err("got a fault with an unexpected code\n"); + _exit(1); + } + + remaining_faults--; +} + +static int pkeys_unsupported(void) +{ + bool hash_mmu = false; + int pkey; + + /* Protection keys are currently supported on Hash MMU only */ + FAIL_IF(using_hash_mmu(&hash_mmu)); + SKIP_IF(!hash_mmu); + + /* Check if the system call is supported */ + pkey = sys_pkey_alloc(0, 0); + SKIP_IF(pkey < 0); + sys_pkey_free(pkey); + + return 0; +} + +static int test(void) +{ + struct sigaction segv_act, trap_act; + int pkey, ret, i; + + ret = pkeys_unsupported(); + if (ret) + return ret; + + /* Setup SIGSEGV handler */ + segv_act.sa_handler = 0; + segv_act.sa_sigaction = segv_handler; + FAIL_IF(sigprocmask(SIG_SETMASK, 0, &segv_act.sa_mask) != 0); + segv_act.sa_flags = SA_SIGINFO; + segv_act.sa_restorer = 0; + FAIL_IF(sigaction(SIGSEGV, &segv_act, NULL) != 0); + + /* Setup SIGTRAP handler */ + trap_act.sa_handler = 0; + trap_act.sa_sigaction = trap_handler; + FAIL_IF(sigprocmask(SIG_SETMASK, 0, &trap_act.sa_mask) != 0); + trap_act.sa_flags = SA_SIGINFO; + trap_act.sa_restorer = 0; + FAIL_IF(sigaction(SIGTRAP, &trap_act, NULL) != 0); + + /* Setup executable region */ + pgsize = getpagesize(); + numinsns = pgsize / sizeof(unsigned int); + insns = (unsigned int *) mmap(NULL, pgsize, PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + FAIL_IF(insns == MAP_FAILED); + + /* Write the instruction words */ + for (i = 1; i < numinsns - 1; i++) + insns[i] = PPC_INST_NOP; + + /* + * Set the first instruction as an unconditional trap. If + * the last write to this address succeeds, this should + * get overwritten by a no-op. + */ + insns[0] = PPC_INST_TRAP; + + /* + * Later, to jump to the executable region, we use a branch + * and link instruction (bctrl) which sets the return address + * automatically in LR. Use that to return back. + */ + insns[numinsns - 1] = PPC_INST_BLR; + + /* Allocate a pkey that restricts execution */ + pkey = sys_pkey_alloc(0, PKEY_DISABLE_EXECUTE); + FAIL_IF(pkey < 0); + + /* + * Pick the first instruction's address from the executable + * region. + */ + fault_addr = insns; + + /* The following two cases will avoid SEGV_PKUERR */ + fault_type = -1; + fault_pkey = -1; + + /* + * Read an instruction word from the address when AMR bits + * are not set i.e. the pkey permits both read and write + * access. + * + * This should not generate a fault as having PROT_EXEC + * implies PROT_READ on GNU systems. The pkey currently + * restricts execution only based on the IAMR bits. The + * AMR bits are cleared. + */ + remaining_faults = 0; + FAIL_IF(sys_pkey_mprotect(insns, pgsize, PROT_EXEC, pkey) != 0); + printf("read from %p, pkey is execute-disabled, access-enabled\n", + (void *) fault_addr); + i = *fault_addr; + FAIL_IF(remaining_faults != 0); + + /* + * Write an instruction word to the address when AMR bits + * are not set i.e. the pkey permits both read and write + * access. + * + * This should generate an access fault as having just + * PROT_EXEC also restricts writes. The pkey currently + * restricts execution only based on the IAMR bits. The + * AMR bits are cleared. + */ + remaining_faults = 1; + FAIL_IF(sys_pkey_mprotect(insns, pgsize, PROT_EXEC, pkey) != 0); + printf("write to %p, pkey is execute-disabled, access-enabled\n", + (void *) fault_addr); + *fault_addr = PPC_INST_TRAP; + FAIL_IF(remaining_faults != 0 || fault_code != SEGV_ACCERR); + + /* The following three cases will generate SEGV_PKUERR */ + fault_type = PKEY_DISABLE_ACCESS; + fault_pkey = pkey; + + /* + * Read an instruction word from the address when AMR bits + * are set i.e. the pkey permits neither read nor write + * access. + * + * This should generate a pkey fault based on AMR bits only + * as having PROT_EXEC implicitly allows reads. + */ + remaining_faults = 1; + FAIL_IF(sys_pkey_mprotect(insns, pgsize, PROT_EXEC, pkey) != 0); + printf("read from %p, pkey is execute-disabled, access-disabled\n", + (void *) fault_addr); + pkey_set_rights(pkey, PKEY_DISABLE_ACCESS); + i = *fault_addr; + FAIL_IF(remaining_faults != 0 || fault_code != SEGV_PKUERR); + + /* + * Write an instruction word to the address when AMR bits + * are set i.e. the pkey permits neither read nor write + * access. + * + * This should generate two faults. First, a pkey fault + * based on AMR bits and then an access fault since + * PROT_EXEC does not allow writes. + */ + remaining_faults = 2; + FAIL_IF(sys_pkey_mprotect(insns, pgsize, PROT_EXEC, pkey) != 0); + printf("write to %p, pkey is execute-disabled, access-disabled\n", + (void *) fault_addr); + pkey_set_rights(pkey, PKEY_DISABLE_ACCESS); + *fault_addr = PPC_INST_NOP; + FAIL_IF(remaining_faults != 0 || fault_code != SEGV_ACCERR); + + /* + * Jump to the executable region when AMR bits are set i.e. + * the pkey permits neither read nor write access. + * + * This should generate a pkey fault based on IAMR bits which + * are set to not permit execution. AMR bits should not affect + * execution. + * + * This also checks if the overwrite of the first instruction + * word from a trap to a no-op succeeded. + */ + fault_addr = insns; + fault_type = PKEY_DISABLE_EXECUTE; + fault_pkey = pkey; + remaining_faults = 1; + FAIL_IF(sys_pkey_mprotect(insns, pgsize, PROT_EXEC, pkey) != 0); + pkey_set_rights(pkey, PKEY_DISABLE_ACCESS); + printf("execute at %p, pkey is execute-disabled, access-disabled\n", + (void *) fault_addr); + asm volatile("mtctr %0; bctrl" : : "r"(insns)); + FAIL_IF(remaining_faults != 0 || fault_code != SEGV_PKUERR); + + /* + * Free the current pkey and allocate a new one that is + * fully permissive. + */ + sys_pkey_free(pkey); + pkey = sys_pkey_alloc(0, 0); + + /* + * Jump to the executable region when AMR bits are not set + * i.e. the pkey permits read and write access. + * + * This should not generate any faults as the IAMR bits are + * also not set and hence will the pkey will not restrict + * execution. + */ + fault_pkey = pkey; + remaining_faults = 0; + FAIL_IF(sys_pkey_mprotect(insns, pgsize, PROT_EXEC, pkey) != 0); + printf("execute at %p, pkey is execute-enabled, access-enabled\n", + (void *) fault_addr); + asm volatile("mtctr %0; bctrl" : : "r"(insns)); + FAIL_IF(remaining_faults != 0); + + /* Cleanup */ + munmap((void *) insns, pgsize); + sys_pkey_free(pkey); + + return 0; +} + +int main(void) +{ + test_harness(test, "pkey_exec_prot"); +} -- GitLab From df4232d96e724d09e54a623362f9f610727f059f Mon Sep 17 00:00:00 2001 From: Nathan Chancellor Date: Tue, 23 Jun 2020 20:59:20 -0700 Subject: [PATCH 0193/1476] powerpc/boot: Use address-of operator on section symbols Clang warns: arch/powerpc/boot/main.c:107:18: warning: array comparison always evaluates to a constant [-Wtautological-compare] if (_initrd_end > _initrd_start) { ^ arch/powerpc/boot/main.c:155:20: warning: array comparison always evaluates to a constant [-Wtautological-compare] if (_esm_blob_end <= _esm_blob_start) ^ 2 warnings generated. These are not true arrays, they are linker defined symbols, which are just addresses. Using the address of operator silences the warning and does not change the resulting assembly with either clang/ld.lld or gcc/ld (tested with diff + objdump -Dr). Reported-by: Joel Stanley Signed-off-by: Nathan Chancellor Tested-by: Geoff Levand Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20200624035920.835571-1-natechancellor@gmail.com --- arch/powerpc/boot/main.c | 4 ++-- arch/powerpc/boot/ps3.c | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/arch/powerpc/boot/main.c b/arch/powerpc/boot/main.c index a9d209135975d..cae31a6e8f029 100644 --- a/arch/powerpc/boot/main.c +++ b/arch/powerpc/boot/main.c @@ -104,7 +104,7 @@ static struct addr_range prep_initrd(struct addr_range vmlinux, void *chosen, { /* If we have an image attached to us, it overrides anything * supplied by the loader. */ - if (_initrd_end > _initrd_start) { + if (&_initrd_end > &_initrd_start) { printf("Attached initrd image at 0x%p-0x%p\n\r", _initrd_start, _initrd_end); initrd_addr = (unsigned long)_initrd_start; @@ -152,7 +152,7 @@ static void prep_esm_blob(struct addr_range vmlinux, void *chosen) unsigned long esm_blob_addr, esm_blob_size; /* Do we have an ESM (Enter Secure Mode) blob? */ - if (_esm_blob_end <= _esm_blob_start) + if (&_esm_blob_end <= &_esm_blob_start) return; printf("Attached ESM blob at 0x%p-0x%p\n\r", diff --git a/arch/powerpc/boot/ps3.c b/arch/powerpc/boot/ps3.c index c52552a681c5e..6e4efbdb6b7cf 100644 --- a/arch/powerpc/boot/ps3.c +++ b/arch/powerpc/boot/ps3.c @@ -127,7 +127,7 @@ void platform_init(void) ps3_repository_read_rm_size(&rm_size); dt_fixup_memory(0, rm_size); - if (_initrd_end > _initrd_start) { + if (&_initrd_end > &_initrd_start) { setprop_val(chosen, "linux,initrd-start", (u32)(_initrd_start)); setprop_val(chosen, "linux,initrd-end", (u32)(_initrd_end)); } -- GitLab From 86bc917d2ac117ec922dbf8ed92ca989bf333281 Mon Sep 17 00:00:00 2001 From: Michael Ellerman Date: Tue, 23 Jun 2020 23:03:20 +1000 Subject: [PATCH 0194/1476] powerpc/boot/dts: Fix dtc "pciex" warnings With CONFIG_OF_ALL_DTBS=y, as set by eg. allmodconfig, we see lots of warnings about our dts files, such as: arch/powerpc/boot/dts/glacier.dts:492.26-532.5: Warning (pci_bridge): /plb/pciex@d00000000: node name is not "pci" or "pcie" The node name should not particularly matter, it's just a name, and AFAICS there's no kernel code that cares whether nodes are *named* "pciex" or "pcie". So shutup these warnings by converting to the name dtc wants. As always there's some risk this could break something obscure that does rely on the name, in which case we can revert. Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20200623130320.405852-1-mpe@ellerman.id.au --- arch/powerpc/boot/dts/akebono.dts | 8 ++++---- arch/powerpc/boot/dts/bluestone.dts | 2 +- arch/powerpc/boot/dts/canyonlands.dts | 4 ++-- arch/powerpc/boot/dts/currituck.dts | 6 +++--- arch/powerpc/boot/dts/glacier.dts | 4 ++-- arch/powerpc/boot/dts/haleakala.dts | 2 +- arch/powerpc/boot/dts/icon.dts | 4 ++-- arch/powerpc/boot/dts/katmai.dts | 6 +++--- arch/powerpc/boot/dts/kilauea.dts | 4 ++-- arch/powerpc/boot/dts/makalu.dts | 4 ++-- arch/powerpc/boot/dts/redwood.dts | 6 +++--- 11 files changed, 25 insertions(+), 25 deletions(-) diff --git a/arch/powerpc/boot/dts/akebono.dts b/arch/powerpc/boot/dts/akebono.dts index cd9d66041a3fe..df18f8dc46428 100644 --- a/arch/powerpc/boot/dts/akebono.dts +++ b/arch/powerpc/boot/dts/akebono.dts @@ -248,7 +248,7 @@ }; }; - PCIE0: pciex@10100000000 { + PCIE0: pcie@10100000000 { device_type = "pci"; #interrupt-cells = <1>; #size-cells = <2>; @@ -288,7 +288,7 @@ 0x0 0x0 0x0 0x4 &MPIC 48 0x2 /* int D */>; }; - PCIE1: pciex@20100000000 { + PCIE1: pcie@20100000000 { device_type = "pci"; #interrupt-cells = <1>; #size-cells = <2>; @@ -328,7 +328,7 @@ 0x0 0x0 0x0 0x4 &MPIC 56 0x2 /* int D */>; }; - PCIE2: pciex@18100000000 { + PCIE2: pcie@18100000000 { device_type = "pci"; #interrupt-cells = <1>; #size-cells = <2>; @@ -368,7 +368,7 @@ 0x0 0x0 0x0 0x4 &MPIC 64 0x2 /* int D */>; }; - PCIE3: pciex@28100000000 { + PCIE3: pcie@28100000000 { device_type = "pci"; #interrupt-cells = <1>; #size-cells = <2>; diff --git a/arch/powerpc/boot/dts/bluestone.dts b/arch/powerpc/boot/dts/bluestone.dts index cc965a1816b69..aa1ae94cd776a 100644 --- a/arch/powerpc/boot/dts/bluestone.dts +++ b/arch/powerpc/boot/dts/bluestone.dts @@ -325,7 +325,7 @@ }; }; - PCIE0: pciex@d00000000 { + PCIE0: pcie@d00000000 { device_type = "pci"; #interrupt-cells = <1>; #size-cells = <2>; diff --git a/arch/powerpc/boot/dts/canyonlands.dts b/arch/powerpc/boot/dts/canyonlands.dts index 0d6ac92d0f5e4..c5fbb08e0a6e1 100644 --- a/arch/powerpc/boot/dts/canyonlands.dts +++ b/arch/powerpc/boot/dts/canyonlands.dts @@ -461,7 +461,7 @@ interrupt-map = < 0x0 0x0 0x0 0x0 &UIC1 0x0 0x8 >; }; - PCIE0: pciex@d00000000 { + PCIE0: pcie@d00000000 { device_type = "pci"; #interrupt-cells = <1>; #size-cells = <2>; @@ -503,7 +503,7 @@ 0x0 0x0 0x0 0x4 &UIC3 0xf 0x4 /* swizzled int D */>; }; - PCIE1: pciex@d20000000 { + PCIE1: pcie@d20000000 { device_type = "pci"; #interrupt-cells = <1>; #size-cells = <2>; diff --git a/arch/powerpc/boot/dts/currituck.dts b/arch/powerpc/boot/dts/currituck.dts index b6d87b9c2cefb..aea8af810106d 100644 --- a/arch/powerpc/boot/dts/currituck.dts +++ b/arch/powerpc/boot/dts/currituck.dts @@ -122,7 +122,7 @@ }; }; - PCIE0: pciex@10100000000 { // 4xGBIF1 + PCIE0: pcie@10100000000 { // 4xGBIF1 device_type = "pci"; #interrupt-cells = <1>; #size-cells = <2>; @@ -160,7 +160,7 @@ 0x0 0x0 0x0 0x4 &MPIC 49 0x2 /* int D */>; }; - PCIE1: pciex@30100000000 { // 4xGBIF0 + PCIE1: pcie@30100000000 { // 4xGBIF0 device_type = "pci"; #interrupt-cells = <1>; #size-cells = <2>; @@ -197,7 +197,7 @@ 0x0 0x0 0x0 0x4 &MPIC 41 0x2 /* int D */>; }; - PCIE2: pciex@38100000000 { // 2xGBIF0 + PCIE2: pcie@38100000000 { // 2xGBIF0 device_type = "pci"; #interrupt-cells = <1>; #size-cells = <2>; diff --git a/arch/powerpc/boot/dts/glacier.dts b/arch/powerpc/boot/dts/glacier.dts index a7a802f4ffddf..e84ff1afb58ca 100644 --- a/arch/powerpc/boot/dts/glacier.dts +++ b/arch/powerpc/boot/dts/glacier.dts @@ -489,7 +489,7 @@ interrupt-map = < 0x0 0x0 0x0 0x0 &UIC1 0x0 0x8 >; }; - PCIE0: pciex@d00000000 { + PCIE0: pcie@d00000000 { device_type = "pci"; #interrupt-cells = <1>; #size-cells = <2>; @@ -531,7 +531,7 @@ 0x0 0x0 0x0 0x4 &UIC3 0xf 0x4 /* swizzled int D */>; }; - PCIE1: pciex@d20000000 { + PCIE1: pcie@d20000000 { device_type = "pci"; #interrupt-cells = <1>; #size-cells = <2>; diff --git a/arch/powerpc/boot/dts/haleakala.dts b/arch/powerpc/boot/dts/haleakala.dts index cb16dad43c922..f81ce8786d597 100644 --- a/arch/powerpc/boot/dts/haleakala.dts +++ b/arch/powerpc/boot/dts/haleakala.dts @@ -237,7 +237,7 @@ }; }; - PCIE0: pciex@a0000000 { + PCIE0: pcie@a0000000 { device_type = "pci"; #interrupt-cells = <1>; #size-cells = <2>; diff --git a/arch/powerpc/boot/dts/icon.dts b/arch/powerpc/boot/dts/icon.dts index 2e6e3a7b26040..fbaa60b8f87a5 100644 --- a/arch/powerpc/boot/dts/icon.dts +++ b/arch/powerpc/boot/dts/icon.dts @@ -315,7 +315,7 @@ interrupt-map = <0x0 0x0 0x0 0x0 &UIC1 19 0x8>; }; - PCIE0: pciex@d00000000 { + PCIE0: pcie@d00000000 { device_type = "pci"; #interrupt-cells = <1>; #size-cells = <2>; @@ -356,7 +356,7 @@ 0x0 0x0 0x0 0x4 &UIC3 0x3 0x4 /* swizzled int D */>; }; - PCIE1: pciex@d20000000 { + PCIE1: pcie@d20000000 { device_type = "pci"; #interrupt-cells = <1>; #size-cells = <2>; diff --git a/arch/powerpc/boot/dts/katmai.dts b/arch/powerpc/boot/dts/katmai.dts index 02629e119b875..a8f353229fb7e 100644 --- a/arch/powerpc/boot/dts/katmai.dts +++ b/arch/powerpc/boot/dts/katmai.dts @@ -319,7 +319,7 @@ >; }; - PCIE0: pciex@d00000000 { + PCIE0: pcie@d00000000 { device_type = "pci"; #interrupt-cells = <1>; #size-cells = <2>; @@ -360,7 +360,7 @@ 0x0 0x0 0x0 0x4 &UIC3 0x3 0x4 /* swizzled int D */>; }; - PCIE1: pciex@d20000000 { + PCIE1: pcie@d20000000 { device_type = "pci"; #interrupt-cells = <1>; #size-cells = <2>; @@ -401,7 +401,7 @@ 0x0 0x0 0x0 0x4 &UIC3 0x7 0x4 /* swizzled int D */>; }; - PCIE2: pciex@d40000000 { + PCIE2: pcie@d40000000 { device_type = "pci"; #interrupt-cells = <1>; #size-cells = <2>; diff --git a/arch/powerpc/boot/dts/kilauea.dts b/arch/powerpc/boot/dts/kilauea.dts index 2a3413221cc1f..a709fb47a180d 100644 --- a/arch/powerpc/boot/dts/kilauea.dts +++ b/arch/powerpc/boot/dts/kilauea.dts @@ -322,7 +322,7 @@ }; }; - PCIE0: pciex@a0000000 { + PCIE0: pcie@a0000000 { device_type = "pci"; #interrupt-cells = <1>; #size-cells = <2>; @@ -363,7 +363,7 @@ 0x0 0x0 0x0 0x4 &UIC2 0x3 0x4 /* swizzled int D */>; }; - PCIE1: pciex@c0000000 { + PCIE1: pcie@c0000000 { device_type = "pci"; #interrupt-cells = <1>; #size-cells = <2>; diff --git a/arch/powerpc/boot/dts/makalu.dts b/arch/powerpc/boot/dts/makalu.dts index bf8fe16293924..c473cd911bca0 100644 --- a/arch/powerpc/boot/dts/makalu.dts +++ b/arch/powerpc/boot/dts/makalu.dts @@ -268,7 +268,7 @@ }; }; - PCIE0: pciex@a0000000 { + PCIE0: pcie@a0000000 { device_type = "pci"; #interrupt-cells = <1>; #size-cells = <2>; @@ -309,7 +309,7 @@ 0x0 0x0 0x0 0x4 &UIC2 0x3 0x4 /* swizzled int D */>; }; - PCIE1: pciex@c0000000 { + PCIE1: pcie@c0000000 { device_type = "pci"; #interrupt-cells = <1>; #size-cells = <2>; diff --git a/arch/powerpc/boot/dts/redwood.dts b/arch/powerpc/boot/dts/redwood.dts index f3e046fb49e28..f38035a1f4a15 100644 --- a/arch/powerpc/boot/dts/redwood.dts +++ b/arch/powerpc/boot/dts/redwood.dts @@ -235,7 +235,7 @@ has-new-stacr-staopc; }; }; - PCIE0: pciex@d00000000 { + PCIE0: pcie@d00000000 { device_type = "pci"; #interrupt-cells = <1>; #size-cells = <2>; @@ -276,7 +276,7 @@ 0x0 0x0 0x0 0x4 &UIC3 0x3 0x4 /* swizzled int D */>; }; - PCIE1: pciex@d20000000 { + PCIE1: pcie@d20000000 { device_type = "pci"; #interrupt-cells = <1>; #size-cells = <2>; @@ -317,7 +317,7 @@ 0x0 0x0 0x0 0x4 &UIC3 0x7 0x4 /* swizzled int D */>; }; - PCIE2: pciex@d40000000 { + PCIE2: pcie@d40000000 { device_type = "pci"; #interrupt-cells = <1>; #size-cells = <2>; -- GitLab From 7999096fa9cfd0253497c8d2ed9a5a1537521d25 Mon Sep 17 00:00:00 2001 From: Herbert Xu Date: Fri, 12 Jun 2020 16:57:37 +1000 Subject: [PATCH 0195/1476] iov_iter: Move unnecessary inclusion of crypto/hash.h The header file linux/uio.h includes crypto/hash.h which pulls in most of the Crypto API. Since linux/uio.h is used throughout the kernel this means that every tiny bit of change to the Crypto API causes the entire kernel to get rebuilt. This patch fixes this by moving it into lib/iov_iter.c instead where it is actually used. This patch also fixes the ifdef to use CRYPTO_HASH instead of just CRYPTO which does not guarantee the existence of ahash. Unfortunately a number of drivers were relying on linux/uio.h to provide access to linux/slab.h. This patch adds inclusions of linux/slab.h as detected by build failures. Also skbuff.h was relying on this to provide a declaration for ahash_request. This patch adds a forward declaration instead. Signed-off-by: Herbert Xu Signed-off-by: Al Viro --- arch/s390/lib/test_unwind.c | 1 + drivers/dma/sf-pdma/sf-pdma.c | 1 + drivers/dma/st_fdma.c | 1 + drivers/dma/uniphier-xdmac.c | 1 + drivers/misc/uacce/uacce.c | 1 + drivers/mtd/mtdpstore.c | 1 + drivers/mtd/nand/raw/cadence-nand-controller.c | 1 + drivers/remoteproc/qcom_q6v5_mss.c | 1 + drivers/soc/qcom/pdr_interface.c | 1 + fs/btrfs/inode.c | 1 + include/linux/skbuff.h | 1 + include/linux/socket.h | 1 + include/linux/uio.h | 1 - lib/iov_iter.c | 3 ++- 14 files changed, 14 insertions(+), 2 deletions(-) diff --git a/arch/s390/lib/test_unwind.c b/arch/s390/lib/test_unwind.c index 32b7a30b2485d..eb382ceaa1161 100644 --- a/arch/s390/lib/test_unwind.c +++ b/arch/s390/lib/test_unwind.c @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include diff --git a/drivers/dma/sf-pdma/sf-pdma.c b/drivers/dma/sf-pdma/sf-pdma.c index 5c118c7e02bdc..6e530dca6d9e8 100644 --- a/drivers/dma/sf-pdma/sf-pdma.c +++ b/drivers/dma/sf-pdma/sf-pdma.c @@ -20,6 +20,7 @@ #include #include #include +#include #include "sf-pdma.h" diff --git a/drivers/dma/st_fdma.c b/drivers/dma/st_fdma.c index 67087dbe2f9fa..962b6e05287b5 100644 --- a/drivers/dma/st_fdma.c +++ b/drivers/dma/st_fdma.c @@ -15,6 +15,7 @@ #include #include #include +#include #include "st_fdma.h" diff --git a/drivers/dma/uniphier-xdmac.c b/drivers/dma/uniphier-xdmac.c index 7b2f8a8c2d319..16b19654873df 100644 --- a/drivers/dma/uniphier-xdmac.c +++ b/drivers/dma/uniphier-xdmac.c @@ -12,6 +12,7 @@ #include #include #include +#include #include "dmaengine.h" #include "virt-dma.h" diff --git a/drivers/misc/uacce/uacce.c b/drivers/misc/uacce/uacce.c index 107028e77ca37..e45bfd409cc54 100644 --- a/drivers/misc/uacce/uacce.c +++ b/drivers/misc/uacce/uacce.c @@ -4,6 +4,7 @@ #include #include #include +#include #include static struct class *uacce_class; diff --git a/drivers/mtd/mtdpstore.c b/drivers/mtd/mtdpstore.c index a4fe6060b960f..a3ae8778f6a9b 100644 --- a/drivers/mtd/mtdpstore.c +++ b/drivers/mtd/mtdpstore.c @@ -7,6 +7,7 @@ #include #include #include +#include static struct mtdpstore_context { int index; diff --git a/drivers/mtd/nand/raw/cadence-nand-controller.c b/drivers/mtd/nand/raw/cadence-nand-controller.c index c405722adfe10..c4f273e2fe78f 100644 --- a/drivers/mtd/nand/raw/cadence-nand-controller.c +++ b/drivers/mtd/nand/raw/cadence-nand-controller.c @@ -17,6 +17,7 @@ #include #include #include +#include /* * HPNFC can work in 3 modes: diff --git a/drivers/remoteproc/qcom_q6v5_mss.c b/drivers/remoteproc/qcom_q6v5_mss.c index feb70283b6a21..903b2bb97e122 100644 --- a/drivers/remoteproc/qcom_q6v5_mss.c +++ b/drivers/remoteproc/qcom_q6v5_mss.c @@ -26,6 +26,7 @@ #include #include #include +#include #include "remoteproc_internal.h" #include "qcom_common.h" diff --git a/drivers/soc/qcom/pdr_interface.c b/drivers/soc/qcom/pdr_interface.c index bdcf16f88a97f..a90d707da6894 100644 --- a/drivers/soc/qcom/pdr_interface.c +++ b/drivers/soc/qcom/pdr_interface.c @@ -5,6 +5,7 @@ #include #include +#include #include #include diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index d04c82c884181..d901d53e4f03b 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -3,6 +3,7 @@ * Copyright (C) 2007 Oracle. All rights reserved. */ +#include #include #include #include diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h index 0c0377fc00c2a..1530e81a6cce7 100644 --- a/include/linux/skbuff.h +++ b/include/linux/skbuff.h @@ -238,6 +238,7 @@ SKB_DATA_ALIGN(sizeof(struct sk_buff)) + \ SKB_DATA_ALIGN(sizeof(struct skb_shared_info))) +struct ahash_request; struct net_device; struct scatterlist; struct pipe_inode_info; diff --git a/include/linux/socket.h b/include/linux/socket.h index 04d2bc97f497d..e9cb30d8cbfb1 100644 --- a/include/linux/socket.h +++ b/include/linux/socket.h @@ -10,6 +10,7 @@ #include /* __user */ #include +struct file; struct pid; struct cred; struct socket; diff --git a/include/linux/uio.h b/include/linux/uio.h index 9576fd8158d7d..3835a8a8e9eae 100644 --- a/include/linux/uio.h +++ b/include/linux/uio.h @@ -7,7 +7,6 @@ #include #include -#include #include struct page; diff --git a/lib/iov_iter.c b/lib/iov_iter.c index bf538c2bec777..5e40786c8f123 100644 --- a/lib/iov_iter.c +++ b/lib/iov_iter.c @@ -1,4 +1,5 @@ // SPDX-License-Identifier: GPL-2.0-only +#include #include #include #include @@ -1567,7 +1568,7 @@ EXPORT_SYMBOL(csum_and_copy_to_iter); size_t hash_and_copy_to_iter(const void *addr, size_t bytes, void *hashp, struct iov_iter *i) { -#ifdef CONFIG_CRYPTO +#ifdef CONFIG_CRYPTO_HASH struct ahash_request *hash = hashp; struct scatterlist sg; size_t copied; -- GitLab From 7ecd4a8175104b55de120dd8847e0bfabf7d75aa Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Sat, 27 Jun 2020 12:30:50 +0200 Subject: [PATCH 0196/1476] PCI: Replace http:// links with https:// Replace http:// links with https:// links. This reduces the likelihood of man-in-the-middle attacks when developers open these links. Deterministic algorithm: For each file: If not .svg: For each line: If doesn't contain `\bxmlns\b`: For each link, `\bhttp://[^# \t\r\n]*(?:\w|/)`: If both the HTTP and HTTPS versions return 200 OK and serve the same content: Replace HTTP with HTTPS. [bhelgaas: also update samsung.com links, drop sourceforge link] Link: https://lore.kernel.org/r/20200627103050.71712-1-grandmaster@al2klimov.de Signed-off-by: Alexander A. Klimov Signed-off-by: Bjorn Helgaas --- Documentation/PCI/pci.rst | 7 +++---- Documentation/devicetree/bindings/pci/pci.txt | 4 ++-- arch/x86/pci/fixup.c | 4 ++-- drivers/pci/controller/dwc/pci-dra7xx.c | 2 +- drivers/pci/controller/dwc/pci-exynos.c | 2 +- drivers/pci/controller/dwc/pci-imx6.c | 2 +- drivers/pci/controller/dwc/pci-keystone.c | 2 +- drivers/pci/controller/dwc/pcie-designware-host.c | 2 +- drivers/pci/controller/dwc/pcie-designware.c | 2 +- drivers/pci/controller/dwc/pcie-designware.h | 2 +- drivers/pci/controller/dwc/pcie-kirin.c | 2 +- drivers/pci/pci-label.c | 2 +- drivers/pci/pcie/Kconfig | 2 +- drivers/pci/pcie/aer_inject.c | 2 +- drivers/pci/quirks.c | 10 +++++----- 15 files changed, 23 insertions(+), 24 deletions(-) diff --git a/Documentation/PCI/pci.rst b/Documentation/PCI/pci.rst index 8c016d8c98623..d2815d735c86e 100644 --- a/Documentation/PCI/pci.rst +++ b/Documentation/PCI/pci.rst @@ -17,7 +17,7 @@ PCI device drivers. A more complete resource is the third edition of "Linux Device Drivers" by Jonathan Corbet, Alessandro Rubini, and Greg Kroah-Hartman. LDD3 is available for free (under Creative Commons License) from: -http://lwn.net/Kernel/LDD3/. +https://lwn.net/Kernel/LDD3/. However, keep in mind that all documents are subject to "bit rot". Refer to the source code if things are not working as described here. @@ -514,9 +514,8 @@ your driver if they're helpful, or just use plain hex constants. The device IDs are arbitrary hex numbers (vendor controlled) and normally used only in a single location, the pci_device_id table. -Please DO submit new vendor/device IDs to http://pci-ids.ucw.cz/. -There are mirrors of the pci.ids file at http://pciids.sourceforge.net/ -and https://github.com/pciutils/pciids. +Please DO submit new vendor/device IDs to https://pci-ids.ucw.cz/. +There's a mirror of the pci.ids file at https://github.com/pciutils/pciids. Obsolete functions diff --git a/Documentation/devicetree/bindings/pci/pci.txt b/Documentation/devicetree/bindings/pci/pci.txt index 29bcbd88f457d..6a8f2874a24db 100644 --- a/Documentation/devicetree/bindings/pci/pci.txt +++ b/Documentation/devicetree/bindings/pci/pci.txt @@ -1,12 +1,12 @@ PCI bus bridges have standardized Device Tree bindings: PCI Bus Binding to: IEEE Std 1275-1994 -http://www.devicetree.org/open-firmware/bindings/pci/pci2_1.pdf +https://www.devicetree.org/open-firmware/bindings/pci/pci2_1.pdf And for the interrupt mapping part: Open Firmware Recommended Practice: Interrupt Mapping -http://www.devicetree.org/open-firmware/practice/imap/imap0_9d.pdf +https://www.devicetree.org/open-firmware/practice/imap/imap0_9d.pdf Additionally to the properties specified in the above standards a host bridge driver implementation may support the following properties: diff --git a/arch/x86/pci/fixup.c b/arch/x86/pci/fixup.c index 0c67a5a94de30..b8c9a5b87f372 100644 --- a/arch/x86/pci/fixup.c +++ b/arch/x86/pci/fixup.c @@ -557,12 +557,12 @@ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x27B9, twinhead_reserve_killing_z * Device [8086:2fc0] * Erratum HSE43 * CONFIG_TDP_NOMINAL CSR Implemented at Incorrect Offset - * http://www.intel.com/content/www/us/en/processors/xeon/xeon-e5-v3-spec-update.html + * https://www.intel.com/content/www/us/en/processors/xeon/xeon-e5-v3-spec-update.html * * Devices [8086:6f60,6fa0,6fc0] * Erratum BDF2 * PCI BARs in the Home Agent Will Return Non-Zero Values During Enumeration - * http://www.intel.com/content/www/us/en/processors/xeon/xeon-e5-v4-spec-update.html + * https://www.intel.com/content/www/us/en/processors/xeon/xeon-e5-v4-spec-update.html */ static void pci_invalid_bar(struct pci_dev *dev) { diff --git a/drivers/pci/controller/dwc/pci-dra7xx.c b/drivers/pci/controller/dwc/pci-dra7xx.c index 6184ebc9392db..1b2e6e6fb8184 100644 --- a/drivers/pci/controller/dwc/pci-dra7xx.c +++ b/drivers/pci/controller/dwc/pci-dra7xx.c @@ -2,7 +2,7 @@ /* * pcie-dra7xx - PCIe controller driver for TI DRA7xx SoCs * - * Copyright (C) 2013-2014 Texas Instruments Incorporated - http://www.ti.com + * Copyright (C) 2013-2014 Texas Instruments Incorporated - https://www.ti.com * * Authors: Kishon Vijay Abraham I */ diff --git a/drivers/pci/controller/dwc/pci-exynos.c b/drivers/pci/controller/dwc/pci-exynos.c index c5043d951e809..a075eba45abb2 100644 --- a/drivers/pci/controller/dwc/pci-exynos.c +++ b/drivers/pci/controller/dwc/pci-exynos.c @@ -3,7 +3,7 @@ * PCIe host controller driver for Samsung Exynos SoCs * * Copyright (C) 2013 Samsung Electronics Co., Ltd. - * http://www.samsung.com + * https://www.samsung.com * * Author: Jingoo Han */ diff --git a/drivers/pci/controller/dwc/pci-imx6.c b/drivers/pci/controller/dwc/pci-imx6.c index 8f08ae53f53e4..4e5c379ae4181 100644 --- a/drivers/pci/controller/dwc/pci-imx6.c +++ b/drivers/pci/controller/dwc/pci-imx6.c @@ -3,7 +3,7 @@ * PCIe host controller driver for Freescale i.MX6 SoCs * * Copyright (C) 2013 Kosagi - * http://www.kosagi.com + * https://www.kosagi.com * * Author: Sean Cross */ diff --git a/drivers/pci/controller/dwc/pci-keystone.c b/drivers/pci/controller/dwc/pci-keystone.c index 790679fdfa488..5191cad906cac 100644 --- a/drivers/pci/controller/dwc/pci-keystone.c +++ b/drivers/pci/controller/dwc/pci-keystone.c @@ -3,7 +3,7 @@ * PCIe host controller driver for Texas Instruments Keystone SoCs * * Copyright (C) 2013-2014 Texas Instruments., Ltd. - * http://www.ti.com + * https://www.ti.com * * Author: Murali Karicheri * Implementation based on pci-exynos.c and pcie-designware.c diff --git a/drivers/pci/controller/dwc/pcie-designware-host.c b/drivers/pci/controller/dwc/pcie-designware-host.c index 0a4a5aa6fe469..2cb286a492933 100644 --- a/drivers/pci/controller/dwc/pcie-designware-host.c +++ b/drivers/pci/controller/dwc/pcie-designware-host.c @@ -3,7 +3,7 @@ * Synopsys DesignWare PCIe host controller driver * * Copyright (C) 2013 Samsung Electronics Co., Ltd. - * http://www.samsung.com + * https://www.samsung.com * * Author: Jingoo Han */ diff --git a/drivers/pci/controller/dwc/pcie-designware.c b/drivers/pci/controller/dwc/pcie-designware.c index c92496e36fd5f..b723e0cc41fb4 100644 --- a/drivers/pci/controller/dwc/pcie-designware.c +++ b/drivers/pci/controller/dwc/pcie-designware.c @@ -3,7 +3,7 @@ * Synopsys DesignWare PCIe host controller driver * * Copyright (C) 2013 Samsung Electronics Co., Ltd. - * http://www.samsung.com + * https://www.samsung.com * * Author: Jingoo Han */ diff --git a/drivers/pci/controller/dwc/pcie-designware.h b/drivers/pci/controller/dwc/pcie-designware.h index 656e00f8fbeb4..90915dc89e086 100644 --- a/drivers/pci/controller/dwc/pcie-designware.h +++ b/drivers/pci/controller/dwc/pcie-designware.h @@ -3,7 +3,7 @@ * Synopsys DesignWare PCIe host controller driver * * Copyright (C) 2013 Samsung Electronics Co., Ltd. - * http://www.samsung.com + * https://www.samsung.com * * Author: Jingoo Han */ diff --git a/drivers/pci/controller/dwc/pcie-kirin.c b/drivers/pci/controller/dwc/pcie-kirin.c index c19617a912bd5..7b86c1608b046 100644 --- a/drivers/pci/controller/dwc/pcie-kirin.c +++ b/drivers/pci/controller/dwc/pcie-kirin.c @@ -3,7 +3,7 @@ * PCIe host controller driver for Kirin Phone SoCs * * Copyright (C) 2017 HiSilicon Electronics Co., Ltd. - * http://www.huawei.com + * https://www.huawei.com * * Author: Xiaowei Song */ diff --git a/drivers/pci/pci-label.c b/drivers/pci/pci-label.c index 707dd98086766..781e45cf60d1c 100644 --- a/drivers/pci/pci-label.c +++ b/drivers/pci/pci-label.c @@ -18,7 +18,7 @@ * the instance number and string from the type 41 record and exports * it to sysfs. * - * Please see http://linux.dell.com/files/biosdevname/ for more + * Please see https://linux.dell.com/files/biosdevname/ for more * information. */ diff --git a/drivers/pci/pcie/Kconfig b/drivers/pci/pcie/Kconfig index 9cd31331aee9f..3946555a60422 100644 --- a/drivers/pci/pcie/Kconfig +++ b/drivers/pci/pcie/Kconfig @@ -43,7 +43,7 @@ config PCIEAER_INJECT error injection can fake almost all kinds of errors with the help of a user space helper tool aer-inject, which can be gotten from: - http://www.kernel.org/pub/linux/utils/pci/aer-inject/ + https://www.kernel.org/pub/linux/utils/pci/aer-inject/ # # PCI Express ECRC diff --git a/drivers/pci/pcie/aer_inject.c b/drivers/pci/pcie/aer_inject.c index 21cc3d3387f79..c2cbf425afc52 100644 --- a/drivers/pci/pcie/aer_inject.c +++ b/drivers/pci/pcie/aer_inject.c @@ -6,7 +6,7 @@ * trigger various real hardware errors. Software based error * injection can fake almost all kinds of errors with the help of a * user space helper tool aer-inject, which can be gotten from: - * http://www.kernel.org/pub/linux/utils/pci/aer-inject/ + * https://www.kernel.org/pub/linux/utils/pci/aer-inject/ * * Copyright 2009 Intel Corporation. * Huang Ying diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c index 812bfc32ecb82..54ebbef850bee 100644 --- a/drivers/pci/quirks.c +++ b/drivers/pci/quirks.c @@ -4620,11 +4620,11 @@ static int pci_quirk_al_acs(struct pci_dev *dev, u16 acs_flags) * * 0x9d10-0x9d1b PCI Express Root port #{1-12} * - * [1] http://www.intel.com/content/www/us/en/chipsets/100-series-chipset-datasheet-vol-2.html - * [2] http://www.intel.com/content/www/us/en/chipsets/100-series-chipset-datasheet-vol-1.html - * [3] http://www.intel.com/content/www/us/en/chipsets/100-series-chipset-spec-update.html - * [4] http://www.intel.com/content/www/us/en/chipsets/200-series-chipset-pch-spec-update.html - * [5] http://www.intel.com/content/www/us/en/chipsets/200-series-chipset-pch-datasheet-vol-1.html + * [1] https://www.intel.com/content/www/us/en/chipsets/100-series-chipset-datasheet-vol-2.html + * [2] https://www.intel.com/content/www/us/en/chipsets/100-series-chipset-datasheet-vol-1.html + * [3] https://www.intel.com/content/www/us/en/chipsets/100-series-chipset-spec-update.html + * [4] https://www.intel.com/content/www/us/en/chipsets/200-series-chipset-pch-spec-update.html + * [5] https://www.intel.com/content/www/us/en/chipsets/200-series-chipset-pch-datasheet-vol-1.html * [6] https://www.intel.com/content/www/us/en/processors/core/7th-gen-core-family-mobile-u-y-processor-lines-i-o-spec-update.html * [7] https://www.intel.com/content/www/us/en/processors/core/7th-gen-core-family-mobile-u-y-processor-lines-i-o-datasheet-vol-1.html */ -- GitLab From 16bbbc874f7f98e6598784896ed4719f0f1ed844 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Tue, 30 Jun 2020 12:41:39 -0500 Subject: [PATCH 0197/1476] PCI: Replace lkml.org, spinics, gmane with lore.kernel.org The lkml.org, spinics.net, and gmane.org archives are not very reliable and, in some cases, not even easily accessible. Replace links to them with links to lore.kernel.org, the archives hosted by kernel.org. I found the gmane items via the Wayback Machine archive at https://web.archive.org/. Signed-off-by: Bjorn Helgaas --- Documentation/PCI/pci.rst | 2 +- drivers/pci/quirks.c | 8 ++++---- drivers/pci/setup-res.c | 3 ++- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/Documentation/PCI/pci.rst b/Documentation/PCI/pci.rst index d2815d735c86e..281d8a241eaee 100644 --- a/Documentation/PCI/pci.rst +++ b/Documentation/PCI/pci.rst @@ -214,7 +214,7 @@ the PCI device by calling pci_enable_device(). This will: problem and unlikely to get fixed soon. This has been discussed before but not changed as of 2.6.19: - http://lkml.org/lkml/2006/3/2/194 + https://lore.kernel.org/r/20060302180025.GC28895@flint.arm.linux.org.uk/ pci_set_master() will enable DMA by setting the bus master bit diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c index 54ebbef850bee..9d7a432616132 100644 --- a/drivers/pci/quirks.c +++ b/drivers/pci/quirks.c @@ -3549,7 +3549,7 @@ static void quirk_no_bus_reset(struct pci_dev *dev) * The device will throw a Link Down error on AER-capable systems and * regardless of AER, config space of the device is never accessible again * and typically causes the system to hang or reset when access is attempted. - * http://www.spinics.net/lists/linux-pci/msg34797.html + * https://lore.kernel.org/r/20140923210318.498dacbd@dualc.maya.org/ */ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_ATHEROS, 0x0030, quirk_no_bus_reset); DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_ATHEROS, 0x0032, quirk_no_bus_reset); @@ -4378,9 +4378,9 @@ static int pci_acs_ctrl_enabled(u16 acs_ctrl_req, u16 acs_ctrl_ena) * redirect (CR) since all transactions are redirected to the upstream * root complex. * - * http://permalink.gmane.org/gmane.comp.emulators.kvm.devel/94086 - * http://permalink.gmane.org/gmane.comp.emulators.kvm.devel/94102 - * http://permalink.gmane.org/gmane.comp.emulators.kvm.devel/99402 + * https://lore.kernel.org/r/201207111426.q6BEQTbh002928@mail.maya.org/ + * https://lore.kernel.org/r/20120711165854.GM25282@amd.com/ + * https://lore.kernel.org/r/20121005130857.GX4009@amd.com/ * * 1002:4385 SBx00 SMBus Controller * 1002:439c SB7x0/SB8x0/SB9x0 IDE Controller diff --git a/drivers/pci/setup-res.c b/drivers/pci/setup-res.c index d21fa04fa44d2..43eda101fcf40 100644 --- a/drivers/pci/setup-res.c +++ b/drivers/pci/setup-res.c @@ -73,7 +73,8 @@ static void pci_std_update_resource(struct pci_dev *dev, int resno) /* * Apparently some Matrox devices have ROM BARs that read * as zero when disabled, so don't update ROM BARs unless - * they're enabled. See https://lkml.org/lkml/2005/8/30/138. + * they're enabled. See + * https://lore.kernel.org/r/43147B3D.1030309@vc.cvut.cz/ */ if (!(res->flags & IORESOURCE_ROM_ENABLE)) return; -- GitLab From e8cf9c8c4c18cda752fadce2da4f2a88d1e3d9e2 Mon Sep 17 00:00:00 2001 From: Wei Yang Date: Fri, 12 Jun 2020 17:28:41 +0800 Subject: [PATCH 0198/1476] tracing: not necessary to undefine DEFINE_EVENT again After un-define DEFINE_EVENT in Stage 2, DEFINE_EVENT is not defined to a specific form. It is not necessary to un-define it again. Let's skip this. Link: http://lkml.kernel.org/r/20200612092844.56107-2-richard.weiyang@linux.alibaba.com Signed-off-by: Wei Yang Signed-off-by: Steven Rostedt (VMware) --- include/trace/trace_events.h | 6 ------ 1 file changed, 6 deletions(-) diff --git a/include/trace/trace_events.h b/include/trace/trace_events.h index 502c7be50b8d9..a4994761bdafd 100644 --- a/include/trace/trace_events.h +++ b/include/trace/trace_events.h @@ -443,9 +443,6 @@ static struct trace_event_fields trace_event_fields_##call[] = { \ tstruct \ {} }; -#undef DEFINE_EVENT -#define DEFINE_EVENT(template, name, proto, args) - #undef DEFINE_EVENT_PRINT #define DEFINE_EVENT_PRINT(template, name, proto, args, print) \ DEFINE_EVENT(template, name, PARAMS(proto), PARAMS(args)) @@ -523,9 +520,6 @@ static inline notrace int trace_event_get_offsets_##call( \ return __data_size; \ } -#undef DEFINE_EVENT -#define DEFINE_EVENT(template, name, proto, args) - #undef DEFINE_EVENT_PRINT #define DEFINE_EVENT_PRINT(template, name, proto, args, print) \ DEFINE_EVENT(template, name, PARAMS(proto), PARAMS(args)) -- GitLab From b6f9eb87078b18cea7015e2575234e9b6b296c2f Mon Sep 17 00:00:00 2001 From: Wei Yang Date: Fri, 12 Jun 2020 17:28:42 +0800 Subject: [PATCH 0199/1476] tracing: not necessary re-define DEFINE_EVENT_PRINT The definition of DEFINE_EVENT_PRINT is not changed after previous one, so not necessary to re-define is as the same form. Link: http://lkml.kernel.org/r/20200612092844.56107-3-richard.weiyang@linux.alibaba.com Signed-off-by: Wei Yang Signed-off-by: Steven Rostedt (VMware) --- include/trace/trace_events.h | 4 ---- 1 file changed, 4 deletions(-) diff --git a/include/trace/trace_events.h b/include/trace/trace_events.h index a4994761bdafd..5219f81b9d748 100644 --- a/include/trace/trace_events.h +++ b/include/trace/trace_events.h @@ -520,10 +520,6 @@ static inline notrace int trace_event_get_offsets_##call( \ return __data_size; \ } -#undef DEFINE_EVENT_PRINT -#define DEFINE_EVENT_PRINT(template, name, proto, args, print) \ - DEFINE_EVENT(template, name, PARAMS(proto), PARAMS(args)) - #include TRACE_INCLUDE(TRACE_INCLUDE_FILE) /* -- GitLab From 61df16fcafad810ea5dcaa640d0fe3e039d8e652 Mon Sep 17 00:00:00 2001 From: Wei Yang Date: Fri, 12 Jun 2020 17:28:43 +0800 Subject: [PATCH 0200/1476] tracing: define DEFINE_EVENT_PRINT not related to DEFINE_EVENT Current definition define DEFINE_EVENT_PRINT to be DEFINE_EVENT. Actually, at this point DEFINE_EVENT is already an empty macro. Let's cut the relationship between DEFINE_EVENT_PRINT and DEFINE_EVENT. Link: http://lkml.kernel.org/r/20200612092844.56107-4-richard.weiyang@linux.alibaba.com Signed-off-by: Wei Yang Signed-off-by: Steven Rostedt (VMware) --- include/trace/trace_events.h | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/include/trace/trace_events.h b/include/trace/trace_events.h index 5219f81b9d748..43023c3e9d74b 100644 --- a/include/trace/trace_events.h +++ b/include/trace/trace_events.h @@ -210,8 +210,7 @@ TRACE_MAKE_SYSTEM_STR(); #define DEFINE_EVENT(template, name, proto, args) #undef DEFINE_EVENT_PRINT -#define DEFINE_EVENT_PRINT(template, name, proto, args, print) \ - DEFINE_EVENT(template, name, PARAMS(proto), PARAMS(args)) +#define DEFINE_EVENT_PRINT(template, name, proto, args, print) #undef TRACE_EVENT_FLAGS #define TRACE_EVENT_FLAGS(event, flag) @@ -444,8 +443,7 @@ static struct trace_event_fields trace_event_fields_##call[] = { \ {} }; #undef DEFINE_EVENT_PRINT -#define DEFINE_EVENT_PRINT(template, name, proto, args, print) \ - DEFINE_EVENT(template, name, PARAMS(proto), PARAMS(args)) +#define DEFINE_EVENT_PRINT(template, name, proto, args, print) #include TRACE_INCLUDE(TRACE_INCLUDE_FILE) -- GitLab From e6bc5b3f423825220a3232ddf08399658c918671 Mon Sep 17 00:00:00 2001 From: Wei Yang Date: Fri, 12 Jun 2020 17:28:44 +0800 Subject: [PATCH 0201/1476] tracing: not necessary to define DEFINE_EVENT_PRINT to be empty again After the previous cleanup, DEFINE_EVENT_PRINT's definition has no relationship with DEFINE_EVENT. So After we re-define DEFINE_EVENT, it is not necessary to define DEFINE_EVENT_PRINT to be empty again. Link: http://lkml.kernel.org/r/20200612092844.56107-5-richard.weiyang@linux.alibaba.com Signed-off-by: Wei Yang Signed-off-by: Steven Rostedt (VMware) --- include/trace/trace_events.h | 3 --- 1 file changed, 3 deletions(-) diff --git a/include/trace/trace_events.h b/include/trace/trace_events.h index 43023c3e9d74b..1bc3e7bba9a44 100644 --- a/include/trace/trace_events.h +++ b/include/trace/trace_events.h @@ -709,9 +709,6 @@ static inline void ftrace_test_probe_##call(void) \ check_trace_callback_type_##call(trace_event_raw_event_##template); \ } -#undef DEFINE_EVENT_PRINT -#define DEFINE_EVENT_PRINT(template, name, proto, args, print) - #include TRACE_INCLUDE(TRACE_INCLUDE_FILE) #undef __entry -- GitLab From 7ef282e05132d56b6f6b71e3873f317664bea78b Mon Sep 17 00:00:00 2001 From: "Steven Rostedt (VMware)" Date: Mon, 29 Jun 2020 23:45:56 -0400 Subject: [PATCH 0202/1476] tracing: Move pipe reference to trace array instead of current_tracer If a process has the trace_pipe open on a trace_array, the current tracer for that trace array should not be changed. This was original enforced by a global lock, but when instances were introduced, it was moved to the current_trace. But this structure is shared by all instances, and a trace_pipe is for a single instance. There's no reason that a process that has trace_pipe open on one instance should prevent another instance from changing its current tracer. Move the reference counter to the trace_array instead. This is marked as "Fixes" but is more of a clean up than a true fix. Backport if you want, but its not critical. Fixes: cf6ab6d9143b1 ("tracing: Add ref count to tracer for when they are being read by pipe") Signed-off-by: Steven Rostedt (VMware) --- kernel/trace/trace.c | 12 ++++++------ kernel/trace/trace.h | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c index 8241d1448d708..64c5b8146ccac 100644 --- a/kernel/trace/trace.c +++ b/kernel/trace/trace.c @@ -5891,7 +5891,7 @@ int tracing_set_tracer(struct trace_array *tr, const char *buf) } /* If trace pipe files are being read, we can't change the tracer */ - if (tr->current_trace->ref) { + if (tr->trace_ref) { ret = -EBUSY; goto out; } @@ -6107,7 +6107,7 @@ static int tracing_open_pipe(struct inode *inode, struct file *filp) nonseekable_open(inode, filp); - tr->current_trace->ref++; + tr->trace_ref++; out: mutex_unlock(&trace_types_lock); return ret; @@ -6126,7 +6126,7 @@ static int tracing_release_pipe(struct inode *inode, struct file *file) mutex_lock(&trace_types_lock); - tr->current_trace->ref--; + tr->trace_ref--; if (iter->trace->pipe_close) iter->trace->pipe_close(iter); @@ -7428,7 +7428,7 @@ static int tracing_buffers_open(struct inode *inode, struct file *filp) filp->private_data = info; - tr->current_trace->ref++; + tr->trace_ref++; mutex_unlock(&trace_types_lock); @@ -7529,7 +7529,7 @@ static int tracing_buffers_release(struct inode *inode, struct file *file) mutex_lock(&trace_types_lock); - iter->tr->current_trace->ref--; + iter->tr->trace_ref--; __trace_array_put(iter->tr); @@ -8737,7 +8737,7 @@ static int __remove_instance(struct trace_array *tr) int i; /* Reference counter for a newly created trace array = 1. */ - if (tr->ref > 1 || (tr->current_trace && tr->current_trace->ref)) + if (tr->ref > 1 || (tr->current_trace && tr->trace_ref)) return -EBUSY; list_del(&tr->list); diff --git a/kernel/trace/trace.h b/kernel/trace/trace.h index 13db4000af3fe..f21607f871891 100644 --- a/kernel/trace/trace.h +++ b/kernel/trace/trace.h @@ -356,6 +356,7 @@ struct trace_array { struct trace_event_file *trace_marker_file; cpumask_var_t tracing_cpumask; /* only trace on set CPUs */ int ref; + int trace_ref; #ifdef CONFIG_FUNCTION_TRACER struct ftrace_ops *ops; struct trace_pid_list __rcu *function_pids; @@ -547,7 +548,6 @@ struct tracer { struct tracer *next; struct tracer_flags *flags; int enabled; - int ref; bool print_max; bool allow_instances; #ifdef CONFIG_TRACER_MAX_TRACE -- GitLab From a389d86f7fd0902e4ce4136a5601988dbd371eb1 Mon Sep 17 00:00:00 2001 From: "Steven Rostedt (VMware)" Date: Sun, 28 Jun 2020 22:52:25 -0400 Subject: [PATCH 0203/1476] ring-buffer: Have nested events still record running time stamp Up until now, if an event is interrupted while it is recorded by an interrupt, and that interrupt records events, the time of those events will all be the same. This is because events only record the delta of the time since the previous event (or beginning of a page), and to handle updating the time keeping for that of nested events is extremely racy. After years of thinking about this and several failed attempts, I finally have a solution to solve this puzzle. The problem is that you need to atomically calculate the delta and then update the time stamp you made the delta from, as well as then record it into the buffer, all this while at any time an interrupt can come in and do the same thing. This is easy to solve with heavy weight atomics, but that would be detrimental to the performance of the ring buffer. The current state of affairs sacrificed the time deltas for nested events for performance. The reason for previous failed attempts at solving this puzzle was because I was trying to completely avoid slow atomic operations like cmpxchg. I final came to the conclusion to always avoid cmpxchg is not possible, which is why those previous attempts always failed. But it is possible to pick one path (the most common case) and avoid cmpxchg in that path, which is the "fast path". The most common case is that an event will not be interrupted and have other events added into it. An event can detect if it has interrupted another event, and for these cases we can make it the slow path and use the heavy operations like cmpxchg. One more player was added to the game that made this possible, and that is the "absolute timestamp" (by Tom Zanussi) that allows us to inject a full 59 bit time stamp. (Of course this breaks if a machine is running for more than 18 years without a reboot!). There's barrier() placements around for being paranoid, even when they are not needed because of other atomic functions near by. But those should not hurt, as if they are not needed, they basically become a nop. Note, this also makes the race window much smaller, which means there are less slow paths to slow down the performance. The basic idea is that there's two main paths taken. 1) Not being interrupted between time stamps and reserving buffer space. In this case, the time stamps taken are true to the location in the buffer. 2) Was interrupted by another path between taking time stamps and reserving buffer space. The objective is to know what the delta is from the last reserved location in the buffer. As it is possible to detect if an event is interrupting another event before reserving data, space is added to the length to be reserved to inject a full time stamp along with the event being reserved. When an event is not interrupted, the write stamp is always the time of the last event written to the buffer. In path 1, there's two sub paths we care about: a) The event did not interrupt another event. b) The event interrupted another event. In case a, as the write stamp was read and known to be correct, the delta between the current time stamp and the write stamp is the delta between the current event and the previously recorded event. In case b, extra space was reserved to just put the full time stamp into the buffer. Which is done, as stated, in this path the time stamp taken is known to match the location in the buffer. In path 2, there's also two sub paths we care about: a) The event was not interrupted by another event since it reserved space on the buffer and re-reading the write stamp. b) The event was interrupted by another event. In case a, the write stamp is that of the last event that interrupted this event between taking the time stamps and reserving. As no event came in after re-reading the write stamp, that event is known to be the time of the event directly before this event and the delta can be the new time stamp and the write stamp. In case b, one or more events came in between reserving the event and re-reading he write stamp. Since this event's buffer reservation is between other events at this path, there's no way to know what the delta is. But because an event interrupted this event after it started, its fine to just give a zero delta, and take the same time stamp as the events that happened within the event being recorded. Here's the implementation of the design of this solution: All this is per cpu, and only needs to worry about nested events (not parallel events). The players: write_tail: The index in the buffer where new events can be written to. It is incremented via local_add() to reserve space for a new event. before_stamp: A time stamp set by all events before reserving space. write_stamp: A time stamp updated by events after it has successfully reserved space. /* Save the current position of write */ [A] w = local_read(write_tail); barrier(); /* Read both before and write stamps before touching anything */ before = local_read(before_stamp); after = local_read(write_stamp); barrier(); /* * If before and after are the same, then this event is not * interrupting a time update. If it is, then reserve space for adding * a full time stamp (this can turn into a time extend which is * just an extended time delta but fill up the extra space). */ if (after != before) abs = true; ts = clock(); /* Now update the before_stamp (everyone does this!) */ [B] local_set(before_stamp, ts); /* Now reserve space on the buffer */ [C] write = local_add_return(len, write_tail); /* Set tail to be were this event's data is */ tail = write - len; if (w == tail) { /* Nothing interrupted this between A and C */ [D] local_set(write_stamp, ts); barrier(); [E] save_before = local_read(before_stamp); if (!abs) { /* This did not interrupt a time update */ delta = ts - after; } else { delta = ts; /* The full time stamp will be in use */ } if (ts != save_before) { /* slow path - Was interrupted between C and E */ /* The update to write_stamp could have overwritten the update to * it by the interrupting event, but before and after should be * the same for all completed top events */ after = local_read(write_stamp); if (save_before > after) local_cmpxchg(write_stamp, after, save_before); } } else { /* slow path - Interrupted between A and C */ after = local_read(write_stamp); temp_ts = clock(); barrier(); [F] if (write == local_read(write_tail) && after < temp_ts) { /* This was not interrupted since C and F * The last write_stamp is still valid for the previous event * in the buffer. */ delta = temp_ts - after; /* OK to keep this new time stamp */ ts = temp_ts; } else { /* Interrupted between C and F * Well, there's no use to try to know what the time stamp * is for the previous event. Just set delta to zero and * be the same time as that event that interrupted us before * the reservation of the buffer. */ delta = 0; } /* No need to use full timestamps here */ abs = 0; } Link: https://lkml.kernel.org/r/20200625094454.732790f7@oasis.local.home Link: https://lore.kernel.org/r/20200627010041.517736087@goodmis.org Link: http://lkml.kernel.org/r/20200629025258.957440797@goodmis.org Reviewed-by: Masami Hiramatsu Signed-off-by: Steven Rostedt (VMware) --- kernel/trace/ring_buffer.c | 276 ++++++++++++++++++++++++------------- 1 file changed, 181 insertions(+), 95 deletions(-) diff --git a/kernel/trace/ring_buffer.c b/kernel/trace/ring_buffer.c index 00867ff82412a..026238c55b0cc 100644 --- a/kernel/trace/ring_buffer.c +++ b/kernel/trace/ring_buffer.c @@ -27,6 +27,7 @@ #include #include +#include static void update_pages_handler(struct work_struct *work); @@ -418,6 +419,17 @@ struct rb_event_info { int add_timestamp; }; +/* + * Used for the add_timestamp + * NONE + * NORMAL - may be for either time extend or absolute + * FORCE - force a full time stamp. + */ +enum { + RB_ADD_STAMP_NONE, + RB_ADD_STAMP_NORMAL, + RB_ADD_STAMP_FORCE +}; /* * Used for which event context the event is in. * NMI = 0 @@ -470,7 +482,8 @@ struct ring_buffer_per_cpu { size_t shortest_full; unsigned long read; unsigned long read_bytes; - u64 write_stamp; + local64_t write_stamp; + local64_t before_stamp; u64 read_stamp; /* ring buffer pages to update, > 0 to add, < 0 to remove */ long nr_pages_to_update; @@ -2416,16 +2429,13 @@ rb_update_event(struct ring_buffer_per_cpu *cpu_buffer, unsigned length = info->length; u64 delta = info->delta; - /* Only a commit updates the timestamp */ - if (unlikely(!rb_event_is_commit(cpu_buffer, event))) - delta = 0; - /* * If we need to add a timestamp, then we * add it to the start of the reserved space. */ if (unlikely(info->add_timestamp)) { - bool abs = ring_buffer_time_stamp_abs(cpu_buffer->buffer); + bool abs = info->add_timestamp == RB_ADD_STAMP_FORCE || + ring_buffer_time_stamp_abs(cpu_buffer->buffer); event = rb_add_time_stamp(event, abs ? info->delta : delta, abs); length -= RB_LEN_TIME_EXTEND; @@ -2480,6 +2490,39 @@ static inline bool sched_clock_stable(void) } #endif +static __always_inline bool +rb_event_is_commit(struct ring_buffer_per_cpu *cpu_buffer, + struct ring_buffer_event *event) +{ + unsigned long addr = (unsigned long)event; + unsigned long index; + + index = rb_event_index(event); + addr &= PAGE_MASK; + + return cpu_buffer->commit_page->page == (void *)addr && + rb_commit_index(cpu_buffer) == index; +} + +static u64 rb_time_delta(struct ring_buffer_event *event) +{ + switch (event->type_len) { + case RINGBUF_TYPE_PADDING: + return 0; + + case RINGBUF_TYPE_TIME_EXTEND: + return ring_buffer_event_time_stamp(event); + + case RINGBUF_TYPE_TIME_STAMP: + return 0; + + case RINGBUF_TYPE_DATA: + return event->time_delta; + default: + return 0; + } +} + static inline int rb_try_to_discard(struct ring_buffer_per_cpu *cpu_buffer, struct ring_buffer_event *event) @@ -2488,6 +2531,8 @@ rb_try_to_discard(struct ring_buffer_per_cpu *cpu_buffer, struct buffer_page *bpage; unsigned long index; unsigned long addr; + u64 write_stamp; + u64 delta; new_index = rb_event_index(event); old_index = new_index + rb_event_ts_length(event); @@ -2496,10 +2541,32 @@ rb_try_to_discard(struct ring_buffer_per_cpu *cpu_buffer, bpage = READ_ONCE(cpu_buffer->tail_page); + delta = rb_time_delta(event); + + write_stamp = local64_read(&cpu_buffer->write_stamp); + + /* Make sure the write stamp is read before testing the location */ + barrier(); + if (bpage->page == (void *)addr && rb_page_write(bpage) == old_index) { unsigned long write_mask = local_read(&bpage->write) & ~RB_WRITE_MASK; unsigned long event_length = rb_event_length(event); + u64 ret; + + ret = local64_cmpxchg(&cpu_buffer->write_stamp, write_stamp, write_stamp - delta); + /* Something came in, can't discard */ + if (ret != write_stamp) + return 0; + + /* + * If an event were to come in now, it would see that the + * write_stamp and the before_stamp are different, and assume + * that this event just added itself before updating + * the write stamp. The interrupting event will fix the + * write stamp for us, and use the before stamp as its delta. + */ + /* * This is on the tail page. It is possible that * a write could come in and move the tail page @@ -2551,10 +2618,6 @@ rb_set_commit_to_write(struct ring_buffer_per_cpu *cpu_buffer) local_set(&cpu_buffer->commit_page->page->commit, rb_page_write(cpu_buffer->commit_page)); rb_inc_page(cpu_buffer, &cpu_buffer->commit_page); - /* Only update the write stamp if the page has an event */ - if (rb_page_write(cpu_buffer->commit_page)) - cpu_buffer->write_stamp = - cpu_buffer->commit_page->page->time_stamp; /* add barrier to keep gcc from optimizing too much */ barrier(); } @@ -2626,54 +2689,10 @@ static inline void rb_event_discard(struct ring_buffer_event *event) event->time_delta = 1; } -static __always_inline bool -rb_event_is_commit(struct ring_buffer_per_cpu *cpu_buffer, - struct ring_buffer_event *event) -{ - unsigned long addr = (unsigned long)event; - unsigned long index; - - index = rb_event_index(event); - addr &= PAGE_MASK; - - return cpu_buffer->commit_page->page == (void *)addr && - rb_commit_index(cpu_buffer) == index; -} - -static __always_inline void -rb_update_write_stamp(struct ring_buffer_per_cpu *cpu_buffer, - struct ring_buffer_event *event) -{ - u64 delta; - - /* - * The event first in the commit queue updates the - * time stamp. - */ - if (rb_event_is_commit(cpu_buffer, event)) { - /* - * A commit event that is first on a page - * updates the write timestamp with the page stamp - */ - if (!rb_event_index(event)) - cpu_buffer->write_stamp = - cpu_buffer->commit_page->page->time_stamp; - else if (event->type_len == RINGBUF_TYPE_TIME_EXTEND) { - delta = ring_buffer_event_time_stamp(event); - cpu_buffer->write_stamp += delta; - } else if (event->type_len == RINGBUF_TYPE_TIME_STAMP) { - delta = ring_buffer_event_time_stamp(event); - cpu_buffer->write_stamp = delta; - } else - cpu_buffer->write_stamp += event->time_delta; - } -} - static void rb_commit(struct ring_buffer_per_cpu *cpu_buffer, struct ring_buffer_event *event) { local_inc(&cpu_buffer->entries); - rb_update_write_stamp(cpu_buffer, event); rb_end_commit(cpu_buffer); } @@ -2872,13 +2891,13 @@ rb_handle_timestamp(struct ring_buffer_per_cpu *cpu_buffer, KERN_WARNING "Delta way too big! %llu ts=%llu write stamp = %llu\n%s", (unsigned long long)info->delta, (unsigned long long)info->ts, - (unsigned long long)cpu_buffer->write_stamp, + (unsigned long long)local64_read(&cpu_buffer->write_stamp), sched_clock_stable() ? "" : "If you just came from a suspend/resume,\n" "please switch to the trace global clock:\n" " echo global > /sys/kernel/debug/tracing/trace_clock\n" "or add trace_clock=global to the kernel command line\n"); - info->add_timestamp = 1; + info->add_timestamp = RB_ADD_STAMP_NORMAL; } static struct ring_buffer_event * @@ -2887,8 +2906,36 @@ __rb_reserve_next(struct ring_buffer_per_cpu *cpu_buffer, { struct ring_buffer_event *event; struct buffer_page *tail_page; - unsigned long tail, write; + unsigned long tail, write, w; + u64 before, after; + bool abs = false; + + /* Don't let the compiler play games with cpu_buffer->tail_page */ + tail_page = info->tail_page = READ_ONCE(cpu_buffer->tail_page); + + /*A*/ w = local_read(&tail_page->write) & RB_WRITE_MASK; + barrier(); + before = local64_read(&cpu_buffer->before_stamp); + after = local64_read(&cpu_buffer->write_stamp); + barrier(); + info->ts = rb_time_stamp(cpu_buffer->buffer); + + if (ring_buffer_time_stamp_abs(cpu_buffer->buffer)) { + info->delta = info->ts; + abs = true; + } else { + info->delta = info->ts - after; + } + + if (unlikely(test_time_stamp(info->delta))) + rb_handle_timestamp(cpu_buffer, info); + /* + * If interrupting an event time update, we may need an absolute timestamp. + * Don't bother if this is the start of a new page (w == 0). + */ + if (unlikely(before != after && w)) + info->add_timestamp = RB_ADD_STAMP_FORCE; /* * If the time delta since the last event is too big to * hold in the time field of the event, then we append a @@ -2897,25 +2944,88 @@ __rb_reserve_next(struct ring_buffer_per_cpu *cpu_buffer, if (unlikely(info->add_timestamp)) info->length += RB_LEN_TIME_EXTEND; - /* Don't let the compiler play games with cpu_buffer->tail_page */ - tail_page = info->tail_page = READ_ONCE(cpu_buffer->tail_page); - write = local_add_return(info->length, &tail_page->write); + /*B*/ local64_set(&cpu_buffer->before_stamp, info->ts); + + /*C*/ write = local_add_return(info->length, &tail_page->write); /* set write to only the index of the write */ write &= RB_WRITE_MASK; + tail = write - info->length; + /* See if we shot pass the end of this buffer page */ + if (unlikely(write > BUF_PAGE_SIZE)) { + if (tail != w) { + /* before and after may now different, fix it up*/ + before = local64_read(&cpu_buffer->before_stamp); + after = local64_read(&cpu_buffer->write_stamp); + if (before != after) + (void)local64_cmpxchg(&cpu_buffer->before_stamp, before, after); + } + return rb_move_tail(cpu_buffer, tail, info); + } + + if (likely(tail == w)) { + u64 save_before; + + /* Nothing interrupted us between A and C */ + /*D*/ local64_set(&cpu_buffer->write_stamp, info->ts); + barrier(); + /*E*/ save_before = local64_read(&cpu_buffer->before_stamp); + if (likely(info->add_timestamp != RB_ADD_STAMP_FORCE)) + /* This did not interrupt any time update */ + info->delta = info->ts - after; + else + /* Just use full timestamp for inerrupting event */ + info->delta = info->ts; + barrier(); + if (unlikely(info->ts != save_before)) { + /* SLOW PATH - Interrupted between C and E */ + + after = local64_read(&cpu_buffer->write_stamp); + /* Write stamp must only go forward */ + if (save_before > after) { + /* + * We do not care about the result, only that + * it gets updated atomically. + */ + (void)local64_cmpxchg(&cpu_buffer->write_stamp, after, save_before); + } + } + } else { + u64 ts; + /* SLOW PATH - Interrupted between A and C */ + after = local64_read(&cpu_buffer->write_stamp); + ts = rb_time_stamp(cpu_buffer->buffer); + barrier(); + /*E*/ if (write == (local_read(&tail_page->write) & RB_WRITE_MASK) && + after < ts) { + /* Nothing came after this event between C and E */ + info->delta = ts - after; + (void)local64_cmpxchg(&cpu_buffer->write_stamp, after, info->ts); + info->ts = ts; + } else { + /* + * Interrupted beween C and E: + * Lost the previous events time stamp. Just set the + * delta to zero, and this will be the same time as + * the event this event interrupted. And the events that + * came after this will still be correct (as they would + * have built their delta on the previous event. + */ + info->delta = 0; + } + if (info->add_timestamp == RB_ADD_STAMP_FORCE) + info->add_timestamp = RB_ADD_STAMP_NORMAL; + } + /* * If this is the first commit on the page, then it has the same * timestamp as the page itself. */ - if (!tail && !ring_buffer_time_stamp_abs(cpu_buffer->buffer)) + if (unlikely(!tail && info->add_timestamp != RB_ADD_STAMP_FORCE && !abs)) info->delta = 0; - /* See if we shot pass the end of this buffer page */ - if (unlikely(write > BUF_PAGE_SIZE)) - return rb_move_tail(cpu_buffer, tail, info); - /* We reserved something on the buffer */ event = __rb_page_index(tail_page, tail); @@ -2944,9 +3054,9 @@ rb_reserve_next_event(struct trace_buffer *buffer, struct ring_buffer_event *event; struct rb_event_info info; int nr_loops = 0; - u64 diff; rb_start_commit(cpu_buffer); + /* The commit page can not change after this */ #ifdef CONFIG_RING_BUFFER_ALLOW_SWAP /* @@ -2965,7 +3075,7 @@ rb_reserve_next_event(struct trace_buffer *buffer, info.length = rb_calculate_event_length(length); again: - info.add_timestamp = 0; + info.add_timestamp = RB_ADD_STAMP_NONE; info.delta = 0; /* @@ -2980,22 +3090,6 @@ rb_reserve_next_event(struct trace_buffer *buffer, if (RB_WARN_ON(cpu_buffer, ++nr_loops > 1000)) goto out_fail; - info.ts = rb_time_stamp(cpu_buffer->buffer); - diff = info.ts - cpu_buffer->write_stamp; - - /* make sure this diff is calculated here */ - barrier(); - - if (ring_buffer_time_stamp_abs(buffer)) { - info.delta = info.ts; - rb_handle_timestamp(cpu_buffer, &info); - } else /* Did the write stamp get updated already? */ - if (likely(info.ts >= cpu_buffer->write_stamp)) { - info.delta = diff; - if (unlikely(test_time_stamp(info.delta))) - rb_handle_timestamp(cpu_buffer, &info); - } - event = __rb_reserve_next(cpu_buffer, &info); if (unlikely(PTR_ERR(event) == -EAGAIN)) { @@ -3004,11 +3098,8 @@ rb_reserve_next_event(struct trace_buffer *buffer, goto again; } - if (!event) - goto out_fail; - - return event; - + if (likely(event)) + return event; out_fail: rb_end_commit(cpu_buffer); return NULL; @@ -3154,11 +3245,6 @@ void ring_buffer_discard_commit(struct trace_buffer *buffer, if (rb_try_to_discard(cpu_buffer, event)) goto out; - /* - * The commit is still visible by the reader, so we - * must still update the timestamp. - */ - rb_update_write_stamp(cpu_buffer, event); out: rb_end_commit(cpu_buffer); @@ -4475,8 +4561,8 @@ rb_reset_cpu(struct ring_buffer_per_cpu *cpu_buffer) cpu_buffer->read = 0; cpu_buffer->read_bytes = 0; - cpu_buffer->write_stamp = 0; - cpu_buffer->read_stamp = 0; + local64_set(&cpu_buffer->write_stamp, 0); + local64_set(&cpu_buffer->before_stamp, 0); cpu_buffer->lost_events = 0; cpu_buffer->last_overrun = 0; -- GitLab From 7c4b4a5164fbedc11c23e3671bd90ba0d23a5efd Mon Sep 17 00:00:00 2001 From: "Steven Rostedt (VMware)" Date: Sun, 28 Jun 2020 22:52:26 -0400 Subject: [PATCH 0204/1476] ring-buffer: Incorporate absolute timestamp into add_timestamp logic Instead of calling out the absolute test for each time to check if the ring buffer wants absolute time stamps for all its recording, incorporate it with the add_timestamp field and turn it into flags for faster processing between wanting a absolute tag and needing to force one. Link: http://lkml.kernel.org/r/20200629025259.154892368@goodmis.org Signed-off-by: Steven Rostedt (VMware) --- kernel/trace/ring_buffer.c | 40 +++++++++++++++++++++----------------- 1 file changed, 22 insertions(+), 18 deletions(-) diff --git a/kernel/trace/ring_buffer.c b/kernel/trace/ring_buffer.c index 026238c55b0cc..7ee6619951ea3 100644 --- a/kernel/trace/ring_buffer.c +++ b/kernel/trace/ring_buffer.c @@ -422,13 +422,15 @@ struct rb_event_info { /* * Used for the add_timestamp * NONE - * NORMAL - may be for either time extend or absolute + * EXTEND - wants a time extend + * ABSOLUTE - the buffer requests all events to have absolute time stamps * FORCE - force a full time stamp. */ enum { - RB_ADD_STAMP_NONE, - RB_ADD_STAMP_NORMAL, - RB_ADD_STAMP_FORCE + RB_ADD_STAMP_NONE = 0, + RB_ADD_STAMP_EXTEND = BIT(1), + RB_ADD_STAMP_ABSOLUTE = BIT(2), + RB_ADD_STAMP_FORCE = BIT(3) }; /* * Used for which event context the event is in. @@ -2434,8 +2436,8 @@ rb_update_event(struct ring_buffer_per_cpu *cpu_buffer, * add it to the start of the reserved space. */ if (unlikely(info->add_timestamp)) { - bool abs = info->add_timestamp == RB_ADD_STAMP_FORCE || - ring_buffer_time_stamp_abs(cpu_buffer->buffer); + bool abs = info->add_timestamp & + (RB_ADD_STAMP_FORCE | RB_ADD_STAMP_ABSOLUTE); event = rb_add_time_stamp(event, abs ? info->delta : delta, abs); length -= RB_LEN_TIME_EXTEND; @@ -2884,8 +2886,8 @@ int ring_buffer_unlock_commit(struct trace_buffer *buffer, EXPORT_SYMBOL_GPL(ring_buffer_unlock_commit); static noinline void -rb_handle_timestamp(struct ring_buffer_per_cpu *cpu_buffer, - struct rb_event_info *info) +rb_check_timestamp(struct ring_buffer_per_cpu *cpu_buffer, + struct rb_event_info *info) { WARN_ONCE(info->delta > (1ULL << 59), KERN_WARNING "Delta way too big! %llu ts=%llu write stamp = %llu\n%s", @@ -2897,7 +2899,6 @@ rb_handle_timestamp(struct ring_buffer_per_cpu *cpu_buffer, "please switch to the trace global clock:\n" " echo global > /sys/kernel/debug/tracing/trace_clock\n" "or add trace_clock=global to the kernel command line\n"); - info->add_timestamp = RB_ADD_STAMP_NORMAL; } static struct ring_buffer_event * @@ -2908,7 +2909,6 @@ __rb_reserve_next(struct ring_buffer_per_cpu *cpu_buffer, struct buffer_page *tail_page; unsigned long tail, write, w; u64 before, after; - bool abs = false; /* Don't let the compiler play games with cpu_buffer->tail_page */ tail_page = info->tail_page = READ_ONCE(cpu_buffer->tail_page); @@ -2922,20 +2922,23 @@ __rb_reserve_next(struct ring_buffer_per_cpu *cpu_buffer, if (ring_buffer_time_stamp_abs(cpu_buffer->buffer)) { info->delta = info->ts; - abs = true; + info->add_timestamp = RB_ADD_STAMP_ABSOLUTE; } else { info->delta = info->ts - after; } - if (unlikely(test_time_stamp(info->delta))) - rb_handle_timestamp(cpu_buffer, info); + if (unlikely(test_time_stamp(info->delta))) { + rb_check_timestamp(cpu_buffer, info); + info->add_timestamp |= RB_ADD_STAMP_EXTEND; + } /* * If interrupting an event time update, we may need an absolute timestamp. * Don't bother if this is the start of a new page (w == 0). */ if (unlikely(before != after && w)) - info->add_timestamp = RB_ADD_STAMP_FORCE; + info->add_timestamp |= RB_ADD_STAMP_FORCE | RB_ADD_STAMP_EXTEND; + /* * If the time delta since the last event is too big to * hold in the time field of the event, then we append a @@ -2972,7 +2975,8 @@ __rb_reserve_next(struct ring_buffer_per_cpu *cpu_buffer, /*D*/ local64_set(&cpu_buffer->write_stamp, info->ts); barrier(); /*E*/ save_before = local64_read(&cpu_buffer->before_stamp); - if (likely(info->add_timestamp != RB_ADD_STAMP_FORCE)) + if (likely(!(info->add_timestamp & + (RB_ADD_STAMP_FORCE | RB_ADD_STAMP_ABSOLUTE)))) /* This did not interrupt any time update */ info->delta = info->ts - after; else @@ -3015,15 +3019,15 @@ __rb_reserve_next(struct ring_buffer_per_cpu *cpu_buffer, */ info->delta = 0; } - if (info->add_timestamp == RB_ADD_STAMP_FORCE) - info->add_timestamp = RB_ADD_STAMP_NORMAL; + info->add_timestamp &= ~RB_ADD_STAMP_FORCE; } /* * If this is the first commit on the page, then it has the same * timestamp as the page itself. */ - if (unlikely(!tail && info->add_timestamp != RB_ADD_STAMP_FORCE && !abs)) + if (unlikely(!tail && !(info->add_timestamp & + (RB_ADD_STAMP_FORCE | RB_ADD_STAMP_ABSOLUTE)))) info->delta = 0; /* We reserved something on the buffer */ -- GitLab From 10464b4aa605ef93c937452f442e74cc0a4a6ceb Mon Sep 17 00:00:00 2001 From: "Steven Rostedt (VMware)" Date: Sun, 28 Jun 2020 22:52:27 -0400 Subject: [PATCH 0205/1476] ring-buffer: Add rb_time_t 64 bit operations for speeding up 32 bit After a discussion with the new time algorithm to have nested events still have proper time keeping but required using local64_t atomic operations. Mathieu was concerned about the performance this would have on 32 bit machines, as in most cases, atomic 64 bit operations on them can be expensive. As the ring buffer's timing needs do not require full features of local64_t, a wrapper is made to implement a new rb_time_t operation that uses two longs on 32 bit machines but still uses the local64_t operations on 64 bit machines. There's a switch that can be made in the file to force 64 bit to use the 32 bit version just for testing purposes. All reads do not need to succeed if a read happened while the stamp being read is in the process of being updated. The requirement is that all reads must succed that were done by an interrupting event (where this event was interrupted by another event that did the write). Or if the event itself did the write first. That is: rb_time_set(t, x) followed by rb_time_read(t) will always succeed (even if it gets interrupted by another event that writes to t. The result of the read will be either the previous set, or a set performed by an interrupting event. If the read is done by an event that interrupted another event that was in the process of setting the time stamp, and no other event came along to write to that time stamp, it will fail and the rb_time_read() will return that it failed (the value to read will be undefined). A set will always write to the time stamp and return with a valid time stamp, such that any read after it will be valid. A cmpxchg may fail if it interrupted an event that was in the process of updating the time stamp just like the reads do. Other than that, it will act like a normal cmpxchg. The way this works is that the rb_time_t is made of of three fields. A cnt, that gets updated atomically everyting a modification is made. A top that represents the most significant 30 bits of the time, and a bottom to represent the least significant 30 bits of the time. Notice, that the time values is only 60 bits long (where the ring buffer only uses 59 bits, which gives us 18 years of nanoseconds!). The top two bits of both the top and bottom is a 2 bit counter that gets set by the value of the least two significant bits of the cnt. A read of the top and the bottom where both the top and bottom have the same most significant top 2 bits, are considered a match and a valid 60 bit number can be created from it. If they do not match, then the number is considered invalid, and this must only happen if an event interrupted another event in the midst of updating the time stamp. This is only used for 32 bits machines as 64 bit machines can get better performance out of the local64_t. This has been tested heavily by forcing 64 bit to use this logic. Link: https://lore.kernel.org/r/20200625225345.18cf5881@oasis.local.home Link: http://lkml.kernel.org/r/20200629025259.309232719@goodmis.org Inspired-by: Mathieu Desnoyers Signed-off-by: Steven Rostedt (VMware) --- kernel/trace/ring_buffer.c | 270 +++++++++++++++++++++++++++++++++---- 1 file changed, 243 insertions(+), 27 deletions(-) diff --git a/kernel/trace/ring_buffer.c b/kernel/trace/ring_buffer.c index 7ee6619951ea3..802bb38d9c81e 100644 --- a/kernel/trace/ring_buffer.c +++ b/kernel/trace/ring_buffer.c @@ -27,7 +27,6 @@ #include #include -#include static void update_pages_handler(struct work_struct *work); @@ -449,6 +448,28 @@ enum { RB_CTX_MAX }; +#if BITS_PER_LONG == 32 +#define RB_TIME_32 +#endif + +/* To test on 64 bit machines */ +//#define RB_TIME_32 + +#ifdef RB_TIME_32 + +struct rb_time_struct { + local_t cnt; + local_t top; + local_t bottom; +}; +#else +#include +struct rb_time_struct { + local64_t time; +}; +#endif +typedef struct rb_time_struct rb_time_t; + /* * head_page == tail_page && head == tail then buffer is empty. */ @@ -484,8 +505,8 @@ struct ring_buffer_per_cpu { size_t shortest_full; unsigned long read; unsigned long read_bytes; - local64_t write_stamp; - local64_t before_stamp; + rb_time_t write_stamp; + rb_time_t before_stamp; u64 read_stamp; /* ring buffer pages to update, > 0 to add, < 0 to remove */ long nr_pages_to_update; @@ -528,6 +549,189 @@ struct ring_buffer_iter { int missed_events; }; +#ifdef RB_TIME_32 + +/* + * On 32 bit machines, local64_t is very expensive. As the ring + * buffer doesn't need all the features of a true 64 bit atomic, + * on 32 bit, it uses these functions (64 still uses local64_t). + * + * For the ring buffer, 64 bit required operations for the time is + * the following: + * + * - Only need 59 bits (uses 60 to make it even). + * - Reads may fail if it interrupted a modification of the time stamp. + * It will succeed if it did not interrupt another write even if + * the read itself is interrupted by a write. + * It returns whether it was successful or not. + * + * - Writes always succeed and will overwrite other writes and writes + * that were done by events interrupting the current write. + * + * - A write followed by a read of the same time stamp will always succeed, + * but may not contain the same value. + * + * - A cmpxchg will fail if it interrupted another write or cmpxchg. + * Other than that, it acts like a normal cmpxchg. + * + * The 60 bit time stamp is broken up by 30 bits in a top and bottom half + * (bottom being the least significant 30 bits of the 60 bit time stamp). + * + * The two most significant bits of each half holds a 2 bit counter (0-3). + * Each update will increment this counter by one. + * When reading the top and bottom, if the two counter bits match then the + * top and bottom together make a valid 60 bit number. + */ +#define RB_TIME_SHIFT 30 +#define RB_TIME_VAL_MASK ((1 << RB_TIME_SHIFT) - 1) + +static inline int rb_time_cnt(unsigned long val) +{ + return (val >> RB_TIME_SHIFT) & 3; +} + +static inline u64 rb_time_val(unsigned long top, unsigned long bottom) +{ + u64 val; + + val = top & RB_TIME_VAL_MASK; + val <<= RB_TIME_SHIFT; + val |= bottom & RB_TIME_VAL_MASK; + + return val; +} + +static inline bool __rb_time_read(rb_time_t *t, u64 *ret, unsigned long *cnt) +{ + unsigned long top, bottom; + unsigned long c; + + /* + * If the read is interrupted by a write, then the cnt will + * be different. Loop until both top and bottom have been read + * without interruption. + */ + do { + c = local_read(&t->cnt); + top = local_read(&t->top); + bottom = local_read(&t->bottom); + } while (c != local_read(&t->cnt)); + + *cnt = rb_time_cnt(top); + + /* If top and bottom counts don't match, this interrupted a write */ + if (*cnt != rb_time_cnt(bottom)) + return false; + + *ret = rb_time_val(top, bottom); + return true; +} + +static bool rb_time_read(rb_time_t *t, u64 *ret) +{ + unsigned long cnt; + + return __rb_time_read(t, ret, &cnt); +} + +static inline unsigned long rb_time_val_cnt(unsigned long val, unsigned long cnt) +{ + return (val & RB_TIME_VAL_MASK) | ((cnt & 3) << RB_TIME_SHIFT); +} + +static inline void rb_time_split(u64 val, unsigned long *top, unsigned long *bottom) +{ + *top = (unsigned long)((val >> RB_TIME_SHIFT) & RB_TIME_VAL_MASK); + *bottom = (unsigned long)(val & RB_TIME_VAL_MASK); +} + +static inline void rb_time_val_set(local_t *t, unsigned long val, unsigned long cnt) +{ + val = rb_time_val_cnt(val, cnt); + local_set(t, val); +} + +static void rb_time_set(rb_time_t *t, u64 val) +{ + unsigned long cnt, top, bottom; + + rb_time_split(val, &top, &bottom); + + /* Writes always succeed with a valid number even if it gets interrupted. */ + do { + cnt = local_inc_return(&t->cnt); + rb_time_val_set(&t->top, top, cnt); + rb_time_val_set(&t->bottom, bottom, cnt); + } while (cnt != local_read(&t->cnt)); +} + +static inline bool +rb_time_read_cmpxchg(local_t *l, unsigned long expect, unsigned long set) +{ + unsigned long ret; + + ret = local_cmpxchg(l, expect, set); + return ret == expect; +} + +static int rb_time_cmpxchg(rb_time_t *t, u64 expect, u64 set) +{ + unsigned long cnt, top, bottom; + unsigned long cnt2, top2, bottom2; + u64 val; + + /* The cmpxchg always fails if it interrupted an update */ + if (!__rb_time_read(t, &val, &cnt2)) + return false; + + if (val != expect) + return false; + + cnt = local_read(&t->cnt); + if ((cnt & 3) != cnt2) + return false; + + cnt2 = cnt + 1; + + rb_time_split(val, &top, &bottom); + top = rb_time_val_cnt(top, cnt); + bottom = rb_time_val_cnt(bottom, cnt); + + rb_time_split(set, &top2, &bottom2); + top2 = rb_time_val_cnt(top2, cnt2); + bottom2 = rb_time_val_cnt(bottom2, cnt2); + + if (!rb_time_read_cmpxchg(&t->cnt, cnt, cnt2)) + return false; + if (!rb_time_read_cmpxchg(&t->top, top, top2)) + return false; + if (!rb_time_read_cmpxchg(&t->bottom, bottom, bottom2)) + return false; + return true; +} + +#else /* 64 bits */ + +/* local64_t always succeeds */ + +static inline bool rb_time_read(rb_time_t *t, u64 *ret) +{ + *ret = local64_read(&t->time); + return true; +} +static void rb_time_set(rb_time_t *t, u64 val) +{ + local64_set(&t->time, val); +} + +static bool rb_time_cmpxchg(rb_time_t *t, u64 expect, u64 set) +{ + u64 val; + val = local64_cmpxchg(&t->time, expect, set); + return val == expect; +} +#endif + /** * ring_buffer_nr_pages - get the number of buffer pages in the ring buffer * @buffer: The ring_buffer to get the number of pages from @@ -2545,7 +2749,8 @@ rb_try_to_discard(struct ring_buffer_per_cpu *cpu_buffer, delta = rb_time_delta(event); - write_stamp = local64_read(&cpu_buffer->write_stamp); + if (!rb_time_read(&cpu_buffer->write_stamp, &write_stamp)) + return 0; /* Make sure the write stamp is read before testing the location */ barrier(); @@ -2554,11 +2759,10 @@ rb_try_to_discard(struct ring_buffer_per_cpu *cpu_buffer, unsigned long write_mask = local_read(&bpage->write) & ~RB_WRITE_MASK; unsigned long event_length = rb_event_length(event); - u64 ret; - ret = local64_cmpxchg(&cpu_buffer->write_stamp, write_stamp, write_stamp - delta); /* Something came in, can't discard */ - if (ret != write_stamp) + if (!rb_time_cmpxchg(&cpu_buffer->write_stamp, + write_stamp, write_stamp - delta)) return 0; /* @@ -2889,11 +3093,13 @@ static noinline void rb_check_timestamp(struct ring_buffer_per_cpu *cpu_buffer, struct rb_event_info *info) { + u64 write_stamp; + WARN_ONCE(info->delta > (1ULL << 59), KERN_WARNING "Delta way too big! %llu ts=%llu write stamp = %llu\n%s", (unsigned long long)info->delta, (unsigned long long)info->ts, - (unsigned long long)local64_read(&cpu_buffer->write_stamp), + (unsigned long long)(rb_time_read(&cpu_buffer->write_stamp, &write_stamp) ? write_stamp : 0), sched_clock_stable() ? "" : "If you just came from a suspend/resume,\n" "please switch to the trace global clock:\n" @@ -2909,14 +3115,16 @@ __rb_reserve_next(struct ring_buffer_per_cpu *cpu_buffer, struct buffer_page *tail_page; unsigned long tail, write, w; u64 before, after; + bool a_ok; + bool b_ok; /* Don't let the compiler play games with cpu_buffer->tail_page */ tail_page = info->tail_page = READ_ONCE(cpu_buffer->tail_page); /*A*/ w = local_read(&tail_page->write) & RB_WRITE_MASK; barrier(); - before = local64_read(&cpu_buffer->before_stamp); - after = local64_read(&cpu_buffer->write_stamp); + b_ok = rb_time_read(&cpu_buffer->before_stamp, &before); + a_ok = rb_time_read(&cpu_buffer->write_stamp, &after); barrier(); info->ts = rb_time_stamp(cpu_buffer->buffer); @@ -2927,16 +3135,18 @@ __rb_reserve_next(struct ring_buffer_per_cpu *cpu_buffer, info->delta = info->ts - after; } - if (unlikely(test_time_stamp(info->delta))) { - rb_check_timestamp(cpu_buffer, info); - info->add_timestamp |= RB_ADD_STAMP_EXTEND; + if (likely(a_ok && b_ok)) { + if (unlikely(test_time_stamp(info->delta))) { + rb_check_timestamp(cpu_buffer, info); + info->add_timestamp |= RB_ADD_STAMP_EXTEND; + } } /* * If interrupting an event time update, we may need an absolute timestamp. * Don't bother if this is the start of a new page (w == 0). */ - if (unlikely(before != after && w)) + if (unlikely(!a_ok || !b_ok || (before != after && w))) info->add_timestamp |= RB_ADD_STAMP_FORCE | RB_ADD_STAMP_EXTEND; /* @@ -2947,7 +3157,7 @@ __rb_reserve_next(struct ring_buffer_per_cpu *cpu_buffer, if (unlikely(info->add_timestamp)) info->length += RB_LEN_TIME_EXTEND; - /*B*/ local64_set(&cpu_buffer->before_stamp, info->ts); + /*B*/ rb_time_set(&cpu_buffer->before_stamp, info->ts); /*C*/ write = local_add_return(info->length, &tail_page->write); @@ -2960,21 +3170,23 @@ __rb_reserve_next(struct ring_buffer_per_cpu *cpu_buffer, if (unlikely(write > BUF_PAGE_SIZE)) { if (tail != w) { /* before and after may now different, fix it up*/ - before = local64_read(&cpu_buffer->before_stamp); - after = local64_read(&cpu_buffer->write_stamp); - if (before != after) - (void)local64_cmpxchg(&cpu_buffer->before_stamp, before, after); + b_ok = rb_time_read(&cpu_buffer->before_stamp, &before); + a_ok = rb_time_read(&cpu_buffer->write_stamp, &after); + if (a_ok && b_ok && before != after) + (void)rb_time_cmpxchg(&cpu_buffer->before_stamp, before, after); } return rb_move_tail(cpu_buffer, tail, info); } if (likely(tail == w)) { u64 save_before; + bool s_ok; /* Nothing interrupted us between A and C */ - /*D*/ local64_set(&cpu_buffer->write_stamp, info->ts); + /*D*/ rb_time_set(&cpu_buffer->write_stamp, info->ts); barrier(); - /*E*/ save_before = local64_read(&cpu_buffer->before_stamp); + /*E*/ s_ok = rb_time_read(&cpu_buffer->before_stamp, &save_before); + RB_WARN_ON(cpu_buffer, !s_ok); if (likely(!(info->add_timestamp & (RB_ADD_STAMP_FORCE | RB_ADD_STAMP_ABSOLUTE)))) /* This did not interrupt any time update */ @@ -2986,27 +3198,31 @@ __rb_reserve_next(struct ring_buffer_per_cpu *cpu_buffer, if (unlikely(info->ts != save_before)) { /* SLOW PATH - Interrupted between C and E */ - after = local64_read(&cpu_buffer->write_stamp); + a_ok = rb_time_read(&cpu_buffer->write_stamp, &after); + RB_WARN_ON(cpu_buffer, !a_ok); + /* Write stamp must only go forward */ if (save_before > after) { /* * We do not care about the result, only that * it gets updated atomically. */ - (void)local64_cmpxchg(&cpu_buffer->write_stamp, after, save_before); + (void)rb_time_cmpxchg(&cpu_buffer->write_stamp, after, save_before); } } } else { u64 ts; /* SLOW PATH - Interrupted between A and C */ - after = local64_read(&cpu_buffer->write_stamp); + a_ok = rb_time_read(&cpu_buffer->write_stamp, &after); + /* Was interrupted before here, write_stamp must be valid */ + RB_WARN_ON(cpu_buffer, !a_ok); ts = rb_time_stamp(cpu_buffer->buffer); barrier(); /*E*/ if (write == (local_read(&tail_page->write) & RB_WRITE_MASK) && after < ts) { /* Nothing came after this event between C and E */ info->delta = ts - after; - (void)local64_cmpxchg(&cpu_buffer->write_stamp, after, info->ts); + (void)rb_time_cmpxchg(&cpu_buffer->write_stamp, after, info->ts); info->ts = ts; } else { /* @@ -4565,8 +4781,8 @@ rb_reset_cpu(struct ring_buffer_per_cpu *cpu_buffer) cpu_buffer->read = 0; cpu_buffer->read_bytes = 0; - local64_set(&cpu_buffer->write_stamp, 0); - local64_set(&cpu_buffer->before_stamp, 0); + rb_time_set(&cpu_buffer->write_stamp, 0); + rb_time_set(&cpu_buffer->before_stamp, 0); cpu_buffer->lost_events = 0; cpu_buffer->last_overrun = 0; -- GitLab From b23d7a5f4a07af02343cdd28fe1f7488bac3afda Mon Sep 17 00:00:00 2001 From: Nicholas Piggin Date: Thu, 25 Jun 2020 15:34:03 +1000 Subject: [PATCH 0206/1476] ring-buffer: speed up buffer resets by avoiding synchronize_rcu for each CPU On a 144 thread system, `perf ftrace` takes about 20 seconds to start up, due to calling synchronize_rcu() for each CPU. cat /proc/108560/stack 0xc0003e7eb336f470 __switch_to+0x2e0/0x480 __wait_rcu_gp+0x20c/0x220 synchronize_rcu+0x9c/0xc0 ring_buffer_reset_cpu+0x88/0x2e0 tracing_reset_online_cpus+0x84/0xe0 tracing_open+0x1d4/0x1f0 On a system with 10x more threads, it starts to become an annoyance. Batch these up so we disable all the per-cpu buffers first, then synchronize_rcu() once, then reset each of the buffers. This brings the time down to about 0.5s. Link: https://lkml.kernel.org/r/20200625053403.2386972-1-npiggin@gmail.com Tested-by: Anton Blanchard Acked-by: Paul E. McKenney Signed-off-by: Nicholas Piggin Signed-off-by: Steven Rostedt (VMware) --- include/linux/ring_buffer.h | 1 + kernel/trace/ring_buffer.c | 85 +++++++++++++++++++++++++++++++------ kernel/trace/trace.c | 4 +- 3 files changed, 73 insertions(+), 17 deletions(-) diff --git a/include/linux/ring_buffer.h b/include/linux/ring_buffer.h index c76b2f3b3ac40..136ea0997e6df 100644 --- a/include/linux/ring_buffer.h +++ b/include/linux/ring_buffer.h @@ -143,6 +143,7 @@ bool ring_buffer_iter_dropped(struct ring_buffer_iter *iter); unsigned long ring_buffer_size(struct trace_buffer *buffer, int cpu); void ring_buffer_reset_cpu(struct trace_buffer *buffer, int cpu); +void ring_buffer_reset_online_cpus(struct trace_buffer *buffer); void ring_buffer_reset(struct trace_buffer *buffer); #ifdef CONFIG_RING_BUFFER_ALLOW_SWAP diff --git a/kernel/trace/ring_buffer.c b/kernel/trace/ring_buffer.c index 802bb38d9c81e..ed1941304f698 100644 --- a/kernel/trace/ring_buffer.c +++ b/kernel/trace/ring_buffer.c @@ -270,6 +270,9 @@ EXPORT_SYMBOL_GPL(ring_buffer_event_data); #define for_each_buffer_cpu(buffer, cpu) \ for_each_cpu(cpu, buffer->cpumask) +#define for_each_online_buffer_cpu(buffer, cpu) \ + for_each_cpu_and(cpu, buffer->cpumask, cpu_online_mask) + #define TS_SHIFT 27 #define TS_MASK ((1ULL << TS_SHIFT) - 1) #define TS_DELTA_TEST (~TS_MASK) @@ -4790,6 +4793,26 @@ rb_reset_cpu(struct ring_buffer_per_cpu *cpu_buffer) rb_head_page_activate(cpu_buffer); } +/* Must have disabled the cpu buffer then done a synchronize_rcu */ +static void reset_disabled_cpu_buffer(struct ring_buffer_per_cpu *cpu_buffer) +{ + unsigned long flags; + + raw_spin_lock_irqsave(&cpu_buffer->reader_lock, flags); + + if (RB_WARN_ON(cpu_buffer, local_read(&cpu_buffer->committing))) + goto out; + + arch_spin_lock(&cpu_buffer->lock); + + rb_reset_cpu(cpu_buffer); + + arch_spin_unlock(&cpu_buffer->lock); + + out: + raw_spin_unlock_irqrestore(&cpu_buffer->reader_lock, flags); +} + /** * ring_buffer_reset_cpu - reset a ring buffer per CPU buffer * @buffer: The ring buffer to reset a per cpu buffer of @@ -4798,7 +4821,6 @@ rb_reset_cpu(struct ring_buffer_per_cpu *cpu_buffer) void ring_buffer_reset_cpu(struct trace_buffer *buffer, int cpu) { struct ring_buffer_per_cpu *cpu_buffer = buffer->buffers[cpu]; - unsigned long flags; if (!cpumask_test_cpu(cpu, buffer->cpumask)) return; @@ -4809,24 +4831,42 @@ void ring_buffer_reset_cpu(struct trace_buffer *buffer, int cpu) /* Make sure all commits have finished */ synchronize_rcu(); - raw_spin_lock_irqsave(&cpu_buffer->reader_lock, flags); + reset_disabled_cpu_buffer(cpu_buffer); - if (RB_WARN_ON(cpu_buffer, local_read(&cpu_buffer->committing))) - goto out; + atomic_dec(&cpu_buffer->record_disabled); + atomic_dec(&cpu_buffer->resize_disabled); +} +EXPORT_SYMBOL_GPL(ring_buffer_reset_cpu); - arch_spin_lock(&cpu_buffer->lock); +/** + * ring_buffer_reset_cpu - reset a ring buffer per CPU buffer + * @buffer: The ring buffer to reset a per cpu buffer of + * @cpu: The CPU buffer to be reset + */ +void ring_buffer_reset_online_cpus(struct trace_buffer *buffer) +{ + struct ring_buffer_per_cpu *cpu_buffer; + int cpu; - rb_reset_cpu(cpu_buffer); + for_each_online_buffer_cpu(buffer, cpu) { + cpu_buffer = buffer->buffers[cpu]; - arch_spin_unlock(&cpu_buffer->lock); + atomic_inc(&cpu_buffer->resize_disabled); + atomic_inc(&cpu_buffer->record_disabled); + } - out: - raw_spin_unlock_irqrestore(&cpu_buffer->reader_lock, flags); + /* Make sure all commits have finished */ + synchronize_rcu(); - atomic_dec(&cpu_buffer->record_disabled); - atomic_dec(&cpu_buffer->resize_disabled); + for_each_online_buffer_cpu(buffer, cpu) { + cpu_buffer = buffer->buffers[cpu]; + + reset_disabled_cpu_buffer(cpu_buffer); + + atomic_dec(&cpu_buffer->record_disabled); + atomic_dec(&cpu_buffer->resize_disabled); + } } -EXPORT_SYMBOL_GPL(ring_buffer_reset_cpu); /** * ring_buffer_reset - reset a ring buffer @@ -4834,10 +4874,27 @@ EXPORT_SYMBOL_GPL(ring_buffer_reset_cpu); */ void ring_buffer_reset(struct trace_buffer *buffer) { + struct ring_buffer_per_cpu *cpu_buffer; int cpu; - for_each_buffer_cpu(buffer, cpu) - ring_buffer_reset_cpu(buffer, cpu); + for_each_buffer_cpu(buffer, cpu) { + cpu_buffer = buffer->buffers[cpu]; + + atomic_inc(&cpu_buffer->resize_disabled); + atomic_inc(&cpu_buffer->record_disabled); + } + + /* Make sure all commits have finished */ + synchronize_rcu(); + + for_each_buffer_cpu(buffer, cpu) { + cpu_buffer = buffer->buffers[cpu]; + + reset_disabled_cpu_buffer(cpu_buffer); + + atomic_dec(&cpu_buffer->record_disabled); + atomic_dec(&cpu_buffer->resize_disabled); + } } EXPORT_SYMBOL_GPL(ring_buffer_reset); diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c index 64c5b8146ccac..4aab712f95676 100644 --- a/kernel/trace/trace.c +++ b/kernel/trace/trace.c @@ -2003,7 +2003,6 @@ static void tracing_reset_cpu(struct array_buffer *buf, int cpu) void tracing_reset_online_cpus(struct array_buffer *buf) { struct trace_buffer *buffer = buf->buffer; - int cpu; if (!buffer) return; @@ -2015,8 +2014,7 @@ void tracing_reset_online_cpus(struct array_buffer *buf) buf->time_start = buffer_ftrace_now(buf, buf->cpu); - for_each_online_cpu(cpu) - ring_buffer_reset_cpu(buffer, cpu); + ring_buffer_reset_online_cpus(buffer); ring_buffer_record_enable(buffer); } -- GitLab From 75b21c6dfa2d816bcabac24a530d9cffc12092b8 Mon Sep 17 00:00:00 2001 From: "Steven Rostedt (VMware)" Date: Tue, 30 Jun 2020 09:04:35 -0400 Subject: [PATCH 0207/1476] ring-buffer: Mark the !tail (crossing a page) as unlikely It is the uncommon case where an event crosses a sub buffer boundary (page) mark that check at the end of reserving an event as unlikely. Suggested-by: Mathieu Desnoyers Signed-off-by: Steven Rostedt (VMware) --- kernel/trace/ring_buffer.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernel/trace/ring_buffer.c b/kernel/trace/ring_buffer.c index ed1941304f698..888bc91779372 100644 --- a/kernel/trace/ring_buffer.c +++ b/kernel/trace/ring_buffer.c @@ -3260,7 +3260,7 @@ __rb_reserve_next(struct ring_buffer_per_cpu *cpu_buffer, * If this is the first commit on the page, then update * its timestamp. */ - if (!tail) + if (unlikely(!tail)) tail_page->page->time_stamp = info->ts; /* account for these added bytes */ -- GitLab From 30ff3e8c287455e9f7f85162df10d62819ba42a9 Mon Sep 17 00:00:00 2001 From: Piotr Stankiewicz Date: Tue, 16 Jun 2020 09:33:16 +0200 Subject: [PATCH 0208/1476] PCI/MSI: Forward MSI-X error code in pci_alloc_irq_vectors_affinity() When debugging an issue where I was asking the PCI machinery to enable a set of MSI-X vectors, without falling back on MSI, I ran across a behaviour which seems odd. The pci_alloc_irq_vectors_affinity() will always return -ENOSPC on failure, when allocating MSI-X vectors only, whereas with MSI fallback it will forward any error returned by __pci_enable_msi_range(). This is a confusing behaviour, so have the pci_alloc_irq_vectors_affinity() forward the error code from __pci_enable_msix_range() when appropriate. Link: https://lore.kernel.org/r/20200616073318.20229-1-piotr.stankiewicz@intel.com Signed-off-by: Piotr Stankiewicz Signed-off-by: Bjorn Helgaas Reviewed-by: Andy Shevchenko Reviewed-by: Logan Gunthorpe Reviewed-by: Christoph Hellwig --- drivers/pci/msi.c | 22 +++++++++------------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/drivers/pci/msi.c b/drivers/pci/msi.c index 6b43a5455c7af..cade9be68b099 100644 --- a/drivers/pci/msi.c +++ b/drivers/pci/msi.c @@ -1191,8 +1191,7 @@ int pci_alloc_irq_vectors_affinity(struct pci_dev *dev, unsigned int min_vecs, struct irq_affinity *affd) { struct irq_affinity msi_default_affd = {0}; - int msix_vecs = -ENOSPC; - int msi_vecs = -ENOSPC; + int nvecs = -ENOSPC; if (flags & PCI_IRQ_AFFINITY) { if (!affd) @@ -1203,17 +1202,16 @@ int pci_alloc_irq_vectors_affinity(struct pci_dev *dev, unsigned int min_vecs, } if (flags & PCI_IRQ_MSIX) { - msix_vecs = __pci_enable_msix_range(dev, NULL, min_vecs, - max_vecs, affd, flags); - if (msix_vecs > 0) - return msix_vecs; + nvecs = __pci_enable_msix_range(dev, NULL, min_vecs, max_vecs, + affd, flags); + if (nvecs > 0) + return nvecs; } if (flags & PCI_IRQ_MSI) { - msi_vecs = __pci_enable_msi_range(dev, min_vecs, max_vecs, - affd); - if (msi_vecs > 0) - return msi_vecs; + nvecs = __pci_enable_msi_range(dev, min_vecs, max_vecs, affd); + if (nvecs > 0) + return nvecs; } /* use legacy IRQ if allowed */ @@ -1231,9 +1229,7 @@ int pci_alloc_irq_vectors_affinity(struct pci_dev *dev, unsigned int min_vecs, } } - if (msix_vecs == -ENOSPC) - return -ENOSPC; - return msi_vecs; + return nvecs; } EXPORT_SYMBOL(pci_alloc_irq_vectors_affinity); -- GitLab From 58fbc3c63275c6255a10ae151cc3882d9190c4e0 Mon Sep 17 00:00:00 2001 From: "Steven Rostedt (VMware)" Date: Tue, 30 Jun 2020 08:59:26 -0400 Subject: [PATCH 0209/1476] ring-buffer: Consolidate add_timestamp to remove some branches Reorganize a little the logic to handle adding the absolute time stamp, extended and forced time stamps, in such a way to remove a branch or two. This is just a micro optimization. Also add before and after time stamps to the rb_event_info structure to display those values in the rb_check_timestamps() code, if something were to go wrong. Suggested-by: Mathieu Desnoyers Signed-off-by: Steven Rostedt (VMware) --- kernel/trace/ring_buffer.c | 139 +++++++++++++++++++------------------ 1 file changed, 73 insertions(+), 66 deletions(-) diff --git a/kernel/trace/ring_buffer.c b/kernel/trace/ring_buffer.c index 888bc91779372..ce125cbe98a5d 100644 --- a/kernel/trace/ring_buffer.c +++ b/kernel/trace/ring_buffer.c @@ -416,6 +416,8 @@ struct rb_irq_work { struct rb_event_info { u64 ts; u64 delta; + u64 before; + u64 after; unsigned long length; struct buffer_page *tail_page; int add_timestamp; @@ -2619,6 +2621,33 @@ rb_add_time_stamp(struct ring_buffer_event *event, u64 delta, bool abs) static inline bool rb_event_is_commit(struct ring_buffer_per_cpu *cpu_buffer, struct ring_buffer_event *event); +#ifndef CONFIG_HAVE_UNSTABLE_SCHED_CLOCK +static inline bool sched_clock_stable(void) +{ + return true; +} +#endif + +static noinline void +rb_check_timestamp(struct ring_buffer_per_cpu *cpu_buffer, + struct rb_event_info *info) +{ + u64 write_stamp; + + WARN_ONCE(info->delta > (1ULL << 59), + KERN_WARNING "Delta way too big! %llu ts=%llu before=%llu after=%llu write stamp=%llu\n%s", + (unsigned long long)info->delta, + (unsigned long long)info->ts, + (unsigned long long)info->before, + (unsigned long long)info->after, + (unsigned long long)(rb_time_read(&cpu_buffer->write_stamp, &write_stamp) ? write_stamp : 0), + sched_clock_stable() ? "" : + "If you just came from a suspend/resume,\n" + "please switch to the trace global clock:\n" + " echo global > /sys/kernel/debug/tracing/trace_clock\n" + "or add trace_clock=global to the kernel command line\n"); +} + /** * rb_update_event - update event type and data * @cpu_buffer: The per cpu buffer of the @event @@ -2646,6 +2675,7 @@ rb_update_event(struct ring_buffer_per_cpu *cpu_buffer, bool abs = info->add_timestamp & (RB_ADD_STAMP_FORCE | RB_ADD_STAMP_ABSOLUTE); + rb_check_timestamp(cpu_buffer, info); event = rb_add_time_stamp(event, abs ? info->delta : delta, abs); length -= RB_LEN_TIME_EXTEND; delta = 0; @@ -2692,13 +2722,6 @@ static unsigned rb_calculate_event_length(unsigned length) return length; } -#ifndef CONFIG_HAVE_UNSTABLE_SCHED_CLOCK -static inline bool sched_clock_stable(void) -{ - return true; -} -#endif - static __always_inline bool rb_event_is_commit(struct ring_buffer_per_cpu *cpu_buffer, struct ring_buffer_event *event) @@ -3092,24 +3115,6 @@ int ring_buffer_unlock_commit(struct trace_buffer *buffer, } EXPORT_SYMBOL_GPL(ring_buffer_unlock_commit); -static noinline void -rb_check_timestamp(struct ring_buffer_per_cpu *cpu_buffer, - struct rb_event_info *info) -{ - u64 write_stamp; - - WARN_ONCE(info->delta > (1ULL << 59), - KERN_WARNING "Delta way too big! %llu ts=%llu write stamp = %llu\n%s", - (unsigned long long)info->delta, - (unsigned long long)info->ts, - (unsigned long long)(rb_time_read(&cpu_buffer->write_stamp, &write_stamp) ? write_stamp : 0), - sched_clock_stable() ? "" : - "If you just came from a suspend/resume,\n" - "please switch to the trace global clock:\n" - " echo global > /sys/kernel/debug/tracing/trace_clock\n" - "or add trace_clock=global to the kernel command line\n"); -} - static struct ring_buffer_event * __rb_reserve_next(struct ring_buffer_per_cpu *cpu_buffer, struct rb_event_info *info) @@ -3117,7 +3122,6 @@ __rb_reserve_next(struct ring_buffer_per_cpu *cpu_buffer, struct ring_buffer_event *event; struct buffer_page *tail_page; unsigned long tail, write, w; - u64 before, after; bool a_ok; bool b_ok; @@ -3126,40 +3130,31 @@ __rb_reserve_next(struct ring_buffer_per_cpu *cpu_buffer, /*A*/ w = local_read(&tail_page->write) & RB_WRITE_MASK; barrier(); - b_ok = rb_time_read(&cpu_buffer->before_stamp, &before); - a_ok = rb_time_read(&cpu_buffer->write_stamp, &after); + b_ok = rb_time_read(&cpu_buffer->before_stamp, &info->before); + a_ok = rb_time_read(&cpu_buffer->write_stamp, &info->after); barrier(); info->ts = rb_time_stamp(cpu_buffer->buffer); - if (ring_buffer_time_stamp_abs(cpu_buffer->buffer)) { + if ((info->add_timestamp & RB_ADD_STAMP_ABSOLUTE)) { info->delta = info->ts; - info->add_timestamp = RB_ADD_STAMP_ABSOLUTE; } else { - info->delta = info->ts - after; - } - - if (likely(a_ok && b_ok)) { - if (unlikely(test_time_stamp(info->delta))) { - rb_check_timestamp(cpu_buffer, info); - info->add_timestamp |= RB_ADD_STAMP_EXTEND; + /* + * If interrupting an event time update, we may need an + * absolute timestamp. + * Don't bother if this is the start of a new page (w == 0). + */ + if (unlikely(!a_ok || !b_ok || (info->before != info->after && w))) { + info->add_timestamp |= RB_ADD_STAMP_FORCE | RB_ADD_STAMP_EXTEND; + info->length += RB_LEN_TIME_EXTEND; + } else { + info->delta = info->ts - info->after; + if (unlikely(test_time_stamp(info->delta))) { + info->add_timestamp |= RB_ADD_STAMP_EXTEND; + info->length += RB_LEN_TIME_EXTEND; + } } } - /* - * If interrupting an event time update, we may need an absolute timestamp. - * Don't bother if this is the start of a new page (w == 0). - */ - if (unlikely(!a_ok || !b_ok || (before != after && w))) - info->add_timestamp |= RB_ADD_STAMP_FORCE | RB_ADD_STAMP_EXTEND; - - /* - * If the time delta since the last event is too big to - * hold in the time field of the event, then we append a - * TIME EXTEND event ahead of the data event. - */ - if (unlikely(info->add_timestamp)) - info->length += RB_LEN_TIME_EXTEND; - /*B*/ rb_time_set(&cpu_buffer->before_stamp, info->ts); /*C*/ write = local_add_return(info->length, &tail_page->write); @@ -3173,10 +3168,11 @@ __rb_reserve_next(struct ring_buffer_per_cpu *cpu_buffer, if (unlikely(write > BUF_PAGE_SIZE)) { if (tail != w) { /* before and after may now different, fix it up*/ - b_ok = rb_time_read(&cpu_buffer->before_stamp, &before); - a_ok = rb_time_read(&cpu_buffer->write_stamp, &after); - if (a_ok && b_ok && before != after) - (void)rb_time_cmpxchg(&cpu_buffer->before_stamp, before, after); + b_ok = rb_time_read(&cpu_buffer->before_stamp, &info->before); + a_ok = rb_time_read(&cpu_buffer->write_stamp, &info->after); + if (a_ok && b_ok && info->before != info->after) + (void)rb_time_cmpxchg(&cpu_buffer->before_stamp, + info->before, info->after); } return rb_move_tail(cpu_buffer, tail, info); } @@ -3193,7 +3189,7 @@ __rb_reserve_next(struct ring_buffer_per_cpu *cpu_buffer, if (likely(!(info->add_timestamp & (RB_ADD_STAMP_FORCE | RB_ADD_STAMP_ABSOLUTE)))) /* This did not interrupt any time update */ - info->delta = info->ts - after; + info->delta = info->ts - info->after; else /* Just use full timestamp for inerrupting event */ info->delta = info->ts; @@ -3201,31 +3197,33 @@ __rb_reserve_next(struct ring_buffer_per_cpu *cpu_buffer, if (unlikely(info->ts != save_before)) { /* SLOW PATH - Interrupted between C and E */ - a_ok = rb_time_read(&cpu_buffer->write_stamp, &after); + a_ok = rb_time_read(&cpu_buffer->write_stamp, &info->after); RB_WARN_ON(cpu_buffer, !a_ok); /* Write stamp must only go forward */ - if (save_before > after) { + if (save_before > info->after) { /* * We do not care about the result, only that * it gets updated atomically. */ - (void)rb_time_cmpxchg(&cpu_buffer->write_stamp, after, save_before); + (void)rb_time_cmpxchg(&cpu_buffer->write_stamp, + info->after, save_before); } } } else { u64 ts; /* SLOW PATH - Interrupted between A and C */ - a_ok = rb_time_read(&cpu_buffer->write_stamp, &after); + a_ok = rb_time_read(&cpu_buffer->write_stamp, &info->after); /* Was interrupted before here, write_stamp must be valid */ RB_WARN_ON(cpu_buffer, !a_ok); ts = rb_time_stamp(cpu_buffer->buffer); barrier(); /*E*/ if (write == (local_read(&tail_page->write) & RB_WRITE_MASK) && - after < ts) { + info->after < ts) { /* Nothing came after this event between C and E */ - info->delta = ts - after; - (void)rb_time_cmpxchg(&cpu_buffer->write_stamp, after, info->ts); + info->delta = ts - info->after; + (void)rb_time_cmpxchg(&cpu_buffer->write_stamp, + info->after, info->ts); info->ts = ts; } else { /* @@ -3277,6 +3275,7 @@ rb_reserve_next_event(struct trace_buffer *buffer, struct ring_buffer_event *event; struct rb_event_info info; int nr_loops = 0; + int add_ts_default; rb_start_commit(cpu_buffer); /* The commit page can not change after this */ @@ -3297,8 +3296,16 @@ rb_reserve_next_event(struct trace_buffer *buffer, #endif info.length = rb_calculate_event_length(length); + + if (ring_buffer_time_stamp_abs(cpu_buffer->buffer)) { + add_ts_default = RB_ADD_STAMP_ABSOLUTE; + info.length += RB_LEN_TIME_EXTEND; + } else { + add_ts_default = RB_ADD_STAMP_NONE; + } + again: - info.add_timestamp = RB_ADD_STAMP_NONE; + info.add_timestamp = add_ts_default; info.delta = 0; /* @@ -3316,7 +3323,7 @@ rb_reserve_next_event(struct trace_buffer *buffer, event = __rb_reserve_next(cpu_buffer, &info); if (unlikely(PTR_ERR(event) == -EAGAIN)) { - if (info.add_timestamp) + if (info.add_timestamp & (RB_ADD_STAMP_FORCE | RB_ADD_STAMP_EXTEND)) info.length -= RB_LEN_TIME_EXTEND; goto again; } -- GitLab From 74e879373b377f15d4ecb45bf8316b77e8badc49 Mon Sep 17 00:00:00 2001 From: "Steven Rostedt (VMware)" Date: Tue, 30 Jun 2020 12:47:56 -0400 Subject: [PATCH 0210/1476] ring-buffer: Move the add_timestamp into its own function Make a helper function rb_add_timestamp() that moves the adding of the extended time stamps into its own function. Also, remove the noinline and inline for the functions it calls, as recent benchmarks appear they do not make a difference (just let gcc decide). Signed-off-by: Steven Rostedt (VMware) --- kernel/trace/ring_buffer.c | 32 ++++++++++++++++++++------------ 1 file changed, 20 insertions(+), 12 deletions(-) diff --git a/kernel/trace/ring_buffer.c b/kernel/trace/ring_buffer.c index ce125cbe98a5d..a30ca7ec22005 100644 --- a/kernel/trace/ring_buffer.c +++ b/kernel/trace/ring_buffer.c @@ -2596,8 +2596,8 @@ rb_move_tail(struct ring_buffer_per_cpu *cpu_buffer, return NULL; } -/* Slow path, do not inline */ -static noinline struct ring_buffer_event * +/* Slow path */ +static struct ring_buffer_event * rb_add_time_stamp(struct ring_buffer_event *event, u64 delta, bool abs) { if (abs) @@ -2628,7 +2628,7 @@ static inline bool sched_clock_stable(void) } #endif -static noinline void +static void rb_check_timestamp(struct ring_buffer_per_cpu *cpu_buffer, struct rb_event_info *info) { @@ -2648,6 +2648,21 @@ rb_check_timestamp(struct ring_buffer_per_cpu *cpu_buffer, "or add trace_clock=global to the kernel command line\n"); } +static void rb_add_timestamp(struct ring_buffer_per_cpu *cpu_buffer, + struct ring_buffer_event **event, + struct rb_event_info *info, + u64 *delta, + unsigned int *length) +{ + bool abs = info->add_timestamp & + (RB_ADD_STAMP_FORCE | RB_ADD_STAMP_ABSOLUTE); + + rb_check_timestamp(cpu_buffer, info); + *event = rb_add_time_stamp(*event, info->delta, abs); + *length -= RB_LEN_TIME_EXTEND; + *delta = 0; +} + /** * rb_update_event - update event type and data * @cpu_buffer: The per cpu buffer of the @event @@ -2671,15 +2686,8 @@ rb_update_event(struct ring_buffer_per_cpu *cpu_buffer, * If we need to add a timestamp, then we * add it to the start of the reserved space. */ - if (unlikely(info->add_timestamp)) { - bool abs = info->add_timestamp & - (RB_ADD_STAMP_FORCE | RB_ADD_STAMP_ABSOLUTE); - - rb_check_timestamp(cpu_buffer, info); - event = rb_add_time_stamp(event, abs ? info->delta : delta, abs); - length -= RB_LEN_TIME_EXTEND; - delta = 0; - } + if (unlikely(info->add_timestamp)) + rb_add_timestamp(cpu_buffer, &event, info, &delta, &length); event->time_delta = delta; length -= RB_EVNT_HDR_SIZE; -- GitLab From bbeba3e58f040a4297a5ba88ebf6e2b16adc3657 Mon Sep 17 00:00:00 2001 From: "Steven Rostedt (VMware)" Date: Tue, 30 Jun 2020 13:05:29 -0400 Subject: [PATCH 0211/1476] ring-buffer: Call trace_clock_local() directly for RETPOLINE kernels After doing some benchmarks and examining the code, I found that the ring buffer clock calls were quite expensive, and noticed that it uses retpolines. This is because the ring buffer clock is programmable, and can be set. But in most cases it simply uses the fastest ns unit clock which is the trace_clock_local(). For RETPOLINE builds, checking if the ring buffer clock is set to trace_clock_local() and then calling it directly has brought the time of an event on my i7 box from an average of 93 nanoseconds an event down to 83 nanoseconds an event, and the minimum time from 81 nanoseconds to 68 nanoseconds! Suggested-by: Mathieu Desnoyers Signed-off-by: Steven Rostedt (VMware) --- kernel/trace/ring_buffer.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/kernel/trace/ring_buffer.c b/kernel/trace/ring_buffer.c index a30ca7ec22005..2bb96ee801209 100644 --- a/kernel/trace/ring_buffer.c +++ b/kernel/trace/ring_buffer.c @@ -970,8 +970,16 @@ __poll_t ring_buffer_poll_wait(struct trace_buffer *buffer, int cpu, static inline u64 rb_time_stamp(struct trace_buffer *buffer) { + u64 ts; + + /* Skip retpolines :-( */ + if (IS_ENABLED(CONFIG_RETPOLINE) && likely(buffer->clock == trace_clock_local)) + ts = trace_clock_local(); + else + ts = buffer->clock(); + /* shift to debug/test normalization and TIME_EXTENTS */ - return buffer->clock() << DEBUG_SHIFT; + return ts << DEBUG_SHIFT; } u64 ring_buffer_time_stamp(struct trace_buffer *buffer, int cpu) -- GitLab From 29ce24519c0692ca7d998d7444a9e016a4c44fa7 Mon Sep 17 00:00:00 2001 From: "Steven Rostedt (VMware)" Date: Wed, 1 Jul 2020 13:10:19 -0400 Subject: [PATCH 0212/1476] ring-buffer: Do not trigger a WARN if clock going backwards is detected After tweaking the ring buffer to be a bit faster, a warning is triggering on one of my machines, and causing my tests to fail. This warning is caused when the delta (current time stamp minus previous time stamp), is larger than the max time held by the ring buffer (59 bits). If the clock were to go backwards slightly, this would then easily trigger this warning. The machine that it triggered on, the clock did go backwards by around 450 nanoseconds, and this happened after a recalibration of the TSC clock. Now that the ring buffer is faster, it detects this, and the delta that is used larger than the max, the warning is triggered and my test fails. To handle the clock going backwards, look at the saved before and after time stamps. If they are the same, it means that the current event did not interrupt another event, and that those timestamp are of a previous event that was recorded. If the max delta is triggered, look at those time stamps, make sure they are the same, then use them to compare with the current timestamp. If the current timestamp is less than the before/after time stamps, then that means the clock being used went backward. Print out a message that this has happened, but do not warn about it (and only print the message once). Still do the warning if the delta is indeed larger than what can be used. Also remove the unneeded KERN_WARNING from the WARN_ONCE() print. Signed-off-by: Steven Rostedt (VMware) --- kernel/trace/ring_buffer.c | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/kernel/trace/ring_buffer.c b/kernel/trace/ring_buffer.c index 2bb96ee801209..c3a2e7509527e 100644 --- a/kernel/trace/ring_buffer.c +++ b/kernel/trace/ring_buffer.c @@ -2642,8 +2642,7 @@ rb_check_timestamp(struct ring_buffer_per_cpu *cpu_buffer, { u64 write_stamp; - WARN_ONCE(info->delta > (1ULL << 59), - KERN_WARNING "Delta way too big! %llu ts=%llu before=%llu after=%llu write stamp=%llu\n%s", + WARN_ONCE(1, "Delta way too big! %llu ts=%llu before=%llu after=%llu write stamp=%llu\n%s", (unsigned long long)info->delta, (unsigned long long)info->ts, (unsigned long long)info->before, @@ -2665,7 +2664,26 @@ static void rb_add_timestamp(struct ring_buffer_per_cpu *cpu_buffer, bool abs = info->add_timestamp & (RB_ADD_STAMP_FORCE | RB_ADD_STAMP_ABSOLUTE); - rb_check_timestamp(cpu_buffer, info); + if (unlikely(info->delta > (1ULL << 59))) { + /* did the clock go backwards */ + if (info->before == info->after && info->before > info->ts) { + /* not interrupted */ + static int once; + + /* + * This is possible with a recalibrating of the TSC. + * Do not produce a call stack, but just report it. + */ + if (!once) { + once++; + pr_warn("Ring buffer clock went backwards: %llu -> %llu\n", + info->before, info->ts); + } + } else + rb_check_timestamp(cpu_buffer, info); + if (!abs) + info->delta = 0; + } *event = rb_add_time_stamp(*event, info->delta, abs); *length -= RB_LEN_TIME_EXTEND; *delta = 0; -- GitLab From 372faeb46b91be672fb0f03f7b358759bcd4aed8 Mon Sep 17 00:00:00 2001 From: Dafna Hirschfeld Date: Fri, 15 May 2020 16:29:52 +0200 Subject: [PATCH 0213/1476] media: staging: rkisp1: cap: remove support of BGR666 format The rkisp1 supports RGB encoding with 6 bits per color with the following format: - - b5 b4 b3 b2 b1 b0 - - g5 g4 g3 g2 g1 g0 - - r5 r4 r3 r2 r1 r0 - - - - - - - - This is not how V4L2_PIX_FMT_BGR666 is defined, so remove this format from the driver's formats list. Signed-off-by: Dafna Hirschfeld Acked-by: Helen Koike Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/rkisp1/rkisp1-capture.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/drivers/staging/media/rkisp1/rkisp1-capture.c b/drivers/staging/media/rkisp1/rkisp1-capture.c index f69235f82c457..793ec884c8940 100644 --- a/drivers/staging/media/rkisp1/rkisp1-capture.c +++ b/drivers/staging/media/rkisp1/rkisp1-capture.c @@ -283,10 +283,6 @@ static const struct rkisp1_capture_fmt_cfg rkisp1_sp_fmts[] = { .fourcc = V4L2_PIX_FMT_RGB565, .write_format = RKISP1_MI_CTRL_SP_WRITE_PLA, .output_format = RKISP1_MI_CTRL_SP_OUTPUT_RGB565, - }, { - .fourcc = V4L2_PIX_FMT_BGR666, - .write_format = RKISP1_MI_CTRL_SP_WRITE_PLA, - .output_format = RKISP1_MI_CTRL_SP_OUTPUT_RGB666, }, }; -- GitLab From 50795910160e78c1bee6b27d8d7a3f93e8b7d48d Mon Sep 17 00:00:00 2001 From: Andrey Konovalov Date: Thu, 9 Apr 2020 13:24:04 +0200 Subject: [PATCH 0214/1476] media: camss: use proper media entity function for subdevices Currently for msm_csiphy, msm_csid, and msm_ispif subdevices the media entity function field is set to MEDIA_ENT_F_IO_V4L. This is incorrect as MEDIA_ENT_F_IO_V4L implies V4L2 video node. Change it to MEDIA_ENT_F_PROC_VIDEO_PIXEL_FORMATTER as this is the best fit from the functions defined in include/uapi/linux/media.h. Signed-off-by: Andrey Konovalov Tested-by: Robert Foss Reviewed-by: Robert Foss Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/qcom/camss/camss-csid.c | 2 +- drivers/media/platform/qcom/camss/camss-csiphy.c | 2 +- drivers/media/platform/qcom/camss/camss-ispif.c | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/media/platform/qcom/camss/camss-csid.c b/drivers/media/platform/qcom/camss/camss-csid.c index 8a247b6f55509..2ffcda06706bc 100644 --- a/drivers/media/platform/qcom/camss/camss-csid.c +++ b/drivers/media/platform/qcom/camss/camss-csid.c @@ -1358,7 +1358,7 @@ int msm_csid_register_entity(struct csid_device *csid, pads[MSM_CSID_PAD_SINK].flags = MEDIA_PAD_FL_SINK; pads[MSM_CSID_PAD_SRC].flags = MEDIA_PAD_FL_SOURCE; - sd->entity.function = MEDIA_ENT_F_IO_V4L; + sd->entity.function = MEDIA_ENT_F_PROC_VIDEO_PIXEL_FORMATTER; sd->entity.ops = &csid_media_ops; ret = media_entity_pads_init(&sd->entity, MSM_CSID_PADS_NUM, pads); if (ret < 0) { diff --git a/drivers/media/platform/qcom/camss/camss-csiphy.c b/drivers/media/platform/qcom/camss/camss-csiphy.c index 008afb85023be..03ef9c5f4774d 100644 --- a/drivers/media/platform/qcom/camss/camss-csiphy.c +++ b/drivers/media/platform/qcom/camss/camss-csiphy.c @@ -737,7 +737,7 @@ int msm_csiphy_register_entity(struct csiphy_device *csiphy, pads[MSM_CSIPHY_PAD_SINK].flags = MEDIA_PAD_FL_SINK; pads[MSM_CSIPHY_PAD_SRC].flags = MEDIA_PAD_FL_SOURCE; - sd->entity.function = MEDIA_ENT_F_IO_V4L; + sd->entity.function = MEDIA_ENT_F_PROC_VIDEO_PIXEL_FORMATTER; sd->entity.ops = &csiphy_media_ops; ret = media_entity_pads_init(&sd->entity, MSM_CSIPHY_PADS_NUM, pads); if (ret < 0) { diff --git a/drivers/media/platform/qcom/camss/camss-ispif.c b/drivers/media/platform/qcom/camss/camss-ispif.c index 5722e971b1848..db94cfd6c5081 100644 --- a/drivers/media/platform/qcom/camss/camss-ispif.c +++ b/drivers/media/platform/qcom/camss/camss-ispif.c @@ -1325,7 +1325,7 @@ int msm_ispif_register_entities(struct ispif_device *ispif, pads[MSM_ISPIF_PAD_SINK].flags = MEDIA_PAD_FL_SINK; pads[MSM_ISPIF_PAD_SRC].flags = MEDIA_PAD_FL_SOURCE; - sd->entity.function = MEDIA_ENT_F_IO_V4L; + sd->entity.function = MEDIA_ENT_F_PROC_VIDEO_PIXEL_FORMATTER; sd->entity.ops = &ispif_media_ops; ret = media_entity_pads_init(&sd->entity, MSM_ISPIF_PADS_NUM, pads); -- GitLab From 7cd08d6eed2772e5ac759063a0287c5ff4551a91 Mon Sep 17 00:00:00 2001 From: "Gustavo A. R. Silva" Date: Thu, 19 Mar 2020 23:23:52 +0100 Subject: [PATCH 0215/1476] media: usb: pwc: pwc.h: Replace zero-length array with flexible-array member The current codebase makes use of the zero-length array language extension to the C90 standard, but the preferred mechanism to declare variable-length types such as these ones is a flexible array member[1][2], introduced in C99: struct foo { int stuff; struct boo array[]; }; By making use of the mechanism above, we will get a compiler warning in case the flexible array does not occur last in the structure, which will help us prevent some kind of undefined behavior bugs from being inadvertently introduced[3] to the codebase from now on. Also, notice that, dynamic memory allocations won't be affected by this change: "Flexible array members have incomplete type, and so the sizeof operator may not be applied. As a quirk of the original implementation of zero-length arrays, sizeof evaluates to zero."[1] This issue was found with the help of Coccinelle. [1] https://gcc.gnu.org/onlinedocs/gcc/Zero-Length.html [2] https://github.com/KSPP/linux/issues/21 [3] commit 76497732932f ("cxgb3/l2t: Fix undefined behaviour") Signed-off-by: Gustavo A. R. Silva Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/pwc/pwc.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/usb/pwc/pwc.h b/drivers/media/usb/pwc/pwc.h index 3362962d0d00e..b02a3c7b7742e 100644 --- a/drivers/media/usb/pwc/pwc.h +++ b/drivers/media/usb/pwc/pwc.h @@ -193,7 +193,7 @@ struct pwc_raw_frame { decompressor) */ __u8 cmd[4]; /* the four byte of the command (in case of nala, only the first 3 bytes is filled) */ - __u8 rawframe[0]; /* frame_size = H / 4 * vbandlength */ + __u8 rawframe[]; /* frame_size = H / 4 * vbandlength */ } __packed; /* intermediate buffers with raw data from the USB cam */ -- GitLab From 6fce59ac719f9adeeed2556ed74e23a607f931e8 Mon Sep 17 00:00:00 2001 From: Eugen Hristev Date: Fri, 12 Jun 2020 12:56:02 +0200 Subject: [PATCH 0216/1476] media: atmel: atmel-sama5d2-isc: fix warning in configs without OF All warnings (new ones prefixed by >>, old ones prefixed by <<): >> drivers/media/platform/atmel/atmel-sama5d2-isc.c:323:34: warning: unused variable 'atmel_isc_of_match' [-Wunused-const-variable] static const struct of_device_id atmel_isc_of_match[] = { ^ 1 warning generated. vim +/atmel_isc_of_match +323 drivers/media/platform/atmel/atmel-sama5d2-isc.c 322 > 323 static const struct of_device_id atmel_isc_of_match[] = { 324 { .compatible = "atmel,sama5d2-isc" }, 325 { } 326 }; 327 MODULE_DEVICE_TABLE(of, atmel_isc_of_match); 328 Fixed warning by guarding the atmel_isc_of_match by CONFIG_OF. Reported-by: kernel test robot Signed-off-by: Eugen Hristev Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/atmel/atmel-sama5d2-isc.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/media/platform/atmel/atmel-sama5d2-isc.c b/drivers/media/platform/atmel/atmel-sama5d2-isc.c index 78381651238dd..a3304f49e4997 100644 --- a/drivers/media/platform/atmel/atmel-sama5d2-isc.c +++ b/drivers/media/platform/atmel/atmel-sama5d2-isc.c @@ -321,11 +321,13 @@ static const struct dev_pm_ops atmel_isc_dev_pm_ops = { SET_RUNTIME_PM_OPS(isc_runtime_suspend, isc_runtime_resume, NULL) }; +#if IS_ENABLED(CONFIG_OF) static const struct of_device_id atmel_isc_of_match[] = { { .compatible = "atmel,sama5d2-isc" }, { } }; MODULE_DEVICE_TABLE(of, atmel_isc_of_match); +#endif static struct platform_driver atmel_isc_driver = { .probe = atmel_isc_probe, -- GitLab From 20171723144ca0d057b72e852536992fd371369a Mon Sep 17 00:00:00 2001 From: Chen Zhou Date: Sat, 9 May 2020 04:07:11 +0200 Subject: [PATCH 0217/1476] media: coda: jpeg: add NULL check after kmalloc Fixes coccicheck warning: ./drivers/media/platform/coda/coda-jpeg.c:331:3-31: alloc with no test, possible model on line 354 Add NULL check after kmalloc. Signed-off-by: Chen Zhou Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/coda/coda-jpeg.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/media/platform/coda/coda-jpeg.c b/drivers/media/platform/coda/coda-jpeg.c index 00d19859db500..b11cfbe166dd3 100644 --- a/drivers/media/platform/coda/coda-jpeg.c +++ b/drivers/media/platform/coda/coda-jpeg.c @@ -327,8 +327,11 @@ int coda_jpeg_decode_header(struct coda_ctx *ctx, struct vb2_buffer *vb) "only 8-bit quantization tables supported\n"); continue; } - if (!ctx->params.jpeg_qmat_tab[i]) + if (!ctx->params.jpeg_qmat_tab[i]) { ctx->params.jpeg_qmat_tab[i] = kmalloc(64, GFP_KERNEL); + if (!ctx->params.jpeg_qmat_tab[i]) + return -ENOMEM; + } memcpy(ctx->params.jpeg_qmat_tab[i], quantization_tables[i].start, 64); } -- GitLab From 2505a210fc126599013aec2be741df20aaacc490 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Fri, 8 May 2020 16:40:22 +0200 Subject: [PATCH 0218/1476] media: firewire: Using uninitialized values in node_probe() If fw_csr_string() returns -ENOENT, then "name" is uninitialized. So then the "strlen(model_names[i]) <= name_len" is true because strlen() is unsigned and -ENOENT is type promoted to a very high positive value. Then the "strncmp(name, model_names[i], name_len)" uses uninitialized data because "name" is uninitialized. Fixes: 92374e886c75 ("[media] firedtv: drop obsolete backend abstraction") Signed-off-by: Dan Carpenter Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/firewire/firedtv-fw.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/media/firewire/firedtv-fw.c b/drivers/media/firewire/firedtv-fw.c index 97144734eb052..3f1ca40b9b987 100644 --- a/drivers/media/firewire/firedtv-fw.c +++ b/drivers/media/firewire/firedtv-fw.c @@ -272,6 +272,8 @@ static int node_probe(struct fw_unit *unit, const struct ieee1394_device_id *id) name_len = fw_csr_string(unit->directory, CSR_MODEL, name, sizeof(name)); + if (name_len < 0) + return name_len; for (i = ARRAY_SIZE(model_names); --i; ) if (strlen(model_names[i]) <= name_len && strncmp(name, model_names[i], name_len) == 0) -- GitLab From 0d1e342a17f5b10e2231fc8f98cac7b88b1b6897 Mon Sep 17 00:00:00 2001 From: Francois Buergisser Date: Wed, 6 May 2020 07:01:37 +0200 Subject: [PATCH 0219/1476] media: mtk-mdp: Remove states for format checks The mtk-mdp driver uses states to check if the formats have been set on the capture and output when turning the streaming on, setting controls or setting the selection rectangles. Those states are reset when 0 buffers are requested like when checking capabilities. This patch removes all format checks and set one by default as queues in V4L2 are expected to always have a format set. https://linuxtv.org/downloads/v4l-dvb-apis/uapi/v4l/vidioc-streamon.html https://linuxtv.org/downloads/v4l-dvb-apis/uapi/v4l/vidioc-g-ctrl.html https://linuxtv.org/downloads/v4l-dvb-apis/uapi/v4l/vidioc-g-selection.html Signed-off-by: Francois Buergisser Signed-off-by: Eizan Miyamoto Reviewed-by: Enric Balletbo I Serra Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/mtk-mdp/mtk_mdp_core.h | 2 - drivers/media/platform/mtk-mdp/mtk_mdp_m2m.c | 90 +++++++------------ 2 files changed, 34 insertions(+), 58 deletions(-) diff --git a/drivers/media/platform/mtk-mdp/mtk_mdp_core.h b/drivers/media/platform/mtk-mdp/mtk_mdp_core.h index bafcccd71f31c..dd130cc218c9f 100644 --- a/drivers/media/platform/mtk-mdp/mtk_mdp_core.h +++ b/drivers/media/platform/mtk-mdp/mtk_mdp_core.h @@ -28,8 +28,6 @@ #define MTK_MDP_FMT_FLAG_CAPTURE BIT(1) #define MTK_MDP_VPU_INIT BIT(0) -#define MTK_MDP_SRC_FMT BIT(1) -#define MTK_MDP_DST_FMT BIT(2) #define MTK_MDP_CTX_ERROR BIT(5) /** diff --git a/drivers/media/platform/mtk-mdp/mtk_mdp_m2m.c b/drivers/media/platform/mtk-mdp/mtk_mdp_m2m.c index 821f2cf325f0d..bb9caaf513bcb 100644 --- a/drivers/media/platform/mtk-mdp/mtk_mdp_m2m.c +++ b/drivers/media/platform/mtk-mdp/mtk_mdp_m2m.c @@ -369,13 +369,6 @@ void mtk_mdp_ctx_state_lock_set(struct mtk_mdp_ctx *ctx, u32 state) mutex_unlock(&ctx->slock); } -static void mtk_mdp_ctx_state_lock_clear(struct mtk_mdp_ctx *ctx, u32 state) -{ - mutex_lock(&ctx->slock); - ctx->state &= ~state; - mutex_unlock(&ctx->slock); -} - static bool mtk_mdp_ctx_state_is_set(struct mtk_mdp_ctx *ctx, u32 mask) { bool ret; @@ -726,11 +719,6 @@ static int mtk_mdp_m2m_s_fmt_mplane(struct file *file, void *fh, ctx->quant = pix_mp->quantization; } - if (V4L2_TYPE_IS_OUTPUT(f->type)) - mtk_mdp_ctx_state_lock_set(ctx, MTK_MDP_SRC_FMT); - else - mtk_mdp_ctx_state_lock_set(ctx, MTK_MDP_DST_FMT); - mtk_mdp_dbg(2, "[%d] type:%d, frame:%dx%d", ctx->id, f->type, frame->width, frame->height); @@ -742,13 +730,6 @@ static int mtk_mdp_m2m_reqbufs(struct file *file, void *fh, { struct mtk_mdp_ctx *ctx = fh_to_ctx(fh); - if (reqbufs->count == 0) { - if (reqbufs->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) - mtk_mdp_ctx_state_lock_clear(ctx, MTK_MDP_SRC_FMT); - else - mtk_mdp_ctx_state_lock_clear(ctx, MTK_MDP_DST_FMT); - } - return v4l2_m2m_reqbufs(file, ctx->m2m_ctx, reqbufs); } @@ -758,14 +739,6 @@ static int mtk_mdp_m2m_streamon(struct file *file, void *fh, struct mtk_mdp_ctx *ctx = fh_to_ctx(fh); int ret; - /* The source and target color format need to be set */ - if (V4L2_TYPE_IS_OUTPUT(type)) { - if (!mtk_mdp_ctx_state_is_set(ctx, MTK_MDP_SRC_FMT)) - return -EINVAL; - } else if (!mtk_mdp_ctx_state_is_set(ctx, MTK_MDP_DST_FMT)) { - return -EINVAL; - } - if (!mtk_mdp_ctx_state_is_set(ctx, MTK_MDP_VPU_INIT)) { ret = mtk_mdp_vpu_init(&ctx->vpu); if (ret < 0) { @@ -899,24 +872,21 @@ static int mtk_mdp_m2m_s_selection(struct file *file, void *fh, frame = &ctx->d_frame; /* Check to see if scaling ratio is within supported range */ - if (mtk_mdp_ctx_state_is_set(ctx, MTK_MDP_DST_FMT | MTK_MDP_SRC_FMT)) { - if (V4L2_TYPE_IS_OUTPUT(s->type)) { - ret = mtk_mdp_check_scaler_ratio(variant, new_r.width, - new_r.height, ctx->d_frame.crop.width, - ctx->d_frame.crop.height, - ctx->ctrls.rotate->val); - } else { - ret = mtk_mdp_check_scaler_ratio(variant, - ctx->s_frame.crop.width, - ctx->s_frame.crop.height, new_r.width, - new_r.height, ctx->ctrls.rotate->val); - } + if (V4L2_TYPE_IS_OUTPUT(s->type)) + ret = mtk_mdp_check_scaler_ratio(variant, new_r.width, + new_r.height, ctx->d_frame.crop.width, + ctx->d_frame.crop.height, + ctx->ctrls.rotate->val); + else + ret = mtk_mdp_check_scaler_ratio(variant, + ctx->s_frame.crop.width, + ctx->s_frame.crop.height, new_r.width, + new_r.height, ctx->ctrls.rotate->val); - if (ret) { - dev_info(&ctx->mdp_dev->pdev->dev, - "Out of scaler range"); - return -EINVAL; - } + if (ret) { + dev_info(&ctx->mdp_dev->pdev->dev, + "Out of scaler range"); + return -EINVAL; } s->r = new_r; @@ -989,7 +959,6 @@ static int mtk_mdp_s_ctrl(struct v4l2_ctrl *ctrl) struct mtk_mdp_ctx *ctx = ctrl_to_ctx(ctrl); struct mtk_mdp_dev *mdp = ctx->mdp_dev; struct mtk_mdp_variant *variant = mdp->variant; - u32 state = MTK_MDP_DST_FMT | MTK_MDP_SRC_FMT; int ret = 0; if (ctrl->flags & V4L2_CTRL_FLAG_INACTIVE) @@ -1003,17 +972,15 @@ static int mtk_mdp_s_ctrl(struct v4l2_ctrl *ctrl) ctx->vflip = ctrl->val; break; case V4L2_CID_ROTATE: - if (mtk_mdp_ctx_state_is_set(ctx, state)) { - ret = mtk_mdp_check_scaler_ratio(variant, - ctx->s_frame.crop.width, - ctx->s_frame.crop.height, - ctx->d_frame.crop.width, - ctx->d_frame.crop.height, - ctx->ctrls.rotate->val); - - if (ret) - return -EINVAL; - } + ret = mtk_mdp_check_scaler_ratio(variant, + ctx->s_frame.crop.width, + ctx->s_frame.crop.height, + ctx->d_frame.crop.width, + ctx->d_frame.crop.height, + ctx->ctrls.rotate->val); + + if (ret) + return -EINVAL; ctx->rotation = ctrl->val; break; @@ -1090,6 +1057,7 @@ static int mtk_mdp_m2m_open(struct file *file) struct video_device *vfd = video_devdata(file); struct mtk_mdp_ctx *ctx = NULL; int ret; + struct v4l2_format default_format; ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); if (!ctx) @@ -1144,6 +1112,16 @@ static int mtk_mdp_m2m_open(struct file *file) list_add(&ctx->list, &mdp->ctx_list); mutex_unlock(&mdp->lock); + /* Default format */ + memset(&default_format, 0, sizeof(default_format)); + default_format.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; + default_format.fmt.pix_mp.width = 32; + default_format.fmt.pix_mp.height = 32; + default_format.fmt.pix_mp.pixelformat = V4L2_PIX_FMT_YUV420M; + mtk_mdp_m2m_s_fmt_mplane(file, &ctx->fh, &default_format); + default_format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; + mtk_mdp_m2m_s_fmt_mplane(file, &ctx->fh, &default_format); + mtk_mdp_dbg(0, "%s [%d]", dev_name(&mdp->pdev->dev), ctx->id); return 0; -- GitLab From 86ef5146fe49bcf0801fba199d98a37151ed3791 Mon Sep 17 00:00:00 2001 From: Eizan Miyamoto Date: Thu, 7 May 2020 12:23:42 +0200 Subject: [PATCH 0220/1476] media: mtk-mdp: remove mtk_mdp_comp.regs from mtk_mdp_comp.h These fields are not used and can be removed. Signed-off-by: Eizan Miyamoto Reviewed-by: Enric Balletbo I Serra Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/mtk-mdp/mtk_mdp_comp.c | 1 - drivers/media/platform/mtk-mdp/mtk_mdp_comp.h | 2 -- 2 files changed, 3 deletions(-) diff --git a/drivers/media/platform/mtk-mdp/mtk_mdp_comp.c b/drivers/media/platform/mtk-mdp/mtk_mdp_comp.c index 58abfbdfb82d3..c76cd61fb1784 100644 --- a/drivers/media/platform/mtk-mdp/mtk_mdp_comp.c +++ b/drivers/media/platform/mtk-mdp/mtk_mdp_comp.c @@ -106,7 +106,6 @@ int mtk_mdp_comp_init(struct device *dev, struct device_node *node, comp->dev_node = of_node_get(node); comp->id = comp_id; comp->type = mtk_mdp_matches[comp_id].type; - comp->regs = of_iomap(node, 0); for (i = 0; i < ARRAY_SIZE(comp->clk); i++) { comp->clk[i] = of_clk_get(node, i); diff --git a/drivers/media/platform/mtk-mdp/mtk_mdp_comp.h b/drivers/media/platform/mtk-mdp/mtk_mdp_comp.h index 998a4b953025c..3b83bd6e0d8be 100644 --- a/drivers/media/platform/mtk-mdp/mtk_mdp_comp.h +++ b/drivers/media/platform/mtk-mdp/mtk_mdp_comp.h @@ -38,7 +38,6 @@ enum mtk_mdp_comp_id { * struct mtk_mdp_comp - the MDP's function component data * @dev_node: component device node * @clk: clocks required for component - * @regs: Mapped address of component registers. * @larb_dev: SMI device required for component * @type: component type * @id: component ID @@ -46,7 +45,6 @@ enum mtk_mdp_comp_id { struct mtk_mdp_comp { struct device_node *dev_node; struct clk *clk[2]; - void __iomem *regs; struct device *larb_dev; enum mtk_mdp_comp_type type; enum mtk_mdp_comp_id id; -- GitLab From 256a12e1a343e1efff00a9065063f0846d219336 Mon Sep 17 00:00:00 2001 From: Eizan Miyamoto Date: Thu, 7 May 2020 12:23:44 +0200 Subject: [PATCH 0221/1476] media: mtk-mdp: handle vb2_dma_contig_set_max_seg_size errors during probe This is a cleanup to better handle errors during MDP probe. Signed-off-by: Eizan Miyamoto Reviewed-by: Enric Balletbo i Serra Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/mtk-mdp/mtk_mdp_core.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/media/platform/mtk-mdp/mtk_mdp_core.c b/drivers/media/platform/mtk-mdp/mtk_mdp_core.c index aeaed2cf44588..9b24b8d46eb76 100644 --- a/drivers/media/platform/mtk-mdp/mtk_mdp_core.c +++ b/drivers/media/platform/mtk-mdp/mtk_mdp_core.c @@ -193,7 +193,11 @@ static int mtk_mdp_probe(struct platform_device *pdev) platform_set_drvdata(pdev, mdp); - vb2_dma_contig_set_max_seg_size(&pdev->dev, DMA_BIT_MASK(32)); + ret = vb2_dma_contig_set_max_seg_size(&pdev->dev, DMA_BIT_MASK(32)); + if (ret) { + dev_err(&pdev->dev, "Failed to set vb2 dma mag seg size\n"); + goto err_m2m_register; + } pm_runtime_enable(dev); dev_dbg(dev, "mdp-%d registered successfully\n", mdp->id); -- GitLab From ee18fc7b0b9517ac62711da1b95095f122aead94 Mon Sep 17 00:00:00 2001 From: Eizan Miyamoto Date: Thu, 7 May 2020 12:23:46 +0200 Subject: [PATCH 0222/1476] media: mtk-mdp: handle vpu_wdt_reg_handler() errors during probe This is a cleanup to better handle errors during MDP probe. Signed-off-by: Eizan Miyamoto Reviewed-by: Enric Balletbo i Serra Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/mtk-mdp/mtk_mdp_core.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/media/platform/mtk-mdp/mtk_mdp_core.c b/drivers/media/platform/mtk-mdp/mtk_mdp_core.c index 9b24b8d46eb76..17d155219ba2b 100644 --- a/drivers/media/platform/mtk-mdp/mtk_mdp_core.c +++ b/drivers/media/platform/mtk-mdp/mtk_mdp_core.c @@ -188,8 +188,12 @@ static int mtk_mdp_probe(struct platform_device *pdev) } mdp->vpu_dev = vpu_get_plat_device(pdev); - vpu_wdt_reg_handler(mdp->vpu_dev, mtk_mdp_reset_handler, mdp, - VPU_RST_MDP); + ret = vpu_wdt_reg_handler(mdp->vpu_dev, mtk_mdp_reset_handler, mdp, + VPU_RST_MDP); + if (ret) { + dev_err(&pdev->dev, "Failed to register reset handler\n"); + goto err_m2m_register; + } platform_set_drvdata(pdev, mdp); -- GitLab From 86698b9505bbc97b1cd6d8e9a2e7b8604a079469 Mon Sep 17 00:00:00 2001 From: Eizan Miyamoto Date: Thu, 7 May 2020 12:23:48 +0200 Subject: [PATCH 0223/1476] media: mtk-mdp: convert mtk_mdp_dev.comp array to list The functions mtk_mdp_register/unregister_component have been created to add / remove items from the list of components. This will eventually enable us to specify a list of components in the device tree instead of hardcoding them into this driver. The list is modified by a single thread at driver probe time, and will not be traversed by another thread until the call to pm_runtime_enable at the end of probing. Signed-off-by: Eizan Miyamoto Reviewed-by: Enric Balletbo I Serra Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/mtk-mdp/mtk_mdp_comp.c | 1 + drivers/media/platform/mtk-mdp/mtk_mdp_comp.h | 2 + drivers/media/platform/mtk-mdp/mtk_mdp_core.c | 46 +++++++++++++------ drivers/media/platform/mtk-mdp/mtk_mdp_core.h | 10 +++- 4 files changed, 43 insertions(+), 16 deletions(-) diff --git a/drivers/media/platform/mtk-mdp/mtk_mdp_comp.c b/drivers/media/platform/mtk-mdp/mtk_mdp_comp.c index c76cd61fb1784..da2bdad7a8d18 100644 --- a/drivers/media/platform/mtk-mdp/mtk_mdp_comp.c +++ b/drivers/media/platform/mtk-mdp/mtk_mdp_comp.c @@ -103,6 +103,7 @@ int mtk_mdp_comp_init(struct device *dev, struct device_node *node, return -EINVAL; } + INIT_LIST_HEAD(&comp->node); comp->dev_node = of_node_get(node); comp->id = comp_id; comp->type = mtk_mdp_matches[comp_id].type; diff --git a/drivers/media/platform/mtk-mdp/mtk_mdp_comp.h b/drivers/media/platform/mtk-mdp/mtk_mdp_comp.h index 3b83bd6e0d8be..1f745891c6c38 100644 --- a/drivers/media/platform/mtk-mdp/mtk_mdp_comp.h +++ b/drivers/media/platform/mtk-mdp/mtk_mdp_comp.h @@ -36,6 +36,7 @@ enum mtk_mdp_comp_id { /** * struct mtk_mdp_comp - the MDP's function component data + * @node: list node to track sibing MDP components * @dev_node: component device node * @clk: clocks required for component * @larb_dev: SMI device required for component @@ -43,6 +44,7 @@ enum mtk_mdp_comp_id { * @id: component ID */ struct mtk_mdp_comp { + struct list_head node; struct device_node *dev_node; struct clk *clk[2]; struct device *larb_dev; diff --git a/drivers/media/platform/mtk-mdp/mtk_mdp_core.c b/drivers/media/platform/mtk-mdp/mtk_mdp_core.c index 17d155219ba2b..40b9fda8b03b5 100644 --- a/drivers/media/platform/mtk-mdp/mtk_mdp_core.c +++ b/drivers/media/platform/mtk-mdp/mtk_mdp_core.c @@ -55,19 +55,19 @@ MODULE_DEVICE_TABLE(of, mtk_mdp_of_ids); static void mtk_mdp_clock_on(struct mtk_mdp_dev *mdp) { struct device *dev = &mdp->pdev->dev; - int i; + struct mtk_mdp_comp *comp_node; - for (i = 0; i < ARRAY_SIZE(mdp->comp); i++) - mtk_mdp_comp_clock_on(dev, mdp->comp[i]); + list_for_each_entry(comp_node, &mdp->comp_list, node) + mtk_mdp_comp_clock_on(dev, comp_node); } static void mtk_mdp_clock_off(struct mtk_mdp_dev *mdp) { struct device *dev = &mdp->pdev->dev; - int i; + struct mtk_mdp_comp *comp_node; - for (i = 0; i < ARRAY_SIZE(mdp->comp); i++) - mtk_mdp_comp_clock_off(dev, mdp->comp[i]); + list_for_each_entry(comp_node, &mdp->comp_list, node) + mtk_mdp_comp_clock_off(dev, comp_node); } static void mtk_mdp_wdt_worker(struct work_struct *work) @@ -91,12 +91,25 @@ static void mtk_mdp_reset_handler(void *priv) queue_work(mdp->wdt_wq, &mdp->wdt_work); } +void mtk_mdp_register_component(struct mtk_mdp_dev *mdp, + struct mtk_mdp_comp *comp) +{ + list_add(&mdp->comp_list, &comp->node); +} + +void mtk_mdp_unregister_component(struct mtk_mdp_dev *mdp, + struct mtk_mdp_comp *comp) +{ + list_del(&comp->node); +} + static int mtk_mdp_probe(struct platform_device *pdev) { struct mtk_mdp_dev *mdp; struct device *dev = &pdev->dev; struct device_node *node, *parent; - int i, ret = 0; + struct mtk_mdp_comp *comp, *comp_temp; + int ret = 0; mdp = devm_kzalloc(dev, sizeof(*mdp), GFP_KERNEL); if (!mdp) @@ -104,6 +117,7 @@ static int mtk_mdp_probe(struct platform_device *pdev) mdp->id = pdev->id; mdp->pdev = pdev; + INIT_LIST_HEAD(&mdp->comp_list); INIT_LIST_HEAD(&mdp->ctx_list); mutex_init(&mdp->lock); @@ -124,7 +138,6 @@ static int mtk_mdp_probe(struct platform_device *pdev) const struct of_device_id *of_id; enum mtk_mdp_comp_type comp_type; int comp_id; - struct mtk_mdp_comp *comp; of_id = of_match_node(mtk_mdp_comp_dt_ids, node); if (!of_id) @@ -150,13 +163,14 @@ static int mtk_mdp_probe(struct platform_device *pdev) of_node_put(node); goto err_comp; } - mdp->comp[comp_id] = comp; ret = mtk_mdp_comp_init(dev, node, comp, comp_id); if (ret) { of_node_put(node); goto err_comp; } + + mtk_mdp_register_component(mdp, comp); } mdp->job_wq = create_singlethread_workqueue(MTK_MDP_MODULE_NAME); @@ -220,8 +234,10 @@ err_alloc_wdt_wq: err_alloc_job_wq: err_comp: - for (i = 0; i < ARRAY_SIZE(mdp->comp); i++) - mtk_mdp_comp_deinit(dev, mdp->comp[i]); + list_for_each_entry_safe(comp, comp_temp, &mdp->comp_list, node) { + mtk_mdp_unregister_component(mdp, comp); + mtk_mdp_comp_deinit(dev, comp); + } dev_dbg(dev, "err %d\n", ret); return ret; @@ -230,7 +246,7 @@ err_comp: static int mtk_mdp_remove(struct platform_device *pdev) { struct mtk_mdp_dev *mdp = platform_get_drvdata(pdev); - int i; + struct mtk_mdp_comp *comp, *comp_temp; pm_runtime_disable(&pdev->dev); vb2_dma_contig_clear_max_seg_size(&pdev->dev); @@ -243,8 +259,10 @@ static int mtk_mdp_remove(struct platform_device *pdev) flush_workqueue(mdp->job_wq); destroy_workqueue(mdp->job_wq); - for (i = 0; i < ARRAY_SIZE(mdp->comp); i++) - mtk_mdp_comp_deinit(&pdev->dev, mdp->comp[i]); + list_for_each_entry_safe(comp, comp_temp, &mdp->comp_list, node) { + mtk_mdp_unregister_component(mdp, comp); + mtk_mdp_comp_deinit(&pdev->dev, comp); + } dev_dbg(&pdev->dev, "%s driver unloaded\n", pdev->name); return 0; diff --git a/drivers/media/platform/mtk-mdp/mtk_mdp_core.h b/drivers/media/platform/mtk-mdp/mtk_mdp_core.h index dd130cc218c9f..a7da14b97077b 100644 --- a/drivers/media/platform/mtk-mdp/mtk_mdp_core.h +++ b/drivers/media/platform/mtk-mdp/mtk_mdp_core.h @@ -136,7 +136,7 @@ struct mtk_mdp_variant { * @pdev: pointer to the image processor platform device * @variant: the IP variant information * @id: image processor device index (0..MTK_MDP_MAX_DEVS) - * @comp: MDP function components + * @comp_list: list of MDP function components * @m2m_dev: v4l2 memory-to-memory device data * @ctx_list: list of struct mtk_mdp_ctx * @vdev: video device for image processor driver @@ -154,7 +154,7 @@ struct mtk_mdp_dev { struct platform_device *pdev; struct mtk_mdp_variant *variant; u16 id; - struct mtk_mdp_comp *comp[MTK_MDP_COMP_ID_MAX]; + struct list_head comp_list; struct v4l2_m2m_dev *m2m_dev; struct list_head ctx_list; struct video_device *vdev; @@ -221,6 +221,12 @@ struct mtk_mdp_ctx { extern int mtk_mdp_dbg_level; +void mtk_mdp_register_component(struct mtk_mdp_dev *mdp, + struct mtk_mdp_comp *comp); + +void mtk_mdp_unregister_component(struct mtk_mdp_dev *mdp, + struct mtk_mdp_comp *comp); + #if defined(DEBUG) #define mtk_mdp_dbg(level, fmt, args...) \ -- GitLab From 37e278c801078dcf165983a83f7cbefbae856004 Mon Sep 17 00:00:00 2001 From: Eizan Miyamoto Date: Thu, 7 May 2020 12:23:50 +0200 Subject: [PATCH 0224/1476] media: mtk-mdp: Remove mtk_mdp_comp.id and supporting functionality Since components are registered in a list, the numeric component id that specified a location in an array is not necessary. Signed-off-by: Eizan Miyamoto Reviewed-by: Enric Balletbo i Serra Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/mtk-mdp/mtk_mdp_comp.c | 60 +++---------------- drivers/media/platform/mtk-mdp/mtk_mdp_comp.h | 19 +----- drivers/media/platform/mtk-mdp/mtk_mdp_core.c | 9 +-- 3 files changed, 10 insertions(+), 78 deletions(-) diff --git a/drivers/media/platform/mtk-mdp/mtk_mdp_comp.c b/drivers/media/platform/mtk-mdp/mtk_mdp_comp.c index da2bdad7a8d18..362fff924aeff 100644 --- a/drivers/media/platform/mtk-mdp/mtk_mdp_comp.c +++ b/drivers/media/platform/mtk-mdp/mtk_mdp_comp.c @@ -14,46 +14,6 @@ #include "mtk_mdp_comp.h" -static const char * const mtk_mdp_comp_stem[MTK_MDP_COMP_TYPE_MAX] = { - "mdp-rdma", - "mdp-rsz", - "mdp-wdma", - "mdp-wrot", -}; - -struct mtk_mdp_comp_match { - enum mtk_mdp_comp_type type; - int alias_id; -}; - -static const struct mtk_mdp_comp_match mtk_mdp_matches[MTK_MDP_COMP_ID_MAX] = { - { MTK_MDP_RDMA, 0 }, - { MTK_MDP_RDMA, 1 }, - { MTK_MDP_RSZ, 0 }, - { MTK_MDP_RSZ, 1 }, - { MTK_MDP_RSZ, 2 }, - { MTK_MDP_WDMA, 0 }, - { MTK_MDP_WROT, 0 }, - { MTK_MDP_WROT, 1 }, -}; - -int mtk_mdp_comp_get_id(struct device *dev, struct device_node *node, - enum mtk_mdp_comp_type comp_type) -{ - int id = of_alias_get_id(node, mtk_mdp_comp_stem[comp_type]); - int i; - - for (i = 0; i < ARRAY_SIZE(mtk_mdp_matches); i++) { - if (comp_type == mtk_mdp_matches[i].type && - id == mtk_mdp_matches[i].alias_id) - return i; - } - - dev_err(dev, "Failed to get id. type: %d, id: %d\n", comp_type, id); - - return -EINVAL; -} - void mtk_mdp_comp_clock_on(struct device *dev, struct mtk_mdp_comp *comp) { int i, err; @@ -62,8 +22,8 @@ void mtk_mdp_comp_clock_on(struct device *dev, struct mtk_mdp_comp *comp) err = mtk_smi_larb_get(comp->larb_dev); if (err) dev_err(dev, - "failed to get larb, err %d. type:%d id:%d\n", - err, comp->type, comp->id); + "failed to get larb, err %d. type:%d\n", + err, comp->type); } for (i = 0; i < ARRAY_SIZE(comp->clk); i++) { @@ -72,8 +32,8 @@ void mtk_mdp_comp_clock_on(struct device *dev, struct mtk_mdp_comp *comp) err = clk_prepare_enable(comp->clk[i]); if (err) dev_err(dev, - "failed to enable clock, err %d. type:%d id:%d i:%d\n", - err, comp->type, comp->id, i); + "failed to enable clock, err %d. type:%d i:%d\n", + err, comp->type, i); } } @@ -92,21 +52,15 @@ void mtk_mdp_comp_clock_off(struct device *dev, struct mtk_mdp_comp *comp) } int mtk_mdp_comp_init(struct device *dev, struct device_node *node, - struct mtk_mdp_comp *comp, enum mtk_mdp_comp_id comp_id) + struct mtk_mdp_comp *comp, + enum mtk_mdp_comp_type comp_type) { struct device_node *larb_node; struct platform_device *larb_pdev; int i; - if (comp_id < 0 || comp_id >= MTK_MDP_COMP_ID_MAX) { - dev_err(dev, "Invalid comp_id %d\n", comp_id); - return -EINVAL; - } - - INIT_LIST_HEAD(&comp->node); comp->dev_node = of_node_get(node); - comp->id = comp_id; - comp->type = mtk_mdp_matches[comp_id].type; + comp->type = comp_type; for (i = 0; i < ARRAY_SIZE(comp->clk); i++) { comp->clk[i] = of_clk_get(node, i); diff --git a/drivers/media/platform/mtk-mdp/mtk_mdp_comp.h b/drivers/media/platform/mtk-mdp/mtk_mdp_comp.h index 1f745891c6c38..1bf0242cce46d 100644 --- a/drivers/media/platform/mtk-mdp/mtk_mdp_comp.h +++ b/drivers/media/platform/mtk-mdp/mtk_mdp_comp.h @@ -22,18 +22,6 @@ enum mtk_mdp_comp_type { MTK_MDP_COMP_TYPE_MAX, }; -enum mtk_mdp_comp_id { - MTK_MDP_COMP_RDMA0, - MTK_MDP_COMP_RDMA1, - MTK_MDP_COMP_RSZ0, - MTK_MDP_COMP_RSZ1, - MTK_MDP_COMP_RSZ2, - MTK_MDP_COMP_WDMA, - MTK_MDP_COMP_WROT0, - MTK_MDP_COMP_WROT1, - MTK_MDP_COMP_ID_MAX, -}; - /** * struct mtk_mdp_comp - the MDP's function component data * @node: list node to track sibing MDP components @@ -41,7 +29,6 @@ enum mtk_mdp_comp_id { * @clk: clocks required for component * @larb_dev: SMI device required for component * @type: component type - * @id: component ID */ struct mtk_mdp_comp { struct list_head node; @@ -49,14 +36,12 @@ struct mtk_mdp_comp { struct clk *clk[2]; struct device *larb_dev; enum mtk_mdp_comp_type type; - enum mtk_mdp_comp_id id; }; int mtk_mdp_comp_init(struct device *dev, struct device_node *node, - struct mtk_mdp_comp *comp, enum mtk_mdp_comp_id comp_id); + struct mtk_mdp_comp *comp, + enum mtk_mdp_comp_type comp_type); void mtk_mdp_comp_deinit(struct device *dev, struct mtk_mdp_comp *comp); -int mtk_mdp_comp_get_id(struct device *dev, struct device_node *node, - enum mtk_mdp_comp_type comp_type); void mtk_mdp_comp_clock_on(struct device *dev, struct mtk_mdp_comp *comp); void mtk_mdp_comp_clock_off(struct device *dev, struct mtk_mdp_comp *comp); diff --git a/drivers/media/platform/mtk-mdp/mtk_mdp_core.c b/drivers/media/platform/mtk-mdp/mtk_mdp_core.c index 40b9fda8b03b5..f96c8b3bf8618 100644 --- a/drivers/media/platform/mtk-mdp/mtk_mdp_core.c +++ b/drivers/media/platform/mtk-mdp/mtk_mdp_core.c @@ -137,7 +137,6 @@ static int mtk_mdp_probe(struct platform_device *pdev) for_each_child_of_node(parent, node) { const struct of_device_id *of_id; enum mtk_mdp_comp_type comp_type; - int comp_id; of_id = of_match_node(mtk_mdp_comp_dt_ids, node); if (!of_id) @@ -150,12 +149,6 @@ static int mtk_mdp_probe(struct platform_device *pdev) } comp_type = (enum mtk_mdp_comp_type)of_id->data; - comp_id = mtk_mdp_comp_get_id(dev, node, comp_type); - if (comp_id < 0) { - dev_warn(dev, "Skipping unknown component %pOF\n", - node); - continue; - } comp = devm_kzalloc(dev, sizeof(*comp), GFP_KERNEL); if (!comp) { @@ -164,7 +157,7 @@ static int mtk_mdp_probe(struct platform_device *pdev) goto err_comp; } - ret = mtk_mdp_comp_init(dev, node, comp, comp_id); + ret = mtk_mdp_comp_init(dev, node, comp, comp_type); if (ret) { of_node_put(node); goto err_comp; -- GitLab From 414953b4833f2b2024ec46213a75586465acea90 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Fri, 24 Apr 2020 13:25:54 +0200 Subject: [PATCH 0225/1476] media: cx231xx: remove redundant assignment to variable err The variable err is being initialized with a value that is never read and it is being updated later with a new value. The initialization is redundant and can be removed. Addresses-Coverity: ("Unused value") Signed-off-by: Colin Ian King Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/cx231xx/cx231xx-417.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/usb/cx231xx/cx231xx-417.c b/drivers/media/usb/cx231xx/cx231xx-417.c index b0cd511346542..c5e21785fafe2 100644 --- a/drivers/media/usb/cx231xx/cx231xx-417.c +++ b/drivers/media/usb/cx231xx/cx231xx-417.c @@ -1734,7 +1734,7 @@ static void cx231xx_video_dev_init( int cx231xx_417_register(struct cx231xx *dev) { /* FIXME: Port1 hardcoded here */ - int err = -ENODEV; + int err; struct cx231xx_tsport *tsport = &dev->ts1; struct vb2_queue *q; -- GitLab From c92d30e4b78dc331909f8c6056c2792aa14e2166 Mon Sep 17 00:00:00 2001 From: Eugeniu Rosca Date: Tue, 2 Jun 2020 21:50:16 +0200 Subject: [PATCH 0226/1476] media: vsp1: dl: Fix NULL pointer dereference on unbind In commit f3b98e3c4d2e16 ("media: vsp1: Provide support for extended command pools"), the vsp pointer used for referencing the VSP1 device structure from a command pool during vsp1_dl_ext_cmd_pool_destroy() was not populated. Correctly assign the pointer to prevent the following null-pointer-dereference when removing the device: [*] h3ulcb-kf #> echo fea28000.vsp > /sys/bus/platform/devices/fea28000.vsp/driver/unbind Unable to handle kernel NULL pointer dereference at virtual address 0000000000000028 Mem abort info: ESR = 0x96000006 EC = 0x25: DABT (current EL), IL = 32 bits SET = 0, FnV = 0 EA = 0, S1PTW = 0 Data abort info: ISV = 0, ISS = 0x00000006 CM = 0, WnR = 0 user pgtable: 4k pages, 48-bit VAs, pgdp=00000007318be000 [0000000000000028] pgd=00000007333a1003, pud=00000007333a6003, pmd=0000000000000000 Internal error: Oops: 96000006 [#1] PREEMPT SMP Modules linked in: CPU: 1 PID: 486 Comm: sh Not tainted 5.7.0-rc6-arm64-renesas-00118-ge644645abf47 #185 Hardware name: Renesas H3ULCB Kingfisher board based on r8a77951 (DT) pstate: 40000005 (nZcv daif -PAN -UAO) pc : vsp1_dlm_destroy+0xe4/0x11c lr : vsp1_dlm_destroy+0xc8/0x11c sp : ffff800012963b60 x29: ffff800012963b60 x28: ffff0006f83fc440 x27: 0000000000000000 x26: ffff0006f5e13e80 x25: ffff0006f5e13ed0 x24: ffff0006f5e13ed0 x23: ffff0006f5e13ed0 x22: dead000000000122 x21: ffff0006f5e3a080 x20: ffff0006f5df2938 x19: ffff0006f5df2980 x18: 0000000000000003 x17: 0000000000000000 x16: 0000000000000016 x15: 0000000000000003 x14: 00000000000393c0 x13: ffff800011a5ec18 x12: ffff800011d8d000 x11: ffff0006f83fcc68 x10: ffff800011a53d70 x9 : ffff8000111f3000 x8 : 0000000000000000 x7 : 0000000000210d00 x6 : 0000000000000000 x5 : ffff800010872e60 x4 : 0000000000000004 x3 : 0000000078068000 x2 : ffff800012781000 x1 : 0000000000002c00 x0 : 0000000000000000 Call trace: vsp1_dlm_destroy+0xe4/0x11c vsp1_wpf_destroy+0x10/0x20 vsp1_entity_destroy+0x24/0x4c vsp1_destroy_entities+0x54/0x130 vsp1_remove+0x1c/0x40 platform_drv_remove+0x28/0x50 __device_release_driver+0x178/0x220 device_driver_detach+0x44/0xc0 unbind_store+0xe0/0x104 drv_attr_store+0x20/0x30 sysfs_kf_write+0x48/0x70 kernfs_fop_write+0x148/0x230 __vfs_write+0x18/0x40 vfs_write+0xdc/0x1c4 ksys_write+0x68/0xf0 __arm64_sys_write+0x18/0x20 el0_svc_common.constprop.0+0x70/0x170 do_el0_svc+0x20/0x80 el0_sync_handler+0x134/0x1b0 el0_sync+0x140/0x180 Code: b40000c2 f9403a60 d2800084 a9400663 (f9401400) ---[ end trace 3875369841fb288a ]--- Fixes: f3b98e3c4d2e16 ("media: vsp1: Provide support for extended command pools") Cc: stable@vger.kernel.org # v4.19+ Signed-off-by: Eugeniu Rosca Reviewed-by: Kieran Bingham Tested-by: Kieran Bingham Reviewed-by: Laurent Pinchart Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/vsp1/vsp1_dl.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/media/platform/vsp1/vsp1_dl.c b/drivers/media/platform/vsp1/vsp1_dl.c index d7b43037e500a..e07b135613eb5 100644 --- a/drivers/media/platform/vsp1/vsp1_dl.c +++ b/drivers/media/platform/vsp1/vsp1_dl.c @@ -431,6 +431,8 @@ vsp1_dl_cmd_pool_create(struct vsp1_device *vsp1, enum vsp1_extcmd_type type, if (!pool) return NULL; + pool->vsp1 = vsp1; + spin_lock_init(&pool->lock); INIT_LIST_HEAD(&pool->free); -- GitLab From 7f52faabd2e533dd3f59d2d69093756fa25a1b2e Mon Sep 17 00:00:00 2001 From: Jeff Chase Date: Tue, 23 Jun 2020 01:59:48 +0200 Subject: [PATCH 0227/1476] media: dt-bindings: Add ch7322 media i2c device The ch7322 is a Chrontel CEC controller. Signed-off-by: Jeff Chase Reviewed-by: Rob Herring Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- .../bindings/media/i2c/chrontel,ch7322.yaml | 67 +++++++++++++++++++ MAINTAINERS | 7 ++ 2 files changed, 74 insertions(+) create mode 100644 Documentation/devicetree/bindings/media/i2c/chrontel,ch7322.yaml diff --git a/Documentation/devicetree/bindings/media/i2c/chrontel,ch7322.yaml b/Documentation/devicetree/bindings/media/i2c/chrontel,ch7322.yaml new file mode 100644 index 0000000000000..daa2869377c55 --- /dev/null +++ b/Documentation/devicetree/bindings/media/i2c/chrontel,ch7322.yaml @@ -0,0 +1,67 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: "http://devicetree.org/schemas/media/i2c/chrontel,ch7322.yaml#" +$schema: "http://devicetree.org/meta-schemas/core.yaml#" + +title: Chrontel HDMI-CEC Controller + +maintainers: + - Jeff Chase + +description: + The Chrontel CH7322 is a discrete HDMI-CEC controller. It is + programmable through I2C and drives a single CEC line. + +properties: + compatible: + const: chrontel,ch7322 + + reg: + description: I2C device address + maxItems: 1 + + clocks: + maxItems: 1 + + interrupts: + maxItems: 1 + + reset-gpios: + description: + Reference to the GPIO connected to the RESET pin, if any. This + pin is active-low. + maxItems: 1 + + standby-gpios: + description: + Reference to the GPIO connected to the OE pin, if any. When low + the device will respond to power status requests with "standby" + if in auto mode. + maxItems: 1 + + # see ../cec.txt + hdmi-phandle: + description: phandle to the HDMI controller + +required: + - compatible + - reg + - interrupts + +examples: + - | + #include + #include + i2c { + #address-cells = <1>; + #size-cells = <0>; + ch7322@75 { + compatible = "chrontel,ch7322"; + reg = <0x75>; + interrupts = <47 IRQ_TYPE_EDGE_RISING>; + standby-gpios = <&gpio 16 GPIO_ACTIVE_LOW>; + reset-gpios = <&gpio 15 GPIO_ACTIVE_LOW>; + hdmi-phandle = <&hdmi>; + }; + }; diff --git a/MAINTAINERS b/MAINTAINERS index 634d2c3d621aa..9982c7c12af92 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -4101,6 +4101,13 @@ F: drivers/power/supply/cros_usbpd-charger.c N: cros_ec N: cros-ec +CHRONTEL CH7322 CEC DRIVER +M: Jeff Chase +L: linux-media@vger.kernel.org +S: Maintained +T: git git://linuxtv.org/media_tree.git +F: Documentation/devicetree/bindings/media/i2c/chontel,ch7322.yaml + CIRRUS LOGIC AUDIO CODEC DRIVERS M: James Schulman M: David Rhodes -- GitLab From 98f803cfa76eb67d0e429ba76a39471f95d83675 Mon Sep 17 00:00:00 2001 From: Jeff Chase Date: Tue, 23 Jun 2020 01:59:49 +0200 Subject: [PATCH 0228/1476] media: cec: add adap_controls_phys_addr option Use of the cec notifier framework is required to support CEC_CAP_CONNECTOR_INFO but some devices do not want physical address updates from the notifier. This adds an option to allow registering with a cec notifier without getting address updates. [hans: document the new adap_controls_phys_addr bool] Signed-off-by: Jeff Chase Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/cec/core/cec-notifier.c | 11 +++++++---- include/media/cec.h | 6 ++++++ 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/drivers/media/cec/core/cec-notifier.c b/drivers/media/cec/core/cec-notifier.c index 517e0035fc993..95f363bb1d198 100644 --- a/drivers/media/cec/core/cec-notifier.c +++ b/drivers/media/cec/core/cec-notifier.c @@ -116,7 +116,8 @@ cec_notifier_conn_register(struct device *hdmi_dev, const char *port_name, else memset(&n->conn_info, 0, sizeof(n->conn_info)); if (n->cec_adap) { - cec_phys_addr_invalidate(n->cec_adap); + if (!n->cec_adap->adap_controls_phys_addr) + cec_phys_addr_invalidate(n->cec_adap); cec_s_conn_info(n->cec_adap, conn_info); } mutex_unlock(&n->lock); @@ -133,7 +134,8 @@ void cec_notifier_conn_unregister(struct cec_notifier *n) memset(&n->conn_info, 0, sizeof(n->conn_info)); n->phys_addr = CEC_PHYS_ADDR_INVALID; if (n->cec_adap) { - cec_phys_addr_invalidate(n->cec_adap); + if (!n->cec_adap->adap_controls_phys_addr) + cec_phys_addr_invalidate(n->cec_adap); cec_s_conn_info(n->cec_adap, NULL); } mutex_unlock(&n->lock); @@ -158,7 +160,8 @@ cec_notifier_cec_adap_register(struct device *hdmi_dev, const char *port_name, n->cec_adap = adap; adap->conn_info = n->conn_info; adap->notifier = n; - cec_s_phys_addr(adap, n->phys_addr, false); + if (!adap->adap_controls_phys_addr) + cec_s_phys_addr(adap, n->phys_addr, false); mutex_unlock(&n->lock); return n; } @@ -185,7 +188,7 @@ void cec_notifier_set_phys_addr(struct cec_notifier *n, u16 pa) mutex_lock(&n->lock); n->phys_addr = pa; - if (n->cec_adap) + if (n->cec_adap && !n->cec_adap->adap_controls_phys_addr) cec_s_phys_addr(n->cec_adap, n->phys_addr, false); mutex_unlock(&n->lock); } diff --git a/include/media/cec.h b/include/media/cec.h index 32f7c695d7b5e..c48b5f2e4b504 100644 --- a/include/media/cec.h +++ b/include/media/cec.h @@ -172,6 +172,11 @@ struct cec_adap_ops { * @is_configured: the CEC adapter is configured (i.e. has claimed LAs) * @cec_pin_is_high: if true then the CEC pin is high. Only used with the * CEC pin framework. + * @adap_controls_phys_addr: if true, then the CEC adapter controls the + * physical address, i.e. the CEC hardware can detect HPD changes and + * read the EDID and is not dependent on an external HDMI driver. + * Drivers that need this can set this field to true after the + * cec_allocate_adapter() call. * @last_initiator: the initiator of the last transmitted message. * @monitor_all_cnt: number of filehandles monitoring all msgs * @monitor_pin_cnt: number of filehandles monitoring pin changes @@ -222,6 +227,7 @@ struct cec_adapter { bool is_configuring; bool is_configured; bool cec_pin_is_high; + bool adap_controls_phys_addr; u8 last_initiator; u32 monitor_all_cnt; u32 monitor_pin_cnt; -- GitLab From 21b9a47e0ec7997c5a762c102cc316ea0c23243f Mon Sep 17 00:00:00 2001 From: Jeff Chase Date: Tue, 23 Jun 2020 01:59:50 +0200 Subject: [PATCH 0229/1476] media: cec: i2c: ch7322: Add ch7322 CEC controller driver Add a CEC device driver for the Chrontel ch7322 CEC conroller. This is an I2C device capable of sending and receiving CEC messages. Signed-off-by: Jeff Chase Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- MAINTAINERS | 1 + drivers/media/cec/Kconfig | 1 + drivers/media/cec/Makefile | 2 +- drivers/media/cec/i2c/Kconfig | 14 + drivers/media/cec/i2c/Makefile | 5 + drivers/media/cec/i2c/ch7322.c | 604 +++++++++++++++++++++++++++++++++ 6 files changed, 626 insertions(+), 1 deletion(-) create mode 100644 drivers/media/cec/i2c/Kconfig create mode 100644 drivers/media/cec/i2c/Makefile create mode 100644 drivers/media/cec/i2c/ch7322.c diff --git a/MAINTAINERS b/MAINTAINERS index 9982c7c12af92..6362b96b7af34 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -4107,6 +4107,7 @@ L: linux-media@vger.kernel.org S: Maintained T: git git://linuxtv.org/media_tree.git F: Documentation/devicetree/bindings/media/i2c/chontel,ch7322.yaml +F: drivers/media/cec/i2c/ch7322.c CIRRUS LOGIC AUDIO CODEC DRIVERS M: James Schulman diff --git a/drivers/media/cec/Kconfig b/drivers/media/cec/Kconfig index 7e830444bdbbb..9ba3a00dce317 100644 --- a/drivers/media/cec/Kconfig +++ b/drivers/media/cec/Kconfig @@ -33,6 +33,7 @@ menuconfig MEDIA_CEC_SUPPORT adapter that supports HDMI CEC. if MEDIA_CEC_SUPPORT +source "drivers/media/cec/i2c/Kconfig" source "drivers/media/cec/platform/Kconfig" source "drivers/media/cec/usb/Kconfig" endif diff --git a/drivers/media/cec/Makefile b/drivers/media/cec/Makefile index 74e80e1b3571e..23539339bc81e 100644 --- a/drivers/media/cec/Makefile +++ b/drivers/media/cec/Makefile @@ -1,2 +1,2 @@ # SPDX-License-Identifier: GPL-2.0 -obj-y += core/ platform/ usb/ +obj-y += core/ i2c/ platform/ usb/ diff --git a/drivers/media/cec/i2c/Kconfig b/drivers/media/cec/i2c/Kconfig new file mode 100644 index 0000000000000..70432a1d69186 --- /dev/null +++ b/drivers/media/cec/i2c/Kconfig @@ -0,0 +1,14 @@ +# SPDX-License-Identifier: GPL-2.0 +# +# I2C drivers + +config CEC_CH7322 + tristate "Chrontel CH7322 CEC controller" + depends on I2C + select REGMAP_I2C + select CEC_CORE + help + This is a driver for the Chrontel CH7322 CEC controller. It uses the + generic CEC framework interface. + CEC bus is present in the HDMI connector and enables communication + between compatible devices. diff --git a/drivers/media/cec/i2c/Makefile b/drivers/media/cec/i2c/Makefile new file mode 100644 index 0000000000000..d7496dfd0fa49 --- /dev/null +++ b/drivers/media/cec/i2c/Makefile @@ -0,0 +1,5 @@ +# SPDX-License-Identifier: GPL-2.0 +# +# Makefile for the CEC I2C device drivers. +# +obj-$(CONFIG_CEC_CH7322) += ch7322.o diff --git a/drivers/media/cec/i2c/ch7322.c b/drivers/media/cec/i2c/ch7322.c new file mode 100644 index 0000000000000..0814338c43e4b --- /dev/null +++ b/drivers/media/cec/i2c/ch7322.c @@ -0,0 +1,604 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Driver for the Chrontel CH7322 CEC Controller + * + * Copyright 2020 Google LLC. + */ + +/* + * Notes + * + * - This device powers on in Auto Mode which has limited functionality. This + * driver disables Auto Mode when it attaches. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define CH7322_WRITE 0x00 +#define CH7322_WRITE_MSENT 0x80 +#define CH7322_WRITE_BOK 0x40 +#define CH7322_WRITE_NMASK 0x0f + +/* Write buffer is 0x01-0x10 */ +#define CH7322_WRBUF 0x01 +#define CH7322_WRBUF_LEN 0x10 + +#define CH7322_READ 0x40 +#define CH7322_READ_NRDT 0x80 +#define CH7322_READ_MSENT 0x20 +#define CH7322_READ_NMASK 0x0f + +/* Read buffer is 0x41-0x50 */ +#define CH7322_RDBUF 0x41 +#define CH7322_RDBUF_LEN 0x10 + +#define CH7322_MODE 0x11 +#define CH7322_MODE_AUTO 0x78 +#define CH7322_MODE_SW 0xb5 + +#define CH7322_RESET 0x12 +#define CH7322_RESET_RST 0x00 + +#define CH7322_POWER 0x13 +#define CH7322_POWER_FPD 0x04 + +#define CH7322_CFG0 0x17 +#define CH7322_CFG0_EOBEN 0x40 +#define CH7322_CFG0_PEOB 0x20 +#define CH7322_CFG0_CLRSPP 0x10 +#define CH7322_CFG0_FLOW 0x08 + +#define CH7322_CFG1 0x1a +#define CH7322_CFG1_STDBYO 0x04 +#define CH7322_CFG1_HPBP 0x02 +#define CH7322_CFG1_PIO 0x01 + +#define CH7322_INTCTL 0x1b +#define CH7322_INTCTL_INTPB 0x80 +#define CH7322_INTCTL_STDBY 0x40 +#define CH7322_INTCTL_HPDFALL 0x20 +#define CH7322_INTCTL_HPDRISE 0x10 +#define CH7322_INTCTL_RXMSG 0x08 +#define CH7322_INTCTL_TXMSG 0x04 +#define CH7322_INTCTL_NEWPHA 0x02 +#define CH7322_INTCTL_ERROR 0x01 + +#define CH7322_DVCLKFNH 0x1d +#define CH7322_DVCLKFNL 0x1e + +#define CH7322_CTL 0x31 +#define CH7322_CTL_FSTDBY 0x80 +#define CH7322_CTL_PLSEN 0x40 +#define CH7322_CTL_PLSPB 0x20 +#define CH7322_CTL_SPADL 0x10 +#define CH7322_CTL_HINIT 0x08 +#define CH7322_CTL_WPHYA 0x04 +#define CH7322_CTL_H1T 0x02 +#define CH7322_CTL_S1T 0x01 + +#define CH7322_PAWH 0x32 +#define CH7322_PAWL 0x33 + +#define CH7322_ADDLW 0x34 +#define CH7322_ADDLW_MASK 0xf0 + +#define CH7322_ADDLR 0x3d +#define CH7322_ADDLR_HPD 0x80 +#define CH7322_ADDLR_MASK 0x0f + +#define CH7322_INTDATA 0x3e +#define CH7322_INTDATA_MODE 0x80 +#define CH7322_INTDATA_STDBY 0x40 +#define CH7322_INTDATA_HPDFALL 0x20 +#define CH7322_INTDATA_HPDRISE 0x10 +#define CH7322_INTDATA_RXMSG 0x08 +#define CH7322_INTDATA_TXMSG 0x04 +#define CH7322_INTDATA_NEWPHA 0x02 +#define CH7322_INTDATA_ERROR 0x01 + +#define CH7322_EVENT 0x3f +#define CH7322_EVENT_TXERR 0x80 +#define CH7322_EVENT_HRST 0x40 +#define CH7322_EVENT_HFST 0x20 +#define CH7322_EVENT_PHACHG 0x10 +#define CH7322_EVENT_ACTST 0x08 +#define CH7322_EVENT_PHARDY 0x04 +#define CH7322_EVENT_BSOK 0x02 +#define CH7322_EVENT_ERRADCF 0x01 + +#define CH7322_DID 0x51 +#define CH7322_DID_CH7322 0x5b +#define CH7322_DID_CH7323 0x5f + +#define CH7322_REVISIONID 0x52 + +#define CH7322_PARH 0x53 +#define CH7322_PARL 0x54 + +#define CH7322_IOCFG2 0x75 +#define CH7322_IOCFG_CIO 0x80 +#define CH7322_IOCFG_IOCFGMASK 0x78 +#define CH7322_IOCFG_AUDIO 0x04 +#define CH7322_IOCFG_SPAMST 0x02 +#define CH7322_IOCFG_SPAMSP 0x01 + +#define CH7322_CTL3 0x7b +#define CH7322_CTL3_SWENA 0x80 +#define CH7322_CTL3_FC_INIT 0x40 +#define CH7322_CTL3_SML_FL 0x20 +#define CH7322_CTL3_SM_RDST 0x10 +#define CH7322_CTL3_SPP_CIAH 0x08 +#define CH7322_CTL3_SPP_CIAL 0x04 +#define CH7322_CTL3_SPP_ACTH 0x02 +#define CH7322_CTL3_SPP_ACTL 0x01 + +/* BOK status means NACK */ +#define CH7322_TX_FLAG_NACK BIT(0) +/* Device will retry automatically */ +#define CH7322_TX_FLAG_RETRY BIT(1) + +struct ch7322 { + struct i2c_client *i2c; + struct regmap *regmap; + struct cec_adapter *cec; + struct mutex mutex; /* device access mutex */ + u8 tx_flags; +}; + +static const struct regmap_config ch7322_regmap = { + .reg_bits = 8, + .val_bits = 8, + .max_register = 0x7f, + .disable_locking = true, +}; + +static int ch7322_send_message(struct ch7322 *ch7322, const struct cec_msg *msg) +{ + unsigned int val; + unsigned int len = msg->len; + int ret; + int i; + + WARN_ON(!mutex_is_locked(&ch7322->mutex)); + + if (len > CH7322_WRBUF_LEN || len < 1) + return -EINVAL; + + ret = regmap_read(ch7322->regmap, CH7322_WRITE, &val); + if (ret) + return ret; + + /* Buffer not ready */ + if (!(val & CH7322_WRITE_MSENT)) + return -EBUSY; + + if (cec_msg_opcode(msg) == -1 && + cec_msg_initiator(msg) == cec_msg_destination(msg)) { + ch7322->tx_flags = CH7322_TX_FLAG_NACK | CH7322_TX_FLAG_RETRY; + } else if (cec_msg_is_broadcast(msg)) { + ch7322->tx_flags = CH7322_TX_FLAG_NACK; + } else { + ch7322->tx_flags = CH7322_TX_FLAG_RETRY; + } + + ret = regmap_write(ch7322->regmap, CH7322_WRITE, len - 1); + if (ret) + return ret; + + for (i = 0; i < len; i++) { + ret = regmap_write(ch7322->regmap, + CH7322_WRBUF + i, msg->msg[i]); + if (ret) + return ret; + } + + return 0; +} + +static int ch7322_receive_message(struct ch7322 *ch7322, struct cec_msg *msg) +{ + unsigned int val; + int ret = 0; + int i; + + WARN_ON(!mutex_is_locked(&ch7322->mutex)); + + ret = regmap_read(ch7322->regmap, CH7322_READ, &val); + if (ret) + return ret; + + /* Message not ready */ + if (!(val & CH7322_READ_NRDT)) + return -EIO; + + msg->len = (val & CH7322_READ_NMASK) + 1; + + /* Read entire RDBUF to clear state */ + for (i = 0; i < CH7322_RDBUF_LEN; i++) { + ret = regmap_read(ch7322->regmap, CH7322_RDBUF + i, &val); + if (ret) + return ret; + msg->msg[i] = (u8)val; + } + + return 0; +} + +static void ch7322_tx_done(struct ch7322 *ch7322) +{ + int ret; + unsigned int val; + u8 status, flags; + + mutex_lock(&ch7322->mutex); + ret = regmap_read(ch7322->regmap, CH7322_WRITE, &val); + flags = ch7322->tx_flags; + mutex_unlock(&ch7322->mutex); + + /* + * The device returns a one-bit OK status which usually means ACK but + * actually means NACK when sending a logical address query or a + * broadcast. + */ + if (ret) + status = CEC_TX_STATUS_ERROR; + else if ((val & CH7322_WRITE_BOK) && (flags & CH7322_TX_FLAG_NACK)) + status = CEC_TX_STATUS_NACK; + else if (val & CH7322_WRITE_BOK) + status = CEC_TX_STATUS_OK; + else if (flags & CH7322_TX_FLAG_NACK) + status = CEC_TX_STATUS_OK; + else + status = CEC_TX_STATUS_NACK; + + if (status == CEC_TX_STATUS_NACK && (flags & CH7322_TX_FLAG_RETRY)) + status |= CEC_TX_STATUS_MAX_RETRIES; + + cec_transmit_attempt_done(ch7322->cec, status); +} + +static void ch7322_rx_done(struct ch7322 *ch7322) +{ + struct cec_msg msg; + int ret; + + mutex_lock(&ch7322->mutex); + ret = ch7322_receive_message(ch7322, &msg); + mutex_unlock(&ch7322->mutex); + + if (ret) + dev_err(&ch7322->i2c->dev, "cec receive error: %d\n", ret); + else + cec_received_msg(ch7322->cec, &msg); +} + +/* + * This device can either monitor the DDC lines to obtain the physical address + * or it can allow the host to program it. This driver lets the device obtain + * it. + */ +static void ch7322_phys_addr(struct ch7322 *ch7322) +{ + unsigned int pah, pal; + int ret = 0; + + mutex_lock(&ch7322->mutex); + ret |= regmap_read(ch7322->regmap, CH7322_PARH, &pah); + ret |= regmap_read(ch7322->regmap, CH7322_PARL, &pal); + mutex_unlock(&ch7322->mutex); + + if (ret) + dev_err(&ch7322->i2c->dev, "phys addr error\n"); + else + cec_s_phys_addr(ch7322->cec, pal | (pah << 8), false); +} + +static irqreturn_t ch7322_irq(int irq, void *dev) +{ + struct ch7322 *ch7322 = dev; + unsigned int data = 0; + + mutex_lock(&ch7322->mutex); + regmap_read(ch7322->regmap, CH7322_INTDATA, &data); + regmap_write(ch7322->regmap, CH7322_INTDATA, data); + mutex_unlock(&ch7322->mutex); + + if (data & CH7322_INTDATA_HPDFALL) + cec_phys_addr_invalidate(ch7322->cec); + + if (data & CH7322_INTDATA_TXMSG) + ch7322_tx_done(ch7322); + + if (data & CH7322_INTDATA_RXMSG) + ch7322_rx_done(ch7322); + + if (data & CH7322_INTDATA_NEWPHA) + ch7322_phys_addr(ch7322); + + if (data & CH7322_INTDATA_ERROR) + dev_dbg(&ch7322->i2c->dev, "unknown error\n"); + + return IRQ_HANDLED; +} + +/* This device is always enabled */ +static int ch7322_cec_adap_enable(struct cec_adapter *adap, bool enable) +{ + return 0; +} + +static int ch7322_cec_adap_log_addr(struct cec_adapter *adap, u8 log_addr) +{ + struct ch7322 *ch7322 = cec_get_drvdata(adap); + int ret; + + mutex_lock(&ch7322->mutex); + ret = regmap_update_bits(ch7322->regmap, CH7322_ADDLW, + CH7322_ADDLW_MASK, log_addr << 4); + mutex_unlock(&ch7322->mutex); + + return ret; +} + +static int ch7322_cec_adap_transmit(struct cec_adapter *adap, u8 attempts, + u32 signal_free_time, struct cec_msg *msg) +{ + struct ch7322 *ch7322 = cec_get_drvdata(adap); + int ret; + + mutex_lock(&ch7322->mutex); + ret = ch7322_send_message(ch7322, msg); + mutex_unlock(&ch7322->mutex); + + return ret; +} + +static const struct cec_adap_ops ch7322_cec_adap_ops = { + .adap_enable = ch7322_cec_adap_enable, + .adap_log_addr = ch7322_cec_adap_log_addr, + .adap_transmit = ch7322_cec_adap_transmit, +}; + +#if IS_ENABLED(CONFIG_PCI) && IS_ENABLED(CONFIG_DMI) + +struct ch7322_conn_match { + const char *dev_name; + const char *pci_name; + const char *port_name; +}; + +static struct ch7322_conn_match google_endeavour[] = { + { "i2c-PRP0001:00", "0000:00:02.0", "Port B" }, + { "i2c-PRP0001:01", "0000:00:02.0", "Port C" }, + { }, +}; + +static const struct dmi_system_id ch7322_dmi_table[] = { + { + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "Google"), + DMI_MATCH(DMI_BOARD_NAME, "Endeavour"), + }, + .driver_data = google_endeavour, + }, + { }, +}; + +/* Make a best-effort attempt to locate a matching HDMI port */ +static int ch7322_get_port(struct i2c_client *client, + struct device **dev, + const char **port) +{ + const struct dmi_system_id *system; + const struct ch7322_conn_match *conn; + + *dev = NULL; + *port = NULL; + + system = dmi_first_match(ch7322_dmi_table); + if (!system) + return 0; + + for (conn = system->driver_data; conn->dev_name; conn++) { + if (!strcmp(dev_name(&client->dev), conn->dev_name)) { + struct device *d; + + d = bus_find_device_by_name(&pci_bus_type, NULL, + conn->pci_name); + if (!d) + return -EPROBE_DEFER; + + put_device(d); + + *dev = d; + *port = conn->port_name; + + return 0; + } + } + + return 0; +} + +#else + +static int ch7322_get_port(struct i2c_client *client, + struct device **dev, + const char **port) +{ + *dev = NULL; + *port = NULL; + + return 0; +} + +#endif + +static int ch7322_probe(struct i2c_client *client) +{ + struct device *hdmi_dev; + const char *port_name; + struct ch7322 *ch7322; + struct cec_notifier *notifier = NULL; + u32 caps = CEC_CAP_DEFAULTS; + int ret; + unsigned int val; + + ret = ch7322_get_port(client, &hdmi_dev, &port_name); + if (ret) + return ret; + + if (hdmi_dev) + caps |= CEC_CAP_CONNECTOR_INFO; + + ch7322 = devm_kzalloc(&client->dev, sizeof(*ch7322), GFP_KERNEL); + if (!ch7322) + return -ENOMEM; + + ch7322->regmap = devm_regmap_init_i2c(client, &ch7322_regmap); + if (IS_ERR(ch7322->regmap)) + return PTR_ERR(ch7322->regmap); + + ret = regmap_read(ch7322->regmap, CH7322_DID, &val); + if (ret) + return ret; + + if (val != CH7322_DID_CH7322) + return -EOPNOTSUPP; + + mutex_init(&ch7322->mutex); + ch7322->i2c = client; + ch7322->tx_flags = 0; + + i2c_set_clientdata(client, ch7322); + + /* Disable auto mode */ + ret = regmap_write(ch7322->regmap, CH7322_MODE, CH7322_MODE_SW); + if (ret) + goto err_mutex; + + /* Enable logical address register */ + ret = regmap_update_bits(ch7322->regmap, CH7322_CTL, + CH7322_CTL_SPADL, CH7322_CTL_SPADL); + if (ret) + goto err_mutex; + + ch7322->cec = cec_allocate_adapter(&ch7322_cec_adap_ops, ch7322, + dev_name(&client->dev), + caps, 1); + + if (IS_ERR(ch7322->cec)) { + ret = PTR_ERR(ch7322->cec); + goto err_mutex; + } + + ch7322->cec->adap_controls_phys_addr = true; + + if (hdmi_dev) { + notifier = cec_notifier_cec_adap_register(hdmi_dev, + port_name, + ch7322->cec); + if (!notifier) { + ret = -ENOMEM; + goto err_cec; + } + } + + /* Configure, mask, and clear interrupt */ + ret = regmap_write(ch7322->regmap, CH7322_CFG1, 0); + if (ret) + goto err_notifier; + ret = regmap_write(ch7322->regmap, CH7322_INTCTL, CH7322_INTCTL_INTPB); + if (ret) + goto err_notifier; + ret = regmap_write(ch7322->regmap, CH7322_INTDATA, 0xff); + if (ret) + goto err_notifier; + + /* If HPD is up read physical address */ + ret = regmap_read(ch7322->regmap, CH7322_ADDLR, &val); + if (ret) + goto err_notifier; + if (val & CH7322_ADDLR_HPD) + ch7322_phys_addr(ch7322); + + ret = devm_request_threaded_irq(&client->dev, client->irq, NULL, + ch7322_irq, + IRQF_ONESHOT | IRQF_TRIGGER_RISING, + client->name, ch7322); + if (ret) + goto err_notifier; + + /* Unmask interrupt */ + mutex_lock(&ch7322->mutex); + ret = regmap_write(ch7322->regmap, CH7322_INTCTL, 0xff); + mutex_unlock(&ch7322->mutex); + + if (ret) + goto err_notifier; + + ret = cec_register_adapter(ch7322->cec, &client->dev); + if (ret) + goto err_notifier; + + dev_info(&client->dev, "device registered\n"); + + return 0; + +err_notifier: + if (notifier) + cec_notifier_cec_adap_unregister(notifier, ch7322->cec); +err_cec: + cec_delete_adapter(ch7322->cec); +err_mutex: + mutex_destroy(&ch7322->mutex); + return ret; +} + +static int ch7322_remove(struct i2c_client *client) +{ + struct ch7322 *ch7322 = i2c_get_clientdata(client); + + /* Mask interrupt */ + mutex_lock(&ch7322->mutex); + regmap_write(ch7322->regmap, CH7322_INTCTL, CH7322_INTCTL_INTPB); + mutex_unlock(&ch7322->mutex); + + cec_unregister_adapter(ch7322->cec); + mutex_destroy(&ch7322->mutex); + + dev_info(&client->dev, "device unregistered\n"); + + return 0; +} + +static const struct of_device_id ch7322_of_match[] = { + { .compatible = "chrontel,ch7322", }, + {}, +}; +MODULE_DEVICE_TABLE(of, ch7322_of_match); + +static struct i2c_driver ch7322_i2c_driver = { + .driver = { + .name = "ch7322", + .of_match_table = of_match_ptr(ch7322_of_match), + }, + .probe_new = ch7322_probe, + .remove = ch7322_remove, +}; + +module_i2c_driver(ch7322_i2c_driver); + +MODULE_DESCRIPTION("Chrontel CH7322 CEC Controller Driver"); +MODULE_AUTHOR("Jeff Chase "); +MODULE_LICENSE("GPL"); -- GitLab From e1db811e5d94cff7fa51398e36b2b6186ab185a0 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 26 Jun 2020 13:53:15 +0200 Subject: [PATCH 0230/1476] media: ov9640: update MODULE_DESCRIPTION Remove the confusing SoC Camera reference. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/ov9640.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/i2c/ov9640.c b/drivers/media/i2c/ov9640.c index 4826096653056..3a21f51d93259 100644 --- a/drivers/media/i2c/ov9640.c +++ b/drivers/media/i2c/ov9640.c @@ -772,6 +772,6 @@ static struct i2c_driver ov9640_i2c_driver = { module_i2c_driver(ov9640_i2c_driver); -MODULE_DESCRIPTION("SoC Camera driver for OmniVision OV96xx"); +MODULE_DESCRIPTION("OmniVision OV96xx CMOS Image Sensor driver"); MODULE_AUTHOR("Marek Vasut "); MODULE_LICENSE("GPL v2"); -- GitLab From 5a55b647395df9c9afabae82550f9e051cc198d8 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 26 Jun 2020 13:53:16 +0200 Subject: [PATCH 0231/1476] media: pxa_camera: update MODULE_DESCRIPTION Remove the confusing SoC Camera reference. Signed-off-by: Hans Verkuil Cc: Robert Jarzmik Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/pxa_camera.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/platform/pxa_camera.c b/drivers/media/platform/pxa_camera.c index 3c5fe737d36f7..a7cf830235b61 100644 --- a/drivers/media/platform/pxa_camera.c +++ b/drivers/media/platform/pxa_camera.c @@ -2588,7 +2588,7 @@ static struct platform_driver pxa_camera_driver = { module_platform_driver(pxa_camera_driver); -MODULE_DESCRIPTION("PXA27x SoC Camera Host driver"); +MODULE_DESCRIPTION("PXA27x Camera Driver"); MODULE_AUTHOR("Guennadi Liakhovetski "); MODULE_LICENSE("GPL"); MODULE_VERSION(PXA_CAM_VERSION); -- GitLab From c694107ae998af3f2fcc89e72e736c22e30c4b6a Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 26 Jun 2020 13:53:17 +0200 Subject: [PATCH 0232/1476] media: mach-imx: mach-imx27_visstrim_m10.c: remove soc_camera dependencies The soc_camera.h header driver is about to be removed, so drop camera support from this board. Note that the soc_camera driver itself has long since been deprecated and can't be compiled anymore (it depends on BROKEN), so camera support on this board has been broken for a long time (at least since 4.6 when the mx2_camera.c was removed from soc_camera). Signed-off-by: Hans Verkuil Acked-by: Arnd Bergmann Signed-off-by: Mauro Carvalho Chehab --- arch/arm/mach-imx/mach-imx27_visstrim_m10.c | 31 --------------------- 1 file changed, 31 deletions(-) diff --git a/arch/arm/mach-imx/mach-imx27_visstrim_m10.c b/arch/arm/mach-imx/mach-imx27_visstrim_m10.c index c0350bc20941a..3da4c0920198b 100644 --- a/arch/arm/mach-imx/mach-imx27_visstrim_m10.c +++ b/arch/arm/mach-imx/mach-imx27_visstrim_m10.c @@ -19,7 +19,6 @@ #include #include #include -#include #include #include #include @@ -191,34 +190,6 @@ static const struct gpio visstrim_m10_gpios[] __initconst = { }; /* Camera */ -static int visstrim_camera_power(struct device *dev, int on) -{ - gpio_set_value(TVP5150_PWDN, on); - - return 0; -}; - -static int visstrim_camera_reset(struct device *dev) -{ - gpio_set_value(TVP5150_RSTN, 0); - ndelay(500); - gpio_set_value(TVP5150_RSTN, 1); - - return 0; -}; - -static struct i2c_board_info visstrim_i2c_camera = { - I2C_BOARD_INFO("tvp5150", 0x5d), -}; - -static struct soc_camera_link iclink_tvp5150 = { - .bus_id = 0, - .board_info = &visstrim_i2c_camera, - .i2c_adapter_id = 0, - .power = visstrim_camera_power, - .reset = visstrim_camera_reset, -}; - static struct mx2_camera_platform_data visstrim_camera = { .flags = MX2_CAMERA_CCIR | MX2_CAMERA_CCIR_INTERLACE | MX2_CAMERA_PCLK_SAMPLE_RISING, @@ -549,8 +520,6 @@ static void __init visstrim_m10_late_init(void) imx_add_platform_device("mx27vis", 0, NULL, 0, &snd_mx27vis_pdata, sizeof(snd_mx27vis_pdata)); - platform_device_register_resndata(NULL, "soc-camera-pdrv", 0, NULL, 0, - &iclink_tvp5150, sizeof(iclink_tvp5150)); gpio_led_register_device(0, &visstrim_m10_led_data); -- GitLab From ce548396a4334a4c6e9faada8db89dacf35822b2 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 26 Jun 2020 13:53:18 +0200 Subject: [PATCH 0233/1476] media: mach-omap1: board-ams-delta.c: remove soc_camera dependencies The soc_camera driver is about to be removed, so drop camera support from this board. Note that the soc_camera driver itself has long since been deprecated and can't be compiled anymore (it depends on BROKEN), so camera support on this board has been broken for a long time (at least since 4.6 when the omap1_camera.c was removed from soc_camera). Signed-off-by: Hans Verkuil Cc: Tony Lindgren Acked-by: Arnd Bergmann Signed-off-by: Mauro Carvalho Chehab --- arch/arm/mach-omap1/board-ams-delta.c | 32 -------------- arch/arm/mach-omap1/camera.h | 14 ------ arch/arm/mach-omap1/devices.c | 43 ------------------- .../linux/platform_data/media/omap1_camera.h | 32 -------------- 4 files changed, 121 deletions(-) delete mode 100644 arch/arm/mach-omap1/camera.h delete mode 100644 include/linux/platform_data/media/omap1_camera.h diff --git a/arch/arm/mach-omap1/board-ams-delta.c b/arch/arm/mach-omap1/board-ams-delta.c index 8d32894ecd2e3..2ee527c002840 100644 --- a/arch/arm/mach-omap1/board-ams-delta.c +++ b/arch/arm/mach-omap1/board-ams-delta.c @@ -29,8 +29,6 @@ #include #include -#include - #include #include #include @@ -40,7 +38,6 @@ #include #include -#include "camera.h" #include #include "ams-delta-fiq.h" @@ -459,12 +456,6 @@ static struct gpiod_lookup_table leds_gpio_table = { }, }; -static struct i2c_board_info ams_delta_camera_board_info[] = { - { - I2C_BOARD_INFO("ov6650", 0x60), - }, -}; - #ifdef CONFIG_LEDS_TRIGGERS DEFINE_LED_TRIGGER(ams_delta_camera_led_trigger); @@ -483,27 +474,6 @@ static int ams_delta_camera_power(struct device *dev, int power) #define ams_delta_camera_power NULL #endif -static struct soc_camera_link ams_delta_iclink = { - .bus_id = 0, /* OMAP1 SoC camera bus */ - .i2c_adapter_id = 1, - .board_info = &ams_delta_camera_board_info[0], - .module_name = "ov6650", - .power = ams_delta_camera_power, -}; - -static struct platform_device ams_delta_camera_device = { - .name = "soc-camera-pdrv", - .id = 0, - .dev = { - .platform_data = &ams_delta_iclink, - }, -}; - -static struct omap1_cam_platform_data ams_delta_camera_platform_data = { - .camexclk_khz = 12000, /* default 12MHz clock, no extra DPLL */ - .lclk_khz_max = 1334, /* results in 5fps CIF, 10fps QCIF */ -}; - static struct platform_device ams_delta_audio_device = { .name = "ams-delta-audio", .id = -1, @@ -598,7 +568,6 @@ static struct platform_device *ams_delta_devices[] __initdata = { &latch1_gpio_device, &latch2_gpio_device, &ams_delta_kp_device, - &ams_delta_camera_device, &ams_delta_audio_device, &ams_delta_serio_device, &ams_delta_nand_device, @@ -750,7 +719,6 @@ static void __init ams_delta_init(void) omap_register_i2c_bus(1, 100, NULL, 0); omap1_usb_init(&ams_delta_usb_config); - omap1_set_camera_info(&ams_delta_camera_platform_data); #ifdef CONFIG_LEDS_TRIGGERS led_trigger_register_simple("ams_delta_camera", &ams_delta_camera_led_trigger); diff --git a/arch/arm/mach-omap1/camera.h b/arch/arm/mach-omap1/camera.h deleted file mode 100644 index 8018f410d0242..0000000000000 --- a/arch/arm/mach-omap1/camera.h +++ /dev/null @@ -1,14 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -#ifndef __ASM_ARCH_CAMERA_H_ -#define __ASM_ARCH_CAMERA_H_ - -#include - -void omap1_camera_init(void *); - -static inline void omap1_set_camera_info(struct omap1_cam_platform_data *info) -{ - omap1_camera_init(info); -} - -#endif /* __ASM_ARCH_CAMERA_H_ */ diff --git a/arch/arm/mach-omap1/devices.c b/arch/arm/mach-omap1/devices.c index 3c4900ac72fc2..eb0f09edb3d14 100644 --- a/arch/arm/mach-omap1/devices.c +++ b/arch/arm/mach-omap1/devices.c @@ -21,7 +21,6 @@ #include #include -#include "camera.h" #include #include "common.h" @@ -258,48 +257,6 @@ static inline void omap_init_spi100k(void) } #endif - -#define OMAP1_CAMERA_BASE 0xfffb6800 -#define OMAP1_CAMERA_IOSIZE 0x1c - -static struct resource omap1_camera_resources[] = { - [0] = { - .start = OMAP1_CAMERA_BASE, - .end = OMAP1_CAMERA_BASE + OMAP1_CAMERA_IOSIZE - 1, - .flags = IORESOURCE_MEM, - }, - [1] = { - .start = INT_CAMERA, - .flags = IORESOURCE_IRQ, - }, -}; - -static u64 omap1_camera_dma_mask = DMA_BIT_MASK(32); - -static struct platform_device omap1_camera_device = { - .name = "omap1-camera", - .id = 0, /* This is used to put cameras on this interface */ - .dev = { - .dma_mask = &omap1_camera_dma_mask, - .coherent_dma_mask = DMA_BIT_MASK(32), - }, - .num_resources = ARRAY_SIZE(omap1_camera_resources), - .resource = omap1_camera_resources, -}; - -void __init omap1_camera_init(void *info) -{ - struct platform_device *dev = &omap1_camera_device; - int ret; - - dev->dev.platform_data = info; - - ret = platform_device_register(dev); - if (ret) - dev_err(&dev->dev, "unable to register device: %d\n", ret); -} - - /*-------------------------------------------------------------------------*/ static inline void omap_init_sti(void) {} diff --git a/include/linux/platform_data/media/omap1_camera.h b/include/linux/platform_data/media/omap1_camera.h deleted file mode 100644 index 386439db68ded..0000000000000 --- a/include/linux/platform_data/media/omap1_camera.h +++ /dev/null @@ -1,32 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* - * Header for V4L2 SoC Camera driver for OMAP1 Camera Interface - * - * Copyright (C) 2010, Janusz Krzysztofik - */ - -#ifndef __MEDIA_OMAP1_CAMERA_H_ -#define __MEDIA_OMAP1_CAMERA_H_ - -#include - -#define OMAP1_CAMERA_IOSIZE 0x1c - -enum omap1_cam_vb_mode { - OMAP1_CAM_DMA_CONTIG = 0, - OMAP1_CAM_DMA_SG, -}; - -#define OMAP1_CAMERA_MIN_BUF_COUNT(x) ((x) == OMAP1_CAM_DMA_CONTIG ? 3 : 2) - -struct omap1_cam_platform_data { - unsigned long camexclk_khz; - unsigned long lclk_khz_max; - unsigned long flags; -}; - -#define OMAP1_CAMERA_LCLK_RISING BIT(0) -#define OMAP1_CAMERA_RST_LOW BIT(1) -#define OMAP1_CAMERA_RST_HIGH BIT(2) - -#endif /* __MEDIA_OMAP1_CAMERA_H_ */ -- GitLab From 63839882c59740c68f16bca45b4f78a7cd5122dd Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 26 Jun 2020 13:53:19 +0200 Subject: [PATCH 0234/1476] media: mach-pxa: palmz72/pcm990: remove soc_camera dependencies The soc_camera driver is about to be removed, so drop camera support from this board. Note that the soc_camera driver itself has long since been deprecated and can't be compiled anymore (it depends on BROKEN), so camera support on this board has been broken for a long time (at least since 4.9 when the pxa_camera.c was removed from soc_camera). Note that there is a new pxa_camera.c driver that replaced the old soc_camera based driver, but using that would require these boards to be converted to use the device tree. Signed-off-by: Hans Verkuil Cc: Robert Jarzmik Acked-by: Arnd Bergmann Signed-off-by: Mauro Carvalho Chehab --- arch/arm/mach-pxa/palmz72.c | 112 ------------------- arch/arm/mach-pxa/pcm990-baseboard.c | 157 --------------------------- 2 files changed, 269 deletions(-) diff --git a/arch/arm/mach-pxa/palmz72.c b/arch/arm/mach-pxa/palmz72.c index 4df443943579c..b4a5fe02a0af1 100644 --- a/arch/arm/mach-pxa/palmz72.c +++ b/arch/arm/mach-pxa/palmz72.c @@ -47,8 +47,6 @@ #include "pm.h" #include -#include - #include "generic.h" #include "devices.h" @@ -272,115 +270,6 @@ static int __init palmz72_pm_init(void) device_initcall(palmz72_pm_init); #endif -/****************************************************************************** - * SoC Camera - ******************************************************************************/ -#if defined(CONFIG_SOC_CAMERA_OV9640) || \ - defined(CONFIG_SOC_CAMERA_OV9640_MODULE) -static struct pxacamera_platform_data palmz72_pxacamera_platform_data = { - .flags = PXA_CAMERA_MASTER | PXA_CAMERA_DATAWIDTH_8 | - PXA_CAMERA_PCLK_EN | PXA_CAMERA_MCLK_EN, - .mclk_10khz = 2600, -}; - -/* Board I2C devices. */ -static struct i2c_board_info palmz72_i2c_device[] = { - { - I2C_BOARD_INFO("ov9640", 0x30), - } -}; - -static int palmz72_camera_power(struct device *dev, int power) -{ - gpio_set_value(GPIO_NR_PALMZ72_CAM_PWDN, !power); - mdelay(50); - return 0; -} - -static int palmz72_camera_reset(struct device *dev) -{ - gpio_set_value(GPIO_NR_PALMZ72_CAM_RESET, 1); - mdelay(50); - gpio_set_value(GPIO_NR_PALMZ72_CAM_RESET, 0); - mdelay(50); - return 0; -} - -static struct soc_camera_link palmz72_iclink = { - .bus_id = 0, /* Match id in pxa27x_device_camera in device.c */ - .board_info = &palmz72_i2c_device[0], - .i2c_adapter_id = 0, - .module_name = "ov96xx", - .power = &palmz72_camera_power, - .reset = &palmz72_camera_reset, - .flags = SOCAM_DATAWIDTH_8, -}; - -static struct gpiod_lookup_table palmz72_i2c_gpiod_table = { - .dev_id = "i2c-gpio.0", - .table = { - GPIO_LOOKUP_IDX("gpio-pxa", 118, NULL, 0, - GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN), - GPIO_LOOKUP_IDX("gpio-pxa", 117, NULL, 1, - GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN), - }, -}; - -static struct i2c_gpio_platform_data palmz72_i2c_bus_data = { - .udelay = 10, - .timeout = 100, -}; - -static struct platform_device palmz72_i2c_bus_device = { - .name = "i2c-gpio", - .id = 0, /* we use this as a replacement for i2c-pxa */ - .dev = { - .platform_data = &palmz72_i2c_bus_data, - } -}; - -static struct platform_device palmz72_camera = { - .name = "soc-camera-pdrv", - .id = -1, - .dev = { - .platform_data = &palmz72_iclink, - }, -}; - -/* Here we request the camera GPIOs and configure them. We power up the camera - * module, deassert the reset pin, but put it into powerdown (low to no power - * consumption) mode. This allows us to later bring the module up fast. */ -static struct gpio palmz72_camera_gpios[] = { - { GPIO_NR_PALMZ72_CAM_POWER, GPIOF_INIT_HIGH,"Camera DVDD" }, - { GPIO_NR_PALMZ72_CAM_RESET, GPIOF_INIT_LOW, "Camera RESET" }, - { GPIO_NR_PALMZ72_CAM_PWDN, GPIOF_INIT_LOW, "Camera PWDN" }, -}; - -static inline void __init palmz72_cam_gpio_init(void) -{ - int ret; - - ret = gpio_request_array(ARRAY_AND_SIZE(palmz72_camera_gpios)); - if (!ret) - gpio_free_array(ARRAY_AND_SIZE(palmz72_camera_gpios)); - else - printk(KERN_ERR "Camera GPIO init failed!\n"); - - return; -} - -static void __init palmz72_camera_init(void) -{ - palmz72_cam_gpio_init(); - pxa_set_camera_info(&palmz72_pxacamera_platform_data); - gpiod_add_lookup_table(&palmz72_i2c_gpiod_table); - platform_device_register(&palmz72_i2c_bus_device); - platform_device_register(&palmz72_camera); -} -#else -static inline void palmz72_camera_init(void) {} -#endif - static struct gpiod_lookup_table palmz72_mci_gpio_table = { .dev_id = "pxa2xx-mci.0", .table = { @@ -416,7 +305,6 @@ static void __init palmz72_init(void) palm27x_pmic_init(); palmz72_kpc_init(); palmz72_leds_init(); - palmz72_camera_init(); } MACHINE_START(PALMZ72, "Palm Zire72") diff --git a/arch/arm/mach-pxa/pcm990-baseboard.c b/arch/arm/mach-pxa/pcm990-baseboard.c index bf613f88d70b1..8dfcc366d0fe9 100644 --- a/arch/arm/mach-pxa/pcm990-baseboard.c +++ b/arch/arm/mach-pxa/pcm990-baseboard.c @@ -24,10 +24,6 @@ #include #include -#include -#include - -#include #include #include "pxa27x.h" #include @@ -374,149 +370,6 @@ static struct pxaohci_platform_data pcm990_ohci_platform_data = { .power_on_delay = 10, }; -/* - * PXA27x Camera specific stuff - */ -#if defined(CONFIG_VIDEO_PXA27x) || defined(CONFIG_VIDEO_PXA27x_MODULE) -static unsigned long pcm990_camera_pin_config[] = { - /* CIF */ - GPIO98_CIF_DD_0, - GPIO105_CIF_DD_1, - GPIO104_CIF_DD_2, - GPIO103_CIF_DD_3, - GPIO95_CIF_DD_4, - GPIO94_CIF_DD_5, - GPIO93_CIF_DD_6, - GPIO108_CIF_DD_7, - GPIO107_CIF_DD_8, - GPIO106_CIF_DD_9, - GPIO42_CIF_MCLK, - GPIO45_CIF_PCLK, - GPIO43_CIF_FV, - GPIO44_CIF_LV, -}; - -/* - * CICR4: PCLK_EN: Pixel clock is supplied by the sensor - * MCLK_EN: Master clock is generated by PXA - * PCP: Data sampled on the falling edge of pixel clock - */ -struct pxacamera_platform_data pcm990_pxacamera_platform_data = { - .flags = PXA_CAMERA_MASTER | PXA_CAMERA_DATAWIDTH_8 | PXA_CAMERA_DATAWIDTH_10 | - PXA_CAMERA_PCLK_EN | PXA_CAMERA_MCLK_EN/* | PXA_CAMERA_PCP*/, - .mclk_10khz = 1000, -}; - -#include - -static struct pca953x_platform_data pca9536_data = { - .gpio_base = PXA_NR_BUILTIN_GPIO, -}; - -static int gpio_bus_switch = -EINVAL; - -static int pcm990_camera_set_bus_param(struct soc_camera_link *link, - unsigned long flags) -{ - if (gpio_bus_switch < 0) { - if (flags == SOCAM_DATAWIDTH_10) - return 0; - else - return -EINVAL; - } - - if (flags & SOCAM_DATAWIDTH_8) - gpio_set_value_cansleep(gpio_bus_switch, 1); - else - gpio_set_value_cansleep(gpio_bus_switch, 0); - - return 0; -} - -static unsigned long pcm990_camera_query_bus_param(struct soc_camera_link *link) -{ - int ret; - - if (gpio_bus_switch < 0) { - ret = gpio_request(PXA_NR_BUILTIN_GPIO, "camera"); - if (!ret) { - gpio_bus_switch = PXA_NR_BUILTIN_GPIO; - gpio_direction_output(gpio_bus_switch, 0); - } - } - - if (gpio_bus_switch >= 0) - return SOCAM_DATAWIDTH_8 | SOCAM_DATAWIDTH_10; - else - return SOCAM_DATAWIDTH_10; -} - -static void pcm990_camera_free_bus(struct soc_camera_link *link) -{ - if (gpio_bus_switch < 0) - return; - - gpio_free(gpio_bus_switch); - gpio_bus_switch = -EINVAL; -} - -/* Board I2C devices. */ -static struct i2c_board_info __initdata pcm990_i2c_devices[] = { - { - /* Must initialize before the camera(s) */ - I2C_BOARD_INFO("pca9536", 0x41), - .platform_data = &pca9536_data, - }, -}; - -static struct mt9v022_platform_data mt9v022_pdata = { - .y_skip_top = 1, -}; - -static struct i2c_board_info pcm990_camera_i2c[] = { - { - I2C_BOARD_INFO("mt9v022", 0x48), - }, { - I2C_BOARD_INFO("mt9m001", 0x5d), - }, -}; - -static struct soc_camera_link iclink[] = { - { - .bus_id = 0, /* Must match with the camera ID */ - .board_info = &pcm990_camera_i2c[0], - .priv = &mt9v022_pdata, - .i2c_adapter_id = 0, - .query_bus_param = pcm990_camera_query_bus_param, - .set_bus_param = pcm990_camera_set_bus_param, - .free_bus = pcm990_camera_free_bus, - }, { - .bus_id = 0, /* Must match with the camera ID */ - .board_info = &pcm990_camera_i2c[1], - .i2c_adapter_id = 0, - .query_bus_param = pcm990_camera_query_bus_param, - .set_bus_param = pcm990_camera_set_bus_param, - .free_bus = pcm990_camera_free_bus, - }, -}; - -static struct platform_device pcm990_camera[] = { - { - .name = "soc-camera-pdrv", - .id = 0, - .dev = { - .platform_data = &iclink[0], - }, - }, { - .name = "soc-camera-pdrv", - .id = 1, - .dev = { - .platform_data = &iclink[1], - }, - }, -}; -#endif /* CONFIG_VIDEO_PXA27x ||CONFIG_VIDEO_PXA27x_MODULE */ - /* * system init for baseboard usage. Will be called by pcm027 init. * @@ -551,15 +404,5 @@ void __init pcm990_baseboard_init(void) pxa_set_i2c_info(NULL); pxa_set_ac97_info(NULL); -#if defined(CONFIG_VIDEO_PXA27x) || defined(CONFIG_VIDEO_PXA27x_MODULE) - pxa2xx_mfp_config(ARRAY_AND_SIZE(pcm990_camera_pin_config)); - pxa_set_camera_info(&pcm990_pxacamera_platform_data); - - i2c_register_board_info(0, ARRAY_AND_SIZE(pcm990_i2c_devices)); - - platform_device_register(&pcm990_camera[0]); - platform_device_register(&pcm990_camera[1]); -#endif - printk(KERN_INFO "PCM-990 Evaluation baseboard initialized\n"); } -- GitLab From e7eab49132ba819632c3bb9cd5b8342f2cdeb939 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 26 Jun 2020 13:53:20 +0200 Subject: [PATCH 0235/1476] media: staging/media/soc_camera: remove this driver The soc_camera driver (and related soc_camera-dependent sensor drivers) is obsolete and depends on BROKEN for a long time now. Nobody is using it, so it is time to kill it off. Signed-off-by: Hans Verkuil Cc: Arnd Bergmann Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/Kconfig | 2 - drivers/staging/media/Makefile | 1 - drivers/staging/media/soc_camera/Kconfig | 51 - drivers/staging/media/soc_camera/Makefile | 7 - drivers/staging/media/soc_camera/TODO | 4 - drivers/staging/media/soc_camera/imx074.c | 492 ---- drivers/staging/media/soc_camera/mt9t031.c | 853 ------- .../staging/media/soc_camera/soc-camera.rst | 171 -- drivers/staging/media/soc_camera/soc_camera.c | 2164 ----------------- .../staging/media/soc_camera/soc_mediabus.c | 529 ---- .../staging/media/soc_camera/soc_mt9v022.c | 1008 -------- drivers/staging/media/soc_camera/soc_ov5642.c | 1085 --------- drivers/staging/media/soc_camera/soc_ov9740.c | 992 -------- include/media/drv-intf/soc_mediabus.h | 107 - 14 files changed, 7466 deletions(-) delete mode 100644 drivers/staging/media/soc_camera/Kconfig delete mode 100644 drivers/staging/media/soc_camera/Makefile delete mode 100644 drivers/staging/media/soc_camera/TODO delete mode 100644 drivers/staging/media/soc_camera/imx074.c delete mode 100644 drivers/staging/media/soc_camera/mt9t031.c delete mode 100644 drivers/staging/media/soc_camera/soc-camera.rst delete mode 100644 drivers/staging/media/soc_camera/soc_camera.c delete mode 100644 drivers/staging/media/soc_camera/soc_mediabus.c delete mode 100644 drivers/staging/media/soc_camera/soc_mt9v022.c delete mode 100644 drivers/staging/media/soc_camera/soc_ov5642.c delete mode 100644 drivers/staging/media/soc_camera/soc_ov9740.c delete mode 100644 include/media/drv-intf/soc_mediabus.h diff --git a/drivers/staging/media/Kconfig b/drivers/staging/media/Kconfig index 4bb1eca6f5976..71d077762698c 100644 --- a/drivers/staging/media/Kconfig +++ b/drivers/staging/media/Kconfig @@ -42,8 +42,6 @@ source "drivers/staging/media/tegra-video/Kconfig" source "drivers/staging/media/ipu3/Kconfig" -source "drivers/staging/media/soc_camera/Kconfig" - source "drivers/staging/media/phy-rockchip-dphy-rx0/Kconfig" source "drivers/staging/media/rkisp1/Kconfig" diff --git a/drivers/staging/media/Makefile b/drivers/staging/media/Makefile index 71a47b61836d1..17ececa1e095f 100644 --- a/drivers/staging/media/Makefile +++ b/drivers/staging/media/Makefile @@ -10,7 +10,6 @@ obj-$(CONFIG_VIDEO_TEGRA) += tegra-video/ obj-$(CONFIG_TEGRA_VDE) += tegra-vde/ obj-$(CONFIG_VIDEO_HANTRO) += hantro/ obj-$(CONFIG_VIDEO_IPU3_IMGU) += ipu3/ -obj-$(CONFIG_SOC_CAMERA) += soc_camera/ obj-$(CONFIG_PHY_ROCKCHIP_DPHY_RX0) += phy-rockchip-dphy-rx0/ obj-$(CONFIG_VIDEO_ROCKCHIP_ISP1) += rkisp1/ obj-$(CONFIG_VIDEO_USBVISION) += usbvision/ diff --git a/drivers/staging/media/soc_camera/Kconfig b/drivers/staging/media/soc_camera/Kconfig deleted file mode 100644 index 4a54db1215740..0000000000000 --- a/drivers/staging/media/soc_camera/Kconfig +++ /dev/null @@ -1,51 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0 -config SOC_CAMERA - tristate "SoC camera support" - depends on VIDEO_V4L2 && HAS_DMA && I2C && BROKEN - select VIDEOBUF2_CORE - help - SoC Camera is a common API to several cameras, not connecting - over a bus like PCI or USB. For example some i2c camera connected - directly to the data bus of an SoC. - -comment "soc_camera sensor drivers" - -config SOC_CAMERA_MT9M111 - tristate "legacy soc_camera mt9m111, mt9m112 and mt9m131 support" - depends on SOC_CAMERA && I2C - select VIDEO_MT9M111 - help - This driver supports MT9M111, MT9M112 and MT9M131 cameras from - Micron/Aptina. - This is the legacy configuration which shouldn't be used anymore, - while VIDEO_MT9M111 should be used instead. - -config SOC_CAMERA_MT9V022 - tristate "mt9v022 and mt9v024 support" - depends on SOC_CAMERA && I2C - help - This driver supports MT9V022 cameras from Micron - -config SOC_CAMERA_OV5642 - tristate "ov5642 camera support" - depends on SOC_CAMERA && I2C - help - This is a V4L2 camera driver for the OmniVision OV5642 sensor - -config SOC_CAMERA_OV9740 - tristate "ov9740 camera support" - depends on SOC_CAMERA && I2C - help - This is a ov9740 camera driver - -config SOC_CAMERA_IMX074 - tristate "imx074 support (DEPRECATED)" - depends on SOC_CAMERA && I2C - help - This driver supports IMX074 cameras from Sony - -config SOC_CAMERA_MT9T031 - tristate "mt9t031 support (DEPRECATED)" - depends on SOC_CAMERA && I2C - help - This driver supports MT9T031 cameras from Micron. diff --git a/drivers/staging/media/soc_camera/Makefile b/drivers/staging/media/soc_camera/Makefile deleted file mode 100644 index 3a351bd629f59..0000000000000 --- a/drivers/staging/media/soc_camera/Makefile +++ /dev/null @@ -1,7 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0 -obj-$(CONFIG_SOC_CAMERA) += soc_camera.o soc_mediabus.o -obj-$(CONFIG_SOC_CAMERA_MT9V022) += soc_mt9v022.o -obj-$(CONFIG_SOC_CAMERA_OV5642) += soc_ov5642.o -obj-$(CONFIG_SOC_CAMERA_OV9740) += soc_ov9740.o -obj-$(CONFIG_SOC_CAMERA_IMX074) += imx074.o -obj-$(CONFIG_SOC_CAMERA_MT9T031) += mt9t031.o diff --git a/drivers/staging/media/soc_camera/TODO b/drivers/staging/media/soc_camera/TODO deleted file mode 100644 index 932af6443b671..0000000000000 --- a/drivers/staging/media/soc_camera/TODO +++ /dev/null @@ -1,4 +0,0 @@ -The SoC camera framework is obsolete and scheduled for removal in the near -future. Developers are encouraged to convert the drivers to use the -regular V4L2 API if these drivers are still needed (and if someone has the -hardware). diff --git a/drivers/staging/media/soc_camera/imx074.c b/drivers/staging/media/soc_camera/imx074.c deleted file mode 100644 index 14240b74cdd06..0000000000000 --- a/drivers/staging/media/soc_camera/imx074.c +++ /dev/null @@ -1,492 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Driver for IMX074 CMOS Image Sensor from Sony - * - * Copyright (C) 2010, Guennadi Liakhovetski - * - * Partially inspired by the IMX074 driver from the Android / MSM tree - */ -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -/* IMX074 registers */ - -#define MODE_SELECT 0x0100 -#define IMAGE_ORIENTATION 0x0101 -#define GROUPED_PARAMETER_HOLD 0x0104 - -/* Integration Time */ -#define COARSE_INTEGRATION_TIME_HI 0x0202 -#define COARSE_INTEGRATION_TIME_LO 0x0203 -/* Gain */ -#define ANALOGUE_GAIN_CODE_GLOBAL_HI 0x0204 -#define ANALOGUE_GAIN_CODE_GLOBAL_LO 0x0205 - -/* PLL registers */ -#define PRE_PLL_CLK_DIV 0x0305 -#define PLL_MULTIPLIER 0x0307 -#define PLSTATIM 0x302b -#define VNDMY_ABLMGSHLMT 0x300a -#define Y_OPBADDR_START_DI 0x3014 -/* mode setting */ -#define FRAME_LENGTH_LINES_HI 0x0340 -#define FRAME_LENGTH_LINES_LO 0x0341 -#define LINE_LENGTH_PCK_HI 0x0342 -#define LINE_LENGTH_PCK_LO 0x0343 -#define YADDR_START 0x0347 -#define YADDR_END 0x034b -#define X_OUTPUT_SIZE_MSB 0x034c -#define X_OUTPUT_SIZE_LSB 0x034d -#define Y_OUTPUT_SIZE_MSB 0x034e -#define Y_OUTPUT_SIZE_LSB 0x034f -#define X_EVEN_INC 0x0381 -#define X_ODD_INC 0x0383 -#define Y_EVEN_INC 0x0385 -#define Y_ODD_INC 0x0387 - -#define HMODEADD 0x3001 -#define VMODEADD 0x3016 -#define VAPPLINE_START 0x3069 -#define VAPPLINE_END 0x306b -#define SHUTTER 0x3086 -#define HADDAVE 0x30e8 -#define LANESEL 0x3301 - -/* IMX074 supported geometry */ -#define IMX074_WIDTH 1052 -#define IMX074_HEIGHT 780 - -/* IMX074 has only one fixed colorspace per pixelcode */ -struct imx074_datafmt { - u32 code; - enum v4l2_colorspace colorspace; -}; - -struct imx074 { - struct v4l2_subdev subdev; - const struct imx074_datafmt *fmt; - struct v4l2_clk *clk; -}; - -static const struct imx074_datafmt imx074_colour_fmts[] = { - {MEDIA_BUS_FMT_SBGGR8_1X8, V4L2_COLORSPACE_SRGB}, -}; - -static struct imx074 *to_imx074(const struct i2c_client *client) -{ - return container_of(i2c_get_clientdata(client), struct imx074, subdev); -} - -/* Find a data format by a pixel code in an array */ -static const struct imx074_datafmt *imx074_find_datafmt(u32 code) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(imx074_colour_fmts); i++) - if (imx074_colour_fmts[i].code == code) - return imx074_colour_fmts + i; - - return NULL; -} - -static int reg_write(struct i2c_client *client, const u16 addr, const u8 data) -{ - struct i2c_adapter *adap = client->adapter; - struct i2c_msg msg; - unsigned char tx[3]; - int ret; - - msg.addr = client->addr; - msg.buf = tx; - msg.len = 3; - msg.flags = 0; - - tx[0] = addr >> 8; - tx[1] = addr & 0xff; - tx[2] = data; - - ret = i2c_transfer(adap, &msg, 1); - - mdelay(2); - - return ret == 1 ? 0 : -EIO; -} - -static int reg_read(struct i2c_client *client, const u16 addr) -{ - u8 buf[2] = {addr >> 8, addr & 0xff}; - int ret; - struct i2c_msg msgs[] = { - { - .addr = client->addr, - .flags = 0, - .len = 2, - .buf = buf, - }, { - .addr = client->addr, - .flags = I2C_M_RD, - .len = 2, - .buf = buf, - }, - }; - - ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); - if (ret < 0) { - dev_warn(&client->dev, "Reading register %x from %x failed\n", - addr, client->addr); - return ret; - } - - return buf[0] & 0xff; /* no sign-extension */ -} - -static int imx074_set_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, - struct v4l2_subdev_format *format) -{ - struct v4l2_mbus_framefmt *mf = &format->format; - const struct imx074_datafmt *fmt = imx074_find_datafmt(mf->code); - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct imx074 *priv = to_imx074(client); - - if (format->pad) - return -EINVAL; - - dev_dbg(sd->v4l2_dev->dev, "%s(%u)\n", __func__, mf->code); - - if (!fmt) { - /* MIPI CSI could have changed the format, double-check */ - if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE) - return -EINVAL; - mf->code = imx074_colour_fmts[0].code; - mf->colorspace = imx074_colour_fmts[0].colorspace; - } - - mf->width = IMX074_WIDTH; - mf->height = IMX074_HEIGHT; - mf->field = V4L2_FIELD_NONE; - - if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE) - priv->fmt = fmt; - else - cfg->try_fmt = *mf; - - return 0; -} - -static int imx074_get_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, - struct v4l2_subdev_format *format) -{ - struct v4l2_mbus_framefmt *mf = &format->format; - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct imx074 *priv = to_imx074(client); - - const struct imx074_datafmt *fmt = priv->fmt; - - if (format->pad) - return -EINVAL; - - mf->code = fmt->code; - mf->colorspace = fmt->colorspace; - mf->width = IMX074_WIDTH; - mf->height = IMX074_HEIGHT; - mf->field = V4L2_FIELD_NONE; - - return 0; -} - -static int imx074_get_selection(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, - struct v4l2_subdev_selection *sel) -{ - if (sel->which != V4L2_SUBDEV_FORMAT_ACTIVE) - return -EINVAL; - - sel->r.left = 0; - sel->r.top = 0; - sel->r.width = IMX074_WIDTH; - sel->r.height = IMX074_HEIGHT; - - switch (sel->target) { - case V4L2_SEL_TGT_CROP_BOUNDS: - case V4L2_SEL_TGT_CROP: - return 0; - default: - return -EINVAL; - } -} - -static int imx074_enum_mbus_code(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, - struct v4l2_subdev_mbus_code_enum *code) -{ - if (code->pad || - (unsigned int)code->index >= ARRAY_SIZE(imx074_colour_fmts)) - return -EINVAL; - - code->code = imx074_colour_fmts[code->index].code; - return 0; -} - -static int imx074_s_stream(struct v4l2_subdev *sd, int enable) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - - /* MODE_SELECT: stream or standby */ - return reg_write(client, MODE_SELECT, !!enable); -} - -static int imx074_s_power(struct v4l2_subdev *sd, int on) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); - struct imx074 *priv = to_imx074(client); - - return soc_camera_set_power(&client->dev, ssdd, priv->clk, on); -} - -static int imx074_g_mbus_config(struct v4l2_subdev *sd, - struct v4l2_mbus_config *cfg) -{ - cfg->type = V4L2_MBUS_CSI2_DPHY; - cfg->flags = V4L2_MBUS_CSI2_2_LANE | - V4L2_MBUS_CSI2_CHANNEL_0 | - V4L2_MBUS_CSI2_CONTINUOUS_CLOCK; - - return 0; -} - -static const struct v4l2_subdev_video_ops imx074_subdev_video_ops = { - .s_stream = imx074_s_stream, - .g_mbus_config = imx074_g_mbus_config, -}; - -static const struct v4l2_subdev_core_ops imx074_subdev_core_ops = { - .s_power = imx074_s_power, -}; - -static const struct v4l2_subdev_pad_ops imx074_subdev_pad_ops = { - .enum_mbus_code = imx074_enum_mbus_code, - .get_selection = imx074_get_selection, - .get_fmt = imx074_get_fmt, - .set_fmt = imx074_set_fmt, -}; - -static const struct v4l2_subdev_ops imx074_subdev_ops = { - .core = &imx074_subdev_core_ops, - .video = &imx074_subdev_video_ops, - .pad = &imx074_subdev_pad_ops, -}; - -static int imx074_video_probe(struct i2c_client *client) -{ - struct v4l2_subdev *subdev = i2c_get_clientdata(client); - int ret; - u16 id; - - ret = imx074_s_power(subdev, 1); - if (ret < 0) - return ret; - - /* Read sensor Model ID */ - ret = reg_read(client, 0); - if (ret < 0) - goto done; - - id = ret << 8; - - ret = reg_read(client, 1); - if (ret < 0) - goto done; - - id |= ret; - - dev_info(&client->dev, "Chip ID 0x%04x detected\n", id); - - if (id != 0x74) { - ret = -ENODEV; - goto done; - } - - /* PLL Setting EXTCLK=24MHz, 22.5times */ - reg_write(client, PLL_MULTIPLIER, 0x2D); - reg_write(client, PRE_PLL_CLK_DIV, 0x02); - reg_write(client, PLSTATIM, 0x4B); - - /* 2-lane mode */ - reg_write(client, 0x3024, 0x00); - - reg_write(client, IMAGE_ORIENTATION, 0x00); - - /* select RAW mode: - * 0x08+0x08 = top 8 bits - * 0x0a+0x08 = compressed 8-bits - * 0x0a+0x0a = 10 bits - */ - reg_write(client, 0x0112, 0x08); - reg_write(client, 0x0113, 0x08); - - /* Base setting for High frame mode */ - reg_write(client, VNDMY_ABLMGSHLMT, 0x80); - reg_write(client, Y_OPBADDR_START_DI, 0x08); - reg_write(client, 0x3015, 0x37); - reg_write(client, 0x301C, 0x01); - reg_write(client, 0x302C, 0x05); - reg_write(client, 0x3031, 0x26); - reg_write(client, 0x3041, 0x60); - reg_write(client, 0x3051, 0x24); - reg_write(client, 0x3053, 0x34); - reg_write(client, 0x3057, 0xC0); - reg_write(client, 0x305C, 0x09); - reg_write(client, 0x305D, 0x07); - reg_write(client, 0x3060, 0x30); - reg_write(client, 0x3065, 0x00); - reg_write(client, 0x30AA, 0x08); - reg_write(client, 0x30AB, 0x1C); - reg_write(client, 0x30B0, 0x32); - reg_write(client, 0x30B2, 0x83); - reg_write(client, 0x30D3, 0x04); - reg_write(client, 0x3106, 0x78); - reg_write(client, 0x310C, 0x82); - reg_write(client, 0x3304, 0x05); - reg_write(client, 0x3305, 0x04); - reg_write(client, 0x3306, 0x11); - reg_write(client, 0x3307, 0x02); - reg_write(client, 0x3308, 0x0C); - reg_write(client, 0x3309, 0x06); - reg_write(client, 0x330A, 0x08); - reg_write(client, 0x330B, 0x04); - reg_write(client, 0x330C, 0x08); - reg_write(client, 0x330D, 0x06); - reg_write(client, 0x330E, 0x01); - reg_write(client, 0x3381, 0x00); - - /* V : 1/2V-addition (1,3), H : 1/2H-averaging (1,3) -> Full HD */ - /* 1608 = 1560 + 48 (black lines) */ - reg_write(client, FRAME_LENGTH_LINES_HI, 0x06); - reg_write(client, FRAME_LENGTH_LINES_LO, 0x48); - reg_write(client, YADDR_START, 0x00); - reg_write(client, YADDR_END, 0x2F); - /* 0x838 == 2104 */ - reg_write(client, X_OUTPUT_SIZE_MSB, 0x08); - reg_write(client, X_OUTPUT_SIZE_LSB, 0x38); - /* 0x618 == 1560 */ - reg_write(client, Y_OUTPUT_SIZE_MSB, 0x06); - reg_write(client, Y_OUTPUT_SIZE_LSB, 0x18); - reg_write(client, X_EVEN_INC, 0x01); - reg_write(client, X_ODD_INC, 0x03); - reg_write(client, Y_EVEN_INC, 0x01); - reg_write(client, Y_ODD_INC, 0x03); - reg_write(client, HMODEADD, 0x00); - reg_write(client, VMODEADD, 0x16); - reg_write(client, VAPPLINE_START, 0x24); - reg_write(client, VAPPLINE_END, 0x53); - reg_write(client, SHUTTER, 0x00); - reg_write(client, HADDAVE, 0x80); - - reg_write(client, LANESEL, 0x00); - - reg_write(client, GROUPED_PARAMETER_HOLD, 0x00); /* off */ - - ret = 0; - -done: - imx074_s_power(subdev, 0); - return ret; -} - -static int imx074_probe(struct i2c_client *client, - const struct i2c_device_id *did) -{ - struct imx074 *priv; - struct i2c_adapter *adapter = client->adapter; - struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); - int ret; - - if (!ssdd) { - dev_err(&client->dev, "IMX074: missing platform data!\n"); - return -EINVAL; - } - - if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) { - dev_warn(&adapter->dev, - "I2C-Adapter doesn't support I2C_FUNC_SMBUS_BYTE\n"); - return -EIO; - } - - priv = devm_kzalloc(&client->dev, sizeof(struct imx074), GFP_KERNEL); - if (!priv) - return -ENOMEM; - - v4l2_i2c_subdev_init(&priv->subdev, client, &imx074_subdev_ops); - - priv->fmt = &imx074_colour_fmts[0]; - - priv->clk = v4l2_clk_get(&client->dev, "mclk"); - if (IS_ERR(priv->clk)) { - dev_info(&client->dev, "Error %ld getting clock\n", PTR_ERR(priv->clk)); - return -EPROBE_DEFER; - } - - ret = soc_camera_power_init(&client->dev, ssdd); - if (ret < 0) - goto epwrinit; - - ret = imx074_video_probe(client); - if (ret < 0) - goto eprobe; - - ret = v4l2_async_register_subdev(&priv->subdev); - if (!ret) - return 0; - -epwrinit: -eprobe: - v4l2_clk_put(priv->clk); - return ret; -} - -static int imx074_remove(struct i2c_client *client) -{ - struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); - struct imx074 *priv = to_imx074(client); - - v4l2_async_unregister_subdev(&priv->subdev); - v4l2_clk_put(priv->clk); - - if (ssdd->free_bus) - ssdd->free_bus(ssdd); - - return 0; -} - -static const struct i2c_device_id imx074_id[] = { - { "imx074", 0 }, - { } -}; -MODULE_DEVICE_TABLE(i2c, imx074_id); - -static struct i2c_driver imx074_i2c_driver = { - .driver = { - .name = "imx074", - }, - .probe = imx074_probe, - .remove = imx074_remove, - .id_table = imx074_id, -}; - -module_i2c_driver(imx074_i2c_driver); - -MODULE_DESCRIPTION("Sony IMX074 Camera driver"); -MODULE_AUTHOR("Guennadi Liakhovetski "); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/staging/media/soc_camera/mt9t031.c b/drivers/staging/media/soc_camera/mt9t031.c deleted file mode 100644 index c14f232215443..0000000000000 --- a/drivers/staging/media/soc_camera/mt9t031.c +++ /dev/null @@ -1,853 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Driver for MT9T031 CMOS Image Sensor from Micron - * - * Copyright (C) 2008, Guennadi Liakhovetski, DENX Software Engineering - */ -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -/* - * ATTENTION: this driver still cannot be used outside of the soc-camera - * framework because of its PM implementation, using the video_device node. - * If hardware becomes available for testing, alternative PM approaches shall - * be considered and tested. - */ - -/* - * mt9t031 i2c address 0x5d - * The platform has to define struct i2c_board_info objects and link to them - * from struct soc_camera_host_desc - */ - -/* mt9t031 selected register addresses */ -#define MT9T031_CHIP_VERSION 0x00 -#define MT9T031_ROW_START 0x01 -#define MT9T031_COLUMN_START 0x02 -#define MT9T031_WINDOW_HEIGHT 0x03 -#define MT9T031_WINDOW_WIDTH 0x04 -#define MT9T031_HORIZONTAL_BLANKING 0x05 -#define MT9T031_VERTICAL_BLANKING 0x06 -#define MT9T031_OUTPUT_CONTROL 0x07 -#define MT9T031_SHUTTER_WIDTH_UPPER 0x08 -#define MT9T031_SHUTTER_WIDTH 0x09 -#define MT9T031_PIXEL_CLOCK_CONTROL 0x0a -#define MT9T031_FRAME_RESTART 0x0b -#define MT9T031_SHUTTER_DELAY 0x0c -#define MT9T031_RESET 0x0d -#define MT9T031_READ_MODE_1 0x1e -#define MT9T031_READ_MODE_2 0x20 -#define MT9T031_READ_MODE_3 0x21 -#define MT9T031_ROW_ADDRESS_MODE 0x22 -#define MT9T031_COLUMN_ADDRESS_MODE 0x23 -#define MT9T031_GLOBAL_GAIN 0x35 -#define MT9T031_CHIP_ENABLE 0xF8 - -#define MT9T031_MAX_HEIGHT 1536 -#define MT9T031_MAX_WIDTH 2048 -#define MT9T031_MIN_HEIGHT 2 -#define MT9T031_MIN_WIDTH 18 -#define MT9T031_HORIZONTAL_BLANK 142 -#define MT9T031_VERTICAL_BLANK 25 -#define MT9T031_COLUMN_SKIP 32 -#define MT9T031_ROW_SKIP 20 - -struct mt9t031 { - struct v4l2_subdev subdev; - struct v4l2_ctrl_handler hdl; - struct { - /* exposure/auto-exposure cluster */ - struct v4l2_ctrl *autoexposure; - struct v4l2_ctrl *exposure; - }; - struct v4l2_rect rect; /* Sensor window */ - struct v4l2_clk *clk; - u16 xskip; - u16 yskip; - unsigned int total_h; - unsigned short y_skip_top; /* Lines to skip at the top */ -}; - -static struct mt9t031 *to_mt9t031(const struct i2c_client *client) -{ - return container_of(i2c_get_clientdata(client), struct mt9t031, subdev); -} - -static int reg_read(struct i2c_client *client, const u8 reg) -{ - return i2c_smbus_read_word_swapped(client, reg); -} - -static int reg_write(struct i2c_client *client, const u8 reg, - const u16 data) -{ - return i2c_smbus_write_word_swapped(client, reg, data); -} - -static int reg_set(struct i2c_client *client, const u8 reg, - const u16 data) -{ - int ret; - - ret = reg_read(client, reg); - if (ret < 0) - return ret; - return reg_write(client, reg, ret | data); -} - -static int reg_clear(struct i2c_client *client, const u8 reg, - const u16 data) -{ - int ret; - - ret = reg_read(client, reg); - if (ret < 0) - return ret; - return reg_write(client, reg, ret & ~data); -} - -static int set_shutter(struct i2c_client *client, const u32 data) -{ - int ret; - - ret = reg_write(client, MT9T031_SHUTTER_WIDTH_UPPER, data >> 16); - - if (ret >= 0) - ret = reg_write(client, MT9T031_SHUTTER_WIDTH, data & 0xffff); - - return ret; -} - -static int get_shutter(struct i2c_client *client, u32 *data) -{ - int ret; - - ret = reg_read(client, MT9T031_SHUTTER_WIDTH_UPPER); - *data = ret << 16; - - if (ret >= 0) - ret = reg_read(client, MT9T031_SHUTTER_WIDTH); - *data |= ret & 0xffff; - - return ret < 0 ? ret : 0; -} - -static int mt9t031_idle(struct i2c_client *client) -{ - int ret; - - /* Disable chip output, synchronous option update */ - ret = reg_write(client, MT9T031_RESET, 1); - if (ret >= 0) - ret = reg_write(client, MT9T031_RESET, 0); - if (ret >= 0) - ret = reg_clear(client, MT9T031_OUTPUT_CONTROL, 2); - - return ret >= 0 ? 0 : -EIO; -} - -static int mt9t031_s_stream(struct v4l2_subdev *sd, int enable) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - int ret; - - if (enable) - /* Switch to master "normal" mode */ - ret = reg_set(client, MT9T031_OUTPUT_CONTROL, 2); - else - /* Stop sensor readout */ - ret = reg_clear(client, MT9T031_OUTPUT_CONTROL, 2); - - if (ret < 0) - return -EIO; - - return 0; -} - -/* target must be _even_ */ -static u16 mt9t031_skip(s32 *source, s32 target, s32 max) -{ - unsigned int skip; - - if (*source < target + target / 2) { - *source = target; - return 1; - } - - skip = min(max, *source + target / 2) / target; - if (skip > 8) - skip = 8; - *source = target * skip; - - return skip; -} - -/* rect is the sensor rectangle, the caller guarantees parameter validity */ -static int mt9t031_set_params(struct i2c_client *client, - struct v4l2_rect *rect, u16 xskip, u16 yskip) -{ - struct mt9t031 *mt9t031 = to_mt9t031(client); - int ret; - u16 xbin, ybin; - const u16 hblank = MT9T031_HORIZONTAL_BLANK, - vblank = MT9T031_VERTICAL_BLANK; - - xbin = min(xskip, (u16)3); - ybin = min(yskip, (u16)3); - - /* - * Could just do roundup(rect->left, [xy]bin * 2); but this is cheaper. - * There is always a valid suitably aligned value. The worst case is - * xbin = 3, width = 2048. Then we will start at 36, the last read out - * pixel will be 2083, which is < 2085 - first black pixel. - * - * MT9T031 datasheet imposes window left border alignment, depending on - * the selected xskip. Failing to conform to this requirement produces - * dark horizontal stripes in the image. However, even obeying to this - * requirement doesn't eliminate the stripes in all configurations. They - * appear "locally reproducibly," but can differ between tests under - * different lighting conditions. - */ - switch (xbin) { - case 1: - rect->left &= ~1; - break; - case 2: - rect->left &= ~3; - break; - case 3: - rect->left = rect->left > roundup(MT9T031_COLUMN_SKIP, 6) ? - (rect->left / 6) * 6 : roundup(MT9T031_COLUMN_SKIP, 6); - } - - rect->top &= ~1; - - dev_dbg(&client->dev, "skip %u:%u, rect %ux%u@%u:%u\n", - xskip, yskip, rect->width, rect->height, rect->left, rect->top); - - /* Disable register update, reconfigure atomically */ - ret = reg_set(client, MT9T031_OUTPUT_CONTROL, 1); - if (ret < 0) - return ret; - - /* Blanking and start values - default... */ - ret = reg_write(client, MT9T031_HORIZONTAL_BLANKING, hblank); - if (ret >= 0) - ret = reg_write(client, MT9T031_VERTICAL_BLANKING, vblank); - - if (yskip != mt9t031->yskip || xskip != mt9t031->xskip) { - /* Binning, skipping */ - if (ret >= 0) - ret = reg_write(client, MT9T031_COLUMN_ADDRESS_MODE, - ((xbin - 1) << 4) | (xskip - 1)); - if (ret >= 0) - ret = reg_write(client, MT9T031_ROW_ADDRESS_MODE, - ((ybin - 1) << 4) | (yskip - 1)); - } - dev_dbg(&client->dev, "new physical left %u, top %u\n", - rect->left, rect->top); - - /* - * The caller provides a supported format, as guaranteed by - * .set_fmt(FORMAT_TRY), soc_camera_s_selection() and soc_camera_cropcap() - */ - if (ret >= 0) - ret = reg_write(client, MT9T031_COLUMN_START, rect->left); - if (ret >= 0) - ret = reg_write(client, MT9T031_ROW_START, rect->top); - if (ret >= 0) - ret = reg_write(client, MT9T031_WINDOW_WIDTH, rect->width - 1); - if (ret >= 0) - ret = reg_write(client, MT9T031_WINDOW_HEIGHT, - rect->height + mt9t031->y_skip_top - 1); - if (ret >= 0 && v4l2_ctrl_g_ctrl(mt9t031->autoexposure) == V4L2_EXPOSURE_AUTO) { - mt9t031->total_h = rect->height + mt9t031->y_skip_top + vblank; - - ret = set_shutter(client, mt9t031->total_h); - } - - /* Re-enable register update, commit all changes */ - if (ret >= 0) - ret = reg_clear(client, MT9T031_OUTPUT_CONTROL, 1); - - if (ret >= 0) { - mt9t031->rect = *rect; - mt9t031->xskip = xskip; - mt9t031->yskip = yskip; - } - - return ret < 0 ? ret : 0; -} - -static int mt9t031_set_selection(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, - struct v4l2_subdev_selection *sel) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct mt9t031 *mt9t031 = to_mt9t031(client); - struct v4l2_rect rect = sel->r; - - if (sel->which != V4L2_SUBDEV_FORMAT_ACTIVE || - sel->target != V4L2_SEL_TGT_CROP) - return -EINVAL; - - rect.width = ALIGN(rect.width, 2); - rect.height = ALIGN(rect.height, 2); - - soc_camera_limit_side(&rect.left, &rect.width, - MT9T031_COLUMN_SKIP, MT9T031_MIN_WIDTH, MT9T031_MAX_WIDTH); - - soc_camera_limit_side(&rect.top, &rect.height, - MT9T031_ROW_SKIP, MT9T031_MIN_HEIGHT, MT9T031_MAX_HEIGHT); - - return mt9t031_set_params(client, &rect, mt9t031->xskip, mt9t031->yskip); -} - -static int mt9t031_get_selection(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, - struct v4l2_subdev_selection *sel) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct mt9t031 *mt9t031 = to_mt9t031(client); - - if (sel->which != V4L2_SUBDEV_FORMAT_ACTIVE) - return -EINVAL; - - switch (sel->target) { - case V4L2_SEL_TGT_CROP_BOUNDS: - sel->r.left = MT9T031_COLUMN_SKIP; - sel->r.top = MT9T031_ROW_SKIP; - sel->r.width = MT9T031_MAX_WIDTH; - sel->r.height = MT9T031_MAX_HEIGHT; - return 0; - case V4L2_SEL_TGT_CROP: - sel->r = mt9t031->rect; - return 0; - default: - return -EINVAL; - } -} - -static int mt9t031_get_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, - struct v4l2_subdev_format *format) -{ - struct v4l2_mbus_framefmt *mf = &format->format; - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct mt9t031 *mt9t031 = to_mt9t031(client); - - if (format->pad) - return -EINVAL; - - mf->width = mt9t031->rect.width / mt9t031->xskip; - mf->height = mt9t031->rect.height / mt9t031->yskip; - mf->code = MEDIA_BUS_FMT_SBGGR10_1X10; - mf->colorspace = V4L2_COLORSPACE_SRGB; - mf->field = V4L2_FIELD_NONE; - - return 0; -} - -/* - * If a user window larger than sensor window is requested, we'll increase the - * sensor window. - */ -static int mt9t031_set_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, - struct v4l2_subdev_format *format) -{ - struct v4l2_mbus_framefmt *mf = &format->format; - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct mt9t031 *mt9t031 = to_mt9t031(client); - u16 xskip, yskip; - struct v4l2_rect rect = mt9t031->rect; - - if (format->pad) - return -EINVAL; - - mf->code = MEDIA_BUS_FMT_SBGGR10_1X10; - mf->colorspace = V4L2_COLORSPACE_SRGB; - v4l_bound_align_image( - &mf->width, MT9T031_MIN_WIDTH, MT9T031_MAX_WIDTH, 1, - &mf->height, MT9T031_MIN_HEIGHT, MT9T031_MAX_HEIGHT, 1, 0); - - if (format->which == V4L2_SUBDEV_FORMAT_TRY) { - cfg->try_fmt = *mf; - return 0; - } - - /* - * Width and height are within limits. - * S_FMT: use binning and skipping for scaling - */ - xskip = mt9t031_skip(&rect.width, mf->width, MT9T031_MAX_WIDTH); - yskip = mt9t031_skip(&rect.height, mf->height, MT9T031_MAX_HEIGHT); - - mf->code = MEDIA_BUS_FMT_SBGGR10_1X10; - mf->colorspace = V4L2_COLORSPACE_SRGB; - - /* mt9t031_set_params() doesn't change width and height */ - return mt9t031_set_params(client, &rect, xskip, yskip); -} - -#ifdef CONFIG_VIDEO_ADV_DEBUG -static int mt9t031_g_register(struct v4l2_subdev *sd, - struct v4l2_dbg_register *reg) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - - if (reg->reg > 0xff) - return -EINVAL; - - reg->size = 1; - reg->val = reg_read(client, reg->reg); - - if (reg->val > 0xffff) - return -EIO; - - return 0; -} - -static int mt9t031_s_register(struct v4l2_subdev *sd, - const struct v4l2_dbg_register *reg) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - - if (reg->reg > 0xff) - return -EINVAL; - - if (reg_write(client, reg->reg, reg->val) < 0) - return -EIO; - - return 0; -} -#endif - -static int mt9t031_g_volatile_ctrl(struct v4l2_ctrl *ctrl) -{ - struct mt9t031 *mt9t031 = container_of(ctrl->handler, - struct mt9t031, hdl); - const u32 shutter_max = MT9T031_MAX_HEIGHT + MT9T031_VERTICAL_BLANK; - s32 min, max; - - switch (ctrl->id) { - case V4L2_CID_EXPOSURE_AUTO: - min = mt9t031->exposure->minimum; - max = mt9t031->exposure->maximum; - mt9t031->exposure->val = - (shutter_max / 2 + (mt9t031->total_h - 1) * (max - min)) - / shutter_max + min; - break; - } - return 0; -} - -static int mt9t031_s_ctrl(struct v4l2_ctrl *ctrl) -{ - struct mt9t031 *mt9t031 = container_of(ctrl->handler, - struct mt9t031, hdl); - struct v4l2_subdev *sd = &mt9t031->subdev; - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct v4l2_ctrl *exp = mt9t031->exposure; - int data; - - switch (ctrl->id) { - case V4L2_CID_VFLIP: - if (ctrl->val) - data = reg_set(client, MT9T031_READ_MODE_2, 0x8000); - else - data = reg_clear(client, MT9T031_READ_MODE_2, 0x8000); - if (data < 0) - return -EIO; - return 0; - case V4L2_CID_HFLIP: - if (ctrl->val) - data = reg_set(client, MT9T031_READ_MODE_2, 0x4000); - else - data = reg_clear(client, MT9T031_READ_MODE_2, 0x4000); - if (data < 0) - return -EIO; - return 0; - case V4L2_CID_GAIN: - /* See Datasheet Table 7, Gain settings. */ - if (ctrl->val <= ctrl->default_value) { - /* Pack it into 0..1 step 0.125, register values 0..8 */ - unsigned long range = ctrl->default_value - ctrl->minimum; - data = ((ctrl->val - (s32)ctrl->minimum) * 8 + range / 2) / range; - - dev_dbg(&client->dev, "Setting gain %d\n", data); - data = reg_write(client, MT9T031_GLOBAL_GAIN, data); - if (data < 0) - return -EIO; - } else { - /* Pack it into 1.125..128 variable step, register values 9..0x7860 */ - /* We assume qctrl->maximum - qctrl->default_value - 1 > 0 */ - unsigned long range = ctrl->maximum - ctrl->default_value - 1; - /* calculated gain: map 65..127 to 9..1024 step 0.125 */ - unsigned long gain = ((ctrl->val - (s32)ctrl->default_value - 1) * - 1015 + range / 2) / range + 9; - - if (gain <= 32) /* calculated gain 9..32 -> 9..32 */ - data = gain; - else if (gain <= 64) /* calculated gain 33..64 -> 0x51..0x60 */ - data = ((gain - 32) * 16 + 16) / 32 + 80; - else - /* calculated gain 65..1024 -> (1..120) << 8 + 0x60 */ - data = (((gain - 64 + 7) * 32) & 0xff00) | 0x60; - - dev_dbg(&client->dev, "Set gain from 0x%x to 0x%x\n", - reg_read(client, MT9T031_GLOBAL_GAIN), data); - data = reg_write(client, MT9T031_GLOBAL_GAIN, data); - if (data < 0) - return -EIO; - } - return 0; - - case V4L2_CID_EXPOSURE_AUTO: - if (ctrl->val == V4L2_EXPOSURE_MANUAL) { - unsigned int range = exp->maximum - exp->minimum; - unsigned int shutter = ((exp->val - (s32)exp->minimum) * 1048 + - range / 2) / range + 1; - u32 old; - - get_shutter(client, &old); - dev_dbg(&client->dev, "Set shutter from %u to %u\n", - old, shutter); - if (set_shutter(client, shutter) < 0) - return -EIO; - } else { - const u16 vblank = MT9T031_VERTICAL_BLANK; - mt9t031->total_h = mt9t031->rect.height + - mt9t031->y_skip_top + vblank; - - if (set_shutter(client, mt9t031->total_h) < 0) - return -EIO; - } - return 0; - default: - return -EINVAL; - } - return 0; -} - -/* - * Power Management: - * This function does nothing for now but must be present for pm to work - */ -static int mt9t031_runtime_suspend(struct device *dev) -{ - return 0; -} - -/* - * Power Management: - * COLUMN_ADDRESS_MODE and ROW_ADDRESS_MODE are not rewritten if unchanged - * they are however changed at reset if the platform hook is present - * thus we rewrite them with the values stored by the driver - */ -static int mt9t031_runtime_resume(struct device *dev) -{ - struct video_device *vdev = to_video_device(dev); - struct v4l2_subdev *sd = soc_camera_vdev_to_subdev(vdev); - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct mt9t031 *mt9t031 = to_mt9t031(client); - - int ret; - u16 xbin, ybin; - - xbin = min(mt9t031->xskip, (u16)3); - ybin = min(mt9t031->yskip, (u16)3); - - ret = reg_write(client, MT9T031_COLUMN_ADDRESS_MODE, - ((xbin - 1) << 4) | (mt9t031->xskip - 1)); - if (ret < 0) - return ret; - - ret = reg_write(client, MT9T031_ROW_ADDRESS_MODE, - ((ybin - 1) << 4) | (mt9t031->yskip - 1)); - if (ret < 0) - return ret; - - return 0; -} - -static const struct dev_pm_ops mt9t031_dev_pm_ops = { - .runtime_suspend = mt9t031_runtime_suspend, - .runtime_resume = mt9t031_runtime_resume, -}; - -static const struct device_type mt9t031_dev_type = { - .name = "MT9T031", - .pm = &mt9t031_dev_pm_ops, -}; - -static int mt9t031_s_power(struct v4l2_subdev *sd, int on) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); - struct video_device *vdev = soc_camera_i2c_to_vdev(client); - struct mt9t031 *mt9t031 = to_mt9t031(client); - int ret; - - if (on) { - ret = soc_camera_power_on(&client->dev, ssdd, mt9t031->clk); - if (ret < 0) - return ret; - if (vdev) - /* Not needed during probing, when vdev isn't available yet */ - vdev->dev.type = &mt9t031_dev_type; - } else { - if (vdev) - vdev->dev.type = NULL; - soc_camera_power_off(&client->dev, ssdd, mt9t031->clk); - } - - return 0; -} - -/* - * Interface active, can use i2c. If it fails, it can indeed mean, that - * this wasn't our capture interface, so, we wait for the right one - */ -static int mt9t031_video_probe(struct i2c_client *client) -{ - struct mt9t031 *mt9t031 = to_mt9t031(client); - s32 data; - int ret; - - ret = mt9t031_s_power(&mt9t031->subdev, 1); - if (ret < 0) - return ret; - - ret = mt9t031_idle(client); - if (ret < 0) { - dev_err(&client->dev, "Failed to initialise the camera\n"); - goto done; - } - - /* Read out the chip version register */ - data = reg_read(client, MT9T031_CHIP_VERSION); - - switch (data) { - case 0x1621: - break; - default: - dev_err(&client->dev, - "No MT9T031 chip detected, register read %x\n", data); - ret = -ENODEV; - goto done; - } - - dev_info(&client->dev, "Detected a MT9T031 chip ID %x\n", data); - - ret = v4l2_ctrl_handler_setup(&mt9t031->hdl); - -done: - mt9t031_s_power(&mt9t031->subdev, 0); - - return ret; -} - -static int mt9t031_g_skip_top_lines(struct v4l2_subdev *sd, u32 *lines) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct mt9t031 *mt9t031 = to_mt9t031(client); - - *lines = mt9t031->y_skip_top; - - return 0; -} - -static const struct v4l2_ctrl_ops mt9t031_ctrl_ops = { - .g_volatile_ctrl = mt9t031_g_volatile_ctrl, - .s_ctrl = mt9t031_s_ctrl, -}; - -static const struct v4l2_subdev_core_ops mt9t031_subdev_core_ops = { - .s_power = mt9t031_s_power, -#ifdef CONFIG_VIDEO_ADV_DEBUG - .g_register = mt9t031_g_register, - .s_register = mt9t031_s_register, -#endif -}; - -static int mt9t031_enum_mbus_code(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, - struct v4l2_subdev_mbus_code_enum *code) -{ - if (code->pad || code->index) - return -EINVAL; - - code->code = MEDIA_BUS_FMT_SBGGR10_1X10; - return 0; -} - -static int mt9t031_g_mbus_config(struct v4l2_subdev *sd, - struct v4l2_mbus_config *cfg) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); - - cfg->flags = V4L2_MBUS_MASTER | V4L2_MBUS_PCLK_SAMPLE_RISING | - V4L2_MBUS_PCLK_SAMPLE_FALLING | V4L2_MBUS_HSYNC_ACTIVE_HIGH | - V4L2_MBUS_VSYNC_ACTIVE_HIGH | V4L2_MBUS_DATA_ACTIVE_HIGH; - cfg->type = V4L2_MBUS_PARALLEL; - cfg->flags = soc_camera_apply_board_flags(ssdd, cfg); - - return 0; -} - -static int mt9t031_s_mbus_config(struct v4l2_subdev *sd, - const struct v4l2_mbus_config *cfg) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); - - if (soc_camera_apply_board_flags(ssdd, cfg) & - V4L2_MBUS_PCLK_SAMPLE_FALLING) - return reg_clear(client, MT9T031_PIXEL_CLOCK_CONTROL, 0x8000); - else - return reg_set(client, MT9T031_PIXEL_CLOCK_CONTROL, 0x8000); -} - -static const struct v4l2_subdev_video_ops mt9t031_subdev_video_ops = { - .s_stream = mt9t031_s_stream, - .g_mbus_config = mt9t031_g_mbus_config, - .s_mbus_config = mt9t031_s_mbus_config, -}; - -static const struct v4l2_subdev_sensor_ops mt9t031_subdev_sensor_ops = { - .g_skip_top_lines = mt9t031_g_skip_top_lines, -}; - -static const struct v4l2_subdev_pad_ops mt9t031_subdev_pad_ops = { - .enum_mbus_code = mt9t031_enum_mbus_code, - .get_selection = mt9t031_get_selection, - .set_selection = mt9t031_set_selection, - .get_fmt = mt9t031_get_fmt, - .set_fmt = mt9t031_set_fmt, -}; - -static const struct v4l2_subdev_ops mt9t031_subdev_ops = { - .core = &mt9t031_subdev_core_ops, - .video = &mt9t031_subdev_video_ops, - .sensor = &mt9t031_subdev_sensor_ops, - .pad = &mt9t031_subdev_pad_ops, -}; - -static int mt9t031_probe(struct i2c_client *client, - const struct i2c_device_id *did) -{ - struct mt9t031 *mt9t031; - struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); - struct i2c_adapter *adapter = client->adapter; - int ret; - - if (!ssdd) { - dev_err(&client->dev, "MT9T031 driver needs platform data\n"); - return -EINVAL; - } - - if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WORD_DATA)) { - dev_warn(&adapter->dev, - "I2C-Adapter doesn't support I2C_FUNC_SMBUS_WORD\n"); - return -EIO; - } - - mt9t031 = devm_kzalloc(&client->dev, sizeof(struct mt9t031), GFP_KERNEL); - if (!mt9t031) - return -ENOMEM; - - v4l2_i2c_subdev_init(&mt9t031->subdev, client, &mt9t031_subdev_ops); - v4l2_ctrl_handler_init(&mt9t031->hdl, 5); - v4l2_ctrl_new_std(&mt9t031->hdl, &mt9t031_ctrl_ops, - V4L2_CID_VFLIP, 0, 1, 1, 0); - v4l2_ctrl_new_std(&mt9t031->hdl, &mt9t031_ctrl_ops, - V4L2_CID_HFLIP, 0, 1, 1, 0); - v4l2_ctrl_new_std(&mt9t031->hdl, &mt9t031_ctrl_ops, - V4L2_CID_GAIN, 0, 127, 1, 64); - - /* - * Simulated autoexposure. If enabled, we calculate shutter width - * ourselves in the driver based on vertical blanking and frame width - */ - mt9t031->autoexposure = v4l2_ctrl_new_std_menu(&mt9t031->hdl, - &mt9t031_ctrl_ops, V4L2_CID_EXPOSURE_AUTO, 1, 0, - V4L2_EXPOSURE_AUTO); - mt9t031->exposure = v4l2_ctrl_new_std(&mt9t031->hdl, &mt9t031_ctrl_ops, - V4L2_CID_EXPOSURE, 1, 255, 1, 255); - - mt9t031->subdev.ctrl_handler = &mt9t031->hdl; - if (mt9t031->hdl.error) - return mt9t031->hdl.error; - - v4l2_ctrl_auto_cluster(2, &mt9t031->autoexposure, - V4L2_EXPOSURE_MANUAL, true); - - mt9t031->y_skip_top = 0; - mt9t031->rect.left = MT9T031_COLUMN_SKIP; - mt9t031->rect.top = MT9T031_ROW_SKIP; - mt9t031->rect.width = MT9T031_MAX_WIDTH; - mt9t031->rect.height = MT9T031_MAX_HEIGHT; - - mt9t031->xskip = 1; - mt9t031->yskip = 1; - - mt9t031->clk = v4l2_clk_get(&client->dev, "mclk"); - if (IS_ERR(mt9t031->clk)) { - ret = PTR_ERR(mt9t031->clk); - goto eclkget; - } - - ret = mt9t031_video_probe(client); - if (ret) { - v4l2_clk_put(mt9t031->clk); -eclkget: - v4l2_ctrl_handler_free(&mt9t031->hdl); - } - - return ret; -} - -static int mt9t031_remove(struct i2c_client *client) -{ - struct mt9t031 *mt9t031 = to_mt9t031(client); - - v4l2_clk_put(mt9t031->clk); - v4l2_device_unregister_subdev(&mt9t031->subdev); - v4l2_ctrl_handler_free(&mt9t031->hdl); - - return 0; -} - -static const struct i2c_device_id mt9t031_id[] = { - { "mt9t031", 0 }, - { } -}; -MODULE_DEVICE_TABLE(i2c, mt9t031_id); - -static struct i2c_driver mt9t031_i2c_driver = { - .driver = { - .name = "mt9t031", - }, - .probe = mt9t031_probe, - .remove = mt9t031_remove, - .id_table = mt9t031_id, -}; - -module_i2c_driver(mt9t031_i2c_driver); - -MODULE_DESCRIPTION("Micron MT9T031 Camera driver"); -MODULE_AUTHOR("Guennadi Liakhovetski "); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/staging/media/soc_camera/soc-camera.rst b/drivers/staging/media/soc_camera/soc-camera.rst deleted file mode 100644 index 7c39711aebf82..0000000000000 --- a/drivers/staging/media/soc_camera/soc-camera.rst +++ /dev/null @@ -1,171 +0,0 @@ -.. SPDX-License-Identifier: GPL-2.0 - -The Soc-Camera Drivers -====================== - -Author: Guennadi Liakhovetski - -Terminology ------------ - -The following terms are used in this document: - - camera / camera device / camera sensor - a video-camera sensor chip, capable - of connecting to a variety of systems and interfaces, typically uses i2c for - control and configuration, and a parallel or a serial bus for data. - - camera host - an interface, to which a camera is connected. Typically a - specialised interface, present on many SoCs, e.g. PXA27x and PXA3xx, SuperH, - i.MX27, i.MX31. - - camera host bus - a connection between a camera host and a camera. Can be - parallel or serial, consists of data and control lines, e.g. clock, vertical - and horizontal synchronization signals. - -Purpose of the soc-camera subsystem ------------------------------------ - -The soc-camera subsystem initially provided a unified API between camera host -drivers and camera sensor drivers. Later the soc-camera sensor API has been -replaced with the V4L2 standard subdev API. This also made camera driver re-use -with non-soc-camera hosts possible. The camera host API to the soc-camera core -has been preserved. - -Soc-camera implements a V4L2 interface to the user, currently only the "mmap" -method is supported by host drivers. However, the soc-camera core also provides -support for the "read" method. - -The subsystem has been designed to support multiple camera host interfaces and -multiple cameras per interface, although most applications have only one camera -sensor. - -Existing drivers ----------------- - -As of 3.7 there are seven host drivers in the mainline: atmel-isi.c, -mx1_camera.c (broken, scheduled for removal), mx2_camera.c, mx3_camera.c, -omap1_camera.c, pxa_camera.c, sh_mobile_ceu_camera.c, and multiple sensor -drivers under drivers/media/i2c/soc_camera/. - -Camera host API ---------------- - -A host camera driver is registered using the - -.. code-block:: none - - soc_camera_host_register(struct soc_camera_host *); - -function. The host object can be initialized as follows: - -.. code-block:: none - - struct soc_camera_host *ici; - ici->drv_name = DRV_NAME; - ici->ops = &camera_host_ops; - ici->priv = pcdev; - ici->v4l2_dev.dev = &pdev->dev; - ici->nr = pdev->id; - -All camera host methods are passed in a struct soc_camera_host_ops: - -.. code-block:: none - - static struct soc_camera_host_ops camera_host_ops = { - .owner = THIS_MODULE, - .add = camera_add_device, - .remove = camera_remove_device, - .set_fmt = camera_set_fmt_cap, - .try_fmt = camera_try_fmt_cap, - .init_videobuf2 = camera_init_videobuf2, - .poll = camera_poll, - .querycap = camera_querycap, - .set_bus_param = camera_set_bus_param, - /* The rest of host operations are optional */ - }; - -.add and .remove methods are called when a sensor is attached to or detached -from the host. .set_bus_param is used to configure physical connection -parameters between the host and the sensor. .init_videobuf2 is called by -soc-camera core when a video-device is opened, the host driver would typically -call vb2_queue_init() in this method. Further video-buffer management is -implemented completely by the specific camera host driver. If the host driver -supports non-standard pixel format conversion, it should implement a -.get_formats and, possibly, a .put_formats operations. See below for more -details about format conversion. The rest of the methods are called from -respective V4L2 operations. - -Camera API ----------- - -Sensor drivers can use struct soc_camera_link, typically provided by the -platform, and used to specify to which camera host bus the sensor is connected, -and optionally provide platform .power and .reset methods for the camera. This -struct is provided to the camera driver via the I2C client device platform data -and can be obtained, using the soc_camera_i2c_to_link() macro. Care should be -taken, when using soc_camera_vdev_to_subdev() and when accessing struct -soc_camera_device, using v4l2_get_subdev_hostdata(): both only work, when -running on an soc-camera host. The actual camera driver operation is implemented -using the V4L2 subdev API. Additionally soc-camera camera drivers can use -auxiliary soc-camera helper functions like soc_camera_power_on() and -soc_camera_power_off(), which switch regulators, provided by the platform and call -board-specific power switching methods. soc_camera_apply_board_flags() takes -camera bus configuration capability flags and applies any board transformations, -e.g. signal polarity inversion. soc_mbus_get_fmtdesc() can be used to obtain a -pixel format descriptor, corresponding to a certain media-bus pixel format code. -soc_camera_limit_side() can be used to restrict beginning and length of a frame -side, based on camera capabilities. - -VIDIOC_S_CROP and VIDIOC_S_FMT behaviour ----------------------------------------- - -Above user ioctls modify image geometry as follows: - -VIDIOC_S_CROP: sets location and sizes of the sensor window. Unit is one sensor -pixel. Changing sensor window sizes preserves any scaling factors, therefore -user window sizes change as well. - -VIDIOC_S_FMT: sets user window. Should preserve previously set sensor window as -much as possible by modifying scaling factors. If the sensor window cannot be -preserved precisely, it may be changed too. - -In soc-camera there are two locations, where scaling and cropping can take -place: in the camera driver and in the host driver. User ioctls are first passed -to the host driver, which then generally passes them down to the camera driver. -It is more efficient to perform scaling and cropping in the camera driver to -save camera bus bandwidth and maximise the framerate. However, if the camera -driver failed to set the required parameters with sufficient precision, the host -driver may decide to also use its own scaling and cropping to fulfill the user's -request. - -Camera drivers are interfaced to the soc-camera core and to host drivers over -the v4l2-subdev API, which is completely functional, it doesn't pass any data. -Therefore all camera drivers shall reply to .g_fmt() requests with their current -output geometry. This is necessary to correctly configure the camera bus. -.s_fmt() and .try_fmt() have to be implemented too. Sensor window and scaling -factors have to be maintained by camera drivers internally. According to the -V4L2 API all capture drivers must support the VIDIOC_CROPCAP ioctl, hence we -rely on camera drivers implementing .cropcap(). If the camera driver does not -support cropping, it may choose to not implement .s_crop(), but to enable -cropping support by the camera host driver at least the .g_crop method must be -implemented. - -User window geometry is kept in .user_width and .user_height fields in struct -soc_camera_device and used by the soc-camera core and host drivers. The core -updates these fields upon successful completion of a .s_fmt() call, but if these -fields change elsewhere, e.g. during .s_crop() processing, the host driver is -responsible for updating them. - -Format conversion ------------------ - -V4L2 distinguishes between pixel formats, as they are stored in memory, and as -they are transferred over a media bus. Soc-camera provides support to -conveniently manage these formats. A table of standard transformations is -maintained by soc-camera core, which describes, what FOURCC pixel format will -be obtained, if a media-bus pixel format is stored in memory according to -certain rules. E.g. if MEDIA_BUS_FMT_YUYV8_2X8 data is sampled with 8 bits per -sample and stored in memory in the little-endian order with no gaps between -bytes, data in memory will represent the V4L2_PIX_FMT_YUYV FOURCC format. These -standard transformations will be used by soc-camera or by camera host drivers to -configure camera drivers to produce the FOURCC format, requested by the user, -using the VIDIOC_S_FMT ioctl(). Apart from those standard format conversions, -host drivers can also provide their own conversion rules by implementing a -.get_formats and, if required, a .put_formats methods. diff --git a/drivers/staging/media/soc_camera/soc_camera.c b/drivers/staging/media/soc_camera/soc_camera.c deleted file mode 100644 index 39f513f69b89b..0000000000000 --- a/drivers/staging/media/soc_camera/soc_camera.c +++ /dev/null @@ -1,2164 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * camera image capture (abstract) bus driver - * - * Copyright (C) 2008, Guennadi Liakhovetski - * - * This driver provides an interface between platform-specific camera - * buses and camera devices. It should be used if the camera is - * connected not over a "proper" bus like PCI or USB, but over a - * special bus, like, for example, the Quick Capture interface on PXA270 - * SoCs. Later it should also be used for i.MX31 SoCs from Freescale. - * It can handle multiple cameras and / or multiple buses, which can - * be used, e.g., in stereo-vision applications. - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* Default to VGA resolution */ -#define DEFAULT_WIDTH 640 -#define DEFAULT_HEIGHT 480 - -#define MAP_MAX_NUM 32 -static DECLARE_BITMAP(device_map, MAP_MAX_NUM); -static LIST_HEAD(hosts); -static LIST_HEAD(devices); -/* - * Protects lists and bitmaps of hosts and devices. - * Lock nesting: Ok to take ->host_lock under list_lock. - */ -static DEFINE_MUTEX(list_lock); - -struct soc_camera_async_client { - struct v4l2_async_subdev *sensor; - struct v4l2_async_notifier notifier; - struct platform_device *pdev; - struct list_head list; /* needed for clean up */ -}; - -static int soc_camera_video_start(struct soc_camera_device *icd); -static int video_dev_create(struct soc_camera_device *icd); - -int soc_camera_power_on(struct device *dev, struct soc_camera_subdev_desc *ssdd, - struct v4l2_clk *clk) -{ - int ret; - bool clock_toggle; - - if (clk && (!ssdd->unbalanced_power || - !test_and_set_bit(0, &ssdd->clock_state))) { - ret = v4l2_clk_enable(clk); - if (ret < 0) { - dev_err(dev, "Cannot enable clock: %d\n", ret); - return ret; - } - clock_toggle = true; - } else { - clock_toggle = false; - } - - ret = regulator_bulk_enable(ssdd->sd_pdata.num_regulators, - ssdd->sd_pdata.regulators); - if (ret < 0) { - dev_err(dev, "Cannot enable regulators\n"); - goto eregenable; - } - - if (ssdd->power) { - ret = ssdd->power(dev, 1); - if (ret < 0) { - dev_err(dev, - "Platform failed to power-on the camera.\n"); - goto epwron; - } - } - - return 0; - -epwron: - regulator_bulk_disable(ssdd->sd_pdata.num_regulators, - ssdd->sd_pdata.regulators); -eregenable: - if (clock_toggle) - v4l2_clk_disable(clk); - - return ret; -} -EXPORT_SYMBOL(soc_camera_power_on); - -int soc_camera_power_off(struct device *dev, struct soc_camera_subdev_desc *ssdd, - struct v4l2_clk *clk) -{ - int ret = 0; - int err; - - if (ssdd->power) { - err = ssdd->power(dev, 0); - if (err < 0) { - dev_err(dev, - "Platform failed to power-off the camera.\n"); - ret = err; - } - } - - err = regulator_bulk_disable(ssdd->sd_pdata.num_regulators, - ssdd->sd_pdata.regulators); - if (err < 0) { - dev_err(dev, "Cannot disable regulators\n"); - ret = ret ? : err; - } - - if (clk && (!ssdd->unbalanced_power || test_and_clear_bit(0, &ssdd->clock_state))) - v4l2_clk_disable(clk); - - return ret; -} -EXPORT_SYMBOL(soc_camera_power_off); - -int soc_camera_power_init(struct device *dev, struct soc_camera_subdev_desc *ssdd) -{ - /* Should not have any effect in synchronous case */ - return devm_regulator_bulk_get(dev, ssdd->sd_pdata.num_regulators, - ssdd->sd_pdata.regulators); -} -EXPORT_SYMBOL(soc_camera_power_init); - -static int __soc_camera_power_on(struct soc_camera_device *icd) -{ - struct v4l2_subdev *sd = soc_camera_to_subdev(icd); - int ret; - - ret = v4l2_subdev_call(sd, core, s_power, 1); - if (ret < 0 && ret != -ENOIOCTLCMD && ret != -ENODEV) - return ret; - - return 0; -} - -static int __soc_camera_power_off(struct soc_camera_device *icd) -{ - struct v4l2_subdev *sd = soc_camera_to_subdev(icd); - int ret; - - ret = v4l2_subdev_call(sd, core, s_power, 0); - if (ret < 0 && ret != -ENOIOCTLCMD && ret != -ENODEV) - return ret; - - return 0; -} - -static int soc_camera_clock_start(struct soc_camera_host *ici) -{ - int ret; - - if (!ici->ops->clock_start) - return 0; - - mutex_lock(&ici->clk_lock); - ret = ici->ops->clock_start(ici); - mutex_unlock(&ici->clk_lock); - - return ret; -} - -static void soc_camera_clock_stop(struct soc_camera_host *ici) -{ - if (!ici->ops->clock_stop) - return; - - mutex_lock(&ici->clk_lock); - ici->ops->clock_stop(ici); - mutex_unlock(&ici->clk_lock); -} - -const struct soc_camera_format_xlate *soc_camera_xlate_by_fourcc( - struct soc_camera_device *icd, unsigned int fourcc) -{ - unsigned int i; - - for (i = 0; i < icd->num_user_formats; i++) - if (icd->user_formats[i].host_fmt->fourcc == fourcc) - return icd->user_formats + i; - return NULL; -} -EXPORT_SYMBOL(soc_camera_xlate_by_fourcc); - -/** - * soc_camera_apply_board_flags() - apply platform SOCAM_SENSOR_INVERT_* flags - * @ssdd: camera platform parameters - * @cfg: media bus configuration - * @return: resulting flags - */ -unsigned long soc_camera_apply_board_flags(struct soc_camera_subdev_desc *ssdd, - const struct v4l2_mbus_config *cfg) -{ - unsigned long f, flags = cfg->flags; - - /* If only one of the two polarities is supported, switch to the opposite */ - if (ssdd->flags & SOCAM_SENSOR_INVERT_HSYNC) { - f = flags & (V4L2_MBUS_HSYNC_ACTIVE_HIGH | V4L2_MBUS_HSYNC_ACTIVE_LOW); - if (f == V4L2_MBUS_HSYNC_ACTIVE_HIGH || f == V4L2_MBUS_HSYNC_ACTIVE_LOW) - flags ^= V4L2_MBUS_HSYNC_ACTIVE_HIGH | V4L2_MBUS_HSYNC_ACTIVE_LOW; - } - - if (ssdd->flags & SOCAM_SENSOR_INVERT_VSYNC) { - f = flags & (V4L2_MBUS_VSYNC_ACTIVE_HIGH | V4L2_MBUS_VSYNC_ACTIVE_LOW); - if (f == V4L2_MBUS_VSYNC_ACTIVE_HIGH || f == V4L2_MBUS_VSYNC_ACTIVE_LOW) - flags ^= V4L2_MBUS_VSYNC_ACTIVE_HIGH | V4L2_MBUS_VSYNC_ACTIVE_LOW; - } - - if (ssdd->flags & SOCAM_SENSOR_INVERT_PCLK) { - f = flags & (V4L2_MBUS_PCLK_SAMPLE_RISING | V4L2_MBUS_PCLK_SAMPLE_FALLING); - if (f == V4L2_MBUS_PCLK_SAMPLE_RISING || f == V4L2_MBUS_PCLK_SAMPLE_FALLING) - flags ^= V4L2_MBUS_PCLK_SAMPLE_RISING | V4L2_MBUS_PCLK_SAMPLE_FALLING; - } - - return flags; -} -EXPORT_SYMBOL(soc_camera_apply_board_flags); - -#define pixfmtstr(x) (x) & 0xff, ((x) >> 8) & 0xff, ((x) >> 16) & 0xff, \ - ((x) >> 24) & 0xff - -static int soc_camera_try_fmt(struct soc_camera_device *icd, - struct v4l2_format *f) -{ - struct soc_camera_host *ici = to_soc_camera_host(icd->parent); - const struct soc_camera_format_xlate *xlate; - struct v4l2_pix_format *pix = &f->fmt.pix; - int ret; - - dev_dbg(icd->pdev, "TRY_FMT(%c%c%c%c, %ux%u)\n", - pixfmtstr(pix->pixelformat), pix->width, pix->height); - - if (pix->pixelformat != V4L2_PIX_FMT_JPEG && - !(ici->capabilities & SOCAM_HOST_CAP_STRIDE)) { - pix->bytesperline = 0; - pix->sizeimage = 0; - } - - ret = ici->ops->try_fmt(icd, f); - if (ret < 0) - return ret; - - xlate = soc_camera_xlate_by_fourcc(icd, pix->pixelformat); - if (!xlate) - return -EINVAL; - - ret = soc_mbus_bytes_per_line(pix->width, xlate->host_fmt); - if (ret < 0) - return ret; - - pix->bytesperline = max_t(u32, pix->bytesperline, ret); - - ret = soc_mbus_image_size(xlate->host_fmt, pix->bytesperline, - pix->height); - if (ret < 0) - return ret; - - pix->sizeimage = max_t(u32, pix->sizeimage, ret); - - return 0; -} - -static int soc_camera_try_fmt_vid_cap(struct file *file, void *priv, - struct v4l2_format *f) -{ - struct soc_camera_device *icd = file->private_data; - - WARN_ON(priv != file->private_data); - - /* Only single-plane capture is supported so far */ - if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - - /* limit format to hardware capabilities */ - return soc_camera_try_fmt(icd, f); -} - -static int soc_camera_enum_input(struct file *file, void *priv, - struct v4l2_input *inp) -{ - struct soc_camera_device *icd = file->private_data; - - if (inp->index != 0) - return -EINVAL; - - /* default is camera */ - inp->type = V4L2_INPUT_TYPE_CAMERA; - inp->std = icd->vdev->tvnorms; - strscpy(inp->name, "Camera", sizeof(inp->name)); - - return 0; -} - -static int soc_camera_g_input(struct file *file, void *priv, unsigned int *i) -{ - *i = 0; - - return 0; -} - -static int soc_camera_s_input(struct file *file, void *priv, unsigned int i) -{ - if (i > 0) - return -EINVAL; - - return 0; -} - -static int soc_camera_s_std(struct file *file, void *priv, v4l2_std_id a) -{ - struct soc_camera_device *icd = file->private_data; - struct v4l2_subdev *sd = soc_camera_to_subdev(icd); - - return v4l2_subdev_call(sd, video, s_std, a); -} - -static int soc_camera_g_std(struct file *file, void *priv, v4l2_std_id *a) -{ - struct soc_camera_device *icd = file->private_data; - struct v4l2_subdev *sd = soc_camera_to_subdev(icd); - - return v4l2_subdev_call(sd, video, g_std, a); -} - -static int soc_camera_enum_framesizes(struct file *file, void *fh, - struct v4l2_frmsizeenum *fsize) -{ - struct soc_camera_device *icd = file->private_data; - struct soc_camera_host *ici = to_soc_camera_host(icd->parent); - - return ici->ops->enum_framesizes(icd, fsize); -} - -static int soc_camera_reqbufs(struct file *file, void *priv, - struct v4l2_requestbuffers *p) -{ - int ret; - struct soc_camera_device *icd = file->private_data; - - WARN_ON(priv != file->private_data); - - if (icd->streamer && icd->streamer != file) - return -EBUSY; - - ret = vb2_reqbufs(&icd->vb2_vidq, p); - if (!ret) - icd->streamer = p->count ? file : NULL; - return ret; -} - -static int soc_camera_querybuf(struct file *file, void *priv, - struct v4l2_buffer *p) -{ - struct soc_camera_device *icd = file->private_data; - - WARN_ON(priv != file->private_data); - - return vb2_querybuf(&icd->vb2_vidq, p); -} - -static int soc_camera_qbuf(struct file *file, void *priv, - struct v4l2_buffer *p) -{ - struct soc_camera_device *icd = file->private_data; - - WARN_ON(priv != file->private_data); - - if (icd->streamer != file) - return -EBUSY; - - return vb2_qbuf(&icd->vb2_vidq, NULL, p); -} - -static int soc_camera_dqbuf(struct file *file, void *priv, - struct v4l2_buffer *p) -{ - struct soc_camera_device *icd = file->private_data; - - WARN_ON(priv != file->private_data); - - if (icd->streamer != file) - return -EBUSY; - - return vb2_dqbuf(&icd->vb2_vidq, p, file->f_flags & O_NONBLOCK); -} - -static int soc_camera_create_bufs(struct file *file, void *priv, - struct v4l2_create_buffers *create) -{ - struct soc_camera_device *icd = file->private_data; - int ret; - - if (icd->streamer && icd->streamer != file) - return -EBUSY; - - ret = vb2_create_bufs(&icd->vb2_vidq, create); - if (!ret) - icd->streamer = file; - return ret; -} - -static int soc_camera_prepare_buf(struct file *file, void *priv, - struct v4l2_buffer *b) -{ - struct soc_camera_device *icd = file->private_data; - - return vb2_prepare_buf(&icd->vb2_vidq, NULL, b); -} - -static int soc_camera_expbuf(struct file *file, void *priv, - struct v4l2_exportbuffer *p) -{ - struct soc_camera_device *icd = file->private_data; - - if (icd->streamer && icd->streamer != file) - return -EBUSY; - return vb2_expbuf(&icd->vb2_vidq, p); -} - -/* Always entered with .host_lock held */ -static int soc_camera_init_user_formats(struct soc_camera_device *icd) -{ - struct v4l2_subdev *sd = soc_camera_to_subdev(icd); - struct soc_camera_host *ici = to_soc_camera_host(icd->parent); - unsigned int i, fmts = 0, raw_fmts = 0; - int ret; - struct v4l2_subdev_mbus_code_enum code = { - .which = V4L2_SUBDEV_FORMAT_ACTIVE, - }; - - while (!v4l2_subdev_call(sd, pad, enum_mbus_code, NULL, &code)) { - raw_fmts++; - code.index++; - } - - if (!ici->ops->get_formats) - /* - * Fallback mode - the host will have to serve all - * sensor-provided formats one-to-one to the user - */ - fmts = raw_fmts; - else - /* - * First pass - only count formats this host-sensor - * configuration can provide - */ - for (i = 0; i < raw_fmts; i++) { - ret = ici->ops->get_formats(icd, i, NULL); - if (ret < 0) - return ret; - fmts += ret; - } - - if (!fmts) - return -ENXIO; - - icd->user_formats = - vmalloc(array_size(fmts, - sizeof(struct soc_camera_format_xlate))); - if (!icd->user_formats) - return -ENOMEM; - - dev_dbg(icd->pdev, "Found %d supported formats.\n", fmts); - - /* Second pass - actually fill data formats */ - fmts = 0; - for (i = 0; i < raw_fmts; i++) - if (!ici->ops->get_formats) { - code.index = i; - v4l2_subdev_call(sd, pad, enum_mbus_code, NULL, &code); - icd->user_formats[fmts].host_fmt = - soc_mbus_get_fmtdesc(code.code); - if (icd->user_formats[fmts].host_fmt) - icd->user_formats[fmts++].code = code.code; - } else { - ret = ici->ops->get_formats(icd, i, - &icd->user_formats[fmts]); - if (ret < 0) - goto egfmt; - fmts += ret; - } - - icd->num_user_formats = fmts; - icd->current_fmt = &icd->user_formats[0]; - - return 0; - -egfmt: - vfree(icd->user_formats); - return ret; -} - -/* Always entered with .host_lock held */ -static void soc_camera_free_user_formats(struct soc_camera_device *icd) -{ - struct soc_camera_host *ici = to_soc_camera_host(icd->parent); - - if (ici->ops->put_formats) - ici->ops->put_formats(icd); - icd->current_fmt = NULL; - icd->num_user_formats = 0; - vfree(icd->user_formats); - icd->user_formats = NULL; -} - -/* Called with .vb_lock held, or from the first open(2), see comment there */ -static int soc_camera_set_fmt(struct soc_camera_device *icd, - struct v4l2_format *f) -{ - struct soc_camera_host *ici = to_soc_camera_host(icd->parent); - struct v4l2_pix_format *pix = &f->fmt.pix; - int ret; - - dev_dbg(icd->pdev, "S_FMT(%c%c%c%c, %ux%u)\n", - pixfmtstr(pix->pixelformat), pix->width, pix->height); - - /* We always call try_fmt() before set_fmt() or set_selection() */ - ret = soc_camera_try_fmt(icd, f); - if (ret < 0) - return ret; - - ret = ici->ops->set_fmt(icd, f); - if (ret < 0) { - return ret; - } else if (!icd->current_fmt || - icd->current_fmt->host_fmt->fourcc != pix->pixelformat) { - dev_err(icd->pdev, - "Host driver hasn't set up current format correctly!\n"); - return -EINVAL; - } - - icd->user_width = pix->width; - icd->user_height = pix->height; - icd->bytesperline = pix->bytesperline; - icd->sizeimage = pix->sizeimage; - icd->colorspace = pix->colorspace; - icd->field = pix->field; - - dev_dbg(icd->pdev, "set width: %d height: %d\n", - icd->user_width, icd->user_height); - - /* set physical bus parameters */ - return ici->ops->set_bus_param(icd); -} - -static int soc_camera_add_device(struct soc_camera_device *icd) -{ - struct soc_camera_host *ici = to_soc_camera_host(icd->parent); - int ret; - - if (ici->icd) - return -EBUSY; - - if (!icd->clk) { - ret = soc_camera_clock_start(ici); - if (ret < 0) - return ret; - } - - if (ici->ops->add) { - ret = ici->ops->add(icd); - if (ret < 0) - goto eadd; - } - - ici->icd = icd; - - return 0; - -eadd: - if (!icd->clk) - soc_camera_clock_stop(ici); - return ret; -} - -static void soc_camera_remove_device(struct soc_camera_device *icd) -{ - struct soc_camera_host *ici = to_soc_camera_host(icd->parent); - - if (WARN_ON(icd != ici->icd)) - return; - - if (ici->ops->remove) - ici->ops->remove(icd); - if (!icd->clk) - soc_camera_clock_stop(ici); - ici->icd = NULL; -} - -static int soc_camera_open(struct file *file) -{ - struct video_device *vdev = video_devdata(file); - struct soc_camera_device *icd; - struct soc_camera_host *ici; - int ret; - - /* - * Don't mess with the host during probe: wait until the loop in - * scan_add_host() completes. Also protect against a race with - * soc_camera_host_unregister(). - */ - if (mutex_lock_interruptible(&list_lock)) - return -ERESTARTSYS; - - if (!vdev || !video_is_registered(vdev)) { - mutex_unlock(&list_lock); - return -ENODEV; - } - - icd = video_get_drvdata(vdev); - ici = to_soc_camera_host(icd->parent); - - ret = try_module_get(ici->ops->owner) ? 0 : -ENODEV; - mutex_unlock(&list_lock); - - if (ret < 0) { - dev_err(icd->pdev, "Couldn't lock capture bus driver.\n"); - return ret; - } - - if (!to_soc_camera_control(icd)) { - /* No device driver attached */ - ret = -ENODEV; - goto econtrol; - } - - if (mutex_lock_interruptible(&ici->host_lock)) { - ret = -ERESTARTSYS; - goto elockhost; - } - icd->use_count++; - - /* Now we really have to activate the camera */ - if (icd->use_count == 1) { - struct soc_camera_desc *sdesc = to_soc_camera_desc(icd); - /* Restore parameters before the last close() per V4L2 API */ - struct v4l2_format f = { - .type = V4L2_BUF_TYPE_VIDEO_CAPTURE, - .fmt.pix = { - .width = icd->user_width, - .height = icd->user_height, - .field = icd->field, - .colorspace = icd->colorspace, - .pixelformat = - icd->current_fmt->host_fmt->fourcc, - }, - }; - - /* The camera could have been already on, try to reset */ - if (sdesc->subdev_desc.reset) - if (icd->control) - sdesc->subdev_desc.reset(icd->control); - - ret = soc_camera_add_device(icd); - if (ret < 0) { - dev_err(icd->pdev, "Couldn't activate the camera: %d\n", ret); - goto eiciadd; - } - - ret = __soc_camera_power_on(icd); - if (ret < 0) - goto epower; - - pm_runtime_enable(&icd->vdev->dev); - ret = pm_runtime_resume(&icd->vdev->dev); - if (ret < 0 && ret != -ENOSYS) - goto eresume; - - /* - * Try to configure with default parameters. Notice: this is the - * very first open, so, we cannot race against other calls, - * apart from someone else calling open() simultaneously, but - * .host_lock is protecting us against it. - */ - ret = soc_camera_set_fmt(icd, &f); - if (ret < 0) - goto esfmt; - - ret = ici->ops->init_videobuf2(&icd->vb2_vidq, icd); - if (ret < 0) - goto einitvb; - v4l2_ctrl_handler_setup(&icd->ctrl_handler); - } - mutex_unlock(&ici->host_lock); - - file->private_data = icd; - dev_dbg(icd->pdev, "camera device open\n"); - - return 0; - - /* - * All errors are entered with the .host_lock held, first four also - * with use_count == 1 - */ -einitvb: -esfmt: - pm_runtime_disable(&icd->vdev->dev); -eresume: - __soc_camera_power_off(icd); -epower: - soc_camera_remove_device(icd); -eiciadd: - icd->use_count--; - mutex_unlock(&ici->host_lock); -elockhost: -econtrol: - module_put(ici->ops->owner); - - return ret; -} - -static int soc_camera_close(struct file *file) -{ - struct soc_camera_device *icd = file->private_data; - struct soc_camera_host *ici = to_soc_camera_host(icd->parent); - - mutex_lock(&ici->host_lock); - if (icd->streamer == file) { - if (ici->ops->init_videobuf2) - vb2_queue_release(&icd->vb2_vidq); - icd->streamer = NULL; - } - icd->use_count--; - if (!icd->use_count) { - pm_runtime_suspend(&icd->vdev->dev); - pm_runtime_disable(&icd->vdev->dev); - - __soc_camera_power_off(icd); - - soc_camera_remove_device(icd); - } - - mutex_unlock(&ici->host_lock); - - module_put(ici->ops->owner); - - dev_dbg(icd->pdev, "camera device close\n"); - - return 0; -} - -static ssize_t soc_camera_read(struct file *file, char __user *buf, - size_t count, loff_t *ppos) -{ - struct soc_camera_device *icd = file->private_data; - struct soc_camera_host *ici = to_soc_camera_host(icd->parent); - - dev_dbg(icd->pdev, "read called, buf %p\n", buf); - - if (ici->ops->init_videobuf2 && icd->vb2_vidq.io_modes & VB2_READ) - return vb2_read(&icd->vb2_vidq, buf, count, ppos, - file->f_flags & O_NONBLOCK); - - dev_err(icd->pdev, "camera device read not implemented\n"); - - return -EINVAL; -} - -static int soc_camera_mmap(struct file *file, struct vm_area_struct *vma) -{ - struct soc_camera_device *icd = file->private_data; - struct soc_camera_host *ici = to_soc_camera_host(icd->parent); - int err; - - dev_dbg(icd->pdev, "mmap called, vma=%p\n", vma); - - if (icd->streamer != file) - return -EBUSY; - - if (mutex_lock_interruptible(&ici->host_lock)) - return -ERESTARTSYS; - err = vb2_mmap(&icd->vb2_vidq, vma); - mutex_unlock(&ici->host_lock); - - dev_dbg(icd->pdev, "vma start=0x%08lx, size=%ld, ret=%d\n", - (unsigned long)vma->vm_start, - (unsigned long)vma->vm_end - (unsigned long)vma->vm_start, - err); - - return err; -} - -static __poll_t soc_camera_poll(struct file *file, poll_table *pt) -{ - struct soc_camera_device *icd = file->private_data; - struct soc_camera_host *ici = to_soc_camera_host(icd->parent); - __poll_t res = EPOLLERR; - - if (icd->streamer != file) - return EPOLLERR; - - mutex_lock(&ici->host_lock); - res = ici->ops->poll(file, pt); - mutex_unlock(&ici->host_lock); - return res; -} - -static const struct v4l2_file_operations soc_camera_fops = { - .owner = THIS_MODULE, - .open = soc_camera_open, - .release = soc_camera_close, - .unlocked_ioctl = video_ioctl2, - .read = soc_camera_read, - .mmap = soc_camera_mmap, - .poll = soc_camera_poll, -}; - -static int soc_camera_s_fmt_vid_cap(struct file *file, void *priv, - struct v4l2_format *f) -{ - struct soc_camera_device *icd = file->private_data; - int ret; - - WARN_ON(priv != file->private_data); - - if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) { - dev_warn(icd->pdev, "Wrong buf-type %d\n", f->type); - return -EINVAL; - } - - if (icd->streamer && icd->streamer != file) - return -EBUSY; - - if (vb2_is_streaming(&icd->vb2_vidq)) { - dev_err(icd->pdev, "S_FMT denied: queue initialised\n"); - return -EBUSY; - } - - ret = soc_camera_set_fmt(icd, f); - - if (!ret && !icd->streamer) - icd->streamer = file; - - return ret; -} - -static int soc_camera_enum_fmt_vid_cap(struct file *file, void *priv, - struct v4l2_fmtdesc *f) -{ - struct soc_camera_device *icd = file->private_data; - const struct soc_mbus_pixelfmt *format; - - WARN_ON(priv != file->private_data); - - if (f->index >= icd->num_user_formats) - return -EINVAL; - - format = icd->user_formats[f->index].host_fmt; - - f->pixelformat = format->fourcc; - return 0; -} - -static int soc_camera_g_fmt_vid_cap(struct file *file, void *priv, - struct v4l2_format *f) -{ - struct soc_camera_device *icd = file->private_data; - struct v4l2_pix_format *pix = &f->fmt.pix; - - WARN_ON(priv != file->private_data); - - if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - - pix->width = icd->user_width; - pix->height = icd->user_height; - pix->bytesperline = icd->bytesperline; - pix->sizeimage = icd->sizeimage; - pix->field = icd->field; - pix->pixelformat = icd->current_fmt->host_fmt->fourcc; - pix->colorspace = icd->colorspace; - dev_dbg(icd->pdev, "current_fmt->fourcc: 0x%08x\n", - icd->current_fmt->host_fmt->fourcc); - return 0; -} - -static int soc_camera_querycap(struct file *file, void *priv, - struct v4l2_capability *cap) -{ - struct soc_camera_device *icd = file->private_data; - struct soc_camera_host *ici = to_soc_camera_host(icd->parent); - - WARN_ON(priv != file->private_data); - - strscpy(cap->driver, ici->drv_name, sizeof(cap->driver)); - return ici->ops->querycap(ici, cap); -} - -static int soc_camera_streamon(struct file *file, void *priv, - enum v4l2_buf_type i) -{ - struct soc_camera_device *icd = file->private_data; - struct v4l2_subdev *sd = soc_camera_to_subdev(icd); - int ret; - - WARN_ON(priv != file->private_data); - - if (i != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - - if (icd->streamer != file) - return -EBUSY; - - /* This calls buf_queue from host driver's videobuf2_queue_ops */ - ret = vb2_streamon(&icd->vb2_vidq, i); - if (!ret) - v4l2_subdev_call(sd, video, s_stream, 1); - - return ret; -} - -static int soc_camera_streamoff(struct file *file, void *priv, - enum v4l2_buf_type i) -{ - struct soc_camera_device *icd = file->private_data; - struct v4l2_subdev *sd = soc_camera_to_subdev(icd); - int ret; - - WARN_ON(priv != file->private_data); - - if (i != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - - if (icd->streamer != file) - return -EBUSY; - - /* - * This calls buf_release from host driver's videobuf2_queue_ops for all - * remaining buffers. When the last buffer is freed, stop capture - */ - ret = vb2_streamoff(&icd->vb2_vidq, i); - - v4l2_subdev_call(sd, video, s_stream, 0); - - return ret; -} - -static int soc_camera_g_selection(struct file *file, void *fh, - struct v4l2_selection *s) -{ - struct soc_camera_device *icd = file->private_data; - struct soc_camera_host *ici = to_soc_camera_host(icd->parent); - - /* With a wrong type no need to try to fall back to cropping */ - if (s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - - return ici->ops->get_selection(icd, s); -} - -static int soc_camera_s_selection(struct file *file, void *fh, - struct v4l2_selection *s) -{ - struct soc_camera_device *icd = file->private_data; - struct soc_camera_host *ici = to_soc_camera_host(icd->parent); - int ret; - - /* In all these cases cropping emulation will not help */ - if (s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE || - (s->target != V4L2_SEL_TGT_COMPOSE && - s->target != V4L2_SEL_TGT_CROP)) - return -EINVAL; - - if (s->target == V4L2_SEL_TGT_COMPOSE) { - /* No output size change during a running capture! */ - if (vb2_is_streaming(&icd->vb2_vidq) && - (icd->user_width != s->r.width || - icd->user_height != s->r.height)) - return -EBUSY; - - /* - * Only one user is allowed to change the output format, touch - * buffers, start / stop streaming, poll for data - */ - if (icd->streamer && icd->streamer != file) - return -EBUSY; - } - - if (s->target == V4L2_SEL_TGT_CROP && - vb2_is_streaming(&icd->vb2_vidq) && - ici->ops->set_liveselection) - ret = ici->ops->set_liveselection(icd, s); - else - ret = ici->ops->set_selection(icd, s); - if (!ret && - s->target == V4L2_SEL_TGT_COMPOSE) { - icd->user_width = s->r.width; - icd->user_height = s->r.height; - if (!icd->streamer) - icd->streamer = file; - } - - return ret; -} - -static int soc_camera_g_parm(struct file *file, void *fh, - struct v4l2_streamparm *a) -{ - struct soc_camera_device *icd = file->private_data; - struct soc_camera_host *ici = to_soc_camera_host(icd->parent); - - if (ici->ops->get_parm) - return ici->ops->get_parm(icd, a); - - return -ENOIOCTLCMD; -} - -static int soc_camera_s_parm(struct file *file, void *fh, - struct v4l2_streamparm *a) -{ - struct soc_camera_device *icd = file->private_data; - struct soc_camera_host *ici = to_soc_camera_host(icd->parent); - - if (ici->ops->set_parm) - return ici->ops->set_parm(icd, a); - - return -ENOIOCTLCMD; -} - -static int soc_camera_probe(struct soc_camera_host *ici, - struct soc_camera_device *icd); - -/* So far this function cannot fail */ -static void scan_add_host(struct soc_camera_host *ici) -{ - struct soc_camera_device *icd; - - mutex_lock(&list_lock); - - list_for_each_entry(icd, &devices, list) - if (icd->iface == ici->nr) { - struct soc_camera_desc *sdesc = to_soc_camera_desc(icd); - struct soc_camera_subdev_desc *ssdd = &sdesc->subdev_desc; - - /* The camera could have been already on, try to reset */ - if (ssdd->reset) - if (icd->control) - ssdd->reset(icd->control); - - icd->parent = ici->v4l2_dev.dev; - - /* Ignore errors */ - soc_camera_probe(ici, icd); - } - - mutex_unlock(&list_lock); -} - -/* - * It is invalid to call v4l2_clk_enable() after a successful probing - * asynchronously outside of V4L2 operations, i.e. with .host_lock not held. - */ -static int soc_camera_clk_enable(struct v4l2_clk *clk) -{ - struct soc_camera_device *icd = clk->priv; - struct soc_camera_host *ici; - - if (!icd || !icd->parent) - return -ENODEV; - - ici = to_soc_camera_host(icd->parent); - - if (!try_module_get(ici->ops->owner)) - return -ENODEV; - - /* - * If a different client is currently being probed, the host will tell - * you to go - */ - return soc_camera_clock_start(ici); -} - -static void soc_camera_clk_disable(struct v4l2_clk *clk) -{ - struct soc_camera_device *icd = clk->priv; - struct soc_camera_host *ici; - - if (!icd || !icd->parent) - return; - - ici = to_soc_camera_host(icd->parent); - - soc_camera_clock_stop(ici); - - module_put(ici->ops->owner); -} - -/* - * Eventually, it would be more logical to make the respective host the clock - * owner, but then we would have to copy this struct for each ici. Besides, it - * would introduce the circular dependency problem, unless we port all client - * drivers to release the clock, when not in use. - */ -static const struct v4l2_clk_ops soc_camera_clk_ops = { - .owner = THIS_MODULE, - .enable = soc_camera_clk_enable, - .disable = soc_camera_clk_disable, -}; - -static int soc_camera_dyn_pdev(struct soc_camera_desc *sdesc, - struct soc_camera_async_client *sasc) -{ - struct platform_device *pdev; - int ret, i; - - mutex_lock(&list_lock); - i = find_first_zero_bit(device_map, MAP_MAX_NUM); - if (i < MAP_MAX_NUM) - set_bit(i, device_map); - mutex_unlock(&list_lock); - if (i >= MAP_MAX_NUM) - return -ENOMEM; - - pdev = platform_device_alloc("soc-camera-pdrv", i); - if (!pdev) - return -ENOMEM; - - ret = platform_device_add_data(pdev, sdesc, sizeof(*sdesc)); - if (ret < 0) { - platform_device_put(pdev); - return ret; - } - - sasc->pdev = pdev; - - return 0; -} - -static struct soc_camera_device *soc_camera_add_pdev(struct soc_camera_async_client *sasc) -{ - struct platform_device *pdev = sasc->pdev; - int ret; - - ret = platform_device_add(pdev); - if (ret < 0 || !pdev->dev.driver) - return NULL; - - return platform_get_drvdata(pdev); -} - -/* Locking: called with .host_lock held */ -static int soc_camera_probe_finish(struct soc_camera_device *icd) -{ - struct v4l2_subdev *sd = soc_camera_to_subdev(icd); - struct v4l2_subdev_format fmt = { - .which = V4L2_SUBDEV_FORMAT_ACTIVE, - }; - struct v4l2_mbus_framefmt *mf = &fmt.format; - int ret; - - sd->grp_id = soc_camera_grp_id(icd); - v4l2_set_subdev_hostdata(sd, icd); - - v4l2_subdev_call(sd, video, g_tvnorms, &icd->vdev->tvnorms); - - ret = v4l2_ctrl_add_handler(&icd->ctrl_handler, sd->ctrl_handler, - NULL, true); - if (ret < 0) - return ret; - - ret = soc_camera_add_device(icd); - if (ret < 0) { - dev_err(icd->pdev, "Couldn't activate the camera: %d\n", ret); - return ret; - } - - /* At this point client .probe() should have run already */ - ret = soc_camera_init_user_formats(icd); - if (ret < 0) - goto eusrfmt; - - icd->field = V4L2_FIELD_ANY; - - ret = soc_camera_video_start(icd); - if (ret < 0) - goto evidstart; - - /* Try to improve our guess of a reasonable window format */ - if (!v4l2_subdev_call(sd, pad, get_fmt, NULL, &fmt)) { - icd->user_width = mf->width; - icd->user_height = mf->height; - icd->colorspace = mf->colorspace; - icd->field = mf->field; - } - soc_camera_remove_device(icd); - - return 0; - -evidstart: - soc_camera_free_user_formats(icd); -eusrfmt: - soc_camera_remove_device(icd); - - return ret; -} - -#ifdef CONFIG_I2C_BOARDINFO -static int soc_camera_i2c_init(struct soc_camera_device *icd, - struct soc_camera_desc *sdesc) -{ - struct soc_camera_subdev_desc *ssdd; - struct i2c_client *client; - struct soc_camera_host *ici; - struct soc_camera_host_desc *shd = &sdesc->host_desc; - struct i2c_adapter *adap; - struct v4l2_subdev *subdev; - char clk_name[V4L2_CLK_NAME_SIZE]; - int ret; - - /* First find out how we link the main client */ - if (icd->sasc) { - /* Async non-OF probing handled by the subdevice list */ - return -EPROBE_DEFER; - } - - ici = to_soc_camera_host(icd->parent); - adap = i2c_get_adapter(shd->i2c_adapter_id); - if (!adap) { - dev_err(icd->pdev, "Cannot get I2C adapter #%d. No driver?\n", - shd->i2c_adapter_id); - return -ENODEV; - } - - ssdd = kmemdup(&sdesc->subdev_desc, sizeof(*ssdd), GFP_KERNEL); - if (!ssdd) { - ret = -ENOMEM; - goto ealloc; - } - /* - * In synchronous case we request regulators ourselves in - * soc_camera_pdrv_probe(), make sure the subdevice driver doesn't try - * to allocate them again. - */ - ssdd->sd_pdata.num_regulators = 0; - ssdd->sd_pdata.regulators = NULL; - shd->board_info->platform_data = ssdd; - - v4l2_clk_name_i2c(clk_name, sizeof(clk_name), - shd->i2c_adapter_id, shd->board_info->addr); - - icd->clk = v4l2_clk_register(&soc_camera_clk_ops, clk_name, icd); - if (IS_ERR(icd->clk)) { - ret = PTR_ERR(icd->clk); - goto eclkreg; - } - - subdev = v4l2_i2c_new_subdev_board(&ici->v4l2_dev, adap, - shd->board_info, NULL); - if (!subdev) { - ret = -ENODEV; - goto ei2cnd; - } - - client = v4l2_get_subdevdata(subdev); - - /* Use to_i2c_client(dev) to recover the i2c client */ - icd->control = &client->dev; - - return 0; -ei2cnd: - v4l2_clk_unregister(icd->clk); - icd->clk = NULL; -eclkreg: - kfree(ssdd); -ealloc: - i2c_put_adapter(adap); - return ret; -} - -static void soc_camera_i2c_free(struct soc_camera_device *icd) -{ - struct i2c_client *client = - to_i2c_client(to_soc_camera_control(icd)); - struct i2c_adapter *adap; - struct soc_camera_subdev_desc *ssdd; - - icd->control = NULL; - if (icd->sasc) - return; - - adap = client->adapter; - ssdd = client->dev.platform_data; - v4l2_device_unregister_subdev(i2c_get_clientdata(client)); - i2c_unregister_device(client); - i2c_put_adapter(adap); - kfree(ssdd); - v4l2_clk_unregister(icd->clk); - icd->clk = NULL; -} - -/* - * V4L2 asynchronous notifier callbacks. They are all called under a v4l2-async - * internal global mutex, therefore cannot race against other asynchronous - * events. Until notifier->complete() (soc_camera_async_complete()) is called, - * the video device node is not registered and no V4L fops can occur. Unloading - * of the host driver also calls a v4l2-async function, so also there we're - * protected. - */ -static int soc_camera_async_bound(struct v4l2_async_notifier *notifier, - struct v4l2_subdev *sd, - struct v4l2_async_subdev *asd) -{ - struct soc_camera_async_client *sasc = container_of(notifier, - struct soc_camera_async_client, notifier); - struct soc_camera_device *icd = platform_get_drvdata(sasc->pdev); - - if (asd == sasc->sensor && !WARN_ON(icd->control)) { - struct i2c_client *client = v4l2_get_subdevdata(sd); - - /* - * Only now we get subdevice-specific information like - * regulators, flags, callbacks, etc. - */ - if (client) { - struct soc_camera_desc *sdesc = to_soc_camera_desc(icd); - struct soc_camera_subdev_desc *ssdd = - soc_camera_i2c_to_desc(client); - if (ssdd) { - memcpy(&sdesc->subdev_desc, ssdd, - sizeof(sdesc->subdev_desc)); - if (ssdd->reset) - ssdd->reset(&client->dev); - } - - icd->control = &client->dev; - } - } - - return 0; -} - -static void soc_camera_async_unbind(struct v4l2_async_notifier *notifier, - struct v4l2_subdev *sd, - struct v4l2_async_subdev *asd) -{ - struct soc_camera_async_client *sasc = container_of(notifier, - struct soc_camera_async_client, notifier); - struct soc_camera_device *icd = platform_get_drvdata(sasc->pdev); - - icd->control = NULL; - - if (icd->clk) { - v4l2_clk_unregister(icd->clk); - icd->clk = NULL; - } -} - -static int soc_camera_async_complete(struct v4l2_async_notifier *notifier) -{ - struct soc_camera_async_client *sasc = container_of(notifier, - struct soc_camera_async_client, notifier); - struct soc_camera_device *icd = platform_get_drvdata(sasc->pdev); - - if (to_soc_camera_control(icd)) { - struct soc_camera_host *ici = to_soc_camera_host(icd->parent); - int ret; - - mutex_lock(&list_lock); - ret = soc_camera_probe(ici, icd); - mutex_unlock(&list_lock); - if (ret < 0) - return ret; - } - - return 0; -} - -static const struct v4l2_async_notifier_operations soc_camera_async_ops = { - .bound = soc_camera_async_bound, - .unbind = soc_camera_async_unbind, - .complete = soc_camera_async_complete, -}; - -static int scan_async_group(struct soc_camera_host *ici, - struct v4l2_async_subdev **asd, unsigned int size) -{ - struct soc_camera_async_subdev *sasd; - struct soc_camera_async_client *sasc; - struct soc_camera_device *icd; - struct soc_camera_desc sdesc = {.host_desc.bus_id = ici->nr,}; - char clk_name[V4L2_CLK_NAME_SIZE]; - unsigned int i; - int ret; - - /* First look for a sensor */ - for (i = 0; i < size; i++) { - sasd = container_of(asd[i], struct soc_camera_async_subdev, asd); - if (sasd->role == SOCAM_SUBDEV_DATA_SOURCE) - break; - } - - if (i >= size || asd[i]->match_type != V4L2_ASYNC_MATCH_I2C) { - /* All useless */ - dev_err(ici->v4l2_dev.dev, "No I2C data source found!\n"); - return -ENODEV; - } - - /* Or shall this be managed by the soc-camera device? */ - sasc = devm_kzalloc(ici->v4l2_dev.dev, sizeof(*sasc), GFP_KERNEL); - if (!sasc) - return -ENOMEM; - - /* HACK: just need a != NULL */ - sdesc.host_desc.board_info = ERR_PTR(-ENODATA); - - ret = soc_camera_dyn_pdev(&sdesc, sasc); - if (ret < 0) - goto eallocpdev; - - sasc->sensor = &sasd->asd; - - icd = soc_camera_add_pdev(sasc); - if (!icd) { - ret = -ENOMEM; - goto eaddpdev; - } - - v4l2_async_notifier_init(&sasc->notifier); - - for (i = 0; i < size; i++) { - ret = v4l2_async_notifier_add_subdev(&sasc->notifier, asd[i]); - if (ret) - goto eaddasd; - } - - sasc->notifier.ops = &soc_camera_async_ops; - - icd->sasc = sasc; - icd->parent = ici->v4l2_dev.dev; - - v4l2_clk_name_i2c(clk_name, sizeof(clk_name), - sasd->asd.match.i2c.adapter_id, - sasd->asd.match.i2c.address); - - icd->clk = v4l2_clk_register(&soc_camera_clk_ops, clk_name, icd); - if (IS_ERR(icd->clk)) { - ret = PTR_ERR(icd->clk); - goto eclkreg; - } - - ret = v4l2_async_notifier_register(&ici->v4l2_dev, &sasc->notifier); - if (!ret) - return 0; - - v4l2_clk_unregister(icd->clk); -eclkreg: - icd->clk = NULL; -eaddasd: - v4l2_async_notifier_cleanup(&sasc->notifier); - platform_device_del(sasc->pdev); -eaddpdev: - platform_device_put(sasc->pdev); -eallocpdev: - devm_kfree(ici->v4l2_dev.dev, sasc); - dev_err(ici->v4l2_dev.dev, "group probe failed: %d\n", ret); - - return ret; -} - -static void scan_async_host(struct soc_camera_host *ici) -{ - struct v4l2_async_subdev **asd; - int j; - - for (j = 0, asd = ici->asd; ici->asd_sizes[j]; j++) { - scan_async_group(ici, asd, ici->asd_sizes[j]); - asd += ici->asd_sizes[j]; - } -} -#else -#define soc_camera_i2c_init(icd, sdesc) (-ENODEV) -#define soc_camera_i2c_free(icd) do {} while (0) -#define scan_async_host(ici) do {} while (0) -#endif - -#ifdef CONFIG_OF - -struct soc_of_info { - struct soc_camera_async_subdev sasd; - struct soc_camera_async_client sasc; - struct v4l2_async_subdev *subdev; -}; - -static int soc_of_bind(struct soc_camera_host *ici, - struct device_node *ep, - struct device_node *remote) -{ - struct soc_camera_device *icd; - struct soc_camera_desc sdesc = {.host_desc.bus_id = ici->nr,}; - struct soc_camera_async_client *sasc; - struct soc_of_info *info; - struct i2c_client *client; - char clk_name[V4L2_CLK_NAME_SIZE]; - int ret; - - /* allocate a new subdev and add match info to it */ - info = devm_kzalloc(ici->v4l2_dev.dev, sizeof(struct soc_of_info), - GFP_KERNEL); - if (!info) - return -ENOMEM; - - info->sasd.asd.match.fwnode = of_fwnode_handle(remote); - info->sasd.asd.match_type = V4L2_ASYNC_MATCH_FWNODE; - info->subdev = &info->sasd.asd; - - /* Or shall this be managed by the soc-camera device? */ - sasc = &info->sasc; - - /* HACK: just need a != NULL */ - sdesc.host_desc.board_info = ERR_PTR(-ENODATA); - - ret = soc_camera_dyn_pdev(&sdesc, sasc); - if (ret < 0) - goto eallocpdev; - - sasc->sensor = &info->sasd.asd; - - icd = soc_camera_add_pdev(sasc); - if (!icd) { - ret = -ENOMEM; - goto eaddpdev; - } - - v4l2_async_notifier_init(&sasc->notifier); - - ret = v4l2_async_notifier_add_subdev(&sasc->notifier, info->subdev); - if (ret) { - of_node_put(remote); - goto eaddasd; - } - - sasc->notifier.ops = &soc_camera_async_ops; - - icd->sasc = sasc; - icd->parent = ici->v4l2_dev.dev; - - client = of_find_i2c_device_by_node(remote); - - if (client) - v4l2_clk_name_i2c(clk_name, sizeof(clk_name), - client->adapter->nr, client->addr); - else - v4l2_clk_name_of(clk_name, sizeof(clk_name), remote); - - icd->clk = v4l2_clk_register(&soc_camera_clk_ops, clk_name, icd); - if (IS_ERR(icd->clk)) { - ret = PTR_ERR(icd->clk); - goto eclkreg; - } - - ret = v4l2_async_notifier_register(&ici->v4l2_dev, &sasc->notifier); - if (!ret) - return 0; - - v4l2_clk_unregister(icd->clk); -eclkreg: - icd->clk = NULL; -eaddasd: - v4l2_async_notifier_cleanup(&sasc->notifier); - platform_device_del(sasc->pdev); -eaddpdev: - platform_device_put(sasc->pdev); -eallocpdev: - devm_kfree(ici->v4l2_dev.dev, info); - dev_err(ici->v4l2_dev.dev, "group probe failed: %d\n", ret); - - return ret; -} - -static void scan_of_host(struct soc_camera_host *ici) -{ - struct device *dev = ici->v4l2_dev.dev; - struct device_node *np = dev->of_node; - struct device_node *epn = NULL, *rem; - unsigned int i; - - for (i = 0; ; i++) { - epn = of_graph_get_next_endpoint(np, epn); - if (!epn) - break; - - rem = of_graph_get_remote_port_parent(epn); - if (!rem) { - dev_notice(dev, "no remote for %pOF\n", epn); - continue; - } - - /* so we now have a remote node to connect */ - if (!i) - soc_of_bind(ici, epn, rem); - - if (i) { - dev_err(dev, "multiple subdevices aren't supported yet!\n"); - break; - } - } - - of_node_put(epn); -} - -#else -static inline void scan_of_host(struct soc_camera_host *ici) { } -#endif - -/* Called during host-driver probe */ -static int soc_camera_probe(struct soc_camera_host *ici, - struct soc_camera_device *icd) -{ - struct soc_camera_desc *sdesc = to_soc_camera_desc(icd); - struct soc_camera_host_desc *shd = &sdesc->host_desc; - struct device *control = NULL; - int ret; - - dev_info(icd->pdev, "Probing %s\n", dev_name(icd->pdev)); - - /* - * Currently the subdev with the largest number of controls (13) is - * ov6550. So let's pick 16 as a hint for the control handler. Note - * that this is a hint only: too large and you waste some memory, too - * small and there is a (very) small performance hit when looking up - * controls in the internal hash. - */ - ret = v4l2_ctrl_handler_init(&icd->ctrl_handler, 16); - if (ret < 0) - return ret; - - /* Must have icd->vdev before registering the device */ - ret = video_dev_create(icd); - if (ret < 0) - goto evdc; - - /* - * ..._video_start() will create a device node, video_register_device() - * itself is protected against concurrent open() calls, but we also have - * to protect our data also during client probing. - */ - - /* Non-i2c cameras, e.g., soc_camera_platform, have no board_info */ - if (shd->board_info) { - ret = soc_camera_i2c_init(icd, sdesc); - if (ret < 0 && ret != -EPROBE_DEFER) - goto eadd; - } else if (!shd->add_device || !shd->del_device) { - ret = -EINVAL; - goto eadd; - } else { - ret = soc_camera_clock_start(ici); - if (ret < 0) - goto eadd; - - if (shd->module_name) - ret = request_module(shd->module_name); - - ret = shd->add_device(icd); - if (ret < 0) - goto eadddev; - - /* - * FIXME: this is racy, have to use driver-binding notification, - * when it is available - */ - control = to_soc_camera_control(icd); - if (!control || !control->driver || !dev_get_drvdata(control) || - !try_module_get(control->driver->owner)) { - shd->del_device(icd); - ret = -ENODEV; - goto enodrv; - } - } - - mutex_lock(&ici->host_lock); - ret = soc_camera_probe_finish(icd); - mutex_unlock(&ici->host_lock); - if (ret < 0) - goto efinish; - - return 0; - -efinish: - if (shd->board_info) { - soc_camera_i2c_free(icd); - } else { - shd->del_device(icd); - module_put(control->driver->owner); -enodrv: -eadddev: - soc_camera_clock_stop(ici); - } -eadd: - if (icd->vdev) { - video_device_release(icd->vdev); - icd->vdev = NULL; - } -evdc: - v4l2_ctrl_handler_free(&icd->ctrl_handler); - return ret; -} - -/* - * This is called on device_unregister, which only means we have to disconnect - * from the host, but not remove ourselves from the device list. With - * asynchronous client probing this can also be called without - * soc_camera_probe_finish() having run. Careful with clean up. - */ -static int soc_camera_remove(struct soc_camera_device *icd) -{ - struct soc_camera_desc *sdesc = to_soc_camera_desc(icd); - struct video_device *vdev = icd->vdev; - - v4l2_ctrl_handler_free(&icd->ctrl_handler); - if (vdev) { - video_unregister_device(vdev); - icd->vdev = NULL; - } - - if (sdesc->host_desc.board_info) { - soc_camera_i2c_free(icd); - } else { - struct device *dev = to_soc_camera_control(icd); - struct device_driver *drv = dev ? dev->driver : NULL; - if (drv) { - sdesc->host_desc.del_device(icd); - module_put(drv->owner); - } - } - - if (icd->num_user_formats) - soc_camera_free_user_formats(icd); - - if (icd->clk) { - /* For the synchronous case */ - v4l2_clk_unregister(icd->clk); - icd->clk = NULL; - } - - if (icd->sasc) - platform_device_unregister(icd->sasc->pdev); - - return 0; -} - -static int default_g_selection(struct soc_camera_device *icd, - struct v4l2_selection *sel) -{ - struct v4l2_subdev *sd = soc_camera_to_subdev(icd); - struct v4l2_subdev_selection sdsel = { - .which = V4L2_SUBDEV_FORMAT_ACTIVE, - .target = sel->target, - }; - int ret; - - ret = v4l2_subdev_call(sd, pad, get_selection, NULL, &sdsel); - if (ret) - return ret; - sel->r = sdsel.r; - return 0; -} - -static int default_s_selection(struct soc_camera_device *icd, - struct v4l2_selection *sel) -{ - struct v4l2_subdev *sd = soc_camera_to_subdev(icd); - struct v4l2_subdev_selection sdsel = { - .which = V4L2_SUBDEV_FORMAT_ACTIVE, - .target = sel->target, - .flags = sel->flags, - .r = sel->r, - }; - int ret; - - ret = v4l2_subdev_call(sd, pad, set_selection, NULL, &sdsel); - if (ret) - return ret; - sel->r = sdsel.r; - return 0; -} - -static int default_g_parm(struct soc_camera_device *icd, - struct v4l2_streamparm *a) -{ - struct v4l2_subdev *sd = soc_camera_to_subdev(icd); - - return v4l2_g_parm_cap(icd->vdev, sd, a); -} - -static int default_s_parm(struct soc_camera_device *icd, - struct v4l2_streamparm *a) -{ - struct v4l2_subdev *sd = soc_camera_to_subdev(icd); - - return v4l2_s_parm_cap(icd->vdev, sd, a); -} - -static int default_enum_framesizes(struct soc_camera_device *icd, - struct v4l2_frmsizeenum *fsize) -{ - int ret; - struct v4l2_subdev *sd = soc_camera_to_subdev(icd); - const struct soc_camera_format_xlate *xlate; - struct v4l2_subdev_frame_size_enum fse = { - .index = fsize->index, - .which = V4L2_SUBDEV_FORMAT_ACTIVE, - }; - - xlate = soc_camera_xlate_by_fourcc(icd, fsize->pixel_format); - if (!xlate) - return -EINVAL; - fse.code = xlate->code; - - ret = v4l2_subdev_call(sd, pad, enum_frame_size, NULL, &fse); - if (ret < 0) - return ret; - - if (fse.min_width == fse.max_width && - fse.min_height == fse.max_height) { - fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE; - fsize->discrete.width = fse.min_width; - fsize->discrete.height = fse.min_height; - return 0; - } - fsize->type = V4L2_FRMSIZE_TYPE_CONTINUOUS; - fsize->stepwise.min_width = fse.min_width; - fsize->stepwise.max_width = fse.max_width; - fsize->stepwise.min_height = fse.min_height; - fsize->stepwise.max_height = fse.max_height; - fsize->stepwise.step_width = 1; - fsize->stepwise.step_height = 1; - return 0; -} - -int soc_camera_host_register(struct soc_camera_host *ici) -{ - struct soc_camera_host *ix; - int ret; - - if (!ici || !ici->ops || - !ici->ops->try_fmt || - !ici->ops->set_fmt || - !ici->ops->set_bus_param || - !ici->ops->querycap || - !ici->ops->init_videobuf2 || - !ici->ops->poll || - !ici->v4l2_dev.dev) - return -EINVAL; - - if (!ici->ops->set_selection) - ici->ops->set_selection = default_s_selection; - if (!ici->ops->get_selection) - ici->ops->get_selection = default_g_selection; - if (!ici->ops->set_parm) - ici->ops->set_parm = default_s_parm; - if (!ici->ops->get_parm) - ici->ops->get_parm = default_g_parm; - if (!ici->ops->enum_framesizes) - ici->ops->enum_framesizes = default_enum_framesizes; - - mutex_lock(&list_lock); - list_for_each_entry(ix, &hosts, list) { - if (ix->nr == ici->nr) { - ret = -EBUSY; - goto edevreg; - } - } - - ret = v4l2_device_register(ici->v4l2_dev.dev, &ici->v4l2_dev); - if (ret < 0) - goto edevreg; - - list_add_tail(&ici->list, &hosts); - mutex_unlock(&list_lock); - - mutex_init(&ici->host_lock); - mutex_init(&ici->clk_lock); - - if (ici->v4l2_dev.dev->of_node) - scan_of_host(ici); - else if (ici->asd_sizes) - /* - * No OF, host with a list of subdevices. Don't try to mix - * modes by initialising some groups statically and some - * dynamically! - */ - scan_async_host(ici); - else - /* Legacy: static platform devices from board data */ - scan_add_host(ici); - - return 0; - -edevreg: - mutex_unlock(&list_lock); - return ret; -} -EXPORT_SYMBOL(soc_camera_host_register); - -/* Unregister all clients! */ -void soc_camera_host_unregister(struct soc_camera_host *ici) -{ - struct soc_camera_device *icd, *tmp; - struct soc_camera_async_client *sasc; - LIST_HEAD(notifiers); - - mutex_lock(&list_lock); - list_del(&ici->list); - list_for_each_entry(icd, &devices, list) - if (icd->iface == ici->nr && icd->sasc) { - /* as long as we hold the device, sasc won't be freed */ - get_device(icd->pdev); - list_add(&icd->sasc->list, ¬ifiers); - } - mutex_unlock(&list_lock); - - list_for_each_entry(sasc, ¬ifiers, list) { - /* Must call unlocked to avoid AB-BA dead-lock */ - v4l2_async_notifier_unregister(&sasc->notifier); - v4l2_async_notifier_cleanup(&sasc->notifier); - put_device(&sasc->pdev->dev); - } - - mutex_lock(&list_lock); - - list_for_each_entry_safe(icd, tmp, &devices, list) - if (icd->iface == ici->nr) - soc_camera_remove(icd); - - mutex_unlock(&list_lock); - - v4l2_device_unregister(&ici->v4l2_dev); -} -EXPORT_SYMBOL(soc_camera_host_unregister); - -/* Image capture device */ -static int soc_camera_device_register(struct soc_camera_device *icd) -{ - struct soc_camera_device *ix; - int num = -1, i; - - mutex_lock(&list_lock); - for (i = 0; i < 256 && num < 0; i++) { - num = i; - /* Check if this index is available on this interface */ - list_for_each_entry(ix, &devices, list) { - if (ix->iface == icd->iface && ix->devnum == i) { - num = -1; - break; - } - } - } - - if (num < 0) { - /* - * ok, we have 256 cameras on this host... - * man, stay reasonable... - */ - mutex_unlock(&list_lock); - return -ENOMEM; - } - - icd->devnum = num; - icd->use_count = 0; - icd->host_priv = NULL; - - /* - * Dynamically allocated devices set the bit earlier, but it doesn't hurt setting - * it again - */ - i = to_platform_device(icd->pdev)->id; - if (i < 0) - /* One static (legacy) soc-camera platform device */ - i = 0; - if (i >= MAP_MAX_NUM) { - mutex_unlock(&list_lock); - return -EBUSY; - } - set_bit(i, device_map); - list_add_tail(&icd->list, &devices); - mutex_unlock(&list_lock); - - return 0; -} - -static const struct v4l2_ioctl_ops soc_camera_ioctl_ops = { - .vidioc_querycap = soc_camera_querycap, - .vidioc_try_fmt_vid_cap = soc_camera_try_fmt_vid_cap, - .vidioc_g_fmt_vid_cap = soc_camera_g_fmt_vid_cap, - .vidioc_s_fmt_vid_cap = soc_camera_s_fmt_vid_cap, - .vidioc_enum_fmt_vid_cap = soc_camera_enum_fmt_vid_cap, - .vidioc_enum_input = soc_camera_enum_input, - .vidioc_g_input = soc_camera_g_input, - .vidioc_s_input = soc_camera_s_input, - .vidioc_s_std = soc_camera_s_std, - .vidioc_g_std = soc_camera_g_std, - .vidioc_enum_framesizes = soc_camera_enum_framesizes, - .vidioc_reqbufs = soc_camera_reqbufs, - .vidioc_querybuf = soc_camera_querybuf, - .vidioc_qbuf = soc_camera_qbuf, - .vidioc_dqbuf = soc_camera_dqbuf, - .vidioc_create_bufs = soc_camera_create_bufs, - .vidioc_prepare_buf = soc_camera_prepare_buf, - .vidioc_expbuf = soc_camera_expbuf, - .vidioc_streamon = soc_camera_streamon, - .vidioc_streamoff = soc_camera_streamoff, - .vidioc_g_selection = soc_camera_g_selection, - .vidioc_s_selection = soc_camera_s_selection, - .vidioc_g_parm = soc_camera_g_parm, - .vidioc_s_parm = soc_camera_s_parm, -}; - -static int video_dev_create(struct soc_camera_device *icd) -{ - struct soc_camera_host *ici = to_soc_camera_host(icd->parent); - struct video_device *vdev = video_device_alloc(); - - if (!vdev) - return -ENOMEM; - - strscpy(vdev->name, ici->drv_name, sizeof(vdev->name)); - - vdev->v4l2_dev = &ici->v4l2_dev; - vdev->fops = &soc_camera_fops; - vdev->ioctl_ops = &soc_camera_ioctl_ops; - vdev->release = video_device_release; - vdev->ctrl_handler = &icd->ctrl_handler; - vdev->lock = &ici->host_lock; - - icd->vdev = vdev; - - return 0; -} - -/* - * Called from soc_camera_probe() above with .host_lock held - */ -static int soc_camera_video_start(struct soc_camera_device *icd) -{ - const struct device_type *type = icd->vdev->dev.type; - int ret; - - if (!icd->parent) - return -ENODEV; - - video_set_drvdata(icd->vdev, icd); - if (icd->vdev->tvnorms == 0) { - /* disable the STD API if there are no tvnorms defined */ - v4l2_disable_ioctl(icd->vdev, VIDIOC_G_STD); - v4l2_disable_ioctl(icd->vdev, VIDIOC_S_STD); - v4l2_disable_ioctl(icd->vdev, VIDIOC_ENUMSTD); - } - ret = video_register_device(icd->vdev, VFL_TYPE_VIDEO, -1); - if (ret < 0) { - dev_err(icd->pdev, "video_register_device failed: %d\n", ret); - return ret; - } - - /* Restore device type, possibly set by the subdevice driver */ - icd->vdev->dev.type = type; - - return 0; -} - -static int soc_camera_pdrv_probe(struct platform_device *pdev) -{ - struct soc_camera_desc *sdesc = pdev->dev.platform_data; - struct soc_camera_subdev_desc *ssdd = &sdesc->subdev_desc; - struct soc_camera_device *icd; - int ret; - - if (!sdesc) - return -EINVAL; - - icd = devm_kzalloc(&pdev->dev, sizeof(*icd), GFP_KERNEL); - if (!icd) - return -ENOMEM; - - /* - * In the asynchronous case ssdd->num_regulators == 0 yet, so, the below - * regulator allocation is a dummy. They are actually requested by the - * subdevice driver, using soc_camera_power_init(). Also note, that in - * that case regulators are attached to the I2C device and not to the - * camera platform device. - */ - ret = devm_regulator_bulk_get(&pdev->dev, ssdd->sd_pdata.num_regulators, - ssdd->sd_pdata.regulators); - if (ret < 0) - return ret; - - icd->iface = sdesc->host_desc.bus_id; - icd->sdesc = sdesc; - icd->pdev = &pdev->dev; - platform_set_drvdata(pdev, icd); - - icd->user_width = DEFAULT_WIDTH; - icd->user_height = DEFAULT_HEIGHT; - - return soc_camera_device_register(icd); -} - -/* - * Only called on rmmod for each platform device, since they are not - * hot-pluggable. Now we know, that all our users - hosts and devices have - * been unloaded already - */ -static int soc_camera_pdrv_remove(struct platform_device *pdev) -{ - struct soc_camera_device *icd = platform_get_drvdata(pdev); - int i; - - if (!icd) - return -EINVAL; - - i = pdev->id; - if (i < 0) - i = 0; - - /* - * In synchronous mode with static platform devices this is called in a - * loop from drivers/base/dd.c::driver_detach(), no parallel execution, - * no need to lock. In asynchronous case the caller - - * soc_camera_host_unregister() - already holds the lock - */ - if (test_bit(i, device_map)) { - clear_bit(i, device_map); - list_del(&icd->list); - } - - return 0; -} - -static struct platform_driver __refdata soc_camera_pdrv = { - .probe = soc_camera_pdrv_probe, - .remove = soc_camera_pdrv_remove, - .driver = { - .name = "soc-camera-pdrv", - }, -}; - -module_platform_driver(soc_camera_pdrv); - -MODULE_DESCRIPTION("Image capture bus driver"); -MODULE_AUTHOR("Guennadi Liakhovetski "); -MODULE_LICENSE("GPL"); -MODULE_ALIAS("platform:soc-camera-pdrv"); diff --git a/drivers/staging/media/soc_camera/soc_mediabus.c b/drivers/staging/media/soc_camera/soc_mediabus.c deleted file mode 100644 index 2aa646c89c1fe..0000000000000 --- a/drivers/staging/media/soc_camera/soc_mediabus.c +++ /dev/null @@ -1,529 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * soc-camera media bus helper routines - * - * Copyright (C) 2009, Guennadi Liakhovetski - */ -#include -#include - -#include -#include -#include - -static const struct soc_mbus_lookup mbus_fmt[] = { -{ - .code = MEDIA_BUS_FMT_YUYV8_2X8, - .fmt = { - .fourcc = V4L2_PIX_FMT_YUYV, - .name = "YUYV", - .bits_per_sample = 8, - .packing = SOC_MBUS_PACKING_2X8_PADHI, - .order = SOC_MBUS_ORDER_LE, - .layout = SOC_MBUS_LAYOUT_PACKED, - }, -}, { - .code = MEDIA_BUS_FMT_YVYU8_2X8, - .fmt = { - .fourcc = V4L2_PIX_FMT_YVYU, - .name = "YVYU", - .bits_per_sample = 8, - .packing = SOC_MBUS_PACKING_2X8_PADHI, - .order = SOC_MBUS_ORDER_LE, - .layout = SOC_MBUS_LAYOUT_PACKED, - }, -}, { - .code = MEDIA_BUS_FMT_UYVY8_2X8, - .fmt = { - .fourcc = V4L2_PIX_FMT_UYVY, - .name = "UYVY", - .bits_per_sample = 8, - .packing = SOC_MBUS_PACKING_2X8_PADHI, - .order = SOC_MBUS_ORDER_LE, - .layout = SOC_MBUS_LAYOUT_PACKED, - }, -}, { - .code = MEDIA_BUS_FMT_VYUY8_2X8, - .fmt = { - .fourcc = V4L2_PIX_FMT_VYUY, - .name = "VYUY", - .bits_per_sample = 8, - .packing = SOC_MBUS_PACKING_2X8_PADHI, - .order = SOC_MBUS_ORDER_LE, - .layout = SOC_MBUS_LAYOUT_PACKED, - }, -}, { - .code = MEDIA_BUS_FMT_RGB555_2X8_PADHI_LE, - .fmt = { - .fourcc = V4L2_PIX_FMT_RGB555, - .name = "RGB555", - .bits_per_sample = 8, - .packing = SOC_MBUS_PACKING_2X8_PADHI, - .order = SOC_MBUS_ORDER_LE, - .layout = SOC_MBUS_LAYOUT_PACKED, - }, -}, { - .code = MEDIA_BUS_FMT_RGB555_2X8_PADHI_BE, - .fmt = { - .fourcc = V4L2_PIX_FMT_RGB555X, - .name = "RGB555X", - .bits_per_sample = 8, - .packing = SOC_MBUS_PACKING_2X8_PADHI, - .order = SOC_MBUS_ORDER_BE, - .layout = SOC_MBUS_LAYOUT_PACKED, - }, -}, { - .code = MEDIA_BUS_FMT_RGB565_2X8_LE, - .fmt = { - .fourcc = V4L2_PIX_FMT_RGB565, - .name = "RGB565", - .bits_per_sample = 8, - .packing = SOC_MBUS_PACKING_2X8_PADHI, - .order = SOC_MBUS_ORDER_LE, - .layout = SOC_MBUS_LAYOUT_PACKED, - }, -}, { - .code = MEDIA_BUS_FMT_RGB565_2X8_BE, - .fmt = { - .fourcc = V4L2_PIX_FMT_RGB565X, - .name = "RGB565X", - .bits_per_sample = 8, - .packing = SOC_MBUS_PACKING_2X8_PADHI, - .order = SOC_MBUS_ORDER_BE, - .layout = SOC_MBUS_LAYOUT_PACKED, - }, -}, { - .code = MEDIA_BUS_FMT_RGB666_1X18, - .fmt = { - .fourcc = V4L2_PIX_FMT_RGB32, - .name = "RGB666/32bpp", - .bits_per_sample = 18, - .packing = SOC_MBUS_PACKING_EXTEND32, - .order = SOC_MBUS_ORDER_LE, - }, -}, { - .code = MEDIA_BUS_FMT_RGB888_1X24, - .fmt = { - .fourcc = V4L2_PIX_FMT_RGB32, - .name = "RGB888/32bpp", - .bits_per_sample = 24, - .packing = SOC_MBUS_PACKING_EXTEND32, - .order = SOC_MBUS_ORDER_LE, - }, -}, { - .code = MEDIA_BUS_FMT_RGB888_2X12_BE, - .fmt = { - .fourcc = V4L2_PIX_FMT_RGB32, - .name = "RGB888/32bpp", - .bits_per_sample = 12, - .packing = SOC_MBUS_PACKING_EXTEND32, - .order = SOC_MBUS_ORDER_BE, - }, -}, { - .code = MEDIA_BUS_FMT_RGB888_2X12_LE, - .fmt = { - .fourcc = V4L2_PIX_FMT_RGB32, - .name = "RGB888/32bpp", - .bits_per_sample = 12, - .packing = SOC_MBUS_PACKING_EXTEND32, - .order = SOC_MBUS_ORDER_LE, - }, -}, { - .code = MEDIA_BUS_FMT_SBGGR8_1X8, - .fmt = { - .fourcc = V4L2_PIX_FMT_SBGGR8, - .name = "Bayer 8 BGGR", - .bits_per_sample = 8, - .packing = SOC_MBUS_PACKING_NONE, - .order = SOC_MBUS_ORDER_LE, - .layout = SOC_MBUS_LAYOUT_PACKED, - }, -}, { - .code = MEDIA_BUS_FMT_SBGGR10_1X10, - .fmt = { - .fourcc = V4L2_PIX_FMT_SBGGR10, - .name = "Bayer 10 BGGR", - .bits_per_sample = 10, - .packing = SOC_MBUS_PACKING_EXTEND16, - .order = SOC_MBUS_ORDER_LE, - .layout = SOC_MBUS_LAYOUT_PACKED, - }, -}, { - .code = MEDIA_BUS_FMT_Y8_1X8, - .fmt = { - .fourcc = V4L2_PIX_FMT_GREY, - .name = "Grey", - .bits_per_sample = 8, - .packing = SOC_MBUS_PACKING_NONE, - .order = SOC_MBUS_ORDER_LE, - .layout = SOC_MBUS_LAYOUT_PACKED, - }, -}, { - .code = MEDIA_BUS_FMT_Y10_1X10, - .fmt = { - .fourcc = V4L2_PIX_FMT_Y10, - .name = "Grey 10bit", - .bits_per_sample = 10, - .packing = SOC_MBUS_PACKING_EXTEND16, - .order = SOC_MBUS_ORDER_LE, - .layout = SOC_MBUS_LAYOUT_PACKED, - }, -}, { - .code = MEDIA_BUS_FMT_SBGGR10_2X8_PADHI_LE, - .fmt = { - .fourcc = V4L2_PIX_FMT_SBGGR10, - .name = "Bayer 10 BGGR", - .bits_per_sample = 8, - .packing = SOC_MBUS_PACKING_2X8_PADHI, - .order = SOC_MBUS_ORDER_LE, - .layout = SOC_MBUS_LAYOUT_PACKED, - }, -}, { - .code = MEDIA_BUS_FMT_SBGGR10_2X8_PADLO_LE, - .fmt = { - .fourcc = V4L2_PIX_FMT_SBGGR10, - .name = "Bayer 10 BGGR", - .bits_per_sample = 8, - .packing = SOC_MBUS_PACKING_2X8_PADLO, - .order = SOC_MBUS_ORDER_LE, - .layout = SOC_MBUS_LAYOUT_PACKED, - }, -}, { - .code = MEDIA_BUS_FMT_SBGGR10_2X8_PADHI_BE, - .fmt = { - .fourcc = V4L2_PIX_FMT_SBGGR10, - .name = "Bayer 10 BGGR", - .bits_per_sample = 8, - .packing = SOC_MBUS_PACKING_2X8_PADHI, - .order = SOC_MBUS_ORDER_BE, - .layout = SOC_MBUS_LAYOUT_PACKED, - }, -}, { - .code = MEDIA_BUS_FMT_SBGGR10_2X8_PADLO_BE, - .fmt = { - .fourcc = V4L2_PIX_FMT_SBGGR10, - .name = "Bayer 10 BGGR", - .bits_per_sample = 8, - .packing = SOC_MBUS_PACKING_2X8_PADLO, - .order = SOC_MBUS_ORDER_BE, - .layout = SOC_MBUS_LAYOUT_PACKED, - }, -}, { - .code = MEDIA_BUS_FMT_JPEG_1X8, - .fmt = { - .fourcc = V4L2_PIX_FMT_JPEG, - .name = "JPEG", - .bits_per_sample = 8, - .packing = SOC_MBUS_PACKING_VARIABLE, - .order = SOC_MBUS_ORDER_LE, - .layout = SOC_MBUS_LAYOUT_PACKED, - }, -}, { - .code = MEDIA_BUS_FMT_RGB444_2X8_PADHI_BE, - .fmt = { - .fourcc = V4L2_PIX_FMT_RGB444, - .name = "RGB444", - .bits_per_sample = 8, - .packing = SOC_MBUS_PACKING_2X8_PADHI, - .order = SOC_MBUS_ORDER_BE, - .layout = SOC_MBUS_LAYOUT_PACKED, - }, -}, { - .code = MEDIA_BUS_FMT_YUYV8_1_5X8, - .fmt = { - .fourcc = V4L2_PIX_FMT_YUV420, - .name = "YUYV 4:2:0", - .bits_per_sample = 8, - .packing = SOC_MBUS_PACKING_1_5X8, - .order = SOC_MBUS_ORDER_LE, - .layout = SOC_MBUS_LAYOUT_PACKED, - }, -}, { - .code = MEDIA_BUS_FMT_YVYU8_1_5X8, - .fmt = { - .fourcc = V4L2_PIX_FMT_YVU420, - .name = "YVYU 4:2:0", - .bits_per_sample = 8, - .packing = SOC_MBUS_PACKING_1_5X8, - .order = SOC_MBUS_ORDER_LE, - .layout = SOC_MBUS_LAYOUT_PACKED, - }, -}, { - .code = MEDIA_BUS_FMT_UYVY8_1X16, - .fmt = { - .fourcc = V4L2_PIX_FMT_UYVY, - .name = "UYVY 16bit", - .bits_per_sample = 16, - .packing = SOC_MBUS_PACKING_EXTEND16, - .order = SOC_MBUS_ORDER_LE, - .layout = SOC_MBUS_LAYOUT_PACKED, - }, -}, { - .code = MEDIA_BUS_FMT_VYUY8_1X16, - .fmt = { - .fourcc = V4L2_PIX_FMT_VYUY, - .name = "VYUY 16bit", - .bits_per_sample = 16, - .packing = SOC_MBUS_PACKING_EXTEND16, - .order = SOC_MBUS_ORDER_LE, - .layout = SOC_MBUS_LAYOUT_PACKED, - }, -}, { - .code = MEDIA_BUS_FMT_YUYV8_1X16, - .fmt = { - .fourcc = V4L2_PIX_FMT_YUYV, - .name = "YUYV 16bit", - .bits_per_sample = 16, - .packing = SOC_MBUS_PACKING_EXTEND16, - .order = SOC_MBUS_ORDER_LE, - .layout = SOC_MBUS_LAYOUT_PACKED, - }, -}, { - .code = MEDIA_BUS_FMT_YVYU8_1X16, - .fmt = { - .fourcc = V4L2_PIX_FMT_YVYU, - .name = "YVYU 16bit", - .bits_per_sample = 16, - .packing = SOC_MBUS_PACKING_EXTEND16, - .order = SOC_MBUS_ORDER_LE, - .layout = SOC_MBUS_LAYOUT_PACKED, - }, -}, { - .code = MEDIA_BUS_FMT_SGRBG8_1X8, - .fmt = { - .fourcc = V4L2_PIX_FMT_SGRBG8, - .name = "Bayer 8 GRBG", - .bits_per_sample = 8, - .packing = SOC_MBUS_PACKING_NONE, - .order = SOC_MBUS_ORDER_LE, - .layout = SOC_MBUS_LAYOUT_PACKED, - }, -}, { - .code = MEDIA_BUS_FMT_SGRBG10_DPCM8_1X8, - .fmt = { - .fourcc = V4L2_PIX_FMT_SGRBG10DPCM8, - .name = "Bayer 10 BGGR DPCM 8", - .bits_per_sample = 8, - .packing = SOC_MBUS_PACKING_NONE, - .order = SOC_MBUS_ORDER_LE, - .layout = SOC_MBUS_LAYOUT_PACKED, - }, -}, { - .code = MEDIA_BUS_FMT_SGBRG10_1X10, - .fmt = { - .fourcc = V4L2_PIX_FMT_SGBRG10, - .name = "Bayer 10 GBRG", - .bits_per_sample = 10, - .packing = SOC_MBUS_PACKING_EXTEND16, - .order = SOC_MBUS_ORDER_LE, - .layout = SOC_MBUS_LAYOUT_PACKED, - }, -}, { - .code = MEDIA_BUS_FMT_SGRBG10_1X10, - .fmt = { - .fourcc = V4L2_PIX_FMT_SGRBG10, - .name = "Bayer 10 GRBG", - .bits_per_sample = 10, - .packing = SOC_MBUS_PACKING_EXTEND16, - .order = SOC_MBUS_ORDER_LE, - .layout = SOC_MBUS_LAYOUT_PACKED, - }, -}, { - .code = MEDIA_BUS_FMT_SRGGB10_1X10, - .fmt = { - .fourcc = V4L2_PIX_FMT_SRGGB10, - .name = "Bayer 10 RGGB", - .bits_per_sample = 10, - .packing = SOC_MBUS_PACKING_EXTEND16, - .order = SOC_MBUS_ORDER_LE, - .layout = SOC_MBUS_LAYOUT_PACKED, - }, -}, { - .code = MEDIA_BUS_FMT_SBGGR12_1X12, - .fmt = { - .fourcc = V4L2_PIX_FMT_SBGGR12, - .name = "Bayer 12 BGGR", - .bits_per_sample = 12, - .packing = SOC_MBUS_PACKING_EXTEND16, - .order = SOC_MBUS_ORDER_LE, - .layout = SOC_MBUS_LAYOUT_PACKED, - }, -}, { - .code = MEDIA_BUS_FMT_SGBRG12_1X12, - .fmt = { - .fourcc = V4L2_PIX_FMT_SGBRG12, - .name = "Bayer 12 GBRG", - .bits_per_sample = 12, - .packing = SOC_MBUS_PACKING_EXTEND16, - .order = SOC_MBUS_ORDER_LE, - .layout = SOC_MBUS_LAYOUT_PACKED, - }, -}, { - .code = MEDIA_BUS_FMT_SGRBG12_1X12, - .fmt = { - .fourcc = V4L2_PIX_FMT_SGRBG12, - .name = "Bayer 12 GRBG", - .bits_per_sample = 12, - .packing = SOC_MBUS_PACKING_EXTEND16, - .order = SOC_MBUS_ORDER_LE, - .layout = SOC_MBUS_LAYOUT_PACKED, - }, -}, { - .code = MEDIA_BUS_FMT_SRGGB12_1X12, - .fmt = { - .fourcc = V4L2_PIX_FMT_SRGGB12, - .name = "Bayer 12 RGGB", - .bits_per_sample = 12, - .packing = SOC_MBUS_PACKING_EXTEND16, - .order = SOC_MBUS_ORDER_LE, - .layout = SOC_MBUS_LAYOUT_PACKED, - }, -}, -}; - -int soc_mbus_samples_per_pixel(const struct soc_mbus_pixelfmt *mf, - unsigned int *numerator, unsigned int *denominator) -{ - switch (mf->packing) { - case SOC_MBUS_PACKING_NONE: - case SOC_MBUS_PACKING_EXTEND16: - *numerator = 1; - *denominator = 1; - return 0; - case SOC_MBUS_PACKING_EXTEND32: - *numerator = 1; - *denominator = 1; - return 0; - case SOC_MBUS_PACKING_2X8_PADHI: - case SOC_MBUS_PACKING_2X8_PADLO: - *numerator = 2; - *denominator = 1; - return 0; - case SOC_MBUS_PACKING_1_5X8: - *numerator = 3; - *denominator = 2; - return 0; - case SOC_MBUS_PACKING_VARIABLE: - *numerator = 0; - *denominator = 1; - return 0; - } - return -EINVAL; -} -EXPORT_SYMBOL(soc_mbus_samples_per_pixel); - -s32 soc_mbus_bytes_per_line(u32 width, const struct soc_mbus_pixelfmt *mf) -{ - if (mf->layout != SOC_MBUS_LAYOUT_PACKED) - return width * mf->bits_per_sample / 8; - - switch (mf->packing) { - case SOC_MBUS_PACKING_NONE: - return width * mf->bits_per_sample / 8; - case SOC_MBUS_PACKING_2X8_PADHI: - case SOC_MBUS_PACKING_2X8_PADLO: - case SOC_MBUS_PACKING_EXTEND16: - return width * 2; - case SOC_MBUS_PACKING_1_5X8: - return width * 3 / 2; - case SOC_MBUS_PACKING_VARIABLE: - return 0; - case SOC_MBUS_PACKING_EXTEND32: - return width * 4; - } - return -EINVAL; -} -EXPORT_SYMBOL(soc_mbus_bytes_per_line); - -s32 soc_mbus_image_size(const struct soc_mbus_pixelfmt *mf, - u32 bytes_per_line, u32 height) -{ - if (mf->layout == SOC_MBUS_LAYOUT_PACKED) - return bytes_per_line * height; - - switch (mf->packing) { - case SOC_MBUS_PACKING_2X8_PADHI: - case SOC_MBUS_PACKING_2X8_PADLO: - return bytes_per_line * height * 2; - case SOC_MBUS_PACKING_1_5X8: - return bytes_per_line * height * 3 / 2; - default: - return -EINVAL; - } -} -EXPORT_SYMBOL(soc_mbus_image_size); - -const struct soc_mbus_pixelfmt *soc_mbus_find_fmtdesc( - u32 code, - const struct soc_mbus_lookup *lookup, - int n) -{ - int i; - - for (i = 0; i < n; i++) - if (lookup[i].code == code) - return &lookup[i].fmt; - - return NULL; -} -EXPORT_SYMBOL(soc_mbus_find_fmtdesc); - -const struct soc_mbus_pixelfmt *soc_mbus_get_fmtdesc( - u32 code) -{ - return soc_mbus_find_fmtdesc(code, mbus_fmt, ARRAY_SIZE(mbus_fmt)); -} -EXPORT_SYMBOL(soc_mbus_get_fmtdesc); - -unsigned int soc_mbus_config_compatible(const struct v4l2_mbus_config *cfg, - unsigned int flags) -{ - unsigned long common_flags; - bool hsync = true, vsync = true, pclk, data, mode; - bool mipi_lanes, mipi_clock; - - common_flags = cfg->flags & flags; - - switch (cfg->type) { - case V4L2_MBUS_PARALLEL: - hsync = common_flags & (V4L2_MBUS_HSYNC_ACTIVE_HIGH | - V4L2_MBUS_HSYNC_ACTIVE_LOW); - vsync = common_flags & (V4L2_MBUS_VSYNC_ACTIVE_HIGH | - V4L2_MBUS_VSYNC_ACTIVE_LOW); - /* fall through */ - case V4L2_MBUS_BT656: - pclk = common_flags & (V4L2_MBUS_PCLK_SAMPLE_RISING | - V4L2_MBUS_PCLK_SAMPLE_FALLING); - data = common_flags & (V4L2_MBUS_DATA_ACTIVE_HIGH | - V4L2_MBUS_DATA_ACTIVE_LOW); - mode = common_flags & (V4L2_MBUS_MASTER | V4L2_MBUS_SLAVE); - return (!hsync || !vsync || !pclk || !data || !mode) ? - 0 : common_flags; - case V4L2_MBUS_CSI2_DPHY: - mipi_lanes = common_flags & V4L2_MBUS_CSI2_LANES; - mipi_clock = common_flags & (V4L2_MBUS_CSI2_NONCONTINUOUS_CLOCK | - V4L2_MBUS_CSI2_CONTINUOUS_CLOCK); - return (!mipi_lanes || !mipi_clock) ? 0 : common_flags; - default: - WARN_ON(1); - return -EINVAL; - } - return 0; -} -EXPORT_SYMBOL(soc_mbus_config_compatible); - -static int __init soc_mbus_init(void) -{ - return 0; -} - -static void __exit soc_mbus_exit(void) -{ -} - -module_init(soc_mbus_init); -module_exit(soc_mbus_exit); - -MODULE_DESCRIPTION("soc-camera media bus interface"); -MODULE_AUTHOR("Guennadi Liakhovetski "); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/staging/media/soc_camera/soc_mt9v022.c b/drivers/staging/media/soc_camera/soc_mt9v022.c deleted file mode 100644 index 1739a618846da..0000000000000 --- a/drivers/staging/media/soc_camera/soc_mt9v022.c +++ /dev/null @@ -1,1008 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Driver for MT9V022 CMOS Image Sensor from Micron - * - * Copyright (C) 2008, Guennadi Liakhovetski - */ -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -/* - * mt9v022 i2c address 0x48, 0x4c, 0x58, 0x5c - * The platform has to define struct i2c_board_info objects and link to them - * from struct soc_camera_host_desc - */ - -static char *sensor_type; -module_param(sensor_type, charp, S_IRUGO); -MODULE_PARM_DESC(sensor_type, "Sensor type: \"colour\" or \"monochrome\""); - -/* mt9v022 selected register addresses */ -#define MT9V022_CHIP_VERSION 0x00 -#define MT9V022_COLUMN_START 0x01 -#define MT9V022_ROW_START 0x02 -#define MT9V022_WINDOW_HEIGHT 0x03 -#define MT9V022_WINDOW_WIDTH 0x04 -#define MT9V022_HORIZONTAL_BLANKING 0x05 -#define MT9V022_VERTICAL_BLANKING 0x06 -#define MT9V022_CHIP_CONTROL 0x07 -#define MT9V022_SHUTTER_WIDTH1 0x08 -#define MT9V022_SHUTTER_WIDTH2 0x09 -#define MT9V022_SHUTTER_WIDTH_CTRL 0x0a -#define MT9V022_TOTAL_SHUTTER_WIDTH 0x0b -#define MT9V022_RESET 0x0c -#define MT9V022_READ_MODE 0x0d -#define MT9V022_MONITOR_MODE 0x0e -#define MT9V022_PIXEL_OPERATION_MODE 0x0f -#define MT9V022_LED_OUT_CONTROL 0x1b -#define MT9V022_ADC_MODE_CONTROL 0x1c -#define MT9V022_REG32 0x20 -#define MT9V022_ANALOG_GAIN 0x35 -#define MT9V022_BLACK_LEVEL_CALIB_CTRL 0x47 -#define MT9V022_PIXCLK_FV_LV 0x74 -#define MT9V022_DIGITAL_TEST_PATTERN 0x7f -#define MT9V022_AEC_AGC_ENABLE 0xAF -#define MT9V022_MAX_TOTAL_SHUTTER_WIDTH 0xBD - -/* mt9v024 partial list register addresses changes with respect to mt9v022 */ -#define MT9V024_PIXCLK_FV_LV 0x72 -#define MT9V024_MAX_TOTAL_SHUTTER_WIDTH 0xAD - -/* Progressive scan, master, defaults */ -#define MT9V022_CHIP_CONTROL_DEFAULT 0x188 - -#define MT9V022_MAX_WIDTH 752 -#define MT9V022_MAX_HEIGHT 480 -#define MT9V022_MIN_WIDTH 48 -#define MT9V022_MIN_HEIGHT 32 -#define MT9V022_COLUMN_SKIP 1 -#define MT9V022_ROW_SKIP 4 - -#define MT9V022_HORIZONTAL_BLANKING_MIN 43 -#define MT9V022_HORIZONTAL_BLANKING_MAX 1023 -#define MT9V022_HORIZONTAL_BLANKING_DEF 94 -#define MT9V022_VERTICAL_BLANKING_MIN 2 -#define MT9V022_VERTICAL_BLANKING_MAX 3000 -#define MT9V022_VERTICAL_BLANKING_DEF 45 - -#define is_mt9v022_rev3(id) (id == 0x1313) -#define is_mt9v024(id) (id == 0x1324) - -/* MT9V022 has only one fixed colorspace per pixelcode */ -struct mt9v022_datafmt { - u32 code; - enum v4l2_colorspace colorspace; -}; - -/* Find a data format by a pixel code in an array */ -static const struct mt9v022_datafmt *mt9v022_find_datafmt( - u32 code, const struct mt9v022_datafmt *fmt, - int n) -{ - int i; - for (i = 0; i < n; i++) - if (fmt[i].code == code) - return fmt + i; - - return NULL; -} - -static const struct mt9v022_datafmt mt9v022_colour_fmts[] = { - /* - * Order important: first natively supported, - * second supported with a GPIO extender - */ - {MEDIA_BUS_FMT_SBGGR10_1X10, V4L2_COLORSPACE_SRGB}, - {MEDIA_BUS_FMT_SBGGR8_1X8, V4L2_COLORSPACE_SRGB}, -}; - -static const struct mt9v022_datafmt mt9v022_monochrome_fmts[] = { - /* Order important - see above */ - {MEDIA_BUS_FMT_Y10_1X10, V4L2_COLORSPACE_JPEG}, - {MEDIA_BUS_FMT_Y8_1X8, V4L2_COLORSPACE_JPEG}, -}; - -/* only registers with different addresses on different mt9v02x sensors */ -struct mt9v02x_register { - u8 max_total_shutter_width; - u8 pixclk_fv_lv; -}; - -static const struct mt9v02x_register mt9v022_register = { - .max_total_shutter_width = MT9V022_MAX_TOTAL_SHUTTER_WIDTH, - .pixclk_fv_lv = MT9V022_PIXCLK_FV_LV, -}; - -static const struct mt9v02x_register mt9v024_register = { - .max_total_shutter_width = MT9V024_MAX_TOTAL_SHUTTER_WIDTH, - .pixclk_fv_lv = MT9V024_PIXCLK_FV_LV, -}; - -enum mt9v022_model { - MT9V022IX7ATM, - MT9V022IX7ATC, -}; - -struct mt9v022 { - struct v4l2_subdev subdev; - struct v4l2_ctrl_handler hdl; - struct { - /* exposure/auto-exposure cluster */ - struct v4l2_ctrl *autoexposure; - struct v4l2_ctrl *exposure; - }; - struct { - /* gain/auto-gain cluster */ - struct v4l2_ctrl *autogain; - struct v4l2_ctrl *gain; - }; - struct v4l2_ctrl *hblank; - struct v4l2_ctrl *vblank; - struct v4l2_rect rect; /* Sensor window */ - struct v4l2_clk *clk; - const struct mt9v022_datafmt *fmt; - const struct mt9v022_datafmt *fmts; - const struct mt9v02x_register *reg; - int num_fmts; - enum mt9v022_model model; - u16 chip_control; - u16 chip_version; - unsigned short y_skip_top; /* Lines to skip at the top */ -}; - -static struct mt9v022 *to_mt9v022(const struct i2c_client *client) -{ - return container_of(i2c_get_clientdata(client), struct mt9v022, subdev); -} - -static int reg_read(struct i2c_client *client, const u8 reg) -{ - return i2c_smbus_read_word_swapped(client, reg); -} - -static int reg_write(struct i2c_client *client, const u8 reg, - const u16 data) -{ - return i2c_smbus_write_word_swapped(client, reg, data); -} - -static int reg_set(struct i2c_client *client, const u8 reg, - const u16 data) -{ - int ret; - - ret = reg_read(client, reg); - if (ret < 0) - return ret; - return reg_write(client, reg, ret | data); -} - -static int reg_clear(struct i2c_client *client, const u8 reg, - const u16 data) -{ - int ret; - - ret = reg_read(client, reg); - if (ret < 0) - return ret; - return reg_write(client, reg, ret & ~data); -} - -static int mt9v022_init(struct i2c_client *client) -{ - struct mt9v022 *mt9v022 = to_mt9v022(client); - int ret; - - /* - * Almost the default mode: master, parallel, simultaneous, and an - * undocumented bit 0x200, which is present in table 7, but not in 8, - * plus snapshot mode to disable scan for now - */ - mt9v022->chip_control |= 0x10; - ret = reg_write(client, MT9V022_CHIP_CONTROL, mt9v022->chip_control); - if (!ret) - ret = reg_write(client, MT9V022_READ_MODE, 0x300); - - /* All defaults */ - if (!ret) - /* AEC, AGC on */ - ret = reg_set(client, MT9V022_AEC_AGC_ENABLE, 0x3); - if (!ret) - ret = reg_write(client, MT9V022_ANALOG_GAIN, 16); - if (!ret) - ret = reg_write(client, MT9V022_TOTAL_SHUTTER_WIDTH, 480); - if (!ret) - ret = reg_write(client, mt9v022->reg->max_total_shutter_width, 480); - if (!ret) - /* default - auto */ - ret = reg_clear(client, MT9V022_BLACK_LEVEL_CALIB_CTRL, 1); - if (!ret) - ret = reg_write(client, MT9V022_DIGITAL_TEST_PATTERN, 0); - if (!ret) - return v4l2_ctrl_handler_setup(&mt9v022->hdl); - - return ret; -} - -static int mt9v022_s_stream(struct v4l2_subdev *sd, int enable) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct mt9v022 *mt9v022 = to_mt9v022(client); - - if (enable) { - /* Switch to master "normal" mode */ - mt9v022->chip_control &= ~0x10; - if (is_mt9v022_rev3(mt9v022->chip_version) || - is_mt9v024(mt9v022->chip_version)) { - /* - * Unset snapshot mode specific settings: clear bit 9 - * and bit 2 in reg. 0x20 when in normal mode. - */ - if (reg_clear(client, MT9V022_REG32, 0x204)) - return -EIO; - } - } else { - /* Switch to snapshot mode */ - mt9v022->chip_control |= 0x10; - if (is_mt9v022_rev3(mt9v022->chip_version) || - is_mt9v024(mt9v022->chip_version)) { - /* - * Required settings for snapshot mode: set bit 9 - * (RST enable) and bit 2 (CR enable) in reg. 0x20 - * See TechNote TN0960 or TN-09-225. - */ - if (reg_set(client, MT9V022_REG32, 0x204)) - return -EIO; - } - } - - if (reg_write(client, MT9V022_CHIP_CONTROL, mt9v022->chip_control) < 0) - return -EIO; - return 0; -} - -static int mt9v022_set_selection(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, - struct v4l2_subdev_selection *sel) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct mt9v022 *mt9v022 = to_mt9v022(client); - struct v4l2_rect rect = sel->r; - int min_row, min_blank; - int ret; - - if (sel->which != V4L2_SUBDEV_FORMAT_ACTIVE || - sel->target != V4L2_SEL_TGT_CROP) - return -EINVAL; - - /* Bayer format - even size lengths */ - if (mt9v022->fmts == mt9v022_colour_fmts) { - rect.width = ALIGN(rect.width, 2); - rect.height = ALIGN(rect.height, 2); - /* Let the user play with the starting pixel */ - } - - soc_camera_limit_side(&rect.left, &rect.width, - MT9V022_COLUMN_SKIP, MT9V022_MIN_WIDTH, MT9V022_MAX_WIDTH); - - soc_camera_limit_side(&rect.top, &rect.height, - MT9V022_ROW_SKIP, MT9V022_MIN_HEIGHT, MT9V022_MAX_HEIGHT); - - /* Like in example app. Contradicts the datasheet though */ - ret = reg_read(client, MT9V022_AEC_AGC_ENABLE); - if (ret >= 0) { - if (ret & 1) /* Autoexposure */ - ret = reg_write(client, mt9v022->reg->max_total_shutter_width, - rect.height + mt9v022->y_skip_top + 43); - /* - * If autoexposure is off, there is no need to set - * MT9V022_TOTAL_SHUTTER_WIDTH here. Autoexposure can be off - * only if the user has set exposure manually, using the - * V4L2_CID_EXPOSURE_AUTO with the value V4L2_EXPOSURE_MANUAL. - * In this case the register MT9V022_TOTAL_SHUTTER_WIDTH - * already contains the correct value. - */ - } - /* Setup frame format: defaults apart from width and height */ - if (!ret) - ret = reg_write(client, MT9V022_COLUMN_START, rect.left); - if (!ret) - ret = reg_write(client, MT9V022_ROW_START, rect.top); - /* - * mt9v022: min total row time is 660 columns, min blanking is 43 - * mt9v024: min total row time is 690 columns, min blanking is 61 - */ - if (is_mt9v024(mt9v022->chip_version)) { - min_row = 690; - min_blank = 61; - } else { - min_row = 660; - min_blank = 43; - } - if (!ret) - ret = v4l2_ctrl_s_ctrl(mt9v022->hblank, - rect.width > min_row - min_blank ? - min_blank : min_row - rect.width); - if (!ret) - ret = v4l2_ctrl_s_ctrl(mt9v022->vblank, 45); - if (!ret) - ret = reg_write(client, MT9V022_WINDOW_WIDTH, rect.width); - if (!ret) - ret = reg_write(client, MT9V022_WINDOW_HEIGHT, - rect.height + mt9v022->y_skip_top); - - if (ret < 0) - return ret; - - dev_dbg(&client->dev, "Frame %dx%d pixel\n", rect.width, rect.height); - - mt9v022->rect = rect; - - return 0; -} - -static int mt9v022_get_selection(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, - struct v4l2_subdev_selection *sel) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct mt9v022 *mt9v022 = to_mt9v022(client); - - if (sel->which != V4L2_SUBDEV_FORMAT_ACTIVE) - return -EINVAL; - - switch (sel->target) { - case V4L2_SEL_TGT_CROP_BOUNDS: - sel->r.left = MT9V022_COLUMN_SKIP; - sel->r.top = MT9V022_ROW_SKIP; - sel->r.width = MT9V022_MAX_WIDTH; - sel->r.height = MT9V022_MAX_HEIGHT; - return 0; - case V4L2_SEL_TGT_CROP: - sel->r = mt9v022->rect; - return 0; - default: - return -EINVAL; - } -} - -static int mt9v022_get_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, - struct v4l2_subdev_format *format) -{ - struct v4l2_mbus_framefmt *mf = &format->format; - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct mt9v022 *mt9v022 = to_mt9v022(client); - - if (format->pad) - return -EINVAL; - - mf->width = mt9v022->rect.width; - mf->height = mt9v022->rect.height; - mf->code = mt9v022->fmt->code; - mf->colorspace = mt9v022->fmt->colorspace; - mf->field = V4L2_FIELD_NONE; - - return 0; -} - -static int mt9v022_s_fmt(struct v4l2_subdev *sd, - const struct mt9v022_datafmt *fmt, - struct v4l2_mbus_framefmt *mf) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct mt9v022 *mt9v022 = to_mt9v022(client); - struct v4l2_subdev_selection sel = { - .which = V4L2_SUBDEV_FORMAT_ACTIVE, - .target = V4L2_SEL_TGT_CROP, - .r.left = mt9v022->rect.left, - .r.top = mt9v022->rect.top, - .r.width = mf->width, - .r.height = mf->height, - }; - int ret; - - /* - * The caller provides a supported format, as verified per call to - * .set_fmt(FORMAT_TRY), datawidth is from our supported format list - */ - switch (mf->code) { - case MEDIA_BUS_FMT_Y8_1X8: - case MEDIA_BUS_FMT_Y10_1X10: - if (mt9v022->model != MT9V022IX7ATM) - return -EINVAL; - break; - case MEDIA_BUS_FMT_SBGGR8_1X8: - case MEDIA_BUS_FMT_SBGGR10_1X10: - if (mt9v022->model != MT9V022IX7ATC) - return -EINVAL; - break; - default: - return -EINVAL; - } - - /* No support for scaling on this camera, just crop. */ - ret = mt9v022_set_selection(sd, NULL, &sel); - if (!ret) { - mf->width = mt9v022->rect.width; - mf->height = mt9v022->rect.height; - mt9v022->fmt = fmt; - mf->colorspace = fmt->colorspace; - } - - return ret; -} - -static int mt9v022_set_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, - struct v4l2_subdev_format *format) -{ - struct v4l2_mbus_framefmt *mf = &format->format; - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct mt9v022 *mt9v022 = to_mt9v022(client); - const struct mt9v022_datafmt *fmt; - int align = mf->code == MEDIA_BUS_FMT_SBGGR8_1X8 || - mf->code == MEDIA_BUS_FMT_SBGGR10_1X10; - - if (format->pad) - return -EINVAL; - - v4l_bound_align_image(&mf->width, MT9V022_MIN_WIDTH, - MT9V022_MAX_WIDTH, align, - &mf->height, MT9V022_MIN_HEIGHT + mt9v022->y_skip_top, - MT9V022_MAX_HEIGHT + mt9v022->y_skip_top, align, 0); - - fmt = mt9v022_find_datafmt(mf->code, mt9v022->fmts, - mt9v022->num_fmts); - if (!fmt) { - fmt = mt9v022->fmt; - mf->code = fmt->code; - } - - mf->colorspace = fmt->colorspace; - - if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE) - return mt9v022_s_fmt(sd, fmt, mf); - cfg->try_fmt = *mf; - return 0; -} - -#ifdef CONFIG_VIDEO_ADV_DEBUG -static int mt9v022_g_register(struct v4l2_subdev *sd, - struct v4l2_dbg_register *reg) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - - if (reg->reg > 0xff) - return -EINVAL; - - reg->size = 2; - reg->val = reg_read(client, reg->reg); - - if (reg->val > 0xffff) - return -EIO; - - return 0; -} - -static int mt9v022_s_register(struct v4l2_subdev *sd, - const struct v4l2_dbg_register *reg) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - - if (reg->reg > 0xff) - return -EINVAL; - - if (reg_write(client, reg->reg, reg->val) < 0) - return -EIO; - - return 0; -} -#endif - -static int mt9v022_s_power(struct v4l2_subdev *sd, int on) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); - struct mt9v022 *mt9v022 = to_mt9v022(client); - - return soc_camera_set_power(&client->dev, ssdd, mt9v022->clk, on); -} - -static int mt9v022_g_volatile_ctrl(struct v4l2_ctrl *ctrl) -{ - struct mt9v022 *mt9v022 = container_of(ctrl->handler, - struct mt9v022, hdl); - struct v4l2_subdev *sd = &mt9v022->subdev; - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct v4l2_ctrl *gain = mt9v022->gain; - struct v4l2_ctrl *exp = mt9v022->exposure; - unsigned long range; - int data; - - switch (ctrl->id) { - case V4L2_CID_AUTOGAIN: - data = reg_read(client, MT9V022_ANALOG_GAIN); - if (data < 0) - return -EIO; - - range = gain->maximum - gain->minimum; - gain->val = ((data - 16) * range + 24) / 48 + gain->minimum; - return 0; - case V4L2_CID_EXPOSURE_AUTO: - data = reg_read(client, MT9V022_TOTAL_SHUTTER_WIDTH); - if (data < 0) - return -EIO; - - range = exp->maximum - exp->minimum; - exp->val = ((data - 1) * range + 239) / 479 + exp->minimum; - return 0; - case V4L2_CID_HBLANK: - data = reg_read(client, MT9V022_HORIZONTAL_BLANKING); - if (data < 0) - return -EIO; - ctrl->val = data; - return 0; - case V4L2_CID_VBLANK: - data = reg_read(client, MT9V022_VERTICAL_BLANKING); - if (data < 0) - return -EIO; - ctrl->val = data; - return 0; - } - return -EINVAL; -} - -static int mt9v022_s_ctrl(struct v4l2_ctrl *ctrl) -{ - struct mt9v022 *mt9v022 = container_of(ctrl->handler, - struct mt9v022, hdl); - struct v4l2_subdev *sd = &mt9v022->subdev; - struct i2c_client *client = v4l2_get_subdevdata(sd); - int data; - - switch (ctrl->id) { - case V4L2_CID_VFLIP: - if (ctrl->val) - data = reg_set(client, MT9V022_READ_MODE, 0x10); - else - data = reg_clear(client, MT9V022_READ_MODE, 0x10); - if (data < 0) - return -EIO; - return 0; - case V4L2_CID_HFLIP: - if (ctrl->val) - data = reg_set(client, MT9V022_READ_MODE, 0x20); - else - data = reg_clear(client, MT9V022_READ_MODE, 0x20); - if (data < 0) - return -EIO; - return 0; - case V4L2_CID_AUTOGAIN: - if (ctrl->val) { - if (reg_set(client, MT9V022_AEC_AGC_ENABLE, 0x2) < 0) - return -EIO; - } else { - struct v4l2_ctrl *gain = mt9v022->gain; - /* mt9v022 has minimum == default */ - unsigned long range = gain->maximum - gain->minimum; - /* Valid values 16 to 64, 32 to 64 must be even. */ - unsigned long gain_val = ((gain->val - (s32)gain->minimum) * - 48 + range / 2) / range + 16; - - if (gain_val >= 32) - gain_val &= ~1; - - /* - * The user wants to set gain manually, hope, she - * knows, what she's doing... Switch AGC off. - */ - if (reg_clear(client, MT9V022_AEC_AGC_ENABLE, 0x2) < 0) - return -EIO; - - dev_dbg(&client->dev, "Setting gain from %d to %lu\n", - reg_read(client, MT9V022_ANALOG_GAIN), gain_val); - if (reg_write(client, MT9V022_ANALOG_GAIN, gain_val) < 0) - return -EIO; - } - return 0; - case V4L2_CID_EXPOSURE_AUTO: - if (ctrl->val == V4L2_EXPOSURE_AUTO) { - data = reg_set(client, MT9V022_AEC_AGC_ENABLE, 0x1); - } else { - struct v4l2_ctrl *exp = mt9v022->exposure; - unsigned long range = exp->maximum - exp->minimum; - unsigned long shutter = ((exp->val - (s32)exp->minimum) * - 479 + range / 2) / range + 1; - - /* - * The user wants to set shutter width manually, hope, - * she knows, what she's doing... Switch AEC off. - */ - data = reg_clear(client, MT9V022_AEC_AGC_ENABLE, 0x1); - if (data < 0) - return -EIO; - dev_dbg(&client->dev, "Shutter width from %d to %lu\n", - reg_read(client, MT9V022_TOTAL_SHUTTER_WIDTH), - shutter); - if (reg_write(client, MT9V022_TOTAL_SHUTTER_WIDTH, - shutter) < 0) - return -EIO; - } - return 0; - case V4L2_CID_HBLANK: - if (reg_write(client, MT9V022_HORIZONTAL_BLANKING, - ctrl->val) < 0) - return -EIO; - return 0; - case V4L2_CID_VBLANK: - if (reg_write(client, MT9V022_VERTICAL_BLANKING, - ctrl->val) < 0) - return -EIO; - return 0; - } - return -EINVAL; -} - -/* - * Interface active, can use i2c. If it fails, it can indeed mean, that - * this wasn't our capture interface, so, we wait for the right one - */ -static int mt9v022_video_probe(struct i2c_client *client) -{ - struct mt9v022 *mt9v022 = to_mt9v022(client); - struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); - s32 data; - int ret; - unsigned long flags; - - ret = mt9v022_s_power(&mt9v022->subdev, 1); - if (ret < 0) - return ret; - - /* Read out the chip version register */ - data = reg_read(client, MT9V022_CHIP_VERSION); - - /* must be 0x1311, 0x1313 or 0x1324 */ - if (data != 0x1311 && data != 0x1313 && data != 0x1324) { - ret = -ENODEV; - dev_info(&client->dev, "No MT9V022 found, ID register 0x%x\n", - data); - goto ei2c; - } - - mt9v022->chip_version = data; - - mt9v022->reg = is_mt9v024(data) ? &mt9v024_register : - &mt9v022_register; - - /* Soft reset */ - ret = reg_write(client, MT9V022_RESET, 1); - if (ret < 0) - goto ei2c; - /* 15 clock cycles */ - udelay(200); - if (reg_read(client, MT9V022_RESET)) { - dev_err(&client->dev, "Resetting MT9V022 failed!\n"); - if (ret > 0) - ret = -EIO; - goto ei2c; - } - - /* Set monochrome or colour sensor type */ - if (sensor_type && (!strcmp("colour", sensor_type) || - !strcmp("color", sensor_type))) { - ret = reg_write(client, MT9V022_PIXEL_OPERATION_MODE, 4 | 0x11); - mt9v022->model = MT9V022IX7ATC; - mt9v022->fmts = mt9v022_colour_fmts; - } else { - ret = reg_write(client, MT9V022_PIXEL_OPERATION_MODE, 0x11); - mt9v022->model = MT9V022IX7ATM; - mt9v022->fmts = mt9v022_monochrome_fmts; - } - - if (ret < 0) - goto ei2c; - - mt9v022->num_fmts = 0; - - /* - * This is a 10bit sensor, so by default we only allow 10bit. - * The platform may support different bus widths due to - * different routing of the data lines. - */ - if (ssdd->query_bus_param) - flags = ssdd->query_bus_param(ssdd); - else - flags = SOCAM_DATAWIDTH_10; - - if (flags & SOCAM_DATAWIDTH_10) - mt9v022->num_fmts++; - else - mt9v022->fmts++; - - if (flags & SOCAM_DATAWIDTH_8) - mt9v022->num_fmts++; - - mt9v022->fmt = &mt9v022->fmts[0]; - - dev_info(&client->dev, "Detected a MT9V022 chip ID %x, %s sensor\n", - data, mt9v022->model == MT9V022IX7ATM ? - "monochrome" : "colour"); - - ret = mt9v022_init(client); - if (ret < 0) - dev_err(&client->dev, "Failed to initialise the camera\n"); - -ei2c: - mt9v022_s_power(&mt9v022->subdev, 0); - return ret; -} - -static int mt9v022_g_skip_top_lines(struct v4l2_subdev *sd, u32 *lines) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct mt9v022 *mt9v022 = to_mt9v022(client); - - *lines = mt9v022->y_skip_top; - - return 0; -} - -static const struct v4l2_ctrl_ops mt9v022_ctrl_ops = { - .g_volatile_ctrl = mt9v022_g_volatile_ctrl, - .s_ctrl = mt9v022_s_ctrl, -}; - -static const struct v4l2_subdev_core_ops mt9v022_subdev_core_ops = { -#ifdef CONFIG_VIDEO_ADV_DEBUG - .g_register = mt9v022_g_register, - .s_register = mt9v022_s_register, -#endif - .s_power = mt9v022_s_power, -}; - -static int mt9v022_enum_mbus_code(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, - struct v4l2_subdev_mbus_code_enum *code) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct mt9v022 *mt9v022 = to_mt9v022(client); - - if (code->pad || code->index >= mt9v022->num_fmts) - return -EINVAL; - - code->code = mt9v022->fmts[code->index].code; - return 0; -} - -static int mt9v022_g_mbus_config(struct v4l2_subdev *sd, - struct v4l2_mbus_config *cfg) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); - - cfg->flags = V4L2_MBUS_MASTER | V4L2_MBUS_SLAVE | - V4L2_MBUS_PCLK_SAMPLE_RISING | V4L2_MBUS_PCLK_SAMPLE_FALLING | - V4L2_MBUS_HSYNC_ACTIVE_HIGH | V4L2_MBUS_HSYNC_ACTIVE_LOW | - V4L2_MBUS_VSYNC_ACTIVE_HIGH | V4L2_MBUS_VSYNC_ACTIVE_LOW | - V4L2_MBUS_DATA_ACTIVE_HIGH; - cfg->type = V4L2_MBUS_PARALLEL; - cfg->flags = soc_camera_apply_board_flags(ssdd, cfg); - - return 0; -} - -static int mt9v022_s_mbus_config(struct v4l2_subdev *sd, - const struct v4l2_mbus_config *cfg) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); - struct mt9v022 *mt9v022 = to_mt9v022(client); - unsigned long flags = soc_camera_apply_board_flags(ssdd, cfg); - unsigned int bps = soc_mbus_get_fmtdesc(mt9v022->fmt->code)->bits_per_sample; - int ret; - u16 pixclk = 0; - - if (ssdd->set_bus_param) { - ret = ssdd->set_bus_param(ssdd, 1 << (bps - 1)); - if (ret) - return ret; - } else if (bps != 10) { - /* - * Without board specific bus width settings we only support the - * sensors native bus width - */ - return -EINVAL; - } - - if (flags & V4L2_MBUS_PCLK_SAMPLE_FALLING) - pixclk |= 0x10; - - if (!(flags & V4L2_MBUS_HSYNC_ACTIVE_HIGH)) - pixclk |= 0x1; - - if (!(flags & V4L2_MBUS_VSYNC_ACTIVE_HIGH)) - pixclk |= 0x2; - - ret = reg_write(client, mt9v022->reg->pixclk_fv_lv, pixclk); - if (ret < 0) - return ret; - - if (!(flags & V4L2_MBUS_MASTER)) - mt9v022->chip_control &= ~0x8; - - ret = reg_write(client, MT9V022_CHIP_CONTROL, mt9v022->chip_control); - if (ret < 0) - return ret; - - dev_dbg(&client->dev, "Calculated pixclk 0x%x, chip control 0x%x\n", - pixclk, mt9v022->chip_control); - - return 0; -} - -static const struct v4l2_subdev_video_ops mt9v022_subdev_video_ops = { - .s_stream = mt9v022_s_stream, - .g_mbus_config = mt9v022_g_mbus_config, - .s_mbus_config = mt9v022_s_mbus_config, -}; - -static const struct v4l2_subdev_sensor_ops mt9v022_subdev_sensor_ops = { - .g_skip_top_lines = mt9v022_g_skip_top_lines, -}; - -static const struct v4l2_subdev_pad_ops mt9v022_subdev_pad_ops = { - .enum_mbus_code = mt9v022_enum_mbus_code, - .get_selection = mt9v022_get_selection, - .set_selection = mt9v022_set_selection, - .get_fmt = mt9v022_get_fmt, - .set_fmt = mt9v022_set_fmt, -}; - -static const struct v4l2_subdev_ops mt9v022_subdev_ops = { - .core = &mt9v022_subdev_core_ops, - .video = &mt9v022_subdev_video_ops, - .sensor = &mt9v022_subdev_sensor_ops, - .pad = &mt9v022_subdev_pad_ops, -}; - -static int mt9v022_probe(struct i2c_client *client, - const struct i2c_device_id *did) -{ - struct mt9v022 *mt9v022; - struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); - struct i2c_adapter *adapter = client->adapter; - struct mt9v022_platform_data *pdata; - int ret; - - if (!ssdd) { - dev_err(&client->dev, "MT9V022 driver needs platform data\n"); - return -EINVAL; - } - - if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WORD_DATA)) { - dev_warn(&adapter->dev, - "I2C-Adapter doesn't support I2C_FUNC_SMBUS_WORD\n"); - return -EIO; - } - - mt9v022 = devm_kzalloc(&client->dev, sizeof(struct mt9v022), GFP_KERNEL); - if (!mt9v022) - return -ENOMEM; - - pdata = ssdd->drv_priv; - v4l2_i2c_subdev_init(&mt9v022->subdev, client, &mt9v022_subdev_ops); - v4l2_ctrl_handler_init(&mt9v022->hdl, 6); - v4l2_ctrl_new_std(&mt9v022->hdl, &mt9v022_ctrl_ops, - V4L2_CID_VFLIP, 0, 1, 1, 0); - v4l2_ctrl_new_std(&mt9v022->hdl, &mt9v022_ctrl_ops, - V4L2_CID_HFLIP, 0, 1, 1, 0); - mt9v022->autogain = v4l2_ctrl_new_std(&mt9v022->hdl, &mt9v022_ctrl_ops, - V4L2_CID_AUTOGAIN, 0, 1, 1, 1); - mt9v022->gain = v4l2_ctrl_new_std(&mt9v022->hdl, &mt9v022_ctrl_ops, - V4L2_CID_GAIN, 0, 127, 1, 64); - - /* - * Simulated autoexposure. If enabled, we calculate shutter width - * ourselves in the driver based on vertical blanking and frame width - */ - mt9v022->autoexposure = v4l2_ctrl_new_std_menu(&mt9v022->hdl, - &mt9v022_ctrl_ops, V4L2_CID_EXPOSURE_AUTO, 1, 0, - V4L2_EXPOSURE_AUTO); - mt9v022->exposure = v4l2_ctrl_new_std(&mt9v022->hdl, &mt9v022_ctrl_ops, - V4L2_CID_EXPOSURE, 1, 255, 1, 255); - - mt9v022->hblank = v4l2_ctrl_new_std(&mt9v022->hdl, &mt9v022_ctrl_ops, - V4L2_CID_HBLANK, MT9V022_HORIZONTAL_BLANKING_MIN, - MT9V022_HORIZONTAL_BLANKING_MAX, 1, - MT9V022_HORIZONTAL_BLANKING_DEF); - - mt9v022->vblank = v4l2_ctrl_new_std(&mt9v022->hdl, &mt9v022_ctrl_ops, - V4L2_CID_VBLANK, MT9V022_VERTICAL_BLANKING_MIN, - MT9V022_VERTICAL_BLANKING_MAX, 1, - MT9V022_VERTICAL_BLANKING_DEF); - - mt9v022->subdev.ctrl_handler = &mt9v022->hdl; - if (mt9v022->hdl.error) { - int err = mt9v022->hdl.error; - - dev_err(&client->dev, "control initialisation err %d\n", err); - return err; - } - v4l2_ctrl_auto_cluster(2, &mt9v022->autoexposure, - V4L2_EXPOSURE_MANUAL, true); - v4l2_ctrl_auto_cluster(2, &mt9v022->autogain, 0, true); - - mt9v022->chip_control = MT9V022_CHIP_CONTROL_DEFAULT; - - /* - * On some platforms the first read out line is corrupted. - * Workaround it by skipping if indicated by platform data. - */ - mt9v022->y_skip_top = pdata ? pdata->y_skip_top : 0; - mt9v022->rect.left = MT9V022_COLUMN_SKIP; - mt9v022->rect.top = MT9V022_ROW_SKIP; - mt9v022->rect.width = MT9V022_MAX_WIDTH; - mt9v022->rect.height = MT9V022_MAX_HEIGHT; - - mt9v022->clk = v4l2_clk_get(&client->dev, "mclk"); - if (IS_ERR(mt9v022->clk)) { - ret = PTR_ERR(mt9v022->clk); - goto eclkget; - } - - ret = mt9v022_video_probe(client); - if (ret) { - v4l2_clk_put(mt9v022->clk); -eclkget: - v4l2_ctrl_handler_free(&mt9v022->hdl); - } - - return ret; -} - -static int mt9v022_remove(struct i2c_client *client) -{ - struct mt9v022 *mt9v022 = to_mt9v022(client); - struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); - - v4l2_clk_put(mt9v022->clk); - v4l2_device_unregister_subdev(&mt9v022->subdev); - if (ssdd->free_bus) - ssdd->free_bus(ssdd); - v4l2_ctrl_handler_free(&mt9v022->hdl); - - return 0; -} -static const struct i2c_device_id mt9v022_id[] = { - { "mt9v022", 0 }, - { } -}; -MODULE_DEVICE_TABLE(i2c, mt9v022_id); - -static struct i2c_driver mt9v022_i2c_driver = { - .driver = { - .name = "mt9v022", - }, - .probe = mt9v022_probe, - .remove = mt9v022_remove, - .id_table = mt9v022_id, -}; - -module_i2c_driver(mt9v022_i2c_driver); - -MODULE_DESCRIPTION("Micron MT9V022 Camera driver"); -MODULE_AUTHOR("Guennadi Liakhovetski "); -MODULE_LICENSE("GPL"); diff --git a/drivers/staging/media/soc_camera/soc_ov5642.c b/drivers/staging/media/soc_camera/soc_ov5642.c deleted file mode 100644 index 39ae24dca65f6..0000000000000 --- a/drivers/staging/media/soc_camera/soc_ov5642.c +++ /dev/null @@ -1,1085 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Driver for OV5642 CMOS Image Sensor from Omnivision - * - * Copyright (C) 2011, Bastian Hecht - * - * Based on Sony IMX074 Camera Driver - * Copyright (C) 2010, Guennadi Liakhovetski - * - * Based on Omnivision OV7670 Camera Driver - * Copyright (C) 2006-7 Jonathan Corbet - */ -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -/* OV5642 registers */ -#define REG_CHIP_ID_HIGH 0x300a -#define REG_CHIP_ID_LOW 0x300b - -#define REG_WINDOW_START_X_HIGH 0x3800 -#define REG_WINDOW_START_X_LOW 0x3801 -#define REG_WINDOW_START_Y_HIGH 0x3802 -#define REG_WINDOW_START_Y_LOW 0x3803 -#define REG_WINDOW_WIDTH_HIGH 0x3804 -#define REG_WINDOW_WIDTH_LOW 0x3805 -#define REG_WINDOW_HEIGHT_HIGH 0x3806 -#define REG_WINDOW_HEIGHT_LOW 0x3807 -#define REG_OUT_WIDTH_HIGH 0x3808 -#define REG_OUT_WIDTH_LOW 0x3809 -#define REG_OUT_HEIGHT_HIGH 0x380a -#define REG_OUT_HEIGHT_LOW 0x380b -#define REG_OUT_TOTAL_WIDTH_HIGH 0x380c -#define REG_OUT_TOTAL_WIDTH_LOW 0x380d -#define REG_OUT_TOTAL_HEIGHT_HIGH 0x380e -#define REG_OUT_TOTAL_HEIGHT_LOW 0x380f -#define REG_OUTPUT_FORMAT 0x4300 -#define REG_ISP_CTRL_01 0x5001 -#define REG_AVG_WINDOW_END_X_HIGH 0x5682 -#define REG_AVG_WINDOW_END_X_LOW 0x5683 -#define REG_AVG_WINDOW_END_Y_HIGH 0x5686 -#define REG_AVG_WINDOW_END_Y_LOW 0x5687 - -/* active pixel array size */ -#define OV5642_SENSOR_SIZE_X 2592 -#define OV5642_SENSOR_SIZE_Y 1944 - -/* - * About OV5642 resolution, cropping and binning: - * This sensor supports it all, at least in the feature description. - * Unfortunately, no combination of appropriate registers settings could make - * the chip work the intended way. As it works with predefined register lists, - * some undocumented registers are presumably changed there to achieve their - * goals. - * This driver currently only works for resolutions up to 720 lines with a - * 1:1 scale. Hopefully these restrictions will be removed in the future. - */ -#define OV5642_MAX_WIDTH OV5642_SENSOR_SIZE_X -#define OV5642_MAX_HEIGHT 720 - -/* default sizes */ -#define OV5642_DEFAULT_WIDTH 1280 -#define OV5642_DEFAULT_HEIGHT OV5642_MAX_HEIGHT - -/* minimum extra blanking */ -#define BLANKING_EXTRA_WIDTH 500 -#define BLANKING_EXTRA_HEIGHT 20 - -/* - * the sensor's autoexposure is buggy when setting total_height low. - * It tries to expose longer than 1 frame period without taking care of it - * and this leads to weird output. So we set 1000 lines as minimum. - */ -#define BLANKING_MIN_HEIGHT 1000 - -struct regval_list { - u16 reg_num; - u8 value; -}; - -static struct regval_list ov5642_default_regs_init[] = { - { 0x3103, 0x93 }, - { 0x3008, 0x82 }, - { 0x3017, 0x7f }, - { 0x3018, 0xfc }, - { 0x3810, 0xc2 }, - { 0x3615, 0xf0 }, - { 0x3000, 0x0 }, - { 0x3001, 0x0 }, - { 0x3002, 0x0 }, - { 0x3003, 0x0 }, - { 0x3004, 0xff }, - { 0x3030, 0x2b }, - { 0x3011, 0x8 }, - { 0x3010, 0x10 }, - { 0x3604, 0x60 }, - { 0x3622, 0x60 }, - { 0x3621, 0x9 }, - { 0x3709, 0x0 }, - { 0x4000, 0x21 }, - { 0x401d, 0x22 }, - { 0x3600, 0x54 }, - { 0x3605, 0x4 }, - { 0x3606, 0x3f }, - { 0x3c01, 0x80 }, - { 0x300d, 0x22 }, - { 0x3623, 0x22 }, - { 0x5000, 0x4f }, - { 0x5020, 0x4 }, - { 0x5181, 0x79 }, - { 0x5182, 0x0 }, - { 0x5185, 0x22 }, - { 0x5197, 0x1 }, - { 0x5500, 0xa }, - { 0x5504, 0x0 }, - { 0x5505, 0x7f }, - { 0x5080, 0x8 }, - { 0x300e, 0x18 }, - { 0x4610, 0x0 }, - { 0x471d, 0x5 }, - { 0x4708, 0x6 }, - { 0x370c, 0xa0 }, - { 0x5687, 0x94 }, - { 0x501f, 0x0 }, - { 0x5000, 0x4f }, - { 0x5001, 0xcf }, - { 0x4300, 0x30 }, - { 0x4300, 0x30 }, - { 0x460b, 0x35 }, - { 0x471d, 0x0 }, - { 0x3002, 0xc }, - { 0x3002, 0x0 }, - { 0x4713, 0x3 }, - { 0x471c, 0x50 }, - { 0x4721, 0x2 }, - { 0x4402, 0x90 }, - { 0x460c, 0x22 }, - { 0x3815, 0x44 }, - { 0x3503, 0x7 }, - { 0x3501, 0x73 }, - { 0x3502, 0x80 }, - { 0x350b, 0x0 }, - { 0x3818, 0xc8 }, - { 0x3824, 0x11 }, - { 0x3a00, 0x78 }, - { 0x3a1a, 0x4 }, - { 0x3a13, 0x30 }, - { 0x3a18, 0x0 }, - { 0x3a19, 0x7c }, - { 0x3a08, 0x12 }, - { 0x3a09, 0xc0 }, - { 0x3a0a, 0xf }, - { 0x3a0b, 0xa0 }, - { 0x350c, 0x7 }, - { 0x350d, 0xd0 }, - { 0x3a0d, 0x8 }, - { 0x3a0e, 0x6 }, - { 0x3500, 0x0 }, - { 0x3501, 0x0 }, - { 0x3502, 0x0 }, - { 0x350a, 0x0 }, - { 0x350b, 0x0 }, - { 0x3503, 0x0 }, - { 0x3a0f, 0x3c }, - { 0x3a10, 0x32 }, - { 0x3a1b, 0x3c }, - { 0x3a1e, 0x32 }, - { 0x3a11, 0x80 }, - { 0x3a1f, 0x20 }, - { 0x3030, 0x2b }, - { 0x3a02, 0x0 }, - { 0x3a03, 0x7d }, - { 0x3a04, 0x0 }, - { 0x3a14, 0x0 }, - { 0x3a15, 0x7d }, - { 0x3a16, 0x0 }, - { 0x3a00, 0x78 }, - { 0x3a08, 0x9 }, - { 0x3a09, 0x60 }, - { 0x3a0a, 0x7 }, - { 0x3a0b, 0xd0 }, - { 0x3a0d, 0x10 }, - { 0x3a0e, 0xd }, - { 0x4407, 0x4 }, - { 0x5193, 0x70 }, - { 0x589b, 0x0 }, - { 0x589a, 0xc0 }, - { 0x401e, 0x20 }, - { 0x4001, 0x42 }, - { 0x401c, 0x6 }, - { 0x3825, 0xac }, - { 0x3827, 0xc }, - { 0x528a, 0x1 }, - { 0x528b, 0x4 }, - { 0x528c, 0x8 }, - { 0x528d, 0x10 }, - { 0x528e, 0x20 }, - { 0x528f, 0x28 }, - { 0x5290, 0x30 }, - { 0x5292, 0x0 }, - { 0x5293, 0x1 }, - { 0x5294, 0x0 }, - { 0x5295, 0x4 }, - { 0x5296, 0x0 }, - { 0x5297, 0x8 }, - { 0x5298, 0x0 }, - { 0x5299, 0x10 }, - { 0x529a, 0x0 }, - { 0x529b, 0x20 }, - { 0x529c, 0x0 }, - { 0x529d, 0x28 }, - { 0x529e, 0x0 }, - { 0x529f, 0x30 }, - { 0x5282, 0x0 }, - { 0x5300, 0x0 }, - { 0x5301, 0x20 }, - { 0x5302, 0x0 }, - { 0x5303, 0x7c }, - { 0x530c, 0x0 }, - { 0x530d, 0xc }, - { 0x530e, 0x20 }, - { 0x530f, 0x80 }, - { 0x5310, 0x20 }, - { 0x5311, 0x80 }, - { 0x5308, 0x20 }, - { 0x5309, 0x40 }, - { 0x5304, 0x0 }, - { 0x5305, 0x30 }, - { 0x5306, 0x0 }, - { 0x5307, 0x80 }, - { 0x5314, 0x8 }, - { 0x5315, 0x20 }, - { 0x5319, 0x30 }, - { 0x5316, 0x10 }, - { 0x5317, 0x0 }, - { 0x5318, 0x2 }, - { 0x5380, 0x1 }, - { 0x5381, 0x0 }, - { 0x5382, 0x0 }, - { 0x5383, 0x4e }, - { 0x5384, 0x0 }, - { 0x5385, 0xf }, - { 0x5386, 0x0 }, - { 0x5387, 0x0 }, - { 0x5388, 0x1 }, - { 0x5389, 0x15 }, - { 0x538a, 0x0 }, - { 0x538b, 0x31 }, - { 0x538c, 0x0 }, - { 0x538d, 0x0 }, - { 0x538e, 0x0 }, - { 0x538f, 0xf }, - { 0x5390, 0x0 }, - { 0x5391, 0xab }, - { 0x5392, 0x0 }, - { 0x5393, 0xa2 }, - { 0x5394, 0x8 }, - { 0x5480, 0x14 }, - { 0x5481, 0x21 }, - { 0x5482, 0x36 }, - { 0x5483, 0x57 }, - { 0x5484, 0x65 }, - { 0x5485, 0x71 }, - { 0x5486, 0x7d }, - { 0x5487, 0x87 }, - { 0x5488, 0x91 }, - { 0x5489, 0x9a }, - { 0x548a, 0xaa }, - { 0x548b, 0xb8 }, - { 0x548c, 0xcd }, - { 0x548d, 0xdd }, - { 0x548e, 0xea }, - { 0x548f, 0x1d }, - { 0x5490, 0x5 }, - { 0x5491, 0x0 }, - { 0x5492, 0x4 }, - { 0x5493, 0x20 }, - { 0x5494, 0x3 }, - { 0x5495, 0x60 }, - { 0x5496, 0x2 }, - { 0x5497, 0xb8 }, - { 0x5498, 0x2 }, - { 0x5499, 0x86 }, - { 0x549a, 0x2 }, - { 0x549b, 0x5b }, - { 0x549c, 0x2 }, - { 0x549d, 0x3b }, - { 0x549e, 0x2 }, - { 0x549f, 0x1c }, - { 0x54a0, 0x2 }, - { 0x54a1, 0x4 }, - { 0x54a2, 0x1 }, - { 0x54a3, 0xed }, - { 0x54a4, 0x1 }, - { 0x54a5, 0xc5 }, - { 0x54a6, 0x1 }, - { 0x54a7, 0xa5 }, - { 0x54a8, 0x1 }, - { 0x54a9, 0x6c }, - { 0x54aa, 0x1 }, - { 0x54ab, 0x41 }, - { 0x54ac, 0x1 }, - { 0x54ad, 0x20 }, - { 0x54ae, 0x0 }, - { 0x54af, 0x16 }, - { 0x54b0, 0x1 }, - { 0x54b1, 0x20 }, - { 0x54b2, 0x0 }, - { 0x54b3, 0x10 }, - { 0x54b4, 0x0 }, - { 0x54b5, 0xf0 }, - { 0x54b6, 0x0 }, - { 0x54b7, 0xdf }, - { 0x5402, 0x3f }, - { 0x5403, 0x0 }, - { 0x3406, 0x0 }, - { 0x5180, 0xff }, - { 0x5181, 0x52 }, - { 0x5182, 0x11 }, - { 0x5183, 0x14 }, - { 0x5184, 0x25 }, - { 0x5185, 0x24 }, - { 0x5186, 0x6 }, - { 0x5187, 0x8 }, - { 0x5188, 0x8 }, - { 0x5189, 0x7c }, - { 0x518a, 0x60 }, - { 0x518b, 0xb2 }, - { 0x518c, 0xb2 }, - { 0x518d, 0x44 }, - { 0x518e, 0x3d }, - { 0x518f, 0x58 }, - { 0x5190, 0x46 }, - { 0x5191, 0xf8 }, - { 0x5192, 0x4 }, - { 0x5193, 0x70 }, - { 0x5194, 0xf0 }, - { 0x5195, 0xf0 }, - { 0x5196, 0x3 }, - { 0x5197, 0x1 }, - { 0x5198, 0x4 }, - { 0x5199, 0x12 }, - { 0x519a, 0x4 }, - { 0x519b, 0x0 }, - { 0x519c, 0x6 }, - { 0x519d, 0x82 }, - { 0x519e, 0x0 }, - { 0x5025, 0x80 }, - { 0x3a0f, 0x38 }, - { 0x3a10, 0x30 }, - { 0x3a1b, 0x3a }, - { 0x3a1e, 0x2e }, - { 0x3a11, 0x60 }, - { 0x3a1f, 0x10 }, - { 0x5688, 0xa6 }, - { 0x5689, 0x6a }, - { 0x568a, 0xea }, - { 0x568b, 0xae }, - { 0x568c, 0xa6 }, - { 0x568d, 0x6a }, - { 0x568e, 0x62 }, - { 0x568f, 0x26 }, - { 0x5583, 0x40 }, - { 0x5584, 0x40 }, - { 0x5580, 0x2 }, - { 0x5000, 0xcf }, - { 0x5800, 0x27 }, - { 0x5801, 0x19 }, - { 0x5802, 0x12 }, - { 0x5803, 0xf }, - { 0x5804, 0x10 }, - { 0x5805, 0x15 }, - { 0x5806, 0x1e }, - { 0x5807, 0x2f }, - { 0x5808, 0x15 }, - { 0x5809, 0xd }, - { 0x580a, 0xa }, - { 0x580b, 0x9 }, - { 0x580c, 0xa }, - { 0x580d, 0xc }, - { 0x580e, 0x12 }, - { 0x580f, 0x19 }, - { 0x5810, 0xb }, - { 0x5811, 0x7 }, - { 0x5812, 0x4 }, - { 0x5813, 0x3 }, - { 0x5814, 0x3 }, - { 0x5815, 0x6 }, - { 0x5816, 0xa }, - { 0x5817, 0xf }, - { 0x5818, 0xa }, - { 0x5819, 0x5 }, - { 0x581a, 0x1 }, - { 0x581b, 0x0 }, - { 0x581c, 0x0 }, - { 0x581d, 0x3 }, - { 0x581e, 0x8 }, - { 0x581f, 0xc }, - { 0x5820, 0xa }, - { 0x5821, 0x5 }, - { 0x5822, 0x1 }, - { 0x5823, 0x0 }, - { 0x5824, 0x0 }, - { 0x5825, 0x3 }, - { 0x5826, 0x8 }, - { 0x5827, 0xc }, - { 0x5828, 0xe }, - { 0x5829, 0x8 }, - { 0x582a, 0x6 }, - { 0x582b, 0x4 }, - { 0x582c, 0x5 }, - { 0x582d, 0x7 }, - { 0x582e, 0xb }, - { 0x582f, 0x12 }, - { 0x5830, 0x18 }, - { 0x5831, 0x10 }, - { 0x5832, 0xc }, - { 0x5833, 0xa }, - { 0x5834, 0xb }, - { 0x5835, 0xe }, - { 0x5836, 0x15 }, - { 0x5837, 0x19 }, - { 0x5838, 0x32 }, - { 0x5839, 0x1f }, - { 0x583a, 0x18 }, - { 0x583b, 0x16 }, - { 0x583c, 0x17 }, - { 0x583d, 0x1e }, - { 0x583e, 0x26 }, - { 0x583f, 0x53 }, - { 0x5840, 0x10 }, - { 0x5841, 0xf }, - { 0x5842, 0xd }, - { 0x5843, 0xc }, - { 0x5844, 0xe }, - { 0x5845, 0x9 }, - { 0x5846, 0x11 }, - { 0x5847, 0x10 }, - { 0x5848, 0x10 }, - { 0x5849, 0x10 }, - { 0x584a, 0x10 }, - { 0x584b, 0xe }, - { 0x584c, 0x10 }, - { 0x584d, 0x10 }, - { 0x584e, 0x11 }, - { 0x584f, 0x10 }, - { 0x5850, 0xf }, - { 0x5851, 0xc }, - { 0x5852, 0xf }, - { 0x5853, 0x10 }, - { 0x5854, 0x10 }, - { 0x5855, 0xf }, - { 0x5856, 0xe }, - { 0x5857, 0xb }, - { 0x5858, 0x10 }, - { 0x5859, 0xd }, - { 0x585a, 0xd }, - { 0x585b, 0xc }, - { 0x585c, 0xc }, - { 0x585d, 0xc }, - { 0x585e, 0xb }, - { 0x585f, 0xc }, - { 0x5860, 0xc }, - { 0x5861, 0xc }, - { 0x5862, 0xd }, - { 0x5863, 0x8 }, - { 0x5864, 0x11 }, - { 0x5865, 0x18 }, - { 0x5866, 0x18 }, - { 0x5867, 0x19 }, - { 0x5868, 0x17 }, - { 0x5869, 0x19 }, - { 0x586a, 0x16 }, - { 0x586b, 0x13 }, - { 0x586c, 0x13 }, - { 0x586d, 0x12 }, - { 0x586e, 0x13 }, - { 0x586f, 0x16 }, - { 0x5870, 0x14 }, - { 0x5871, 0x12 }, - { 0x5872, 0x10 }, - { 0x5873, 0x11 }, - { 0x5874, 0x11 }, - { 0x5875, 0x16 }, - { 0x5876, 0x14 }, - { 0x5877, 0x11 }, - { 0x5878, 0x10 }, - { 0x5879, 0xf }, - { 0x587a, 0x10 }, - { 0x587b, 0x14 }, - { 0x587c, 0x13 }, - { 0x587d, 0x12 }, - { 0x587e, 0x11 }, - { 0x587f, 0x11 }, - { 0x5880, 0x12 }, - { 0x5881, 0x15 }, - { 0x5882, 0x14 }, - { 0x5883, 0x15 }, - { 0x5884, 0x15 }, - { 0x5885, 0x15 }, - { 0x5886, 0x13 }, - { 0x5887, 0x17 }, - { 0x3710, 0x10 }, - { 0x3632, 0x51 }, - { 0x3702, 0x10 }, - { 0x3703, 0xb2 }, - { 0x3704, 0x18 }, - { 0x370b, 0x40 }, - { 0x370d, 0x3 }, - { 0x3631, 0x1 }, - { 0x3632, 0x52 }, - { 0x3606, 0x24 }, - { 0x3620, 0x96 }, - { 0x5785, 0x7 }, - { 0x3a13, 0x30 }, - { 0x3600, 0x52 }, - { 0x3604, 0x48 }, - { 0x3606, 0x1b }, - { 0x370d, 0xb }, - { 0x370f, 0xc0 }, - { 0x3709, 0x1 }, - { 0x3823, 0x0 }, - { 0x5007, 0x0 }, - { 0x5009, 0x0 }, - { 0x5011, 0x0 }, - { 0x5013, 0x0 }, - { 0x519e, 0x0 }, - { 0x5086, 0x0 }, - { 0x5087, 0x0 }, - { 0x5088, 0x0 }, - { 0x5089, 0x0 }, - { 0x302b, 0x0 }, - { 0x3503, 0x7 }, - { 0x3011, 0x8 }, - { 0x350c, 0x2 }, - { 0x350d, 0xe4 }, - { 0x3621, 0xc9 }, - { 0x370a, 0x81 }, - { 0xffff, 0xff }, -}; - -static struct regval_list ov5642_default_regs_finalise[] = { - { 0x3810, 0xc2 }, - { 0x3818, 0xc9 }, - { 0x381c, 0x10 }, - { 0x381d, 0xa0 }, - { 0x381e, 0x5 }, - { 0x381f, 0xb0 }, - { 0x3820, 0x0 }, - { 0x3821, 0x0 }, - { 0x3824, 0x11 }, - { 0x3a08, 0x1b }, - { 0x3a09, 0xc0 }, - { 0x3a0a, 0x17 }, - { 0x3a0b, 0x20 }, - { 0x3a0d, 0x2 }, - { 0x3a0e, 0x1 }, - { 0x401c, 0x4 }, - { 0x5682, 0x5 }, - { 0x5683, 0x0 }, - { 0x5686, 0x2 }, - { 0x5687, 0xcc }, - { 0x5001, 0x4f }, - { 0x589b, 0x6 }, - { 0x589a, 0xc5 }, - { 0x3503, 0x0 }, - { 0x460c, 0x20 }, - { 0x460b, 0x37 }, - { 0x471c, 0xd0 }, - { 0x471d, 0x5 }, - { 0x3815, 0x1 }, - { 0x3818, 0xc1 }, - { 0x501f, 0x0 }, - { 0x5002, 0xe0 }, - { 0x4300, 0x32 }, /* UYVY */ - { 0x3002, 0x1c }, - { 0x4800, 0x14 }, - { 0x4801, 0xf }, - { 0x3007, 0x3b }, - { 0x300e, 0x4 }, - { 0x4803, 0x50 }, - { 0x3815, 0x1 }, - { 0x4713, 0x2 }, - { 0x4842, 0x1 }, - { 0x300f, 0xe }, - { 0x3003, 0x3 }, - { 0x3003, 0x1 }, - { 0xffff, 0xff }, -}; - -struct ov5642_datafmt { - u32 code; - enum v4l2_colorspace colorspace; -}; - -struct ov5642 { - struct v4l2_subdev subdev; - const struct ov5642_datafmt *fmt; - struct v4l2_rect crop_rect; - struct v4l2_clk *clk; - - /* blanking information */ - int total_width; - int total_height; -}; - -static const struct ov5642_datafmt ov5642_colour_fmts[] = { - {MEDIA_BUS_FMT_UYVY8_2X8, V4L2_COLORSPACE_JPEG}, -}; - -static struct ov5642 *to_ov5642(const struct i2c_client *client) -{ - return container_of(i2c_get_clientdata(client), struct ov5642, subdev); -} - -/* Find a data format by a pixel code in an array */ -static const struct ov5642_datafmt - *ov5642_find_datafmt(u32 code) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(ov5642_colour_fmts); i++) - if (ov5642_colour_fmts[i].code == code) - return ov5642_colour_fmts + i; - - return NULL; -} - -static int reg_read(struct i2c_client *client, u16 reg, u8 *val) -{ - int ret; - /* We have 16-bit i2c addresses - care for endianness */ - unsigned char data[2] = { reg >> 8, reg & 0xff }; - - ret = i2c_master_send(client, data, 2); - if (ret < 2) { - dev_err(&client->dev, "%s: i2c read error, reg: %x\n", - __func__, reg); - return ret < 0 ? ret : -EIO; - } - - ret = i2c_master_recv(client, val, 1); - if (ret < 1) { - dev_err(&client->dev, "%s: i2c read error, reg: %x\n", - __func__, reg); - return ret < 0 ? ret : -EIO; - } - return 0; -} - -static int reg_write(struct i2c_client *client, u16 reg, u8 val) -{ - int ret; - unsigned char data[3] = { reg >> 8, reg & 0xff, val }; - - ret = i2c_master_send(client, data, 3); - if (ret < 3) { - dev_err(&client->dev, "%s: i2c write error, reg: %x\n", - __func__, reg); - return ret < 0 ? ret : -EIO; - } - - return 0; -} - -/* - * convenience function to write 16 bit register values that are split up - * into two consecutive high and low parts - */ -static int reg_write16(struct i2c_client *client, u16 reg, u16 val16) -{ - int ret; - - ret = reg_write(client, reg, val16 >> 8); - if (ret) - return ret; - return reg_write(client, reg + 1, val16 & 0x00ff); -} - -#ifdef CONFIG_VIDEO_ADV_DEBUG -static int ov5642_get_register(struct v4l2_subdev *sd, - struct v4l2_dbg_register *reg) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - int ret; - u8 val; - - if (reg->reg & ~0xffff) - return -EINVAL; - - reg->size = 1; - - ret = reg_read(client, reg->reg, &val); - if (!ret) - reg->val = (__u64)val; - - return ret; -} - -static int ov5642_set_register(struct v4l2_subdev *sd, - const struct v4l2_dbg_register *reg) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - - if (reg->reg & ~0xffff || reg->val & ~0xff) - return -EINVAL; - - return reg_write(client, reg->reg, reg->val); -} -#endif - -static int ov5642_write_array(struct i2c_client *client, - struct regval_list *vals) -{ - while (vals->reg_num != 0xffff || vals->value != 0xff) { - int ret = reg_write(client, vals->reg_num, vals->value); - if (ret < 0) - return ret; - vals++; - } - dev_dbg(&client->dev, "Register list loaded\n"); - return 0; -} - -static int ov5642_set_resolution(struct v4l2_subdev *sd) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct ov5642 *priv = to_ov5642(client); - int width = priv->crop_rect.width; - int height = priv->crop_rect.height; - int total_width = priv->total_width; - int total_height = priv->total_height; - int start_x = (OV5642_SENSOR_SIZE_X - width) / 2; - int start_y = (OV5642_SENSOR_SIZE_Y - height) / 2; - int ret; - - /* - * This should set the starting point for cropping. - * Doesn't work so far. - */ - ret = reg_write16(client, REG_WINDOW_START_X_HIGH, start_x); - if (!ret) - ret = reg_write16(client, REG_WINDOW_START_Y_HIGH, start_y); - if (!ret) { - priv->crop_rect.left = start_x; - priv->crop_rect.top = start_y; - } - - if (!ret) - ret = reg_write16(client, REG_WINDOW_WIDTH_HIGH, width); - if (!ret) - ret = reg_write16(client, REG_WINDOW_HEIGHT_HIGH, height); - if (ret) - return ret; - priv->crop_rect.width = width; - priv->crop_rect.height = height; - - /* Set the output window size. Only 1:1 scale is supported so far. */ - ret = reg_write16(client, REG_OUT_WIDTH_HIGH, width); - if (!ret) - ret = reg_write16(client, REG_OUT_HEIGHT_HIGH, height); - - /* Total width = output size + blanking */ - if (!ret) - ret = reg_write16(client, REG_OUT_TOTAL_WIDTH_HIGH, total_width); - if (!ret) - ret = reg_write16(client, REG_OUT_TOTAL_HEIGHT_HIGH, total_height); - - /* Sets the window for AWB calculations */ - if (!ret) - ret = reg_write16(client, REG_AVG_WINDOW_END_X_HIGH, width); - if (!ret) - ret = reg_write16(client, REG_AVG_WINDOW_END_Y_HIGH, height); - - return ret; -} - -static int ov5642_set_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, - struct v4l2_subdev_format *format) -{ - struct v4l2_mbus_framefmt *mf = &format->format; - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct ov5642 *priv = to_ov5642(client); - const struct ov5642_datafmt *fmt = ov5642_find_datafmt(mf->code); - - if (format->pad) - return -EINVAL; - - mf->width = priv->crop_rect.width; - mf->height = priv->crop_rect.height; - - if (!fmt) { - if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE) - return -EINVAL; - mf->code = ov5642_colour_fmts[0].code; - mf->colorspace = ov5642_colour_fmts[0].colorspace; - } - - mf->field = V4L2_FIELD_NONE; - - if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE) - priv->fmt = fmt; - else - cfg->try_fmt = *mf; - return 0; -} - -static int ov5642_get_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, - struct v4l2_subdev_format *format) -{ - struct v4l2_mbus_framefmt *mf = &format->format; - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct ov5642 *priv = to_ov5642(client); - - const struct ov5642_datafmt *fmt = priv->fmt; - - if (format->pad) - return -EINVAL; - - mf->code = fmt->code; - mf->colorspace = fmt->colorspace; - mf->width = priv->crop_rect.width; - mf->height = priv->crop_rect.height; - mf->field = V4L2_FIELD_NONE; - - return 0; -} - -static int ov5642_enum_mbus_code(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, - struct v4l2_subdev_mbus_code_enum *code) -{ - if (code->pad || code->index >= ARRAY_SIZE(ov5642_colour_fmts)) - return -EINVAL; - - code->code = ov5642_colour_fmts[code->index].code; - return 0; -} - -static int ov5642_set_selection(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, - struct v4l2_subdev_selection *sel) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct ov5642 *priv = to_ov5642(client); - struct v4l2_rect rect = sel->r; - int ret; - - if (sel->which != V4L2_SUBDEV_FORMAT_ACTIVE || - sel->target != V4L2_SEL_TGT_CROP) - return -EINVAL; - - v4l_bound_align_image(&rect.width, 48, OV5642_MAX_WIDTH, 1, - &rect.height, 32, OV5642_MAX_HEIGHT, 1, 0); - - priv->crop_rect.width = rect.width; - priv->crop_rect.height = rect.height; - priv->total_width = rect.width + BLANKING_EXTRA_WIDTH; - priv->total_height = max_t(int, rect.height + - BLANKING_EXTRA_HEIGHT, - BLANKING_MIN_HEIGHT); - priv->crop_rect.width = rect.width; - priv->crop_rect.height = rect.height; - - ret = ov5642_write_array(client, ov5642_default_regs_init); - if (!ret) - ret = ov5642_set_resolution(sd); - if (!ret) - ret = ov5642_write_array(client, ov5642_default_regs_finalise); - - return ret; -} - -static int ov5642_get_selection(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, - struct v4l2_subdev_selection *sel) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct ov5642 *priv = to_ov5642(client); - - if (sel->which != V4L2_SUBDEV_FORMAT_ACTIVE) - return -EINVAL; - - switch (sel->target) { - case V4L2_SEL_TGT_CROP_BOUNDS: - sel->r.left = 0; - sel->r.top = 0; - sel->r.width = OV5642_MAX_WIDTH; - sel->r.height = OV5642_MAX_HEIGHT; - return 0; - case V4L2_SEL_TGT_CROP: - sel->r = priv->crop_rect; - return 0; - default: - return -EINVAL; - } -} - -static int ov5642_g_mbus_config(struct v4l2_subdev *sd, - struct v4l2_mbus_config *cfg) -{ - cfg->type = V4L2_MBUS_CSI2_DPHY; - cfg->flags = V4L2_MBUS_CSI2_2_LANE | V4L2_MBUS_CSI2_CHANNEL_0 | - V4L2_MBUS_CSI2_CONTINUOUS_CLOCK; - - return 0; -} - -static int ov5642_s_power(struct v4l2_subdev *sd, int on) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); - struct ov5642 *priv = to_ov5642(client); - int ret; - - if (!on) - return soc_camera_power_off(&client->dev, ssdd, priv->clk); - - ret = soc_camera_power_on(&client->dev, ssdd, priv->clk); - if (ret < 0) - return ret; - - ret = ov5642_write_array(client, ov5642_default_regs_init); - if (!ret) - ret = ov5642_set_resolution(sd); - if (!ret) - ret = ov5642_write_array(client, ov5642_default_regs_finalise); - - return ret; -} - -static const struct v4l2_subdev_video_ops ov5642_subdev_video_ops = { - .g_mbus_config = ov5642_g_mbus_config, -}; - -static const struct v4l2_subdev_pad_ops ov5642_subdev_pad_ops = { - .enum_mbus_code = ov5642_enum_mbus_code, - .get_selection = ov5642_get_selection, - .set_selection = ov5642_set_selection, - .get_fmt = ov5642_get_fmt, - .set_fmt = ov5642_set_fmt, -}; - -static const struct v4l2_subdev_core_ops ov5642_subdev_core_ops = { - .s_power = ov5642_s_power, -#ifdef CONFIG_VIDEO_ADV_DEBUG - .g_register = ov5642_get_register, - .s_register = ov5642_set_register, -#endif -}; - -static const struct v4l2_subdev_ops ov5642_subdev_ops = { - .core = &ov5642_subdev_core_ops, - .video = &ov5642_subdev_video_ops, - .pad = &ov5642_subdev_pad_ops, -}; - -static int ov5642_video_probe(struct i2c_client *client) -{ - struct v4l2_subdev *subdev = i2c_get_clientdata(client); - int ret; - u8 id_high, id_low; - u16 id; - - ret = ov5642_s_power(subdev, 1); - if (ret < 0) - return ret; - - /* Read sensor Model ID */ - ret = reg_read(client, REG_CHIP_ID_HIGH, &id_high); - if (ret < 0) - goto done; - - id = id_high << 8; - - ret = reg_read(client, REG_CHIP_ID_LOW, &id_low); - if (ret < 0) - goto done; - - id |= id_low; - - dev_info(&client->dev, "Chip ID 0x%04x detected\n", id); - - if (id != 0x5642) { - ret = -ENODEV; - goto done; - } - - ret = 0; - -done: - ov5642_s_power(subdev, 0); - return ret; -} - -static int ov5642_probe(struct i2c_client *client, - const struct i2c_device_id *did) -{ - struct ov5642 *priv; - struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); - int ret; - - if (!ssdd) { - dev_err(&client->dev, "OV5642: missing platform data!\n"); - return -EINVAL; - } - - priv = devm_kzalloc(&client->dev, sizeof(struct ov5642), GFP_KERNEL); - if (!priv) - return -ENOMEM; - - v4l2_i2c_subdev_init(&priv->subdev, client, &ov5642_subdev_ops); - - priv->fmt = &ov5642_colour_fmts[0]; - - priv->crop_rect.width = OV5642_DEFAULT_WIDTH; - priv->crop_rect.height = OV5642_DEFAULT_HEIGHT; - priv->crop_rect.left = (OV5642_MAX_WIDTH - OV5642_DEFAULT_WIDTH) / 2; - priv->crop_rect.top = (OV5642_MAX_HEIGHT - OV5642_DEFAULT_HEIGHT) / 2; - priv->total_width = OV5642_DEFAULT_WIDTH + BLANKING_EXTRA_WIDTH; - priv->total_height = BLANKING_MIN_HEIGHT; - - priv->clk = v4l2_clk_get(&client->dev, "mclk"); - if (IS_ERR(priv->clk)) - return PTR_ERR(priv->clk); - - ret = ov5642_video_probe(client); - if (ret < 0) - v4l2_clk_put(priv->clk); - - return ret; -} - -static int ov5642_remove(struct i2c_client *client) -{ - struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); - struct ov5642 *priv = to_ov5642(client); - - v4l2_clk_put(priv->clk); - if (ssdd->free_bus) - ssdd->free_bus(ssdd); - - return 0; -} - -static const struct i2c_device_id ov5642_id[] = { - { "ov5642", 0 }, - { } -}; -MODULE_DEVICE_TABLE(i2c, ov5642_id); - -#if IS_ENABLED(CONFIG_OF) -static const struct of_device_id ov5642_of_match[] = { - { .compatible = "ovti,ov5642" }, - { }, -}; -MODULE_DEVICE_TABLE(of, ov5642_of_match); -#endif - -static struct i2c_driver ov5642_i2c_driver = { - .driver = { - .name = "ov5642", - .of_match_table = of_match_ptr(ov5642_of_match), - }, - .probe = ov5642_probe, - .remove = ov5642_remove, - .id_table = ov5642_id, -}; - -module_i2c_driver(ov5642_i2c_driver); - -MODULE_DESCRIPTION("Omnivision OV5642 Camera driver"); -MODULE_AUTHOR("Bastian Hecht "); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/staging/media/soc_camera/soc_ov9740.c b/drivers/staging/media/soc_camera/soc_ov9740.c deleted file mode 100644 index 7c765595d85fb..0000000000000 --- a/drivers/staging/media/soc_camera/soc_ov9740.c +++ /dev/null @@ -1,992 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * OmniVision OV9740 Camera Driver - * - * Copyright (C) 2011 NVIDIA Corporation - * - * Based on ov9640 camera driver. - */ -#include -#include -#include -#include -#include - -#include -#include -#include - -#define to_ov9740(sd) container_of(sd, struct ov9740_priv, subdev) - -/* General Status Registers */ -#define OV9740_MODEL_ID_HI 0x0000 -#define OV9740_MODEL_ID_LO 0x0001 -#define OV9740_REVISION_NUMBER 0x0002 -#define OV9740_MANUFACTURER_ID 0x0003 -#define OV9740_SMIA_VERSION 0x0004 - -/* General Setup Registers */ -#define OV9740_MODE_SELECT 0x0100 -#define OV9740_IMAGE_ORT 0x0101 -#define OV9740_SOFTWARE_RESET 0x0103 -#define OV9740_GRP_PARAM_HOLD 0x0104 -#define OV9740_MSK_CORRUP_FM 0x0105 - -/* Timing Setting */ -#define OV9740_FRM_LENGTH_LN_HI 0x0340 /* VTS */ -#define OV9740_FRM_LENGTH_LN_LO 0x0341 /* VTS */ -#define OV9740_LN_LENGTH_PCK_HI 0x0342 /* HTS */ -#define OV9740_LN_LENGTH_PCK_LO 0x0343 /* HTS */ -#define OV9740_X_ADDR_START_HI 0x0344 -#define OV9740_X_ADDR_START_LO 0x0345 -#define OV9740_Y_ADDR_START_HI 0x0346 -#define OV9740_Y_ADDR_START_LO 0x0347 -#define OV9740_X_ADDR_END_HI 0x0348 -#define OV9740_X_ADDR_END_LO 0x0349 -#define OV9740_Y_ADDR_END_HI 0x034a -#define OV9740_Y_ADDR_END_LO 0x034b -#define OV9740_X_OUTPUT_SIZE_HI 0x034c -#define OV9740_X_OUTPUT_SIZE_LO 0x034d -#define OV9740_Y_OUTPUT_SIZE_HI 0x034e -#define OV9740_Y_OUTPUT_SIZE_LO 0x034f - -/* IO Control Registers */ -#define OV9740_IO_CREL00 0x3002 -#define OV9740_IO_CREL01 0x3004 -#define OV9740_IO_CREL02 0x3005 -#define OV9740_IO_OUTPUT_SEL01 0x3026 -#define OV9740_IO_OUTPUT_SEL02 0x3027 - -/* AWB Registers */ -#define OV9740_AWB_MANUAL_CTRL 0x3406 - -/* Analog Control Registers */ -#define OV9740_ANALOG_CTRL01 0x3601 -#define OV9740_ANALOG_CTRL02 0x3602 -#define OV9740_ANALOG_CTRL03 0x3603 -#define OV9740_ANALOG_CTRL04 0x3604 -#define OV9740_ANALOG_CTRL10 0x3610 -#define OV9740_ANALOG_CTRL12 0x3612 -#define OV9740_ANALOG_CTRL15 0x3615 -#define OV9740_ANALOG_CTRL20 0x3620 -#define OV9740_ANALOG_CTRL21 0x3621 -#define OV9740_ANALOG_CTRL22 0x3622 -#define OV9740_ANALOG_CTRL30 0x3630 -#define OV9740_ANALOG_CTRL31 0x3631 -#define OV9740_ANALOG_CTRL32 0x3632 -#define OV9740_ANALOG_CTRL33 0x3633 - -/* Sensor Control */ -#define OV9740_SENSOR_CTRL03 0x3703 -#define OV9740_SENSOR_CTRL04 0x3704 -#define OV9740_SENSOR_CTRL05 0x3705 -#define OV9740_SENSOR_CTRL07 0x3707 - -/* Timing Control */ -#define OV9740_TIMING_CTRL17 0x3817 -#define OV9740_TIMING_CTRL19 0x3819 -#define OV9740_TIMING_CTRL33 0x3833 -#define OV9740_TIMING_CTRL35 0x3835 - -/* Banding Filter */ -#define OV9740_AEC_MAXEXPO_60_H 0x3a02 -#define OV9740_AEC_MAXEXPO_60_L 0x3a03 -#define OV9740_AEC_B50_STEP_HI 0x3a08 -#define OV9740_AEC_B50_STEP_LO 0x3a09 -#define OV9740_AEC_B60_STEP_HI 0x3a0a -#define OV9740_AEC_B60_STEP_LO 0x3a0b -#define OV9740_AEC_CTRL0D 0x3a0d -#define OV9740_AEC_CTRL0E 0x3a0e -#define OV9740_AEC_MAXEXPO_50_H 0x3a14 -#define OV9740_AEC_MAXEXPO_50_L 0x3a15 - -/* AEC/AGC Control */ -#define OV9740_AEC_ENABLE 0x3503 -#define OV9740_GAIN_CEILING_01 0x3a18 -#define OV9740_GAIN_CEILING_02 0x3a19 -#define OV9740_AEC_HI_THRESHOLD 0x3a11 -#define OV9740_AEC_3A1A 0x3a1a -#define OV9740_AEC_CTRL1B_WPT2 0x3a1b -#define OV9740_AEC_CTRL0F_WPT 0x3a0f -#define OV9740_AEC_CTRL10_BPT 0x3a10 -#define OV9740_AEC_CTRL1E_BPT2 0x3a1e -#define OV9740_AEC_LO_THRESHOLD 0x3a1f - -/* BLC Control */ -#define OV9740_BLC_AUTO_ENABLE 0x4002 -#define OV9740_BLC_MODE 0x4005 - -/* VFIFO */ -#define OV9740_VFIFO_READ_START_HI 0x4608 -#define OV9740_VFIFO_READ_START_LO 0x4609 - -/* DVP Control */ -#define OV9740_DVP_VSYNC_CTRL02 0x4702 -#define OV9740_DVP_VSYNC_MODE 0x4704 -#define OV9740_DVP_VSYNC_CTRL06 0x4706 - -/* PLL Setting */ -#define OV9740_PLL_MODE_CTRL01 0x3104 -#define OV9740_PRE_PLL_CLK_DIV 0x0305 -#define OV9740_PLL_MULTIPLIER 0x0307 -#define OV9740_VT_SYS_CLK_DIV 0x0303 -#define OV9740_VT_PIX_CLK_DIV 0x0301 -#define OV9740_PLL_CTRL3010 0x3010 -#define OV9740_VFIFO_CTRL00 0x460e - -/* ISP Control */ -#define OV9740_ISP_CTRL00 0x5000 -#define OV9740_ISP_CTRL01 0x5001 -#define OV9740_ISP_CTRL03 0x5003 -#define OV9740_ISP_CTRL05 0x5005 -#define OV9740_ISP_CTRL12 0x5012 -#define OV9740_ISP_CTRL19 0x5019 -#define OV9740_ISP_CTRL1A 0x501a -#define OV9740_ISP_CTRL1E 0x501e -#define OV9740_ISP_CTRL1F 0x501f -#define OV9740_ISP_CTRL20 0x5020 -#define OV9740_ISP_CTRL21 0x5021 - -/* AWB */ -#define OV9740_AWB_CTRL00 0x5180 -#define OV9740_AWB_CTRL01 0x5181 -#define OV9740_AWB_CTRL02 0x5182 -#define OV9740_AWB_CTRL03 0x5183 -#define OV9740_AWB_ADV_CTRL01 0x5184 -#define OV9740_AWB_ADV_CTRL02 0x5185 -#define OV9740_AWB_ADV_CTRL03 0x5186 -#define OV9740_AWB_ADV_CTRL04 0x5187 -#define OV9740_AWB_ADV_CTRL05 0x5188 -#define OV9740_AWB_ADV_CTRL06 0x5189 -#define OV9740_AWB_ADV_CTRL07 0x518a -#define OV9740_AWB_ADV_CTRL08 0x518b -#define OV9740_AWB_ADV_CTRL09 0x518c -#define OV9740_AWB_ADV_CTRL10 0x518d -#define OV9740_AWB_ADV_CTRL11 0x518e -#define OV9740_AWB_CTRL0F 0x518f -#define OV9740_AWB_CTRL10 0x5190 -#define OV9740_AWB_CTRL11 0x5191 -#define OV9740_AWB_CTRL12 0x5192 -#define OV9740_AWB_CTRL13 0x5193 -#define OV9740_AWB_CTRL14 0x5194 - -/* MIPI Control */ -#define OV9740_MIPI_CTRL00 0x4800 -#define OV9740_MIPI_3837 0x3837 -#define OV9740_MIPI_CTRL01 0x4801 -#define OV9740_MIPI_CTRL03 0x4803 -#define OV9740_MIPI_CTRL05 0x4805 -#define OV9740_VFIFO_RD_CTRL 0x4601 -#define OV9740_MIPI_CTRL_3012 0x3012 -#define OV9740_SC_CMMM_MIPI_CTR 0x3014 - -#define OV9740_MAX_WIDTH 1280 -#define OV9740_MAX_HEIGHT 720 - -/* Misc. structures */ -struct ov9740_reg { - u16 reg; - u8 val; -}; - -struct ov9740_priv { - struct v4l2_subdev subdev; - struct v4l2_ctrl_handler hdl; - struct v4l2_clk *clk; - - u16 model; - u8 revision; - u8 manid; - u8 smiaver; - - bool flag_vflip; - bool flag_hflip; - - /* For suspend/resume. */ - struct v4l2_mbus_framefmt current_mf; - bool current_enable; -}; - -static const struct ov9740_reg ov9740_defaults[] = { - /* Software Reset */ - { OV9740_SOFTWARE_RESET, 0x01 }, - - /* Banding Filter */ - { OV9740_AEC_B50_STEP_HI, 0x00 }, - { OV9740_AEC_B50_STEP_LO, 0xe8 }, - { OV9740_AEC_CTRL0E, 0x03 }, - { OV9740_AEC_MAXEXPO_50_H, 0x15 }, - { OV9740_AEC_MAXEXPO_50_L, 0xc6 }, - { OV9740_AEC_B60_STEP_HI, 0x00 }, - { OV9740_AEC_B60_STEP_LO, 0xc0 }, - { OV9740_AEC_CTRL0D, 0x04 }, - { OV9740_AEC_MAXEXPO_60_H, 0x18 }, - { OV9740_AEC_MAXEXPO_60_L, 0x20 }, - - /* LC */ - { 0x5842, 0x02 }, { 0x5843, 0x5e }, { 0x5844, 0x04 }, { 0x5845, 0x32 }, - { 0x5846, 0x03 }, { 0x5847, 0x29 }, { 0x5848, 0x02 }, { 0x5849, 0xcc }, - - /* Un-documented OV9740 registers */ - { 0x5800, 0x29 }, { 0x5801, 0x25 }, { 0x5802, 0x20 }, { 0x5803, 0x21 }, - { 0x5804, 0x26 }, { 0x5805, 0x2e }, { 0x5806, 0x11 }, { 0x5807, 0x0c }, - { 0x5808, 0x09 }, { 0x5809, 0x0a }, { 0x580a, 0x0e }, { 0x580b, 0x16 }, - { 0x580c, 0x06 }, { 0x580d, 0x02 }, { 0x580e, 0x00 }, { 0x580f, 0x00 }, - { 0x5810, 0x04 }, { 0x5811, 0x0a }, { 0x5812, 0x05 }, { 0x5813, 0x02 }, - { 0x5814, 0x00 }, { 0x5815, 0x00 }, { 0x5816, 0x03 }, { 0x5817, 0x09 }, - { 0x5818, 0x0f }, { 0x5819, 0x0a }, { 0x581a, 0x07 }, { 0x581b, 0x08 }, - { 0x581c, 0x0b }, { 0x581d, 0x14 }, { 0x581e, 0x28 }, { 0x581f, 0x23 }, - { 0x5820, 0x1d }, { 0x5821, 0x1e }, { 0x5822, 0x24 }, { 0x5823, 0x2a }, - { 0x5824, 0x4f }, { 0x5825, 0x6f }, { 0x5826, 0x5f }, { 0x5827, 0x7f }, - { 0x5828, 0x9f }, { 0x5829, 0x5f }, { 0x582a, 0x8f }, { 0x582b, 0x9e }, - { 0x582c, 0x8f }, { 0x582d, 0x9f }, { 0x582e, 0x4f }, { 0x582f, 0x87 }, - { 0x5830, 0x86 }, { 0x5831, 0x97 }, { 0x5832, 0xae }, { 0x5833, 0x3f }, - { 0x5834, 0x8e }, { 0x5835, 0x7c }, { 0x5836, 0x7e }, { 0x5837, 0xaf }, - { 0x5838, 0x8f }, { 0x5839, 0x8f }, { 0x583a, 0x9f }, { 0x583b, 0x7f }, - { 0x583c, 0x5f }, - - /* Y Gamma */ - { 0x5480, 0x07 }, { 0x5481, 0x18 }, { 0x5482, 0x2c }, { 0x5483, 0x4e }, - { 0x5484, 0x5e }, { 0x5485, 0x6b }, { 0x5486, 0x77 }, { 0x5487, 0x82 }, - { 0x5488, 0x8c }, { 0x5489, 0x95 }, { 0x548a, 0xa4 }, { 0x548b, 0xb1 }, - { 0x548c, 0xc6 }, { 0x548d, 0xd8 }, { 0x548e, 0xe9 }, - - /* UV Gamma */ - { 0x5490, 0x0f }, { 0x5491, 0xff }, { 0x5492, 0x0d }, { 0x5493, 0x05 }, - { 0x5494, 0x07 }, { 0x5495, 0x1a }, { 0x5496, 0x04 }, { 0x5497, 0x01 }, - { 0x5498, 0x03 }, { 0x5499, 0x53 }, { 0x549a, 0x02 }, { 0x549b, 0xeb }, - { 0x549c, 0x02 }, { 0x549d, 0xa0 }, { 0x549e, 0x02 }, { 0x549f, 0x67 }, - { 0x54a0, 0x02 }, { 0x54a1, 0x3b }, { 0x54a2, 0x02 }, { 0x54a3, 0x18 }, - { 0x54a4, 0x01 }, { 0x54a5, 0xe7 }, { 0x54a6, 0x01 }, { 0x54a7, 0xc3 }, - { 0x54a8, 0x01 }, { 0x54a9, 0x94 }, { 0x54aa, 0x01 }, { 0x54ab, 0x72 }, - { 0x54ac, 0x01 }, { 0x54ad, 0x57 }, - - /* AWB */ - { OV9740_AWB_CTRL00, 0xf0 }, - { OV9740_AWB_CTRL01, 0x00 }, - { OV9740_AWB_CTRL02, 0x41 }, - { OV9740_AWB_CTRL03, 0x42 }, - { OV9740_AWB_ADV_CTRL01, 0x8a }, - { OV9740_AWB_ADV_CTRL02, 0x61 }, - { OV9740_AWB_ADV_CTRL03, 0xce }, - { OV9740_AWB_ADV_CTRL04, 0xa8 }, - { OV9740_AWB_ADV_CTRL05, 0x17 }, - { OV9740_AWB_ADV_CTRL06, 0x1f }, - { OV9740_AWB_ADV_CTRL07, 0x27 }, - { OV9740_AWB_ADV_CTRL08, 0x41 }, - { OV9740_AWB_ADV_CTRL09, 0x34 }, - { OV9740_AWB_ADV_CTRL10, 0xf0 }, - { OV9740_AWB_ADV_CTRL11, 0x10 }, - { OV9740_AWB_CTRL0F, 0xff }, - { OV9740_AWB_CTRL10, 0x00 }, - { OV9740_AWB_CTRL11, 0xff }, - { OV9740_AWB_CTRL12, 0x00 }, - { OV9740_AWB_CTRL13, 0xff }, - { OV9740_AWB_CTRL14, 0x00 }, - - /* CIP */ - { 0x530d, 0x12 }, - - /* CMX */ - { 0x5380, 0x01 }, { 0x5381, 0x00 }, { 0x5382, 0x00 }, { 0x5383, 0x17 }, - { 0x5384, 0x00 }, { 0x5385, 0x01 }, { 0x5386, 0x00 }, { 0x5387, 0x00 }, - { 0x5388, 0x00 }, { 0x5389, 0xe0 }, { 0x538a, 0x00 }, { 0x538b, 0x20 }, - { 0x538c, 0x00 }, { 0x538d, 0x00 }, { 0x538e, 0x00 }, { 0x538f, 0x16 }, - { 0x5390, 0x00 }, { 0x5391, 0x9c }, { 0x5392, 0x00 }, { 0x5393, 0xa0 }, - { 0x5394, 0x18 }, - - /* 50/60 Detection */ - { 0x3c0a, 0x9c }, { 0x3c0b, 0x3f }, - - /* Output Select */ - { OV9740_IO_OUTPUT_SEL01, 0x00 }, - { OV9740_IO_OUTPUT_SEL02, 0x00 }, - { OV9740_IO_CREL00, 0x00 }, - { OV9740_IO_CREL01, 0x00 }, - { OV9740_IO_CREL02, 0x00 }, - - /* AWB Control */ - { OV9740_AWB_MANUAL_CTRL, 0x00 }, - - /* Analog Control */ - { OV9740_ANALOG_CTRL03, 0xaa }, - { OV9740_ANALOG_CTRL32, 0x2f }, - { OV9740_ANALOG_CTRL20, 0x66 }, - { OV9740_ANALOG_CTRL21, 0xc0 }, - { OV9740_ANALOG_CTRL31, 0x52 }, - { OV9740_ANALOG_CTRL33, 0x50 }, - { OV9740_ANALOG_CTRL30, 0xca }, - { OV9740_ANALOG_CTRL04, 0x0c }, - { OV9740_ANALOG_CTRL01, 0x40 }, - { OV9740_ANALOG_CTRL02, 0x16 }, - { OV9740_ANALOG_CTRL10, 0xa1 }, - { OV9740_ANALOG_CTRL12, 0x24 }, - { OV9740_ANALOG_CTRL22, 0x9f }, - { OV9740_ANALOG_CTRL15, 0xf0 }, - - /* Sensor Control */ - { OV9740_SENSOR_CTRL03, 0x42 }, - { OV9740_SENSOR_CTRL04, 0x10 }, - { OV9740_SENSOR_CTRL05, 0x45 }, - { OV9740_SENSOR_CTRL07, 0x14 }, - - /* Timing Control */ - { OV9740_TIMING_CTRL33, 0x04 }, - { OV9740_TIMING_CTRL35, 0x02 }, - { OV9740_TIMING_CTRL19, 0x6e }, - { OV9740_TIMING_CTRL17, 0x94 }, - - /* AEC/AGC Control */ - { OV9740_AEC_ENABLE, 0x10 }, - { OV9740_GAIN_CEILING_01, 0x00 }, - { OV9740_GAIN_CEILING_02, 0x7f }, - { OV9740_AEC_HI_THRESHOLD, 0xa0 }, - { OV9740_AEC_3A1A, 0x05 }, - { OV9740_AEC_CTRL1B_WPT2, 0x50 }, - { OV9740_AEC_CTRL0F_WPT, 0x50 }, - { OV9740_AEC_CTRL10_BPT, 0x4c }, - { OV9740_AEC_CTRL1E_BPT2, 0x4c }, - { OV9740_AEC_LO_THRESHOLD, 0x26 }, - - /* BLC Control */ - { OV9740_BLC_AUTO_ENABLE, 0x45 }, - { OV9740_BLC_MODE, 0x18 }, - - /* DVP Control */ - { OV9740_DVP_VSYNC_CTRL02, 0x04 }, - { OV9740_DVP_VSYNC_MODE, 0x00 }, - { OV9740_DVP_VSYNC_CTRL06, 0x08 }, - - /* PLL Setting */ - { OV9740_PLL_MODE_CTRL01, 0x20 }, - { OV9740_PRE_PLL_CLK_DIV, 0x03 }, - { OV9740_PLL_MULTIPLIER, 0x4c }, - { OV9740_VT_SYS_CLK_DIV, 0x01 }, - { OV9740_VT_PIX_CLK_DIV, 0x08 }, - { OV9740_PLL_CTRL3010, 0x01 }, - { OV9740_VFIFO_CTRL00, 0x82 }, - - /* Timing Setting */ - /* VTS */ - { OV9740_FRM_LENGTH_LN_HI, 0x03 }, - { OV9740_FRM_LENGTH_LN_LO, 0x07 }, - /* HTS */ - { OV9740_LN_LENGTH_PCK_HI, 0x06 }, - { OV9740_LN_LENGTH_PCK_LO, 0x62 }, - - /* MIPI Control */ - { OV9740_MIPI_CTRL00, 0x44 }, /* 0x64 for discontinuous clk */ - { OV9740_MIPI_3837, 0x01 }, - { OV9740_MIPI_CTRL01, 0x0f }, - { OV9740_MIPI_CTRL03, 0x05 }, - { OV9740_MIPI_CTRL05, 0x10 }, - { OV9740_VFIFO_RD_CTRL, 0x16 }, - { OV9740_MIPI_CTRL_3012, 0x70 }, - { OV9740_SC_CMMM_MIPI_CTR, 0x01 }, - - /* YUYV order */ - { OV9740_ISP_CTRL19, 0x02 }, -}; - -static u32 ov9740_codes[] = { - MEDIA_BUS_FMT_YUYV8_2X8, -}; - -/* read a register */ -static int ov9740_reg_read(struct i2c_client *client, u16 reg, u8 *val) -{ - int ret; - struct i2c_msg msg[] = { - { - .addr = client->addr, - .flags = 0, - .len = 2, - .buf = (u8 *)®, - }, - { - .addr = client->addr, - .flags = I2C_M_RD, - .len = 1, - .buf = val, - }, - }; - - reg = swab16(reg); - - ret = i2c_transfer(client->adapter, msg, 2); - if (ret < 0) { - dev_err(&client->dev, "Failed reading register 0x%04x!\n", reg); - return ret; - } - - return 0; -} - -/* write a register */ -static int ov9740_reg_write(struct i2c_client *client, u16 reg, u8 val) -{ - struct i2c_msg msg; - struct { - u16 reg; - u8 val; - } __packed buf; - int ret; - - reg = swab16(reg); - - buf.reg = reg; - buf.val = val; - - msg.addr = client->addr; - msg.flags = 0; - msg.len = 3; - msg.buf = (u8 *)&buf; - - ret = i2c_transfer(client->adapter, &msg, 1); - if (ret < 0) { - dev_err(&client->dev, "Failed writing register 0x%04x!\n", reg); - return ret; - } - - return 0; -} - - -/* Read a register, alter its bits, write it back */ -static int ov9740_reg_rmw(struct i2c_client *client, u16 reg, u8 set, u8 unset) -{ - u8 val; - int ret; - - ret = ov9740_reg_read(client, reg, &val); - if (ret < 0) { - dev_err(&client->dev, - "[Read]-Modify-Write of register 0x%04x failed!\n", - reg); - return ret; - } - - val |= set; - val &= ~unset; - - ret = ov9740_reg_write(client, reg, val); - if (ret < 0) { - dev_err(&client->dev, - "Read-Modify-[Write] of register 0x%04x failed!\n", - reg); - return ret; - } - - return 0; -} - -static int ov9740_reg_write_array(struct i2c_client *client, - const struct ov9740_reg *regarray, - int regarraylen) -{ - int i; - int ret; - - for (i = 0; i < regarraylen; i++) { - ret = ov9740_reg_write(client, - regarray[i].reg, regarray[i].val); - if (ret < 0) - return ret; - } - - return 0; -} - -/* Start/Stop streaming from the device */ -static int ov9740_s_stream(struct v4l2_subdev *sd, int enable) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct ov9740_priv *priv = to_ov9740(sd); - int ret; - - /* Program orientation register. */ - if (priv->flag_vflip) - ret = ov9740_reg_rmw(client, OV9740_IMAGE_ORT, 0x2, 0); - else - ret = ov9740_reg_rmw(client, OV9740_IMAGE_ORT, 0, 0x2); - if (ret < 0) - return ret; - - if (priv->flag_hflip) - ret = ov9740_reg_rmw(client, OV9740_IMAGE_ORT, 0x1, 0); - else - ret = ov9740_reg_rmw(client, OV9740_IMAGE_ORT, 0, 0x1); - if (ret < 0) - return ret; - - if (enable) { - dev_dbg(&client->dev, "Enabling Streaming\n"); - /* Start Streaming */ - ret = ov9740_reg_write(client, OV9740_MODE_SELECT, 0x01); - - } else { - dev_dbg(&client->dev, "Disabling Streaming\n"); - /* Software Reset */ - ret = ov9740_reg_write(client, OV9740_SOFTWARE_RESET, 0x01); - if (!ret) - /* Setting Streaming to Standby */ - ret = ov9740_reg_write(client, OV9740_MODE_SELECT, - 0x00); - } - - priv->current_enable = enable; - - return ret; -} - -/* select nearest higher resolution for capture */ -static void ov9740_res_roundup(u32 *width, u32 *height) -{ - /* Width must be a multiple of 4 pixels. */ - *width = ALIGN(*width, 4); - - /* Max resolution is 1280x720 (720p). */ - if (*width > OV9740_MAX_WIDTH) - *width = OV9740_MAX_WIDTH; - - if (*height > OV9740_MAX_HEIGHT) - *height = OV9740_MAX_HEIGHT; -} - -/* Setup registers according to resolution and color encoding */ -static int ov9740_set_res(struct i2c_client *client, u32 width, u32 height) -{ - u32 x_start; - u32 y_start; - u32 x_end; - u32 y_end; - bool scaling = false; - u32 scale_input_x; - u32 scale_input_y; - int ret; - - if ((width != OV9740_MAX_WIDTH) || (height != OV9740_MAX_HEIGHT)) - scaling = true; - - /* - * Try to use as much of the sensor area as possible when supporting - * smaller resolutions. Depending on the aspect ratio of the - * chosen resolution, we can either use the full width of the sensor, - * or the full height of the sensor (or both if the aspect ratio is - * the same as 1280x720. - */ - if ((OV9740_MAX_WIDTH * height) > (OV9740_MAX_HEIGHT * width)) { - scale_input_x = (OV9740_MAX_HEIGHT * width) / height; - scale_input_y = OV9740_MAX_HEIGHT; - } else { - scale_input_x = OV9740_MAX_WIDTH; - scale_input_y = (OV9740_MAX_WIDTH * height) / width; - } - - /* These describe the area of the sensor to use. */ - x_start = (OV9740_MAX_WIDTH - scale_input_x) / 2; - y_start = (OV9740_MAX_HEIGHT - scale_input_y) / 2; - x_end = x_start + scale_input_x - 1; - y_end = y_start + scale_input_y - 1; - - ret = ov9740_reg_write(client, OV9740_X_ADDR_START_HI, x_start >> 8); - if (ret) - goto done; - ret = ov9740_reg_write(client, OV9740_X_ADDR_START_LO, x_start & 0xff); - if (ret) - goto done; - ret = ov9740_reg_write(client, OV9740_Y_ADDR_START_HI, y_start >> 8); - if (ret) - goto done; - ret = ov9740_reg_write(client, OV9740_Y_ADDR_START_LO, y_start & 0xff); - if (ret) - goto done; - - ret = ov9740_reg_write(client, OV9740_X_ADDR_END_HI, x_end >> 8); - if (ret) - goto done; - ret = ov9740_reg_write(client, OV9740_X_ADDR_END_LO, x_end & 0xff); - if (ret) - goto done; - ret = ov9740_reg_write(client, OV9740_Y_ADDR_END_HI, y_end >> 8); - if (ret) - goto done; - ret = ov9740_reg_write(client, OV9740_Y_ADDR_END_LO, y_end & 0xff); - if (ret) - goto done; - - ret = ov9740_reg_write(client, OV9740_X_OUTPUT_SIZE_HI, width >> 8); - if (ret) - goto done; - ret = ov9740_reg_write(client, OV9740_X_OUTPUT_SIZE_LO, width & 0xff); - if (ret) - goto done; - ret = ov9740_reg_write(client, OV9740_Y_OUTPUT_SIZE_HI, height >> 8); - if (ret) - goto done; - ret = ov9740_reg_write(client, OV9740_Y_OUTPUT_SIZE_LO, height & 0xff); - if (ret) - goto done; - - ret = ov9740_reg_write(client, OV9740_ISP_CTRL1E, scale_input_x >> 8); - if (ret) - goto done; - ret = ov9740_reg_write(client, OV9740_ISP_CTRL1F, scale_input_x & 0xff); - if (ret) - goto done; - ret = ov9740_reg_write(client, OV9740_ISP_CTRL20, scale_input_y >> 8); - if (ret) - goto done; - ret = ov9740_reg_write(client, OV9740_ISP_CTRL21, scale_input_y & 0xff); - if (ret) - goto done; - - ret = ov9740_reg_write(client, OV9740_VFIFO_READ_START_HI, - (scale_input_x - width) >> 8); - if (ret) - goto done; - ret = ov9740_reg_write(client, OV9740_VFIFO_READ_START_LO, - (scale_input_x - width) & 0xff); - if (ret) - goto done; - - ret = ov9740_reg_write(client, OV9740_ISP_CTRL00, 0xff); - if (ret) - goto done; - ret = ov9740_reg_write(client, OV9740_ISP_CTRL01, 0xef | - (scaling << 4)); - if (ret) - goto done; - ret = ov9740_reg_write(client, OV9740_ISP_CTRL03, 0xff); - -done: - return ret; -} - -/* set the format we will capture in */ -static int ov9740_s_fmt(struct v4l2_subdev *sd, - struct v4l2_mbus_framefmt *mf) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct ov9740_priv *priv = to_ov9740(sd); - int ret; - - ret = ov9740_reg_write_array(client, ov9740_defaults, - ARRAY_SIZE(ov9740_defaults)); - if (ret < 0) - return ret; - - ret = ov9740_set_res(client, mf->width, mf->height); - if (ret < 0) - return ret; - - priv->current_mf = *mf; - return ret; -} - -static int ov9740_set_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, - struct v4l2_subdev_format *format) -{ - struct v4l2_mbus_framefmt *mf = &format->format; - - if (format->pad) - return -EINVAL; - - ov9740_res_roundup(&mf->width, &mf->height); - - mf->field = V4L2_FIELD_NONE; - mf->code = MEDIA_BUS_FMT_YUYV8_2X8; - mf->colorspace = V4L2_COLORSPACE_SRGB; - - if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE) - return ov9740_s_fmt(sd, mf); - cfg->try_fmt = *mf; - return 0; -} - -static int ov9740_enum_mbus_code(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, - struct v4l2_subdev_mbus_code_enum *code) -{ - if (code->pad || code->index >= ARRAY_SIZE(ov9740_codes)) - return -EINVAL; - - code->code = ov9740_codes[code->index]; - - return 0; -} - -static int ov9740_get_selection(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, - struct v4l2_subdev_selection *sel) -{ - if (sel->which != V4L2_SUBDEV_FORMAT_ACTIVE) - return -EINVAL; - - switch (sel->target) { - case V4L2_SEL_TGT_CROP_BOUNDS: - case V4L2_SEL_TGT_CROP: - sel->r.left = 0; - sel->r.top = 0; - sel->r.width = OV9740_MAX_WIDTH; - sel->r.height = OV9740_MAX_HEIGHT; - return 0; - default: - return -EINVAL; - } -} - -/* Set status of additional camera capabilities */ -static int ov9740_s_ctrl(struct v4l2_ctrl *ctrl) -{ - struct ov9740_priv *priv = - container_of(ctrl->handler, struct ov9740_priv, hdl); - - switch (ctrl->id) { - case V4L2_CID_VFLIP: - priv->flag_vflip = ctrl->val; - break; - case V4L2_CID_HFLIP: - priv->flag_hflip = ctrl->val; - break; - default: - return -EINVAL; - } - - return 0; -} - -static int ov9740_s_power(struct v4l2_subdev *sd, int on) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); - struct ov9740_priv *priv = to_ov9740(sd); - int ret; - - if (on) { - ret = soc_camera_power_on(&client->dev, ssdd, priv->clk); - if (ret < 0) - return ret; - - if (priv->current_enable) { - ov9740_s_fmt(sd, &priv->current_mf); - ov9740_s_stream(sd, 1); - } - } else { - if (priv->current_enable) { - ov9740_s_stream(sd, 0); - priv->current_enable = true; - } - - soc_camera_power_off(&client->dev, ssdd, priv->clk); - } - - return 0; -} - -#ifdef CONFIG_VIDEO_ADV_DEBUG -static int ov9740_get_register(struct v4l2_subdev *sd, - struct v4l2_dbg_register *reg) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - int ret; - u8 val; - - if (reg->reg & ~0xffff) - return -EINVAL; - - reg->size = 2; - - ret = ov9740_reg_read(client, reg->reg, &val); - if (ret) - return ret; - - reg->val = (__u64)val; - - return ret; -} - -static int ov9740_set_register(struct v4l2_subdev *sd, - const struct v4l2_dbg_register *reg) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - - if (reg->reg & ~0xffff || reg->val & ~0xff) - return -EINVAL; - - return ov9740_reg_write(client, reg->reg, reg->val); -} -#endif - -static int ov9740_video_probe(struct i2c_client *client) -{ - struct v4l2_subdev *sd = i2c_get_clientdata(client); - struct ov9740_priv *priv = to_ov9740(sd); - u8 modelhi, modello; - int ret; - - ret = ov9740_s_power(&priv->subdev, 1); - if (ret < 0) - return ret; - - /* - * check and show product ID and manufacturer ID - */ - ret = ov9740_reg_read(client, OV9740_MODEL_ID_HI, &modelhi); - if (ret < 0) - goto done; - - ret = ov9740_reg_read(client, OV9740_MODEL_ID_LO, &modello); - if (ret < 0) - goto done; - - priv->model = (modelhi << 8) | modello; - - ret = ov9740_reg_read(client, OV9740_REVISION_NUMBER, &priv->revision); - if (ret < 0) - goto done; - - ret = ov9740_reg_read(client, OV9740_MANUFACTURER_ID, &priv->manid); - if (ret < 0) - goto done; - - ret = ov9740_reg_read(client, OV9740_SMIA_VERSION, &priv->smiaver); - if (ret < 0) - goto done; - - if (priv->model != 0x9740) { - ret = -ENODEV; - goto done; - } - - dev_info(&client->dev, "ov9740 Model ID 0x%04x, Revision 0x%02x, Manufacturer 0x%02x, SMIA Version 0x%02x\n", - priv->model, priv->revision, priv->manid, priv->smiaver); - - ret = v4l2_ctrl_handler_setup(&priv->hdl); - -done: - ov9740_s_power(&priv->subdev, 0); - return ret; -} - -/* Request bus settings on camera side */ -static int ov9740_g_mbus_config(struct v4l2_subdev *sd, - struct v4l2_mbus_config *cfg) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); - - cfg->flags = V4L2_MBUS_PCLK_SAMPLE_RISING | V4L2_MBUS_MASTER | - V4L2_MBUS_VSYNC_ACTIVE_HIGH | V4L2_MBUS_HSYNC_ACTIVE_HIGH | - V4L2_MBUS_DATA_ACTIVE_HIGH; - cfg->type = V4L2_MBUS_PARALLEL; - cfg->flags = soc_camera_apply_board_flags(ssdd, cfg); - - return 0; -} - -static const struct v4l2_subdev_video_ops ov9740_video_ops = { - .s_stream = ov9740_s_stream, - .g_mbus_config = ov9740_g_mbus_config, -}; - -static const struct v4l2_subdev_core_ops ov9740_core_ops = { - .s_power = ov9740_s_power, -#ifdef CONFIG_VIDEO_ADV_DEBUG - .g_register = ov9740_get_register, - .s_register = ov9740_set_register, -#endif -}; - -static const struct v4l2_subdev_pad_ops ov9740_pad_ops = { - .enum_mbus_code = ov9740_enum_mbus_code, - .get_selection = ov9740_get_selection, - .set_fmt = ov9740_set_fmt, -}; - -static const struct v4l2_subdev_ops ov9740_subdev_ops = { - .core = &ov9740_core_ops, - .video = &ov9740_video_ops, - .pad = &ov9740_pad_ops, -}; - -static const struct v4l2_ctrl_ops ov9740_ctrl_ops = { - .s_ctrl = ov9740_s_ctrl, -}; - -/* - * i2c_driver function - */ -static int ov9740_probe(struct i2c_client *client, - const struct i2c_device_id *did) -{ - struct ov9740_priv *priv; - struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); - int ret; - - if (!ssdd) { - dev_err(&client->dev, "Missing platform_data for driver\n"); - return -EINVAL; - } - - priv = devm_kzalloc(&client->dev, sizeof(*priv), GFP_KERNEL); - if (!priv) - return -ENOMEM; - - v4l2_i2c_subdev_init(&priv->subdev, client, &ov9740_subdev_ops); - v4l2_ctrl_handler_init(&priv->hdl, 13); - v4l2_ctrl_new_std(&priv->hdl, &ov9740_ctrl_ops, - V4L2_CID_VFLIP, 0, 1, 1, 0); - v4l2_ctrl_new_std(&priv->hdl, &ov9740_ctrl_ops, - V4L2_CID_HFLIP, 0, 1, 1, 0); - priv->subdev.ctrl_handler = &priv->hdl; - if (priv->hdl.error) - return priv->hdl.error; - - priv->clk = v4l2_clk_get(&client->dev, "mclk"); - if (IS_ERR(priv->clk)) { - ret = PTR_ERR(priv->clk); - goto eclkget; - } - - ret = ov9740_video_probe(client); - if (ret < 0) { - v4l2_clk_put(priv->clk); -eclkget: - v4l2_ctrl_handler_free(&priv->hdl); - } - - return ret; -} - -static int ov9740_remove(struct i2c_client *client) -{ - struct ov9740_priv *priv = i2c_get_clientdata(client); - - v4l2_clk_put(priv->clk); - v4l2_device_unregister_subdev(&priv->subdev); - v4l2_ctrl_handler_free(&priv->hdl); - return 0; -} - -static const struct i2c_device_id ov9740_id[] = { - { "ov9740", 0 }, - { } -}; -MODULE_DEVICE_TABLE(i2c, ov9740_id); - -static struct i2c_driver ov9740_i2c_driver = { - .driver = { - .name = "ov9740", - }, - .probe = ov9740_probe, - .remove = ov9740_remove, - .id_table = ov9740_id, -}; - -module_i2c_driver(ov9740_i2c_driver); - -MODULE_DESCRIPTION("SoC Camera driver for OmniVision OV9740"); -MODULE_AUTHOR("Andrew Chew "); -MODULE_LICENSE("GPL v2"); diff --git a/include/media/drv-intf/soc_mediabus.h b/include/media/drv-intf/soc_mediabus.h deleted file mode 100644 index 361f8852c9fcb..0000000000000 --- a/include/media/drv-intf/soc_mediabus.h +++ /dev/null @@ -1,107 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* - * SoC-camera Media Bus API extensions - * - * Copyright (C) 2009, Guennadi Liakhovetski - */ - -#ifndef SOC_MEDIABUS_H -#define SOC_MEDIABUS_H - -#include -#include - -/** - * enum soc_mbus_packing - data packing types on the media-bus - * @SOC_MBUS_PACKING_NONE: no packing, bit-for-bit transfer to RAM, one - * sample represents one pixel - * @SOC_MBUS_PACKING_2X8_PADHI: 16 bits transferred in 2 8-bit samples, in the - * possibly incomplete byte high bits are padding - * @SOC_MBUS_PACKING_2X8_PADLO: as above, but low bits are padding - * @SOC_MBUS_PACKING_EXTEND16: sample width (e.g., 10 bits) has to be extended - * to 16 bits - * @SOC_MBUS_PACKING_VARIABLE: compressed formats with variable packing - * @SOC_MBUS_PACKING_1_5X8: used for packed YUV 4:2:0 formats, where 4 - * pixels occupy 6 bytes in RAM - * @SOC_MBUS_PACKING_EXTEND32: sample width (e.g., 24 bits) has to be extended - * to 32 bits - */ -enum soc_mbus_packing { - SOC_MBUS_PACKING_NONE, - SOC_MBUS_PACKING_2X8_PADHI, - SOC_MBUS_PACKING_2X8_PADLO, - SOC_MBUS_PACKING_EXTEND16, - SOC_MBUS_PACKING_VARIABLE, - SOC_MBUS_PACKING_1_5X8, - SOC_MBUS_PACKING_EXTEND32, -}; - -/** - * enum soc_mbus_order - sample order on the media bus - * @SOC_MBUS_ORDER_LE: least significant sample first - * @SOC_MBUS_ORDER_BE: most significant sample first - */ -enum soc_mbus_order { - SOC_MBUS_ORDER_LE, - SOC_MBUS_ORDER_BE, -}; - -/** - * enum soc_mbus_layout - planes layout in memory - * @SOC_MBUS_LAYOUT_PACKED: color components packed - * @SOC_MBUS_LAYOUT_PLANAR_2Y_U_V: YUV components stored in 3 planes (4:2:2) - * @SOC_MBUS_LAYOUT_PLANAR_2Y_C: YUV components stored in a luma and a - * chroma plane (C plane is half the size - * of Y plane) - * @SOC_MBUS_LAYOUT_PLANAR_Y_C: YUV components stored in a luma and a - * chroma plane (C plane is the same size - * as Y plane) - */ -enum soc_mbus_layout { - SOC_MBUS_LAYOUT_PACKED = 0, - SOC_MBUS_LAYOUT_PLANAR_2Y_U_V, - SOC_MBUS_LAYOUT_PLANAR_2Y_C, - SOC_MBUS_LAYOUT_PLANAR_Y_C, -}; - -/** - * struct soc_mbus_pixelfmt - Data format on the media bus - * @fourcc: Fourcc code, that will be obtained if the data is - * stored in memory in the following way: - * @packing: Type of sample-packing, that has to be used - * @order: Sample order when storing in memory - * @bits_per_sample: How many bits the bridge has to sample - */ -struct soc_mbus_pixelfmt { - u32 fourcc; - enum soc_mbus_packing packing; - enum soc_mbus_order order; - enum soc_mbus_layout layout; - u8 bits_per_sample; -}; - -/** - * struct soc_mbus_lookup - Lookup FOURCC IDs by mediabus codes for pass-through - * @code: mediabus pixel-code - * @fmt: pixel format description - */ -struct soc_mbus_lookup { - u32 code; - struct soc_mbus_pixelfmt fmt; -}; - -const struct soc_mbus_pixelfmt *soc_mbus_find_fmtdesc( - u32 code, - const struct soc_mbus_lookup *lookup, - int n); -const struct soc_mbus_pixelfmt *soc_mbus_get_fmtdesc( - u32 code); -s32 soc_mbus_bytes_per_line(u32 width, const struct soc_mbus_pixelfmt *mf); -s32 soc_mbus_image_size(const struct soc_mbus_pixelfmt *mf, - u32 bytes_per_line, u32 height); -int soc_mbus_samples_per_pixel(const struct soc_mbus_pixelfmt *mf, - unsigned int *numerator, unsigned int *denominator); -unsigned int soc_mbus_config_compatible(const struct v4l2_mbus_config *cfg, - unsigned int flags); - -#endif -- GitLab From 19d1d03ed232242c7d6246179c7c2a83ee3e107f Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 26 Jun 2020 13:53:21 +0200 Subject: [PATCH 0236/1476] media: soc_camera.h: remove this unused header The soc_camera driver has been removed and all board files that used it have been fixed. This header can now be removed altogether. Signed-off-by: Hans Verkuil Cc: Arnd Bergmann Signed-off-by: Mauro Carvalho Chehab --- include/media/soc_camera.h | 397 ------------------------------------- 1 file changed, 397 deletions(-) delete mode 100644 include/media/soc_camera.h diff --git a/include/media/soc_camera.h b/include/media/soc_camera.h deleted file mode 100644 index 331c343a5b5a9..0000000000000 --- a/include/media/soc_camera.h +++ /dev/null @@ -1,397 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* - * camera image capture (abstract) bus driver header - * - * Copyright (C) 2006, Sascha Hauer, Pengutronix - * Copyright (C) 2008, Guennadi Liakhovetski - */ - -#ifndef SOC_CAMERA_H -#define SOC_CAMERA_H - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -struct file; -struct soc_camera_desc; -struct soc_camera_async_client; - -struct soc_camera_device { - struct list_head list; /* list of all registered devices */ - struct soc_camera_desc *sdesc; - struct device *pdev; /* Platform device */ - struct device *parent; /* Camera host device */ - struct device *control; /* E.g., the i2c client */ - s32 user_width; - s32 user_height; - u32 bytesperline; /* for padding, zero if unused */ - u32 sizeimage; - enum v4l2_colorspace colorspace; - unsigned char iface; /* Host number */ - unsigned char devnum; /* Device number per host */ - struct soc_camera_sense *sense; /* See comment in struct definition */ - struct video_device *vdev; - struct v4l2_ctrl_handler ctrl_handler; - const struct soc_camera_format_xlate *current_fmt; - struct soc_camera_format_xlate *user_formats; - int num_user_formats; - enum v4l2_field field; /* Preserve field over close() */ - void *host_priv; /* Per-device host private data */ - /* soc_camera.c private count. Only accessed with .host_lock held */ - int use_count; - struct file *streamer; /* stream owner */ - struct v4l2_clk *clk; - /* Asynchronous subdevice management */ - struct soc_camera_async_client *sasc; - /* video buffer queue */ - struct vb2_queue vb2_vidq; -}; - -/* Host supports programmable stride */ -#define SOCAM_HOST_CAP_STRIDE (1 << 0) - -enum soc_camera_subdev_role { - SOCAM_SUBDEV_DATA_SOURCE = 1, - SOCAM_SUBDEV_DATA_SINK, - SOCAM_SUBDEV_DATA_PROCESSOR, -}; - -struct soc_camera_async_subdev { - struct v4l2_async_subdev asd; - enum soc_camera_subdev_role role; -}; - -struct soc_camera_host { - struct v4l2_device v4l2_dev; - struct list_head list; - struct mutex host_lock; /* Main synchronisation lock */ - struct mutex clk_lock; /* Protect pipeline modifications */ - unsigned char nr; /* Host number */ - u32 capabilities; - struct soc_camera_device *icd; /* Currently attached client */ - void *priv; - const char *drv_name; - struct soc_camera_host_ops *ops; - struct v4l2_async_subdev **asd; /* Flat array, arranged in groups */ - unsigned int *asd_sizes; /* 0-terminated array of asd group sizes */ -}; - -struct soc_camera_host_ops { - struct module *owner; - int (*add)(struct soc_camera_device *); - void (*remove)(struct soc_camera_device *); - int (*clock_start)(struct soc_camera_host *); - void (*clock_stop)(struct soc_camera_host *); - /* - * .get_formats() is called for each client device format, but - * .put_formats() is only called once. Further, if any of the calls to - * .get_formats() fail, .put_formats() will not be called at all, the - * failing .get_formats() must then clean up internally. - */ - int (*get_formats)(struct soc_camera_device *, unsigned int, - struct soc_camera_format_xlate *); - void (*put_formats)(struct soc_camera_device *); - int (*get_selection)(struct soc_camera_device *, struct v4l2_selection *); - int (*set_selection)(struct soc_camera_device *, struct v4l2_selection *); - /* - * The difference to .set_selection() is, that .set_liveselection is not allowed - * to change the output sizes - */ - int (*set_liveselection)(struct soc_camera_device *, struct v4l2_selection *); - int (*set_fmt)(struct soc_camera_device *, struct v4l2_format *); - int (*try_fmt)(struct soc_camera_device *, struct v4l2_format *); - int (*init_videobuf2)(struct vb2_queue *, - struct soc_camera_device *); - int (*querycap)(struct soc_camera_host *, struct v4l2_capability *); - int (*set_bus_param)(struct soc_camera_device *); - int (*get_parm)(struct soc_camera_device *, struct v4l2_streamparm *); - int (*set_parm)(struct soc_camera_device *, struct v4l2_streamparm *); - int (*enum_framesizes)(struct soc_camera_device *, struct v4l2_frmsizeenum *); - __poll_t (*poll)(struct file *, poll_table *); -}; - -#define SOCAM_SENSOR_INVERT_PCLK (1 << 0) -#define SOCAM_SENSOR_INVERT_MCLK (1 << 1) -#define SOCAM_SENSOR_INVERT_HSYNC (1 << 2) -#define SOCAM_SENSOR_INVERT_VSYNC (1 << 3) -#define SOCAM_SENSOR_INVERT_DATA (1 << 4) - -struct i2c_board_info; -struct regulator_bulk_data; - -struct soc_camera_subdev_desc { - /* Per camera SOCAM_SENSOR_* bus flags */ - unsigned long flags; - - /* sensor driver private platform data */ - void *drv_priv; - - /* - * Set unbalanced_power to true to deal with legacy drivers, failing to - * balance their calls to subdevice's .s_power() method. clock_state is - * then used internally by helper functions, it shouldn't be touched by - * drivers or the platform code. - */ - bool unbalanced_power; - unsigned long clock_state; - - /* Optional callbacks to power on or off and reset the sensor */ - int (*power)(struct device *, int); - int (*reset)(struct device *); - - /* - * some platforms may support different data widths than the sensors - * native ones due to different data line routing. Let the board code - * overwrite the width flags. - */ - int (*set_bus_param)(struct soc_camera_subdev_desc *, unsigned long flags); - unsigned long (*query_bus_param)(struct soc_camera_subdev_desc *); - void (*free_bus)(struct soc_camera_subdev_desc *); - - /* Optional regulators that have to be managed on power on/off events */ - struct v4l2_subdev_platform_data sd_pdata; -}; - -struct soc_camera_host_desc { - /* Camera bus id, used to match a camera and a bus */ - int bus_id; - int i2c_adapter_id; - struct i2c_board_info *board_info; - const char *module_name; - - /* - * For non-I2C devices platform has to provide methods to add a device - * to the system and to remove it - */ - int (*add_device)(struct soc_camera_device *); - void (*del_device)(struct soc_camera_device *); -}; - -/* - * Platform data for "soc-camera-pdrv" - * This MUST be kept binary-identical to struct soc_camera_link below, until - * it is completely replaced by this one, after which we can split it into its - * two components. - */ -struct soc_camera_desc { - struct soc_camera_subdev_desc subdev_desc; - struct soc_camera_host_desc host_desc; -}; - -/* Prepare to replace this struct: don't change its layout any more! */ -struct soc_camera_link { - /* - * Subdevice part - keep at top and compatible to - * struct soc_camera_subdev_desc - */ - - /* Per camera SOCAM_SENSOR_* bus flags */ - unsigned long flags; - - void *priv; - - /* Set by platforms to handle misbehaving drivers */ - bool unbalanced_power; - /* Used by soc-camera helper functions */ - unsigned long clock_state; - - /* Optional callbacks to power on or off and reset the sensor */ - int (*power)(struct device *, int); - int (*reset)(struct device *); - /* - * some platforms may support different data widths than the sensors - * native ones due to different data line routing. Let the board code - * overwrite the width flags. - */ - int (*set_bus_param)(struct soc_camera_link *, unsigned long flags); - unsigned long (*query_bus_param)(struct soc_camera_link *); - void (*free_bus)(struct soc_camera_link *); - - /* Optional regulators that have to be managed on power on/off events */ - struct regulator_bulk_data *regulators; - int num_regulators; - - void *host_priv; - - /* - * Host part - keep at bottom and compatible to - * struct soc_camera_host_desc - */ - - /* Camera bus id, used to match a camera and a bus */ - int bus_id; - int i2c_adapter_id; - struct i2c_board_info *board_info; - const char *module_name; - - /* - * For non-I2C devices platform has to provide methods to add a device - * to the system and to remove it - */ - int (*add_device)(struct soc_camera_device *); - void (*del_device)(struct soc_camera_device *); -}; - -static inline struct soc_camera_host *to_soc_camera_host( - const struct device *dev) -{ - struct v4l2_device *v4l2_dev = dev_get_drvdata(dev); - - return container_of(v4l2_dev, struct soc_camera_host, v4l2_dev); -} - -static inline struct soc_camera_desc *to_soc_camera_desc( - const struct soc_camera_device *icd) -{ - return icd->sdesc; -} - -static inline struct device *to_soc_camera_control( - const struct soc_camera_device *icd) -{ - return icd->control; -} - -static inline struct v4l2_subdev *soc_camera_to_subdev( - const struct soc_camera_device *icd) -{ - struct device *control = to_soc_camera_control(icd); - return dev_get_drvdata(control); -} - -int soc_camera_host_register(struct soc_camera_host *ici); -void soc_camera_host_unregister(struct soc_camera_host *ici); - -const struct soc_camera_format_xlate *soc_camera_xlate_by_fourcc( - struct soc_camera_device *icd, unsigned int fourcc); - -/** - * struct soc_camera_format_xlate - match between host and sensor formats - * @code: code of a sensor provided format - * @host_fmt: host format after host translation from code - * - * Host and sensor translation structure. Used in table of host and sensor - * formats matchings in soc_camera_device. A host can override the generic list - * generation by implementing get_formats(), and use it for format checks and - * format setup. - */ -struct soc_camera_format_xlate { - u32 code; - const struct soc_mbus_pixelfmt *host_fmt; -}; - -#define SOCAM_SENSE_PCLK_CHANGED (1 << 0) - -/** - * This struct can be attached to struct soc_camera_device by the host driver - * to request sense from the camera, for example, when calling .set_fmt(). The - * host then can check which flags are set and verify respective values if any. - * For example, if SOCAM_SENSE_PCLK_CHANGED is set, it means, pixclock has - * changed during this operation. After completion the host should detach sense. - * - * @flags ored SOCAM_SENSE_* flags - * @master_clock if the host wants to be informed about pixel-clock - * change, it better set master_clock. - * @pixel_clock_max maximum pixel clock frequency supported by the host, - * camera is not allowed to exceed this. - * @pixel_clock if the camera driver changed pixel clock during this - * operation, it sets SOCAM_SENSE_PCLK_CHANGED, uses - * master_clock to calculate the new pixel-clock and - * sets this field. - */ -struct soc_camera_sense { - unsigned long flags; - unsigned long master_clock; - unsigned long pixel_clock_max; - unsigned long pixel_clock; -}; - -#define SOCAM_DATAWIDTH(x) BIT((x) - 1) -#define SOCAM_DATAWIDTH_4 SOCAM_DATAWIDTH(4) -#define SOCAM_DATAWIDTH_8 SOCAM_DATAWIDTH(8) -#define SOCAM_DATAWIDTH_9 SOCAM_DATAWIDTH(9) -#define SOCAM_DATAWIDTH_10 SOCAM_DATAWIDTH(10) -#define SOCAM_DATAWIDTH_12 SOCAM_DATAWIDTH(12) -#define SOCAM_DATAWIDTH_15 SOCAM_DATAWIDTH(15) -#define SOCAM_DATAWIDTH_16 SOCAM_DATAWIDTH(16) -#define SOCAM_DATAWIDTH_18 SOCAM_DATAWIDTH(18) -#define SOCAM_DATAWIDTH_24 SOCAM_DATAWIDTH(24) - -#define SOCAM_DATAWIDTH_MASK (SOCAM_DATAWIDTH_4 | SOCAM_DATAWIDTH_8 | \ - SOCAM_DATAWIDTH_9 | SOCAM_DATAWIDTH_10 | \ - SOCAM_DATAWIDTH_12 | SOCAM_DATAWIDTH_15 | \ - SOCAM_DATAWIDTH_16 | SOCAM_DATAWIDTH_18 | \ - SOCAM_DATAWIDTH_24) - -static inline void soc_camera_limit_side(int *start, int *length, - unsigned int start_min, - unsigned int length_min, unsigned int length_max) -{ - if (*length < length_min) - *length = length_min; - else if (*length > length_max) - *length = length_max; - - if (*start < start_min) - *start = start_min; - else if (*start > start_min + length_max - *length) - *start = start_min + length_max - *length; -} - -unsigned long soc_camera_apply_board_flags(struct soc_camera_subdev_desc *ssdd, - const struct v4l2_mbus_config *cfg); - -int soc_camera_power_init(struct device *dev, struct soc_camera_subdev_desc *ssdd); -int soc_camera_power_on(struct device *dev, struct soc_camera_subdev_desc *ssdd, - struct v4l2_clk *clk); -int soc_camera_power_off(struct device *dev, struct soc_camera_subdev_desc *ssdd, - struct v4l2_clk *clk); - -static inline int soc_camera_set_power(struct device *dev, - struct soc_camera_subdev_desc *ssdd, struct v4l2_clk *clk, bool on) -{ - return on ? soc_camera_power_on(dev, ssdd, clk) - : soc_camera_power_off(dev, ssdd, clk); -} - -/* This is only temporary here - until v4l2-subdev begins to link to video_device */ -#include -static inline struct video_device *soc_camera_i2c_to_vdev(const struct i2c_client *client) -{ - struct v4l2_subdev *sd = i2c_get_clientdata(client); - struct soc_camera_device *icd = v4l2_get_subdev_hostdata(sd); - return icd ? icd->vdev : NULL; -} - -static inline struct soc_camera_subdev_desc *soc_camera_i2c_to_desc(const struct i2c_client *client) -{ - return client->dev.platform_data; -} - -static inline struct v4l2_subdev *soc_camera_vdev_to_subdev(struct video_device *vdev) -{ - struct soc_camera_device *icd = video_get_drvdata(vdev); - return soc_camera_to_subdev(icd); -} - -static inline struct soc_camera_device *soc_camera_from_vb2q(const struct vb2_queue *vq) -{ - return container_of(vq, struct soc_camera_device, vb2_vidq); -} - -static inline u32 soc_camera_grp_id(const struct soc_camera_device *icd) -{ - return (icd->iface << 8) | (icd->devnum + 1); -} - -void soc_camera_lock(struct vb2_queue *vq); -void soc_camera_unlock(struct vb2_queue *vq); - -#endif -- GitLab From 39bc4e4e1534dceceba1646cd324dcecfaa6379d Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 26 Jun 2020 15:11:18 +0200 Subject: [PATCH 0237/1476] media: arch/arm/configs: remove obsolete CONFIG_SOC_CAMERA* Drop all configs with the CONFIG_SOC_CAMERA prefix since those have been removed. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- arch/arm/configs/ezx_defconfig | 2 -- arch/arm/configs/imote2_defconfig | 2 -- arch/arm/configs/imx_v4_v5_defconfig | 1 - arch/arm/configs/imx_v6_v7_defconfig | 1 - arch/arm/configs/pxa_defconfig | 3 --- arch/arm/configs/sama5_defconfig | 2 -- 6 files changed, 11 deletions(-) diff --git a/arch/arm/configs/ezx_defconfig b/arch/arm/configs/ezx_defconfig index bd7b7f945e018..81665b7abf830 100644 --- a/arch/arm/configs/ezx_defconfig +++ b/arch/arm/configs/ezx_defconfig @@ -236,8 +236,6 @@ CONFIG_MEDIA_TUNER_CUSTOMISE=y # CONFIG_MEDIA_TUNER_MXL5007T is not set # CONFIG_MEDIA_TUNER_MC44S803 is not set # CONFIG_VIDEO_HELPER_CHIPS_AUTO is not set -CONFIG_SOC_CAMERA=y -CONFIG_SOC_CAMERA_MT9M111=y CONFIG_VIDEO_PXA27x=y # CONFIG_V4L_USB_DRIVERS is not set CONFIG_RADIO_TEA5764=y diff --git a/arch/arm/configs/imote2_defconfig b/arch/arm/configs/imote2_defconfig index 05c5515fa8710..ae15a2a33802a 100644 --- a/arch/arm/configs/imote2_defconfig +++ b/arch/arm/configs/imote2_defconfig @@ -217,8 +217,6 @@ CONFIG_MEDIA_TUNER_CUSTOMISE=y # CONFIG_MEDIA_TUNER_MXL5007T is not set # CONFIG_MEDIA_TUNER_MC44S803 is not set # CONFIG_VIDEO_HELPER_CHIPS_AUTO is not set -CONFIG_SOC_CAMERA=y -CONFIG_SOC_CAMERA_MT9M111=y CONFIG_VIDEO_PXA27x=y # CONFIG_V4L_USB_DRIVERS is not set # CONFIG_RADIO_ADAPTERS is not set diff --git a/arch/arm/configs/imx_v4_v5_defconfig b/arch/arm/configs/imx_v4_v5_defconfig index 3df90fc383983..f5f1111f21187 100644 --- a/arch/arm/configs/imx_v4_v5_defconfig +++ b/arch/arm/configs/imx_v4_v5_defconfig @@ -110,7 +110,6 @@ CONFIG_REGULATOR_MC13892=y CONFIG_MEDIA_SUPPORT=y CONFIG_MEDIA_CAMERA_SUPPORT=y CONFIG_V4L_PLATFORM_DRIVERS=y -CONFIG_SOC_CAMERA=y CONFIG_V4L_MEM2MEM_DRIVERS=y CONFIG_VIDEO_CODA=y CONFIG_FB=y diff --git a/arch/arm/configs/imx_v6_v7_defconfig b/arch/arm/configs/imx_v6_v7_defconfig index 87e6400c436bd..38968b862630b 100644 --- a/arch/arm/configs/imx_v6_v7_defconfig +++ b/arch/arm/configs/imx_v6_v7_defconfig @@ -271,7 +271,6 @@ CONFIG_MEDIA_USB_SUPPORT=y CONFIG_USB_VIDEO_CLASS=m CONFIG_V4L_PLATFORM_DRIVERS=y CONFIG_VIDEO_MUX=y -CONFIG_SOC_CAMERA=y CONFIG_V4L_MEM2MEM_DRIVERS=y CONFIG_VIDEO_CODA=m CONFIG_VIDEO_IMX_PXP=y diff --git a/arch/arm/configs/pxa_defconfig b/arch/arm/configs/pxa_defconfig index e6559e3350e63..aa94d22df0aa7 100644 --- a/arch/arm/configs/pxa_defconfig +++ b/arch/arm/configs/pxa_defconfig @@ -445,11 +445,8 @@ CONFIG_VIDEO_V4L2_SUBDEV_API=y CONFIG_MEDIA_USB_SUPPORT=y CONFIG_USB_VIDEO_CLASS=m CONFIG_V4L_PLATFORM_DRIVERS=y -CONFIG_SOC_CAMERA=m -CONFIG_SOC_CAMERA_PLATFORM=m CONFIG_VIDEO_PXA27x=m CONFIG_V4L_MEM2MEM_DRIVERS=y -CONFIG_SOC_CAMERA_MT9M111=m CONFIG_DRM=m CONFIG_FIRMWARE_EDID=y CONFIG_FB_TILEBLITTING=y diff --git a/arch/arm/configs/sama5_defconfig b/arch/arm/configs/sama5_defconfig index 8e1f78c199202..012812d6bb33a 100644 --- a/arch/arm/configs/sama5_defconfig +++ b/arch/arm/configs/sama5_defconfig @@ -148,9 +148,7 @@ CONFIG_REGULATOR_PWM=m CONFIG_MEDIA_SUPPORT=y CONFIG_MEDIA_CAMERA_SUPPORT=y CONFIG_V4L_PLATFORM_DRIVERS=y -CONFIG_SOC_CAMERA=y CONFIG_VIDEO_ATMEL_ISI=y -CONFIG_SOC_CAMERA_OV2640=m CONFIG_DRM=y CONFIG_DRM_ATMEL_HLCDC=y CONFIG_DRM_PANEL_SIMPLE=y -- GitLab From 078336914d6bf4ff9e4ddc3675396d729135ae4d Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 26 Jun 2020 15:15:24 +0200 Subject: [PATCH 0238/1476] media: arch/sh/configs: remove obsolete CONFIG_SOC_CAMERA* Drop all configs with the CONFIG_SOC_CAMERA prefix since those have been removed. SOC_CAMERA support for the sh architecture was removed a long time ago. Drop it from the configs. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- arch/sh/configs/ap325rxa_defconfig | 3 --- arch/sh/configs/ecovec24_defconfig | 3 --- arch/sh/configs/migor_defconfig | 3 --- arch/sh/configs/se7724_defconfig | 2 -- 4 files changed, 11 deletions(-) diff --git a/arch/sh/configs/ap325rxa_defconfig b/arch/sh/configs/ap325rxa_defconfig index cc6e4ce53dacc..5193b3e099b9f 100644 --- a/arch/sh/configs/ap325rxa_defconfig +++ b/arch/sh/configs/ap325rxa_defconfig @@ -65,9 +65,6 @@ CONFIG_VIDEO_DEV=y # CONFIG_VIDEO_ALLOW_V4L1 is not set # CONFIG_MEDIA_TUNER_CUSTOMISE is not set CONFIG_VIDEO_HELPER_CHIPS_AUTO=y -CONFIG_SOC_CAMERA=y -CONFIG_SOC_CAMERA_PLATFORM=y -CONFIG_SOC_CAMERA_OV772X=y CONFIG_VIDEO_SH_MOBILE_CEU=y # CONFIG_RADIO_ADAPTERS is not set CONFIG_FB=y diff --git a/arch/sh/configs/ecovec24_defconfig b/arch/sh/configs/ecovec24_defconfig index 2fb7db4957ceb..03cb916819faf 100644 --- a/arch/sh/configs/ecovec24_defconfig +++ b/arch/sh/configs/ecovec24_defconfig @@ -72,9 +72,6 @@ CONFIG_MEDIA_SUPPORT=y CONFIG_VIDEO_DEV=y # CONFIG_MEDIA_TUNER_CUSTOMISE is not set CONFIG_VIDEO_HELPER_CHIPS_AUTO=y -CONFIG_SOC_CAMERA=y -CONFIG_SOC_CAMERA_MT9T112=y -CONFIG_SOC_CAMERA_TW9910=y CONFIG_VIDEO_SH_MOBILE_CEU=y # CONFIG_V4L_USB_DRIVERS is not set CONFIG_FB=y diff --git a/arch/sh/configs/migor_defconfig b/arch/sh/configs/migor_defconfig index 494a1675c226e..37e9521a99e5b 100644 --- a/arch/sh/configs/migor_defconfig +++ b/arch/sh/configs/migor_defconfig @@ -62,9 +62,6 @@ CONFIG_VIDEO_DEV=y # CONFIG_VIDEO_ALLOW_V4L1 is not set # CONFIG_MEDIA_TUNER_CUSTOMISE is not set CONFIG_VIDEO_HELPER_CHIPS_AUTO=y -CONFIG_SOC_CAMERA=y -CONFIG_SOC_CAMERA_TW9910=y -CONFIG_SOC_CAMERA_OV772X=y CONFIG_VIDEO_SH_MOBILE_CEU=y # CONFIG_RADIO_ADAPTERS is not set CONFIG_FB=y diff --git a/arch/sh/configs/se7724_defconfig b/arch/sh/configs/se7724_defconfig index 0e8d5cc1e1070..a26f7f1841c7a 100644 --- a/arch/sh/configs/se7724_defconfig +++ b/arch/sh/configs/se7724_defconfig @@ -70,8 +70,6 @@ CONFIG_VIDEO_DEV=y CONFIG_DVB_CORE=m # CONFIG_MEDIA_TUNER_CUSTOMISE is not set CONFIG_VIDEO_HELPER_CHIPS_AUTO=y -CONFIG_SOC_CAMERA=y -CONFIG_SOC_CAMERA_OV772X=y CONFIG_VIDEO_SH_MOBILE_CEU=y # CONFIG_RADIO_ADAPTERS is not set # CONFIG_DVB_FE_CUSTOMISE is not set -- GitLab From 89316195f7518e565182a3736101a6c34644e564 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Sat, 27 Jun 2020 11:35:22 +0200 Subject: [PATCH 0239/1476] media: MAINTAINERS: remove SOC-CAMERA entry After the removal of the soc_camera driver and the soc_camera.h header the SOC-CAMERA entry in the MAINTAINERS file can also be removed. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- MAINTAINERS | 7 ------- 1 file changed, 7 deletions(-) diff --git a/MAINTAINERS b/MAINTAINERS index 6362b96b7af34..2730af1265ead 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -15772,13 +15772,6 @@ L: netdev@vger.kernel.org S: Maintained F: drivers/net/ethernet/smsc/smsc9420.* -SOC-CAMERA V4L2 SUBSYSTEM -L: linux-media@vger.kernel.org -S: Orphan -T: git git://linuxtv.org/media_tree.git -F: drivers/staging/media/soc_camera/ -F: include/media/soc_camera.h - SOCIONEXT (SNI) AVE NETWORK DRIVER M: Kunihiko Hayashi L: netdev@vger.kernel.org -- GitLab From 29af79e17b6b16430f308a6998afc318b5d26888 Mon Sep 17 00:00:00 2001 From: Sean Young Date: Tue, 16 Jun 2020 11:45:14 +0200 Subject: [PATCH 0240/1476] media: drx-k: remove unused MulDiv32 function This function has never been used. Found with clang: drivers/media/dvb-frontends/drxk_hard.c:159:19: warning: unused function 'MulDiv32' [-Wunused-function] static inline u32 MulDiv32(u32 a, u32 b, u32 c) Signed-off-by: Sean Young Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb-frontends/drxk_hard.c | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/drivers/media/dvb-frontends/drxk_hard.c b/drivers/media/dvb-frontends/drxk_hard.c index 7e8e5c308d1c6..0ae9d8c72d8dc 100644 --- a/drivers/media/dvb-frontends/drxk_hard.c +++ b/drivers/media/dvb-frontends/drxk_hard.c @@ -155,17 +155,6 @@ if (debug >= level) \ printk(KERN_DEBUG KBUILD_MODNAME ": %s " fmt, __func__, ##arg); \ } while (0) - -static inline u32 MulDiv32(u32 a, u32 b, u32 c) -{ - u64 tmp64; - - tmp64 = (u64) a * (u64) b; - do_div(tmp64, c); - - return (u32) tmp64; -} - static inline u32 Frac28a(u32 a, u32 c) { int i = 0; -- GitLab From 3fd63734bca51c6b7bb6615fc10eefa620ca58a0 Mon Sep 17 00:00:00 2001 From: Sean Young Date: Tue, 16 Jun 2020 11:49:42 +0200 Subject: [PATCH 0241/1476] media: nuvoton-cir: remove setting tx carrier functions Since this driver could never transmit IR, setting the tx carrier makes no sense. Fixes: 87284271b73b8 ("media: rc: nuvoton: remove rudimentary transmit functionality") Signed-off-by: Sean Young Signed-off-by: Mauro Carvalho Chehab --- drivers/media/rc/nuvoton-cir.c | 32 -------------------------------- 1 file changed, 32 deletions(-) diff --git a/drivers/media/rc/nuvoton-cir.c b/drivers/media/rc/nuvoton-cir.c index 48a69bf23236e..52d246dc5b3da 100644 --- a/drivers/media/rc/nuvoton-cir.c +++ b/drivers/media/rc/nuvoton-cir.c @@ -74,13 +74,6 @@ static inline void nvt_set_reg_bit(struct nvt_dev *nvt, u8 val, u8 reg) nvt_cr_write(nvt, tmp, reg); } -/* clear config register bit without changing other bits */ -static inline void nvt_clear_reg_bit(struct nvt_dev *nvt, u8 val, u8 reg) -{ - u8 tmp = nvt_cr_read(nvt, reg) & ~val; - nvt_cr_write(nvt, tmp, reg); -} - /* enter extended function mode */ static inline int nvt_efm_enable(struct nvt_dev *nvt) { @@ -631,30 +624,6 @@ static u32 nvt_rx_carrier_detect(struct nvt_dev *nvt) return carrier; } #endif -/* - * set carrier frequency - * - * set carrier on 2 registers: CP & CC - * always set CP as 0x81 - * set CC by SPEC, CC = 3MHz/carrier - 1 - */ -static int nvt_set_tx_carrier(struct rc_dev *dev, u32 carrier) -{ - struct nvt_dev *nvt = dev->priv; - u16 val; - - if (carrier == 0) - return -EINVAL; - - nvt_cir_reg_write(nvt, 1, CIR_CP); - val = 3000000 / (carrier) - 1; - nvt_cir_reg_write(nvt, val & 0xff, CIR_CC); - - nvt_dbg("cp: 0x%x cc: 0x%x\n", - nvt_cir_reg_read(nvt, CIR_CP), nvt_cir_reg_read(nvt, CIR_CC)); - - return 0; -} static int nvt_ir_raw_set_wakeup_filter(struct rc_dev *dev, struct rc_scancode_filter *sc_filter) @@ -1022,7 +991,6 @@ static int nvt_probe(struct pnp_dev *pdev, const struct pnp_device_id *dev_id) rdev->encode_wakeup = true; rdev->open = nvt_open; rdev->close = nvt_close; - rdev->s_tx_carrier = nvt_set_tx_carrier; rdev->s_wakeup_filter = nvt_ir_raw_set_wakeup_filter; rdev->device_name = "Nuvoton w836x7hg Infrared Remote Transceiver"; rdev->input_phys = "nuvoton/cir0"; -- GitLab From 0390430deffbeb05bf08dfc8168d53f6e693b768 Mon Sep 17 00:00:00 2001 From: Sean Young Date: Tue, 16 Jun 2020 11:54:06 +0200 Subject: [PATCH 0242/1476] media: fintek-cir: remove unused function fintek_clear_reg_bit Found with clang: drivers/media/rc/fintek-cir.c:55:20: warning: unused function 'fintek_clear_reg_bit' [-Wunused-function] static inline void fintek_clear_reg_bit(struct fintek_dev *fintek, u8 val, u8 reg) Signed-off-by: Sean Young Signed-off-by: Mauro Carvalho Chehab --- drivers/media/rc/fintek-cir.c | 7 ------- 1 file changed, 7 deletions(-) diff --git a/drivers/media/rc/fintek-cir.c b/drivers/media/rc/fintek-cir.c index b74bb13161fd4..8e3177c5b5865 100644 --- a/drivers/media/rc/fintek-cir.c +++ b/drivers/media/rc/fintek-cir.c @@ -51,13 +51,6 @@ static inline void fintek_set_reg_bit(struct fintek_dev *fintek, u8 val, u8 reg) fintek_cr_write(fintek, tmp, reg); } -/* clear config register bit without changing other bits */ -static inline void fintek_clear_reg_bit(struct fintek_dev *fintek, u8 val, u8 reg) -{ - u8 tmp = fintek_cr_read(fintek, reg) & ~val; - fintek_cr_write(fintek, tmp, reg); -} - /* enter config mode */ static inline void fintek_config_mode_enable(struct fintek_dev *fintek) { -- GitLab From 39c2cf7544d63882709236ca2fb801db7cc2bc52 Mon Sep 17 00:00:00 2001 From: Sean Young Date: Tue, 16 Jun 2020 12:04:17 +0200 Subject: [PATCH 0243/1476] media: cx23888: remove unused functions Found with clang: drivers/media/pci/cx23885/cx23888-ir.c:178:19: warning: unused function 'ns_to_clock_divider' [-Wunused-function] static inline u16 ns_to_clock_divider(unsigned int ns) ^ drivers/media/pci/cx23885/cx23888-ir.c:184:28: warning: unused function 'clock_divider_to_ns' [-Wunused-function] static inline unsigned int clock_divider_to_ns(unsigned int divider) ^ drivers/media/pci/cx23885/cx23888-ir.c:202:19: warning: unused function 'freq_to_clock_divider' [-Wunused-function] static inline u16 freq_to_clock_divider(unsigned int freq, ^ Cc: Andy Walls Signed-off-by: Sean Young Signed-off-by: Mauro Carvalho Chehab --- drivers/media/pci/cx23885/cx23888-ir.c | 20 -------------------- 1 file changed, 20 deletions(-) diff --git a/drivers/media/pci/cx23885/cx23888-ir.c b/drivers/media/pci/cx23885/cx23888-ir.c index d59ca3601785e..ad7f8ccad526c 100644 --- a/drivers/media/pci/cx23885/cx23888-ir.c +++ b/drivers/media/pci/cx23885/cx23888-ir.c @@ -175,19 +175,6 @@ static inline u16 count_to_clock_divider(unsigned int d) return (u16) d; } -static inline u16 ns_to_clock_divider(unsigned int ns) -{ - return count_to_clock_divider( - DIV_ROUND_CLOSEST(CX23888_IR_REFCLK_FREQ / 1000000 * ns, 1000)); -} - -static inline unsigned int clock_divider_to_ns(unsigned int divider) -{ - /* Period of the Rx or Tx clock in ns */ - return DIV_ROUND_CLOSEST((divider + 1) * 1000, - CX23888_IR_REFCLK_FREQ / 1000000); -} - static inline u16 carrier_freq_to_clock_divider(unsigned int freq) { return count_to_clock_divider( @@ -199,13 +186,6 @@ static inline unsigned int clock_divider_to_carrier_freq(unsigned int divider) return DIV_ROUND_CLOSEST(CX23888_IR_REFCLK_FREQ, (divider + 1) * 16); } -static inline u16 freq_to_clock_divider(unsigned int freq, - unsigned int rollovers) -{ - return count_to_clock_divider( - DIV_ROUND_CLOSEST(CX23888_IR_REFCLK_FREQ, freq * rollovers)); -} - static inline unsigned int clock_divider_to_freq(unsigned int divider, unsigned int rollovers) { -- GitLab From 3b4cfc6966ecaefdf46a1369923a55e7bdc86d19 Mon Sep 17 00:00:00 2001 From: Evgeny Novikov Date: Thu, 18 Jun 2020 15:15:38 +0200 Subject: [PATCH 0244/1476] media: rc: return proper error code on error handling path in init If lirc_dev_init() fails during module initialization, rc_core_init() returns 0 denoting success. This can cause different issues during further operation of the module. The patch fixes the return value of rc_core_init() on the corresponding error handling path. Found by Linux Driver Verification project (linuxtesting.org). Signed-off-by: Evgeny Novikov Signed-off-by: Sean Young Signed-off-by: Mauro Carvalho Chehab --- drivers/media/rc/rc-main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/rc/rc-main.c b/drivers/media/rc/rc-main.c index d7064d664d528..7b53066d9d07c 100644 --- a/drivers/media/rc/rc-main.c +++ b/drivers/media/rc/rc-main.c @@ -2052,7 +2052,7 @@ static int __init rc_core_init(void) if (rc) { pr_err("rc_core: unable to init lirc\n"); class_unregister(&rc_class); - return 0; + return rc; } led_trigger_register_simple("rc-feedback", &led_feedback); -- GitLab From e0eb34810113dbbf1ace57440cf48d514312a373 Mon Sep 17 00:00:00 2001 From: Mansur Alisha Shaik Date: Fri, 1 May 2020 08:28:00 +0200 Subject: [PATCH 0245/1476] media: venus: fix multiple encoder crash Currently we are considering the instances which are available in core->inst list for load calculation in min_loaded_core() function, but this is incorrect because by the time we call decide_core() for second instance, the third instance not filled yet codec_freq_data pointer. Solve this by considering the instances whose session has started. Cc: stable@vger.kernel.org # v5.7+ Fixes: 4ebf969375bc ("media: venus: introduce core selection") Tested-by: Douglas Anderson Signed-off-by: Mansur Alisha Shaik Signed-off-by: Stanimir Varbanov Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/qcom/venus/pm_helpers.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/media/platform/qcom/venus/pm_helpers.c b/drivers/media/platform/qcom/venus/pm_helpers.c index abf93158857b9..531e7a41658f7 100644 --- a/drivers/media/platform/qcom/venus/pm_helpers.c +++ b/drivers/media/platform/qcom/venus/pm_helpers.c @@ -496,6 +496,10 @@ min_loaded_core(struct venus_inst *inst, u32 *min_coreid, u32 *min_load) list_for_each_entry(inst_pos, &core->instances, list) { if (inst_pos == inst) continue; + + if (inst_pos->state != INST_START) + continue; + vpp_freq = inst_pos->clk_data.codec_freq_data->vpp_freq; coreid = inst_pos->clk_data.core_id; -- GitLab From 831b8755fd2f60cf00cbf854c2a2eccb03f2060b Mon Sep 17 00:00:00 2001 From: Dinghao Liu Date: Sun, 24 May 2020 06:31:32 +0200 Subject: [PATCH 0246/1476] media: venus: venc: Fix runtime PM imbalance in venc_open pm_runtime_get_sync() increments the runtime PM usage counter even when it returns an error code. Thus a pairing decrement is needed on the error handling path to keep the counter balanced. Signed-off-by: Dinghao Liu Signed-off-by: Stanimir Varbanov Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/qcom/venus/venc.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/media/platform/qcom/venus/venc.c b/drivers/media/platform/qcom/venus/venc.c index feed648550d19..513bbc07f7bcc 100644 --- a/drivers/media/platform/qcom/venus/venc.c +++ b/drivers/media/platform/qcom/venus/venc.c @@ -1177,7 +1177,7 @@ static int venc_open(struct file *file) ret = pm_runtime_get_sync(core->dev_enc); if (ret < 0) - goto err_free_inst; + goto err_put_sync; ret = venc_ctrl_init(inst); if (ret) @@ -1222,7 +1222,6 @@ err_ctrl_deinit: venc_ctrl_deinit(inst); err_put_sync: pm_runtime_put_sync(core->dev_enc); -err_free_inst: kfree(inst); return ret; } -- GitLab From fd22e8eb4145852223b36b0e6c7e308ed5905ad2 Mon Sep 17 00:00:00 2001 From: Lad Prabhakar Date: Wed, 27 May 2020 14:16:48 +0200 Subject: [PATCH 0247/1476] media: rcar-vin: Invalidate pipeline if conversion is not possible on input formats MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Up until now the VIN was capable to convert any of its supported input mbus formats to any of it's supported output pixel formats. With the addition of RAW formats this is no longer true. This patch invalidates the pipeline by adding a check if given vin input format can be converted to supported output pixel format. Signed-off-by: Lad Prabhakar Reviewed-by: Niklas Söderlund Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/rcar-vin/rcar-dma.c | 6 +++++- drivers/media/platform/rcar-vin/rcar-v4l2.c | 11 ++++++++--- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/drivers/media/platform/rcar-vin/rcar-dma.c b/drivers/media/platform/rcar-vin/rcar-dma.c index 1a30cd0363711..2b26204910fd6 100644 --- a/drivers/media/platform/rcar-vin/rcar-dma.c +++ b/drivers/media/platform/rcar-vin/rcar-dma.c @@ -1110,11 +1110,15 @@ static int rvin_mc_validate_format(struct rvin_dev *vin, struct v4l2_subdev *sd, case MEDIA_BUS_FMT_UYVY8_2X8: case MEDIA_BUS_FMT_UYVY10_2X10: case MEDIA_BUS_FMT_RGB888_1X24: - vin->mbus_code = fmt.format.code; + break; + case MEDIA_BUS_FMT_SRGGB8_1X8: + if (vin->format.pixelformat != V4L2_PIX_FMT_SRGGB8) + return -EPIPE; break; default: return -EPIPE; } + vin->mbus_code = fmt.format.code; switch (fmt.format.field) { case V4L2_FIELD_TOP: diff --git a/drivers/media/platform/rcar-vin/rcar-v4l2.c b/drivers/media/platform/rcar-vin/rcar-v4l2.c index f421e25848756..d3b6a992b4a24 100644 --- a/drivers/media/platform/rcar-vin/rcar-v4l2.c +++ b/drivers/media/platform/rcar-vin/rcar-v4l2.c @@ -350,9 +350,9 @@ static int rvin_enum_fmt_vid_cap(struct file *file, void *priv, * all of the related pixel formats. If mbus_code is not set enumerate * all possible pixelformats. * - * TODO: Once raw capture formats are added to the driver this needs - * to be extended so raw media bus codes only result in raw pixel - * formats. + * TODO: Once raw MEDIA_BUS_FMT_SRGGB12_1X12 format is added to the + * driver this needs to be extended so raw media bus code only result in + * raw pixel format. */ switch (f->mbus_code) { case 0: @@ -362,6 +362,11 @@ static int rvin_enum_fmt_vid_cap(struct file *file, void *priv, case MEDIA_BUS_FMT_UYVY10_2X10: case MEDIA_BUS_FMT_RGB888_1X24: break; + case MEDIA_BUS_FMT_SRGGB8_1X8: + if (f->index) + return -EINVAL; + f->pixelformat = V4L2_PIX_FMT_SRGGB8; + return 0; default: return -EINVAL; } -- GitLab From e87c1a81f158d6fc7b3346eb88c2d76a044f837d Mon Sep 17 00:00:00 2001 From: Lad Prabhakar Date: Wed, 27 May 2020 14:16:49 +0200 Subject: [PATCH 0248/1476] media: rcar-vin: Add support for MEDIA_BUS_FMT_SRGGB8_1X8 format MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add support for MEDIA_BUS_FMT_SRGGB8_1X8 format in rcar-vin by setting format type to RAW8 in VNMC register and appropriately setting the bpp and bytesperline to enable V4L2_PIX_FMT_SRGGB8. Signed-off-by: Lad Prabhakar Reviewed-by: Niklas Söderlund Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/rcar-vin/rcar-dma.c | 15 ++++++++++++++- drivers/media/platform/rcar-vin/rcar-v4l2.c | 4 ++++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/drivers/media/platform/rcar-vin/rcar-dma.c b/drivers/media/platform/rcar-vin/rcar-dma.c index 2b26204910fd6..a5dbb90c5210b 100644 --- a/drivers/media/platform/rcar-vin/rcar-dma.c +++ b/drivers/media/platform/rcar-vin/rcar-dma.c @@ -85,6 +85,7 @@ #define VNMC_INF_YUV8_BT601 (1 << 16) #define VNMC_INF_YUV10_BT656 (2 << 16) #define VNMC_INF_YUV10_BT601 (3 << 16) +#define VNMC_INF_RAW8 (4 << 16) #define VNMC_INF_YUV16 (5 << 16) #define VNMC_INF_RGB888 (6 << 16) #define VNMC_VUP (1 << 10) @@ -587,13 +588,19 @@ void rvin_crop_scale_comp(struct rvin_dev *vin) rvin_write(vin, vin->crop.top, VNSLPRC_REG); rvin_write(vin, vin->crop.top + vin->crop.height - 1, VNELPRC_REG); - /* TODO: Add support for the UDS scaler. */ if (vin->info->model != RCAR_GEN3) rvin_crop_scale_comp_gen2(vin); fmt = rvin_format_from_pixel(vin, vin->format.pixelformat); stride = vin->format.bytesperline / fmt->bpp; + + /* For RAW8 format bpp is 1, but the hardware process RAW8 + * format in 2 pixel unit hence configure VNIS_REG as stride / 2. + */ + if (vin->format.pixelformat == V4L2_PIX_FMT_SRGGB8) + stride /= 2; + rvin_write(vin, stride, VNIS_REG); } @@ -676,6 +683,9 @@ static int rvin_setup(struct rvin_dev *vin) input_is_yuv = true; break; + case MEDIA_BUS_FMT_SRGGB8_1X8: + vnmc |= VNMC_INF_RAW8; + break; default: break; } @@ -737,6 +747,9 @@ static int rvin_setup(struct rvin_dev *vin) case V4L2_PIX_FMT_ABGR32: dmr = VNDMR_A8BIT(vin->alpha) | VNDMR_EXRGB | VNDMR_DTMD_ARGB; break; + case V4L2_PIX_FMT_SRGGB8: + dmr = 0; + break; default: vin_err(vin, "Invalid pixelformat (0x%x)\n", vin->format.pixelformat); diff --git a/drivers/media/platform/rcar-vin/rcar-v4l2.c b/drivers/media/platform/rcar-vin/rcar-v4l2.c index d3b6a992b4a24..0e066bba747e0 100644 --- a/drivers/media/platform/rcar-vin/rcar-v4l2.c +++ b/drivers/media/platform/rcar-vin/rcar-v4l2.c @@ -66,6 +66,10 @@ static const struct rvin_video_format rvin_formats[] = { .fourcc = V4L2_PIX_FMT_ABGR32, .bpp = 4, }, + { + .fourcc = V4L2_PIX_FMT_SRGGB8, + .bpp = 1, + }, }; const struct rvin_video_format *rvin_format_from_pixel(struct rvin_dev *vin, -- GitLab From 675616554d0a5caff138008ee9bd4623bc4390b2 Mon Sep 17 00:00:00 2001 From: Lad Prabhakar Date: Wed, 27 May 2020 14:16:50 +0200 Subject: [PATCH 0249/1476] media: rcar-csi2: Add support for MEDIA_BUS_FMT_SRGGB8_1X8 format MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch adds support for MEDIA_BUS_FMT_SRGGB8_1X8 format for CSI2 input. Signed-off-by: Lad Prabhakar Reviewed-by: Niklas Söderlund Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/rcar-vin/rcar-csi2.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/media/platform/rcar-vin/rcar-csi2.c b/drivers/media/platform/rcar-vin/rcar-csi2.c index 151e6a90c5fbc..45fdf4e74e513 100644 --- a/drivers/media/platform/rcar-vin/rcar-csi2.c +++ b/drivers/media/platform/rcar-vin/rcar-csi2.c @@ -320,6 +320,7 @@ static const struct rcar_csi2_format rcar_csi2_formats[] = { { .code = MEDIA_BUS_FMT_YUYV8_1X16, .datatype = 0x1e, .bpp = 16 }, { .code = MEDIA_BUS_FMT_UYVY8_2X8, .datatype = 0x1e, .bpp = 16 }, { .code = MEDIA_BUS_FMT_YUYV10_2X10, .datatype = 0x1e, .bpp = 20 }, + { .code = MEDIA_BUS_FMT_SRGGB8_1X8, .datatype = 0x2a, .bpp = 8 }, }; static const struct rcar_csi2_format *rcsi2_code_to_fmt(unsigned int code) -- GitLab From d3246337d00fcb635e4ade45f748f155e4056b28 Mon Sep 17 00:00:00 2001 From: Benoit Parrot Date: Thu, 28 May 2020 15:26:04 +0200 Subject: [PATCH 0250/1476] media: v4l2-rect.h: add enclosed rectangle helper Add a helper function to check if one rectangle is enclosed inside another. Signed-off-by: Benoit Parrot Acked-by: Andrzej Pietrasiewicz Reviewed-by: Lad Prabhakar Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- include/media/v4l2-rect.h | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/include/media/v4l2-rect.h b/include/media/v4l2-rect.h index 8800a640c2241..bd587d0c0dc38 100644 --- a/include/media/v4l2-rect.h +++ b/include/media/v4l2-rect.h @@ -184,4 +184,24 @@ static inline bool v4l2_rect_overlap(const struct v4l2_rect *r1, return true; } +/** + * v4l2_rect_enclosed() - is r1 enclosed in r2? + * @r1: rectangle. + * @r2: rectangle. + * + * Returns true if @r1 is enclosed in @r2. + */ +static inline bool v4l2_rect_enclosed(struct v4l2_rect *r1, + struct v4l2_rect *r2) +{ + if (r1->left < r2->left || r1->top < r2->top) + return false; + if (r1->left + r1->width > r2->left + r2->width) + return false; + if (r1->top + r1->height > r2->top + r2->height) + return false; + + return true; +} + #endif -- GitLab From 030b161c4dc8a4d9a3dcecd7101680e3f871748d Mon Sep 17 00:00:00 2001 From: Benoit Parrot Date: Thu, 28 May 2020 15:26:05 +0200 Subject: [PATCH 0251/1476] media: use v4l2_rect_enclosed helper Several drivers implement the same enclosed_rectangle() function to check if a rectangle is enclosed into another. Replace this with the newly added v4l2_rect_enclosed() helper function. Signed-off-by: Benoit Parrot Acked-by: Andrzej Pietrasiewicz Reviewed-by: Lad Prabhakar Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/am437x/am437x-vpfe.c | 19 +++---------------- .../media/platform/exynos4-is/fimc-capture.c | 18 +++--------------- drivers/media/platform/exynos4-is/fimc-lite.c | 18 +++--------------- drivers/media/platform/s5p-jpeg/jpeg-core.c | 16 ++-------------- 4 files changed, 11 insertions(+), 60 deletions(-) diff --git a/drivers/media/platform/am437x/am437x-vpfe.c b/drivers/media/platform/am437x/am437x-vpfe.c index 66079cc41f38e..0fb9f9ba1219d 100644 --- a/drivers/media/platform/am437x/am437x-vpfe.c +++ b/drivers/media/platform/am437x/am437x-vpfe.c @@ -26,6 +26,7 @@ #include #include #include +#include #include "am437x-vpfe.h" @@ -1987,20 +1988,6 @@ vpfe_g_selection(struct file *file, void *fh, struct v4l2_selection *s) return 0; } -static int enclosed_rectangle(struct v4l2_rect *a, struct v4l2_rect *b) -{ - if (a->left < b->left || a->top < b->top) - return 0; - - if (a->left + a->width > b->left + b->width) - return 0; - - if (a->top + a->height > b->top + b->height) - return 0; - - return 1; -} - static int vpfe_s_selection(struct file *file, void *fh, struct v4l2_selection *s) { @@ -2025,10 +2012,10 @@ vpfe_s_selection(struct file *file, void *fh, struct v4l2_selection *s) r.left = clamp_t(unsigned int, r.left, 0, cr.width - r.width); r.top = clamp_t(unsigned int, r.top, 0, cr.height - r.height); - if (s->flags & V4L2_SEL_FLAG_LE && !enclosed_rectangle(&r, &s->r)) + if (s->flags & V4L2_SEL_FLAG_LE && !v4l2_rect_enclosed(&r, &s->r)) return -ERANGE; - if (s->flags & V4L2_SEL_FLAG_GE && !enclosed_rectangle(&s->r, &r)) + if (s->flags & V4L2_SEL_FLAG_GE && !v4l2_rect_enclosed(&s->r, &r)) return -ERANGE; s->r = vpfe->crop = r; diff --git a/drivers/media/platform/exynos4-is/fimc-capture.c b/drivers/media/platform/exynos4-is/fimc-capture.c index dd33b888971fb..e7a4b06e6dfe6 100644 --- a/drivers/media/platform/exynos4-is/fimc-capture.c +++ b/drivers/media/platform/exynos4-is/fimc-capture.c @@ -21,6 +21,7 @@ #include #include #include +#include #include #include @@ -1301,19 +1302,6 @@ static int fimc_cap_g_selection(struct file *file, void *fh, return -EINVAL; } -/* Return 1 if rectangle a is enclosed in rectangle b, or 0 otherwise. */ -static int enclosed_rectangle(struct v4l2_rect *a, struct v4l2_rect *b) -{ - if (a->left < b->left || a->top < b->top) - return 0; - if (a->left + a->width > b->left + b->width) - return 0; - if (a->top + a->height > b->top + b->height) - return 0; - - return 1; -} - static int fimc_cap_s_selection(struct file *file, void *fh, struct v4l2_selection *s) { @@ -1336,11 +1324,11 @@ static int fimc_cap_s_selection(struct file *file, void *fh, fimc_capture_try_selection(ctx, &rect, s->target); if (s->flags & V4L2_SEL_FLAG_LE && - !enclosed_rectangle(&rect, &s->r)) + !v4l2_rect_enclosed(&rect, &s->r)) return -ERANGE; if (s->flags & V4L2_SEL_FLAG_GE && - !enclosed_rectangle(&s->r, &rect)) + !v4l2_rect_enclosed(&s->r, &rect)) return -ERANGE; s->r = rect; diff --git a/drivers/media/platform/exynos4-is/fimc-lite.c b/drivers/media/platform/exynos4-is/fimc-lite.c index 394e0818f2d5c..9c666f663ab43 100644 --- a/drivers/media/platform/exynos4-is/fimc-lite.c +++ b/drivers/media/platform/exynos4-is/fimc-lite.c @@ -25,6 +25,7 @@ #include #include #include +#include #include #include #include @@ -868,19 +869,6 @@ static int fimc_lite_reqbufs(struct file *file, void *priv, return ret; } -/* Return 1 if rectangle a is enclosed in rectangle b, or 0 otherwise. */ -static int enclosed_rectangle(struct v4l2_rect *a, struct v4l2_rect *b) -{ - if (a->left < b->left || a->top < b->top) - return 0; - if (a->left + a->width > b->left + b->width) - return 0; - if (a->top + a->height > b->top + b->height) - return 0; - - return 1; -} - static int fimc_lite_g_selection(struct file *file, void *fh, struct v4l2_selection *sel) { @@ -922,11 +910,11 @@ static int fimc_lite_s_selection(struct file *file, void *fh, fimc_lite_try_compose(fimc, &rect); if ((sel->flags & V4L2_SEL_FLAG_LE) && - !enclosed_rectangle(&rect, &sel->r)) + !v4l2_rect_enclosed(&rect, &sel->r)) return -ERANGE; if ((sel->flags & V4L2_SEL_FLAG_GE) && - !enclosed_rectangle(&sel->r, &rect)) + !v4l2_rect_enclosed(&sel->r, &rect)) return -ERANGE; sel->r = rect; diff --git a/drivers/media/platform/s5p-jpeg/jpeg-core.c b/drivers/media/platform/s5p-jpeg/jpeg-core.c index 86bda3947110d..9b22dd8e34f44 100644 --- a/drivers/media/platform/s5p-jpeg/jpeg-core.c +++ b/drivers/media/platform/s5p-jpeg/jpeg-core.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #include @@ -1735,19 +1736,6 @@ static int exynos3250_jpeg_try_downscale(struct s5p_jpeg_ctx *ctx, return 0; } -/* Return 1 if rectangle a is enclosed in rectangle b, or 0 otherwise. */ -static int enclosed_rectangle(struct v4l2_rect *a, struct v4l2_rect *b) -{ - if (a->left < b->left || a->top < b->top) - return 0; - if (a->left + a->width > b->left + b->width) - return 0; - if (a->top + a->height > b->top + b->height) - return 0; - - return 1; -} - static int exynos3250_jpeg_try_crop(struct s5p_jpeg_ctx *ctx, struct v4l2_rect *r) { @@ -1780,7 +1768,7 @@ static int exynos3250_jpeg_try_crop(struct s5p_jpeg_ctx *ctx, r->left = round_down(r->left, 2); r->top = round_down(r->top, 2); - if (!enclosed_rectangle(r, &base_rect)) + if (!v4l2_rect_enclosed(r, &base_rect)) return -EINVAL; ctx->crop_rect.left = r->left; -- GitLab From b820935b35479e823eb6ce133cdbe371dced7e95 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Sun, 21 Jun 2020 02:23:43 +0200 Subject: [PATCH 0252/1476] media: vb2: Print the queue pointer in debug messages When debugging issues that involve more than one video queue, messages related to multiple queues get interleaved without any easy way to tell which queue they relate to. Fix this by adding a queue name to vb2_queue, and printing it in all debug messages in the vb2 core and V4L2 layers. If the name isn't set by drivers, it is automatically filled with the queue direction and address. Signed-off-by: Laurent Pinchart Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- .../media/common/videobuf2/videobuf2-core.c | 226 +++++++++--------- .../media/common/videobuf2/videobuf2-v4l2.c | 68 +++--- include/media/videobuf2-core.h | 4 + include/media/videobuf2-v4l2.h | 13 + 4 files changed, 174 insertions(+), 137 deletions(-) diff --git a/drivers/media/common/videobuf2/videobuf2-core.c b/drivers/media/common/videobuf2/videobuf2-core.c index 37d0186ba3308..abaf28e057ebe 100644 --- a/drivers/media/common/videobuf2/videobuf2-core.c +++ b/drivers/media/common/videobuf2/videobuf2-core.c @@ -34,10 +34,11 @@ static int debug; module_param(debug, int, 0644); -#define dprintk(level, fmt, arg...) \ - do { \ - if (debug >= level) \ - pr_info("%s: " fmt, __func__, ## arg); \ +#define dprintk(q, level, fmt, arg...) \ + do { \ + if (debug >= level) \ + pr_info("[%s] %s: " fmt, (q)->name, __func__, \ + ## arg); \ } while (0) #ifdef CONFIG_VIDEO_ADV_DEBUG @@ -51,8 +52,8 @@ module_param(debug, int, 0644); */ #define log_memop(vb, op) \ - dprintk(2, "call_memop(%p, %d, %s)%s\n", \ - (vb)->vb2_queue, (vb)->index, #op, \ + dprintk((vb)->vb2_queue, 2, "call_memop(%d, %s)%s\n", \ + (vb)->index, #op, \ (vb)->vb2_queue->mem_ops->op ? "" : " (nop)") #define call_memop(vb, op, args...) \ @@ -90,7 +91,7 @@ module_param(debug, int, 0644); }) #define log_qop(q, op) \ - dprintk(2, "call_qop(%p, %s)%s\n", q, #op, \ + dprintk(q, 2, "call_qop(%s)%s\n", #op, \ (q)->ops->op ? "" : " (nop)") #define call_qop(q, op, args...) \ @@ -113,8 +114,8 @@ module_param(debug, int, 0644); }) #define log_vb_qop(vb, op, args...) \ - dprintk(2, "call_vb_qop(%p, %d, %s)%s\n", \ - (vb)->vb2_queue, (vb)->index, #op, \ + dprintk((vb)->vb2_queue, 2, "call_vb_qop(%d, %s)%s\n", \ + (vb)->index, #op, \ (vb)->vb2_queue->ops->op ? "" : " (nop)") #define call_vb_qop(vb, op, args...) \ @@ -246,7 +247,8 @@ static void __vb2_buf_mem_free(struct vb2_buffer *vb) for (plane = 0; plane < vb->num_planes; ++plane) { call_void_memop(vb, put, vb->planes[plane].mem_priv); vb->planes[plane].mem_priv = NULL; - dprintk(3, "freed plane %d of buffer %d\n", plane, vb->index); + dprintk(vb->vb2_queue, 3, "freed plane %d of buffer %d\n", + plane, vb->index); } } @@ -354,7 +356,7 @@ static void __setup_offsets(struct vb2_buffer *vb) for (plane = 0; plane < vb->num_planes; ++plane) { vb->planes[plane].m.offset = off; - dprintk(3, "buffer %d, plane %d offset 0x%08lx\n", + dprintk(q, 3, "buffer %d, plane %d offset 0x%08lx\n", vb->index, plane, off); off += vb->planes[plane].length; @@ -385,7 +387,7 @@ static int __vb2_queue_alloc(struct vb2_queue *q, enum vb2_memory memory, /* Allocate videobuf buffer structures */ vb = kzalloc(q->buf_struct_size, GFP_KERNEL); if (!vb) { - dprintk(1, "memory alloc for buffer struct failed\n"); + dprintk(q, 1, "memory alloc for buffer struct failed\n"); break; } @@ -407,7 +409,7 @@ static int __vb2_queue_alloc(struct vb2_queue *q, enum vb2_memory memory, if (memory == VB2_MEMORY_MMAP) { ret = __vb2_buf_mem_alloc(vb); if (ret) { - dprintk(1, "failed allocating memory for buffer %d\n", + dprintk(q, 1, "failed allocating memory for buffer %d\n", buffer); q->bufs[vb->index] = NULL; kfree(vb); @@ -421,7 +423,7 @@ static int __vb2_queue_alloc(struct vb2_queue *q, enum vb2_memory memory, */ ret = call_vb_qop(vb, buf_init, vb); if (ret) { - dprintk(1, "buffer %d %p initialization failed\n", + dprintk(q, 1, "buffer %d %p initialization failed\n", buffer, vb); __vb2_buf_mem_free(vb); q->bufs[vb->index] = NULL; @@ -431,7 +433,7 @@ static int __vb2_queue_alloc(struct vb2_queue *q, enum vb2_memory memory, } } - dprintk(3, "allocated %d buffers, %d plane(s) each\n", + dprintk(q, 3, "allocated %d buffers, %d plane(s) each\n", buffer, num_planes); return buffer; @@ -483,7 +485,7 @@ static int __vb2_queue_free(struct vb2_queue *q, unsigned int buffers) if (q->bufs[buffer] == NULL) continue; if (q->bufs[buffer]->state == VB2_BUF_STATE_PREPARING) { - dprintk(1, "preparing buffers, cannot free\n"); + dprintk(q, 1, "preparing buffers, cannot free\n"); return -EAGAIN; } } @@ -661,12 +663,12 @@ int vb2_verify_memory_type(struct vb2_queue *q, { if (memory != VB2_MEMORY_MMAP && memory != VB2_MEMORY_USERPTR && memory != VB2_MEMORY_DMABUF) { - dprintk(1, "unsupported memory type\n"); + dprintk(q, 1, "unsupported memory type\n"); return -EINVAL; } if (type != q->type) { - dprintk(1, "requested type is incorrect\n"); + dprintk(q, 1, "requested type is incorrect\n"); return -EINVAL; } @@ -675,17 +677,17 @@ int vb2_verify_memory_type(struct vb2_queue *q, * are available. */ if (memory == VB2_MEMORY_MMAP && __verify_mmap_ops(q)) { - dprintk(1, "MMAP for current setup unsupported\n"); + dprintk(q, 1, "MMAP for current setup unsupported\n"); return -EINVAL; } if (memory == VB2_MEMORY_USERPTR && __verify_userptr_ops(q)) { - dprintk(1, "USERPTR for current setup unsupported\n"); + dprintk(q, 1, "USERPTR for current setup unsupported\n"); return -EINVAL; } if (memory == VB2_MEMORY_DMABUF && __verify_dmabuf_ops(q)) { - dprintk(1, "DMABUF for current setup unsupported\n"); + dprintk(q, 1, "DMABUF for current setup unsupported\n"); return -EINVAL; } @@ -695,7 +697,7 @@ int vb2_verify_memory_type(struct vb2_queue *q, * do the memory and type validation. */ if (vb2_fileio_is_active(q)) { - dprintk(1, "file io in progress\n"); + dprintk(q, 1, "file io in progress\n"); return -EBUSY; } return 0; @@ -717,7 +719,7 @@ static bool verify_consistency_attr(struct vb2_queue *q, bool consistent_mem) bool queue_is_consistent = !(q->dma_attrs & DMA_ATTR_NON_CONSISTENT); if (consistent_mem != queue_is_consistent) { - dprintk(1, "memory consistency model mismatch\n"); + dprintk(q, 1, "memory consistency model mismatch\n"); return false; } return true; @@ -736,12 +738,12 @@ int vb2_core_reqbufs(struct vb2_queue *q, enum vb2_memory memory, consistent_mem = false; if (q->streaming) { - dprintk(1, "streaming active\n"); + dprintk(q, 1, "streaming active\n"); return -EBUSY; } if (q->waiting_in_dqbuf && *count) { - dprintk(1, "another dup()ped fd is waiting for a buffer\n"); + dprintk(q, 1, "another dup()ped fd is waiting for a buffer\n"); return -EBUSY; } @@ -755,7 +757,7 @@ int vb2_core_reqbufs(struct vb2_queue *q, enum vb2_memory memory, mutex_lock(&q->mmap_lock); if (debug && q->memory == VB2_MEMORY_MMAP && __buffers_in_use(q)) - dprintk(1, "memory in use, orphaning buffers\n"); + dprintk(q, 1, "memory in use, orphaning buffers\n"); /* * Call queue_cancel to clean up any buffers in the @@ -807,7 +809,7 @@ int vb2_core_reqbufs(struct vb2_queue *q, enum vb2_memory memory, allocated_buffers = __vb2_queue_alloc(q, memory, num_buffers, num_planes, plane_sizes); if (allocated_buffers == 0) { - dprintk(1, "memory allocation failed\n"); + dprintk(q, 1, "memory allocation failed\n"); return -ENOMEM; } @@ -882,13 +884,13 @@ int vb2_core_create_bufs(struct vb2_queue *q, enum vb2_memory memory, consistent_mem = false; if (q->num_buffers == VB2_MAX_FRAME) { - dprintk(1, "maximum number of buffers already allocated\n"); + dprintk(q, 1, "maximum number of buffers already allocated\n"); return -ENOBUFS; } if (!q->num_buffers) { if (q->waiting_in_dqbuf && *count) { - dprintk(1, "another dup()ped fd is waiting for a buffer\n"); + dprintk(q, 1, "another dup()ped fd is waiting for a buffer\n"); return -EBUSY; } memset(q->alloc_devs, 0, sizeof(q->alloc_devs)); @@ -897,7 +899,7 @@ int vb2_core_create_bufs(struct vb2_queue *q, enum vb2_memory memory, q->waiting_for_buffers = !q->is_output; } else { if (q->memory != memory) { - dprintk(1, "memory model mismatch\n"); + dprintk(q, 1, "memory model mismatch\n"); return -EINVAL; } if (!verify_consistency_attr(q, consistent_mem)) @@ -924,7 +926,7 @@ int vb2_core_create_bufs(struct vb2_queue *q, enum vb2_memory memory, allocated_buffers = __vb2_queue_alloc(q, memory, num_buffers, num_planes, plane_sizes); if (allocated_buffers == 0) { - dprintk(1, "memory allocation failed\n"); + dprintk(q, 1, "memory allocation failed\n"); return -ENOMEM; } @@ -1013,7 +1015,7 @@ void vb2_buffer_done(struct vb2_buffer *vb, enum vb2_buffer_state state) */ vb->cnt_buf_done++; #endif - dprintk(4, "done processing on buffer %d, state: %d\n", + dprintk(q, 4, "done processing on buffer %d, state: %d\n", vb->index, state); if (state != VB2_BUF_STATE_QUEUED) @@ -1099,12 +1101,12 @@ static int __prepare_userptr(struct vb2_buffer *vb) && vb->planes[plane].length == planes[plane].length) continue; - dprintk(3, "userspace address for plane %d changed, reacquiring memory\n", + dprintk(q, 3, "userspace address for plane %d changed, reacquiring memory\n", plane); /* Check if the provided plane buffer is large enough */ if (planes[plane].length < vb->planes[plane].min_length) { - dprintk(1, "provided buffer size %u is less than setup size %u for plane %d\n", + dprintk(q, 1, "provided buffer size %u is less than setup size %u for plane %d\n", planes[plane].length, vb->planes[plane].min_length, plane); @@ -1134,7 +1136,7 @@ static int __prepare_userptr(struct vb2_buffer *vb) planes[plane].m.userptr, planes[plane].length, q->dma_dir); if (IS_ERR(mem_priv)) { - dprintk(1, "failed acquiring userspace memory for plane %d\n", + dprintk(q, 1, "failed acquiring userspace memory for plane %d\n", plane); ret = PTR_ERR(mem_priv); goto err; @@ -1161,14 +1163,14 @@ static int __prepare_userptr(struct vb2_buffer *vb) */ ret = call_vb_qop(vb, buf_init, vb); if (ret) { - dprintk(1, "buffer initialization failed\n"); + dprintk(q, 1, "buffer initialization failed\n"); goto err; } } ret = call_vb_qop(vb, buf_prepare, vb); if (ret) { - dprintk(1, "buffer preparation failed\n"); + dprintk(q, 1, "buffer preparation failed\n"); call_void_vb_qop(vb, buf_cleanup, vb); goto err; } @@ -1211,7 +1213,7 @@ static int __prepare_dmabuf(struct vb2_buffer *vb) struct dma_buf *dbuf = dma_buf_get(planes[plane].m.fd); if (IS_ERR_OR_NULL(dbuf)) { - dprintk(1, "invalid dmabuf fd for plane %d\n", + dprintk(q, 1, "invalid dmabuf fd for plane %d\n", plane); ret = -EINVAL; goto err; @@ -1222,7 +1224,7 @@ static int __prepare_dmabuf(struct vb2_buffer *vb) planes[plane].length = dbuf->size; if (planes[plane].length < vb->planes[plane].min_length) { - dprintk(1, "invalid dmabuf length %u for plane %d, minimum length %u\n", + dprintk(q, 1, "invalid dmabuf length %u for plane %d, minimum length %u\n", planes[plane].length, plane, vb->planes[plane].min_length); dma_buf_put(dbuf); @@ -1237,7 +1239,7 @@ static int __prepare_dmabuf(struct vb2_buffer *vb) continue; } - dprintk(3, "buffer for plane %d changed\n", plane); + dprintk(q, 3, "buffer for plane %d changed\n", plane); if (!reacquired) { reacquired = true; @@ -1257,7 +1259,7 @@ static int __prepare_dmabuf(struct vb2_buffer *vb) q->alloc_devs[plane] ? : q->dev, dbuf, planes[plane].length, q->dma_dir); if (IS_ERR(mem_priv)) { - dprintk(1, "failed to attach dmabuf\n"); + dprintk(q, 1, "failed to attach dmabuf\n"); ret = PTR_ERR(mem_priv); dma_buf_put(dbuf); goto err; @@ -1278,7 +1280,7 @@ static int __prepare_dmabuf(struct vb2_buffer *vb) ret = call_memop(vb, map_dmabuf, vb->planes[plane].mem_priv); if (ret) { - dprintk(1, "failed to map dmabuf for plane %d\n", + dprintk(q, 1, "failed to map dmabuf for plane %d\n", plane); goto err; } @@ -1303,14 +1305,14 @@ static int __prepare_dmabuf(struct vb2_buffer *vb) */ ret = call_vb_qop(vb, buf_init, vb); if (ret) { - dprintk(1, "buffer initialization failed\n"); + dprintk(q, 1, "buffer initialization failed\n"); goto err; } } ret = call_vb_qop(vb, buf_prepare, vb); if (ret) { - dprintk(1, "buffer preparation failed\n"); + dprintk(q, 1, "buffer preparation failed\n"); call_void_vb_qop(vb, buf_cleanup, vb); goto err; } @@ -1345,7 +1347,7 @@ static int __buf_prepare(struct vb2_buffer *vb) int ret; if (q->error) { - dprintk(1, "fatal error occurred on queue\n"); + dprintk(q, 1, "fatal error occurred on queue\n"); return -EIO; } @@ -1356,7 +1358,7 @@ static int __buf_prepare(struct vb2_buffer *vb) if (q->is_output) { ret = call_vb_qop(vb, buf_out_validate, vb); if (ret) { - dprintk(1, "buffer validation failed\n"); + dprintk(q, 1, "buffer validation failed\n"); return ret; } } @@ -1380,7 +1382,7 @@ static int __buf_prepare(struct vb2_buffer *vb) } if (ret) { - dprintk(1, "buffer preparation failed: %d\n", ret); + dprintk(q, 1, "buffer preparation failed: %d\n", ret); vb->state = orig_state; return ret; } @@ -1488,12 +1490,12 @@ int vb2_core_prepare_buf(struct vb2_queue *q, unsigned int index, void *pb) vb = q->bufs[index]; if (vb->state != VB2_BUF_STATE_DEQUEUED) { - dprintk(1, "invalid buffer state %d\n", + dprintk(q, 1, "invalid buffer state %d\n", vb->state); return -EINVAL; } if (vb->prepared) { - dprintk(1, "buffer already prepared\n"); + dprintk(q, 1, "buffer already prepared\n"); return -EINVAL; } @@ -1504,7 +1506,7 @@ int vb2_core_prepare_buf(struct vb2_queue *q, unsigned int index, void *pb) /* Fill buffer information for the userspace */ call_void_bufop(q, fill_user_buffer, vb, pb); - dprintk(2, "prepare of buffer %d succeeded\n", vb->index); + dprintk(q, 2, "prepare of buffer %d succeeded\n", vb->index); return 0; } @@ -1542,7 +1544,7 @@ static int vb2_start_streaming(struct vb2_queue *q) q->start_streaming_called = 0; - dprintk(1, "driver refused to start streaming\n"); + dprintk(q, 1, "driver refused to start streaming\n"); /* * If you see this warning, then the driver isn't cleaning up properly * after a failed start_streaming(). See the start_streaming() @@ -1580,7 +1582,7 @@ int vb2_core_qbuf(struct vb2_queue *q, unsigned int index, void *pb, int ret; if (q->error) { - dprintk(1, "fatal error occurred on queue\n"); + dprintk(q, 1, "fatal error occurred on queue\n"); return -EIO; } @@ -1588,14 +1590,14 @@ int vb2_core_qbuf(struct vb2_queue *q, unsigned int index, void *pb, if (!req && vb->state != VB2_BUF_STATE_IN_REQUEST && q->requires_requests) { - dprintk(1, "qbuf requires a request\n"); + dprintk(q, 1, "qbuf requires a request\n"); return -EBADR; } if ((req && q->uses_qbuf) || (!req && vb->state != VB2_BUF_STATE_IN_REQUEST && q->uses_requests)) { - dprintk(1, "queue in wrong mode (qbuf vs requests)\n"); + dprintk(q, 1, "queue in wrong mode (qbuf vs requests)\n"); return -EBUSY; } @@ -1604,7 +1606,7 @@ int vb2_core_qbuf(struct vb2_queue *q, unsigned int index, void *pb, q->uses_requests = 1; if (vb->state != VB2_BUF_STATE_DEQUEUED) { - dprintk(1, "buffer %d not in dequeued state\n", + dprintk(q, 1, "buffer %d not in dequeued state\n", vb->index); return -EINVAL; } @@ -1612,7 +1614,7 @@ int vb2_core_qbuf(struct vb2_queue *q, unsigned int index, void *pb, if (q->is_output && !vb->prepared) { ret = call_vb_qop(vb, buf_out_validate, vb); if (ret) { - dprintk(1, "buffer validation failed\n"); + dprintk(q, 1, "buffer validation failed\n"); return ret; } } @@ -1648,7 +1650,7 @@ int vb2_core_qbuf(struct vb2_queue *q, unsigned int index, void *pb, call_void_bufop(q, fill_user_buffer, vb, pb); } - dprintk(2, "qbuf of buffer %d succeeded\n", vb->index); + dprintk(q, 2, "qbuf of buffer %d succeeded\n", vb->index); return 0; } @@ -1665,10 +1667,10 @@ int vb2_core_qbuf(struct vb2_queue *q, unsigned int index, void *pb, } break; case VB2_BUF_STATE_PREPARING: - dprintk(1, "buffer still being prepared\n"); + dprintk(q, 1, "buffer still being prepared\n"); return -EINVAL; default: - dprintk(1, "invalid buffer state %d\n", vb->state); + dprintk(q, 1, "invalid buffer state %d\n", vb->state); return -EINVAL; } @@ -1710,7 +1712,7 @@ int vb2_core_qbuf(struct vb2_queue *q, unsigned int index, void *pb, return ret; } - dprintk(2, "qbuf of buffer %d succeeded\n", vb->index); + dprintk(q, 2, "qbuf of buffer %d succeeded\n", vb->index); return 0; } EXPORT_SYMBOL_GPL(vb2_core_qbuf); @@ -1736,22 +1738,22 @@ static int __vb2_wait_for_done_vb(struct vb2_queue *q, int nonblocking) int ret; if (q->waiting_in_dqbuf) { - dprintk(1, "another dup()ped fd is waiting for a buffer\n"); + dprintk(q, 1, "another dup()ped fd is waiting for a buffer\n"); return -EBUSY; } if (!q->streaming) { - dprintk(1, "streaming off, will not wait for buffers\n"); + dprintk(q, 1, "streaming off, will not wait for buffers\n"); return -EINVAL; } if (q->error) { - dprintk(1, "Queue in error state, will not wait for buffers\n"); + dprintk(q, 1, "Queue in error state, will not wait for buffers\n"); return -EIO; } if (q->last_buffer_dequeued) { - dprintk(3, "last buffer dequeued already, will not wait for buffers\n"); + dprintk(q, 3, "last buffer dequeued already, will not wait for buffers\n"); return -EPIPE; } @@ -1763,7 +1765,7 @@ static int __vb2_wait_for_done_vb(struct vb2_queue *q, int nonblocking) } if (nonblocking) { - dprintk(3, "nonblocking and no buffers to dequeue, will not wait\n"); + dprintk(q, 3, "nonblocking and no buffers to dequeue, will not wait\n"); return -EAGAIN; } @@ -1778,7 +1780,7 @@ static int __vb2_wait_for_done_vb(struct vb2_queue *q, int nonblocking) /* * All locks have been released, it is safe to sleep now. */ - dprintk(3, "will sleep waiting for buffers\n"); + dprintk(q, 3, "will sleep waiting for buffers\n"); ret = wait_event_interruptible(q->done_wq, !list_empty(&q->done_list) || !q->streaming || q->error); @@ -1790,7 +1792,7 @@ static int __vb2_wait_for_done_vb(struct vb2_queue *q, int nonblocking) call_void_qop(q, wait_finish, q); q->waiting_in_dqbuf = 0; if (ret) { - dprintk(1, "sleep was interrupted\n"); + dprintk(q, 1, "sleep was interrupted\n"); return ret; } } @@ -1838,7 +1840,7 @@ static int __vb2_get_done_vb(struct vb2_queue *q, struct vb2_buffer **vb, int vb2_wait_for_all_buffers(struct vb2_queue *q) { if (!q->streaming) { - dprintk(1, "streaming off, will not wait for buffers\n"); + dprintk(q, 1, "streaming off, will not wait for buffers\n"); return -EINVAL; } @@ -1876,13 +1878,13 @@ int vb2_core_dqbuf(struct vb2_queue *q, unsigned int *pindex, void *pb, switch (vb->state) { case VB2_BUF_STATE_DONE: - dprintk(3, "returning done buffer\n"); + dprintk(q, 3, "returning done buffer\n"); break; case VB2_BUF_STATE_ERROR: - dprintk(3, "returning done buffer with errors\n"); + dprintk(q, 3, "returning done buffer with errors\n"); break; default: - dprintk(1, "invalid buffer state\n"); + dprintk(q, 1, "invalid buffer state\n"); return -EINVAL; } @@ -1913,7 +1915,7 @@ int vb2_core_dqbuf(struct vb2_queue *q, unsigned int *pindex, void *pb, media_request_put(vb->request); vb->request = NULL; - dprintk(2, "dqbuf of buffer %d, with state %d\n", + dprintk(q, 2, "dqbuf of buffer %d, with state %d\n", vb->index, vb->state); return 0; @@ -2029,22 +2031,22 @@ int vb2_core_streamon(struct vb2_queue *q, unsigned int type) int ret; if (type != q->type) { - dprintk(1, "invalid stream type\n"); + dprintk(q, 1, "invalid stream type\n"); return -EINVAL; } if (q->streaming) { - dprintk(3, "already streaming\n"); + dprintk(q, 3, "already streaming\n"); return 0; } if (!q->num_buffers) { - dprintk(1, "no buffers have been allocated\n"); + dprintk(q, 1, "no buffers have been allocated\n"); return -EINVAL; } if (q->num_buffers < q->min_buffers_needed) { - dprintk(1, "need at least %u allocated buffers\n", + dprintk(q, 1, "need at least %u allocated buffers\n", q->min_buffers_needed); return -EINVAL; } @@ -2064,7 +2066,7 @@ int vb2_core_streamon(struct vb2_queue *q, unsigned int type) q->streaming = 1; - dprintk(3, "successful\n"); + dprintk(q, 3, "successful\n"); return 0; } EXPORT_SYMBOL_GPL(vb2_core_streamon); @@ -2080,7 +2082,7 @@ EXPORT_SYMBOL_GPL(vb2_queue_error); int vb2_core_streamoff(struct vb2_queue *q, unsigned int type) { if (type != q->type) { - dprintk(1, "invalid stream type\n"); + dprintk(q, 1, "invalid stream type\n"); return -EINVAL; } @@ -2097,7 +2099,7 @@ int vb2_core_streamoff(struct vb2_queue *q, unsigned int type) q->waiting_for_buffers = !q->is_output; q->last_buffer_dequeued = false; - dprintk(3, "successful\n"); + dprintk(q, 3, "successful\n"); return 0; } EXPORT_SYMBOL_GPL(vb2_core_streamoff); @@ -2140,39 +2142,39 @@ int vb2_core_expbuf(struct vb2_queue *q, int *fd, unsigned int type, struct dma_buf *dbuf; if (q->memory != VB2_MEMORY_MMAP) { - dprintk(1, "queue is not currently set up for mmap\n"); + dprintk(q, 1, "queue is not currently set up for mmap\n"); return -EINVAL; } if (!q->mem_ops->get_dmabuf) { - dprintk(1, "queue does not support DMA buffer exporting\n"); + dprintk(q, 1, "queue does not support DMA buffer exporting\n"); return -EINVAL; } if (flags & ~(O_CLOEXEC | O_ACCMODE)) { - dprintk(1, "queue does support only O_CLOEXEC and access mode flags\n"); + dprintk(q, 1, "queue does support only O_CLOEXEC and access mode flags\n"); return -EINVAL; } if (type != q->type) { - dprintk(1, "invalid buffer type\n"); + dprintk(q, 1, "invalid buffer type\n"); return -EINVAL; } if (index >= q->num_buffers) { - dprintk(1, "buffer index out of range\n"); + dprintk(q, 1, "buffer index out of range\n"); return -EINVAL; } vb = q->bufs[index]; if (plane >= vb->num_planes) { - dprintk(1, "buffer plane out of range\n"); + dprintk(q, 1, "buffer plane out of range\n"); return -EINVAL; } if (vb2_fileio_is_active(q)) { - dprintk(1, "expbuf: file io in progress\n"); + dprintk(q, 1, "expbuf: file io in progress\n"); return -EBUSY; } @@ -2181,20 +2183,20 @@ int vb2_core_expbuf(struct vb2_queue *q, int *fd, unsigned int type, dbuf = call_ptr_memop(vb, get_dmabuf, vb_plane->mem_priv, flags & O_ACCMODE); if (IS_ERR_OR_NULL(dbuf)) { - dprintk(1, "failed to export buffer %d, plane %d\n", + dprintk(q, 1, "failed to export buffer %d, plane %d\n", index, plane); return -EINVAL; } ret = dma_buf_fd(dbuf, flags & ~O_ACCMODE); if (ret < 0) { - dprintk(3, "buffer %d, plane %d failed to export (%d)\n", + dprintk(q, 3, "buffer %d, plane %d failed to export (%d)\n", index, plane, ret); dma_buf_put(dbuf); return ret; } - dprintk(3, "buffer %d, plane %d exported as %d descriptor\n", + dprintk(q, 3, "buffer %d, plane %d exported as %d descriptor\n", index, plane, ret); *fd = ret; @@ -2211,7 +2213,7 @@ int vb2_mmap(struct vb2_queue *q, struct vm_area_struct *vma) unsigned long length; if (q->memory != VB2_MEMORY_MMAP) { - dprintk(1, "queue is not currently set up for mmap\n"); + dprintk(q, 1, "queue is not currently set up for mmap\n"); return -EINVAL; } @@ -2219,17 +2221,17 @@ int vb2_mmap(struct vb2_queue *q, struct vm_area_struct *vma) * Check memory area access mode. */ if (!(vma->vm_flags & VM_SHARED)) { - dprintk(1, "invalid vma flags, VM_SHARED needed\n"); + dprintk(q, 1, "invalid vma flags, VM_SHARED needed\n"); return -EINVAL; } if (q->is_output) { if (!(vma->vm_flags & VM_WRITE)) { - dprintk(1, "invalid vma flags, VM_WRITE needed\n"); + dprintk(q, 1, "invalid vma flags, VM_WRITE needed\n"); return -EINVAL; } } else { if (!(vma->vm_flags & VM_READ)) { - dprintk(1, "invalid vma flags, VM_READ needed\n"); + dprintk(q, 1, "invalid vma flags, VM_READ needed\n"); return -EINVAL; } } @@ -2237,7 +2239,7 @@ int vb2_mmap(struct vb2_queue *q, struct vm_area_struct *vma) mutex_lock(&q->mmap_lock); if (vb2_fileio_is_active(q)) { - dprintk(1, "mmap: file io in progress\n"); + dprintk(q, 1, "mmap: file io in progress\n"); ret = -EBUSY; goto unlock; } @@ -2258,7 +2260,7 @@ int vb2_mmap(struct vb2_queue *q, struct vm_area_struct *vma) */ length = PAGE_ALIGN(vb->planes[plane].length); if (length < (vma->vm_end - vma->vm_start)) { - dprintk(1, + dprintk(q, 1, "MMAP invalid, as it would overflow buffer length\n"); ret = -EINVAL; goto unlock; @@ -2278,7 +2280,7 @@ unlock: if (ret) return ret; - dprintk(3, "buffer %d, plane %d successfully mapped\n", buffer, plane); + dprintk(q, 3, "buffer %d, plane %d successfully mapped\n", buffer, plane); return 0; } EXPORT_SYMBOL_GPL(vb2_mmap); @@ -2297,7 +2299,7 @@ unsigned long vb2_get_unmapped_area(struct vb2_queue *q, int ret; if (q->memory != VB2_MEMORY_MMAP) { - dprintk(1, "queue is not currently set up for mmap\n"); + dprintk(q, 1, "queue is not currently set up for mmap\n"); return -EINVAL; } @@ -2349,6 +2351,10 @@ int vb2_core_queue_init(struct vb2_queue *q) else q->dma_dir = q->is_output ? DMA_TO_DEVICE : DMA_FROM_DEVICE; + if (q->name[0] == '\0') + snprintf(q->name, sizeof(q->name), "%s-%p", + q->is_output ? "out" : "cap", q); + return 0; } EXPORT_SYMBOL_GPL(vb2_core_queue_init); @@ -2537,7 +2543,7 @@ static int __vb2_init_fileio(struct vb2_queue *q, int read) */ count = 1; - dprintk(3, "setting up file io: mode %s, count %d, read_once %d, write_immediately %d\n", + dprintk(q, 3, "setting up file io: mode %s, count %d, read_once %d, write_immediately %d\n", (read) ? "read" : "write", count, q->fileio_read_once, q->fileio_write_immediately); @@ -2635,7 +2641,7 @@ static int __vb2_cleanup_fileio(struct vb2_queue *q) fileio->count = 0; vb2_core_reqbufs(q, fileio->memory, 0, &fileio->count); kfree(fileio); - dprintk(3, "file io emulator closed\n"); + dprintk(q, 3, "file io emulator closed\n"); } return 0; } @@ -2664,7 +2670,7 @@ static size_t __vb2_perform_fileio(struct vb2_queue *q, char __user *data, size_ unsigned index; int ret; - dprintk(3, "mode %s, offset %ld, count %zd, %sblocking\n", + dprintk(q, 3, "mode %s, offset %ld, count %zd, %sblocking\n", read ? "read" : "write", (long)*ppos, count, nonblock ? "non" : ""); @@ -2672,7 +2678,7 @@ static size_t __vb2_perform_fileio(struct vb2_queue *q, char __user *data, size_ return -EINVAL; if (q->waiting_in_dqbuf) { - dprintk(3, "another dup()ped fd is %s\n", + dprintk(q, 3, "another dup()ped fd is %s\n", read ? "reading" : "writing"); return -EBUSY; } @@ -2682,7 +2688,7 @@ static size_t __vb2_perform_fileio(struct vb2_queue *q, char __user *data, size_ */ if (!vb2_fileio_is_active(q)) { ret = __vb2_init_fileio(q, read); - dprintk(3, "vb2_init_fileio result: %d\n", ret); + dprintk(q, 3, "vb2_init_fileio result: %d\n", ret); if (ret) return ret; } @@ -2699,7 +2705,7 @@ static size_t __vb2_perform_fileio(struct vb2_queue *q, char __user *data, size_ * Call vb2_dqbuf to get buffer back. */ ret = vb2_core_dqbuf(q, &index, NULL, nonblock); - dprintk(5, "vb2_dqbuf result: %d\n", ret); + dprintk(q, 5, "vb2_dqbuf result: %d\n", ret); if (ret) return ret; fileio->dq_count += 1; @@ -2730,20 +2736,20 @@ static size_t __vb2_perform_fileio(struct vb2_queue *q, char __user *data, size_ */ if (buf->pos + count > buf->size) { count = buf->size - buf->pos; - dprintk(5, "reducing read count: %zd\n", count); + dprintk(q, 5, "reducing read count: %zd\n", count); } /* * Transfer data to userspace. */ - dprintk(3, "copying %zd bytes - buffer %d, offset %u\n", + dprintk(q, 3, "copying %zd bytes - buffer %d, offset %u\n", count, index, buf->pos); if (read) ret = copy_to_user(data, buf->vaddr + buf->pos, count); else ret = copy_from_user(buf->vaddr + buf->pos, data, count); if (ret) { - dprintk(3, "error copying data\n"); + dprintk(q, 3, "error copying data\n"); return -EFAULT; } @@ -2763,7 +2769,7 @@ static size_t __vb2_perform_fileio(struct vb2_queue *q, char __user *data, size_ * Check if this is the last buffer to read. */ if (read && fileio->read_once && fileio->dq_count == 1) { - dprintk(3, "read limit reached\n"); + dprintk(q, 3, "read limit reached\n"); return __vb2_cleanup_fileio(q); } @@ -2775,7 +2781,7 @@ static size_t __vb2_perform_fileio(struct vb2_queue *q, char __user *data, size_ if (copy_timestamp) b->timestamp = ktime_get_ns(); ret = vb2_core_qbuf(q, index, NULL, NULL); - dprintk(5, "vb2_dbuf result: %d\n", ret); + dprintk(q, 5, "vb2_dbuf result: %d\n", ret); if (ret) return ret; @@ -2862,7 +2868,7 @@ static int vb2_thread(void *data) if (!threadio->stop) ret = vb2_core_dqbuf(q, &index, NULL, 0); call_void_qop(q, wait_prepare, q); - dprintk(5, "file io: vb2_dqbuf result: %d\n", ret); + dprintk(q, 5, "file io: vb2_dqbuf result: %d\n", ret); if (!ret) vb = q->bufs[index]; } @@ -2916,7 +2922,7 @@ int vb2_thread_start(struct vb2_queue *q, vb2_thread_fnc fnc, void *priv, threadio->priv = priv; ret = __vb2_init_fileio(q, !q->is_output); - dprintk(3, "file io: vb2_init_fileio result: %d\n", ret); + dprintk(q, 3, "file io: vb2_init_fileio result: %d\n", ret); if (ret) goto nomem; q->threadio = threadio; diff --git a/drivers/media/common/videobuf2/videobuf2-v4l2.c b/drivers/media/common/videobuf2/videobuf2-v4l2.c index 559a229cac414..98477a82c8109 100644 --- a/drivers/media/common/videobuf2/videobuf2-v4l2.c +++ b/drivers/media/common/videobuf2/videobuf2-v4l2.c @@ -35,10 +35,11 @@ static int debug; module_param(debug, int, 0644); -#define dprintk(level, fmt, arg...) \ +#define dprintk(q, level, fmt, arg...) \ do { \ if (debug >= level) \ - pr_info("vb2-v4l2: %s: " fmt, __func__, ## arg); \ + pr_info("vb2-v4l2: [%p] %s: " fmt, \ + (q)->name, __func__, ## arg); \ } while (0) /* Flags that are set by us */ @@ -66,12 +67,14 @@ static int __verify_planes_array(struct vb2_buffer *vb, const struct v4l2_buffer /* Is memory for copying plane information present? */ if (b->m.planes == NULL) { - dprintk(1, "multi-planar buffer passed but planes array not provided\n"); + dprintk(vb->vb2_queue, 1, + "multi-planar buffer passed but planes array not provided\n"); return -EINVAL; } if (b->length < vb->num_planes || b->length > VB2_MAX_PLANES) { - dprintk(1, "incorrect planes array length, expected %d, got %d\n", + dprintk(vb->vb2_queue, 1, + "incorrect planes array length, expected %d, got %d\n", vb->num_planes, b->length); return -EINVAL; } @@ -179,7 +182,7 @@ static int vb2_fill_vb2_v4l2_buffer(struct vb2_buffer *vb, struct v4l2_buffer *b ret = __verify_length(vb, b); if (ret < 0) { - dprintk(1, "plane parameters verification failed: %d\n", ret); + dprintk(q, 1, "plane parameters verification failed: %d\n", ret); return ret; } if (b->field == V4L2_FIELD_ALTERNATE && q->is_output) { @@ -192,7 +195,7 @@ static int vb2_fill_vb2_v4l2_buffer(struct vb2_buffer *vb, struct v4l2_buffer *b * that just says that it is either a top or a bottom field, * but not which of the two it is. */ - dprintk(1, "the field is incorrectly set to ALTERNATE for an output buffer\n"); + dprintk(q, 1, "the field is incorrectly set to ALTERNATE for an output buffer\n"); return -EINVAL; } vbuf->sequence = 0; @@ -395,23 +398,23 @@ static int vb2_queue_or_prepare_buf(struct vb2_queue *q, struct media_device *md int ret; if (b->type != q->type) { - dprintk(1, "%s: invalid buffer type\n", opname); + dprintk(q, 1, "%s: invalid buffer type\n", opname); return -EINVAL; } if (b->index >= q->num_buffers) { - dprintk(1, "%s: buffer index out of range\n", opname); + dprintk(q, 1, "%s: buffer index out of range\n", opname); return -EINVAL; } if (q->bufs[b->index] == NULL) { /* Should never happen */ - dprintk(1, "%s: buffer is NULL\n", opname); + dprintk(q, 1, "%s: buffer is NULL\n", opname); return -EINVAL; } if (b->memory != q->memory) { - dprintk(1, "%s: invalid memory type\n", opname); + dprintk(q, 1, "%s: invalid memory type\n", opname); return -EINVAL; } @@ -423,7 +426,7 @@ static int vb2_queue_or_prepare_buf(struct vb2_queue *q, struct media_device *md if (!is_prepare && (b->flags & V4L2_BUF_FLAG_REQUEST_FD) && vb->state != VB2_BUF_STATE_DEQUEUED) { - dprintk(1, "%s: buffer is not in dequeued state\n", opname); + dprintk(q, 1, "%s: buffer is not in dequeued state\n", opname); return -EINVAL; } @@ -442,19 +445,19 @@ static int vb2_queue_or_prepare_buf(struct vb2_queue *q, struct media_device *md if (!(b->flags & V4L2_BUF_FLAG_REQUEST_FD)) { if (q->requires_requests) { - dprintk(1, "%s: queue requires requests\n", opname); + dprintk(q, 1, "%s: queue requires requests\n", opname); return -EBADR; } if (q->uses_requests) { - dprintk(1, "%s: queue uses requests\n", opname); + dprintk(q, 1, "%s: queue uses requests\n", opname); return -EBUSY; } return 0; } else if (!q->supports_requests) { - dprintk(1, "%s: queue does not support requests\n", opname); + dprintk(q, 1, "%s: queue does not support requests\n", opname); return -EBADR; } else if (q->uses_qbuf) { - dprintk(1, "%s: queue does not use requests\n", opname); + dprintk(q, 1, "%s: queue does not use requests\n", opname); return -EBUSY; } @@ -484,13 +487,13 @@ static int vb2_queue_or_prepare_buf(struct vb2_queue *q, struct media_device *md return -EINVAL; if (b->request_fd < 0) { - dprintk(1, "%s: request_fd < 0\n", opname); + dprintk(q, 1, "%s: request_fd < 0\n", opname); return -EINVAL; } req = media_request_get_by_fd(mdev, b->request_fd); if (IS_ERR(req)) { - dprintk(1, "%s: invalid request_fd\n", opname); + dprintk(q, 1, "%s: invalid request_fd\n", opname); return PTR_ERR(req); } @@ -500,7 +503,7 @@ static int vb2_queue_or_prepare_buf(struct vb2_queue *q, struct media_device *md */ if (req->state != MEDIA_REQUEST_STATE_IDLE && req->state != MEDIA_REQUEST_STATE_UPDATING) { - dprintk(1, "%s: request is not idle\n", opname); + dprintk(q, 1, "%s: request is not idle\n", opname); media_request_put(req); return -EBUSY; } @@ -683,12 +686,12 @@ int vb2_querybuf(struct vb2_queue *q, struct v4l2_buffer *b) int ret; if (b->type != q->type) { - dprintk(1, "wrong buffer type\n"); + dprintk(q, 1, "wrong buffer type\n"); return -EINVAL; } if (b->index >= q->num_buffers) { - dprintk(1, "buffer index out of range\n"); + dprintk(q, 1, "buffer index out of range\n"); return -EINVAL; } vb = q->bufs[b->index]; @@ -743,7 +746,7 @@ int vb2_prepare_buf(struct vb2_queue *q, struct media_device *mdev, int ret; if (vb2_fileio_is_active(q)) { - dprintk(1, "file io in progress\n"); + dprintk(q, 1, "file io in progress\n"); return -EBUSY; } @@ -823,7 +826,7 @@ int vb2_qbuf(struct vb2_queue *q, struct media_device *mdev, int ret; if (vb2_fileio_is_active(q)) { - dprintk(1, "file io in progress\n"); + dprintk(q, 1, "file io in progress\n"); return -EBUSY; } @@ -842,12 +845,12 @@ int vb2_dqbuf(struct vb2_queue *q, struct v4l2_buffer *b, bool nonblocking) int ret; if (vb2_fileio_is_active(q)) { - dprintk(1, "file io in progress\n"); + dprintk(q, 1, "file io in progress\n"); return -EBUSY; } if (b->type != q->type) { - dprintk(1, "invalid buffer type\n"); + dprintk(q, 1, "invalid buffer type\n"); return -EINVAL; } @@ -871,7 +874,7 @@ EXPORT_SYMBOL_GPL(vb2_dqbuf); int vb2_streamon(struct vb2_queue *q, enum v4l2_buf_type type) { if (vb2_fileio_is_active(q)) { - dprintk(1, "file io in progress\n"); + dprintk(q, 1, "file io in progress\n"); return -EBUSY; } return vb2_core_streamon(q, type); @@ -881,7 +884,7 @@ EXPORT_SYMBOL_GPL(vb2_streamon); int vb2_streamoff(struct vb2_queue *q, enum v4l2_buf_type type) { if (vb2_fileio_is_active(q)) { - dprintk(1, "file io in progress\n"); + dprintk(q, 1, "file io in progress\n"); return -EBUSY; } return vb2_core_streamoff(q, type); @@ -895,7 +898,7 @@ int vb2_expbuf(struct vb2_queue *q, struct v4l2_exportbuffer *eb) } EXPORT_SYMBOL_GPL(vb2_expbuf); -int vb2_queue_init(struct vb2_queue *q) +int vb2_queue_init_name(struct vb2_queue *q, const char *name) { /* * Sanity check @@ -931,8 +934,19 @@ int vb2_queue_init(struct vb2_queue *q) */ q->quirk_poll_must_check_waiting_for_buffers = true; + if (name) + strscpy(q->name, name, sizeof(q->name)); + else + q->name[0] = '\0'; + return vb2_core_queue_init(q); } +EXPORT_SYMBOL_GPL(vb2_queue_init_name); + +int vb2_queue_init(struct vb2_queue *q) +{ + return vb2_queue_init_name(q, NULL); +} EXPORT_SYMBOL_GPL(vb2_queue_init); void vb2_queue_release(struct vb2_queue *q) diff --git a/include/media/videobuf2-core.h b/include/media/videobuf2-core.h index 9e68fe043a6cb..52ef92049073e 100644 --- a/include/media/videobuf2-core.h +++ b/include/media/videobuf2-core.h @@ -561,6 +561,8 @@ struct vb2_buf_ops { * when a buffer with the %V4L2_BUF_FLAG_LAST is dequeued. * @fileio: file io emulator internal data, used only if emulator is active * @threadio: thread io internal data, used only if thread is active + * @name: queue name, used for logging purpose. Initialized automatically + * if left empty by drivers. */ struct vb2_queue { unsigned int type; @@ -622,6 +624,8 @@ struct vb2_queue { struct vb2_fileio_data *fileio; struct vb2_threadio_data *threadio; + char name[32]; + #ifdef CONFIG_VIDEO_ADV_DEBUG /* * Counters for how often these queue-related ops are diff --git a/include/media/videobuf2-v4l2.h b/include/media/videobuf2-v4l2.h index 59bf33a12648b..b7b5a9cb5a280 100644 --- a/include/media/videobuf2-v4l2.h +++ b/include/media/videobuf2-v4l2.h @@ -236,6 +236,19 @@ int vb2_streamoff(struct vb2_queue *q, enum v4l2_buf_type type); */ int __must_check vb2_queue_init(struct vb2_queue *q); +/** + * vb2_queue_init_name() - initialize a videobuf2 queue with a name + * @q: pointer to &struct vb2_queue with videobuf2 queue. + * @name: the queue name + * + * This function initializes the vb2_queue exactly like vb2_queue_init(), + * and additionally sets the queue name. The queue name is used for logging + * purpose, and should uniquely identify the queue within the context of the + * device it belongs to. This is useful to attribute kernel log messages to the + * right queue for m2m devices or other devices that handle multiple queues. + */ +int __must_check vb2_queue_init_name(struct vb2_queue *q, const char *name); + /** * vb2_queue_release() - stop streaming, release the queue and free memory * @q: pointer to &struct vb2_queue with videobuf2 queue. -- GitLab From d93d45ab716e4107056be54969c8c70e50a8346d Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Fri, 19 Jun 2020 16:30:07 +0200 Subject: [PATCH 0253/1476] media: allegro: Fix some NULL vs IS_ERR() checks in probe The devm_ioremap() function doesn't return error pointers, it returns NULL on error. Fixes: f20387dfd065 ("media: allegro: add Allegro DVT video IP core driver") Signed-off-by: Dan Carpenter Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/allegro-dvt/allegro-core.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/staging/media/allegro-dvt/allegro-core.c b/drivers/staging/media/allegro-dvt/allegro-core.c index 70f133a842ddf..3ed66aae741d5 100644 --- a/drivers/staging/media/allegro-dvt/allegro-core.c +++ b/drivers/staging/media/allegro-dvt/allegro-core.c @@ -3065,9 +3065,9 @@ static int allegro_probe(struct platform_device *pdev) return -EINVAL; } regs = devm_ioremap(&pdev->dev, res->start, resource_size(res)); - if (IS_ERR(regs)) { + if (!regs) { dev_err(&pdev->dev, "failed to map registers\n"); - return PTR_ERR(regs); + return -ENOMEM; } dev->regmap = devm_regmap_init_mmio(&pdev->dev, regs, &allegro_regmap_config); @@ -3085,9 +3085,9 @@ static int allegro_probe(struct platform_device *pdev) sram_regs = devm_ioremap(&pdev->dev, sram_res->start, resource_size(sram_res)); - if (IS_ERR(sram_regs)) { + if (!sram_regs) { dev_err(&pdev->dev, "failed to map sram\n"); - return PTR_ERR(sram_regs); + return -ENOMEM; } dev->sram = devm_regmap_init_mmio(&pdev->dev, sram_regs, &allegro_sram_config); -- GitLab From 7b8ce1f2763b9351a4cb04b802835470e76770a5 Mon Sep 17 00:00:00 2001 From: Dafna Hirschfeld Date: Thu, 18 Jun 2020 13:35:15 +0200 Subject: [PATCH 0254/1476] media: staging: rkisp1: rsz: supported formats are the isp's src formats, not sink formats The rkisp1_resizer's enum callback 'rkisp1_rsz_enum_mbus_code' calls the enum callback of the 'rkisp1_isp' on it's video sink pad. This is a bug, the resizer should support the same formats supported by the 'rkisp1_isp' on the source pad (not the sink pad). Fixes: 56e3b29f9f6b "media: staging: rkisp1: add streaming paths" Signed-off-by: Dafna Hirschfeld Acked-by: Helen Koike Reviewed-by: Tomasz Figa Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/rkisp1/rkisp1-resizer.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/staging/media/rkisp1/rkisp1-resizer.c b/drivers/staging/media/rkisp1/rkisp1-resizer.c index 26fb41053f56c..27f4b948ad415 100644 --- a/drivers/staging/media/rkisp1/rkisp1-resizer.c +++ b/drivers/staging/media/rkisp1/rkisp1-resizer.c @@ -437,8 +437,8 @@ static int rkisp1_rsz_enum_mbus_code(struct v4l2_subdev *sd, u32 pad = code->pad; int ret; - /* supported mbus codes are the same in isp sink pad */ - code->pad = RKISP1_ISP_PAD_SINK_VIDEO; + /* supported mbus codes are the same in isp video src pad */ + code->pad = RKISP1_ISP_PAD_SOURCE_VIDEO; ret = v4l2_subdev_call(&rsz->rkisp1->isp.sd, pad, enum_mbus_code, &dummy_cfg, code); -- GitLab From b861d139a36a4593498932bfec957bdcc7d98eb3 Mon Sep 17 00:00:00 2001 From: Dafna Hirschfeld Date: Thu, 18 Jun 2020 13:35:16 +0200 Subject: [PATCH 0255/1476] media: staging: rkisp1: remove macro RKISP1_DIR_SINK_SRC The macro RKISP1_DIR_SINK_SRC is a mask of two flags. The macro hides the fact that it's a mask and the code is actually more clear if we replace it the with bitwise-or explicitly. Signed-off-by: Dafna Hirschfeld Acked-by: Helen Koike Reviewed-by: Tomasz Figa Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/rkisp1/rkisp1-isp.c | 25 +++++++++++------------ 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/drivers/staging/media/rkisp1/rkisp1-isp.c b/drivers/staging/media/rkisp1/rkisp1-isp.c index 6763379d6a653..8ea82ed1e6448 100644 --- a/drivers/staging/media/rkisp1/rkisp1-isp.c +++ b/drivers/staging/media/rkisp1/rkisp1-isp.c @@ -25,7 +25,6 @@ #define RKISP1_DIR_SRC BIT(0) #define RKISP1_DIR_SINK BIT(1) -#define RKISP1_DIR_SINK_SRC (RKISP1_DIR_SINK | RKISP1_DIR_SRC) /* * NOTE: MIPI controller and input MUX are also configured in this file. @@ -69,84 +68,84 @@ static const struct rkisp1_isp_mbus_info rkisp1_isp_formats[] = { .mipi_dt = RKISP1_CIF_CSI2_DT_RAW10, .bayer_pat = RKISP1_RAW_RGGB, .bus_width = 10, - .direction = RKISP1_DIR_SINK_SRC, + .direction = RKISP1_DIR_SINK | RKISP1_DIR_SRC, }, { .mbus_code = MEDIA_BUS_FMT_SBGGR10_1X10, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mipi_dt = RKISP1_CIF_CSI2_DT_RAW10, .bayer_pat = RKISP1_RAW_BGGR, .bus_width = 10, - .direction = RKISP1_DIR_SINK_SRC, + .direction = RKISP1_DIR_SINK | RKISP1_DIR_SRC, }, { .mbus_code = MEDIA_BUS_FMT_SGBRG10_1X10, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mipi_dt = RKISP1_CIF_CSI2_DT_RAW10, .bayer_pat = RKISP1_RAW_GBRG, .bus_width = 10, - .direction = RKISP1_DIR_SINK_SRC, + .direction = RKISP1_DIR_SINK | RKISP1_DIR_SRC, }, { .mbus_code = MEDIA_BUS_FMT_SGRBG10_1X10, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mipi_dt = RKISP1_CIF_CSI2_DT_RAW10, .bayer_pat = RKISP1_RAW_GRBG, .bus_width = 10, - .direction = RKISP1_DIR_SINK_SRC, + .direction = RKISP1_DIR_SINK | RKISP1_DIR_SRC, }, { .mbus_code = MEDIA_BUS_FMT_SRGGB12_1X12, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mipi_dt = RKISP1_CIF_CSI2_DT_RAW12, .bayer_pat = RKISP1_RAW_RGGB, .bus_width = 12, - .direction = RKISP1_DIR_SINK_SRC, + .direction = RKISP1_DIR_SINK | RKISP1_DIR_SRC, }, { .mbus_code = MEDIA_BUS_FMT_SBGGR12_1X12, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mipi_dt = RKISP1_CIF_CSI2_DT_RAW12, .bayer_pat = RKISP1_RAW_BGGR, .bus_width = 12, - .direction = RKISP1_DIR_SINK_SRC, + .direction = RKISP1_DIR_SINK | RKISP1_DIR_SRC, }, { .mbus_code = MEDIA_BUS_FMT_SGBRG12_1X12, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mipi_dt = RKISP1_CIF_CSI2_DT_RAW12, .bayer_pat = RKISP1_RAW_GBRG, .bus_width = 12, - .direction = RKISP1_DIR_SINK_SRC, + .direction = RKISP1_DIR_SINK | RKISP1_DIR_SRC, }, { .mbus_code = MEDIA_BUS_FMT_SGRBG12_1X12, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mipi_dt = RKISP1_CIF_CSI2_DT_RAW12, .bayer_pat = RKISP1_RAW_GRBG, .bus_width = 12, - .direction = RKISP1_DIR_SINK_SRC, + .direction = RKISP1_DIR_SINK | RKISP1_DIR_SRC, }, { .mbus_code = MEDIA_BUS_FMT_SRGGB8_1X8, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mipi_dt = RKISP1_CIF_CSI2_DT_RAW8, .bayer_pat = RKISP1_RAW_RGGB, .bus_width = 8, - .direction = RKISP1_DIR_SINK_SRC, + .direction = RKISP1_DIR_SINK | RKISP1_DIR_SRC, }, { .mbus_code = MEDIA_BUS_FMT_SBGGR8_1X8, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mipi_dt = RKISP1_CIF_CSI2_DT_RAW8, .bayer_pat = RKISP1_RAW_BGGR, .bus_width = 8, - .direction = RKISP1_DIR_SINK_SRC, + .direction = RKISP1_DIR_SINK | RKISP1_DIR_SRC, }, { .mbus_code = MEDIA_BUS_FMT_SGBRG8_1X8, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mipi_dt = RKISP1_CIF_CSI2_DT_RAW8, .bayer_pat = RKISP1_RAW_GBRG, .bus_width = 8, - .direction = RKISP1_DIR_SINK_SRC, + .direction = RKISP1_DIR_SINK | RKISP1_DIR_SRC, }, { .mbus_code = MEDIA_BUS_FMT_SGRBG8_1X8, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mipi_dt = RKISP1_CIF_CSI2_DT_RAW8, .bayer_pat = RKISP1_RAW_GRBG, .bus_width = 8, - .direction = RKISP1_DIR_SINK_SRC, + .direction = RKISP1_DIR_SINK | RKISP1_DIR_SRC, }, { .mbus_code = MEDIA_BUS_FMT_YUYV8_1X16, .pixel_enc = V4L2_PIXEL_ENC_YUV, -- GitLab From c247818a873adcb8488021eed38c330ea8b288a3 Mon Sep 17 00:00:00 2001 From: Dafna Hirschfeld Date: Thu, 18 Jun 2020 13:35:17 +0200 Subject: [PATCH 0256/1476] media: staging: rkisp1: rename macros 'RKISP1_DIR_*' to 'RKISP1_ISP_SD_*' The macros 'RKISP1_DIR_*' are flags that indicate on which pads of the isp subdevice the media bus code is supported. So the prefix RKISP1_ISP_SD_ is better. Signed-off-by: Dafna Hirschfeld Acked-by: Helen Koike Reviewed-by: Tomasz Figa Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/rkisp1/rkisp1-isp.c | 46 +++++++++++------------ 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/drivers/staging/media/rkisp1/rkisp1-isp.c b/drivers/staging/media/rkisp1/rkisp1-isp.c index 8ea82ed1e6448..1d0b35133f65a 100644 --- a/drivers/staging/media/rkisp1/rkisp1-isp.c +++ b/drivers/staging/media/rkisp1/rkisp1-isp.c @@ -23,8 +23,8 @@ #define RKISP1_ISP_DEV_NAME RKISP1_DRIVER_NAME "_isp" -#define RKISP1_DIR_SRC BIT(0) -#define RKISP1_DIR_SINK BIT(1) +#define RKISP1_ISP_SD_SRC BIT(0) +#define RKISP1_ISP_SD_SINK BIT(1) /* * NOTE: MIPI controller and input MUX are also configured in this file. @@ -61,119 +61,119 @@ static const struct rkisp1_isp_mbus_info rkisp1_isp_formats[] = { { .mbus_code = MEDIA_BUS_FMT_YUYV8_2X8, .pixel_enc = V4L2_PIXEL_ENC_YUV, - .direction = RKISP1_DIR_SRC, + .direction = RKISP1_ISP_SD_SRC, }, { .mbus_code = MEDIA_BUS_FMT_SRGGB10_1X10, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mipi_dt = RKISP1_CIF_CSI2_DT_RAW10, .bayer_pat = RKISP1_RAW_RGGB, .bus_width = 10, - .direction = RKISP1_DIR_SINK | RKISP1_DIR_SRC, + .direction = RKISP1_ISP_SD_SINK | RKISP1_ISP_SD_SRC, }, { .mbus_code = MEDIA_BUS_FMT_SBGGR10_1X10, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mipi_dt = RKISP1_CIF_CSI2_DT_RAW10, .bayer_pat = RKISP1_RAW_BGGR, .bus_width = 10, - .direction = RKISP1_DIR_SINK | RKISP1_DIR_SRC, + .direction = RKISP1_ISP_SD_SINK | RKISP1_ISP_SD_SRC, }, { .mbus_code = MEDIA_BUS_FMT_SGBRG10_1X10, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mipi_dt = RKISP1_CIF_CSI2_DT_RAW10, .bayer_pat = RKISP1_RAW_GBRG, .bus_width = 10, - .direction = RKISP1_DIR_SINK | RKISP1_DIR_SRC, + .direction = RKISP1_ISP_SD_SINK | RKISP1_ISP_SD_SRC, }, { .mbus_code = MEDIA_BUS_FMT_SGRBG10_1X10, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mipi_dt = RKISP1_CIF_CSI2_DT_RAW10, .bayer_pat = RKISP1_RAW_GRBG, .bus_width = 10, - .direction = RKISP1_DIR_SINK | RKISP1_DIR_SRC, + .direction = RKISP1_ISP_SD_SINK | RKISP1_ISP_SD_SRC, }, { .mbus_code = MEDIA_BUS_FMT_SRGGB12_1X12, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mipi_dt = RKISP1_CIF_CSI2_DT_RAW12, .bayer_pat = RKISP1_RAW_RGGB, .bus_width = 12, - .direction = RKISP1_DIR_SINK | RKISP1_DIR_SRC, + .direction = RKISP1_ISP_SD_SINK | RKISP1_ISP_SD_SRC, }, { .mbus_code = MEDIA_BUS_FMT_SBGGR12_1X12, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mipi_dt = RKISP1_CIF_CSI2_DT_RAW12, .bayer_pat = RKISP1_RAW_BGGR, .bus_width = 12, - .direction = RKISP1_DIR_SINK | RKISP1_DIR_SRC, + .direction = RKISP1_ISP_SD_SINK | RKISP1_ISP_SD_SRC, }, { .mbus_code = MEDIA_BUS_FMT_SGBRG12_1X12, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mipi_dt = RKISP1_CIF_CSI2_DT_RAW12, .bayer_pat = RKISP1_RAW_GBRG, .bus_width = 12, - .direction = RKISP1_DIR_SINK | RKISP1_DIR_SRC, + .direction = RKISP1_ISP_SD_SINK | RKISP1_ISP_SD_SRC, }, { .mbus_code = MEDIA_BUS_FMT_SGRBG12_1X12, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mipi_dt = RKISP1_CIF_CSI2_DT_RAW12, .bayer_pat = RKISP1_RAW_GRBG, .bus_width = 12, - .direction = RKISP1_DIR_SINK | RKISP1_DIR_SRC, + .direction = RKISP1_ISP_SD_SINK | RKISP1_ISP_SD_SRC, }, { .mbus_code = MEDIA_BUS_FMT_SRGGB8_1X8, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mipi_dt = RKISP1_CIF_CSI2_DT_RAW8, .bayer_pat = RKISP1_RAW_RGGB, .bus_width = 8, - .direction = RKISP1_DIR_SINK | RKISP1_DIR_SRC, + .direction = RKISP1_ISP_SD_SINK | RKISP1_ISP_SD_SRC, }, { .mbus_code = MEDIA_BUS_FMT_SBGGR8_1X8, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mipi_dt = RKISP1_CIF_CSI2_DT_RAW8, .bayer_pat = RKISP1_RAW_BGGR, .bus_width = 8, - .direction = RKISP1_DIR_SINK | RKISP1_DIR_SRC, + .direction = RKISP1_ISP_SD_SINK | RKISP1_ISP_SD_SRC, }, { .mbus_code = MEDIA_BUS_FMT_SGBRG8_1X8, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mipi_dt = RKISP1_CIF_CSI2_DT_RAW8, .bayer_pat = RKISP1_RAW_GBRG, .bus_width = 8, - .direction = RKISP1_DIR_SINK | RKISP1_DIR_SRC, + .direction = RKISP1_ISP_SD_SINK | RKISP1_ISP_SD_SRC, }, { .mbus_code = MEDIA_BUS_FMT_SGRBG8_1X8, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mipi_dt = RKISP1_CIF_CSI2_DT_RAW8, .bayer_pat = RKISP1_RAW_GRBG, .bus_width = 8, - .direction = RKISP1_DIR_SINK | RKISP1_DIR_SRC, + .direction = RKISP1_ISP_SD_SINK | RKISP1_ISP_SD_SRC, }, { .mbus_code = MEDIA_BUS_FMT_YUYV8_1X16, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mipi_dt = RKISP1_CIF_CSI2_DT_YUV422_8b, .yuv_seq = RKISP1_CIF_ISP_ACQ_PROP_YCBYCR, .bus_width = 16, - .direction = RKISP1_DIR_SINK, + .direction = RKISP1_ISP_SD_SINK, }, { .mbus_code = MEDIA_BUS_FMT_YVYU8_1X16, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mipi_dt = RKISP1_CIF_CSI2_DT_YUV422_8b, .yuv_seq = RKISP1_CIF_ISP_ACQ_PROP_YCRYCB, .bus_width = 16, - .direction = RKISP1_DIR_SINK, + .direction = RKISP1_ISP_SD_SINK, }, { .mbus_code = MEDIA_BUS_FMT_UYVY8_1X16, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mipi_dt = RKISP1_CIF_CSI2_DT_YUV422_8b, .yuv_seq = RKISP1_CIF_ISP_ACQ_PROP_CBYCRY, .bus_width = 16, - .direction = RKISP1_DIR_SINK, + .direction = RKISP1_ISP_SD_SINK, }, { .mbus_code = MEDIA_BUS_FMT_VYUY8_1X16, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mipi_dt = RKISP1_CIF_CSI2_DT_YUV422_8b, .yuv_seq = RKISP1_CIF_ISP_ACQ_PROP_CRYCBY, .bus_width = 16, - .direction = RKISP1_DIR_SINK, + .direction = RKISP1_ISP_SD_SINK, }, }; @@ -571,9 +571,9 @@ static int rkisp1_isp_enum_mbus_code(struct v4l2_subdev *sd, int pos = 0; if (code->pad == RKISP1_ISP_PAD_SINK_VIDEO) { - dir = RKISP1_DIR_SINK; + dir = RKISP1_ISP_SD_SINK; } else if (code->pad == RKISP1_ISP_PAD_SOURCE_VIDEO) { - dir = RKISP1_DIR_SRC; + dir = RKISP1_ISP_SD_SRC; } else { if (code->index > 0) return -EINVAL; @@ -658,7 +658,7 @@ static void rkisp1_isp_set_src_fmt(struct rkisp1_isp *isp, src_fmt->code = format->code; mbus_info = rkisp1_isp_mbus_info_get(src_fmt->code); - if (!mbus_info || !(mbus_info->direction & RKISP1_DIR_SRC)) { + if (!mbus_info || !(mbus_info->direction & RKISP1_ISP_SD_SRC)) { src_fmt->code = RKISP1_DEF_SRC_PAD_FMT; mbus_info = rkisp1_isp_mbus_info_get(src_fmt->code); } @@ -742,7 +742,7 @@ static void rkisp1_isp_set_sink_fmt(struct rkisp1_isp *isp, which); sink_fmt->code = format->code; mbus_info = rkisp1_isp_mbus_info_get(sink_fmt->code); - if (!mbus_info || !(mbus_info->direction & RKISP1_DIR_SINK)) { + if (!mbus_info || !(mbus_info->direction & RKISP1_ISP_SD_SINK)) { sink_fmt->code = RKISP1_DEF_SINK_PAD_FMT; mbus_info = rkisp1_isp_mbus_info_get(sink_fmt->code); } -- GitLab From 206003b18bb264521607440752814ccff59f91f3 Mon Sep 17 00:00:00 2001 From: Dafna Hirschfeld Date: Thu, 18 Jun 2020 13:35:18 +0200 Subject: [PATCH 0257/1476] media: staging: rkisp1: rsz: set default format if the given format is not RKISP1_ISP_SD_SRC When setting the sink format of the 'rkisp1_resizer' the format should be supported by 'rkisp1_isp' on the video source pad. This patch checks this condition and sets the format to default if the condition is false. Fixes: 56e3b29f9f6b "media: staging: rkisp1: add streaming paths" Signed-off-by: Dafna Hirschfeld Reviewed-by: Tomasz Figa Acked-by: Helen Koike Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/rkisp1/rkisp1-common.h | 3 +++ drivers/staging/media/rkisp1/rkisp1-isp.c | 3 --- drivers/staging/media/rkisp1/rkisp1-resizer.c | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/staging/media/rkisp1/rkisp1-common.h b/drivers/staging/media/rkisp1/rkisp1-common.h index 0c4fe503adc90..12bd9d05050db 100644 --- a/drivers/staging/media/rkisp1/rkisp1-common.h +++ b/drivers/staging/media/rkisp1/rkisp1-common.h @@ -22,6 +22,9 @@ #include "rkisp1-regs.h" #include "uapi/rkisp1-config.h" +#define RKISP1_ISP_SD_SRC BIT(0) +#define RKISP1_ISP_SD_SINK BIT(1) + #define RKISP1_ISP_MAX_WIDTH 4032 #define RKISP1_ISP_MAX_HEIGHT 3024 #define RKISP1_ISP_MIN_WIDTH 32 diff --git a/drivers/staging/media/rkisp1/rkisp1-isp.c b/drivers/staging/media/rkisp1/rkisp1-isp.c index 1d0b35133f65a..58c90c67594db 100644 --- a/drivers/staging/media/rkisp1/rkisp1-isp.c +++ b/drivers/staging/media/rkisp1/rkisp1-isp.c @@ -23,9 +23,6 @@ #define RKISP1_ISP_DEV_NAME RKISP1_DRIVER_NAME "_isp" -#define RKISP1_ISP_SD_SRC BIT(0) -#define RKISP1_ISP_SD_SINK BIT(1) - /* * NOTE: MIPI controller and input MUX are also configured in this file. * This is because ISP Subdev describes not only ISP submodule (input size, diff --git a/drivers/staging/media/rkisp1/rkisp1-resizer.c b/drivers/staging/media/rkisp1/rkisp1-resizer.c index 27f4b948ad415..8bc907ffa09bb 100644 --- a/drivers/staging/media/rkisp1/rkisp1-resizer.c +++ b/drivers/staging/media/rkisp1/rkisp1-resizer.c @@ -542,7 +542,7 @@ static void rkisp1_rsz_set_sink_fmt(struct rkisp1_resizer *rsz, which); sink_fmt->code = format->code; mbus_info = rkisp1_isp_mbus_info_get(sink_fmt->code); - if (!mbus_info) { + if (!mbus_info || !(mbus_info->direction & RKISP1_ISP_SD_SRC)) { sink_fmt->code = RKISP1_DEF_FMT; mbus_info = rkisp1_isp_mbus_info_get(sink_fmt->code); } -- GitLab From 2f00f3a4916c546582060d114b4b96286ab3451e Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Wed, 17 Jun 2020 17:27:09 +0200 Subject: [PATCH 0258/1476] media: samsung: Rename Samsung and Exynos to lowercase Fix up inconsistent usage of upper and lowercase letters in "Samsung" and "Exynos" names. "SAMSUNG" and "EXYNOS" are not abbreviations but regular trademarked names. Therefore they should be written with lowercase letters starting with capital letter. The lowercase "Exynos" name is promoted by its manufacturer Samsung Electronics Co., Ltd., in advertisement materials and on website. Although advertisement materials usually use uppercase "SAMSUNG", the lowercase version is used in all legal aspects (e.g. on Wikipedia and in privacy/legal statements on https://www.samsung.com/semiconductor/privacy-global/). Signed-off-by: Krzysztof Kozlowski Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- Documentation/admin-guide/media/fimc.rst | 6 +++--- Documentation/driver-api/media/drivers/tuners.rst | 2 +- drivers/media/platform/exynos4-is/media-dev.c | 2 +- drivers/media/platform/s3c-camif/camif-core.c | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Documentation/admin-guide/media/fimc.rst b/Documentation/admin-guide/media/fimc.rst index 0b8ddc4a3008d..56b149d9a527c 100644 --- a/Documentation/admin-guide/media/fimc.rst +++ b/Documentation/admin-guide/media/fimc.rst @@ -2,7 +2,7 @@ .. include:: -The Samsung S5P/EXYNOS4 FIMC driver +The Samsung S5P/Exynos4 FIMC driver =================================== Copyright |copy| 2012 - 2013 Samsung Electronics Co., Ltd. @@ -19,7 +19,7 @@ drivers/media/platform/exynos4-is directory. Supported SoCs -------------- -S5PC100 (mem-to-mem only), S5PV210, EXYNOS4210 +S5PC100 (mem-to-mem only), S5PV210, Exynos4210 Supported features ------------------ @@ -45,7 +45,7 @@ Media device interface ~~~~~~~~~~~~~~~~~~~~~~ The driver supports Media Controller API as defined at :ref:`media_controller`. -The media device driver name is "SAMSUNG S5P FIMC". +The media device driver name is "Samsung S5P FIMC". The purpose of this interface is to allow changing assignment of FIMC instances to the SoC peripheral camera input at runtime and optionally to control internal diff --git a/Documentation/driver-api/media/drivers/tuners.rst b/Documentation/driver-api/media/drivers/tuners.rst index 7509be8889093..d7924141c544a 100644 --- a/Documentation/driver-api/media/drivers/tuners.rst +++ b/Documentation/driver-api/media/drivers/tuners.rst @@ -18,7 +18,7 @@ These differ mainly by the bandswitch byte. Tuner Manufacturers ------------------- -- SAMSUNG Tuner identification: (e.g. TCPM9091PD27) +- Samsung Tuner identification: (e.g. TCPM9091PD27) .. code-block:: none diff --git a/drivers/media/platform/exynos4-is/media-dev.c b/drivers/media/platform/exynos4-is/media-dev.c index 9aaf3b8060d50..96e336b19cc3b 100644 --- a/drivers/media/platform/exynos4-is/media-dev.c +++ b/drivers/media/platform/exynos4-is/media-dev.c @@ -1439,7 +1439,7 @@ static int fimc_md_probe(struct platform_device *pdev) INIT_LIST_HEAD(&fmd->pipelines); fmd->pdev = pdev; - strscpy(fmd->media_dev.model, "SAMSUNG S5P FIMC", + strscpy(fmd->media_dev.model, "Samsung S5P FIMC", sizeof(fmd->media_dev.model)); fmd->media_dev.ops = &fimc_md_ops; fmd->media_dev.dev = dev; diff --git a/drivers/media/platform/s3c-camif/camif-core.c b/drivers/media/platform/s3c-camif/camif-core.c index c6fbcd7036d6d..92f43c0cbc0c0 100644 --- a/drivers/media/platform/s3c-camif/camif-core.c +++ b/drivers/media/platform/s3c-camif/camif-core.c @@ -304,7 +304,7 @@ static int camif_media_dev_init(struct camif_dev *camif) int ret; memset(md, 0, sizeof(*md)); - snprintf(md->model, sizeof(md->model), "SAMSUNG S3C%s CAMIF", + snprintf(md->model, sizeof(md->model), "Samsung S3C%s CAMIF", ip_rev == S3C6410_CAMIF_IP_REV ? "6410" : "244X"); strscpy(md->bus_info, "platform", sizeof(md->bus_info)); md->hw_revision = ip_rev; -- GitLab From 906dceb48dfa1e7c99c32e6b25878d47023e916b Mon Sep 17 00:00:00 2001 From: Helen Koike Date: Wed, 17 Jun 2020 20:08:01 +0200 Subject: [PATCH 0259/1476] media: staging: rkisp1: rsz: fix resolution limitation on sink pad Resizer sink pad is limited by what the ISP can generate. The configurations describes what the resizer can produce. This was tested on a Scarlet device with ChromiumOs, where the selfpath receives 2592x1944 and produces 1600x1200 (which isn't possible without this fix). Fixes: 56e3b29f9f6b2 ("media: staging: rkisp1: add streaming paths") Signed-off-by: Helen Koike Reviewed-by: Tomasz Figa Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/rkisp1/rkisp1-resizer.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/staging/media/rkisp1/rkisp1-resizer.c b/drivers/staging/media/rkisp1/rkisp1-resizer.c index 8bc907ffa09bb..31aea7dfc9766 100644 --- a/drivers/staging/media/rkisp1/rkisp1-resizer.c +++ b/drivers/staging/media/rkisp1/rkisp1-resizer.c @@ -553,11 +553,11 @@ static void rkisp1_rsz_set_sink_fmt(struct rkisp1_resizer *rsz, src_fmt->code = sink_fmt->code; sink_fmt->width = clamp_t(u32, format->width, - rsz->config->min_rsz_width, - rsz->config->max_rsz_width); + RKISP1_ISP_MIN_WIDTH, + RKISP1_ISP_MAX_WIDTH); sink_fmt->height = clamp_t(u32, format->height, - rsz->config->min_rsz_height, - rsz->config->max_rsz_height); + RKISP1_ISP_MIN_HEIGHT, + RKISP1_ISP_MAX_HEIGHT); *format = *sink_fmt; -- GitLab From 961d3b27a2aff52dda0b97d35085a743a27c2f46 Mon Sep 17 00:00:00 2001 From: John Cox Date: Wed, 17 Jun 2020 15:21:52 +0200 Subject: [PATCH 0260/1476] media: videobuf2: Fix length check for single plane dmabuf queueing Check against length in v4l2_buffer rather than vb2_buffer when the buffer is a dmabuf. This makes the single plane test the same as the existing multiplanar test. Signed-off-by: John Cox Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/common/videobuf2/videobuf2-v4l2.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/media/common/videobuf2/videobuf2-v4l2.c b/drivers/media/common/videobuf2/videobuf2-v4l2.c index 98477a82c8109..57aa183bd1983 100644 --- a/drivers/media/common/videobuf2/videobuf2-v4l2.c +++ b/drivers/media/common/videobuf2/videobuf2-v4l2.c @@ -117,7 +117,8 @@ static int __verify_length(struct vb2_buffer *vb, const struct v4l2_buffer *b) return -EINVAL; } } else { - length = (b->memory == VB2_MEMORY_USERPTR) + length = (b->memory == VB2_MEMORY_USERPTR || + b->memory == VB2_MEMORY_DMABUF) ? b->length : vb->planes[0].length; if (b->bytesused > length) -- GitLab From a32e8adf2fdbd4fd1d046769140a8afd0489fe65 Mon Sep 17 00:00:00 2001 From: "Gustavo A. R. Silva" Date: Mon, 15 Jun 2020 23:40:15 +0200 Subject: [PATCH 0261/1476] media: test_drivers: vivid-core: Use array_size() helper The tpg_alloc() and vmalloc() functions have no 2-factor argument form, so multiplication factors need to be wrapped in array_size(). This issue was found with the help of Coccinelle and, audited and fixed manually. Addresses-KSPP-ID: https://github.com/KSPP/linux/issues/83 Signed-off-by: Gustavo A. R. Silva Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/test-drivers/vivid/vivid-core.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/media/test-drivers/vivid/vivid-core.c b/drivers/media/test-drivers/vivid/vivid-core.c index 5c986df4a8d4e..f7ee37e9508db 100644 --- a/drivers/media/test-drivers/vivid/vivid-core.c +++ b/drivers/media/test-drivers/vivid/vivid-core.c @@ -1126,7 +1126,7 @@ static int vivid_create_instance(struct platform_device *pdev, int inst) ret = -ENOMEM; /* initialize the test pattern generator */ tpg_init(&dev->tpg, 640, 360); - if (tpg_alloc(&dev->tpg, MAX_ZOOM * MAX_WIDTH)) + if (tpg_alloc(&dev->tpg, array_size(MAX_WIDTH, MAX_ZOOM))) goto free_dev; dev->scaled_line = vzalloc(array_size(MAX_WIDTH, MAX_ZOOM)); if (!dev->scaled_line) @@ -1136,7 +1136,7 @@ static int vivid_create_instance(struct platform_device *pdev, int inst) goto free_dev; /* load the edid */ - dev->edid = vmalloc(256 * 128); + dev->edid = vmalloc(array_size(256, 128)); if (!dev->edid) goto free_dev; -- GitLab From 874b8055bb3c42fe28dd8a1280a1d93a4f4156fa Mon Sep 17 00:00:00 2001 From: Stanimir Varbanov Date: Tue, 16 Jun 2020 16:37:35 +0200 Subject: [PATCH 0262/1476] media: docs: dev-decoder: Add one more reason for dynamic change Add bit-depth change as one more reason which could change in the middle of the stream. For the worst case the stream is 8bit at the beginning but later in the bit-stream it changes to 10bit. That change should be propagated to the client so that it can take the appropriate action. In that case it has to stop the streaming on the capture queue, re-negotiate the pixel format, allocate new buffers and start the streaming again. Signed-off-by: Stanimir Varbanov Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- Documentation/userspace-api/media/v4l/dev-decoder.rst | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Documentation/userspace-api/media/v4l/dev-decoder.rst b/Documentation/userspace-api/media/v4l/dev-decoder.rst index 04034dbc0b3ed..3d4138a4ba691 100644 --- a/Documentation/userspace-api/media/v4l/dev-decoder.rst +++ b/Documentation/userspace-api/media/v4l/dev-decoder.rst @@ -906,7 +906,9 @@ reflected by corresponding queries): * visible resolution (selection rectangles), -* the minimum number of buffers needed for decoding. +* the minimum number of buffers needed for decoding, + +* bit-depth of the bitstream has been changed. Whenever that happens, the decoder must proceed as follows: -- GitLab From ec050e022f8da39c2c9d6f4eb8c7f343fe2664ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niklas=20S=C3=B6derlund?= Date: Thu, 11 Jun 2020 21:32:32 +0200 Subject: [PATCH 0263/1476] media: rcar-csi2: Rename confirm_start() to phy_post_init() to match its usage MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since the driver was picked-up the starting of the PHY have changed quiet a bit. An artifact of these changes is the now poorly named callback confirm_start(). It used to confirm start of the PHY but now performs post PHY start initialization, rename it to phy_post_init() to reflect this. Signed-off-by: Niklas Söderlund Acked-by: Jacopo Mondi Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/rcar-vin/rcar-csi2.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/drivers/media/platform/rcar-vin/rcar-csi2.c b/drivers/media/platform/rcar-vin/rcar-csi2.c index 45fdf4e74e513..c6cc4f473a077 100644 --- a/drivers/media/platform/rcar-vin/rcar-csi2.c +++ b/drivers/media/platform/rcar-vin/rcar-csi2.c @@ -345,7 +345,7 @@ enum rcar_csi2_pads { struct rcar_csi2_info { int (*init_phtw)(struct rcar_csi2 *priv, unsigned int mbps); - int (*confirm_start)(struct rcar_csi2 *priv); + int (*phy_post_init)(struct rcar_csi2 *priv); const struct rcsi2_mbps_reg *hsfreqrange; unsigned int csi0clkfreqrange; unsigned int num_channels; @@ -576,9 +576,9 @@ static int rcsi2_start_receiver(struct rcar_csi2 *priv) if (ret) return ret; - /* Confirm start */ - if (priv->info->confirm_start) { - ret = priv->info->confirm_start(priv); + /* Run post PHY start initialization, if needed. */ + if (priv->info->phy_post_init) { + ret = priv->info->phy_post_init(priv); if (ret) return ret; } @@ -976,7 +976,7 @@ static int rcsi2_init_phtw_v3m_e3(struct rcar_csi2 *priv, unsigned int mbps) return rcsi2_phtw_write_mbps(priv, mbps, phtw_mbps_v3m_e3, 0x44); } -static int rcsi2_confirm_start_v3m_e3(struct rcar_csi2 *priv) +static int rcsi2_phy_post_init_v3m_e3(struct rcar_csi2 *priv) { static const struct phtw_value step1[] = { { .data = 0xee, .code = 0x34 }, @@ -1060,7 +1060,7 @@ static const struct rcar_csi2_info rcar_csi2_info_r8a77965 = { static const struct rcar_csi2_info rcar_csi2_info_r8a77970 = { .init_phtw = rcsi2_init_phtw_v3m_e3, - .confirm_start = rcsi2_confirm_start_v3m_e3, + .phy_post_init = rcsi2_phy_post_init_v3m_e3, .num_channels = 4, }; @@ -1073,7 +1073,7 @@ static const struct rcar_csi2_info rcar_csi2_info_r8a77980 = { static const struct rcar_csi2_info rcar_csi2_info_r8a77990 = { .init_phtw = rcsi2_init_phtw_v3m_e3, - .confirm_start = rcsi2_confirm_start_v3m_e3, + .phy_post_init = rcsi2_phy_post_init_v3m_e3, .num_channels = 2, }; -- GitLab From 477d4d38b93d457d6391a864dcfcc114a8b44060 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Wed, 10 Jun 2020 19:26:15 +0200 Subject: [PATCH 0264/1476] media: pxa_camera: remove an impossible condition It's not possible for "pcdev->mclk" to be zero because we check for that earlier and set it to 20000000 in that situation. If it were possible, that would be a problem because it could have lead to an Oops in the error handling when we call v4l2_clk_unregister(pcdev->mclk_clk); Signed-off-by: Dan Carpenter Acked-by: Robert Jarzmik Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/pxa_camera.c | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/drivers/media/platform/pxa_camera.c b/drivers/media/platform/pxa_camera.c index a7cf830235b61..6dce33f350416 100644 --- a/drivers/media/platform/pxa_camera.c +++ b/drivers/media/platform/pxa_camera.c @@ -2504,17 +2504,14 @@ static int pxa_camera_probe(struct platform_device *pdev) if (err) goto exit_notifier_cleanup; - if (pcdev->mclk) { - v4l2_clk_name_i2c(clk_name, sizeof(clk_name), - pcdev->asd.match.i2c.adapter_id, - pcdev->asd.match.i2c.address); - - pcdev->mclk_clk = v4l2_clk_register(&pxa_camera_mclk_ops, - clk_name, NULL); - if (IS_ERR(pcdev->mclk_clk)) { - err = PTR_ERR(pcdev->mclk_clk); - goto exit_notifier_cleanup; - } + v4l2_clk_name_i2c(clk_name, sizeof(clk_name), + pcdev->asd.match.i2c.adapter_id, + pcdev->asd.match.i2c.address); + + pcdev->mclk_clk = v4l2_clk_register(&pxa_camera_mclk_ops, clk_name, NULL); + if (IS_ERR(pcdev->mclk_clk)) { + err = PTR_ERR(pcdev->mclk_clk); + goto exit_notifier_cleanup; } err = v4l2_async_notifier_register(&pcdev->v4l2_dev, &pcdev->notifier); -- GitLab From d000e9b5e4a23dd700b3f58a4738c94bb5179ff0 Mon Sep 17 00:00:00 2001 From: Chuhong Yuan Date: Sat, 6 Jun 2020 16:39:18 +0200 Subject: [PATCH 0265/1476] media: tvp5150: Add missed media_entity_cleanup() This driver does not call media_entity_cleanup() in the error handler of tvp5150_registered() and tvp5150_remove(), while it has called media_entity_pads_init() at first. Add the missed calls to fix it. Fixes: 0556f1d580d4 ("media: tvp5150: add input source selection of_graph support") Signed-off-by: Chuhong Yuan Reviewed-by: Marco Felsch Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/tvp5150.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/media/i2c/tvp5150.c b/drivers/media/i2c/tvp5150.c index eb39cf5ea0895..9df575238952a 100644 --- a/drivers/media/i2c/tvp5150.c +++ b/drivers/media/i2c/tvp5150.c @@ -1664,8 +1664,10 @@ static int tvp5150_registered(struct v4l2_subdev *sd) return 0; err: - for (i = 0; i < decoder->connectors_num; i++) + for (i = 0; i < decoder->connectors_num; i++) { media_device_unregister_entity(&decoder->connectors[i].ent); + media_entity_cleanup(&decoder->connectors[i].ent); + } return ret; #endif @@ -2248,8 +2250,10 @@ static int tvp5150_remove(struct i2c_client *c) for (i = 0; i < decoder->connectors_num; i++) v4l2_fwnode_connector_free(&decoder->connectors[i].base); - for (i = 0; i < decoder->connectors_num; i++) + for (i = 0; i < decoder->connectors_num; i++) { media_device_unregister_entity(&decoder->connectors[i].ent); + media_entity_cleanup(&decoder->connectors[i].ent); + } v4l2_async_unregister_subdev(sd); v4l2_ctrl_handler_free(&decoder->hdl); pm_runtime_disable(&c->dev); -- GitLab From 875c0d75954f022b2118f8dbfe57b96021de383b Mon Sep 17 00:00:00 2001 From: Dafna Hirschfeld Date: Fri, 29 May 2020 16:03:51 +0200 Subject: [PATCH 0266/1476] media: staging: rkisp1: set more precise size errors in debugfs When a size error is signaled, it is possible to read a register to see where the error comes from. So, in debugfs the general error 'pic_size_err' can be replaced with 3 more precise errors. Signed-off-by: Dafna Hirschfeld Acked-by: Helen Koike Reviewed-by: Laurent Pinchart Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/rkisp1/rkisp1-common.h | 4 +++- drivers/staging/media/rkisp1/rkisp1-dev.c | 9 +++++++-- drivers/staging/media/rkisp1/rkisp1-isp.c | 7 ++++++- 3 files changed, 16 insertions(+), 4 deletions(-) diff --git a/drivers/staging/media/rkisp1/rkisp1-common.h b/drivers/staging/media/rkisp1/rkisp1-common.h index 12bd9d05050db..a9ede3f71ff33 100644 --- a/drivers/staging/media/rkisp1/rkisp1-common.h +++ b/drivers/staging/media/rkisp1/rkisp1-common.h @@ -229,7 +229,9 @@ struct rkisp1_resizer { struct rkisp1_debug { struct dentry *debugfs_dir; unsigned long data_loss; - unsigned long pic_size_error; + unsigned long outform_size_error; + unsigned long img_stabilization_size_error; + unsigned long inform_size_error; unsigned long mipi_error; unsigned long stats_error; unsigned long stop_timeout[2]; diff --git a/drivers/staging/media/rkisp1/rkisp1-dev.c b/drivers/staging/media/rkisp1/rkisp1-dev.c index f38801fea10d9..a0eb8f08708b9 100644 --- a/drivers/staging/media/rkisp1/rkisp1-dev.c +++ b/drivers/staging/media/rkisp1/rkisp1-dev.c @@ -438,8 +438,13 @@ static void rkisp1_debug_init(struct rkisp1_device *rkisp1) } debugfs_create_ulong("data_loss", 0444, debug->debugfs_dir, &debug->data_loss); - debugfs_create_ulong("pic_size_error", 0444, debug->debugfs_dir, - &debug->pic_size_error); + debugfs_create_ulong("outform_size_err", 0444, debug->debugfs_dir, + &debug->outform_size_error); + debugfs_create_ulong("img_stabilization_size_error", 0444, + debug->debugfs_dir, + &debug->img_stabilization_size_error); + debugfs_create_ulong("inform_size_error", 0444, debug->debugfs_dir, + &debug->inform_size_error); debugfs_create_ulong("mipi_error", 0444, debug->debugfs_dir, &debug->mipi_error); debugfs_create_ulong("stats_error", 0444, debug->debugfs_dir, diff --git a/drivers/staging/media/rkisp1/rkisp1-isp.c b/drivers/staging/media/rkisp1/rkisp1-isp.c index 58c90c67594db..76229ecbc41aa 100644 --- a/drivers/staging/media/rkisp1/rkisp1-isp.c +++ b/drivers/staging/media/rkisp1/rkisp1-isp.c @@ -1122,8 +1122,13 @@ void rkisp1_isp_isr(struct rkisp1_device *rkisp1) if (status & RKISP1_CIF_ISP_PIC_SIZE_ERROR) { /* Clear pic_size_error */ isp_err = rkisp1_read(rkisp1, RKISP1_CIF_ISP_ERR); + if (isp_err & RKISP1_CIF_ISP_ERR_INFORM_SIZE) + rkisp1->debug.inform_size_error++; + if (isp_err & RKISP1_CIF_ISP_ERR_IS_SIZE) + rkisp1->debug.img_stabilization_size_error++; + if (isp_err & RKISP1_CIF_ISP_ERR_OUTFORM_SIZE) + rkisp1->debug.outform_size_error++; rkisp1_write(rkisp1, isp_err, RKISP1_CIF_ISP_ERR_CLR); - rkisp1->debug.pic_size_error++; } else if (status & RKISP1_CIF_ISP_DATA_LOSS) { /* keep track of data_loss in debugfs */ rkisp1->debug.data_loss++; -- GitLab From 18ffec750578f7447c288647d7282c7d12b1d969 Mon Sep 17 00:00:00 2001 From: Chuhong Yuan Date: Thu, 28 May 2020 08:41:47 +0200 Subject: [PATCH 0267/1476] media: exynos4-is: Add missed check for pinctrl_lookup_state() fimc_md_get_pinctrl() misses a check for pinctrl_lookup_state(). Add the missed check to fix it. Fixes: 4163851f7b99 ("[media] s5p-fimc: Use pinctrl API for camera ports configuration]") Signed-off-by: Chuhong Yuan Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/exynos4-is/media-dev.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/media/platform/exynos4-is/media-dev.c b/drivers/media/platform/exynos4-is/media-dev.c index 96e336b19cc3b..16dd660137a8d 100644 --- a/drivers/media/platform/exynos4-is/media-dev.c +++ b/drivers/media/platform/exynos4-is/media-dev.c @@ -1270,6 +1270,9 @@ static int fimc_md_get_pinctrl(struct fimc_md *fmd) pctl->state_idle = pinctrl_lookup_state(pctl->pinctrl, PINCTRL_STATE_IDLE); + if (IS_ERR(pctl->state_idle)) + return PTR_ERR(pctl->state_idle); + return 0; } -- GitLab From ded874ece29d3fe2abd3775810a06056067eb68c Mon Sep 17 00:00:00 2001 From: Paul Kocialkowski Date: Thu, 30 Apr 2020 18:42:44 +0200 Subject: [PATCH 0268/1476] media: rockchip: rga: Introduce color fmt macros and refactor CSC mode logic This introduces two macros: RGA_COLOR_FMT_IS_YUV and RGA_COLOR_FMT_IS_RGB which allow quick checking of the colorspace familily of a RGA color format. These macros are then used to refactor the logic for CSC mode selection. The two nested tests for input colorspace are simplified into a single one, with a logical and, making the whole more readable. Signed-off-by: Paul Kocialkowski Reviewed-by: Ezequiel Garcia Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/rockchip/rga/rga-hw.c | 23 +++++++++----------- drivers/media/platform/rockchip/rga/rga-hw.h | 5 +++++ 2 files changed, 15 insertions(+), 13 deletions(-) diff --git a/drivers/media/platform/rockchip/rga/rga-hw.c b/drivers/media/platform/rockchip/rga/rga-hw.c index 4be6dcf292fff..5607ee8d19176 100644 --- a/drivers/media/platform/rockchip/rga/rga-hw.c +++ b/drivers/media/platform/rockchip/rga/rga-hw.c @@ -200,22 +200,19 @@ static void rga_cmd_set_trans_info(struct rga_ctx *ctx) dst_info.data.format = ctx->out.fmt->hw_format; dst_info.data.swap = ctx->out.fmt->color_swap; - if (ctx->in.fmt->hw_format >= RGA_COLOR_FMT_YUV422SP) { - if (ctx->out.fmt->hw_format < RGA_COLOR_FMT_YUV422SP) { - switch (ctx->in.colorspace) { - case V4L2_COLORSPACE_REC709: - src_info.data.csc_mode = - RGA_SRC_CSC_MODE_BT709_R0; - break; - default: - src_info.data.csc_mode = - RGA_SRC_CSC_MODE_BT601_R0; - break; - } + if (RGA_COLOR_FMT_IS_YUV(ctx->in.fmt->hw_format) && + RGA_COLOR_FMT_IS_RGB(ctx->out.fmt->hw_format)) { + switch (ctx->in.colorspace) { + case V4L2_COLORSPACE_REC709: + src_info.data.csc_mode = RGA_SRC_CSC_MODE_BT709_R0; + break; + default: + src_info.data.csc_mode = RGA_SRC_CSC_MODE_BT601_R0; + break; } } - if (ctx->out.fmt->hw_format >= RGA_COLOR_FMT_YUV422SP) { + if (RGA_COLOR_FMT_IS_YUV(ctx->out.fmt->hw_format)) { switch (ctx->out.colorspace) { case V4L2_COLORSPACE_REC709: dst_info.data.csc_mode = RGA_SRC_CSC_MODE_BT709_R0; diff --git a/drivers/media/platform/rockchip/rga/rga-hw.h b/drivers/media/platform/rockchip/rga/rga-hw.h index 96cb0314dfa70..e8917e5630a48 100644 --- a/drivers/media/platform/rockchip/rga/rga-hw.h +++ b/drivers/media/platform/rockchip/rga/rga-hw.h @@ -95,6 +95,11 @@ #define RGA_COLOR_FMT_CP_8BPP 15 #define RGA_COLOR_FMT_MASK 15 +#define RGA_COLOR_FMT_IS_YUV(fmt) \ + (((fmt) >= RGA_COLOR_FMT_YUV422SP) && ((fmt) < RGA_COLOR_FMT_CP_1BPP)) +#define RGA_COLOR_FMT_IS_RGB(fmt) \ + ((fmt) < RGA_COLOR_FMT_YUV422SP) + #define RGA_COLOR_NONE_SWAP 0 #define RGA_COLOR_RB_SWAP 1 #define RGA_COLOR_ALPHA_SWAP 2 -- GitLab From 0f879bab72f47e8ba2421a984e7acfa763d3e84e Mon Sep 17 00:00:00 2001 From: Paul Kocialkowski Date: Thu, 30 Apr 2020 18:42:45 +0200 Subject: [PATCH 0269/1476] media: rockchip: rga: Only set output CSC mode for RGB input Setting the output CSC mode is required for a YUV output, but must not be set when the input is also YUV. Doing this (as tested with a YUV420P to YUV420P conversion) results in wrong colors. Adapt the logic to only set the output CSC mode when the output is YUV and the input is RGB. Also add a comment to clarify the rationale. Fixes: f7e7b48e6d79 ("[media] rockchip/rga: v4l2 m2m support") Signed-off-by: Paul Kocialkowski Reviewed-by: Ezequiel Garcia Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/rockchip/rga/rga-hw.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/drivers/media/platform/rockchip/rga/rga-hw.c b/drivers/media/platform/rockchip/rga/rga-hw.c index 5607ee8d19176..aaa96f256356b 100644 --- a/drivers/media/platform/rockchip/rga/rga-hw.c +++ b/drivers/media/platform/rockchip/rga/rga-hw.c @@ -200,6 +200,11 @@ static void rga_cmd_set_trans_info(struct rga_ctx *ctx) dst_info.data.format = ctx->out.fmt->hw_format; dst_info.data.swap = ctx->out.fmt->color_swap; + /* + * CSC mode must only be set when the colorspace families differ between + * input and output. It must remain unset (zeroed) if both are the same. + */ + if (RGA_COLOR_FMT_IS_YUV(ctx->in.fmt->hw_format) && RGA_COLOR_FMT_IS_RGB(ctx->out.fmt->hw_format)) { switch (ctx->in.colorspace) { @@ -212,7 +217,8 @@ static void rga_cmd_set_trans_info(struct rga_ctx *ctx) } } - if (RGA_COLOR_FMT_IS_YUV(ctx->out.fmt->hw_format)) { + if (RGA_COLOR_FMT_IS_RGB(ctx->in.fmt->hw_format) && + RGA_COLOR_FMT_IS_YUV(ctx->out.fmt->hw_format)) { switch (ctx->out.colorspace) { case V4L2_COLORSPACE_REC709: dst_info.data.csc_mode = RGA_SRC_CSC_MODE_BT709_R0; -- GitLab From 6f01dfb760c027d5dd6199d91ee9599f2676b5c6 Mon Sep 17 00:00:00 2001 From: Dariusz Marcinkiewicz Date: Mon, 22 Jun 2020 13:46:36 +0200 Subject: [PATCH 0270/1476] media: cros-ec-cec: do not bail on device_init_wakeup failure Do not fail probing when device_init_wakeup fails. device_init_wakeup fails when the device is already enabled as wakeup device. Hence, the driver fails to probe the device if: - The device has already been enabled for wakeup (by e.g. sysfs) - The driver has been unloaded and is being loaded again. This goal of the patch is to fix the above cases. Overwhelming majority of the drivers do not check device_init_wakeup return code. Fixes: cd70de2d356ee ("media: platform: Add ChromeOS EC CEC driver") Signed-off-by: Dariusz Marcinkiewicz Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/cec/platform/cros-ec/cros-ec-cec.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/drivers/media/cec/platform/cros-ec/cros-ec-cec.c b/drivers/media/cec/platform/cros-ec/cros-ec-cec.c index 0e7e2772f08f9..2d95e16cd2489 100644 --- a/drivers/media/cec/platform/cros-ec/cros-ec-cec.c +++ b/drivers/media/cec/platform/cros-ec/cros-ec-cec.c @@ -277,11 +277,7 @@ static int cros_ec_cec_probe(struct platform_device *pdev) platform_set_drvdata(pdev, cros_ec_cec); cros_ec_cec->cros_ec = cros_ec; - ret = device_init_wakeup(&pdev->dev, 1); - if (ret) { - dev_err(&pdev->dev, "failed to initialize wakeup\n"); - return ret; - } + device_init_wakeup(&pdev->dev, 1); cros_ec_cec->adap = cec_allocate_adapter(&cros_ec_cec_ops, cros_ec_cec, DRV_NAME, -- GitLab From 24344226f66beda742f233e08dfb3fb33932275b Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Fri, 22 May 2020 17:48:18 -0600 Subject: [PATCH 0271/1476] PCI: cadence: Use struct pci_host_bridge.windows list directly There's no need to create a temporary resource list and then splice it to struct pci_host_bridge.windows list. Just use pci_host_bridge.windows directly. The necessary clean-up is already handled by the PCI core. Link: https://lore.kernel.org/r/20200522234832.954484-2-robh@kernel.org Signed-off-by: Rob Herring Signed-off-by: Lorenzo Pieralisi Cc: Tom Joseph --- .../controller/cadence/pcie-cadence-host.c | 26 +++++-------------- 1 file changed, 7 insertions(+), 19 deletions(-) diff --git a/drivers/pci/controller/cadence/pcie-cadence-host.c b/drivers/pci/controller/cadence/pcie-cadence-host.c index 8c2543f28ba05..9f77e47983c3e 100644 --- a/drivers/pci/controller/cadence/pcie-cadence-host.c +++ b/drivers/pci/controller/cadence/pcie-cadence-host.c @@ -169,14 +169,15 @@ static int cdns_pcie_host_init_address_translation(struct cdns_pcie_rc *rc) } static int cdns_pcie_host_init(struct device *dev, - struct list_head *resources, struct cdns_pcie_rc *rc) { + struct pci_host_bridge *bridge = pci_host_bridge_from_priv(rc); struct resource *bus_range = NULL; int err; /* Parse our PCI ranges and request their resources */ - err = pci_parse_request_of_pci_ranges(dev, resources, NULL, &bus_range); + err = pci_parse_request_of_pci_ranges(dev, &bridge->windows, NULL, + &bus_range); if (err) return err; @@ -185,17 +186,9 @@ static int cdns_pcie_host_init(struct device *dev, err = cdns_pcie_host_init_root_port(rc); if (err) - goto err_out; - - err = cdns_pcie_host_init_address_translation(rc); - if (err) - goto err_out; - - return 0; + return err; - err_out: - pci_free_resource_list(resources); - return err; + return cdns_pcie_host_init_address_translation(rc); } int cdns_pcie_host_setup(struct cdns_pcie_rc *rc) @@ -204,7 +197,6 @@ int cdns_pcie_host_setup(struct cdns_pcie_rc *rc) struct platform_device *pdev = to_platform_device(dev); struct device_node *np = dev->of_node; struct pci_host_bridge *bridge; - struct list_head resources; struct cdns_pcie *pcie; struct resource *res; int ret; @@ -248,11 +240,10 @@ int cdns_pcie_host_setup(struct cdns_pcie_rc *rc) pcie->mem_res = res; - ret = cdns_pcie_host_init(dev, &resources, rc); + ret = cdns_pcie_host_init(dev, rc); if (ret) goto err_init; - list_splice_init(&resources, &bridge->windows); bridge->dev.parent = dev; bridge->busnr = pcie->bus; bridge->ops = &cdns_pcie_host_ops; @@ -261,13 +252,10 @@ int cdns_pcie_host_setup(struct cdns_pcie_rc *rc) ret = pci_host_probe(bridge); if (ret < 0) - goto err_host_probe; + goto err_init; return 0; - err_host_probe: - pci_free_resource_list(&resources); - err_init: pm_runtime_put_sync(dev); -- GitLab From c322fa0b3fa948010a278794e60c45ec860e4a1e Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Fri, 22 May 2020 17:48:19 -0600 Subject: [PATCH 0272/1476] PCI: mvebu: Use struct pci_host_bridge.windows list directly There's no need to create a temporary resource list and then splice it to struct pci_host_bridge.windows list. Just use pci_host_bridge.windows directly. The necessary clean-up is already handled by the PCI core. Link: https://lore.kernel.org/r/20200522234832.954484-3-robh@kernel.org Signed-off-by: Rob Herring Signed-off-by: Lorenzo Pieralisi Cc: Thomas Petazzoni Cc: Jason Cooper --- drivers/pci/controller/pci-mvebu.c | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/drivers/pci/controller/pci-mvebu.c b/drivers/pci/controller/pci-mvebu.c index 153a64676bc90..801044523a3d1 100644 --- a/drivers/pci/controller/pci-mvebu.c +++ b/drivers/pci/controller/pci-mvebu.c @@ -71,7 +71,6 @@ struct mvebu_pcie { struct platform_device *pdev; struct mvebu_pcie_port *ports; struct msi_controller *msi; - struct list_head resources; struct resource io; struct resource realio; struct resource mem; @@ -961,17 +960,16 @@ static int mvebu_pcie_parse_request_resources(struct mvebu_pcie *pcie) { struct device *dev = &pcie->pdev->dev; struct device_node *np = dev->of_node; + struct pci_host_bridge *bridge = pci_host_bridge_from_priv(pcie); int ret; - INIT_LIST_HEAD(&pcie->resources); - /* Get the bus range */ ret = of_pci_parse_bus_range(np, &pcie->busn); if (ret) { dev_err(dev, "failed to parse bus-range property: %d\n", ret); return ret; } - pci_add_resource(&pcie->resources, &pcie->busn); + pci_add_resource(&bridge->windows, &pcie->busn); /* Get the PCIe memory aperture */ mvebu_mbus_get_pcie_mem_aperture(&pcie->mem); @@ -981,7 +979,7 @@ static int mvebu_pcie_parse_request_resources(struct mvebu_pcie *pcie) } pcie->mem.name = "PCI MEM"; - pci_add_resource(&pcie->resources, &pcie->mem); + pci_add_resource(&bridge->windows, &pcie->mem); /* Get the PCIe IO aperture */ mvebu_mbus_get_pcie_io_aperture(&pcie->io); @@ -994,10 +992,10 @@ static int mvebu_pcie_parse_request_resources(struct mvebu_pcie *pcie) resource_size(&pcie->io) - 1); pcie->realio.name = "PCI I/O"; - pci_add_resource(&pcie->resources, &pcie->realio); + pci_add_resource(&bridge->windows, &pcie->realio); } - return devm_request_pci_bus_resources(dev, &pcie->resources); + return devm_request_pci_bus_resources(dev, &bridge->windows); } /* @@ -1118,7 +1116,6 @@ static int mvebu_pcie_probe(struct platform_device *pdev) pcie->nports = i; - list_splice_init(&pcie->resources, &bridge->windows); bridge->dev.parent = dev; bridge->sysdata = pcie; bridge->busnr = 0; -- GitLab From 51f939b11cb1c47ed2a8d56b23f25483b7363f8e Mon Sep 17 00:00:00 2001 From: Jon Derrick Date: Wed, 27 May 2020 23:02:40 -0400 Subject: [PATCH 0273/1476] PCI: vmd: Use Shadow MEMBAR registers for QEMU/KVM guests VMD device 28C0 natively assists guest passthrough of the VMD endpoint through the use of shadow registers that provide Host Physical Addresses to correctly assign bridge windows. These shadow registers are only available if VMD config space register 0x70, bit 1 is set. In order to support this mode in existing VMD devices which don't natively support the shadow register, it was decided that the hypervisor could offer the shadow registers in a vendor-specific PCI capability. QEMU has been modified to create this vendor-specific capability and supply the shadow membar registers for VMDs which don't natively support this feature. This patch adds this mode and updates the supported device list to allow this feature to be used on these VMDs. Link: https://lore.kernel.org/r/20200528030240.16024-4-jonathan.derrick@intel.com Signed-off-by: Jon Derrick Signed-off-by: Lorenzo Pieralisi --- drivers/pci/controller/vmd.c | 44 +++++++++++++++++++++++++++++++----- 1 file changed, 38 insertions(+), 6 deletions(-) diff --git a/drivers/pci/controller/vmd.c b/drivers/pci/controller/vmd.c index e386d4eac4070..76d8acbee7d5e 100644 --- a/drivers/pci/controller/vmd.c +++ b/drivers/pci/controller/vmd.c @@ -40,13 +40,19 @@ enum vmd_features { * membars, in order to allow proper address translation during * resource assignment to enable guest virtualization */ - VMD_FEAT_HAS_MEMBAR_SHADOW = (1 << 0), + VMD_FEAT_HAS_MEMBAR_SHADOW = (1 << 0), /* * Device may provide root port configuration information which limits * bus numbering */ - VMD_FEAT_HAS_BUS_RESTRICTIONS = (1 << 1), + VMD_FEAT_HAS_BUS_RESTRICTIONS = (1 << 1), + + /* + * Device contains physical location shadow registers in + * vendor-specific capability space + */ + VMD_FEAT_HAS_MEMBAR_SHADOW_VSCAP = (1 << 2), }; /* @@ -454,6 +460,28 @@ static int vmd_enable_domain(struct vmd_dev *vmd, unsigned long features) } } + if (features & VMD_FEAT_HAS_MEMBAR_SHADOW_VSCAP) { + int pos = pci_find_capability(vmd->dev, PCI_CAP_ID_VNDR); + u32 reg, regu; + + pci_read_config_dword(vmd->dev, pos + 4, ®); + + /* "SHDW" */ + if (pos && reg == 0x53484457) { + pci_read_config_dword(vmd->dev, pos + 8, ®); + pci_read_config_dword(vmd->dev, pos + 12, ®u); + offset[0] = vmd->dev->resource[VMD_MEMBAR1].start - + (((u64) regu << 32 | reg) & + PCI_BASE_ADDRESS_MEM_MASK); + + pci_read_config_dword(vmd->dev, pos + 16, ®); + pci_read_config_dword(vmd->dev, pos + 20, ®u); + offset[1] = vmd->dev->resource[VMD_MEMBAR2].start - + (((u64) regu << 32 | reg) & + PCI_BASE_ADDRESS_MEM_MASK); + } + } + /* * Certain VMD devices may have a root port configuration option which * limits the bus range to between 0-127, 128-255, or 224-255 @@ -716,16 +744,20 @@ static int vmd_resume(struct device *dev) static SIMPLE_DEV_PM_OPS(vmd_dev_pm_ops, vmd_suspend, vmd_resume); static const struct pci_device_id vmd_ids[] = { - {PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_VMD_201D),}, + {PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_VMD_201D), + .driver_data = VMD_FEAT_HAS_MEMBAR_SHADOW_VSCAP,}, {PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_VMD_28C0), .driver_data = VMD_FEAT_HAS_MEMBAR_SHADOW | VMD_FEAT_HAS_BUS_RESTRICTIONS,}, {PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x467f), - .driver_data = VMD_FEAT_HAS_BUS_RESTRICTIONS,}, + .driver_data = VMD_FEAT_HAS_MEMBAR_SHADOW_VSCAP | + VMD_FEAT_HAS_BUS_RESTRICTIONS,}, {PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x4c3d), - .driver_data = VMD_FEAT_HAS_BUS_RESTRICTIONS,}, + .driver_data = VMD_FEAT_HAS_MEMBAR_SHADOW_VSCAP | + VMD_FEAT_HAS_BUS_RESTRICTIONS,}, {PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_VMD_9A0B), - .driver_data = VMD_FEAT_HAS_BUS_RESTRICTIONS,}, + .driver_data = VMD_FEAT_HAS_MEMBAR_SHADOW_VSCAP | + VMD_FEAT_HAS_BUS_RESTRICTIONS,}, {0,} }; MODULE_DEVICE_TABLE(pci, vmd_ids); -- GitLab From 76f09371bc05d6eb8d5a01823c9eaab768d6e934 Mon Sep 17 00:00:00 2001 From: Bin Meng Date: Fri, 1 May 2020 21:44:54 -0700 Subject: [PATCH 0274/1476] powerpc: Drop CONFIG_MTD_M25P80 in 85xx-hw.config Drop CONFIG_MTD_M25P80 that was removed in commit b35b9a10362d ("mtd: spi-nor: Move m25p80 code in spi-nor.c") Signed-off-by: Bin Meng Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/1588394694-517-1-git-send-email-bmeng.cn@gmail.com --- arch/powerpc/configs/85xx-hw.config | 1 - 1 file changed, 1 deletion(-) diff --git a/arch/powerpc/configs/85xx-hw.config b/arch/powerpc/configs/85xx-hw.config index b507df6ac69fa..524db76f47b73 100644 --- a/arch/powerpc/configs/85xx-hw.config +++ b/arch/powerpc/configs/85xx-hw.config @@ -67,7 +67,6 @@ CONFIG_MTD_CFI_AMDSTD=y CONFIG_MTD_CFI_INTELEXT=y CONFIG_MTD_CFI=y CONFIG_MTD_CMDLINE_PARTS=y -CONFIG_MTD_M25P80=y CONFIG_MTD_NAND_FSL_ELBC=y CONFIG_MTD_NAND_FSL_IFC=y CONFIG_MTD_RAW_NAND=y -- GitLab From 06734e3c95a34e4d71342f0583f1bb88c61ed9b7 Mon Sep 17 00:00:00 2001 From: Keyur Patel Date: Mon, 29 Jun 2020 14:44:35 -0700 Subject: [PATCH 0275/1476] xfs: Couple of typo fixes in comments ./xfs/libxfs/xfs_inode_buf.c:56: unnecssary ==> unnecessary ./xfs/libxfs/xfs_inode_buf.c:59: behavour ==> behaviour ./xfs/libxfs/xfs_inode_buf.c:206: unitialized ==> uninitialized Signed-off-by: Keyur Patel Reviewed-by: Darrick J. Wong Signed-off-by: Darrick J. Wong --- fs/xfs/libxfs/xfs_inode_buf.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/fs/xfs/libxfs/xfs_inode_buf.c b/fs/xfs/libxfs/xfs_inode_buf.c index 6f84ea85fdd83..5c93e8e6de745 100644 --- a/fs/xfs/libxfs/xfs_inode_buf.c +++ b/fs/xfs/libxfs/xfs_inode_buf.c @@ -53,10 +53,10 @@ xfs_inobp_check( * If the readahead buffer is invalid, we need to mark it with an error and * clear the DONE status of the buffer so that a followup read will re-read it * from disk. We don't report the error otherwise to avoid warnings during log - * recovery and we don't get unnecssary panics on debug kernels. We use EIO here + * recovery and we don't get unnecessary panics on debug kernels. We use EIO here * because all we want to do is say readahead failed; there is no-one to report * the error to, so this will distinguish it from a non-ra verifier failure. - * Changes to this readahead error behavour also need to be reflected in + * Changes to this readahead error behaviour also need to be reflected in * xfs_dquot_buf_readahead_verify(). */ static void @@ -203,7 +203,7 @@ xfs_inode_from_disk( /* * First get the permanent information that is needed to allocate an * inode. If the inode is unused, mode is zero and we shouldn't mess - * with the unitialized part of it. + * with the uninitialized part of it. */ to->di_flushiter = be16_to_cpu(from->di_flushiter); inode->i_generation = be32_to_cpu(from->di_gen); -- GitLab From f74681ba2006434be195402e0b15fc5763cddd7e Mon Sep 17 00:00:00 2001 From: Brian Foster Date: Mon, 29 Jun 2020 14:44:36 -0700 Subject: [PATCH 0276/1476] xfs: preserve rmapbt swapext block reservation from freed blocks The rmapbt extent swap algorithm remaps individual extents between the source inode and the target to trigger reverse mapping metadata updates. If either inode straddles a format or other bmap allocation boundary, the individual unmap and map cycles can trigger repeated bmap block allocations and frees as the extent count bounces back and forth across the boundary. While net block usage is bound across the swap operation, this behavior can prematurely exhaust the transaction block reservation because it continuously drains as the transaction rolls. Each allocation accounts against the reservation and each free returns to global free space on transaction roll. The previous workaround to this problem attempted to detect this boundary condition and provide surplus block reservation to acommodate it. This is insufficient because more remaps can occur than implied by the extent counts; if start offset boundaries are not aligned between the two inodes, for example. To address this problem more generically and dynamically, add a transaction accounting mode that returns freed blocks to the transaction reservation instead of the superblock counters on transaction roll and use it when the rmapbt based algorithm is active. This allows the chain of remap transactions to preserve the block reservation based own its own frees and prevent premature exhaustion regardless of the remap pattern. Note that this is only safe for superblocks with lazy sb accounting, but the latter is required for v5 supers and the rmap feature depends on v5. Fixes: b3fed434822d0 ("xfs: account format bouncing into rmapbt swapext tx reservation") Root-caused-by: Darrick J. Wong Signed-off-by: Brian Foster Reviewed-by: Darrick J. Wong Signed-off-by: Darrick J. Wong --- fs/xfs/libxfs/xfs_shared.h | 1 + fs/xfs/xfs_bmap_util.c | 18 +++++++++--------- fs/xfs/xfs_trans.c | 19 ++++++++++++++++++- 3 files changed, 28 insertions(+), 10 deletions(-) diff --git a/fs/xfs/libxfs/xfs_shared.h b/fs/xfs/libxfs/xfs_shared.h index c45acbd3add94..708feb8eac766 100644 --- a/fs/xfs/libxfs/xfs_shared.h +++ b/fs/xfs/libxfs/xfs_shared.h @@ -65,6 +65,7 @@ void xfs_log_get_max_trans_res(struct xfs_mount *mp, #define XFS_TRANS_DQ_DIRTY 0x10 /* at least one dquot in trx dirty */ #define XFS_TRANS_RESERVE 0x20 /* OK to use reserved data blocks */ #define XFS_TRANS_NO_WRITECOUNT 0x40 /* do not elevate SB writecount */ +#define XFS_TRANS_RES_FDBLKS 0x80 /* reserve newly freed blocks */ /* * LOWMODE is used by the allocator to activate the lowspace algorithm - when * free space is running low the extent allocator may choose to allocate an diff --git a/fs/xfs/xfs_bmap_util.c b/fs/xfs/xfs_bmap_util.c index f37f5cc4b19ff..afdc7f8e0e701 100644 --- a/fs/xfs/xfs_bmap_util.c +++ b/fs/xfs/xfs_bmap_util.c @@ -1567,6 +1567,7 @@ xfs_swap_extents( int lock_flags; uint64_t f; int resblks = 0; + unsigned int flags = 0; /* * Lock the inodes against other IO, page faults and truncate to @@ -1630,17 +1631,16 @@ xfs_swap_extents( resblks += XFS_SWAP_RMAP_SPACE_RES(mp, tipnext, w); /* - * Handle the corner case where either inode might straddle the - * btree format boundary. If so, the inode could bounce between - * btree <-> extent format on unmap -> remap cycles, freeing and - * allocating a bmapbt block each time. + * If either inode straddles a bmapbt block allocation boundary, + * the rmapbt algorithm triggers repeated allocs and frees as + * extents are remapped. This can exhaust the block reservation + * prematurely and cause shutdown. Return freed blocks to the + * transaction reservation to counter this behavior. */ - if (ipnext == (XFS_IFORK_MAXEXT(ip, w) + 1)) - resblks += XFS_IFORK_MAXEXT(ip, w); - if (tipnext == (XFS_IFORK_MAXEXT(tip, w) + 1)) - resblks += XFS_IFORK_MAXEXT(tip, w); + flags |= XFS_TRANS_RES_FDBLKS; } - error = xfs_trans_alloc(mp, &M_RES(mp)->tr_write, resblks, 0, 0, &tp); + error = xfs_trans_alloc(mp, &M_RES(mp)->tr_write, resblks, 0, flags, + &tp); if (error) goto out_unlock; diff --git a/fs/xfs/xfs_trans.c b/fs/xfs/xfs_trans.c index 3c94e5ff43160..0ad72a83edac4 100644 --- a/fs/xfs/xfs_trans.c +++ b/fs/xfs/xfs_trans.c @@ -107,7 +107,8 @@ xfs_trans_dup( ntp->t_flags = XFS_TRANS_PERM_LOG_RES | (tp->t_flags & XFS_TRANS_RESERVE) | - (tp->t_flags & XFS_TRANS_NO_WRITECOUNT); + (tp->t_flags & XFS_TRANS_NO_WRITECOUNT) | + (tp->t_flags & XFS_TRANS_RES_FDBLKS); /* We gave our writer reference to the new transaction */ tp->t_flags |= XFS_TRANS_NO_WRITECOUNT; ntp->t_ticket = xfs_log_ticket_get(tp->t_ticket); @@ -272,6 +273,8 @@ xfs_trans_alloc( */ WARN_ON(resp->tr_logres > 0 && mp->m_super->s_writers.frozen == SB_FREEZE_COMPLETE); + ASSERT(!(flags & XFS_TRANS_RES_FDBLKS) || + xfs_sb_version_haslazysbcount(&mp->m_sb)); tp->t_magic = XFS_TRANS_HEADER_MAGIC; tp->t_flags = flags; @@ -365,6 +368,20 @@ xfs_trans_mod_sb( tp->t_blk_res_used += (uint)-delta; if (tp->t_blk_res_used > tp->t_blk_res) xfs_force_shutdown(mp, SHUTDOWN_CORRUPT_INCORE); + } else if (delta > 0 && (tp->t_flags & XFS_TRANS_RES_FDBLKS)) { + int64_t blkres_delta; + + /* + * Return freed blocks directly to the reservation + * instead of the global pool, being careful not to + * overflow the trans counter. This is used to preserve + * reservation across chains of transaction rolls that + * repeatedly free and allocate blocks. + */ + blkres_delta = min_t(int64_t, delta, + UINT_MAX - tp->t_blk_res); + tp->t_blk_res += blkres_delta; + delta -= blkres_delta; } tp->t_fdblocks_delta += delta; if (xfs_sb_version_haslazysbcount(&mp->m_sb)) -- GitLab From eb0efe5063bb10bcb653e4f8e92a74719c03a347 Mon Sep 17 00:00:00 2001 From: "Darrick J. Wong" Date: Mon, 29 Jun 2020 14:47:17 -0700 Subject: [PATCH 0277/1476] xfs: don't eat an EIO/ENOSPC writeback error when scrubbing data fork The data fork scrubber calls filemap_write_and_wait to flush dirty pages and delalloc reservations out to disk prior to checking the data fork's extent mappings. Unfortunately, this means that scrub can consume the EIO/ENOSPC errors that would otherwise have stayed around in the address space until (we hope) the writer application calls fsync to persist data and collect errors. The end result is that programs that wrote to a file might never see the error code and proceed as if nothing were wrong. xfs_scrub is not in a position to notify file writers about the writeback failure, and it's only here to check metadata, not file contents. Therefore, if writeback fails, we should stuff the error code back into the address space so that an fsync by the writer application can pick that up. Fixes: 99d9d8d05da2 ("xfs: scrub inode block mappings") Signed-off-by: Darrick J. Wong Reviewed-by: Brian Foster Reviewed-by: Dave Chinner --- fs/xfs/scrub/bmap.c | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/fs/xfs/scrub/bmap.c b/fs/xfs/scrub/bmap.c index 7badd6dfe5449..955302e7cdde9 100644 --- a/fs/xfs/scrub/bmap.c +++ b/fs/xfs/scrub/bmap.c @@ -45,9 +45,27 @@ xchk_setup_inode_bmap( */ if (S_ISREG(VFS_I(sc->ip)->i_mode) && sc->sm->sm_type == XFS_SCRUB_TYPE_BMBTD) { + struct address_space *mapping = VFS_I(sc->ip)->i_mapping; + inode_dio_wait(VFS_I(sc->ip)); - error = filemap_write_and_wait(VFS_I(sc->ip)->i_mapping); - if (error) + + /* + * Try to flush all incore state to disk before we examine the + * space mappings for the data fork. Leave accumulated errors + * in the mapping for the writer threads to consume. + * + * On ENOSPC or EIO writeback errors, we continue into the + * extent mapping checks because write failures do not + * necessarily imply anything about the correctness of the file + * metadata. The metadata and the file data could be on + * completely separate devices; a media failure might only + * affect a subset of the disk, etc. We can handle delalloc + * extents in the scrubber, so leaving them in memory is fine. + */ + error = filemap_fdatawrite(mapping); + if (!error) + error = filemap_fdatawait_keep_errors(mapping); + if (error && (error != -ENOSPC && error != -EIO)) goto out; } -- GitLab From 83895227aba1ade33e81f586aa7b6b1e143096a5 Mon Sep 17 00:00:00 2001 From: "Darrick J. Wong" Date: Mon, 29 Jun 2020 14:47:18 -0700 Subject: [PATCH 0278/1476] xfs: fix reflink quota reservation accounting error Quota reservations are supposed to account for the blocks that might be allocated due to a bmap btree split. Reflink doesn't do this, so fix this to make the quota accounting more accurate before we start rearranging things. Fixes: 862bb360ef56 ("xfs: reflink extents from one file to another") Signed-off-by: Darrick J. Wong Reviewed-by: Brian Foster --- fs/xfs/xfs_reflink.c | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/fs/xfs/xfs_reflink.c b/fs/xfs/xfs_reflink.c index 107bf2a2f3448..d89201d40891f 100644 --- a/fs/xfs/xfs_reflink.c +++ b/fs/xfs/xfs_reflink.c @@ -1003,6 +1003,7 @@ xfs_reflink_remap_extent( xfs_filblks_t rlen; xfs_filblks_t unmap_len; xfs_off_t newlen; + int64_t qres; int error; unmap_len = irec->br_startoff + irec->br_blockcount - destoff; @@ -1025,13 +1026,19 @@ xfs_reflink_remap_extent( xfs_ilock(ip, XFS_ILOCK_EXCL); xfs_trans_ijoin(tp, ip, 0); - /* If we're not just clearing space, then do we have enough quota? */ - if (real_extent) { - error = xfs_trans_reserve_quota_nblks(tp, ip, - irec->br_blockcount, 0, XFS_QMOPT_RES_REGBLKS); - if (error) - goto out_cancel; - } + /* + * Reserve quota for this operation. We don't know if the first unmap + * in the dest file will cause a bmap btree split, so we always reserve + * at least enough blocks for that split. If the extent being mapped + * in is written, we need to reserve quota for that too. + */ + qres = XFS_EXTENTADD_SPACE_RES(mp, XFS_DATA_FORK); + if (real_extent) + qres += irec->br_blockcount; + error = xfs_trans_reserve_quota_nblks(tp, ip, qres, 0, + XFS_QMOPT_RES_REGBLKS); + if (error) + goto out_cancel; trace_xfs_reflink_remap(ip, irec->br_startoff, irec->br_blockcount, irec->br_startblock); -- GitLab From 877f58f53684f14ca3202640f70592bf44890924 Mon Sep 17 00:00:00 2001 From: "Darrick J. Wong" Date: Mon, 29 Jun 2020 14:47:18 -0700 Subject: [PATCH 0279/1476] xfs: rename xfs_bmap_is_real_extent to is_written_extent The name of this predicate is a little misleading -- it decides if the extent mapping is allocated and written. Change the name to be more direct, as we're going to add a new predicate in the next patch. Signed-off-by: Darrick J. Wong Reviewed-by: Brian Foster --- fs/xfs/libxfs/xfs_bmap.h | 2 +- fs/xfs/libxfs/xfs_rtbitmap.c | 2 +- fs/xfs/xfs_reflink.c | 6 +++--- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/fs/xfs/libxfs/xfs_bmap.h b/fs/xfs/libxfs/xfs_bmap.h index 6028a3c825bac..2b18338d06437 100644 --- a/fs/xfs/libxfs/xfs_bmap.h +++ b/fs/xfs/libxfs/xfs_bmap.h @@ -163,7 +163,7 @@ static inline int xfs_bmapi_whichfork(int bmapi_flags) * Return true if the extent is a real, allocated extent, or false if it is a * delayed allocation, and unwritten extent or a hole. */ -static inline bool xfs_bmap_is_real_extent(struct xfs_bmbt_irec *irec) +static inline bool xfs_bmap_is_written_extent(struct xfs_bmbt_irec *irec) { return irec->br_state != XFS_EXT_UNWRITTEN && irec->br_startblock != HOLESTARTBLOCK && diff --git a/fs/xfs/libxfs/xfs_rtbitmap.c b/fs/xfs/libxfs/xfs_rtbitmap.c index 9498ced947be9..1d9fa8a300f15 100644 --- a/fs/xfs/libxfs/xfs_rtbitmap.c +++ b/fs/xfs/libxfs/xfs_rtbitmap.c @@ -70,7 +70,7 @@ xfs_rtbuf_get( if (error) return error; - if (XFS_IS_CORRUPT(mp, nmap == 0 || !xfs_bmap_is_real_extent(&map))) + if (XFS_IS_CORRUPT(mp, nmap == 0 || !xfs_bmap_is_written_extent(&map))) return -EFSCORRUPTED; ASSERT(map.br_startblock != NULLFSBLOCK); diff --git a/fs/xfs/xfs_reflink.c b/fs/xfs/xfs_reflink.c index d89201d40891f..22fdea6d69d3d 100644 --- a/fs/xfs/xfs_reflink.c +++ b/fs/xfs/xfs_reflink.c @@ -179,7 +179,7 @@ xfs_reflink_trim_around_shared( int error = 0; /* Holes, unwritten, and delalloc extents cannot be shared */ - if (!xfs_is_cow_inode(ip) || !xfs_bmap_is_real_extent(irec)) { + if (!xfs_is_cow_inode(ip) || !xfs_bmap_is_written_extent(irec)) { *shared = false; return 0; } @@ -655,7 +655,7 @@ xfs_reflink_end_cow_extent( * preallocations can leak into the range we are called upon, and we * need to skip them. */ - if (!xfs_bmap_is_real_extent(&got)) { + if (!xfs_bmap_is_written_extent(&got)) { *end_fsb = del.br_startoff; goto out_cancel; } @@ -996,7 +996,7 @@ xfs_reflink_remap_extent( xfs_off_t new_isize) { struct xfs_mount *mp = ip->i_mount; - bool real_extent = xfs_bmap_is_real_extent(irec); + bool real_extent = xfs_bmap_is_written_extent(irec); struct xfs_trans *tp; unsigned int resblks; struct xfs_bmbt_irec uirec; -- GitLab From 00fd1d56dd08a8ceaa9e4ee1a41fefd9f6c6bc7d Mon Sep 17 00:00:00 2001 From: "Darrick J. Wong" Date: Mon, 29 Jun 2020 14:47:18 -0700 Subject: [PATCH 0280/1476] xfs: redesign the reflink remap loop to fix blkres depletion crash MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The existing reflink remapping loop has some structural problems that need addressing: The biggest problem is that we create one transaction for each extent in the source file without accounting for the number of mappings there are for the same range in the destination file. In other words, we don't know the number of remap operations that will be necessary and we therefore cannot guess the block reservation required. On highly fragmented filesystems (e.g. ones with active dedupe) we guess wrong, run out of block reservation, and fail. The second problem is that we don't actually use the bmap intents to their full potential -- instead of calling bunmapi directly and having to deal with its backwards operation, we could call the deferred ops xfs_bmap_unmap_extent and xfs_refcount_decrease_extent instead. This makes the frontend loop much simpler. Solve all of these problems by refactoring the remapping loops so that we only perform one remapping operation per transaction, and each operation only tries to remap a single extent from source to dest. Signed-off-by: Darrick J. Wong Reviewed-by: Brian Foster Reported-by: Edwin Török Tested-by: Edwin Török --- fs/xfs/libxfs/xfs_bmap.h | 13 ++- fs/xfs/xfs_reflink.c | 238 +++++++++++++++++++++------------------ fs/xfs/xfs_trace.h | 52 +-------- 3 files changed, 141 insertions(+), 162 deletions(-) diff --git a/fs/xfs/libxfs/xfs_bmap.h b/fs/xfs/libxfs/xfs_bmap.h index 2b18338d06437..e1bd484e55485 100644 --- a/fs/xfs/libxfs/xfs_bmap.h +++ b/fs/xfs/libxfs/xfs_bmap.h @@ -158,6 +158,13 @@ static inline int xfs_bmapi_whichfork(int bmapi_flags) { BMAP_ATTRFORK, "ATTR" }, \ { BMAP_COWFORK, "COW" } +/* Return true if the extent is an allocated extent, written or not. */ +static inline bool xfs_bmap_is_real_extent(struct xfs_bmbt_irec *irec) +{ + return irec->br_startblock != HOLESTARTBLOCK && + irec->br_startblock != DELAYSTARTBLOCK && + !isnullstartblock(irec->br_startblock); +} /* * Return true if the extent is a real, allocated extent, or false if it is a @@ -165,10 +172,8 @@ static inline int xfs_bmapi_whichfork(int bmapi_flags) */ static inline bool xfs_bmap_is_written_extent(struct xfs_bmbt_irec *irec) { - return irec->br_state != XFS_EXT_UNWRITTEN && - irec->br_startblock != HOLESTARTBLOCK && - irec->br_startblock != DELAYSTARTBLOCK && - !isnullstartblock(irec->br_startblock); + return xfs_bmap_is_real_extent(irec) && + irec->br_state != XFS_EXT_UNWRITTEN; } /* diff --git a/fs/xfs/xfs_reflink.c b/fs/xfs/xfs_reflink.c index 22fdea6d69d3d..e7dd8950d40a8 100644 --- a/fs/xfs/xfs_reflink.c +++ b/fs/xfs/xfs_reflink.c @@ -984,41 +984,28 @@ xfs_reflink_ag_has_free_space( } /* - * Unmap a range of blocks from a file, then map other blocks into the hole. - * The range to unmap is (destoff : destoff + srcioff + irec->br_blockcount). - * The extent irec is mapped into dest at irec->br_startoff. + * Remap the given extent into the file. The dmap blockcount will be set to + * the number of blocks that were actually remapped. */ STATIC int xfs_reflink_remap_extent( struct xfs_inode *ip, - struct xfs_bmbt_irec *irec, - xfs_fileoff_t destoff, + struct xfs_bmbt_irec *dmap, xfs_off_t new_isize) { + struct xfs_bmbt_irec smap; struct xfs_mount *mp = ip->i_mount; - bool real_extent = xfs_bmap_is_written_extent(irec); struct xfs_trans *tp; - unsigned int resblks; - struct xfs_bmbt_irec uirec; - xfs_filblks_t rlen; - xfs_filblks_t unmap_len; xfs_off_t newlen; - int64_t qres; + int64_t qres, qdelta; + unsigned int resblks; + bool smap_real; + bool dmap_written = xfs_bmap_is_written_extent(dmap); + int nimaps; int error; - unmap_len = irec->br_startoff + irec->br_blockcount - destoff; - trace_xfs_reflink_punch_range(ip, destoff, unmap_len); - - /* No reflinking if we're low on space */ - if (real_extent) { - error = xfs_reflink_ag_has_free_space(mp, - XFS_FSB_TO_AGNO(mp, irec->br_startblock)); - if (error) - goto out; - } - /* Start a rolling transaction to switch the mappings */ - resblks = XFS_EXTENTADD_SPACE_RES(ip->i_mount, XFS_DATA_FORK); + resblks = XFS_EXTENTADD_SPACE_RES(mp, XFS_DATA_FORK); error = xfs_trans_alloc(mp, &M_RES(mp)->tr_write, resblks, 0, 0, &tp); if (error) goto out; @@ -1027,92 +1014,121 @@ xfs_reflink_remap_extent( xfs_trans_ijoin(tp, ip, 0); /* - * Reserve quota for this operation. We don't know if the first unmap - * in the dest file will cause a bmap btree split, so we always reserve - * at least enough blocks for that split. If the extent being mapped - * in is written, we need to reserve quota for that too. + * Read what's currently mapped in the destination file into smap. + * If smap isn't a hole, we will have to remove it before we can add + * dmap to the destination file. */ - qres = XFS_EXTENTADD_SPACE_RES(mp, XFS_DATA_FORK); - if (real_extent) - qres += irec->br_blockcount; - error = xfs_trans_reserve_quota_nblks(tp, ip, qres, 0, - XFS_QMOPT_RES_REGBLKS); + nimaps = 1; + error = xfs_bmapi_read(ip, dmap->br_startoff, dmap->br_blockcount, + &smap, &nimaps, 0); if (error) goto out_cancel; + ASSERT(nimaps == 1 && smap.br_startoff == dmap->br_startoff); + smap_real = xfs_bmap_is_real_extent(&smap); - trace_xfs_reflink_remap(ip, irec->br_startoff, - irec->br_blockcount, irec->br_startblock); + /* + * We can only remap as many blocks as the smaller of the two extent + * maps, because we can only remap one extent at a time. + */ + dmap->br_blockcount = min(dmap->br_blockcount, smap.br_blockcount); + ASSERT(dmap->br_blockcount == smap.br_blockcount); - /* Unmap the old blocks in the data fork. */ - rlen = unmap_len; - while (rlen) { - ASSERT(tp->t_firstblock == NULLFSBLOCK); - error = __xfs_bunmapi(tp, ip, destoff, &rlen, 0, 1); + trace_xfs_reflink_remap_extent_dest(ip, &smap); + + /* No reflinking if the AG of the dest mapping is low on space. */ + if (dmap_written) { + error = xfs_reflink_ag_has_free_space(mp, + XFS_FSB_TO_AGNO(mp, dmap->br_startblock)); if (error) goto out_cancel; + } + + /* + * Compute quota reservation if we think the quota block counter for + * this file could increase. + * + * We start by reserving enough blocks to handle a bmbt split. + * + * If we are mapping a written extent into the file, we need to have + * enough quota block count reservation to handle the blocks in that + * extent. + * + * Note that if we're replacing a delalloc reservation with a written + * extent, we have to take the full quota reservation because removing + * the delalloc reservation gives the block count back to the quota + * count. This is suboptimal, but the VFS flushed the dest range + * before we started. That should have removed all the delalloc + * reservations, but we code defensively. + */ + qdelta = 0; + qres = XFS_EXTENTADD_SPACE_RES(mp, XFS_DATA_FORK); + if (dmap_written) + qres += dmap->br_blockcount; + error = xfs_trans_reserve_quota_nblks(tp, ip, qres, 0, + XFS_QMOPT_RES_REGBLKS); + if (error) + goto out_cancel; + if (smap_real) { /* - * Trim the extent to whatever got unmapped. - * Remember, bunmapi works backwards. + * If the extent we're unmapping is backed by storage (written + * or not), unmap the extent and drop its refcount. */ - uirec.br_startblock = irec->br_startblock + rlen; - uirec.br_startoff = irec->br_startoff + rlen; - uirec.br_blockcount = unmap_len - rlen; - uirec.br_state = irec->br_state; - unmap_len = rlen; - - /* If this isn't a real mapping, we're done. */ - if (!real_extent || uirec.br_blockcount == 0) - goto next_extent; - - trace_xfs_reflink_remap(ip, uirec.br_startoff, - uirec.br_blockcount, uirec.br_startblock); + xfs_bmap_unmap_extent(tp, ip, &smap); + xfs_refcount_decrease_extent(tp, &smap); + qdelta -= smap.br_blockcount; + } else if (smap.br_startblock == DELAYSTARTBLOCK) { + xfs_filblks_t len = smap.br_blockcount; - /* Update the refcount tree */ - xfs_refcount_increase_extent(tp, &uirec); - - /* Map the new blocks into the data fork. */ - xfs_bmap_map_extent(tp, ip, &uirec); + /* + * If the extent we're unmapping is a delalloc reservation, + * we can use the regular bunmapi function to release the + * incore state. Dropping the delalloc reservation takes care + * of the quota reservation for us. + */ + error = __xfs_bunmapi(NULL, ip, smap.br_startoff, &len, 0, 1); + if (error) + goto out_cancel; + ASSERT(len == 0); + } - /* Update quota accounting. */ - xfs_trans_mod_dquot_byino(tp, ip, XFS_TRANS_DQ_BCOUNT, - uirec.br_blockcount); + /* + * If the extent we're sharing is backed by written storage, increase + * its refcount and map it into the file. + */ + if (dmap_written) { + xfs_refcount_increase_extent(tp, dmap); + xfs_bmap_map_extent(tp, ip, dmap); + qdelta += dmap->br_blockcount; + } - /* Update dest isize if needed. */ - newlen = XFS_FSB_TO_B(mp, - uirec.br_startoff + uirec.br_blockcount); - newlen = min_t(xfs_off_t, newlen, new_isize); - if (newlen > i_size_read(VFS_I(ip))) { - trace_xfs_reflink_update_inode_size(ip, newlen); - i_size_write(VFS_I(ip), newlen); - ip->i_d.di_size = newlen; - xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE); - } + xfs_trans_mod_dquot_byino(tp, ip, XFS_TRANS_DQ_BCOUNT, qdelta); -next_extent: - /* Process all the deferred stuff. */ - error = xfs_defer_finish(&tp); - if (error) - goto out_cancel; + /* Update dest isize if needed. */ + newlen = XFS_FSB_TO_B(mp, dmap->br_startoff + dmap->br_blockcount); + newlen = min_t(xfs_off_t, newlen, new_isize); + if (newlen > i_size_read(VFS_I(ip))) { + trace_xfs_reflink_update_inode_size(ip, newlen); + i_size_write(VFS_I(ip), newlen); + ip->i_d.di_size = newlen; + xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE); } + /* Commit everything and unlock. */ error = xfs_trans_commit(tp); - xfs_iunlock(ip, XFS_ILOCK_EXCL); - if (error) - goto out; - return 0; + goto out_unlock; out_cancel: xfs_trans_cancel(tp); +out_unlock: xfs_iunlock(ip, XFS_ILOCK_EXCL); out: - trace_xfs_reflink_remap_extent_error(ip, error, _RET_IP_); + if (error) + trace_xfs_reflink_remap_extent_error(ip, error, _RET_IP_); return error; } -/* - * Iteratively remap one file's extents (and holes) to another's. - */ +/* Remap a range of one file to the other. */ int xfs_reflink_remap_blocks( struct xfs_inode *src, @@ -1123,25 +1139,22 @@ xfs_reflink_remap_blocks( loff_t *remapped) { struct xfs_bmbt_irec imap; - xfs_fileoff_t srcoff; - xfs_fileoff_t destoff; + struct xfs_mount *mp = src->i_mount; + xfs_fileoff_t srcoff = XFS_B_TO_FSBT(mp, pos_in); + xfs_fileoff_t destoff = XFS_B_TO_FSBT(mp, pos_out); xfs_filblks_t len; - xfs_filblks_t range_len; xfs_filblks_t remapped_len = 0; xfs_off_t new_isize = pos_out + remap_len; int nimaps; int error = 0; - destoff = XFS_B_TO_FSBT(src->i_mount, pos_out); - srcoff = XFS_B_TO_FSBT(src->i_mount, pos_in); - len = XFS_B_TO_FSB(src->i_mount, remap_len); + len = min_t(xfs_filblks_t, XFS_B_TO_FSB(mp, remap_len), + XFS_MAX_FILEOFF); - /* drange = (destoff, destoff + len); srange = (srcoff, srcoff + len) */ - while (len) { - uint lock_mode; + trace_xfs_reflink_remap_blocks(src, srcoff, len, dest, destoff); - trace_xfs_reflink_remap_blocks_loop(src, srcoff, len, - dest, destoff); + while (len > 0) { + unsigned int lock_mode; /* Read extent from the source file */ nimaps = 1; @@ -1150,18 +1163,25 @@ xfs_reflink_remap_blocks( xfs_iunlock(src, lock_mode); if (error) break; - ASSERT(nimaps == 1); - - trace_xfs_reflink_remap_imap(src, srcoff, len, XFS_DATA_FORK, - &imap); + /* + * The caller supposedly flushed all dirty pages in the source + * file range, which means that writeback should have allocated + * or deleted all delalloc reservations in that range. If we + * find one, that's a good sign that something is seriously + * wrong here. + */ + ASSERT(nimaps == 1 && imap.br_startoff == srcoff); + if (imap.br_startblock == DELAYSTARTBLOCK) { + ASSERT(imap.br_startblock != DELAYSTARTBLOCK); + error = -EFSCORRUPTED; + break; + } - /* Translate imap into the destination file. */ - range_len = imap.br_startoff + imap.br_blockcount - srcoff; - imap.br_startoff += destoff - srcoff; + trace_xfs_reflink_remap_extent_src(src, &imap); - /* Clear dest from destoff to the end of imap and map it in. */ - error = xfs_reflink_remap_extent(dest, &imap, destoff, - new_isize); + /* Remap into the destination file at the given offset. */ + imap.br_startoff = destoff; + error = xfs_reflink_remap_extent(dest, &imap, new_isize); if (error) break; @@ -1171,10 +1191,10 @@ xfs_reflink_remap_blocks( } /* Advance drange/srange */ - srcoff += range_len; - destoff += range_len; - len -= range_len; - remapped_len += range_len; + srcoff += imap.br_blockcount; + destoff += imap.br_blockcount; + len -= imap.br_blockcount; + remapped_len += imap.br_blockcount; } if (error) diff --git a/fs/xfs/xfs_trace.h b/fs/xfs/xfs_trace.h index 460136628a795..50c478374a31b 100644 --- a/fs/xfs/xfs_trace.h +++ b/fs/xfs/xfs_trace.h @@ -3052,8 +3052,7 @@ DEFINE_EVENT(xfs_inode_irec_class, name, \ DEFINE_INODE_EVENT(xfs_reflink_set_inode_flag); DEFINE_INODE_EVENT(xfs_reflink_unset_inode_flag); DEFINE_ITRUNC_EVENT(xfs_reflink_update_inode_size); -DEFINE_IMAP_EVENT(xfs_reflink_remap_imap); -TRACE_EVENT(xfs_reflink_remap_blocks_loop, +TRACE_EVENT(xfs_reflink_remap_blocks, TP_PROTO(struct xfs_inode *src, xfs_fileoff_t soffset, xfs_filblks_t len, struct xfs_inode *dest, xfs_fileoff_t doffset), @@ -3084,59 +3083,14 @@ TRACE_EVENT(xfs_reflink_remap_blocks_loop, __entry->dest_ino, __entry->dest_lblk) ); -TRACE_EVENT(xfs_reflink_punch_range, - TP_PROTO(struct xfs_inode *ip, xfs_fileoff_t lblk, - xfs_extlen_t len), - TP_ARGS(ip, lblk, len), - TP_STRUCT__entry( - __field(dev_t, dev) - __field(xfs_ino_t, ino) - __field(xfs_fileoff_t, lblk) - __field(xfs_extlen_t, len) - ), - TP_fast_assign( - __entry->dev = VFS_I(ip)->i_sb->s_dev; - __entry->ino = ip->i_ino; - __entry->lblk = lblk; - __entry->len = len; - ), - TP_printk("dev %d:%d ino 0x%llx lblk 0x%llx len 0x%x", - MAJOR(__entry->dev), MINOR(__entry->dev), - __entry->ino, - __entry->lblk, - __entry->len) -); -TRACE_EVENT(xfs_reflink_remap, - TP_PROTO(struct xfs_inode *ip, xfs_fileoff_t lblk, - xfs_extlen_t len, xfs_fsblock_t new_pblk), - TP_ARGS(ip, lblk, len, new_pblk), - TP_STRUCT__entry( - __field(dev_t, dev) - __field(xfs_ino_t, ino) - __field(xfs_fileoff_t, lblk) - __field(xfs_extlen_t, len) - __field(xfs_fsblock_t, new_pblk) - ), - TP_fast_assign( - __entry->dev = VFS_I(ip)->i_sb->s_dev; - __entry->ino = ip->i_ino; - __entry->lblk = lblk; - __entry->len = len; - __entry->new_pblk = new_pblk; - ), - TP_printk("dev %d:%d ino 0x%llx lblk 0x%llx len 0x%x new_pblk %llu", - MAJOR(__entry->dev), MINOR(__entry->dev), - __entry->ino, - __entry->lblk, - __entry->len, - __entry->new_pblk) -); DEFINE_DOUBLE_IO_EVENT(xfs_reflink_remap_range); DEFINE_INODE_ERROR_EVENT(xfs_reflink_remap_range_error); DEFINE_INODE_ERROR_EVENT(xfs_reflink_set_inode_flag_error); DEFINE_INODE_ERROR_EVENT(xfs_reflink_update_inode_size_error); DEFINE_INODE_ERROR_EVENT(xfs_reflink_remap_blocks_error); DEFINE_INODE_ERROR_EVENT(xfs_reflink_remap_extent_error); +DEFINE_INODE_IREC_EVENT(xfs_reflink_remap_extent_src); +DEFINE_INODE_IREC_EVENT(xfs_reflink_remap_extent_dest); /* dedupe tracepoints */ DEFINE_DOUBLE_IO_EVENT(xfs_reflink_compare_extents); -- GitLab From aa5d0ba0b5dbb5105276214c7f9124855b20f75e Mon Sep 17 00:00:00 2001 From: "Darrick J. Wong" Date: Mon, 29 Jun 2020 14:47:19 -0700 Subject: [PATCH 0281/1476] xfs: only reserve quota blocks for bmbt changes if we're changing the data fork Now that we've reworked xfs_reflink_remap_extent to remap only one extent per transaction, we actually know if the extent being removed is an allocated mapping. This means that we now know ahead of time if we're going to be touching the data fork. Since we only need blocks for a bmbt split if we're going to update the data fork, we only need to get quota reservation if we know we're going to touch the data fork. Signed-off-by: Darrick J. Wong Reviewed-by: Brian Foster --- fs/xfs/xfs_reflink.c | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/fs/xfs/xfs_reflink.c b/fs/xfs/xfs_reflink.c index e7dd8950d40a8..3073c608216c6 100644 --- a/fs/xfs/xfs_reflink.c +++ b/fs/xfs/xfs_reflink.c @@ -1047,7 +1047,11 @@ xfs_reflink_remap_extent( * Compute quota reservation if we think the quota block counter for * this file could increase. * - * We start by reserving enough blocks to handle a bmbt split. + * Adding a written extent to the extent map can cause a bmbt split, + * and removing a mapped extent from the extent can cause a bmbt split. + * The two operations cannot both cause a split since they operate on + * the same index in the bmap btree, so we only need a reservation for + * one bmbt split if either thing is happening. * * If we are mapping a written extent into the file, we need to have * enough quota block count reservation to handle the blocks in that @@ -1060,14 +1064,17 @@ xfs_reflink_remap_extent( * before we started. That should have removed all the delalloc * reservations, but we code defensively. */ - qdelta = 0; - qres = XFS_EXTENTADD_SPACE_RES(mp, XFS_DATA_FORK); + qres = qdelta = 0; + if (smap_real || dmap_written) + qres = XFS_EXTENTADD_SPACE_RES(mp, XFS_DATA_FORK); if (dmap_written) qres += dmap->br_blockcount; - error = xfs_trans_reserve_quota_nblks(tp, ip, qres, 0, - XFS_QMOPT_RES_REGBLKS); - if (error) - goto out_cancel; + if (qres > 0) { + error = xfs_trans_reserve_quota_nblks(tp, ip, qres, 0, + XFS_QMOPT_RES_REGBLKS); + if (error) + goto out_cancel; + } if (smap_real) { /* -- GitLab From 94b941fd7a98cebd6b77b8925c54ef76bbf7473f Mon Sep 17 00:00:00 2001 From: "Darrick J. Wong" Date: Mon, 29 Jun 2020 14:47:19 -0700 Subject: [PATCH 0282/1476] xfs: only reserve quota blocks if we're mapping into a hole When logging quota block count updates during a reflink operation, we only log the /delta/ of the block count changes to the dquot. Since we now know ahead of time the extent type of both dmap and smap (and that they have the same length), we know that we only need to reserve quota blocks for dmap's blockcount if we're mapping it into a hole. Signed-off-by: Darrick J. Wong Reviewed-by: Brian Foster --- fs/xfs/xfs_reflink.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/fs/xfs/xfs_reflink.c b/fs/xfs/xfs_reflink.c index 3073c608216c6..35a17ca5b5089 100644 --- a/fs/xfs/xfs_reflink.c +++ b/fs/xfs/xfs_reflink.c @@ -1055,7 +1055,9 @@ xfs_reflink_remap_extent( * * If we are mapping a written extent into the file, we need to have * enough quota block count reservation to handle the blocks in that - * extent. + * extent. We log only the delta to the quota block counts, so if the + * extent we're unmapping also has blocks allocated to it, we don't + * need a quota reservation for the extent itself. * * Note that if we're replacing a delalloc reservation with a written * extent, we have to take the full quota reservation because removing @@ -1067,7 +1069,7 @@ xfs_reflink_remap_extent( qres = qdelta = 0; if (smap_real || dmap_written) qres = XFS_EXTENTADD_SPACE_RES(mp, XFS_DATA_FORK); - if (dmap_written) + if (!smap_real && dmap_written) qres += dmap->br_blockcount; if (qres > 0) { error = xfs_trans_reserve_quota_nblks(tp, ip, qres, 0, -- GitLab From 168eae803cede459d67ed0ab3ddb19539700a78a Mon Sep 17 00:00:00 2001 From: "Darrick J. Wong" Date: Mon, 29 Jun 2020 14:47:19 -0700 Subject: [PATCH 0283/1476] xfs: reflink can skip remap existing mappings If the source and destination map are identical, we can skip the remap step to save some time. Signed-off-by: Darrick J. Wong Reviewed-by: Brian Foster --- fs/xfs/xfs_reflink.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/fs/xfs/xfs_reflink.c b/fs/xfs/xfs_reflink.c index 35a17ca5b5089..0a3681646fc96 100644 --- a/fs/xfs/xfs_reflink.c +++ b/fs/xfs/xfs_reflink.c @@ -1035,6 +1035,22 @@ xfs_reflink_remap_extent( trace_xfs_reflink_remap_extent_dest(ip, &smap); + /* + * Two extents mapped to the same physical block must not have + * different states; that's filesystem corruption. Move on to the next + * extent if they're both holes or both the same physical extent. + */ + if (dmap->br_startblock == smap.br_startblock) { + if (dmap->br_state != smap.br_state) + error = -EFSCORRUPTED; + goto out_cancel; + } + + /* If both extents are unwritten, leave them alone. */ + if (dmap->br_state == XFS_EXT_UNWRITTEN && + smap.br_state == XFS_EXT_UNWRITTEN) + goto out_cancel; + /* No reflinking if the AG of the dest mapping is low on space. */ if (dmap_written) { error = xfs_reflink_ag_has_free_space(mp, -- GitLab From 451d34ee075023d790213885a947fc7a71f26e6d Mon Sep 17 00:00:00 2001 From: "Darrick J. Wong" Date: Mon, 29 Jun 2020 14:47:20 -0700 Subject: [PATCH 0284/1476] xfs: fix xfs_reflink_remap_prep calling conventions Fix the return value of xfs_reflink_remap_prep so that its return value conventions match the rest of xfs. Signed-off-by: Darrick J. Wong Reviewed-by: Brian Foster --- fs/xfs/xfs_file.c | 2 +- fs/xfs/xfs_reflink.c | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/fs/xfs/xfs_file.c b/fs/xfs/xfs_file.c index 00db81eac80d6..b375fae811f2c 100644 --- a/fs/xfs/xfs_file.c +++ b/fs/xfs/xfs_file.c @@ -1035,7 +1035,7 @@ xfs_file_remap_range( /* Prepare and then clone file data. */ ret = xfs_reflink_remap_prep(file_in, pos_in, file_out, pos_out, &len, remap_flags); - if (ret < 0 || len == 0) + if (ret || len == 0) return ret; trace_xfs_reflink_remap_range(src, pos_in, len, dest, pos_out); diff --git a/fs/xfs/xfs_reflink.c b/fs/xfs/xfs_reflink.c index 0a3681646fc96..f07a09d35a68d 100644 --- a/fs/xfs/xfs_reflink.c +++ b/fs/xfs/xfs_reflink.c @@ -1367,7 +1367,7 @@ xfs_reflink_remap_prep( struct inode *inode_out = file_inode(file_out); struct xfs_inode *dest = XFS_I(inode_out); bool same_inode = (inode_in == inode_out); - ssize_t ret; + int ret; /* Lock both files against IO */ ret = xfs_iolock_two_inodes_and_break_layout(inode_in, inode_out); @@ -1391,7 +1391,7 @@ xfs_reflink_remap_prep( ret = generic_remap_file_range_prep(file_in, pos_in, file_out, pos_out, len, remap_flags); - if (ret < 0 || *len == 0) + if (ret || *len == 0) goto out_unlock; /* Attach dquots to dest inode before changing block map */ @@ -1426,7 +1426,7 @@ xfs_reflink_remap_prep( if (ret) goto out_unlock; - return 1; + return 0; out_unlock: xfs_reflink_remap_unlock(file_in, file_out); return ret; -- GitLab From 10b4bd6c9cbc1434c9aca523dac9c4313b56f1ae Mon Sep 17 00:00:00 2001 From: "Darrick J. Wong" Date: Mon, 29 Jun 2020 14:47:20 -0700 Subject: [PATCH 0285/1476] xfs: refactor locking and unlocking two inodes against userspace IO Refactor the two functions that we use to lock and unlock two inodes to block userspace from initiating IO against a file, whether via system calls or mmap activity. Signed-off-by: Darrick J. Wong Reviewed-by: Brian Foster --- fs/xfs/xfs_file.c | 2 +- fs/xfs/xfs_reflink.c | 52 +++++++++++++++++++++++++++----------------- fs/xfs/xfs_reflink.h | 3 +-- 3 files changed, 34 insertions(+), 23 deletions(-) diff --git a/fs/xfs/xfs_file.c b/fs/xfs/xfs_file.c index b375fae811f2c..f189bdcbedddd 100644 --- a/fs/xfs/xfs_file.c +++ b/fs/xfs/xfs_file.c @@ -1065,7 +1065,7 @@ xfs_file_remap_range( if (mp->m_flags & XFS_MOUNT_WSYNC) xfs_log_force_inode(dest); out_unlock: - xfs_reflink_remap_unlock(file_in, file_out); + xfs_reflink_remap_unlock(src, dest); if (ret) trace_xfs_reflink_remap_range_error(dest, ret, _RET_IP_); return remapped > 0 ? remapped : ret; diff --git a/fs/xfs/xfs_reflink.c b/fs/xfs/xfs_reflink.c index f07a09d35a68d..c1f2222ffece6 100644 --- a/fs/xfs/xfs_reflink.c +++ b/fs/xfs/xfs_reflink.c @@ -1284,24 +1284,42 @@ retry: return 0; } -/* Unlock both inodes after they've been prepped for a range clone. */ +/* + * Lock two files so that userspace cannot initiate I/O via file syscalls or + * mmap activity. + */ +static int +xfs_reflink_remap_lock( + struct xfs_inode *ip1, + struct xfs_inode *ip2) +{ + int ret; + + ret = xfs_iolock_two_inodes_and_break_layout(VFS_I(ip1), VFS_I(ip2)); + if (ret) + return ret; + if (ip1 == ip2) + xfs_ilock(ip1, XFS_MMAPLOCK_EXCL); + else + xfs_lock_two_inodes(ip1, XFS_MMAPLOCK_EXCL, + ip2, XFS_MMAPLOCK_EXCL); + return 0; +} + +/* Unlock both files to allow IO and mmap activity. */ void xfs_reflink_remap_unlock( - struct file *file_in, - struct file *file_out) + struct xfs_inode *ip1, + struct xfs_inode *ip2) { - struct inode *inode_in = file_inode(file_in); - struct xfs_inode *src = XFS_I(inode_in); - struct inode *inode_out = file_inode(file_out); - struct xfs_inode *dest = XFS_I(inode_out); - bool same_inode = (inode_in == inode_out); + bool same_inode = (ip1 == ip2); - xfs_iunlock(dest, XFS_MMAPLOCK_EXCL); + xfs_iunlock(ip2, XFS_MMAPLOCK_EXCL); if (!same_inode) - xfs_iunlock(src, XFS_MMAPLOCK_EXCL); - inode_unlock(inode_out); + xfs_iunlock(ip1, XFS_MMAPLOCK_EXCL); + inode_unlock(VFS_I(ip2)); if (!same_inode) - inode_unlock(inode_in); + inode_unlock(VFS_I(ip1)); } /* @@ -1366,18 +1384,12 @@ xfs_reflink_remap_prep( struct xfs_inode *src = XFS_I(inode_in); struct inode *inode_out = file_inode(file_out); struct xfs_inode *dest = XFS_I(inode_out); - bool same_inode = (inode_in == inode_out); int ret; /* Lock both files against IO */ - ret = xfs_iolock_two_inodes_and_break_layout(inode_in, inode_out); + ret = xfs_reflink_remap_lock(src, dest); if (ret) return ret; - if (same_inode) - xfs_ilock(src, XFS_MMAPLOCK_EXCL); - else - xfs_lock_two_inodes(src, XFS_MMAPLOCK_EXCL, dest, - XFS_MMAPLOCK_EXCL); /* Check file eligibility and prepare for block sharing. */ ret = -EINVAL; @@ -1428,7 +1440,7 @@ xfs_reflink_remap_prep( return 0; out_unlock: - xfs_reflink_remap_unlock(file_in, file_out); + xfs_reflink_remap_unlock(src, dest); return ret; } diff --git a/fs/xfs/xfs_reflink.h b/fs/xfs/xfs_reflink.h index 3e4fd46373abe..ceeb59b86b291 100644 --- a/fs/xfs/xfs_reflink.h +++ b/fs/xfs/xfs_reflink.h @@ -56,7 +56,6 @@ extern int xfs_reflink_remap_blocks(struct xfs_inode *src, loff_t pos_in, loff_t *remapped); extern int xfs_reflink_update_dest(struct xfs_inode *dest, xfs_off_t newlen, xfs_extlen_t cowextsize, unsigned int remap_flags); -extern void xfs_reflink_remap_unlock(struct file *file_in, - struct file *file_out); +extern void xfs_reflink_remap_unlock(struct xfs_inode *ip1, struct xfs_inode *ip2); #endif /* __XFS_REFLINK_H */ -- GitLab From e2aaee9cd34d8396a48abf0b1be81a464c1d51c5 Mon Sep 17 00:00:00 2001 From: "Darrick J. Wong" Date: Mon, 29 Jun 2020 14:47:20 -0700 Subject: [PATCH 0286/1476] xfs: move helpers that lock and unlock two inodes against userspace IO Move the double-inode locking helpers to xfs_inode.c since they're not specific to reflink. Signed-off-by: Darrick J. Wong Reviewed-by: Brian Foster --- fs/xfs/xfs_file.c | 2 +- fs/xfs/xfs_inode.c | 93 ++++++++++++++++++++++++++++++++++++++++++ fs/xfs/xfs_inode.h | 3 ++ fs/xfs/xfs_reflink.c | 97 +------------------------------------------- fs/xfs/xfs_reflink.h | 1 - 5 files changed, 99 insertions(+), 97 deletions(-) diff --git a/fs/xfs/xfs_file.c b/fs/xfs/xfs_file.c index f189bdcbedddd..97aa74800bd99 100644 --- a/fs/xfs/xfs_file.c +++ b/fs/xfs/xfs_file.c @@ -1065,7 +1065,7 @@ xfs_file_remap_range( if (mp->m_flags & XFS_MOUNT_WSYNC) xfs_log_force_inode(dest); out_unlock: - xfs_reflink_remap_unlock(src, dest); + xfs_iunlock2_io_mmap(src, dest); if (ret) trace_xfs_reflink_remap_range_error(dest, ret, _RET_IP_); return remapped > 0 ? remapped : ret; diff --git a/fs/xfs/xfs_inode.c b/fs/xfs/xfs_inode.c index 9aea7d68d8ab9..24edec472a7c5 100644 --- a/fs/xfs/xfs_inode.c +++ b/fs/xfs/xfs_inode.c @@ -3881,3 +3881,96 @@ xfs_log_force_inode( return 0; return xfs_log_force_lsn(ip->i_mount, lsn, XFS_LOG_SYNC, NULL); } + +/* + * Grab the exclusive iolock for a data copy from src to dest, making sure to + * abide vfs locking order (lowest pointer value goes first) and breaking the + * layout leases before proceeding. The loop is needed because we cannot call + * the blocking break_layout() with the iolocks held, and therefore have to + * back out both locks. + */ +static int +xfs_iolock_two_inodes_and_break_layout( + struct inode *src, + struct inode *dest) +{ + int error; + + if (src > dest) + swap(src, dest); + +retry: + /* Wait to break both inodes' layouts before we start locking. */ + error = break_layout(src, true); + if (error) + return error; + if (src != dest) { + error = break_layout(dest, true); + if (error) + return error; + } + + /* Lock one inode and make sure nobody got in and leased it. */ + inode_lock(src); + error = break_layout(src, false); + if (error) { + inode_unlock(src); + if (error == -EWOULDBLOCK) + goto retry; + return error; + } + + if (src == dest) + return 0; + + /* Lock the other inode and make sure nobody got in and leased it. */ + inode_lock_nested(dest, I_MUTEX_NONDIR2); + error = break_layout(dest, false); + if (error) { + inode_unlock(src); + inode_unlock(dest); + if (error == -EWOULDBLOCK) + goto retry; + return error; + } + + return 0; +} + +/* + * Lock two inodes so that userspace cannot initiate I/O via file syscalls or + * mmap activity. + */ +int +xfs_ilock2_io_mmap( + struct xfs_inode *ip1, + struct xfs_inode *ip2) +{ + int ret; + + ret = xfs_iolock_two_inodes_and_break_layout(VFS_I(ip1), VFS_I(ip2)); + if (ret) + return ret; + if (ip1 == ip2) + xfs_ilock(ip1, XFS_MMAPLOCK_EXCL); + else + xfs_lock_two_inodes(ip1, XFS_MMAPLOCK_EXCL, + ip2, XFS_MMAPLOCK_EXCL); + return 0; +} + +/* Unlock both inodes to allow IO and mmap activity. */ +void +xfs_iunlock2_io_mmap( + struct xfs_inode *ip1, + struct xfs_inode *ip2) +{ + bool same_inode = (ip1 == ip2); + + xfs_iunlock(ip2, XFS_MMAPLOCK_EXCL); + if (!same_inode) + xfs_iunlock(ip1, XFS_MMAPLOCK_EXCL); + inode_unlock(VFS_I(ip2)); + if (!same_inode) + inode_unlock(VFS_I(ip1)); +} diff --git a/fs/xfs/xfs_inode.h b/fs/xfs/xfs_inode.h index 47d3b391030d6..1534386b430c6 100644 --- a/fs/xfs/xfs_inode.h +++ b/fs/xfs/xfs_inode.h @@ -499,4 +499,7 @@ void xfs_iunlink_destroy(struct xfs_perag *pag); void xfs_end_io(struct work_struct *work); +int xfs_ilock2_io_mmap(struct xfs_inode *ip1, struct xfs_inode *ip2); +void xfs_iunlock2_io_mmap(struct xfs_inode *ip1, struct xfs_inode *ip2); + #endif /* __XFS_INODE_H__ */ diff --git a/fs/xfs/xfs_reflink.c b/fs/xfs/xfs_reflink.c index c1f2222ffece6..aac83f9d61075 100644 --- a/fs/xfs/xfs_reflink.c +++ b/fs/xfs/xfs_reflink.c @@ -1229,99 +1229,6 @@ xfs_reflink_remap_blocks( return error; } -/* - * Grab the exclusive iolock for a data copy from src to dest, making sure to - * abide vfs locking order (lowest pointer value goes first) and breaking the - * layout leases before proceeding. The loop is needed because we cannot call - * the blocking break_layout() with the iolocks held, and therefore have to - * back out both locks. - */ -static int -xfs_iolock_two_inodes_and_break_layout( - struct inode *src, - struct inode *dest) -{ - int error; - - if (src > dest) - swap(src, dest); - -retry: - /* Wait to break both inodes' layouts before we start locking. */ - error = break_layout(src, true); - if (error) - return error; - if (src != dest) { - error = break_layout(dest, true); - if (error) - return error; - } - - /* Lock one inode and make sure nobody got in and leased it. */ - inode_lock(src); - error = break_layout(src, false); - if (error) { - inode_unlock(src); - if (error == -EWOULDBLOCK) - goto retry; - return error; - } - - if (src == dest) - return 0; - - /* Lock the other inode and make sure nobody got in and leased it. */ - inode_lock_nested(dest, I_MUTEX_NONDIR2); - error = break_layout(dest, false); - if (error) { - inode_unlock(src); - inode_unlock(dest); - if (error == -EWOULDBLOCK) - goto retry; - return error; - } - - return 0; -} - -/* - * Lock two files so that userspace cannot initiate I/O via file syscalls or - * mmap activity. - */ -static int -xfs_reflink_remap_lock( - struct xfs_inode *ip1, - struct xfs_inode *ip2) -{ - int ret; - - ret = xfs_iolock_two_inodes_and_break_layout(VFS_I(ip1), VFS_I(ip2)); - if (ret) - return ret; - if (ip1 == ip2) - xfs_ilock(ip1, XFS_MMAPLOCK_EXCL); - else - xfs_lock_two_inodes(ip1, XFS_MMAPLOCK_EXCL, - ip2, XFS_MMAPLOCK_EXCL); - return 0; -} - -/* Unlock both files to allow IO and mmap activity. */ -void -xfs_reflink_remap_unlock( - struct xfs_inode *ip1, - struct xfs_inode *ip2) -{ - bool same_inode = (ip1 == ip2); - - xfs_iunlock(ip2, XFS_MMAPLOCK_EXCL); - if (!same_inode) - xfs_iunlock(ip1, XFS_MMAPLOCK_EXCL); - inode_unlock(VFS_I(ip2)); - if (!same_inode) - inode_unlock(VFS_I(ip1)); -} - /* * If we're reflinking to a point past the destination file's EOF, we must * zero any speculative post-EOF preallocations that sit between the old EOF @@ -1387,7 +1294,7 @@ xfs_reflink_remap_prep( int ret; /* Lock both files against IO */ - ret = xfs_reflink_remap_lock(src, dest); + ret = xfs_ilock2_io_mmap(src, dest); if (ret) return ret; @@ -1440,7 +1347,7 @@ xfs_reflink_remap_prep( return 0; out_unlock: - xfs_reflink_remap_unlock(src, dest); + xfs_iunlock2_io_mmap(src, dest); return ret; } diff --git a/fs/xfs/xfs_reflink.h b/fs/xfs/xfs_reflink.h index ceeb59b86b291..487b00434b962 100644 --- a/fs/xfs/xfs_reflink.h +++ b/fs/xfs/xfs_reflink.h @@ -56,6 +56,5 @@ extern int xfs_reflink_remap_blocks(struct xfs_inode *src, loff_t pos_in, loff_t *remapped); extern int xfs_reflink_update_dest(struct xfs_inode *dest, xfs_off_t newlen, xfs_extlen_t cowextsize, unsigned int remap_flags); -extern void xfs_reflink_remap_unlock(struct xfs_inode *ip1, struct xfs_inode *ip2); #endif /* __XFS_REFLINK_H */ -- GitLab From cd647d5651c0b0deaa26c1acb9e1789437ba9bc7 Mon Sep 17 00:00:00 2001 From: Dave Chinner Date: Tue, 30 Jun 2020 11:28:53 -0700 Subject: [PATCH 0287/1476] xfs: use MMAPLOCK around filemap_map_pages() The page faultround path ->map_pages is implemented in XFS via filemap_map_pages(). This function checks that pages found in page cache lookups have not raced with truncate based invalidation by checking page->mapping is correct and page->index is within EOF. However, we've known for a long time that this is not sufficient to protect against races with invalidations done by operations that do not change EOF. e.g. hole punching and other fallocate() based direct extent manipulations. The way we protect against these races is we wrap the page fault operations in a XFS_MMAPLOCK_SHARED lock so they serialise against fallocate and truncate before calling into the filemap function that processes the fault. Do the same for XFS's ->map_pages implementation to close this potential data corruption issue. Signed-off-by: Dave Chinner Reviewed-by: Amir Goldstein Reviewed-by: Brian Foster Reviewed-by: Darrick J. Wong Signed-off-by: Darrick J. Wong --- fs/xfs/xfs_file.c | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/fs/xfs/xfs_file.c b/fs/xfs/xfs_file.c index 97aa74800bd99..cc65287261872 100644 --- a/fs/xfs/xfs_file.c +++ b/fs/xfs/xfs_file.c @@ -1263,10 +1263,23 @@ xfs_filemap_pfn_mkwrite( return __xfs_filemap_fault(vmf, PE_SIZE_PTE, true); } +static void +xfs_filemap_map_pages( + struct vm_fault *vmf, + pgoff_t start_pgoff, + pgoff_t end_pgoff) +{ + struct inode *inode = file_inode(vmf->vma->vm_file); + + xfs_ilock(XFS_I(inode), XFS_MMAPLOCK_SHARED); + filemap_map_pages(vmf, start_pgoff, end_pgoff); + xfs_iunlock(XFS_I(inode), XFS_MMAPLOCK_SHARED); +} + static const struct vm_operations_struct xfs_file_vm_ops = { .fault = xfs_filemap_fault, .huge_fault = xfs_filemap_huge_fault, - .map_pages = filemap_map_pages, + .map_pages = xfs_filemap_map_pages, .page_mkwrite = xfs_filemap_page_mkwrite, .pfn_mkwrite = xfs_filemap_pfn_mkwrite, }; -- GitLab From 0d5a57140b3e942a26352815a2d1defe6eebde35 Mon Sep 17 00:00:00 2001 From: Yafang Shao Date: Thu, 2 Jul 2020 08:37:28 -0700 Subject: [PATCH 0288/1476] xfs: remove useless definitions in xfs_linux.h Remove current_pid(), current_test_flags() and current_clear_flags_nested(), because they are useless. Signed-off-by: Yafang Shao Reviewed-by: Christoph Hellwig Reviewed-by: Darrick J. Wong Signed-off-by: Darrick J. Wong --- fs/xfs/xfs_linux.h | 4 ---- 1 file changed, 4 deletions(-) diff --git a/fs/xfs/xfs_linux.h b/fs/xfs/xfs_linux.h index 9f70d2f68e059..ab737fed7b121 100644 --- a/fs/xfs/xfs_linux.h +++ b/fs/xfs/xfs_linux.h @@ -102,12 +102,8 @@ typedef __u32 xfs_nlink_t; #define xfs_cowb_secs xfs_params.cowb_timer.val #define current_cpu() (raw_smp_processor_id()) -#define current_pid() (current->pid) -#define current_test_flags(f) (current->flags & (f)) #define current_set_flags_nested(sp, f) \ (*(sp) = current->flags, current->flags |= (f)) -#define current_clear_flags_nested(sp, f) \ - (*(sp) = current->flags, current->flags &= ~(f)) #define current_restore_flags_nested(sp, f) \ (current->flags = ((current->flags & ~(f)) | (*(sp) & (f)))) -- GitLab From 96355d5a1f0ee6dcc182c37db4894ec0c29f1692 Mon Sep 17 00:00:00 2001 From: Dave Chinner Date: Mon, 29 Jun 2020 14:48:45 -0700 Subject: [PATCH 0289/1476] xfs: Don't allow logging of XFS_ISTALE inodes In tracking down a problem in this patchset, I discovered we are reclaiming dirty stale inodes. This wasn't discovered until inodes were always attached to the cluster buffer and then the rcu callback that freed inodes was assert failing because the inode still had an active pointer to the cluster buffer after it had been reclaimed. Debugging the issue indicated that this was a pre-existing issue resulting from the way the inodes are handled in xfs_inactive_ifree. When we free a cluster buffer from xfs_ifree_cluster, all the inodes in cache are marked XFS_ISTALE. Those that are clean have nothing else done to them and so eventually get cleaned up by background reclaim. i.e. it is assumed we'll never dirty/relog an inode marked XFS_ISTALE. On journal commit dirty stale inodes as are handled by both buffer and inode log items to run though xfs_istale_done() and removed from the AIL (buffer log item commit) or the log item will simply unpin it because the buffer log item will clean it. What happens to any specific inode is entirely dependent on which log item wins the commit race, but the result is the same - stale inodes are clean, not attached to the cluster buffer, and not in the AIL. Hence inode reclaim can just free these inodes without further care. However, if the stale inode is relogged, it gets dirtied again and relogged into the CIL. Most of the time this isn't an issue, because relogging simply changes the inode's location in the current checkpoint. Problems arise, however, when the CIL checkpoints between two transactions in the xfs_inactive_ifree() deferops processing. This results in the XFS_ISTALE inode being redirtied and inserted into the CIL without any of the other stale cluster buffer infrastructure being in place. Hence on journal commit, it simply gets unpinned, so it remains dirty in memory. Everything in inode writeback avoids XFS_ISTALE inodes so it can't be written back, and it is not tracked in the AIL so there's not even a trigger to attempt to clean the inode. Hence the inode just sits dirty in memory until inode reclaim comes along, sees that it is XFS_ISTALE, and goes to reclaim it. This reclaiming of a dirty inode caused use after free, list corruptions and other nasty issues later in this patchset. Hence this patch addresses a violation of the "never log XFS_ISTALE inodes" caused by the deferops processing rolling a transaction and relogging a stale inode in xfs_inactive_free. It also adds a bunch of asserts to catch this problem in debug kernels so that we don't reintroduce this problem in future. Reproducer for this issue was generic/558 on a v4 filesystem. Signed-off-by: Dave Chinner Reviewed-by: Brian Foster Reviewed-by: Darrick J. Wong Signed-off-by: Darrick J. Wong --- fs/xfs/libxfs/xfs_trans_inode.c | 2 ++ fs/xfs/xfs_icache.c | 3 ++- fs/xfs/xfs_inode.c | 25 ++++++++++++++++++++++--- 3 files changed, 26 insertions(+), 4 deletions(-) diff --git a/fs/xfs/libxfs/xfs_trans_inode.c b/fs/xfs/libxfs/xfs_trans_inode.c index b5dfb66548422..4504d215cd590 100644 --- a/fs/xfs/libxfs/xfs_trans_inode.c +++ b/fs/xfs/libxfs/xfs_trans_inode.c @@ -36,6 +36,7 @@ xfs_trans_ijoin( ASSERT(iip->ili_lock_flags == 0); iip->ili_lock_flags = lock_flags; + ASSERT(!xfs_iflags_test(ip, XFS_ISTALE)); /* * Get a log_item_desc to point at the new item. @@ -89,6 +90,7 @@ xfs_trans_log_inode( ASSERT(ip->i_itemp != NULL); ASSERT(xfs_isilocked(ip, XFS_ILOCK_EXCL)); + ASSERT(!xfs_iflags_test(ip, XFS_ISTALE)); /* * Don't bother with i_lock for the I_DIRTY_TIME check here, as races diff --git a/fs/xfs/xfs_icache.c b/fs/xfs/xfs_icache.c index 5daef654956cb..59dea8178ae3c 100644 --- a/fs/xfs/xfs_icache.c +++ b/fs/xfs/xfs_icache.c @@ -1141,7 +1141,7 @@ restart: goto out_ifunlock; xfs_iunpin_wait(ip); } - if (xfs_iflags_test(ip, XFS_ISTALE) || xfs_inode_clean(ip)) { + if (xfs_inode_clean(ip)) { xfs_ifunlock(ip); goto reclaim; } @@ -1228,6 +1228,7 @@ reclaim: xfs_ilock(ip, XFS_ILOCK_EXCL); xfs_qm_dqdetach(ip); xfs_iunlock(ip, XFS_ILOCK_EXCL); + ASSERT(xfs_inode_clean(ip)); __xfs_inode_free(ip); return error; diff --git a/fs/xfs/xfs_inode.c b/fs/xfs/xfs_inode.c index 24edec472a7c5..917998801a99b 100644 --- a/fs/xfs/xfs_inode.c +++ b/fs/xfs/xfs_inode.c @@ -1740,10 +1740,31 @@ xfs_inactive_ifree( return error; } + /* + * We do not hold the inode locked across the entire rolling transaction + * here. We only need to hold it for the first transaction that + * xfs_ifree() builds, which may mark the inode XFS_ISTALE if the + * underlying cluster buffer is freed. Relogging an XFS_ISTALE inode + * here breaks the relationship between cluster buffer invalidation and + * stale inode invalidation on cluster buffer item journal commit + * completion, and can result in leaving dirty stale inodes hanging + * around in memory. + * + * We have no need for serialising this inode operation against other + * operations - we freed the inode and hence reallocation is required + * and that will serialise on reallocating the space the deferops need + * to free. Hence we can unlock the inode on the first commit of + * the transaction rather than roll it right through the deferops. This + * avoids relogging the XFS_ISTALE inode. + * + * We check that xfs_ifree() hasn't grown an internal transaction roll + * by asserting that the inode is still locked when it returns. + */ xfs_ilock(ip, XFS_ILOCK_EXCL); - xfs_trans_ijoin(tp, ip, 0); + xfs_trans_ijoin(tp, ip, XFS_ILOCK_EXCL); error = xfs_ifree(tp, ip); + ASSERT(xfs_isilocked(ip, XFS_ILOCK_EXCL)); if (error) { /* * If we fail to free the inode, shut down. The cancel @@ -1756,7 +1777,6 @@ xfs_inactive_ifree( xfs_force_shutdown(mp, SHUTDOWN_META_IO_ERROR); } xfs_trans_cancel(tp); - xfs_iunlock(ip, XFS_ILOCK_EXCL); return error; } @@ -1774,7 +1794,6 @@ xfs_inactive_ifree( xfs_notice(mp, "%s: xfs_trans_commit returned error %d", __func__, error); - xfs_iunlock(ip, XFS_ILOCK_EXCL); return 0; } -- GitLab From 1dfde687a65fec73e6914c184ecf8e9e54ccfe74 Mon Sep 17 00:00:00 2001 From: Dave Chinner Date: Mon, 29 Jun 2020 14:48:45 -0700 Subject: [PATCH 0290/1476] xfs: remove logged flag from inode log item This was used to track if the item had logged fields being flushed to disk. We log everything in the inode these days, so this logic is no longer needed. Remove it. Signed-off-by: Dave Chinner Reviewed-by: Christoph Hellwig Reviewed-by: Darrick J. Wong Reviewed-by: Brian Foster Signed-off-by: Darrick J. Wong --- fs/xfs/xfs_inode.c | 13 ++++--------- fs/xfs/xfs_inode_item.c | 35 ++++++++++------------------------- fs/xfs/xfs_inode_item.h | 1 - 3 files changed, 14 insertions(+), 35 deletions(-) diff --git a/fs/xfs/xfs_inode.c b/fs/xfs/xfs_inode.c index 917998801a99b..2f65fe70d3057 100644 --- a/fs/xfs/xfs_inode.c +++ b/fs/xfs/xfs_inode.c @@ -2679,7 +2679,6 @@ xfs_ifree_cluster( list_for_each_entry(lip, &bp->b_li_list, li_bio_list) { if (lip->li_type == XFS_LI_INODE) { iip = (struct xfs_inode_log_item *)lip; - ASSERT(iip->ili_logged == 1); lip->li_cb = xfs_istale_done; xfs_trans_ail_copy_lsn(mp->m_ail, &iip->ili_flush_lsn, @@ -2708,7 +2707,6 @@ xfs_ifree_cluster( iip->ili_last_fields = iip->ili_fields; iip->ili_fields = 0; iip->ili_fsync_fields = 0; - iip->ili_logged = 1; xfs_trans_ail_copy_lsn(mp->m_ail, &iip->ili_flush_lsn, &iip->ili_item.li_lsn); @@ -3840,19 +3838,16 @@ xfs_iflush_int( * * We can play with the ili_fields bits here, because the inode lock * must be held exclusively in order to set bits there and the flush - * lock protects the ili_last_fields bits. Set ili_logged so the flush - * done routine can tell whether or not to look in the AIL. Also, store - * the current LSN of the inode so that we can tell whether the item has - * moved in the AIL from xfs_iflush_done(). In order to read the lsn we - * need the AIL lock, because it is a 64 bit value that cannot be read - * atomically. + * lock protects the ili_last_fields bits. Store the current LSN of the + * inode so that we can tell whether the item has moved in the AIL from + * xfs_iflush_done(). In order to read the lsn we need the AIL lock, + * because it is a 64 bit value that cannot be read atomically. */ error = 0; flush_out: iip->ili_last_fields = iip->ili_fields; iip->ili_fields = 0; iip->ili_fsync_fields = 0; - iip->ili_logged = 1; xfs_trans_ail_copy_lsn(mp->m_ail, &iip->ili_flush_lsn, &iip->ili_item.li_lsn); diff --git a/fs/xfs/xfs_inode_item.c b/fs/xfs/xfs_inode_item.c index ba47bf65b772b..b17384aa8df40 100644 --- a/fs/xfs/xfs_inode_item.c +++ b/fs/xfs/xfs_inode_item.c @@ -528,8 +528,6 @@ xfs_inode_item_push( } ASSERT(iip->ili_fields != 0 || XFS_FORCED_SHUTDOWN(ip->i_mount)); - ASSERT(iip->ili_logged == 0 || XFS_FORCED_SHUTDOWN(ip->i_mount)); - spin_unlock(&lip->li_ailp->ail_lock); error = xfs_iflush(ip, &bp); @@ -690,30 +688,24 @@ xfs_iflush_done( continue; list_move_tail(&blip->li_bio_list, &tmp); - /* - * while we have the item, do the unlocked check for needing - * the AIL lock. - */ + + /* Do an unlocked check for needing the AIL lock. */ iip = INODE_ITEM(blip); - if ((iip->ili_logged && blip->li_lsn == iip->ili_flush_lsn) || + if (blip->li_lsn == iip->ili_flush_lsn || test_bit(XFS_LI_FAILED, &blip->li_flags)) need_ail++; } /* make sure we capture the state of the initial inode. */ iip = INODE_ITEM(lip); - if ((iip->ili_logged && lip->li_lsn == iip->ili_flush_lsn) || + if (lip->li_lsn == iip->ili_flush_lsn || test_bit(XFS_LI_FAILED, &lip->li_flags)) need_ail++; /* - * We only want to pull the item from the AIL if it is - * actually there and its location in the log has not - * changed since we started the flush. Thus, we only bother - * if the ili_logged flag is set and the inode's lsn has not - * changed. First we check the lsn outside - * the lock since it's cheaper, and then we recheck while - * holding the lock before removing the inode from the AIL. + * We only want to pull the item from the AIL if it is actually there + * and its location in the log has not changed since we started the + * flush. Thus, we only bother if the inode's lsn has not changed. */ if (need_ail) { xfs_lsn_t tail_lsn = 0; @@ -721,8 +713,7 @@ xfs_iflush_done( /* this is an opencoded batch version of xfs_trans_ail_delete */ spin_lock(&ailp->ail_lock); list_for_each_entry(blip, &tmp, li_bio_list) { - if (INODE_ITEM(blip)->ili_logged && - blip->li_lsn == INODE_ITEM(blip)->ili_flush_lsn) { + if (blip->li_lsn == INODE_ITEM(blip)->ili_flush_lsn) { /* * xfs_ail_update_finish() only cares about the * lsn of the first tail item removed, any @@ -740,14 +731,13 @@ xfs_iflush_done( } /* - * clean up and unlock the flush lock now we are done. We can clear the + * Clean up and unlock the flush lock now we are done. We can clear the * ili_last_fields bits now that we know that the data corresponding to * them is safely on disk. */ list_for_each_entry_safe(blip, n, &tmp, li_bio_list) { list_del_init(&blip->li_bio_list); iip = INODE_ITEM(blip); - iip->ili_logged = 0; iip->ili_last_fields = 0; xfs_ifunlock(iip->ili_inode); } @@ -768,16 +758,11 @@ xfs_iflush_abort( if (iip) { xfs_trans_ail_delete(&iip->ili_item, 0); - iip->ili_logged = 0; - /* - * Clear the ili_last_fields bits now that we know that the - * data corresponding to them is safely on disk. - */ - iip->ili_last_fields = 0; /* * Clear the inode logging fields so no more flushes are * attempted. */ + iip->ili_last_fields = 0; iip->ili_fields = 0; iip->ili_fsync_fields = 0; } diff --git a/fs/xfs/xfs_inode_item.h b/fs/xfs/xfs_inode_item.h index 60b34bb66e8ed..4de5070e07655 100644 --- a/fs/xfs/xfs_inode_item.h +++ b/fs/xfs/xfs_inode_item.h @@ -19,7 +19,6 @@ struct xfs_inode_log_item { xfs_lsn_t ili_flush_lsn; /* lsn at last flush */ xfs_lsn_t ili_last_lsn; /* lsn at last transaction */ unsigned short ili_lock_flags; /* lock flags */ - unsigned short ili_logged; /* flushed logged data */ unsigned int ili_last_fields; /* fields when flushed */ unsigned int ili_fields; /* fields to be logged */ unsigned int ili_fsync_fields; /* logged since last fsync */ -- GitLab From 1319ebefd6ed7a9988b7b4bc9317fbcf61a28bfc Mon Sep 17 00:00:00 2001 From: Dave Chinner Date: Mon, 29 Jun 2020 14:48:46 -0700 Subject: [PATCH 0291/1476] xfs: add an inode item lock The inode log item is kind of special in that it can be aggregating new changes in memory at the same time time existing changes are being written back to disk. This means there are fields in the log item that are accessed concurrently from contexts that don't share any locking at all. e.g. updating ili_last_fields occurs at flush time under the ILOCK_EXCL and flush lock at flush time, under the flush lock at IO completion time, and is read under the ILOCK_EXCL when the inode is logged. Hence there is no actual serialisation between reading the field during logging of the inode in transactions vs clearing the field in IO completion. We currently get away with this by the fact that we are only clearing fields in IO completion, and nothing bad happens if we accidentally log more of the inode than we actually modify. Worst case is we consume a tiny bit more memory and log bandwidth. However, if we want to do more complex state manipulations on the log item that requires updates at all three of these potential locations, we need to have some mechanism of serialising those operations. To do this, introduce a spinlock into the log item to serialise internal state. This could be done via the xfs_inode i_flags_lock, but this then leads to potential lock inversion issues where inode flag updates need to occur inside locks that best nest inside the inode log item locks (e.g. marking inodes stale during inode cluster freeing). Using a separate spinlock avoids these sorts of problems and simplifies future code. This does not touch the use of ili_fields in the item formatting code - that is entirely protected by the ILOCK_EXCL at this point in time, so it remains untouched. Signed-off-by: Dave Chinner Reviewed-by: Brian Foster Reviewed-by: Darrick J. Wong Signed-off-by: Darrick J. Wong --- fs/xfs/libxfs/xfs_trans_inode.c | 52 ++++++++++++++++----------------- fs/xfs/xfs_file.c | 9 ++++-- fs/xfs/xfs_inode.c | 20 ++++++++----- fs/xfs/xfs_inode_item.c | 7 +++++ fs/xfs/xfs_inode_item.h | 18 ++++++++++-- 5 files changed, 66 insertions(+), 40 deletions(-) diff --git a/fs/xfs/libxfs/xfs_trans_inode.c b/fs/xfs/libxfs/xfs_trans_inode.c index 4504d215cd590..c66d9d1dd58b9 100644 --- a/fs/xfs/libxfs/xfs_trans_inode.c +++ b/fs/xfs/libxfs/xfs_trans_inode.c @@ -82,16 +82,20 @@ xfs_trans_ichgtime( */ void xfs_trans_log_inode( - xfs_trans_t *tp, - xfs_inode_t *ip, - uint flags) + struct xfs_trans *tp, + struct xfs_inode *ip, + uint flags) { - struct inode *inode = VFS_I(ip); + struct xfs_inode_log_item *iip = ip->i_itemp; + struct inode *inode = VFS_I(ip); + uint iversion_flags = 0; - ASSERT(ip->i_itemp != NULL); + ASSERT(iip); ASSERT(xfs_isilocked(ip, XFS_ILOCK_EXCL)); ASSERT(!xfs_iflags_test(ip, XFS_ISTALE)); + tp->t_flags |= XFS_TRANS_DIRTY; + /* * Don't bother with i_lock for the I_DIRTY_TIME check here, as races * don't matter - we either will need an extra transaction in 24 hours @@ -104,15 +108,6 @@ xfs_trans_log_inode( spin_unlock(&inode->i_lock); } - /* - * Record the specific change for fdatasync optimisation. This - * allows fdatasync to skip log forces for inodes that are only - * timestamp dirty. We do this before the change count so that - * the core being logged in this case does not impact on fdatasync - * behaviour. - */ - ip->i_itemp->ili_fsync_fields |= flags; - /* * First time we log the inode in a transaction, bump the inode change * counter if it is configured for this to occur. While we have the @@ -122,23 +117,28 @@ xfs_trans_log_inode( * set however, then go ahead and bump the i_version counter * unconditionally. */ - if (!test_and_set_bit(XFS_LI_DIRTY, &ip->i_itemp->ili_item.li_flags) && - IS_I_VERSION(VFS_I(ip))) { - if (inode_maybe_inc_iversion(VFS_I(ip), flags & XFS_ILOG_CORE)) - flags |= XFS_ILOG_CORE; + if (!test_and_set_bit(XFS_LI_DIRTY, &iip->ili_item.li_flags)) { + if (IS_I_VERSION(inode) && + inode_maybe_inc_iversion(inode, flags & XFS_ILOG_CORE)) + iversion_flags = XFS_ILOG_CORE; } - tp->t_flags |= XFS_TRANS_DIRTY; + /* + * Record the specific change for fdatasync optimisation. This allows + * fdatasync to skip log forces for inodes that are only timestamp + * dirty. + */ + spin_lock(&iip->ili_lock); + iip->ili_fsync_fields |= flags; /* - * Always OR in the bits from the ili_last_fields field. - * This is to coordinate with the xfs_iflush() and xfs_iflush_done() - * routines in the eventual clearing of the ili_fields bits. - * See the big comment in xfs_iflush() for an explanation of - * this coordination mechanism. + * Always OR in the bits from the ili_last_fields field. This is to + * coordinate with the xfs_iflush() and xfs_iflush_done() routines in + * the eventual clearing of the ili_fields bits. See the big comment in + * xfs_iflush() for an explanation of this coordination mechanism. */ - flags |= ip->i_itemp->ili_last_fields; - ip->i_itemp->ili_fields |= flags; + iip->ili_fields |= (flags | iip->ili_last_fields | iversion_flags); + spin_unlock(&iip->ili_lock); } int diff --git a/fs/xfs/xfs_file.c b/fs/xfs/xfs_file.c index cc65287261872..01c098834c4bf 100644 --- a/fs/xfs/xfs_file.c +++ b/fs/xfs/xfs_file.c @@ -94,6 +94,7 @@ xfs_file_fsync( { struct inode *inode = file->f_mapping->host; struct xfs_inode *ip = XFS_I(inode); + struct xfs_inode_log_item *iip = ip->i_itemp; struct xfs_mount *mp = ip->i_mount; int error = 0; int log_flushed = 0; @@ -137,13 +138,15 @@ xfs_file_fsync( xfs_ilock(ip, XFS_ILOCK_SHARED); if (xfs_ipincount(ip)) { if (!datasync || - (ip->i_itemp->ili_fsync_fields & ~XFS_ILOG_TIMESTAMP)) - lsn = ip->i_itemp->ili_last_lsn; + (iip->ili_fsync_fields & ~XFS_ILOG_TIMESTAMP)) + lsn = iip->ili_last_lsn; } if (lsn) { error = xfs_log_force_lsn(mp, lsn, XFS_LOG_SYNC, &log_flushed); - ip->i_itemp->ili_fsync_fields = 0; + spin_lock(&iip->ili_lock); + iip->ili_fsync_fields = 0; + spin_unlock(&iip->ili_lock); } xfs_iunlock(ip, XFS_ILOCK_SHARED); diff --git a/fs/xfs/xfs_inode.c b/fs/xfs/xfs_inode.c index 2f65fe70d3057..d6da08165a2e2 100644 --- a/fs/xfs/xfs_inode.c +++ b/fs/xfs/xfs_inode.c @@ -2704,9 +2704,11 @@ xfs_ifree_cluster( continue; iip = ip->i_itemp; + spin_lock(&iip->ili_lock); iip->ili_last_fields = iip->ili_fields; iip->ili_fields = 0; iip->ili_fsync_fields = 0; + spin_unlock(&iip->ili_lock); xfs_trans_ail_copy_lsn(mp->m_ail, &iip->ili_flush_lsn, &iip->ili_item.li_lsn); @@ -2742,6 +2744,7 @@ xfs_ifree( { int error; struct xfs_icluster xic = { 0 }; + struct xfs_inode_log_item *iip = ip->i_itemp; ASSERT(xfs_isilocked(ip, XFS_ILOCK_EXCL)); ASSERT(VFS_I(ip)->i_nlink == 0); @@ -2779,7 +2782,9 @@ xfs_ifree( ip->i_df.if_format = XFS_DINODE_FMT_EXTENTS; /* Don't attempt to replay owner changes for a deleted inode */ - ip->i_itemp->ili_fields &= ~(XFS_ILOG_AOWNER|XFS_ILOG_DOWNER); + spin_lock(&iip->ili_lock); + iip->ili_fields &= ~(XFS_ILOG_AOWNER | XFS_ILOG_DOWNER); + spin_unlock(&iip->ili_lock); /* * Bump the generation count so no one will be confused @@ -3835,20 +3840,19 @@ xfs_iflush_int( * know that the information those bits represent is permanently on * disk. As long as the flush completes before the inode is logged * again, then both ili_fields and ili_last_fields will be cleared. - * - * We can play with the ili_fields bits here, because the inode lock - * must be held exclusively in order to set bits there and the flush - * lock protects the ili_last_fields bits. Store the current LSN of the - * inode so that we can tell whether the item has moved in the AIL from - * xfs_iflush_done(). In order to read the lsn we need the AIL lock, - * because it is a 64 bit value that cannot be read atomically. */ error = 0; flush_out: + spin_lock(&iip->ili_lock); iip->ili_last_fields = iip->ili_fields; iip->ili_fields = 0; iip->ili_fsync_fields = 0; + spin_unlock(&iip->ili_lock); + /* + * Store the current LSN of the inode so that we can tell whether the + * item has moved in the AIL from xfs_iflush_done(). + */ xfs_trans_ail_copy_lsn(mp->m_ail, &iip->ili_flush_lsn, &iip->ili_item.li_lsn); diff --git a/fs/xfs/xfs_inode_item.c b/fs/xfs/xfs_inode_item.c index b17384aa8df40..6ef9cbcfc94a7 100644 --- a/fs/xfs/xfs_inode_item.c +++ b/fs/xfs/xfs_inode_item.c @@ -637,6 +637,7 @@ xfs_inode_item_init( iip = ip->i_itemp = kmem_zone_zalloc(xfs_ili_zone, 0); iip->ili_inode = ip; + spin_lock_init(&iip->ili_lock); xfs_log_item_init(mp, &iip->ili_item, XFS_LI_INODE, &xfs_inode_item_ops); } @@ -738,7 +739,11 @@ xfs_iflush_done( list_for_each_entry_safe(blip, n, &tmp, li_bio_list) { list_del_init(&blip->li_bio_list); iip = INODE_ITEM(blip); + + spin_lock(&iip->ili_lock); iip->ili_last_fields = 0; + spin_unlock(&iip->ili_lock); + xfs_ifunlock(iip->ili_inode); } list_del(&tmp); @@ -762,9 +767,11 @@ xfs_iflush_abort( * Clear the inode logging fields so no more flushes are * attempted. */ + spin_lock(&iip->ili_lock); iip->ili_last_fields = 0; iip->ili_fields = 0; iip->ili_fsync_fields = 0; + spin_unlock(&iip->ili_lock); } /* * Release the inode's flush lock since we're done with it. diff --git a/fs/xfs/xfs_inode_item.h b/fs/xfs/xfs_inode_item.h index 4de5070e07655..4a10a1b92ee99 100644 --- a/fs/xfs/xfs_inode_item.h +++ b/fs/xfs/xfs_inode_item.h @@ -16,12 +16,24 @@ struct xfs_mount; struct xfs_inode_log_item { struct xfs_log_item ili_item; /* common portion */ struct xfs_inode *ili_inode; /* inode ptr */ - xfs_lsn_t ili_flush_lsn; /* lsn at last flush */ - xfs_lsn_t ili_last_lsn; /* lsn at last transaction */ - unsigned short ili_lock_flags; /* lock flags */ + unsigned short ili_lock_flags; /* inode lock flags */ + /* + * The ili_lock protects the interactions between the dirty state and + * the flush state of the inode log item. This allows us to do atomic + * modifications of multiple state fields without having to hold a + * specific inode lock to serialise them. + * + * We need atomic changes between inode dirtying, inode flushing and + * inode completion, but these all hold different combinations of + * ILOCK and iflock and hence we need some other method of serialising + * updates to the flush state. + */ + spinlock_t ili_lock; /* flush state lock */ unsigned int ili_last_fields; /* fields when flushed */ unsigned int ili_fields; /* fields to be logged */ unsigned int ili_fsync_fields; /* logged since last fsync */ + xfs_lsn_t ili_flush_lsn; /* lsn at last flush */ + xfs_lsn_t ili_last_lsn; /* lsn at last transaction */ }; static inline int xfs_inode_clean(xfs_inode_t *ip) -- GitLab From f593bf144c7dfee9715aa787ebbbe5dd8882e8e9 Mon Sep 17 00:00:00 2001 From: Dave Chinner Date: Mon, 29 Jun 2020 14:48:46 -0700 Subject: [PATCH 0292/1476] xfs: mark inode buffers in cache Inode buffers always have write IO callbacks, so by marking them directly we can avoid needing to attach ->b_iodone functions to them. This avoids an indirect call, and makes future modifications much simpler. While this is largely a refactor of existing functionality, we broaden the scope of the flag to beyond where inodes are explicitly attached because future changes need to know what type of log items are attached to the buffer. Adding this buffer flag may invoke the inode iodone callback in cases where it wouldn't have been previously, but this is not a functional change because the callback is identical to the normal buffer write iodone callback when inodes are not attached. Signed-off-by: Dave Chinner Reviewed-by: Brian Foster Reviewed-by: Darrick J. Wong Signed-off-by: Darrick J. Wong --- fs/xfs/xfs_buf.c | 21 ++++++++++++++++----- fs/xfs/xfs_buf.h | 38 +++++++++++++++++++++++++------------- fs/xfs/xfs_buf_item.c | 42 +++++++++++++++++++++++++++++++----------- fs/xfs/xfs_buf_item.h | 1 + fs/xfs/xfs_inode.c | 2 +- fs/xfs/xfs_trans_buf.c | 3 +++ 6 files changed, 77 insertions(+), 30 deletions(-) diff --git a/fs/xfs/xfs_buf.c b/fs/xfs/xfs_buf.c index 20b748f7e1862..ae0c923574df1 100644 --- a/fs/xfs/xfs_buf.c +++ b/fs/xfs/xfs_buf.c @@ -14,6 +14,8 @@ #include "xfs_mount.h" #include "xfs_trace.h" #include "xfs_log.h" +#include "xfs_trans.h" +#include "xfs_buf_item.h" #include "xfs_errortag.h" #include "xfs_error.h" @@ -1202,12 +1204,21 @@ xfs_buf_ioend( bp->b_flags |= XBF_DONE; } - if (bp->b_iodone) + if (read) + goto out_finish; + + if (bp->b_flags & _XBF_INODES) { + xfs_buf_inode_iodone(bp); + return; + } + + if (bp->b_iodone) { (*(bp->b_iodone))(bp); - else if (bp->b_flags & XBF_ASYNC) - xfs_buf_relse(bp); - else - complete(&bp->b_iowait); + return; + } + +out_finish: + xfs_buf_ioend_finish(bp); } static void diff --git a/fs/xfs/xfs_buf.h b/fs/xfs/xfs_buf.h index 050c53b739e24..2400cb90a04c6 100644 --- a/fs/xfs/xfs_buf.h +++ b/fs/xfs/xfs_buf.h @@ -30,15 +30,18 @@ #define XBF_STALE (1 << 6) /* buffer has been staled, do not find it */ #define XBF_WRITE_FAIL (1 << 7) /* async writes have failed on this buffer */ -/* flags used only as arguments to access routines */ -#define XBF_TRYLOCK (1 << 16)/* lock requested, but do not wait */ -#define XBF_UNMAPPED (1 << 17)/* do not map the buffer */ +/* buffer type flags for write callbacks */ +#define _XBF_INODES (1 << 16)/* inode buffer */ /* flags used only internally */ #define _XBF_PAGES (1 << 20)/* backed by refcounted pages */ #define _XBF_KMEM (1 << 21)/* backed by heap memory */ #define _XBF_DELWRI_Q (1 << 22)/* buffer on a delwri queue */ +/* flags used only as arguments to access routines */ +#define XBF_TRYLOCK (1 << 30)/* lock requested, but do not wait */ +#define XBF_UNMAPPED (1 << 31)/* do not map the buffer */ + typedef unsigned int xfs_buf_flags_t; #define XFS_BUF_FLAGS \ @@ -50,12 +53,13 @@ typedef unsigned int xfs_buf_flags_t; { XBF_DONE, "DONE" }, \ { XBF_STALE, "STALE" }, \ { XBF_WRITE_FAIL, "WRITE_FAIL" }, \ - { XBF_TRYLOCK, "TRYLOCK" }, /* should never be set */\ - { XBF_UNMAPPED, "UNMAPPED" }, /* ditto */\ + { _XBF_INODES, "INODES" }, \ { _XBF_PAGES, "PAGES" }, \ { _XBF_KMEM, "KMEM" }, \ - { _XBF_DELWRI_Q, "DELWRI_Q" } - + { _XBF_DELWRI_Q, "DELWRI_Q" }, \ + /* The following interface flags should never be set */ \ + { XBF_TRYLOCK, "TRYLOCK" }, \ + { XBF_UNMAPPED, "UNMAPPED" } /* * Internal state flags. @@ -257,9 +261,23 @@ extern void xfs_buf_unlock(xfs_buf_t *); #define xfs_buf_islocked(bp) \ ((bp)->b_sema.count <= 0) +static inline void xfs_buf_relse(xfs_buf_t *bp) +{ + xfs_buf_unlock(bp); + xfs_buf_rele(bp); +} + /* Buffer Read and Write Routines */ extern int xfs_bwrite(struct xfs_buf *bp); extern void xfs_buf_ioend(struct xfs_buf *bp); +static inline void xfs_buf_ioend_finish(struct xfs_buf *bp) +{ + if (bp->b_flags & XBF_ASYNC) + xfs_buf_relse(bp); + else + complete(&bp->b_iowait); +} + extern void __xfs_buf_ioerror(struct xfs_buf *bp, int error, xfs_failaddr_t failaddr); #define xfs_buf_ioerror(bp, err) __xfs_buf_ioerror((bp), (err), __this_address) @@ -324,12 +342,6 @@ static inline int xfs_buf_ispinned(struct xfs_buf *bp) return atomic_read(&bp->b_pin_count); } -static inline void xfs_buf_relse(xfs_buf_t *bp) -{ - xfs_buf_unlock(bp); - xfs_buf_rele(bp); -} - static inline int xfs_buf_verify_cksum(struct xfs_buf *bp, unsigned long cksum_offset) { diff --git a/fs/xfs/xfs_buf_item.c b/fs/xfs/xfs_buf_item.c index 9e75e8d6042ec..8659cf4282a64 100644 --- a/fs/xfs/xfs_buf_item.c +++ b/fs/xfs/xfs_buf_item.c @@ -1158,20 +1158,15 @@ out_stale: return false; } -/* - * This is the iodone() function for buffers which have had callbacks attached - * to them by xfs_buf_attach_iodone(). We need to iterate the items on the - * callback list, mark the buffer as having no more callbacks and then push the - * buffer through IO completion processing. - */ -void -xfs_buf_iodone_callbacks( +static void +xfs_buf_run_callbacks( struct xfs_buf *bp) { + /* - * If there is an error, process it. Some errors require us - * to run callbacks after failure processing is done so we - * detect that and take appropriate action. + * If there is an error, process it. Some errors require us to run + * callbacks after failure processing is done so we detect that and take + * appropriate action. */ if (bp->b_error && xfs_buf_iodone_callback_error(bp)) return; @@ -1188,9 +1183,34 @@ xfs_buf_iodone_callbacks( bp->b_log_item = NULL; list_del_init(&bp->b_li_list); bp->b_iodone = NULL; +} + +/* + * This is the iodone() function for buffers which have had callbacks attached + * to them by xfs_buf_attach_iodone(). We need to iterate the items on the + * callback list, mark the buffer as having no more callbacks and then push the + * buffer through IO completion processing. + */ +void +xfs_buf_iodone_callbacks( + struct xfs_buf *bp) +{ + xfs_buf_run_callbacks(bp); xfs_buf_ioend(bp); } +/* + * Inode buffer iodone callback function. + */ +void +xfs_buf_inode_iodone( + struct xfs_buf *bp) +{ + xfs_buf_run_callbacks(bp); + xfs_buf_ioend_finish(bp); +} + + /* * This is the iodone() function for buffers which have been * logged. It is called when they are eventually flushed out. diff --git a/fs/xfs/xfs_buf_item.h b/fs/xfs/xfs_buf_item.h index c9c57e2da9327..a342933ad9b8d 100644 --- a/fs/xfs/xfs_buf_item.h +++ b/fs/xfs/xfs_buf_item.h @@ -59,6 +59,7 @@ void xfs_buf_attach_iodone(struct xfs_buf *, struct xfs_log_item *); void xfs_buf_iodone_callbacks(struct xfs_buf *); void xfs_buf_iodone(struct xfs_buf *, struct xfs_log_item *); +void xfs_buf_inode_iodone(struct xfs_buf *); bool xfs_buf_log_check_iovec(struct xfs_log_iovec *iovec); extern kmem_zone_t *xfs_buf_item_zone; diff --git a/fs/xfs/xfs_inode.c b/fs/xfs/xfs_inode.c index d6da08165a2e2..4621d67f3428b 100644 --- a/fs/xfs/xfs_inode.c +++ b/fs/xfs/xfs_inode.c @@ -3862,13 +3862,13 @@ flush_out: * completion on the buffer to remove the inode from the AIL and release * the flush lock. */ + bp->b_flags |= _XBF_INODES; xfs_buf_attach_iodone(bp, xfs_iflush_done, &iip->ili_item); /* generate the checksum. */ xfs_dinode_calc_crc(mp, dip); ASSERT(!list_empty(&bp->b_li_list)); - ASSERT(bp->b_iodone != NULL); return error; } diff --git a/fs/xfs/xfs_trans_buf.c b/fs/xfs/xfs_trans_buf.c index 08174ffa21189..552d0869aa0fe 100644 --- a/fs/xfs/xfs_trans_buf.c +++ b/fs/xfs/xfs_trans_buf.c @@ -626,6 +626,7 @@ xfs_trans_inode_buf( ASSERT(atomic_read(&bip->bli_refcount) > 0); bip->bli_flags |= XFS_BLI_INODE_BUF; + bp->b_flags |= _XBF_INODES; xfs_trans_buf_set_type(tp, bp, XFS_BLFT_DINO_BUF); } @@ -651,6 +652,7 @@ xfs_trans_stale_inode_buf( bip->bli_flags |= XFS_BLI_STALE_INODE; bip->bli_item.li_cb = xfs_buf_iodone; + bp->b_flags |= _XBF_INODES; xfs_trans_buf_set_type(tp, bp, XFS_BLFT_DINO_BUF); } @@ -675,6 +677,7 @@ xfs_trans_inode_alloc_buf( ASSERT(atomic_read(&bip->bli_refcount) > 0); bip->bli_flags |= XFS_BLI_INODE_ALLOC_BUF; + bp->b_flags |= _XBF_INODES; xfs_trans_buf_set_type(tp, bp, XFS_BLFT_DINO_BUF); } -- GitLab From 0c7e5afbea9962bc65c54337c30559bf913a97d6 Mon Sep 17 00:00:00 2001 From: Dave Chinner Date: Mon, 29 Jun 2020 14:48:46 -0700 Subject: [PATCH 0293/1476] xfs: mark dquot buffers in cache dquot buffers always have write IO callbacks, so by marking them directly we can avoid needing to attach ->b_iodone functions to them. This avoids an indirect call, and makes future modifications much simpler. This is largely a rearrangement of the code at this point - no IO completion functionality changes at this point, just how the code is run is modified. Signed-off-by: Dave Chinner Reviewed-by: Brian Foster Reviewed-by: Darrick J. Wong Signed-off-by: Darrick J. Wong --- fs/xfs/xfs_buf.c | 5 +++++ fs/xfs/xfs_buf.h | 2 ++ fs/xfs/xfs_buf_item.c | 10 ++++++++++ fs/xfs/xfs_buf_item.h | 1 + fs/xfs/xfs_dquot.c | 1 + fs/xfs/xfs_trans_buf.c | 1 + 6 files changed, 20 insertions(+) diff --git a/fs/xfs/xfs_buf.c b/fs/xfs/xfs_buf.c index ae0c923574df1..517932675b12c 100644 --- a/fs/xfs/xfs_buf.c +++ b/fs/xfs/xfs_buf.c @@ -1212,6 +1212,11 @@ xfs_buf_ioend( return; } + if (bp->b_flags & _XBF_DQUOTS) { + xfs_buf_dquot_iodone(bp); + return; + } + if (bp->b_iodone) { (*(bp->b_iodone))(bp); return; diff --git a/fs/xfs/xfs_buf.h b/fs/xfs/xfs_buf.h index 2400cb90a04c6..c1d0843206dd6 100644 --- a/fs/xfs/xfs_buf.h +++ b/fs/xfs/xfs_buf.h @@ -32,6 +32,7 @@ /* buffer type flags for write callbacks */ #define _XBF_INODES (1 << 16)/* inode buffer */ +#define _XBF_DQUOTS (1 << 17)/* dquot buffer */ /* flags used only internally */ #define _XBF_PAGES (1 << 20)/* backed by refcounted pages */ @@ -54,6 +55,7 @@ typedef unsigned int xfs_buf_flags_t; { XBF_STALE, "STALE" }, \ { XBF_WRITE_FAIL, "WRITE_FAIL" }, \ { _XBF_INODES, "INODES" }, \ + { _XBF_DQUOTS, "DQUOTS" }, \ { _XBF_PAGES, "PAGES" }, \ { _XBF_KMEM, "KMEM" }, \ { _XBF_DELWRI_Q, "DELWRI_Q" }, \ diff --git a/fs/xfs/xfs_buf_item.c b/fs/xfs/xfs_buf_item.c index 8659cf4282a64..a42cdf9ccc47d 100644 --- a/fs/xfs/xfs_buf_item.c +++ b/fs/xfs/xfs_buf_item.c @@ -1210,6 +1210,16 @@ xfs_buf_inode_iodone( xfs_buf_ioend_finish(bp); } +/* + * Dquot buffer iodone callback function. + */ +void +xfs_buf_dquot_iodone( + struct xfs_buf *bp) +{ + xfs_buf_run_callbacks(bp); + xfs_buf_ioend_finish(bp); +} /* * This is the iodone() function for buffers which have been diff --git a/fs/xfs/xfs_buf_item.h b/fs/xfs/xfs_buf_item.h index a342933ad9b8d..27d13d29b5bbb 100644 --- a/fs/xfs/xfs_buf_item.h +++ b/fs/xfs/xfs_buf_item.h @@ -60,6 +60,7 @@ void xfs_buf_attach_iodone(struct xfs_buf *, void xfs_buf_iodone_callbacks(struct xfs_buf *); void xfs_buf_iodone(struct xfs_buf *, struct xfs_log_item *); void xfs_buf_inode_iodone(struct xfs_buf *); +void xfs_buf_dquot_iodone(struct xfs_buf *); bool xfs_buf_log_check_iovec(struct xfs_log_iovec *iovec); extern kmem_zone_t *xfs_buf_item_zone; diff --git a/fs/xfs/xfs_dquot.c b/fs/xfs/xfs_dquot.c index d5b7f03e93c8d..2e2146fa0914c 100644 --- a/fs/xfs/xfs_dquot.c +++ b/fs/xfs/xfs_dquot.c @@ -1179,6 +1179,7 @@ xfs_qm_dqflush( * Attach an iodone routine so that we can remove this dquot from the * AIL and release the flush lock once the dquot is synced to disk. */ + bp->b_flags |= _XBF_DQUOTS; xfs_buf_attach_iodone(bp, xfs_qm_dqflush_done, &dqp->q_logitem.qli_item); diff --git a/fs/xfs/xfs_trans_buf.c b/fs/xfs/xfs_trans_buf.c index 552d0869aa0fe..93d62cb864c15 100644 --- a/fs/xfs/xfs_trans_buf.c +++ b/fs/xfs/xfs_trans_buf.c @@ -788,5 +788,6 @@ xfs_trans_dquot_buf( break; } + bp->b_flags |= _XBF_DQUOTS; xfs_trans_buf_set_type(tp, bp, type); } -- GitLab From 9fe5c77cbe3cacc60d03ae5940033e4173fc1847 Mon Sep 17 00:00:00 2001 From: Dave Chinner Date: Mon, 29 Jun 2020 14:48:47 -0700 Subject: [PATCH 0294/1476] xfs: mark log recovery buffers for completion Log recovery has it's own buffer write completion handler for buffers that it directly recovers. Convert these to direct calls by flagging these buffers as being log recovery buffers. The flag will get cleared by the log recovery IO completion routine, so it will never leak out of log recovery. Signed-off-by: Dave Chinner Reviewed-by: Brian Foster Reviewed-by: Darrick J. Wong Signed-off-by: Darrick J. Wong --- fs/xfs/xfs_buf.c | 10 ++++++++++ fs/xfs/xfs_buf.h | 2 ++ fs/xfs/xfs_buf_item_recover.c | 5 ++--- fs/xfs/xfs_dquot_item_recover.c | 2 +- fs/xfs/xfs_inode_item_recover.c | 2 +- fs/xfs/xfs_log_recover.c | 5 ++--- 6 files changed, 18 insertions(+), 8 deletions(-) diff --git a/fs/xfs/xfs_buf.c b/fs/xfs/xfs_buf.c index 517932675b12c..6a2c942372fc6 100644 --- a/fs/xfs/xfs_buf.c +++ b/fs/xfs/xfs_buf.c @@ -14,6 +14,7 @@ #include "xfs_mount.h" #include "xfs_trace.h" #include "xfs_log.h" +#include "xfs_log_recover.h" #include "xfs_trans.h" #include "xfs_buf_item.h" #include "xfs_errortag.h" @@ -1207,6 +1208,15 @@ xfs_buf_ioend( if (read) goto out_finish; + /* + * If this is a log recovery buffer, we aren't doing transactional IO + * yet so we need to let it handle IO completions. + */ + if (bp->b_flags & _XBF_LOGRECOVERY) { + xlog_recover_iodone(bp); + return; + } + if (bp->b_flags & _XBF_INODES) { xfs_buf_inode_iodone(bp); return; diff --git a/fs/xfs/xfs_buf.h b/fs/xfs/xfs_buf.h index c1d0843206dd6..30dabc5bae96d 100644 --- a/fs/xfs/xfs_buf.h +++ b/fs/xfs/xfs_buf.h @@ -33,6 +33,7 @@ /* buffer type flags for write callbacks */ #define _XBF_INODES (1 << 16)/* inode buffer */ #define _XBF_DQUOTS (1 << 17)/* dquot buffer */ +#define _XBF_LOGRECOVERY (1 << 18)/* log recovery buffer */ /* flags used only internally */ #define _XBF_PAGES (1 << 20)/* backed by refcounted pages */ @@ -56,6 +57,7 @@ typedef unsigned int xfs_buf_flags_t; { XBF_WRITE_FAIL, "WRITE_FAIL" }, \ { _XBF_INODES, "INODES" }, \ { _XBF_DQUOTS, "DQUOTS" }, \ + { _XBF_LOGRECOVERY, "LOG_RECOVERY" }, \ { _XBF_PAGES, "PAGES" }, \ { _XBF_KMEM, "KMEM" }, \ { _XBF_DELWRI_Q, "DELWRI_Q" }, \ diff --git a/fs/xfs/xfs_buf_item_recover.c b/fs/xfs/xfs_buf_item_recover.c index 04faa7310c4f0..74c851f60eeeb 100644 --- a/fs/xfs/xfs_buf_item_recover.c +++ b/fs/xfs/xfs_buf_item_recover.c @@ -419,8 +419,7 @@ xlog_recover_validate_buf_type( if (bp->b_ops) { struct xfs_buf_log_item *bip; - ASSERT(!bp->b_iodone || bp->b_iodone == xlog_recover_iodone); - bp->b_iodone = xlog_recover_iodone; + bp->b_flags |= _XBF_LOGRECOVERY; xfs_buf_item_init(bp, mp); bip = bp->b_log_item; bip->bli_item.li_lsn = current_lsn; @@ -963,7 +962,7 @@ xlog_recover_buf_commit_pass2( error = xfs_bwrite(bp); } else { ASSERT(bp->b_mount == mp); - bp->b_iodone = xlog_recover_iodone; + bp->b_flags |= _XBF_LOGRECOVERY; xfs_buf_delwri_queue(bp, buffer_list); } diff --git a/fs/xfs/xfs_dquot_item_recover.c b/fs/xfs/xfs_dquot_item_recover.c index 3400be4c88f08..f9ea9f55aa7cc 100644 --- a/fs/xfs/xfs_dquot_item_recover.c +++ b/fs/xfs/xfs_dquot_item_recover.c @@ -153,7 +153,7 @@ xlog_recover_dquot_commit_pass2( ASSERT(dq_f->qlf_size == 2); ASSERT(bp->b_mount == mp); - bp->b_iodone = xlog_recover_iodone; + bp->b_flags |= _XBF_LOGRECOVERY; xfs_buf_delwri_queue(bp, buffer_list); out_release: diff --git a/fs/xfs/xfs_inode_item_recover.c b/fs/xfs/xfs_inode_item_recover.c index dc3e26ff16c90..5e0d291835b35 100644 --- a/fs/xfs/xfs_inode_item_recover.c +++ b/fs/xfs/xfs_inode_item_recover.c @@ -376,7 +376,7 @@ out_owner_change: xfs_dinode_calc_crc(log->l_mp, dip); ASSERT(bp->b_mount == mp); - bp->b_iodone = xlog_recover_iodone; + bp->b_flags |= _XBF_LOGRECOVERY; xfs_buf_delwri_queue(bp, buffer_list); out_release: diff --git a/fs/xfs/xfs_log_recover.c b/fs/xfs/xfs_log_recover.c index ec015df55b77a..52a65a74208ff 100644 --- a/fs/xfs/xfs_log_recover.c +++ b/fs/xfs/xfs_log_recover.c @@ -287,9 +287,8 @@ xlog_recover_iodone( if (bp->b_log_item) xfs_buf_item_relse(bp); ASSERT(bp->b_log_item == NULL); - - bp->b_iodone = NULL; - xfs_buf_ioend(bp); + bp->b_flags &= ~_XBF_LOGRECOVERY; + xfs_buf_ioend_finish(bp); } /* -- GitLab From b01d1461ae6d98165cddab6f7219b459e2ac413d Mon Sep 17 00:00:00 2001 From: Dave Chinner Date: Mon, 29 Jun 2020 14:48:47 -0700 Subject: [PATCH 0295/1476] xfs: call xfs_buf_iodone directly All unmarked dirty buffers should be in the AIL and have log items attached to them. Hence when they are written, we will run a callback to remove the item from the AIL if appropriate. Now that we've handled inode and dquot buffers, all remaining calls are to xfs_buf_iodone() and so we can hard code this rather than use an indirect call. Signed-off-by: Dave Chinner Reviewed-by: Darrick J. Wong Reviewed-by: Amir Goldstein Reviewed-by: Brian Foster Signed-off-by: Darrick J. Wong --- fs/xfs/xfs_buf.c | 24 ++++++++---------------- fs/xfs/xfs_buf.h | 6 +----- fs/xfs/xfs_buf_item.c | 40 ++++++++++------------------------------ fs/xfs/xfs_buf_item.h | 4 ++-- fs/xfs/xfs_trans_buf.c | 13 +++---------- 5 files changed, 24 insertions(+), 63 deletions(-) diff --git a/fs/xfs/xfs_buf.c b/fs/xfs/xfs_buf.c index 6a2c942372fc6..dda0c94458797 100644 --- a/fs/xfs/xfs_buf.c +++ b/fs/xfs/xfs_buf.c @@ -658,7 +658,6 @@ found: */ if (bp->b_flags & XBF_STALE) { ASSERT((bp->b_flags & _XBF_DELWRI_Q) == 0); - ASSERT(bp->b_iodone == NULL); bp->b_flags &= _XBF_KMEM | _XBF_PAGES; bp->b_ops = NULL; } @@ -1194,10 +1193,13 @@ xfs_buf_ioend( if (!bp->b_error && bp->b_io_error) xfs_buf_ioerror(bp, bp->b_io_error); - /* Only validate buffers that were read without errors */ - if (read && !bp->b_error && bp->b_ops) { - ASSERT(!bp->b_iodone); - bp->b_ops->verify_read(bp); + if (read) { + if (!bp->b_error && bp->b_ops) + bp->b_ops->verify_read(bp); + if (!bp->b_error) + bp->b_flags |= XBF_DONE; + xfs_buf_ioend_finish(bp); + return; } if (!bp->b_error) { @@ -1205,9 +1207,6 @@ xfs_buf_ioend( bp->b_flags |= XBF_DONE; } - if (read) - goto out_finish; - /* * If this is a log recovery buffer, we aren't doing transactional IO * yet so we need to let it handle IO completions. @@ -1226,14 +1225,7 @@ xfs_buf_ioend( xfs_buf_dquot_iodone(bp); return; } - - if (bp->b_iodone) { - (*(bp->b_iodone))(bp); - return; - } - -out_finish: - xfs_buf_ioend_finish(bp); + xfs_buf_iodone(bp); } static void diff --git a/fs/xfs/xfs_buf.h b/fs/xfs/xfs_buf.h index 30dabc5bae96d..755b652e695ac 100644 --- a/fs/xfs/xfs_buf.h +++ b/fs/xfs/xfs_buf.h @@ -18,6 +18,7 @@ /* * Base types */ +struct xfs_buf; #define XFS_BUF_DADDR_NULL ((xfs_daddr_t) (-1LL)) @@ -102,10 +103,6 @@ typedef struct xfs_buftarg { struct ratelimit_state bt_ioerror_rl; } xfs_buftarg_t; -struct xfs_buf; -typedef void (*xfs_buf_iodone_t)(struct xfs_buf *); - - #define XB_PAGES 2 struct xfs_buf_map { @@ -158,7 +155,6 @@ typedef struct xfs_buf { xfs_buftarg_t *b_target; /* buffer target (device) */ void *b_addr; /* virtual address of buffer */ struct work_struct b_ioend_work; - xfs_buf_iodone_t b_iodone; /* I/O completion function */ struct completion b_iowait; /* queue for I/O waiters */ struct xfs_buf_log_item *b_log_item; struct list_head b_li_list; /* Log items list head */ diff --git a/fs/xfs/xfs_buf_item.c b/fs/xfs/xfs_buf_item.c index a42cdf9ccc47d..d87ae6363a130 100644 --- a/fs/xfs/xfs_buf_item.c +++ b/fs/xfs/xfs_buf_item.c @@ -460,7 +460,6 @@ xfs_buf_item_unpin( xfs_buf_do_callbacks(bp); bp->b_log_item = NULL; list_del_init(&bp->b_li_list); - bp->b_iodone = NULL; } else { xfs_trans_ail_delete(lip, SHUTDOWN_LOG_IO_ERROR); xfs_buf_item_relse(bp); @@ -936,11 +935,7 @@ xfs_buf_item_free( } /* - * This is called when the buf log item is no longer needed. It should - * free the buf log item associated with the given buffer and clear - * the buffer's pointer to the buf log item. If there are no more - * items in the list, clear the b_iodone field of the buffer (see - * xfs_buf_attach_iodone() below). + * xfs_buf_item_relse() is called when the buf log item is no longer needed. */ void xfs_buf_item_relse( @@ -952,9 +947,6 @@ xfs_buf_item_relse( ASSERT(!test_bit(XFS_LI_IN_AIL, &bip->bli_item.li_flags)); bp->b_log_item = NULL; - if (list_empty(&bp->b_li_list)) - bp->b_iodone = NULL; - xfs_buf_rele(bp); xfs_buf_item_free(bip); } @@ -962,10 +954,7 @@ xfs_buf_item_relse( /* * Add the given log item with its callback to the list of callbacks - * to be called when the buffer's I/O completes. If it is not set - * already, set the buffer's b_iodone() routine to be - * xfs_buf_iodone_callbacks() and link the log item into the list of - * items rooted at b_li_list. + * to be called when the buffer's I/O completes. */ void xfs_buf_attach_iodone( @@ -977,10 +966,6 @@ xfs_buf_attach_iodone( lip->li_cb = cb; list_add_tail(&lip->li_bio_list, &bp->b_li_list); - - ASSERT(bp->b_iodone == NULL || - bp->b_iodone == xfs_buf_iodone_callbacks); - bp->b_iodone = xfs_buf_iodone_callbacks; } /* @@ -1096,7 +1081,6 @@ xfs_buf_iodone_callback_error( goto out_stale; trace_xfs_buf_item_iodone_async(bp, _RET_IP_); - ASSERT(bp->b_iodone != NULL); cfg = xfs_error_get_cfg(mp, XFS_ERR_METADATA, bp->b_error); @@ -1182,28 +1166,24 @@ xfs_buf_run_callbacks( xfs_buf_do_callbacks(bp); bp->b_log_item = NULL; list_del_init(&bp->b_li_list); - bp->b_iodone = NULL; } /* - * This is the iodone() function for buffers which have had callbacks attached - * to them by xfs_buf_attach_iodone(). We need to iterate the items on the - * callback list, mark the buffer as having no more callbacks and then push the - * buffer through IO completion processing. + * Inode buffer iodone callback function. */ void -xfs_buf_iodone_callbacks( +xfs_buf_inode_iodone( struct xfs_buf *bp) { xfs_buf_run_callbacks(bp); - xfs_buf_ioend(bp); + xfs_buf_ioend_finish(bp); } /* - * Inode buffer iodone callback function. + * Dquot buffer iodone callback function. */ void -xfs_buf_inode_iodone( +xfs_buf_dquot_iodone( struct xfs_buf *bp) { xfs_buf_run_callbacks(bp); @@ -1211,10 +1191,10 @@ xfs_buf_inode_iodone( } /* - * Dquot buffer iodone callback function. + * Dirty buffer iodone callback function. */ void -xfs_buf_dquot_iodone( +xfs_buf_iodone( struct xfs_buf *bp) { xfs_buf_run_callbacks(bp); @@ -1229,7 +1209,7 @@ xfs_buf_dquot_iodone( * care of cleaning up the buffer itself. */ void -xfs_buf_iodone( +xfs_buf_item_iodone( struct xfs_buf *bp, struct xfs_log_item *lip) { diff --git a/fs/xfs/xfs_buf_item.h b/fs/xfs/xfs_buf_item.h index 27d13d29b5bbb..610cd00193289 100644 --- a/fs/xfs/xfs_buf_item.h +++ b/fs/xfs/xfs_buf_item.h @@ -57,10 +57,10 @@ bool xfs_buf_item_dirty_format(struct xfs_buf_log_item *); void xfs_buf_attach_iodone(struct xfs_buf *, void(*)(struct xfs_buf *, struct xfs_log_item *), struct xfs_log_item *); -void xfs_buf_iodone_callbacks(struct xfs_buf *); -void xfs_buf_iodone(struct xfs_buf *, struct xfs_log_item *); +void xfs_buf_item_iodone(struct xfs_buf *, struct xfs_log_item *); void xfs_buf_inode_iodone(struct xfs_buf *); void xfs_buf_dquot_iodone(struct xfs_buf *); +void xfs_buf_iodone(struct xfs_buf *); bool xfs_buf_log_check_iovec(struct xfs_log_iovec *iovec); extern kmem_zone_t *xfs_buf_item_zone; diff --git a/fs/xfs/xfs_trans_buf.c b/fs/xfs/xfs_trans_buf.c index 93d62cb864c15..6752676b94fe7 100644 --- a/fs/xfs/xfs_trans_buf.c +++ b/fs/xfs/xfs_trans_buf.c @@ -465,24 +465,17 @@ xfs_trans_dirty_buf( ASSERT(bp->b_transp == tp); ASSERT(bip != NULL); - ASSERT(bp->b_iodone == NULL || - bp->b_iodone == xfs_buf_iodone_callbacks); /* * Mark the buffer as needing to be written out eventually, * and set its iodone function to remove the buffer's buf log * item from the AIL and free it when the buffer is flushed - * to disk. See xfs_buf_attach_iodone() for more details - * on li_cb and xfs_buf_iodone_callbacks(). - * If we end up aborting this transaction, we trap this buffer - * inside the b_bdstrat callback so that this won't get written to - * disk. + * to disk. */ bp->b_flags |= XBF_DONE; ASSERT(atomic_read(&bip->bli_refcount) > 0); - bp->b_iodone = xfs_buf_iodone_callbacks; - bip->bli_item.li_cb = xfs_buf_iodone; + bip->bli_item.li_cb = xfs_buf_item_iodone; /* * If we invalidated the buffer within this transaction, then @@ -651,7 +644,7 @@ xfs_trans_stale_inode_buf( ASSERT(atomic_read(&bip->bli_refcount) > 0); bip->bli_flags |= XFS_BLI_STALE_INODE; - bip->bli_item.li_cb = xfs_buf_iodone; + bip->bli_item.li_cb = xfs_buf_item_iodone; bp->b_flags |= _XBF_INODES; xfs_trans_buf_set_type(tp, bp, XFS_BLFT_DINO_BUF); } -- GitLab From a7e134ef37172fd4f13bbb11f8f440c807ba294b Mon Sep 17 00:00:00 2001 From: Dave Chinner Date: Mon, 29 Jun 2020 14:48:47 -0700 Subject: [PATCH 0296/1476] xfs: clean up whacky buffer log item list reinit When we've emptied the buffer log item list, it does a list_del_init on itself to reset it's pointers to itself. This is unnecessary as the list is already empty at this point - it was a left-over fragment from the list_head conversion of the buffer log item list. Remove them. Signed-off-by: Dave Chinner Reviewed-by: Darrick J. Wong Reviewed-by: Christoph Hellwig Reviewed-by: Brian Foster Signed-off-by: Darrick J. Wong --- fs/xfs/xfs_buf_item.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/fs/xfs/xfs_buf_item.c b/fs/xfs/xfs_buf_item.c index d87ae6363a130..5b3cd5e90947c 100644 --- a/fs/xfs/xfs_buf_item.c +++ b/fs/xfs/xfs_buf_item.c @@ -459,7 +459,6 @@ xfs_buf_item_unpin( if (bip->bli_flags & XFS_BLI_STALE_INODE) { xfs_buf_do_callbacks(bp); bp->b_log_item = NULL; - list_del_init(&bp->b_li_list); } else { xfs_trans_ail_delete(lip, SHUTDOWN_LOG_IO_ERROR); xfs_buf_item_relse(bp); @@ -1165,7 +1164,6 @@ xfs_buf_run_callbacks( xfs_buf_do_callbacks(bp); bp->b_log_item = NULL; - list_del_init(&bp->b_li_list); } /* -- GitLab From aac855ab1a98d9c20762047f26af47d391c3ba7a Mon Sep 17 00:00:00 2001 From: Dave Chinner Date: Mon, 29 Jun 2020 14:48:48 -0700 Subject: [PATCH 0297/1476] xfs: make inode IO completion buffer centric Having different io completion callbacks for different inode states makes things complex. We can detect if the inode is stale via the XFS_ISTALE flag in IO completion, so we don't need a special callback just for this. This means inodes only have a single iodone callback, and inode IO completion is entirely buffer centric at this point. Hence we no longer need to use a log item callback at all as we can just call xfs_iflush_done() directly from the buffer completions and walk the buffer log item list to complete the all inodes under IO. Signed-off-by: Dave Chinner Reviewed-by: Christoph Hellwig Reviewed-by: Darrick J. Wong Reviewed-by: Brian Foster Signed-off-by: Darrick J. Wong --- fs/xfs/xfs_buf_item.c | 35 ++++++++++++++++++---- fs/xfs/xfs_inode.c | 6 ++-- fs/xfs/xfs_inode_item.c | 65 ++++++++++++++--------------------------- fs/xfs/xfs_inode_item.h | 5 ++-- 4 files changed, 56 insertions(+), 55 deletions(-) diff --git a/fs/xfs/xfs_buf_item.c b/fs/xfs/xfs_buf_item.c index 5b3cd5e90947c..a4e416af5c614 100644 --- a/fs/xfs/xfs_buf_item.c +++ b/fs/xfs/xfs_buf_item.c @@ -13,6 +13,8 @@ #include "xfs_mount.h" #include "xfs_trans.h" #include "xfs_buf_item.h" +#include "xfs_inode.h" +#include "xfs_inode_item.h" #include "xfs_trans_priv.h" #include "xfs_trace.h" #include "xfs_log.h" @@ -457,7 +459,8 @@ xfs_buf_item_unpin( * the AIL lock. */ if (bip->bli_flags & XFS_BLI_STALE_INODE) { - xfs_buf_do_callbacks(bp); + lip->li_cb(bp, lip); + xfs_iflush_done(bp); bp->b_log_item = NULL; } else { xfs_trans_ail_delete(lip, SHUTDOWN_LOG_IO_ERROR); @@ -1141,8 +1144,8 @@ out_stale: return false; } -static void -xfs_buf_run_callbacks( +static inline bool +xfs_buf_had_callback_errors( struct xfs_buf *bp) { @@ -1152,7 +1155,7 @@ xfs_buf_run_callbacks( * appropriate action. */ if (bp->b_error && xfs_buf_iodone_callback_error(bp)) - return; + return true; /* * Successful IO or permanent error. Either way, we can clear the @@ -1161,7 +1164,16 @@ xfs_buf_run_callbacks( bp->b_last_error = 0; bp->b_retries = 0; bp->b_first_retry_time = 0; + return false; +} +static void +xfs_buf_run_callbacks( + struct xfs_buf *bp) +{ + + if (xfs_buf_had_callback_errors(bp)) + return; xfs_buf_do_callbacks(bp); bp->b_log_item = NULL; } @@ -1173,7 +1185,20 @@ void xfs_buf_inode_iodone( struct xfs_buf *bp) { - xfs_buf_run_callbacks(bp); + struct xfs_buf_log_item *blip = bp->b_log_item; + struct xfs_log_item *lip; + + if (xfs_buf_had_callback_errors(bp)) + return; + + /* If there is a buf_log_item attached, run its callback */ + if (blip) { + lip = &blip->bli_item; + lip->li_cb(bp, lip); + bp->b_log_item = NULL; + } + + xfs_iflush_done(bp); xfs_buf_ioend_finish(bp); } diff --git a/fs/xfs/xfs_inode.c b/fs/xfs/xfs_inode.c index 4621d67f3428b..721b8420be046 100644 --- a/fs/xfs/xfs_inode.c +++ b/fs/xfs/xfs_inode.c @@ -2679,7 +2679,6 @@ xfs_ifree_cluster( list_for_each_entry(lip, &bp->b_li_list, li_bio_list) { if (lip->li_type == XFS_LI_INODE) { iip = (struct xfs_inode_log_item *)lip; - lip->li_cb = xfs_istale_done; xfs_trans_ail_copy_lsn(mp->m_ail, &iip->ili_flush_lsn, &iip->ili_item.li_lsn); @@ -2712,8 +2711,7 @@ xfs_ifree_cluster( xfs_trans_ail_copy_lsn(mp->m_ail, &iip->ili_flush_lsn, &iip->ili_item.li_lsn); - xfs_buf_attach_iodone(bp, xfs_istale_done, - &iip->ili_item); + xfs_buf_attach_iodone(bp, NULL, &iip->ili_item); if (ip != free_ip) xfs_iunlock(ip, XFS_ILOCK_EXCL); @@ -3863,7 +3861,7 @@ flush_out: * the flush lock. */ bp->b_flags |= _XBF_INODES; - xfs_buf_attach_iodone(bp, xfs_iflush_done, &iip->ili_item); + xfs_buf_attach_iodone(bp, NULL, &iip->ili_item); /* generate the checksum. */ xfs_dinode_calc_crc(mp, dip); diff --git a/fs/xfs/xfs_inode_item.c b/fs/xfs/xfs_inode_item.c index 6ef9cbcfc94a7..7049f2ae8d186 100644 --- a/fs/xfs/xfs_inode_item.c +++ b/fs/xfs/xfs_inode_item.c @@ -668,40 +668,34 @@ xfs_inode_item_destroy( */ void xfs_iflush_done( - struct xfs_buf *bp, - struct xfs_log_item *lip) + struct xfs_buf *bp) { struct xfs_inode_log_item *iip; - struct xfs_log_item *blip, *n; - struct xfs_ail *ailp = lip->li_ailp; + struct xfs_log_item *lip, *n; + struct xfs_ail *ailp = bp->b_mount->m_ail; int need_ail = 0; LIST_HEAD(tmp); /* - * Scan the buffer IO completions for other inodes being completed and - * attach them to the current inode log item. + * Pull the attached inodes from the buffer one at a time and take the + * appropriate action on them. */ - - list_add_tail(&lip->li_bio_list, &tmp); - - list_for_each_entry_safe(blip, n, &bp->b_li_list, li_bio_list) { - if (lip->li_cb != xfs_iflush_done) + list_for_each_entry_safe(lip, n, &bp->b_li_list, li_bio_list) { + iip = INODE_ITEM(lip); + if (xfs_iflags_test(iip->ili_inode, XFS_ISTALE)) { + list_del_init(&lip->li_bio_list); + xfs_iflush_abort(iip->ili_inode); continue; + } - list_move_tail(&blip->li_bio_list, &tmp); + list_move_tail(&lip->li_bio_list, &tmp); /* Do an unlocked check for needing the AIL lock. */ - iip = INODE_ITEM(blip); - if (blip->li_lsn == iip->ili_flush_lsn || - test_bit(XFS_LI_FAILED, &blip->li_flags)) + if (lip->li_lsn == iip->ili_flush_lsn || + test_bit(XFS_LI_FAILED, &lip->li_flags)) need_ail++; } - - /* make sure we capture the state of the initial inode. */ - iip = INODE_ITEM(lip); - if (lip->li_lsn == iip->ili_flush_lsn || - test_bit(XFS_LI_FAILED, &lip->li_flags)) - need_ail++; + ASSERT(list_empty(&bp->b_li_list)); /* * We only want to pull the item from the AIL if it is actually there @@ -713,19 +707,13 @@ xfs_iflush_done( /* this is an opencoded batch version of xfs_trans_ail_delete */ spin_lock(&ailp->ail_lock); - list_for_each_entry(blip, &tmp, li_bio_list) { - if (blip->li_lsn == INODE_ITEM(blip)->ili_flush_lsn) { - /* - * xfs_ail_update_finish() only cares about the - * lsn of the first tail item removed, any - * others will be at the same or higher lsn so - * we just ignore them. - */ - xfs_lsn_t lsn = xfs_ail_delete_one(ailp, blip); + list_for_each_entry(lip, &tmp, li_bio_list) { + if (lip->li_lsn == INODE_ITEM(lip)->ili_flush_lsn) { + xfs_lsn_t lsn = xfs_ail_delete_one(ailp, lip); if (!tail_lsn && lsn) tail_lsn = lsn; } else { - xfs_clear_li_failed(blip); + xfs_clear_li_failed(lip); } } xfs_ail_update_finish(ailp, tail_lsn); @@ -736,9 +724,9 @@ xfs_iflush_done( * ili_last_fields bits now that we know that the data corresponding to * them is safely on disk. */ - list_for_each_entry_safe(blip, n, &tmp, li_bio_list) { - list_del_init(&blip->li_bio_list); - iip = INODE_ITEM(blip); + list_for_each_entry_safe(lip, n, &tmp, li_bio_list) { + list_del_init(&lip->li_bio_list); + iip = INODE_ITEM(lip); spin_lock(&iip->ili_lock); iip->ili_last_fields = 0; @@ -746,7 +734,6 @@ xfs_iflush_done( xfs_ifunlock(iip->ili_inode); } - list_del(&tmp); } /* @@ -779,14 +766,6 @@ xfs_iflush_abort( xfs_ifunlock(ip); } -void -xfs_istale_done( - struct xfs_buf *bp, - struct xfs_log_item *lip) -{ - xfs_iflush_abort(INODE_ITEM(lip)->ili_inode); -} - /* * convert an xfs_inode_log_format struct from the old 32 bit version * (which can have different field alignments) to the native 64 bit version diff --git a/fs/xfs/xfs_inode_item.h b/fs/xfs/xfs_inode_item.h index 4a10a1b92ee99..048b5e7dee901 100644 --- a/fs/xfs/xfs_inode_item.h +++ b/fs/xfs/xfs_inode_item.h @@ -36,15 +36,14 @@ struct xfs_inode_log_item { xfs_lsn_t ili_last_lsn; /* lsn at last transaction */ }; -static inline int xfs_inode_clean(xfs_inode_t *ip) +static inline int xfs_inode_clean(struct xfs_inode *ip) { return !ip->i_itemp || !(ip->i_itemp->ili_fields & XFS_ILOG_ALL); } extern void xfs_inode_item_init(struct xfs_inode *, struct xfs_mount *); extern void xfs_inode_item_destroy(struct xfs_inode *); -extern void xfs_iflush_done(struct xfs_buf *, struct xfs_log_item *); -extern void xfs_istale_done(struct xfs_buf *, struct xfs_log_item *); +extern void xfs_iflush_done(struct xfs_buf *); extern void xfs_iflush_abort(struct xfs_inode *); extern int xfs_inode_item_format_convert(xfs_log_iovec_t *, struct xfs_inode_log_format *); -- GitLab From 6f5de1808e3663917b5c682e2d91d95645ce2df2 Mon Sep 17 00:00:00 2001 From: Dave Chinner Date: Mon, 29 Jun 2020 14:48:59 -0700 Subject: [PATCH 0298/1476] xfs: use direct calls for dquot IO completion Similar to inodes, we can call the dquot IO completion functions directly from the buffer completion code, removing another user of log item callbacks for IO completion processing. Signed-off-by: Dave Chinner Reviewed-by: Brian Foster Reviewed-by: Darrick J. Wong Signed-off-by: Darrick J. Wong --- fs/xfs/xfs_buf_item.c | 18 +++++++++++++++++- fs/xfs/xfs_dquot.c | 18 ++++++++++++++---- fs/xfs/xfs_quota.h | 9 +++++++++ 3 files changed, 40 insertions(+), 5 deletions(-) diff --git a/fs/xfs/xfs_buf_item.c b/fs/xfs/xfs_buf_item.c index a4e416af5c614..f46e5ec28111c 100644 --- a/fs/xfs/xfs_buf_item.c +++ b/fs/xfs/xfs_buf_item.c @@ -15,6 +15,9 @@ #include "xfs_buf_item.h" #include "xfs_inode.h" #include "xfs_inode_item.h" +#include "xfs_quota.h" +#include "xfs_dquot_item.h" +#include "xfs_dquot.h" #include "xfs_trans_priv.h" #include "xfs_trace.h" #include "xfs_log.h" @@ -1209,7 +1212,20 @@ void xfs_buf_dquot_iodone( struct xfs_buf *bp) { - xfs_buf_run_callbacks(bp); + struct xfs_buf_log_item *blip = bp->b_log_item; + struct xfs_log_item *lip; + + if (xfs_buf_had_callback_errors(bp)) + return; + + /* a newly allocated dquot buffer might have a log item attached */ + if (blip) { + lip = &blip->bli_item; + lip->li_cb(bp, lip); + bp->b_log_item = NULL; + } + + xfs_dquot_done(bp); xfs_buf_ioend_finish(bp); } diff --git a/fs/xfs/xfs_dquot.c b/fs/xfs/xfs_dquot.c index 2e2146fa0914c..403bc4e9f21ff 100644 --- a/fs/xfs/xfs_dquot.c +++ b/fs/xfs/xfs_dquot.c @@ -1048,9 +1048,8 @@ xfs_qm_dqrele( * from the AIL if it has not been re-logged, and unlocking the dquot's * flush lock. This behavior is very similar to that of inodes.. */ -STATIC void +static void xfs_qm_dqflush_done( - struct xfs_buf *bp, struct xfs_log_item *lip) { struct xfs_dq_logitem *qip = (struct xfs_dq_logitem *)lip; @@ -1091,6 +1090,18 @@ xfs_qm_dqflush_done( xfs_dqfunlock(dqp); } +void +xfs_dquot_done( + struct xfs_buf *bp) +{ + struct xfs_log_item *lip, *n; + + list_for_each_entry_safe(lip, n, &bp->b_li_list, li_bio_list) { + list_del_init(&lip->li_bio_list); + xfs_qm_dqflush_done(lip); + } +} + /* * Write a modified dquot to disk. * The dquot must be locked and the flush lock too taken by caller. @@ -1180,8 +1191,7 @@ xfs_qm_dqflush( * AIL and release the flush lock once the dquot is synced to disk. */ bp->b_flags |= _XBF_DQUOTS; - xfs_buf_attach_iodone(bp, xfs_qm_dqflush_done, - &dqp->q_logitem.qli_item); + xfs_buf_attach_iodone(bp, NULL, &dqp->q_logitem.qli_item); /* * If the buffer is pinned then push on the log so we won't diff --git a/fs/xfs/xfs_quota.h b/fs/xfs/xfs_quota.h index aa8fc1f55fbd7..c92ae5e02ce86 100644 --- a/fs/xfs/xfs_quota.h +++ b/fs/xfs/xfs_quota.h @@ -13,6 +13,7 @@ */ struct xfs_trans; +struct xfs_buf; /* * This check is done typically without holding the inode lock; @@ -107,6 +108,8 @@ extern void xfs_qm_mount_quotas(struct xfs_mount *); extern void xfs_qm_unmount(struct xfs_mount *); extern void xfs_qm_unmount_quotas(struct xfs_mount *); +void xfs_dquot_done(struct xfs_buf *); + #else static inline int xfs_qm_vop_dqalloc(struct xfs_inode *ip, kuid_t kuid, kgid_t kgid, @@ -148,6 +151,12 @@ static inline int xfs_trans_reserve_quota_bydquots(struct xfs_trans *tp, #define xfs_qm_mount_quotas(mp) #define xfs_qm_unmount(mp) #define xfs_qm_unmount_quotas(mp) + +static inline void xfs_dquot_done(struct xfs_buf *bp) +{ + return; +} + #endif /* CONFIG_XFS_QUOTA */ #define xfs_trans_unreserve_quota_nblks(tp, ip, nblks, ninos, flags) \ -- GitLab From c2615d620a852adf43d425dea81eb854c8673cfd Mon Sep 17 00:00:00 2001 From: Dinghao Liu Date: Wed, 20 May 2020 16:47:56 +0800 Subject: [PATCH 0299/1476] PCI: dwc: pci-dra7xx: Fix runtime PM imbalance on error pm_runtime_get_sync() increments the runtime PM usage counter even it returns an error code. Thus a pairing decrement is needed on the error handling path to keep the counter balanced. Link: https://lore.kernel.org/r/20200520084756.31620-1-dinghao.liu@zju.edu.cn Signed-off-by: Dinghao Liu Signed-off-by: Lorenzo Pieralisi --- drivers/pci/controller/dwc/pci-dra7xx.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/pci/controller/dwc/pci-dra7xx.c b/drivers/pci/controller/dwc/pci-dra7xx.c index 6184ebc9392db..01d821e98f0c6 100644 --- a/drivers/pci/controller/dwc/pci-dra7xx.c +++ b/drivers/pci/controller/dwc/pci-dra7xx.c @@ -998,9 +998,8 @@ static int __init dra7xx_pcie_probe(struct platform_device *pdev) return 0; err_gpio: - pm_runtime_put(dev); - err_get_sync: + pm_runtime_put(dev); pm_runtime_disable(dev); dra7xx_pcie_disable_phy(dra7xx); -- GitLab From 7a790087c1ad110d1f4019021342d9ec55aead14 Mon Sep 17 00:00:00 2001 From: Dinghao Liu Date: Wed, 20 May 2020 17:02:53 +0800 Subject: [PATCH 0300/1476] PCI: cadence: Fix runtime PM imbalance on error pm_runtime_get_sync() increments the runtime PM usage counter even it returns an error code. Thus a pairing decrement is needed on the error handling path to keep the counter balanced. Link: https://lore.kernel.org/r/20200520090253.2761-1-dinghao.liu@zju.edu.cn Signed-off-by: Dinghao Liu Signed-off-by: Lorenzo Pieralisi --- drivers/pci/controller/cadence/pcie-cadence-plat.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/pci/controller/cadence/pcie-cadence-plat.c b/drivers/pci/controller/cadence/pcie-cadence-plat.c index f5c6bf6dfcb8b..33c3868e6dbd3 100644 --- a/drivers/pci/controller/cadence/pcie-cadence-plat.c +++ b/drivers/pci/controller/cadence/pcie-cadence-plat.c @@ -115,9 +115,8 @@ static int cdns_plat_pcie_probe(struct platform_device *pdev) } err_init: - pm_runtime_put_sync(dev); - err_get_sync: + pm_runtime_put_sync(dev); pm_runtime_disable(dev); cdns_pcie_disable_phy(cdns_plat_pcie->pcie); phy_count = cdns_plat_pcie->pcie->phy_count; -- GitLab From cb52a40202420d3886b84ea13dba699c9da13eb0 Mon Sep 17 00:00:00 2001 From: Dinghao Liu Date: Tue, 7 Jul 2020 13:50:00 +0800 Subject: [PATCH 0301/1476] PCI: qcom: Fix runtime PM imbalance on error pm_runtime_get_sync() increments the runtime PM usage counter even when it returns an error code, thus a matching decrement is needed on the error handling path to keep the counter balanced. Link: https://lore.kernel.org/r/20200707055000.9453-1-dinghao.liu@zju.edu.cn Signed-off-by: Dinghao Liu Signed-off-by: Lorenzo Pieralisi --- drivers/pci/controller/dwc/pcie-qcom.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/pci/controller/dwc/pcie-qcom.c b/drivers/pci/controller/dwc/pcie-qcom.c index 138e1a2d21ccd..12abdfbff5ca6 100644 --- a/drivers/pci/controller/dwc/pcie-qcom.c +++ b/drivers/pci/controller/dwc/pcie-qcom.c @@ -1339,10 +1339,8 @@ static int qcom_pcie_probe(struct platform_device *pdev) pm_runtime_enable(dev); ret = pm_runtime_get_sync(dev); - if (ret < 0) { - pm_runtime_disable(dev); - return ret; - } + if (ret < 0) + goto err_pm_runtime_put; pci->dev = dev; pci->ops = &dw_pcie_ops; -- GitLab From fec671cd350ff3ef737a83236ab2c6d3e4b8d600 Mon Sep 17 00:00:00 2001 From: Dave Chinner Date: Mon, 29 Jun 2020 14:49:14 -0700 Subject: [PATCH 0302/1476] xfs: clean up the buffer iodone callback functions Now that we've sorted inode and dquot buffers, we can apply the same cleanups to dirty buffers with buffer log items. They only have one callback, too, so we don't need the log item callback. Collapse the iodone functions and remove all the now unnecessary infrastructure around callback processing. Signed-off-by: Dave Chinner Reviewed-by: Christoph Hellwig Reviewed-by: Darrick J. Wong Reviewed-by: Brian Foster Signed-off-by: Darrick J. Wong --- fs/xfs/xfs_buf_item.c | 140 +++++++++-------------------------------- fs/xfs/xfs_buf_item.h | 1 - fs/xfs/xfs_trans_buf.c | 2 - 3 files changed, 29 insertions(+), 114 deletions(-) diff --git a/fs/xfs/xfs_buf_item.c b/fs/xfs/xfs_buf_item.c index f46e5ec28111c..0ece5de9dd711 100644 --- a/fs/xfs/xfs_buf_item.c +++ b/fs/xfs/xfs_buf_item.c @@ -30,7 +30,7 @@ static inline struct xfs_buf_log_item *BUF_ITEM(struct xfs_log_item *lip) return container_of(lip, struct xfs_buf_log_item, bli_item); } -STATIC void xfs_buf_do_callbacks(struct xfs_buf *bp); +static void xfs_buf_item_done(struct xfs_buf *bp); /* Is this log iovec plausibly large enough to contain the buffer log format? */ bool @@ -462,9 +462,8 @@ xfs_buf_item_unpin( * the AIL lock. */ if (bip->bli_flags & XFS_BLI_STALE_INODE) { - lip->li_cb(bp, lip); + xfs_buf_item_done(bp); xfs_iflush_done(bp); - bp->b_log_item = NULL; } else { xfs_trans_ail_delete(lip, SHUTDOWN_LOG_IO_ERROR); xfs_buf_item_relse(bp); @@ -973,46 +972,6 @@ xfs_buf_attach_iodone( list_add_tail(&lip->li_bio_list, &bp->b_li_list); } -/* - * We can have many callbacks on a buffer. Running the callbacks individually - * can cause a lot of contention on the AIL lock, so we allow for a single - * callback to be able to scan the remaining items in bp->b_li_list for other - * items of the same type and callback to be processed in the first call. - * - * As a result, the loop walking the callback list below will also modify the - * list. it removes the first item from the list and then runs the callback. - * The loop then restarts from the new first item int the list. This allows the - * callback to scan and modify the list attached to the buffer and we don't - * have to care about maintaining a next item pointer. - */ -STATIC void -xfs_buf_do_callbacks( - struct xfs_buf *bp) -{ - struct xfs_buf_log_item *blip = bp->b_log_item; - struct xfs_log_item *lip; - - /* If there is a buf_log_item attached, run its callback */ - if (blip) { - lip = &blip->bli_item; - lip->li_cb(bp, lip); - } - - while (!list_empty(&bp->b_li_list)) { - lip = list_first_entry(&bp->b_li_list, struct xfs_log_item, - li_bio_list); - - /* - * Remove the item from the list, so we don't have any - * confusion if the item is added to another buf. - * Don't touch the log item after calling its - * callback, because it could have freed itself. - */ - list_del_init(&lip->li_bio_list); - lip->li_cb(bp, lip); - } -} - /* * Invoke the error state callback for each log item affected by the failed I/O. * @@ -1025,8 +984,8 @@ STATIC void xfs_buf_do_callbacks_fail( struct xfs_buf *bp) { + struct xfs_ail *ailp = bp->b_mount->m_ail; struct xfs_log_item *lip; - struct xfs_ail *ailp; /* * Buffer log item errors are handled directly by xfs_buf_item_push() @@ -1036,9 +995,6 @@ xfs_buf_do_callbacks_fail( if (list_empty(&bp->b_li_list)) return; - lip = list_first_entry(&bp->b_li_list, struct xfs_log_item, - li_bio_list); - ailp = lip->li_ailp; spin_lock(&ailp->ail_lock); list_for_each_entry(lip, &bp->b_li_list, li_bio_list) { if (lip->li_ops->iop_error) @@ -1051,22 +1007,11 @@ static bool xfs_buf_iodone_callback_error( struct xfs_buf *bp) { - struct xfs_buf_log_item *bip = bp->b_log_item; - struct xfs_log_item *lip; - struct xfs_mount *mp; + struct xfs_mount *mp = bp->b_mount; static ulong lasttime; static xfs_buftarg_t *lasttarg; struct xfs_error_cfg *cfg; - /* - * The failed buffer might not have a buf_log_item attached or the - * log_item list might be empty. Get the mp from the available - * xfs_log_item - */ - lip = list_first_entry_or_null(&bp->b_li_list, struct xfs_log_item, - li_bio_list); - mp = lip ? lip->li_mountp : bip->bli_item.li_mountp; - /* * If we've already decided to shutdown the filesystem because of * I/O errors, there's no point in giving this a retry. @@ -1171,14 +1116,27 @@ xfs_buf_had_callback_errors( } static void -xfs_buf_run_callbacks( +xfs_buf_item_done( struct xfs_buf *bp) { + struct xfs_buf_log_item *bip = bp->b_log_item; - if (xfs_buf_had_callback_errors(bp)) + if (!bip) return; - xfs_buf_do_callbacks(bp); + + /* + * If we are forcibly shutting down, this may well be off the AIL + * already. That's because we simulate the log-committed callbacks to + * unpin these buffers. Or we may never have put this item on AIL + * because of the transaction was aborted forcibly. + * xfs_trans_ail_delete() takes care of these. + * + * Either way, AIL is useless if we're forcing a shutdown. + */ + xfs_trans_ail_delete(&bip->bli_item, SHUTDOWN_CORRUPT_INCORE); bp->b_log_item = NULL; + xfs_buf_item_free(bip); + xfs_buf_rele(bp); } /* @@ -1188,19 +1146,10 @@ void xfs_buf_inode_iodone( struct xfs_buf *bp) { - struct xfs_buf_log_item *blip = bp->b_log_item; - struct xfs_log_item *lip; - if (xfs_buf_had_callback_errors(bp)) return; - /* If there is a buf_log_item attached, run its callback */ - if (blip) { - lip = &blip->bli_item; - lip->li_cb(bp, lip); - bp->b_log_item = NULL; - } - + xfs_buf_item_done(bp); xfs_iflush_done(bp); xfs_buf_ioend_finish(bp); } @@ -1212,59 +1161,28 @@ void xfs_buf_dquot_iodone( struct xfs_buf *bp) { - struct xfs_buf_log_item *blip = bp->b_log_item; - struct xfs_log_item *lip; - if (xfs_buf_had_callback_errors(bp)) return; /* a newly allocated dquot buffer might have a log item attached */ - if (blip) { - lip = &blip->bli_item; - lip->li_cb(bp, lip); - bp->b_log_item = NULL; - } - + xfs_buf_item_done(bp); xfs_dquot_done(bp); xfs_buf_ioend_finish(bp); } /* * Dirty buffer iodone callback function. + * + * Note that for things like remote attribute buffers, there may not be a buffer + * log item here, so processing the buffer log item must remain be optional. */ void xfs_buf_iodone( struct xfs_buf *bp) { - xfs_buf_run_callbacks(bp); - xfs_buf_ioend_finish(bp); -} - -/* - * This is the iodone() function for buffers which have been - * logged. It is called when they are eventually flushed out. - * It should remove the buf item from the AIL, and free the buf item. - * It is called by xfs_buf_iodone_callbacks() above which will take - * care of cleaning up the buffer itself. - */ -void -xfs_buf_item_iodone( - struct xfs_buf *bp, - struct xfs_log_item *lip) -{ - ASSERT(BUF_ITEM(lip)->bli_buf == bp); - - xfs_buf_rele(bp); + if (xfs_buf_had_callback_errors(bp)) + return; - /* - * If we are forcibly shutting down, this may well be off the AIL - * already. That's because we simulate the log-committed callbacks to - * unpin these buffers. Or we may never have put this item on AIL - * because of the transaction was aborted forcibly. - * xfs_trans_ail_delete() takes care of these. - * - * Either way, AIL is useless if we're forcing a shutdown. - */ - xfs_trans_ail_delete(lip, SHUTDOWN_CORRUPT_INCORE); - xfs_buf_item_free(BUF_ITEM(lip)); + xfs_buf_item_done(bp); + xfs_buf_ioend_finish(bp); } diff --git a/fs/xfs/xfs_buf_item.h b/fs/xfs/xfs_buf_item.h index 610cd00193289..7c0bd2a210aff 100644 --- a/fs/xfs/xfs_buf_item.h +++ b/fs/xfs/xfs_buf_item.h @@ -57,7 +57,6 @@ bool xfs_buf_item_dirty_format(struct xfs_buf_log_item *); void xfs_buf_attach_iodone(struct xfs_buf *, void(*)(struct xfs_buf *, struct xfs_log_item *), struct xfs_log_item *); -void xfs_buf_item_iodone(struct xfs_buf *, struct xfs_log_item *); void xfs_buf_inode_iodone(struct xfs_buf *); void xfs_buf_dquot_iodone(struct xfs_buf *); void xfs_buf_iodone(struct xfs_buf *); diff --git a/fs/xfs/xfs_trans_buf.c b/fs/xfs/xfs_trans_buf.c index 6752676b94fe7..11cd666cd99a6 100644 --- a/fs/xfs/xfs_trans_buf.c +++ b/fs/xfs/xfs_trans_buf.c @@ -475,7 +475,6 @@ xfs_trans_dirty_buf( bp->b_flags |= XBF_DONE; ASSERT(atomic_read(&bip->bli_refcount) > 0); - bip->bli_item.li_cb = xfs_buf_item_iodone; /* * If we invalidated the buffer within this transaction, then @@ -644,7 +643,6 @@ xfs_trans_stale_inode_buf( ASSERT(atomic_read(&bip->bli_refcount) > 0); bip->bli_flags |= XFS_BLI_STALE_INODE; - bip->bli_item.li_cb = xfs_buf_item_iodone; bp->b_flags |= _XBF_INODES; xfs_trans_buf_set_type(tp, bp, XFS_BLFT_DINO_BUF); } -- GitLab From 2ef3f7f5db15aea47b92fd770bc45cf317aa2b97 Mon Sep 17 00:00:00 2001 From: Dave Chinner Date: Mon, 29 Jun 2020 14:49:14 -0700 Subject: [PATCH 0303/1476] xfs: get rid of log item callbacks They are not used anymore, so remove them from the log item and the buffer iodone attachment interfaces. Signed-off-by: Dave Chinner Reviewed-by: Christoph Hellwig Reviewed-by: Darrick J. Wong Reviewed-by: Brian Foster Signed-off-by: Darrick J. Wong --- fs/xfs/xfs_buf_item.c | 17 ----------------- fs/xfs/xfs_buf_item.h | 3 --- fs/xfs/xfs_dquot.c | 6 +++--- fs/xfs/xfs_inode.c | 5 +++-- fs/xfs/xfs_trans.h | 4 ---- 5 files changed, 6 insertions(+), 29 deletions(-) diff --git a/fs/xfs/xfs_buf_item.c b/fs/xfs/xfs_buf_item.c index 0ece5de9dd711..09bfe9c52dbdb 100644 --- a/fs/xfs/xfs_buf_item.c +++ b/fs/xfs/xfs_buf_item.c @@ -955,23 +955,6 @@ xfs_buf_item_relse( xfs_buf_item_free(bip); } - -/* - * Add the given log item with its callback to the list of callbacks - * to be called when the buffer's I/O completes. - */ -void -xfs_buf_attach_iodone( - struct xfs_buf *bp, - void (*cb)(struct xfs_buf *, struct xfs_log_item *), - struct xfs_log_item *lip) -{ - ASSERT(xfs_buf_islocked(bp)); - - lip->li_cb = cb; - list_add_tail(&lip->li_bio_list, &bp->b_li_list); -} - /* * Invoke the error state callback for each log item affected by the failed I/O. * diff --git a/fs/xfs/xfs_buf_item.h b/fs/xfs/xfs_buf_item.h index 7c0bd2a210aff..23507cbb4c413 100644 --- a/fs/xfs/xfs_buf_item.h +++ b/fs/xfs/xfs_buf_item.h @@ -54,9 +54,6 @@ void xfs_buf_item_relse(struct xfs_buf *); bool xfs_buf_item_put(struct xfs_buf_log_item *); void xfs_buf_item_log(struct xfs_buf_log_item *, uint, uint); bool xfs_buf_item_dirty_format(struct xfs_buf_log_item *); -void xfs_buf_attach_iodone(struct xfs_buf *, - void(*)(struct xfs_buf *, struct xfs_log_item *), - struct xfs_log_item *); void xfs_buf_inode_iodone(struct xfs_buf *); void xfs_buf_dquot_iodone(struct xfs_buf *); void xfs_buf_iodone(struct xfs_buf *); diff --git a/fs/xfs/xfs_dquot.c b/fs/xfs/xfs_dquot.c index 403bc4e9f21ff..d5984a926d1d0 100644 --- a/fs/xfs/xfs_dquot.c +++ b/fs/xfs/xfs_dquot.c @@ -1187,11 +1187,11 @@ xfs_qm_dqflush( } /* - * Attach an iodone routine so that we can remove this dquot from the - * AIL and release the flush lock once the dquot is synced to disk. + * Attach the dquot to the buffer so that we can remove this dquot from + * the AIL and release the flush lock once the dquot is synced to disk. */ bp->b_flags |= _XBF_DQUOTS; - xfs_buf_attach_iodone(bp, NULL, &dqp->q_logitem.qli_item); + list_add_tail(&dqp->q_logitem.qli_item.li_bio_list, &bp->b_li_list); /* * If the buffer is pinned then push on the log so we won't diff --git a/fs/xfs/xfs_inode.c b/fs/xfs/xfs_inode.c index 721b8420be046..77cc9cbcd311e 100644 --- a/fs/xfs/xfs_inode.c +++ b/fs/xfs/xfs_inode.c @@ -2711,7 +2711,8 @@ xfs_ifree_cluster( xfs_trans_ail_copy_lsn(mp->m_ail, &iip->ili_flush_lsn, &iip->ili_item.li_lsn); - xfs_buf_attach_iodone(bp, NULL, &iip->ili_item); + list_add_tail(&iip->ili_item.li_bio_list, + &bp->b_li_list); if (ip != free_ip) xfs_iunlock(ip, XFS_ILOCK_EXCL); @@ -3861,7 +3862,7 @@ flush_out: * the flush lock. */ bp->b_flags |= _XBF_INODES; - xfs_buf_attach_iodone(bp, NULL, &iip->ili_item); + list_add_tail(&iip->ili_item.li_bio_list, &bp->b_li_list); /* generate the checksum. */ xfs_dinode_calc_crc(mp, dip); diff --git a/fs/xfs/xfs_trans.h b/fs/xfs/xfs_trans.h index 8308bf6d7e404..99a9ab9cab25b 100644 --- a/fs/xfs/xfs_trans.h +++ b/fs/xfs/xfs_trans.h @@ -37,10 +37,6 @@ struct xfs_log_item { unsigned long li_flags; /* misc flags */ struct xfs_buf *li_buf; /* real buffer pointer */ struct list_head li_bio_list; /* buffer item list */ - void (*li_cb)(struct xfs_buf *, - struct xfs_log_item *); - /* buffer item iodone */ - /* callback func */ const struct xfs_item_ops *li_ops; /* function list */ /* delayed logging */ -- GitLab From 428947e9d525ae3a03dbdce4cdbcb2afe020732d Mon Sep 17 00:00:00 2001 From: Dave Chinner Date: Mon, 29 Jun 2020 14:49:14 -0700 Subject: [PATCH 0304/1476] xfs: handle buffer log item IO errors directly Currently when a buffer with attached log items has an IO error it called ->iop_error for each attched log item. These all call xfs_set_li_failed() to handle the error, but we are about to change the way log items manage buffers. hence we first need to remove the per-item dependency on buffer handling done by xfs_set_li_failed(). We already have specific buffer type IO completion routines, so move the log item error handling out of the generic error handling and into the log item specific functions so we can implement per-type error handling easily. This requires a more complex return value from the error handling code so that we can take the correct action the failure handling requires. This results in some repeated boilerplate in the functions, but that can be cleaned up later once all the changes cascade through this code. Signed-off-by: Dave Chinner Reviewed-by: Brian Foster Reviewed-by: Darrick J. Wong Signed-off-by: Darrick J. Wong --- fs/xfs/xfs_buf_item.c | 214 ++++++++++++++++++++++++++++-------------- 1 file changed, 144 insertions(+), 70 deletions(-) diff --git a/fs/xfs/xfs_buf_item.c b/fs/xfs/xfs_buf_item.c index 09bfe9c52dbdb..f80fc5bd3bffa 100644 --- a/fs/xfs/xfs_buf_item.c +++ b/fs/xfs/xfs_buf_item.c @@ -986,21 +986,24 @@ xfs_buf_do_callbacks_fail( spin_unlock(&ailp->ail_lock); } +/* + * Decide if we're going to retry the write after a failure, and prepare + * the buffer for retrying the write. + */ static bool -xfs_buf_iodone_callback_error( +xfs_buf_ioerror_fail_without_retry( struct xfs_buf *bp) { struct xfs_mount *mp = bp->b_mount; static ulong lasttime; static xfs_buftarg_t *lasttarg; - struct xfs_error_cfg *cfg; /* * If we've already decided to shutdown the filesystem because of * I/O errors, there's no point in giving this a retry. */ if (XFS_FORCED_SHUTDOWN(mp)) - goto out_stale; + return true; if (bp->b_target != lasttarg || time_after(jiffies, (lasttime + 5*HZ))) { @@ -1011,91 +1014,114 @@ xfs_buf_iodone_callback_error( /* synchronous writes will have callers process the error */ if (!(bp->b_flags & XBF_ASYNC)) - goto out_stale; - - trace_xfs_buf_item_iodone_async(bp, _RET_IP_); - - cfg = xfs_error_get_cfg(mp, XFS_ERR_METADATA, bp->b_error); + return true; + return false; +} - /* - * If the write was asynchronous then no one will be looking for the - * error. If this is the first failure of this type, clear the error - * state and write the buffer out again. This means we always retry an - * async write failure at least once, but we also need to set the buffer - * up to behave correctly now for repeated failures. - */ - if (!(bp->b_flags & (XBF_STALE | XBF_WRITE_FAIL)) || - bp->b_last_error != bp->b_error) { - bp->b_flags |= (XBF_WRITE | XBF_DONE | XBF_WRITE_FAIL); - bp->b_last_error = bp->b_error; - if (cfg->retry_timeout != XFS_ERR_RETRY_FOREVER && - !bp->b_first_retry_time) - bp->b_first_retry_time = jiffies; +static bool +xfs_buf_ioerror_retry( + struct xfs_buf *bp, + struct xfs_error_cfg *cfg) +{ + if ((bp->b_flags & (XBF_STALE | XBF_WRITE_FAIL)) && + bp->b_last_error == bp->b_error) + return false; - xfs_buf_ioerror(bp, 0); - xfs_buf_submit(bp); - return true; - } + bp->b_flags |= (XBF_WRITE | XBF_DONE | XBF_WRITE_FAIL); + bp->b_last_error = bp->b_error; + if (cfg->retry_timeout != XFS_ERR_RETRY_FOREVER && + !bp->b_first_retry_time) + bp->b_first_retry_time = jiffies; + return true; +} - /* - * Repeated failure on an async write. Take action according to the - * error configuration we have been set up to use. - */ +/* + * Account for this latest trip around the retry handler, and decide if + * we've failed enough times to constitute a permanent failure. + */ +static bool +xfs_buf_ioerror_permanent( + struct xfs_buf *bp, + struct xfs_error_cfg *cfg) +{ + struct xfs_mount *mp = bp->b_mount; if (cfg->max_retries != XFS_ERR_RETRY_FOREVER && ++bp->b_retries > cfg->max_retries) - goto permanent_error; + return true; if (cfg->retry_timeout != XFS_ERR_RETRY_FOREVER && time_after(jiffies, cfg->retry_timeout + bp->b_first_retry_time)) - goto permanent_error; + return true; /* At unmount we may treat errors differently */ if ((mp->m_flags & XFS_MOUNT_UNMOUNTING) && mp->m_fail_unmount) - goto permanent_error; - - /* - * Still a transient error, run IO completion failure callbacks and let - * the higher layers retry the buffer. - */ - xfs_buf_do_callbacks_fail(bp); - xfs_buf_ioerror(bp, 0); - xfs_buf_relse(bp); - return true; + return true; - /* - * Permanent error - we need to trigger a shutdown if we haven't already - * to indicate that inconsistency will result from this action. - */ -permanent_error: - xfs_force_shutdown(mp, SHUTDOWN_META_IO_ERROR); -out_stale: - xfs_buf_stale(bp); - bp->b_flags |= XBF_DONE; - trace_xfs_buf_error_relse(bp, _RET_IP_); return false; } -static inline bool -xfs_buf_had_callback_errors( +/* + * On a sync write or shutdown we just want to stale the buffer and let the + * caller handle the error in bp->b_error appropriately. + * + * If the write was asynchronous then no one will be looking for the error. If + * this is the first failure of this type, clear the error state and write the + * buffer out again. This means we always retry an async write failure at least + * once, but we also need to set the buffer up to behave correctly now for + * repeated failures. + * + * If we get repeated async write failures, then we take action according to the + * error configuration we have been set up to use. + * + * Multi-state return value: + * + * XBF_IOERROR_FINISH: clear IO error retry state and run callback completions + * XBF_IOERROR_DONE: resubmitted immediately, do not run any completions + * XBF_IOERROR_FAIL: transient error, run failure callback completions and then + * release the buffer + */ +enum { + XBF_IOERROR_FINISH, + XBF_IOERROR_DONE, + XBF_IOERROR_FAIL, +}; + +static int +xfs_buf_iodone_error( struct xfs_buf *bp) { + struct xfs_mount *mp = bp->b_mount; + struct xfs_error_cfg *cfg; - /* - * If there is an error, process it. Some errors require us to run - * callbacks after failure processing is done so we detect that and take - * appropriate action. - */ - if (bp->b_error && xfs_buf_iodone_callback_error(bp)) - return true; + if (xfs_buf_ioerror_fail_without_retry(bp)) + goto out_stale; + + trace_xfs_buf_item_iodone_async(bp, _RET_IP_); + + cfg = xfs_error_get_cfg(mp, XFS_ERR_METADATA, bp->b_error); + if (xfs_buf_ioerror_retry(bp, cfg)) { + xfs_buf_ioerror(bp, 0); + xfs_buf_submit(bp); + return XBF_IOERROR_DONE; + } /* - * Successful IO or permanent error. Either way, we can clear the - * retry state here in preparation for the next error that may occur. + * Permanent error - we need to trigger a shutdown if we haven't already + * to indicate that inconsistency will result from this action. */ - bp->b_last_error = 0; - bp->b_retries = 0; - bp->b_first_retry_time = 0; - return false; + if (xfs_buf_ioerror_permanent(bp, cfg)) { + xfs_force_shutdown(mp, SHUTDOWN_META_IO_ERROR); + goto out_stale; + } + + /* Still considered a transient error. Caller will schedule retries. */ + return XBF_IOERROR_FAIL; + +out_stale: + xfs_buf_stale(bp); + bp->b_flags |= XBF_DONE; + trace_xfs_buf_error_relse(bp, _RET_IP_); + return XBF_IOERROR_FINISH; } static void @@ -1122,6 +1148,15 @@ xfs_buf_item_done( xfs_buf_rele(bp); } +static inline void +xfs_buf_clear_ioerror_retry_state( + struct xfs_buf *bp) +{ + bp->b_last_error = 0; + bp->b_retries = 0; + bp->b_first_retry_time = 0; +} + /* * Inode buffer iodone callback function. */ @@ -1129,9 +1164,22 @@ void xfs_buf_inode_iodone( struct xfs_buf *bp) { - if (xfs_buf_had_callback_errors(bp)) + if (bp->b_error) { + int ret = xfs_buf_iodone_error(bp); + + if (ret == XBF_IOERROR_FINISH) + goto finish_iodone; + if (ret == XBF_IOERROR_DONE) + return; + ASSERT(ret == XBF_IOERROR_FAIL); + xfs_buf_do_callbacks_fail(bp); + xfs_buf_ioerror(bp, 0); + xfs_buf_relse(bp); return; + } +finish_iodone: + xfs_buf_clear_ioerror_retry_state(bp); xfs_buf_item_done(bp); xfs_iflush_done(bp); xfs_buf_ioend_finish(bp); @@ -1144,9 +1192,22 @@ void xfs_buf_dquot_iodone( struct xfs_buf *bp) { - if (xfs_buf_had_callback_errors(bp)) + if (bp->b_error) { + int ret = xfs_buf_iodone_error(bp); + + if (ret == XBF_IOERROR_FINISH) + goto finish_iodone; + if (ret == XBF_IOERROR_DONE) + return; + ASSERT(ret == XBF_IOERROR_FAIL); + xfs_buf_do_callbacks_fail(bp); + xfs_buf_ioerror(bp, 0); + xfs_buf_relse(bp); return; + } +finish_iodone: + xfs_buf_clear_ioerror_retry_state(bp); /* a newly allocated dquot buffer might have a log item attached */ xfs_buf_item_done(bp); xfs_dquot_done(bp); @@ -1163,9 +1224,22 @@ void xfs_buf_iodone( struct xfs_buf *bp) { - if (xfs_buf_had_callback_errors(bp)) + if (bp->b_error) { + int ret = xfs_buf_iodone_error(bp); + + if (ret == XBF_IOERROR_FINISH) + goto finish_iodone; + if (ret == XBF_IOERROR_DONE) + return; + ASSERT(ret == XBF_IOERROR_FAIL); + xfs_buf_do_callbacks_fail(bp); + xfs_buf_ioerror(bp, 0); + xfs_buf_relse(bp); return; + } +finish_iodone: + xfs_buf_clear_ioerror_retry_state(bp); xfs_buf_item_done(bp); xfs_buf_ioend_finish(bp); } -- GitLab From 3536b61e74aa232d0ae42cff57b80278724f209c Mon Sep 17 00:00:00 2001 From: Dave Chinner Date: Mon, 29 Jun 2020 14:49:15 -0700 Subject: [PATCH 0305/1476] xfs: unwind log item error flagging When an buffer IO error occurs, we want to mark all the log items attached to the buffer as failed. Open code the error handling loop so that we can modify the flagging for the different types of objects directly and independently of each other. This also allows us to remove the ->iop_error method from the log item operations. Signed-off-by: Dave Chinner Reviewed-by: Brian Foster Reviewed-by: Darrick J. Wong Signed-off-by: Darrick J. Wong --- fs/xfs/xfs_buf_item.c | 48 ++++++++++++----------------------------- fs/xfs/xfs_dquot_item.c | 18 ---------------- fs/xfs/xfs_inode_item.c | 18 ---------------- fs/xfs/xfs_trans.h | 1 - 4 files changed, 14 insertions(+), 71 deletions(-) diff --git a/fs/xfs/xfs_buf_item.c b/fs/xfs/xfs_buf_item.c index f80fc5bd3bffa..d61f20b989cde 100644 --- a/fs/xfs/xfs_buf_item.c +++ b/fs/xfs/xfs_buf_item.c @@ -12,6 +12,7 @@ #include "xfs_bit.h" #include "xfs_mount.h" #include "xfs_trans.h" +#include "xfs_trans_priv.h" #include "xfs_buf_item.h" #include "xfs_inode.h" #include "xfs_inode_item.h" @@ -955,37 +956,6 @@ xfs_buf_item_relse( xfs_buf_item_free(bip); } -/* - * Invoke the error state callback for each log item affected by the failed I/O. - * - * If a metadata buffer write fails with a non-permanent error, the buffer is - * eventually resubmitted and so the completion callbacks are not run. The error - * state may need to be propagated to the log items attached to the buffer, - * however, so the next AIL push of the item knows hot to handle it correctly. - */ -STATIC void -xfs_buf_do_callbacks_fail( - struct xfs_buf *bp) -{ - struct xfs_ail *ailp = bp->b_mount->m_ail; - struct xfs_log_item *lip; - - /* - * Buffer log item errors are handled directly by xfs_buf_item_push() - * and xfs_buf_iodone_callback_error, and they have no IO error - * callbacks. Check only for items in b_li_list. - */ - if (list_empty(&bp->b_li_list)) - return; - - spin_lock(&ailp->ail_lock); - list_for_each_entry(lip, &bp->b_li_list, li_bio_list) { - if (lip->li_ops->iop_error) - lip->li_ops->iop_error(lip, bp); - } - spin_unlock(&ailp->ail_lock); -} - /* * Decide if we're going to retry the write after a failure, and prepare * the buffer for retrying the write. @@ -1165,6 +1135,7 @@ xfs_buf_inode_iodone( struct xfs_buf *bp) { if (bp->b_error) { + struct xfs_log_item *lip; int ret = xfs_buf_iodone_error(bp); if (ret == XBF_IOERROR_FINISH) @@ -1172,7 +1143,11 @@ xfs_buf_inode_iodone( if (ret == XBF_IOERROR_DONE) return; ASSERT(ret == XBF_IOERROR_FAIL); - xfs_buf_do_callbacks_fail(bp); + spin_lock(&bp->b_mount->m_ail->ail_lock); + list_for_each_entry(lip, &bp->b_li_list, li_bio_list) { + xfs_set_li_failed(lip, bp); + } + spin_unlock(&bp->b_mount->m_ail->ail_lock); xfs_buf_ioerror(bp, 0); xfs_buf_relse(bp); return; @@ -1193,6 +1168,7 @@ xfs_buf_dquot_iodone( struct xfs_buf *bp) { if (bp->b_error) { + struct xfs_log_item *lip; int ret = xfs_buf_iodone_error(bp); if (ret == XBF_IOERROR_FINISH) @@ -1200,7 +1176,11 @@ xfs_buf_dquot_iodone( if (ret == XBF_IOERROR_DONE) return; ASSERT(ret == XBF_IOERROR_FAIL); - xfs_buf_do_callbacks_fail(bp); + spin_lock(&bp->b_mount->m_ail->ail_lock); + list_for_each_entry(lip, &bp->b_li_list, li_bio_list) { + xfs_set_li_failed(lip, bp); + } + spin_unlock(&bp->b_mount->m_ail->ail_lock); xfs_buf_ioerror(bp, 0); xfs_buf_relse(bp); return; @@ -1232,7 +1212,7 @@ xfs_buf_iodone( if (ret == XBF_IOERROR_DONE) return; ASSERT(ret == XBF_IOERROR_FAIL); - xfs_buf_do_callbacks_fail(bp); + ASSERT(list_empty(&bp->b_li_list)); xfs_buf_ioerror(bp, 0); xfs_buf_relse(bp); return; diff --git a/fs/xfs/xfs_dquot_item.c b/fs/xfs/xfs_dquot_item.c index 349c92d26570c..d7e4de7151d7f 100644 --- a/fs/xfs/xfs_dquot_item.c +++ b/fs/xfs/xfs_dquot_item.c @@ -113,23 +113,6 @@ xfs_qm_dqunpin_wait( wait_event(dqp->q_pinwait, (atomic_read(&dqp->q_pincount) == 0)); } -/* - * Callback used to mark a buffer with XFS_LI_FAILED when items in the buffer - * have been failed during writeback - * - * this informs the AIL that the dquot is already flush locked on the next push, - * and acquires a hold on the buffer to ensure that it isn't reclaimed before - * dirty data makes it to disk. - */ -STATIC void -xfs_dquot_item_error( - struct xfs_log_item *lip, - struct xfs_buf *bp) -{ - ASSERT(!completion_done(&DQUOT_ITEM(lip)->qli_dquot->q_flush)); - xfs_set_li_failed(lip, bp); -} - STATIC uint xfs_qm_dquot_logitem_push( struct xfs_log_item *lip, @@ -216,7 +199,6 @@ static const struct xfs_item_ops xfs_dquot_item_ops = { .iop_release = xfs_qm_dquot_logitem_release, .iop_committing = xfs_qm_dquot_logitem_committing, .iop_push = xfs_qm_dquot_logitem_push, - .iop_error = xfs_dquot_item_error }; /* diff --git a/fs/xfs/xfs_inode_item.c b/fs/xfs/xfs_inode_item.c index 7049f2ae8d186..86c783dec2bac 100644 --- a/fs/xfs/xfs_inode_item.c +++ b/fs/xfs/xfs_inode_item.c @@ -464,23 +464,6 @@ xfs_inode_item_unpin( wake_up_bit(&ip->i_flags, __XFS_IPINNED_BIT); } -/* - * Callback used to mark a buffer with XFS_LI_FAILED when items in the buffer - * have been failed during writeback - * - * This informs the AIL that the inode is already flush locked on the next push, - * and acquires a hold on the buffer to ensure that it isn't reclaimed before - * dirty data makes it to disk. - */ -STATIC void -xfs_inode_item_error( - struct xfs_log_item *lip, - struct xfs_buf *bp) -{ - ASSERT(xfs_isiflocked(INODE_ITEM(lip)->ili_inode)); - xfs_set_li_failed(lip, bp); -} - STATIC uint xfs_inode_item_push( struct xfs_log_item *lip, @@ -619,7 +602,6 @@ static const struct xfs_item_ops xfs_inode_item_ops = { .iop_committed = xfs_inode_item_committed, .iop_push = xfs_inode_item_push, .iop_committing = xfs_inode_item_committing, - .iop_error = xfs_inode_item_error }; diff --git a/fs/xfs/xfs_trans.h b/fs/xfs/xfs_trans.h index 99a9ab9cab25b..b752501818d25 100644 --- a/fs/xfs/xfs_trans.h +++ b/fs/xfs/xfs_trans.h @@ -74,7 +74,6 @@ struct xfs_item_ops { void (*iop_committing)(struct xfs_log_item *, xfs_lsn_t commit_lsn); void (*iop_release)(struct xfs_log_item *); xfs_lsn_t (*iop_committed)(struct xfs_log_item *, xfs_lsn_t); - void (*iop_error)(struct xfs_log_item *, xfs_buf_t *); int (*iop_recover)(struct xfs_log_item *lip, struct xfs_trans *tp); bool (*iop_match)(struct xfs_log_item *item, uint64_t id); }; -- GitLab From e98084b8bef7e357dbd201b162fea0817d1908c5 Mon Sep 17 00:00:00 2001 From: Dave Chinner Date: Mon, 29 Jun 2020 14:49:15 -0700 Subject: [PATCH 0306/1476] xfs: move xfs_clear_li_failed out of xfs_ail_delete_one() xfs_ail_delete_one() is called directly from dquot and inode IO completion, as well as from the generic xfs_trans_ail_delete() function. Inodes are about to have their own failure handling, and dquots will in future, too. Pull the clearing of the LI_FAILED flag up into the callers so we can customise the code appropriately. Signed-off-by: Dave Chinner Reviewed-by: Brian Foster Reviewed-by: Darrick J. Wong Signed-off-by: Darrick J. Wong --- fs/xfs/xfs_dquot.c | 6 +----- fs/xfs/xfs_inode_item.c | 3 +-- fs/xfs/xfs_trans_ail.c | 2 +- 3 files changed, 3 insertions(+), 8 deletions(-) diff --git a/fs/xfs/xfs_dquot.c b/fs/xfs/xfs_dquot.c index d5984a926d1d0..76353c9a723ee 100644 --- a/fs/xfs/xfs_dquot.c +++ b/fs/xfs/xfs_dquot.c @@ -1070,16 +1070,12 @@ xfs_qm_dqflush_done( test_bit(XFS_LI_FAILED, &lip->li_flags))) { spin_lock(&ailp->ail_lock); + xfs_clear_li_failed(lip); if (lip->li_lsn == qip->qli_flush_lsn) { /* xfs_ail_update_finish() drops the AIL lock */ tail_lsn = xfs_ail_delete_one(ailp, lip); xfs_ail_update_finish(ailp, tail_lsn); } else { - /* - * Clear the failed state since we are about to drop the - * flush lock - */ - xfs_clear_li_failed(lip); spin_unlock(&ailp->ail_lock); } } diff --git a/fs/xfs/xfs_inode_item.c b/fs/xfs/xfs_inode_item.c index 86c783dec2bac..0ba75764a8dc5 100644 --- a/fs/xfs/xfs_inode_item.c +++ b/fs/xfs/xfs_inode_item.c @@ -690,12 +690,11 @@ xfs_iflush_done( /* this is an opencoded batch version of xfs_trans_ail_delete */ spin_lock(&ailp->ail_lock); list_for_each_entry(lip, &tmp, li_bio_list) { + xfs_clear_li_failed(lip); if (lip->li_lsn == INODE_ITEM(lip)->ili_flush_lsn) { xfs_lsn_t lsn = xfs_ail_delete_one(ailp, lip); if (!tail_lsn && lsn) tail_lsn = lsn; - } else { - xfs_clear_li_failed(lip); } } xfs_ail_update_finish(ailp, tail_lsn); diff --git a/fs/xfs/xfs_trans_ail.c b/fs/xfs/xfs_trans_ail.c index ac5019361a139..ac33f6393f99c 100644 --- a/fs/xfs/xfs_trans_ail.c +++ b/fs/xfs/xfs_trans_ail.c @@ -843,7 +843,6 @@ xfs_ail_delete_one( trace_xfs_ail_delete(lip, mlip->li_lsn, lip->li_lsn); xfs_ail_delete(ailp, lip); - xfs_clear_li_failed(lip); clear_bit(XFS_LI_IN_AIL, &lip->li_flags); lip->li_lsn = 0; @@ -874,6 +873,7 @@ xfs_trans_ail_delete( } /* xfs_ail_update_finish() drops the AIL lock */ + xfs_clear_li_failed(lip); tail_lsn = xfs_ail_delete_one(ailp, lip); xfs_ail_update_finish(ailp, tail_lsn); } -- GitLab From 298f7bec503f30bd98242ec02df6abe13b31a677 Mon Sep 17 00:00:00 2001 From: Dave Chinner Date: Mon, 29 Jun 2020 14:49:15 -0700 Subject: [PATCH 0307/1476] xfs: pin inode backing buffer to the inode log item When we dirty an inode, we are going to have to write it disk at some point in the near future. This requires the inode cluster backing buffer to be present in memory. Unfortunately, under severe memory pressure we can reclaim the inode backing buffer while the inode is dirty in memory, resulting in stalling the AIL pushing because it has to do a read-modify-write cycle on the cluster buffer. When we have no memory available, the read of the cluster buffer blocks the AIL pushing process, and this causes all sorts of issues for memory reclaim as it requires inode writeback to make forwards progress. Allocating a cluster buffer causes more memory pressure, and results in more cluster buffers to be reclaimed, resulting in more RMW cycles to be done in the AIL context and everything then backs up on AIL progress. Only the synchronous inode cluster writeback in the the inode reclaim code provides some level of forwards progress guarantees that prevent OOM-killer rampages in this situation. Fix this by pinning the inode backing buffer to the inode log item when the inode is first dirtied (i.e. in xfs_trans_log_inode()). This may mean the first modification of an inode that has been held in cache for a long time may block on a cluster buffer read, but we can do that in transaction context and block safely until the buffer has been allocated and read. Once we have the cluster buffer, the inode log item takes a reference to it, pinning it in memory, and attaches it to the log item for future reference. This means we can always grab the cluster buffer from the inode log item when we need it. When the inode is finally cleaned and removed from the AIL, we can drop the reference the inode log item holds on the cluster buffer. Once all inodes on the cluster buffer are clean, the cluster buffer will be unpinned and it will be available for memory reclaim to reclaim again. This avoids the issues with needing to do RMW cycles in the AIL pushing context, and hence allows complete non-blocking inode flushing to be performed by the AIL pushing context. Signed-off-by: Dave Chinner Reviewed-by: Brian Foster Reviewed-by: Darrick J. Wong Signed-off-by: Darrick J. Wong --- fs/xfs/libxfs/xfs_inode_buf.c | 3 +- fs/xfs/libxfs/xfs_trans_inode.c | 53 ++++++++++++++++++++++++---- fs/xfs/xfs_buf_item.c | 4 +-- fs/xfs/xfs_inode_item.c | 61 ++++++++++++++++++++++++++------- fs/xfs/xfs_trans_ail.c | 8 +++-- 5 files changed, 105 insertions(+), 24 deletions(-) diff --git a/fs/xfs/libxfs/xfs_inode_buf.c b/fs/xfs/libxfs/xfs_inode_buf.c index 5c93e8e6de745..b4a6c091571e6 100644 --- a/fs/xfs/libxfs/xfs_inode_buf.c +++ b/fs/xfs/libxfs/xfs_inode_buf.c @@ -176,7 +176,8 @@ xfs_imap_to_bp( } *bpp = bp; - *dipp = xfs_buf_offset(bp, imap->im_boffset); + if (dipp) + *dipp = xfs_buf_offset(bp, imap->im_boffset); return 0; } diff --git a/fs/xfs/libxfs/xfs_trans_inode.c b/fs/xfs/libxfs/xfs_trans_inode.c index c66d9d1dd58b9..ad5974365c589 100644 --- a/fs/xfs/libxfs/xfs_trans_inode.c +++ b/fs/xfs/libxfs/xfs_trans_inode.c @@ -8,6 +8,8 @@ #include "xfs_shared.h" #include "xfs_format.h" #include "xfs_log_format.h" +#include "xfs_trans_resv.h" +#include "xfs_mount.h" #include "xfs_inode.h" #include "xfs_trans.h" #include "xfs_trans_priv.h" @@ -72,13 +74,19 @@ xfs_trans_ichgtime( } /* - * This is called to mark the fields indicated in fieldmask as needing - * to be logged when the transaction is committed. The inode must - * already be associated with the given transaction. + * This is called to mark the fields indicated in fieldmask as needing to be + * logged when the transaction is committed. The inode must already be + * associated with the given transaction. * - * The values for fieldmask are defined in xfs_inode_item.h. We always - * log all of the core inode if any of it has changed, and we always log - * all of the inline data/extents/b-tree root if any of them has changed. + * The values for fieldmask are defined in xfs_inode_item.h. We always log all + * of the core inode if any of it has changed, and we always log all of the + * inline data/extents/b-tree root if any of them has changed. + * + * Grab and pin the cluster buffer associated with this inode to avoid RMW + * cycles at inode writeback time. Avoid the need to add error handling to every + * xfs_trans_log_inode() call by shutting down on read error. This will cause + * transactions to fail and everything to error out, just like if we return a + * read error in a dirty transaction and cancel it. */ void xfs_trans_log_inode( @@ -131,6 +139,39 @@ xfs_trans_log_inode( spin_lock(&iip->ili_lock); iip->ili_fsync_fields |= flags; + if (!iip->ili_item.li_buf) { + struct xfs_buf *bp; + int error; + + /* + * We hold the ILOCK here, so this inode is not going to be + * flushed while we are here. Further, because there is no + * buffer attached to the item, we know that there is no IO in + * progress, so nothing will clear the ili_fields while we read + * in the buffer. Hence we can safely drop the spin lock and + * read the buffer knowing that the state will not change from + * here. + */ + spin_unlock(&iip->ili_lock); + error = xfs_imap_to_bp(ip->i_mount, tp, &ip->i_imap, NULL, + &bp, 0); + if (error) { + xfs_force_shutdown(ip->i_mount, SHUTDOWN_META_IO_ERROR); + return; + } + + /* + * We need an explicit buffer reference for the log item but + * don't want the buffer to remain attached to the transaction. + * Hold the buffer but release the transaction reference. + */ + xfs_buf_hold(bp); + xfs_trans_brelse(tp, bp); + + spin_lock(&iip->ili_lock); + iip->ili_item.li_buf = bp; + } + /* * Always OR in the bits from the ili_last_fields field. This is to * coordinate with the xfs_iflush() and xfs_iflush_done() routines in diff --git a/fs/xfs/xfs_buf_item.c b/fs/xfs/xfs_buf_item.c index d61f20b989cde..ecb3362395af3 100644 --- a/fs/xfs/xfs_buf_item.c +++ b/fs/xfs/xfs_buf_item.c @@ -1143,11 +1143,9 @@ xfs_buf_inode_iodone( if (ret == XBF_IOERROR_DONE) return; ASSERT(ret == XBF_IOERROR_FAIL); - spin_lock(&bp->b_mount->m_ail->ail_lock); list_for_each_entry(lip, &bp->b_li_list, li_bio_list) { - xfs_set_li_failed(lip, bp); + set_bit(XFS_LI_FAILED, &lip->li_flags); } - spin_unlock(&bp->b_mount->m_ail->ail_lock); xfs_buf_ioerror(bp, 0); xfs_buf_relse(bp); return; diff --git a/fs/xfs/xfs_inode_item.c b/fs/xfs/xfs_inode_item.c index 0ba75764a8dc5..64bdda72f7b27 100644 --- a/fs/xfs/xfs_inode_item.c +++ b/fs/xfs/xfs_inode_item.c @@ -439,6 +439,7 @@ xfs_inode_item_pin( struct xfs_inode *ip = INODE_ITEM(lip)->ili_inode; ASSERT(xfs_isilocked(ip, XFS_ILOCK_EXCL)); + ASSERT(lip->li_buf); trace_xfs_inode_pin(ip, _RET_IP_); atomic_inc(&ip->i_pincount); @@ -450,6 +451,12 @@ xfs_inode_item_pin( * item which was previously pinned with a call to xfs_inode_item_pin(). * * Also wake up anyone in xfs_iunpin_wait() if the count goes to 0. + * + * Note that unpin can race with inode cluster buffer freeing marking the buffer + * stale. In that case, flush completions are run from the buffer unpin call, + * which may happen before the inode is unpinned. If we lose the race, there + * will be no buffer attached to the log item, but the inode will be marked + * XFS_ISTALE. */ STATIC void xfs_inode_item_unpin( @@ -459,6 +466,7 @@ xfs_inode_item_unpin( struct xfs_inode *ip = INODE_ITEM(lip)->ili_inode; trace_xfs_inode_unpin(ip, _RET_IP_); + ASSERT(lip->li_buf || xfs_iflags_test(ip, XFS_ISTALE)); ASSERT(atomic_read(&ip->i_pincount) > 0); if (atomic_dec_and_test(&ip->i_pincount)) wake_up_bit(&ip->i_flags, __XFS_IPINNED_BIT); @@ -629,10 +637,15 @@ xfs_inode_item_init( */ void xfs_inode_item_destroy( - xfs_inode_t *ip) + struct xfs_inode *ip) { - kmem_free(ip->i_itemp->ili_item.li_lv_shadow); - kmem_cache_free(xfs_ili_zone, ip->i_itemp); + struct xfs_inode_log_item *iip = ip->i_itemp; + + ASSERT(iip->ili_item.li_buf == NULL); + + ip->i_itemp = NULL; + kmem_free(iip->ili_item.li_lv_shadow); + kmem_cache_free(xfs_ili_zone, iip); } @@ -673,11 +686,10 @@ xfs_iflush_done( list_move_tail(&lip->li_bio_list, &tmp); /* Do an unlocked check for needing the AIL lock. */ - if (lip->li_lsn == iip->ili_flush_lsn || + if (iip->ili_flush_lsn == lip->li_lsn || test_bit(XFS_LI_FAILED, &lip->li_flags)) need_ail++; } - ASSERT(list_empty(&bp->b_li_list)); /* * We only want to pull the item from the AIL if it is actually there @@ -690,7 +702,7 @@ xfs_iflush_done( /* this is an opencoded batch version of xfs_trans_ail_delete */ spin_lock(&ailp->ail_lock); list_for_each_entry(lip, &tmp, li_bio_list) { - xfs_clear_li_failed(lip); + clear_bit(XFS_LI_FAILED, &lip->li_flags); if (lip->li_lsn == INODE_ITEM(lip)->ili_flush_lsn) { xfs_lsn_t lsn = xfs_ail_delete_one(ailp, lip); if (!tail_lsn && lsn) @@ -706,14 +718,29 @@ xfs_iflush_done( * them is safely on disk. */ list_for_each_entry_safe(lip, n, &tmp, li_bio_list) { + bool drop_buffer = false; + list_del_init(&lip->li_bio_list); iip = INODE_ITEM(lip); spin_lock(&iip->ili_lock); + + /* + * Remove the reference to the cluster buffer if the inode is + * clean in memory. Drop the buffer reference once we've dropped + * the locks we hold. + */ + ASSERT(iip->ili_item.li_buf == bp); + if (!iip->ili_fields) { + iip->ili_item.li_buf = NULL; + drop_buffer = true; + } iip->ili_last_fields = 0; + iip->ili_flush_lsn = 0; spin_unlock(&iip->ili_lock); - xfs_ifunlock(iip->ili_inode); + if (drop_buffer) + xfs_buf_rele(bp); } } @@ -725,12 +752,20 @@ xfs_iflush_done( */ void xfs_iflush_abort( - struct xfs_inode *ip) + struct xfs_inode *ip) { - struct xfs_inode_log_item *iip = ip->i_itemp; + struct xfs_inode_log_item *iip = ip->i_itemp; + struct xfs_buf *bp = NULL; if (iip) { + /* + * Clear the failed bit before removing the item from the AIL so + * xfs_trans_ail_delete() doesn't try to clear and release the + * buffer attached to the log item before we are done with it. + */ + clear_bit(XFS_LI_FAILED, &iip->ili_item.li_flags); xfs_trans_ail_delete(&iip->ili_item, 0); + /* * Clear the inode logging fields so no more flushes are * attempted. @@ -739,12 +774,14 @@ xfs_iflush_abort( iip->ili_last_fields = 0; iip->ili_fields = 0; iip->ili_fsync_fields = 0; + iip->ili_flush_lsn = 0; + bp = iip->ili_item.li_buf; + iip->ili_item.li_buf = NULL; spin_unlock(&iip->ili_lock); } - /* - * Release the inode's flush lock since we're done with it. - */ xfs_ifunlock(ip); + if (bp) + xfs_buf_rele(bp); } /* diff --git a/fs/xfs/xfs_trans_ail.c b/fs/xfs/xfs_trans_ail.c index ac33f6393f99c..c3be6e4401343 100644 --- a/fs/xfs/xfs_trans_ail.c +++ b/fs/xfs/xfs_trans_ail.c @@ -377,8 +377,12 @@ xfsaild_resubmit_item( } /* protected by ail_lock */ - list_for_each_entry(lip, &bp->b_li_list, li_bio_list) - xfs_clear_li_failed(lip); + list_for_each_entry(lip, &bp->b_li_list, li_bio_list) { + if (bp->b_flags & _XBF_INODES) + clear_bit(XFS_LI_FAILED, &lip->li_flags); + else + xfs_clear_li_failed(lip); + } xfs_buf_unlock(bp); return XFS_ITEM_SUCCESS; -- GitLab From 993f951f501c85e963b3664739c07196a286eac7 Mon Sep 17 00:00:00 2001 From: Dave Chinner Date: Mon, 29 Jun 2020 14:49:16 -0700 Subject: [PATCH 0308/1476] xfs: make inode reclaim almost non-blocking Now that dirty inode writeback doesn't cause read-modify-write cycles on the inode cluster buffer under memory pressure, the need to throttle memory reclaim to the rate at which we can clean dirty inodes goes away. That is due to the fact that we no longer thrash inode cluster buffers under memory pressure to clean dirty inodes. This means inode writeback no longer stalls on memory allocation or read IO, and hence can be done asynchronously without generating memory pressure. As a result, blocking inode writeback in reclaim is no longer necessary to prevent reclaim priority windup as cleaning dirty inodes is no longer dependent on having memory reserves available for the filesystem to make progress reclaiming inodes. Hence we can convert inode reclaim to be non-blocking for shrinker callouts, both for direct reclaim and kswapd. On a vanilla kernel, running a 16-way fsmark create workload on a 4 node/16p/16GB RAM machine, I can reliably pin 14.75GB of RAM via userspace mlock(). The OOM killer gets invoked at 15GB of pinned RAM. Without the inode cluster pinning, this non-blocking reclaim patch triggers premature OOM killer invocation with the same memory pinning, sometimes with as much as 45% of RAM being free. It's trivially easy to trigger the OOM killer when reclaim does not block. With pinning inode clusters in RAM and then adding this patch, I can reliably pin 14.5GB of RAM and still have the fsmark workload run to completion. The OOM killer gets invoked 14.75GB of pinned RAM, which is only a small amount of memory less than the vanilla kernel. It is much more reliable than just with async reclaim alone. simoops shows that allocation stalls go away when async reclaim is used. Vanilla kernel: Run time: 1924 seconds Read latency (p50: 3,305,472) (p95: 3,723,264) (p99: 4,001,792) Write latency (p50: 184,064) (p95: 553,984) (p99: 807,936) Allocation latency (p50: 2,641,920) (p95: 3,911,680) (p99: 4,464,640) work rate = 13.45/sec (avg 13.44/sec) (p50: 13.46) (p95: 13.58) (p99: 13.70) alloc stall rate = 3.80/sec (avg: 2.59) (p50: 2.54) (p95: 2.96) (p99: 3.02) With inode cluster pinning and async reclaim: Run time: 1924 seconds Read latency (p50: 3,305,472) (p95: 3,715,072) (p99: 3,977,216) Write latency (p50: 187,648) (p95: 553,984) (p99: 789,504) Allocation latency (p50: 2,748,416) (p95: 3,919,872) (p99: 4,448,256) work rate = 13.28/sec (avg 13.32/sec) (p50: 13.26) (p95: 13.34) (p99: 13.34) alloc stall rate = 0.02/sec (avg: 0.02) (p50: 0.01) (p95: 0.03) (p99: 0.03) Latencies don't really change much, nor does the work rate. However, allocation almost never stalls with these changes, whilst the vanilla kernel is sometimes reporting 20 stalls/s over a 60s sample period. This difference is due to inode reclaim being largely non-blocking now. IOWs, once we have pinned inode cluster buffers, we can make inode reclaim non-blocking without a major risk of premature and/or spurious OOM killer invocation, and without any changes to memory reclaim infrastructure. Signed-off-by: Dave Chinner Reviewed-by: Amir Goldstein Reviewed-by: Darrick J. Wong Reviewed-by: Brian Foster Signed-off-by: Darrick J. Wong --- fs/xfs/xfs_icache.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/xfs/xfs_icache.c b/fs/xfs/xfs_icache.c index 59dea8178ae3c..01f8efc5f59cd 100644 --- a/fs/xfs/xfs_icache.c +++ b/fs/xfs/xfs_icache.c @@ -1402,7 +1402,7 @@ xfs_reclaim_inodes_nr( xfs_reclaim_work_queue(mp); xfs_ail_push_all(mp->m_ail); - return xfs_reclaim_inodes_ag(mp, SYNC_TRYLOCK | SYNC_WAIT, &nr_to_scan); + return xfs_reclaim_inodes_ag(mp, SYNC_TRYLOCK, &nr_to_scan); } /* -- GitLab From 617825fe3489ac231790e5c843107168838b8547 Mon Sep 17 00:00:00 2001 From: Dave Chinner Date: Mon, 29 Jun 2020 14:49:16 -0700 Subject: [PATCH 0309/1476] xfs: remove IO submission from xfs_reclaim_inode() We no longer need to issue IO from shrinker based inode reclaim to prevent spurious OOM killer invocation. This leaves only the global filesystem management operations such as unmount needing to writeback dirty inodes and reclaim them. Instead of using the reclaim pass to write dirty inodes before reclaiming them, use the AIL to push all the dirty inodes before we try to reclaim them. This allows us to remove all the conditional SYNC_WAIT locking and the writeback code from xfs_reclaim_inode() and greatly simplify the checks we need to do to reclaim an inode. Signed-off-by: Dave Chinner Reviewed-by: Darrick J. Wong Reviewed-by: Brian Foster Signed-off-by: Darrick J. Wong --- fs/xfs/xfs_icache.c | 117 ++++++++++++-------------------------------- 1 file changed, 31 insertions(+), 86 deletions(-) diff --git a/fs/xfs/xfs_icache.c b/fs/xfs/xfs_icache.c index 01f8efc5f59cd..fa4df9b4edb56 100644 --- a/fs/xfs/xfs_icache.c +++ b/fs/xfs/xfs_icache.c @@ -1111,24 +1111,17 @@ xfs_reclaim_inode_grab( * dirty, async => requeue * dirty, sync => flush, wait and reclaim */ -STATIC int +static bool xfs_reclaim_inode( struct xfs_inode *ip, struct xfs_perag *pag, int sync_mode) { - struct xfs_buf *bp = NULL; xfs_ino_t ino = ip->i_ino; /* for radix_tree_delete */ - int error; -restart: - error = 0; xfs_ilock(ip, XFS_ILOCK_EXCL); - if (!xfs_iflock_nowait(ip)) { - if (!(sync_mode & SYNC_WAIT)) - goto out; - xfs_iflock(ip); - } + if (!xfs_iflock_nowait(ip)) + goto out; if (XFS_FORCED_SHUTDOWN(ip->i_mount)) { xfs_iunpin_wait(ip); @@ -1136,52 +1129,12 @@ restart: xfs_iflush_abort(ip); goto reclaim; } - if (xfs_ipincount(ip)) { - if (!(sync_mode & SYNC_WAIT)) - goto out_ifunlock; - xfs_iunpin_wait(ip); - } - if (xfs_inode_clean(ip)) { - xfs_ifunlock(ip); - goto reclaim; - } - - /* - * Never flush out dirty data during non-blocking reclaim, as it would - * just contend with AIL pushing trying to do the same job. - */ - if (!(sync_mode & SYNC_WAIT)) + if (xfs_ipincount(ip)) + goto out_ifunlock; + if (!xfs_inode_clean(ip)) goto out_ifunlock; - /* - * Now we have an inode that needs flushing. - * - * Note that xfs_iflush will never block on the inode buffer lock, as - * xfs_ifree_cluster() can lock the inode buffer before it locks the - * ip->i_lock, and we are doing the exact opposite here. As a result, - * doing a blocking xfs_imap_to_bp() to get the cluster buffer would - * result in an ABBA deadlock with xfs_ifree_cluster(). - * - * As xfs_ifree_cluser() must gather all inodes that are active in the - * cache to mark them stale, if we hit this case we don't actually want - * to do IO here - we want the inode marked stale so we can simply - * reclaim it. Hence if we get an EAGAIN error here, just unlock the - * inode, back off and try again. Hopefully the next pass through will - * see the stale flag set on the inode. - */ - error = xfs_iflush(ip, &bp); - if (error == -EAGAIN) { - xfs_iunlock(ip, XFS_ILOCK_EXCL); - /* backoff longer than in xfs_ifree_cluster */ - delay(2); - goto restart; - } - - if (!error) { - error = xfs_bwrite(bp); - xfs_buf_relse(bp); - } - + xfs_ifunlock(ip); reclaim: ASSERT(!xfs_isiflocked(ip)); @@ -1231,21 +1184,14 @@ reclaim: ASSERT(xfs_inode_clean(ip)); __xfs_inode_free(ip); - return error; + return true; out_ifunlock: xfs_ifunlock(ip); out: - xfs_iflags_clear(ip, XFS_IRECLAIM); xfs_iunlock(ip, XFS_ILOCK_EXCL); - /* - * We could return -EAGAIN here to make reclaim rescan the inode tree in - * a short while. However, this just burns CPU time scanning the tree - * waiting for IO to complete and the reclaim work never goes back to - * the idle state. Instead, return 0 to let the next scheduled - * background reclaim attempt to reclaim the inode again. - */ - return 0; + xfs_iflags_clear(ip, XFS_IRECLAIM); + return false; } /* @@ -1253,21 +1199,22 @@ out: * corrupted, we still want to try to reclaim all the inodes. If we don't, * then a shut down during filesystem unmount reclaim walk leak all the * unreclaimed inodes. + * + * Returns non-zero if any AGs or inodes were skipped in the reclaim pass + * so that callers that want to block until all dirty inodes are written back + * and reclaimed can sanely loop. */ -STATIC int +static int xfs_reclaim_inodes_ag( struct xfs_mount *mp, int flags, int *nr_to_scan) { struct xfs_perag *pag; - int error = 0; - int last_error = 0; xfs_agnumber_t ag; int trylock = flags & SYNC_TRYLOCK; int skipped; -restart: ag = 0; skipped = 0; while ((pag = xfs_perag_get_tag(mp, ag, XFS_ICI_RECLAIM_TAG))) { @@ -1341,9 +1288,8 @@ restart: for (i = 0; i < nr_found; i++) { if (!batch[i]) continue; - error = xfs_reclaim_inode(batch[i], pag, flags); - if (error && last_error != -EFSCORRUPTED) - last_error = error; + if (!xfs_reclaim_inode(batch[i], pag, flags)) + skipped++; } *nr_to_scan -= XFS_LOOKUP_BATCH; @@ -1359,19 +1305,7 @@ restart: mutex_unlock(&pag->pag_ici_reclaim_lock); xfs_perag_put(pag); } - - /* - * if we skipped any AG, and we still have scan count remaining, do - * another pass this time using blocking reclaim semantics (i.e - * waiting on the reclaim locks and ignoring the reclaim cursors). This - * ensure that when we get more reclaimers than AGs we block rather - * than spin trying to execute reclaim. - */ - if (skipped && (flags & SYNC_WAIT) && *nr_to_scan > 0) { - trylock = 0; - goto restart; - } - return last_error; + return skipped; } int @@ -1380,8 +1314,18 @@ xfs_reclaim_inodes( int mode) { int nr_to_scan = INT_MAX; + int skipped; - return xfs_reclaim_inodes_ag(mp, mode, &nr_to_scan); + xfs_reclaim_inodes_ag(mp, mode, &nr_to_scan); + if (!(mode & SYNC_WAIT)) + return 0; + + do { + xfs_ail_push_all_sync(mp->m_ail); + skipped = xfs_reclaim_inodes_ag(mp, mode, &nr_to_scan); + } while (skipped > 0); + + return 0; } /* @@ -1402,7 +1346,8 @@ xfs_reclaim_inodes_nr( xfs_reclaim_work_queue(mp); xfs_ail_push_all(mp->m_ail); - return xfs_reclaim_inodes_ag(mp, SYNC_TRYLOCK, &nr_to_scan); + xfs_reclaim_inodes_ag(mp, SYNC_TRYLOCK, &nr_to_scan); + return 0; } /* -- GitLab From 0e8e2c6343dd74a4f55f8507a9fae9064d456436 Mon Sep 17 00:00:00 2001 From: Dave Chinner Date: Mon, 29 Jun 2020 14:49:16 -0700 Subject: [PATCH 0310/1476] xfs: allow multiple reclaimers per AG Inode reclaim will still throttle direct reclaim on the per-ag reclaim locks. This is no longer necessary as reclaim can run non-blocking now. Hence we can remove these locks so that we don't arbitrarily block reclaimers just because there are more direct reclaimers than there are AGs. This can result in multiple reclaimers working on the same range of an AG, but this doesn't cause any apparent issues. Optimising the spread of concurrent reclaimers for best efficiency can be done in a future patchset. Signed-off-by: Dave Chinner Reviewed-by: Darrick J. Wong Reviewed-by: Brian Foster Signed-off-by: Darrick J. Wong --- fs/xfs/xfs_icache.c | 31 ++++++++++++------------------- fs/xfs/xfs_mount.c | 4 ---- fs/xfs/xfs_mount.h | 1 - 3 files changed, 12 insertions(+), 24 deletions(-) diff --git a/fs/xfs/xfs_icache.c b/fs/xfs/xfs_icache.c index fa4df9b4edb56..592eab23c6e73 100644 --- a/fs/xfs/xfs_icache.c +++ b/fs/xfs/xfs_icache.c @@ -1211,12 +1211,9 @@ xfs_reclaim_inodes_ag( int *nr_to_scan) { struct xfs_perag *pag; - xfs_agnumber_t ag; - int trylock = flags & SYNC_TRYLOCK; - int skipped; + xfs_agnumber_t ag = 0; + int skipped = 0; - ag = 0; - skipped = 0; while ((pag = xfs_perag_get_tag(mp, ag, XFS_ICI_RECLAIM_TAG))) { unsigned long first_index = 0; int done = 0; @@ -1224,15 +1221,13 @@ xfs_reclaim_inodes_ag( ag = pag->pag_agno + 1; - if (trylock) { - if (!mutex_trylock(&pag->pag_ici_reclaim_lock)) { - skipped++; - xfs_perag_put(pag); - continue; - } - first_index = pag->pag_ici_reclaim_cursor; - } else - mutex_lock(&pag->pag_ici_reclaim_lock); + /* + * If the cursor is not zero, we haven't scanned the whole AG + * so we might have skipped inodes here. + */ + first_index = READ_ONCE(pag->pag_ici_reclaim_cursor); + if (first_index) + skipped++; do { struct xfs_inode *batch[XFS_LOOKUP_BATCH]; @@ -1298,11 +1293,9 @@ xfs_reclaim_inodes_ag( } while (nr_found && !done && *nr_to_scan > 0); - if (trylock && !done) - pag->pag_ici_reclaim_cursor = first_index; - else - pag->pag_ici_reclaim_cursor = 0; - mutex_unlock(&pag->pag_ici_reclaim_lock); + if (done) + first_index = 0; + WRITE_ONCE(pag->pag_ici_reclaim_cursor, first_index); xfs_perag_put(pag); } return skipped; diff --git a/fs/xfs/xfs_mount.c b/fs/xfs/xfs_mount.c index d5dcf98698600..03158b42a1943 100644 --- a/fs/xfs/xfs_mount.c +++ b/fs/xfs/xfs_mount.c @@ -148,7 +148,6 @@ xfs_free_perag( ASSERT(atomic_read(&pag->pag_ref) == 0); xfs_iunlink_destroy(pag); xfs_buf_hash_destroy(pag); - mutex_destroy(&pag->pag_ici_reclaim_lock); call_rcu(&pag->rcu_head, __xfs_free_perag); } } @@ -200,7 +199,6 @@ xfs_initialize_perag( pag->pag_agno = index; pag->pag_mount = mp; spin_lock_init(&pag->pag_ici_lock); - mutex_init(&pag->pag_ici_reclaim_lock); INIT_RADIX_TREE(&pag->pag_ici_root, GFP_ATOMIC); if (xfs_buf_hash_init(pag)) goto out_free_pag; @@ -242,7 +240,6 @@ xfs_initialize_perag( out_hash_destroy: xfs_buf_hash_destroy(pag); out_free_pag: - mutex_destroy(&pag->pag_ici_reclaim_lock); kmem_free(pag); out_unwind_new_pags: /* unwind any prior newly initialized pags */ @@ -252,7 +249,6 @@ out_unwind_new_pags: break; xfs_buf_hash_destroy(pag); xfs_iunlink_destroy(pag); - mutex_destroy(&pag->pag_ici_reclaim_lock); kmem_free(pag); } return error; diff --git a/fs/xfs/xfs_mount.h b/fs/xfs/xfs_mount.h index 3725d25ad97e8..a72cfcaa4ad12 100644 --- a/fs/xfs/xfs_mount.h +++ b/fs/xfs/xfs_mount.h @@ -354,7 +354,6 @@ typedef struct xfs_perag { spinlock_t pag_ici_lock; /* incore inode cache lock */ struct radix_tree_root pag_ici_root; /* incore inode cache root */ int pag_ici_reclaimable; /* reclaimable inodes */ - struct mutex pag_ici_reclaim_lock; /* serialisation point */ unsigned long pag_ici_reclaim_cursor; /* reclaim restart point */ /* buffer cache index */ -- GitLab From 9552e14d3e879a3b4281427ef368271f371ea167 Mon Sep 17 00:00:00 2001 From: Dave Chinner Date: Mon, 29 Jun 2020 14:49:17 -0700 Subject: [PATCH 0311/1476] xfs: don't block inode reclaim on the ILOCK When we attempt to reclaim an inode, the first thing we do is take the inode lock. This is blocking right now, so if the inode being accessed by something else (e.g. being flushed to the cluster buffer) we will block here. Change this to a trylock so that we do not block inode reclaim unnecessarily here. Signed-off-by: Dave Chinner Reviewed-by: Darrick J. Wong Reviewed-by: Brian Foster Signed-off-by: Darrick J. Wong --- fs/xfs/xfs_icache.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/fs/xfs/xfs_icache.c b/fs/xfs/xfs_icache.c index 592eab23c6e73..f387ec21dd354 100644 --- a/fs/xfs/xfs_icache.c +++ b/fs/xfs/xfs_icache.c @@ -1119,9 +1119,10 @@ xfs_reclaim_inode( { xfs_ino_t ino = ip->i_ino; /* for radix_tree_delete */ - xfs_ilock(ip, XFS_ILOCK_EXCL); - if (!xfs_iflock_nowait(ip)) + if (!xfs_ilock_nowait(ip, XFS_ILOCK_EXCL)) goto out; + if (!xfs_iflock_nowait(ip)) + goto out_iunlock; if (XFS_FORCED_SHUTDOWN(ip->i_mount)) { xfs_iunpin_wait(ip); @@ -1188,8 +1189,9 @@ reclaim: out_ifunlock: xfs_ifunlock(ip); -out: +out_iunlock: xfs_iunlock(ip, XFS_ILOCK_EXCL); +out: xfs_iflags_clear(ip, XFS_IRECLAIM); return false; } -- GitLab From 50718b8d73dda01bb168f9f3b16f6311a2debe7b Mon Sep 17 00:00:00 2001 From: Dave Chinner Date: Wed, 1 Jul 2020 10:21:05 -0700 Subject: [PATCH 0312/1476] xfs: remove SYNC_TRYLOCK from inode reclaim All background reclaim is SYNC_TRYLOCK already, and even blocking reclaim (SYNC_WAIT) can use trylock mechanisms as xfs_reclaim_inodes_ag() will keep cycling until there are no more reclaimable inodes. Hence we can kill SYNC_TRYLOCK from inode reclaim and make everything unconditionally non-blocking. We remove all the optimistic "avoid blocking on locks" checks done in xfs_reclaim_inode_grab() as nothing blocks on locks anymore. Further, checking XFS_IFLOCK optimistically can result in detecting inodes in the process of being cleaned (i.e. between being removed from the AIL and having the flush lock dropped), so for xfs_reclaim_inodes() to reliably reclaim all inodes we need to drop these checks anyway. Signed-off-by: Dave Chinner Reviewed-by: Darrick J. Wong Reviewed-by: Brian Foster Signed-off-by: Darrick J. Wong --- fs/xfs/xfs_icache.c | 63 ++++++++++++++++++--------------------------- 1 file changed, 25 insertions(+), 38 deletions(-) diff --git a/fs/xfs/xfs_icache.c b/fs/xfs/xfs_icache.c index f387ec21dd354..8d18117242e1f 100644 --- a/fs/xfs/xfs_icache.c +++ b/fs/xfs/xfs_icache.c @@ -174,7 +174,7 @@ xfs_reclaim_worker( struct xfs_mount *mp = container_of(to_delayed_work(work), struct xfs_mount, m_reclaim_work); - xfs_reclaim_inodes(mp, SYNC_TRYLOCK); + xfs_reclaim_inodes(mp, 0); xfs_reclaim_work_queue(mp); } @@ -1028,48 +1028,37 @@ xfs_cowblocks_worker( /* * Grab the inode for reclaim exclusively. - * Return 0 if we grabbed it, non-zero otherwise. + * + * We have found this inode via a lookup under RCU, so the inode may have + * already been freed, or it may be in the process of being recycled by + * xfs_iget(). In both cases, the inode will have XFS_IRECLAIM set. If the inode + * has been fully recycled by the time we get the i_flags_lock, XFS_IRECLAIMABLE + * will not be set. Hence we need to check for both these flag conditions to + * avoid inodes that are no longer reclaim candidates. + * + * Note: checking for other state flags here, under the i_flags_lock or not, is + * racy and should be avoided. Those races should be resolved only after we have + * ensured that we are able to reclaim this inode and the world can see that we + * are going to reclaim it. + * + * Return true if we grabbed it, false otherwise. */ -STATIC int +static bool xfs_reclaim_inode_grab( - struct xfs_inode *ip, - int flags) + struct xfs_inode *ip) { ASSERT(rcu_read_lock_held()); - /* quick check for stale RCU freed inode */ - if (!ip->i_ino) - return 1; - - /* - * If we are asked for non-blocking operation, do unlocked checks to - * see if the inode already is being flushed or in reclaim to avoid - * lock traffic. - */ - if ((flags & SYNC_TRYLOCK) && - __xfs_iflags_test(ip, XFS_IFLOCK | XFS_IRECLAIM)) - return 1; - - /* - * The radix tree lock here protects a thread in xfs_iget from racing - * with us starting reclaim on the inode. Once we have the - * XFS_IRECLAIM flag set it will not touch us. - * - * Due to RCU lookup, we may find inodes that have been freed and only - * have XFS_IRECLAIM set. Indeed, we may see reallocated inodes that - * aren't candidates for reclaim at all, so we must check the - * XFS_IRECLAIMABLE is set first before proceeding to reclaim. - */ spin_lock(&ip->i_flags_lock); if (!__xfs_iflags_test(ip, XFS_IRECLAIMABLE) || __xfs_iflags_test(ip, XFS_IRECLAIM)) { /* not a reclaim candidate. */ spin_unlock(&ip->i_flags_lock); - return 1; + return false; } __xfs_iflags_set(ip, XFS_IRECLAIM); spin_unlock(&ip->i_flags_lock); - return 0; + return true; } /* @@ -1114,8 +1103,7 @@ xfs_reclaim_inode_grab( static bool xfs_reclaim_inode( struct xfs_inode *ip, - struct xfs_perag *pag, - int sync_mode) + struct xfs_perag *pag) { xfs_ino_t ino = ip->i_ino; /* for radix_tree_delete */ @@ -1209,7 +1197,6 @@ out: static int xfs_reclaim_inodes_ag( struct xfs_mount *mp, - int flags, int *nr_to_scan) { struct xfs_perag *pag; @@ -1254,7 +1241,7 @@ xfs_reclaim_inodes_ag( for (i = 0; i < nr_found; i++) { struct xfs_inode *ip = batch[i]; - if (done || xfs_reclaim_inode_grab(ip, flags)) + if (done || !xfs_reclaim_inode_grab(ip)) batch[i] = NULL; /* @@ -1285,7 +1272,7 @@ xfs_reclaim_inodes_ag( for (i = 0; i < nr_found; i++) { if (!batch[i]) continue; - if (!xfs_reclaim_inode(batch[i], pag, flags)) + if (!xfs_reclaim_inode(batch[i], pag)) skipped++; } @@ -1311,13 +1298,13 @@ xfs_reclaim_inodes( int nr_to_scan = INT_MAX; int skipped; - xfs_reclaim_inodes_ag(mp, mode, &nr_to_scan); + xfs_reclaim_inodes_ag(mp, &nr_to_scan); if (!(mode & SYNC_WAIT)) return 0; do { xfs_ail_push_all_sync(mp->m_ail); - skipped = xfs_reclaim_inodes_ag(mp, mode, &nr_to_scan); + skipped = xfs_reclaim_inodes_ag(mp, &nr_to_scan); } while (skipped > 0); return 0; @@ -1341,7 +1328,7 @@ xfs_reclaim_inodes_nr( xfs_reclaim_work_queue(mp); xfs_ail_push_all(mp->m_ail); - xfs_reclaim_inodes_ag(mp, SYNC_TRYLOCK, &nr_to_scan); + xfs_reclaim_inodes_ag(mp, &nr_to_scan); return 0; } -- GitLab From 4d0bab3a44686f26be7ee7295c6c1987605ae35e Mon Sep 17 00:00:00 2001 From: Dave Chinner Date: Wed, 1 Jul 2020 10:21:28 -0700 Subject: [PATCH 0313/1476] xfs: remove SYNC_WAIT from xfs_reclaim_inodes() Clean up xfs_reclaim_inodes() callers. Most callers want blocking behaviour, so just make the existing SYNC_WAIT behaviour the default. For the xfs_reclaim_worker(), just call xfs_reclaim_inodes_ag() directly because we just want optimistic clean inode reclaim to be done in the background. For xfs_quiesce_attr() we can just remove the inode reclaim calls as they are a historic relic that was required to flush dirty inodes that contained unlogged changes. We now log all changes to the inodes, so the sync AIL push from xfs_log_quiesce() called by xfs_quiesce_attr() will do all the required inode writeback for freeze. Seeing as we now want to loop until all reclaimable inodes have been reclaimed, make xfs_reclaim_inodes() loop on the XFS_ICI_RECLAIM_TAG tag rather than having xfs_reclaim_inodes_ag() tell it that inodes were skipped. This is much more reliable and will always loop until all reclaimable inodes are reclaimed. Signed-off-by: Dave Chinner Reviewed-by: Darrick J. Wong Reviewed-by: Brian Foster Signed-off-by: Darrick J. Wong --- fs/xfs/xfs_icache.c | 79 +++++++++++++++++---------------------------- fs/xfs/xfs_icache.h | 2 +- fs/xfs/xfs_mount.c | 11 +++---- fs/xfs/xfs_super.c | 3 -- 4 files changed, 35 insertions(+), 60 deletions(-) diff --git a/fs/xfs/xfs_icache.c b/fs/xfs/xfs_icache.c index 8d18117242e1f..f4e7b98d96399 100644 --- a/fs/xfs/xfs_icache.c +++ b/fs/xfs/xfs_icache.c @@ -160,24 +160,6 @@ xfs_reclaim_work_queue( rcu_read_unlock(); } -/* - * This is a fast pass over the inode cache to try to get reclaim moving on as - * many inodes as possible in a short period of time. It kicks itself every few - * seconds, as well as being kicked by the inode cache shrinker when memory - * goes low. It scans as quickly as possible avoiding locked inodes or those - * already being flushed, and once done schedules a future pass. - */ -void -xfs_reclaim_worker( - struct work_struct *work) -{ - struct xfs_mount *mp = container_of(to_delayed_work(work), - struct xfs_mount, m_reclaim_work); - - xfs_reclaim_inodes(mp, 0); - xfs_reclaim_work_queue(mp); -} - static void xfs_perag_set_reclaim_tag( struct xfs_perag *pag) @@ -1100,7 +1082,7 @@ xfs_reclaim_inode_grab( * dirty, async => requeue * dirty, sync => flush, wait and reclaim */ -static bool +static void xfs_reclaim_inode( struct xfs_inode *ip, struct xfs_perag *pag) @@ -1173,7 +1155,7 @@ reclaim: ASSERT(xfs_inode_clean(ip)); __xfs_inode_free(ip); - return true; + return; out_ifunlock: xfs_ifunlock(ip); @@ -1181,7 +1163,6 @@ out_iunlock: xfs_iunlock(ip, XFS_ILOCK_EXCL); out: xfs_iflags_clear(ip, XFS_IRECLAIM); - return false; } /* @@ -1194,14 +1175,13 @@ out: * so that callers that want to block until all dirty inodes are written back * and reclaimed can sanely loop. */ -static int +static void xfs_reclaim_inodes_ag( struct xfs_mount *mp, int *nr_to_scan) { struct xfs_perag *pag; xfs_agnumber_t ag = 0; - int skipped = 0; while ((pag = xfs_perag_get_tag(mp, ag, XFS_ICI_RECLAIM_TAG))) { unsigned long first_index = 0; @@ -1210,14 +1190,7 @@ xfs_reclaim_inodes_ag( ag = pag->pag_agno + 1; - /* - * If the cursor is not zero, we haven't scanned the whole AG - * so we might have skipped inodes here. - */ first_index = READ_ONCE(pag->pag_ici_reclaim_cursor); - if (first_index) - skipped++; - do { struct xfs_inode *batch[XFS_LOOKUP_BATCH]; int i; @@ -1270,16 +1243,12 @@ xfs_reclaim_inodes_ag( rcu_read_unlock(); for (i = 0; i < nr_found; i++) { - if (!batch[i]) - continue; - if (!xfs_reclaim_inode(batch[i], pag)) - skipped++; + if (batch[i]) + xfs_reclaim_inode(batch[i], pag); } *nr_to_scan -= XFS_LOOKUP_BATCH; - cond_resched(); - } while (nr_found && !done && *nr_to_scan > 0); if (done) @@ -1287,27 +1256,18 @@ xfs_reclaim_inodes_ag( WRITE_ONCE(pag->pag_ici_reclaim_cursor, first_index); xfs_perag_put(pag); } - return skipped; } -int +void xfs_reclaim_inodes( - xfs_mount_t *mp, - int mode) + struct xfs_mount *mp) { int nr_to_scan = INT_MAX; - int skipped; - xfs_reclaim_inodes_ag(mp, &nr_to_scan); - if (!(mode & SYNC_WAIT)) - return 0; - - do { + while (radix_tree_tagged(&mp->m_perag_tree, XFS_ICI_RECLAIM_TAG)) { xfs_ail_push_all_sync(mp->m_ail); - skipped = xfs_reclaim_inodes_ag(mp, &nr_to_scan); - } while (skipped > 0); - - return 0; + xfs_reclaim_inodes_ag(mp, &nr_to_scan); + }; } /* @@ -1426,6 +1386,25 @@ xfs_inode_matches_eofb( return true; } +/* + * This is a fast pass over the inode cache to try to get reclaim moving on as + * many inodes as possible in a short period of time. It kicks itself every few + * seconds, as well as being kicked by the inode cache shrinker when memory + * goes low. It scans as quickly as possible avoiding locked inodes or those + * already being flushed, and once done schedules a future pass. + */ +void +xfs_reclaim_worker( + struct work_struct *work) +{ + struct xfs_mount *mp = container_of(to_delayed_work(work), + struct xfs_mount, m_reclaim_work); + int nr_to_scan = INT_MAX; + + xfs_reclaim_inodes_ag(mp, &nr_to_scan); + xfs_reclaim_work_queue(mp); +} + STATIC int xfs_inode_free_eofblocks( struct xfs_inode *ip, diff --git a/fs/xfs/xfs_icache.h b/fs/xfs/xfs_icache.h index 93b54e7d55f0d..ae92ca53de423 100644 --- a/fs/xfs/xfs_icache.h +++ b/fs/xfs/xfs_icache.h @@ -51,7 +51,7 @@ void xfs_inode_free(struct xfs_inode *ip); void xfs_reclaim_worker(struct work_struct *work); -int xfs_reclaim_inodes(struct xfs_mount *mp, int mode); +void xfs_reclaim_inodes(struct xfs_mount *mp); int xfs_reclaim_inodes_count(struct xfs_mount *mp); long xfs_reclaim_inodes_nr(struct xfs_mount *mp, int nr_to_scan); diff --git a/fs/xfs/xfs_mount.c b/fs/xfs/xfs_mount.c index 03158b42a1943..c8ae49a1e99c3 100644 --- a/fs/xfs/xfs_mount.c +++ b/fs/xfs/xfs_mount.c @@ -1011,7 +1011,7 @@ xfs_mountfs( * quota inodes. */ cancel_delayed_work_sync(&mp->m_reclaim_work); - xfs_reclaim_inodes(mp, SYNC_WAIT); + xfs_reclaim_inodes(mp); xfs_health_unmount(mp); out_log_dealloc: mp->m_flags |= XFS_MOUNT_UNMOUNTING; @@ -1088,13 +1088,12 @@ xfs_unmountfs( xfs_ail_push_all_sync(mp->m_ail); /* - * And reclaim all inodes. At this point there should be no dirty - * inodes and none should be pinned or locked, but use synchronous - * reclaim just to be sure. We can stop background inode reclaim - * here as well if it is still running. + * Reclaim all inodes. At this point there should be no dirty inodes and + * none should be pinned or locked. Stop background inode reclaim here + * if it is still running. */ cancel_delayed_work_sync(&mp->m_reclaim_work); - xfs_reclaim_inodes(mp, SYNC_WAIT); + xfs_reclaim_inodes(mp); xfs_health_unmount(mp); xfs_qm_unmount(mp); diff --git a/fs/xfs/xfs_super.c b/fs/xfs/xfs_super.c index 379cbff438bce..5a5d9453cf51d 100644 --- a/fs/xfs/xfs_super.c +++ b/fs/xfs/xfs_super.c @@ -890,9 +890,6 @@ xfs_quiesce_attr( /* force the log to unpin objects from the now complete transactions */ xfs_log_force(mp, XFS_LOG_SYNC); - /* reclaim inodes to do any IO before the freeze completes */ - xfs_reclaim_inodes(mp, 0); - xfs_reclaim_inodes(mp, SYNC_WAIT); /* Push the superblock and write an unmount record */ error = xfs_log_sbcount(mp); -- GitLab From 02511a5a6a49f9730ad215caa77e4d980008c6c6 Mon Sep 17 00:00:00 2001 From: Dave Chinner Date: Mon, 29 Jun 2020 14:49:18 -0700 Subject: [PATCH 0314/1476] xfs: clean up inode reclaim comments Inode reclaim is quite different now to the way described in various comments, so update all the comments explaining what it does and how it works. Signed-off-by: Dave Chinner Reviewed-by: Darrick J. Wong Reviewed-by: Brian Foster Signed-off-by: Darrick J. Wong --- fs/xfs/xfs_icache.c | 128 ++++++++++++-------------------------------- 1 file changed, 35 insertions(+), 93 deletions(-) diff --git a/fs/xfs/xfs_icache.c b/fs/xfs/xfs_icache.c index f4e7b98d96399..dc90a81abb1af 100644 --- a/fs/xfs/xfs_icache.c +++ b/fs/xfs/xfs_icache.c @@ -141,11 +141,8 @@ xfs_inode_free( } /* - * Queue a new inode reclaim pass if there are reclaimable inodes and there - * isn't a reclaim pass already in progress. By default it runs every 5s based - * on the xfs periodic sync default of 30s. Perhaps this should have it's own - * tunable, but that can be done if this method proves to be ineffective or too - * aggressive. + * Queue background inode reclaim work if there are reclaimable inodes and there + * isn't reclaim work already scheduled or in progress. */ static void xfs_reclaim_work_queue( @@ -600,48 +597,31 @@ out_destroy: } /* - * Look up an inode by number in the given file system. - * The inode is looked up in the cache held in each AG. - * If the inode is found in the cache, initialise the vfs inode - * if necessary. + * Look up an inode by number in the given file system. The inode is looked up + * in the cache held in each AG. If the inode is found in the cache, initialise + * the vfs inode if necessary. * - * If it is not in core, read it in from the file system's device, - * add it to the cache and initialise the vfs inode. + * If it is not in core, read it in from the file system's device, add it to the + * cache and initialise the vfs inode. * * The inode is locked according to the value of the lock_flags parameter. - * This flag parameter indicates how and if the inode's IO lock and inode lock - * should be taken. - * - * mp -- the mount point structure for the current file system. It points - * to the inode hash table. - * tp -- a pointer to the current transaction if there is one. This is - * simply passed through to the xfs_iread() call. - * ino -- the number of the inode desired. This is the unique identifier - * within the file system for the inode being requested. - * lock_flags -- flags indicating how to lock the inode. See the comment - * for xfs_ilock() for a list of valid values. + * Inode lookup is only done during metadata operations and not as part of the + * data IO path. Hence we only allow locking of the XFS_ILOCK during lookup. */ int xfs_iget( - xfs_mount_t *mp, - xfs_trans_t *tp, - xfs_ino_t ino, - uint flags, - uint lock_flags, - xfs_inode_t **ipp) + struct xfs_mount *mp, + struct xfs_trans *tp, + xfs_ino_t ino, + uint flags, + uint lock_flags, + struct xfs_inode **ipp) { - xfs_inode_t *ip; - int error; - xfs_perag_t *pag; - xfs_agino_t agino; + struct xfs_inode *ip; + struct xfs_perag *pag; + xfs_agino_t agino; + int error; - /* - * xfs_reclaim_inode() uses the ILOCK to ensure an inode - * doesn't get freed while it's being referenced during a - * radix tree traversal here. It assumes this function - * aqcuires only the ILOCK (and therefore it has no need to - * involve the IOLOCK in this synchronization). - */ ASSERT((lock_flags & (XFS_IOLOCK_EXCL | XFS_IOLOCK_SHARED)) == 0); /* reject inode numbers outside existing AGs */ @@ -758,15 +738,7 @@ xfs_inode_walk_ag_grab( ASSERT(rcu_read_lock_held()); - /* - * check for stale RCU freed inode - * - * If the inode has been reallocated, it doesn't matter if it's not in - * the AG we are walking - we are walking for writeback, so if it - * passes all the "valid inode" checks and is dirty, then we'll write - * it back anyway. If it has been reallocated and still being - * initialised, the XFS_INEW check below will catch it. - */ + /* Check for stale RCU freed inode */ spin_lock(&ip->i_flags_lock); if (!ip->i_ino) goto out_unlock_noent; @@ -1044,43 +1016,16 @@ xfs_reclaim_inode_grab( } /* - * Inodes in different states need to be treated differently. The following - * table lists the inode states and the reclaim actions necessary: - * - * inode state iflush ret required action - * --------------- ---------- --------------- - * bad - reclaim - * shutdown EIO unpin and reclaim - * clean, unpinned 0 reclaim - * stale, unpinned 0 reclaim - * clean, pinned(*) 0 requeue - * stale, pinned EAGAIN requeue - * dirty, async - requeue - * dirty, sync 0 reclaim + * Inode reclaim is non-blocking, so the default action if progress cannot be + * made is to "requeue" the inode for reclaim by unlocking it and clearing the + * XFS_IRECLAIM flag. If we are in a shutdown state, we don't care about + * blocking anymore and hence we can wait for the inode to be able to reclaim + * it. * - * (*) dgc: I don't think the clean, pinned state is possible but it gets - * handled anyway given the order of checks implemented. - * - * Also, because we get the flush lock first, we know that any inode that has - * been flushed delwri has had the flush completed by the time we check that - * the inode is clean. - * - * Note that because the inode is flushed delayed write by AIL pushing, the - * flush lock may already be held here and waiting on it can result in very - * long latencies. Hence for sync reclaims, where we wait on the flush lock, - * the caller should push the AIL first before trying to reclaim inodes to - * minimise the amount of time spent waiting. For background relaim, we only - * bother to reclaim clean inodes anyway. - * - * Hence the order of actions after gaining the locks should be: - * bad => reclaim - * shutdown => unpin and reclaim - * pinned, async => requeue - * pinned, sync => unpin - * stale => reclaim - * clean => reclaim - * dirty, async => requeue - * dirty, sync => flush, wait and reclaim + * We do no IO here - if callers require inodes to be cleaned they must push the + * AIL first to trigger writeback of dirty inodes. This enables writeback to be + * done in the background in a non-blocking manner, and enables memory reclaim + * to make progress without blocking. */ static void xfs_reclaim_inode( @@ -1271,13 +1216,11 @@ xfs_reclaim_inodes( } /* - * Scan a certain number of inodes for reclaim. - * - * When called we make sure that there is a background (fast) inode reclaim in - * progress, while we will throttle the speed of reclaim via doing synchronous - * reclaim of inodes. That means if we come across dirty inodes, we wait for - * them to be cleaned, which we hope will not be very long due to the - * background walker having already kicked the IO off on those dirty inodes. + * The shrinker infrastructure determines how many inodes we should scan for + * reclaim. We want as many clean inodes ready to reclaim as possible, so we + * push the AIL here. We also want to proactively free up memory if we can to + * minimise the amount of work memory reclaim has to do so we kick the + * background reclaim if it isn't already scheduled. */ long xfs_reclaim_inodes_nr( @@ -1390,8 +1333,7 @@ xfs_inode_matches_eofb( * This is a fast pass over the inode cache to try to get reclaim moving on as * many inodes as possible in a short period of time. It kicks itself every few * seconds, as well as being kicked by the inode cache shrinker when memory - * goes low. It scans as quickly as possible avoiding locked inodes or those - * already being flushed, and once done schedules a future pass. + * goes low. */ void xfs_reclaim_worker( -- GitLab From 71e3e35646861f2f9b8d36e00720904ed3ca31cb Mon Sep 17 00:00:00 2001 From: Dave Chinner Date: Mon, 29 Jun 2020 14:49:18 -0700 Subject: [PATCH 0315/1476] xfs: rework stale inodes in xfs_ifree_cluster Once we have inodes pinning the cluster buffer and attached whenever they are dirty, we no longer have a guarantee that the items are flush locked when we lock the cluster buffer. Hence we cannot just walk the buffer log item list and modify the attached inodes. If the inode is not flush locked, we have to ILOCK it first and then flush lock it to do all the prerequisite checks needed to avoid races with other code. This is already handled by xfs_ifree_get_one_inode(), so rework the inode iteration loop and function to update all inodes in cache whether they are attached to the buffer or not. Note: we also remove the copying of the log item lsn to the ili_flush_lsn as xfs_iflush_done() now uses the XFS_ISTALE flag to trigger aborts and so flush lsn matching is not needed in IO completion for processing freed inodes. Signed-off-by: Dave Chinner Reviewed-by: Darrick J. Wong Reviewed-by: Brian Foster Signed-off-by: Darrick J. Wong --- fs/xfs/xfs_inode.c | 161 +++++++++++++++++---------------------------- 1 file changed, 62 insertions(+), 99 deletions(-) diff --git a/fs/xfs/xfs_inode.c b/fs/xfs/xfs_inode.c index 77cc9cbcd311e..1c3a8bed4875c 100644 --- a/fs/xfs/xfs_inode.c +++ b/fs/xfs/xfs_inode.c @@ -2517,17 +2517,19 @@ out: } /* - * Look up the inode number specified and mark it stale if it is found. If it is - * dirty, return the inode so it can be attached to the cluster buffer so it can - * be processed appropriately when the cluster free transaction completes. + * Look up the inode number specified and if it is not already marked XFS_ISTALE + * mark it stale. We should only find clean inodes in this lookup that aren't + * already stale. */ -static struct xfs_inode * -xfs_ifree_get_one_inode( - struct xfs_perag *pag, +static void +xfs_ifree_mark_inode_stale( + struct xfs_buf *bp, struct xfs_inode *free_ip, xfs_ino_t inum) { - struct xfs_mount *mp = pag->pag_mount; + struct xfs_mount *mp = bp->b_mount; + struct xfs_perag *pag = bp->b_pag; + struct xfs_inode_log_item *iip; struct xfs_inode *ip; retry: @@ -2535,8 +2537,10 @@ retry: ip = radix_tree_lookup(&pag->pag_ici_root, XFS_INO_TO_AGINO(mp, inum)); /* Inode not in memory, nothing to do */ - if (!ip) - goto out_rcu_unlock; + if (!ip) { + rcu_read_unlock(); + return; + } /* * because this is an RCU protected lookup, we could find a recently @@ -2547,9 +2551,9 @@ retry: spin_lock(&ip->i_flags_lock); if (ip->i_ino != inum || __xfs_iflags_test(ip, XFS_ISTALE)) { spin_unlock(&ip->i_flags_lock); - goto out_rcu_unlock; + rcu_read_unlock(); + return; } - spin_unlock(&ip->i_flags_lock); /* * Don't try to lock/unlock the current inode, but we _cannot_ skip the @@ -2559,43 +2563,53 @@ retry: */ if (ip != free_ip) { if (!xfs_ilock_nowait(ip, XFS_ILOCK_EXCL)) { + spin_unlock(&ip->i_flags_lock); rcu_read_unlock(); delay(1); goto retry; } - - /* - * Check the inode number again in case we're racing with - * freeing in xfs_reclaim_inode(). See the comments in that - * function for more information as to why the initial check is - * not sufficient. - */ - if (ip->i_ino != inum) { - xfs_iunlock(ip, XFS_ILOCK_EXCL); - goto out_rcu_unlock; - } } + ip->i_flags |= XFS_ISTALE; + spin_unlock(&ip->i_flags_lock); rcu_read_unlock(); - xfs_iflock(ip); - xfs_iflags_set(ip, XFS_ISTALE); + /* + * If we can't get the flush lock, the inode is already attached. All + * we needed to do here is mark the inode stale so buffer IO completion + * will remove it from the AIL. + */ + iip = ip->i_itemp; + if (!xfs_iflock_nowait(ip)) { + ASSERT(!list_empty(&iip->ili_item.li_bio_list)); + ASSERT(iip->ili_last_fields); + goto out_iunlock; + } + ASSERT(!iip || list_empty(&iip->ili_item.li_bio_list)); /* - * We don't need to attach clean inodes or those only with unlogged - * changes (which we throw away, anyway). + * Clean inodes can be released immediately. Everything else has to go + * through xfs_iflush_abort() on journal commit as the flock + * synchronises removal of the inode from the cluster buffer against + * inode reclaim. */ - if (!ip->i_itemp || xfs_inode_clean(ip)) { - ASSERT(ip != free_ip); + if (xfs_inode_clean(ip)) { xfs_ifunlock(ip); - xfs_iunlock(ip, XFS_ILOCK_EXCL); - goto out_no_inode; + goto out_iunlock; } - return ip; -out_rcu_unlock: - rcu_read_unlock(); -out_no_inode: - return NULL; + /* we have a dirty inode in memory that has not yet been flushed. */ + ASSERT(iip->ili_fields); + spin_lock(&iip->ili_lock); + iip->ili_last_fields = iip->ili_fields; + iip->ili_fields = 0; + iip->ili_fsync_fields = 0; + spin_unlock(&iip->ili_lock); + list_add_tail(&iip->ili_item.li_bio_list, &bp->b_li_list); + ASSERT(iip->ili_last_fields); + +out_iunlock: + if (ip != free_ip) + xfs_iunlock(ip, XFS_ILOCK_EXCL); } /* @@ -2605,26 +2619,20 @@ out_no_inode: */ STATIC int xfs_ifree_cluster( - xfs_inode_t *free_ip, - xfs_trans_t *tp, + struct xfs_inode *free_ip, + struct xfs_trans *tp, struct xfs_icluster *xic) { - xfs_mount_t *mp = free_ip->i_mount; + struct xfs_mount *mp = free_ip->i_mount; + struct xfs_ino_geometry *igeo = M_IGEO(mp); + struct xfs_buf *bp; + xfs_daddr_t blkno; + xfs_ino_t inum = xic->first_ino; int nbufs; int i, j; int ioffset; - xfs_daddr_t blkno; - xfs_buf_t *bp; - xfs_inode_t *ip; - struct xfs_inode_log_item *iip; - struct xfs_log_item *lip; - struct xfs_perag *pag; - struct xfs_ino_geometry *igeo = M_IGEO(mp); - xfs_ino_t inum; int error; - inum = xic->first_ino; - pag = xfs_perag_get(mp, XFS_INO_TO_AGNO(mp, inum)); nbufs = igeo->ialloc_blks / igeo->blocks_per_cluster; for (j = 0; j < nbufs; j++, inum += igeo->inodes_per_cluster) { @@ -2653,10 +2661,8 @@ xfs_ifree_cluster( error = xfs_trans_get_buf(tp, mp->m_ddev_targp, blkno, mp->m_bsize * igeo->blocks_per_cluster, XBF_UNMAPPED, &bp); - if (error) { - xfs_perag_put(pag); + if (error) return error; - } /* * This buffer may not have been correctly initialised as we @@ -2670,59 +2676,16 @@ xfs_ifree_cluster( bp->b_ops = &xfs_inode_buf_ops; /* - * Walk the inodes already attached to the buffer and mark them - * stale. These will all have the flush locks held, so an - * in-memory inode walk can't lock them. By marking them all - * stale first, we will not attempt to lock them in the loop - * below as the XFS_ISTALE flag will be set. + * Now we need to set all the cached clean inodes as XFS_ISTALE, + * too. This requires lookups, and will skip inodes that we've + * already marked XFS_ISTALE. */ - list_for_each_entry(lip, &bp->b_li_list, li_bio_list) { - if (lip->li_type == XFS_LI_INODE) { - iip = (struct xfs_inode_log_item *)lip; - xfs_trans_ail_copy_lsn(mp->m_ail, - &iip->ili_flush_lsn, - &iip->ili_item.li_lsn); - xfs_iflags_set(iip->ili_inode, XFS_ISTALE); - } - } - - - /* - * For each inode in memory attempt to add it to the inode - * buffer and set it up for being staled on buffer IO - * completion. This is safe as we've locked out tail pushing - * and flushing by locking the buffer. - * - * We have already marked every inode that was part of a - * transaction stale above, which means there is no point in - * even trying to lock them. - */ - for (i = 0; i < igeo->inodes_per_cluster; i++) { - ip = xfs_ifree_get_one_inode(pag, free_ip, inum + i); - if (!ip) - continue; - - iip = ip->i_itemp; - spin_lock(&iip->ili_lock); - iip->ili_last_fields = iip->ili_fields; - iip->ili_fields = 0; - iip->ili_fsync_fields = 0; - spin_unlock(&iip->ili_lock); - xfs_trans_ail_copy_lsn(mp->m_ail, &iip->ili_flush_lsn, - &iip->ili_item.li_lsn); - - list_add_tail(&iip->ili_item.li_bio_list, - &bp->b_li_list); - - if (ip != free_ip) - xfs_iunlock(ip, XFS_ILOCK_EXCL); - } + for (i = 0; i < igeo->inodes_per_cluster; i++) + xfs_ifree_mark_inode_stale(bp, free_ip, inum + i); xfs_trans_stale_inode_buf(tp, bp); xfs_trans_binval(tp, bp); } - - xfs_perag_put(pag); return 0; } -- GitLab From 48d55e2ae3ce837598c073995bbbac5d24a35fe1 Mon Sep 17 00:00:00 2001 From: Dave Chinner Date: Mon, 29 Jun 2020 14:49:18 -0700 Subject: [PATCH 0316/1476] xfs: attach inodes to the cluster buffer when dirtied Rather than attach inodes to the cluster buffer just when we are doing IO, attach the inodes to the cluster buffer when they are dirtied. The means the buffer always carries a list of dirty inodes that reference it, and we can use that list to make more fundamental changes to inode writeback that aren't otherwise possible. Signed-off-by: Dave Chinner Reviewed-by: Darrick J. Wong Reviewed-by: Brian Foster Signed-off-by: Darrick J. Wong --- fs/xfs/libxfs/xfs_trans_inode.c | 9 ++++++--- fs/xfs/xfs_buf_item.c | 1 + fs/xfs/xfs_icache.c | 1 + fs/xfs/xfs_inode.c | 24 +++++------------------- fs/xfs/xfs_inode_item.c | 16 ++++++++++++++-- 5 files changed, 27 insertions(+), 24 deletions(-) diff --git a/fs/xfs/libxfs/xfs_trans_inode.c b/fs/xfs/libxfs/xfs_trans_inode.c index ad5974365c589..e15129647e00c 100644 --- a/fs/xfs/libxfs/xfs_trans_inode.c +++ b/fs/xfs/libxfs/xfs_trans_inode.c @@ -163,13 +163,16 @@ xfs_trans_log_inode( /* * We need an explicit buffer reference for the log item but * don't want the buffer to remain attached to the transaction. - * Hold the buffer but release the transaction reference. + * Hold the buffer but release the transaction reference once + * we've attached the inode log item to the buffer log item + * list. */ xfs_buf_hold(bp); - xfs_trans_brelse(tp, bp); - spin_lock(&iip->ili_lock); iip->ili_item.li_buf = bp; + bp->b_flags |= _XBF_INODES; + list_add_tail(&iip->ili_item.li_bio_list, &bp->b_li_list); + xfs_trans_brelse(tp, bp); } /* diff --git a/fs/xfs/xfs_buf_item.c b/fs/xfs/xfs_buf_item.c index ecb3362395af3..e9428c30862a9 100644 --- a/fs/xfs/xfs_buf_item.c +++ b/fs/xfs/xfs_buf_item.c @@ -465,6 +465,7 @@ xfs_buf_item_unpin( if (bip->bli_flags & XFS_BLI_STALE_INODE) { xfs_buf_item_done(bp); xfs_iflush_done(bp); + ASSERT(list_empty(&bp->b_li_list)); } else { xfs_trans_ail_delete(lip, SHUTDOWN_LOG_IO_ERROR); xfs_buf_item_relse(bp); diff --git a/fs/xfs/xfs_icache.c b/fs/xfs/xfs_icache.c index dc90a81abb1af..58a750ce689c0 100644 --- a/fs/xfs/xfs_icache.c +++ b/fs/xfs/xfs_icache.c @@ -115,6 +115,7 @@ __xfs_inode_free( { /* asserts to verify all state is correct here */ ASSERT(atomic_read(&ip->i_pincount) == 0); + ASSERT(!ip->i_itemp || list_empty(&ip->i_itemp->ili_item.li_bio_list)); XFS_STATS_DEC(ip->i_mount, vn_active); call_rcu(&VFS_I(ip)->i_rcu, xfs_inode_free_callback); diff --git a/fs/xfs/xfs_inode.c b/fs/xfs/xfs_inode.c index 1c3a8bed4875c..c4586ac3656af 100644 --- a/fs/xfs/xfs_inode.c +++ b/fs/xfs/xfs_inode.c @@ -2584,27 +2584,24 @@ retry: ASSERT(iip->ili_last_fields); goto out_iunlock; } - ASSERT(!iip || list_empty(&iip->ili_item.li_bio_list)); /* - * Clean inodes can be released immediately. Everything else has to go - * through xfs_iflush_abort() on journal commit as the flock - * synchronises removal of the inode from the cluster buffer against - * inode reclaim. + * Inodes not attached to the buffer can be released immediately. + * Everything else has to go through xfs_iflush_abort() on journal + * commit as the flock synchronises removal of the inode from the + * cluster buffer against inode reclaim. */ - if (xfs_inode_clean(ip)) { + if (!iip || list_empty(&iip->ili_item.li_bio_list)) { xfs_ifunlock(ip); goto out_iunlock; } /* we have a dirty inode in memory that has not yet been flushed. */ - ASSERT(iip->ili_fields); spin_lock(&iip->ili_lock); iip->ili_last_fields = iip->ili_fields; iip->ili_fields = 0; iip->ili_fsync_fields = 0; spin_unlock(&iip->ili_lock); - list_add_tail(&iip->ili_item.li_bio_list, &bp->b_li_list); ASSERT(iip->ili_last_fields); out_iunlock: @@ -3818,19 +3815,8 @@ flush_out: xfs_trans_ail_copy_lsn(mp->m_ail, &iip->ili_flush_lsn, &iip->ili_item.li_lsn); - /* - * Attach the inode item callback to the buffer whether the flush - * succeeded or not. If not, the caller will shut down and fail I/O - * completion on the buffer to remove the inode from the AIL and release - * the flush lock. - */ - bp->b_flags |= _XBF_INODES; - list_add_tail(&iip->ili_item.li_bio_list, &bp->b_li_list); - /* generate the checksum. */ xfs_dinode_calc_crc(mp, dip); - - ASSERT(!list_empty(&bp->b_li_list)); return error; } diff --git a/fs/xfs/xfs_inode_item.c b/fs/xfs/xfs_inode_item.c index 64bdda72f7b27..697248b7eb2be 100644 --- a/fs/xfs/xfs_inode_item.c +++ b/fs/xfs/xfs_inode_item.c @@ -660,6 +660,10 @@ xfs_inode_item_destroy( * list for other inodes that will run this function. We remove them from the * buffer list so we can process all the inode IO completions in one AIL lock * traversal. + * + * Note: Now that we attach the log item to the buffer when we first log the + * inode in memory, we can have unflushed inodes on the buffer list here. These + * inodes will have a zero ili_last_fields, so skip over them here. */ void xfs_iflush_done( @@ -677,12 +681,15 @@ xfs_iflush_done( */ list_for_each_entry_safe(lip, n, &bp->b_li_list, li_bio_list) { iip = INODE_ITEM(lip); + if (xfs_iflags_test(iip->ili_inode, XFS_ISTALE)) { - list_del_init(&lip->li_bio_list); xfs_iflush_abort(iip->ili_inode); continue; } + if (!iip->ili_last_fields) + continue; + list_move_tail(&lip->li_bio_list, &tmp); /* Do an unlocked check for needing the AIL lock. */ @@ -728,12 +735,16 @@ xfs_iflush_done( /* * Remove the reference to the cluster buffer if the inode is * clean in memory. Drop the buffer reference once we've dropped - * the locks we hold. + * the locks we hold. If the inode is dirty in memory, we need + * to put the inode item back on the buffer list for another + * pass through the flush machinery. */ ASSERT(iip->ili_item.li_buf == bp); if (!iip->ili_fields) { iip->ili_item.li_buf = NULL; drop_buffer = true; + } else { + list_add(&lip->li_bio_list, &bp->b_li_list); } iip->ili_last_fields = 0; iip->ili_flush_lsn = 0; @@ -777,6 +788,7 @@ xfs_iflush_abort( iip->ili_flush_lsn = 0; bp = iip->ili_item.li_buf; iip->ili_item.li_buf = NULL; + list_del_init(&iip->ili_item.li_bio_list); spin_unlock(&iip->ili_lock); } xfs_ifunlock(ip); -- GitLab From 90c60e16401248a4900f3f9387f563d0178dcf34 Mon Sep 17 00:00:00 2001 From: Dave Chinner Date: Mon, 29 Jun 2020 14:49:19 -0700 Subject: [PATCH 0317/1476] xfs: xfs_iflush() is no longer necessary Now we have a cached buffer on inode log items, we don't need to do buffer lookups when flushing inodes anymore - all we need to do is lock the buffer and we are ready to go. This largely gets rid of the need for xfs_iflush(), which is essentially just a mechanism to look up the buffer and flush the inode to it. Instead, we can just call xfs_iflush_cluster() with a few modifications to ensure it also flushes the inode we already hold locked. This allows the AIL inode item pushing to be almost entirely non-blocking in XFS - we won't block unless memory allocation for the cluster inode lookup blocks or the block device queues are full. Writeback during inode reclaim becomes a little more complex because we now have to lock the buffer ourselves, but otherwise this change is largely a functional no-op that removes a whole lot of code. Signed-off-by: Dave Chinner Reviewed-by: Darrick J. Wong Reviewed-by: Brian Foster Signed-off-by: Darrick J. Wong --- fs/xfs/xfs_inode.c | 107 ++++++---------------------------------- fs/xfs/xfs_inode.h | 2 +- fs/xfs/xfs_inode_item.c | 51 +++++++------------ 3 files changed, 33 insertions(+), 127 deletions(-) diff --git a/fs/xfs/xfs_inode.c b/fs/xfs/xfs_inode.c index c4586ac3656af..4a95390486396 100644 --- a/fs/xfs/xfs_inode.c +++ b/fs/xfs/xfs_inode.c @@ -3450,7 +3450,18 @@ out_release_wip: return error; } -STATIC int +/* + * Non-blocking flush of dirty inode metadata into the backing buffer. + * + * The caller must have a reference to the inode and hold the cluster buffer + * locked. The function will walk across all the inodes on the cluster buffer it + * can find and lock without blocking, and flush them to the cluster buffer. + * + * On success, the caller must write out the buffer returned in *bp and + * release it. On failure, the filesystem will be shut down, the buffer will + * have been unlocked and released, and EFSCORRUPTED will be returned. + */ +int xfs_iflush_cluster( struct xfs_inode *ip, struct xfs_buf *bp) @@ -3485,8 +3496,6 @@ xfs_iflush_cluster( for (i = 0; i < nr_found; i++) { cip = cilist[i]; - if (cip == ip) - continue; /* * because this is an RCU protected lookup, we could find a @@ -3577,99 +3586,11 @@ out_free: kmem_free(cilist); out_put: xfs_perag_put(pag); - return error; -} - -/* - * Flush dirty inode metadata into the backing buffer. - * - * The caller must have the inode lock and the inode flush lock held. The - * inode lock will still be held upon return to the caller, and the inode - * flush lock will be released after the inode has reached the disk. - * - * The caller must write out the buffer returned in *bpp and release it. - */ -int -xfs_iflush( - struct xfs_inode *ip, - struct xfs_buf **bpp) -{ - struct xfs_mount *mp = ip->i_mount; - struct xfs_buf *bp = NULL; - struct xfs_dinode *dip; - int error; - - XFS_STATS_INC(mp, xs_iflush_count); - - ASSERT(xfs_isilocked(ip, XFS_ILOCK_EXCL|XFS_ILOCK_SHARED)); - ASSERT(xfs_isiflocked(ip)); - ASSERT(ip->i_df.if_format != XFS_DINODE_FMT_BTREE || - ip->i_df.if_nextents > XFS_IFORK_MAXEXT(ip, XFS_DATA_FORK)); - - *bpp = NULL; - - xfs_iunpin_wait(ip); - - /* - * For stale inodes we cannot rely on the backing buffer remaining - * stale in cache for the remaining life of the stale inode and so - * xfs_imap_to_bp() below may give us a buffer that no longer contains - * inodes below. We have to check this after ensuring the inode is - * unpinned so that it is safe to reclaim the stale inode after the - * flush call. - */ - if (xfs_iflags_test(ip, XFS_ISTALE)) { - xfs_ifunlock(ip); - return 0; - } - - /* - * Get the buffer containing the on-disk inode. We are doing a try-lock - * operation here, so we may get an EAGAIN error. In that case, return - * leaving the inode dirty. - * - * If we get any other error, we effectively have a corruption situation - * and we cannot flush the inode. Abort the flush and shut down. - */ - error = xfs_imap_to_bp(mp, NULL, &ip->i_imap, &dip, &bp, XBF_TRYLOCK); - if (error == -EAGAIN) { - xfs_ifunlock(ip); - return error; - } - if (error) - goto abort; - - /* - * If the buffer is pinned then push on the log now so we won't - * get stuck waiting in the write for too long. - */ - if (xfs_buf_ispinned(bp)) - xfs_log_force(mp, 0); - - /* - * Flush the provided inode then attempt to gather others from the - * cluster into the write. - * - * Note: Once we attempt to flush an inode, we must run buffer - * completion callbacks on any failure. If this fails, simulate an I/O - * failure on the buffer and shut down. - */ - error = xfs_iflush_int(ip, bp); - if (!error) - error = xfs_iflush_cluster(ip, bp); if (error) { bp->b_flags |= XBF_ASYNC; xfs_buf_ioend_fail(bp); - goto shutdown; + xfs_force_shutdown(mp, SHUTDOWN_CORRUPT_INCORE); } - - *bpp = bp; - return 0; - -abort: - xfs_iflush_abort(ip); -shutdown: - xfs_force_shutdown(mp, SHUTDOWN_CORRUPT_INCORE); return error; } @@ -3687,7 +3608,7 @@ xfs_iflush_int( ASSERT(xfs_isiflocked(ip)); ASSERT(ip->i_df.if_format != XFS_DINODE_FMT_BTREE || ip->i_df.if_nextents > XFS_IFORK_MAXEXT(ip, XFS_DATA_FORK)); - ASSERT(iip != NULL && iip->ili_fields != 0); + ASSERT(iip->ili_item.li_buf == bp); dip = xfs_buf_offset(bp, ip->i_imap.im_boffset); diff --git a/fs/xfs/xfs_inode.h b/fs/xfs/xfs_inode.h index 1534386b430c6..c482e7306fe00 100644 --- a/fs/xfs/xfs_inode.h +++ b/fs/xfs/xfs_inode.h @@ -426,7 +426,7 @@ int xfs_log_force_inode(struct xfs_inode *ip); void xfs_iunpin_wait(xfs_inode_t *); #define xfs_ipincount(ip) ((unsigned int) atomic_read(&ip->i_pincount)) -int xfs_iflush(struct xfs_inode *, struct xfs_buf **); +int xfs_iflush_cluster(struct xfs_inode *, struct xfs_buf *); void xfs_lock_two_inodes(struct xfs_inode *ip0, uint ip0_mode, struct xfs_inode *ip1, uint ip1_mode); diff --git a/fs/xfs/xfs_inode_item.c b/fs/xfs/xfs_inode_item.c index 697248b7eb2be..e8eda2ac25fb4 100644 --- a/fs/xfs/xfs_inode_item.c +++ b/fs/xfs/xfs_inode_item.c @@ -485,53 +485,38 @@ xfs_inode_item_push( uint rval = XFS_ITEM_SUCCESS; int error; - if (xfs_ipincount(ip) > 0) + ASSERT(iip->ili_item.li_buf); + + if (xfs_ipincount(ip) > 0 || xfs_buf_ispinned(bp) || + (ip->i_flags & XFS_ISTALE)) return XFS_ITEM_PINNED; - if (!xfs_ilock_nowait(ip, XFS_ILOCK_SHARED)) - return XFS_ITEM_LOCKED; + /* If the inode is already flush locked, we're already flushing. */ + if (xfs_isiflocked(ip)) + return XFS_ITEM_FLUSHING; - /* - * Re-check the pincount now that we stabilized the value by - * taking the ilock. - */ - if (xfs_ipincount(ip) > 0) { - rval = XFS_ITEM_PINNED; - goto out_unlock; - } + if (!xfs_buf_trylock(bp)) + return XFS_ITEM_LOCKED; - /* - * Stale inode items should force out the iclog. - */ - if (ip->i_flags & XFS_ISTALE) { - rval = XFS_ITEM_PINNED; - goto out_unlock; - } + spin_unlock(&lip->li_ailp->ail_lock); /* - * Someone else is already flushing the inode. Nothing we can do - * here but wait for the flush to finish and remove the item from - * the AIL. + * We need to hold a reference for flushing the cluster buffer as it may + * fail the buffer without IO submission. In which case, we better get a + * reference for that completion because otherwise we don't get a + * reference for IO until we queue the buffer for delwri submission. */ - if (!xfs_iflock_nowait(ip)) { - rval = XFS_ITEM_FLUSHING; - goto out_unlock; - } - - ASSERT(iip->ili_fields != 0 || XFS_FORCED_SHUTDOWN(ip->i_mount)); - spin_unlock(&lip->li_ailp->ail_lock); - - error = xfs_iflush(ip, &bp); + xfs_buf_hold(bp); + error = xfs_iflush_cluster(ip, bp); if (!error) { if (!xfs_buf_delwri_queue(bp, buffer_list)) rval = XFS_ITEM_FLUSHING; xfs_buf_relse(bp); - } else if (error == -EAGAIN) + } else { rval = XFS_ITEM_LOCKED; + } spin_lock(&lip->li_ailp->ail_lock); -out_unlock: - xfs_iunlock(ip, XFS_ILOCK_SHARED); return rval; } -- GitLab From e6187b3444e88ed9aa5f3843603e1f024b6d0309 Mon Sep 17 00:00:00 2001 From: Dave Chinner Date: Mon, 29 Jun 2020 14:49:19 -0700 Subject: [PATCH 0318/1476] xfs: rename xfs_iflush_int() with xfs_iflush() gone, we can rename xfs_iflush_int() back to xfs_iflush(). Also move it up above xfs_iflush_cluster() so we don't need the forward definition any more. Signed-off-by: Dave Chinner Reviewed-by: Amir Goldstein Reviewed-by: Darrick J. Wong Reviewed-by: Brian Foster Signed-off-by: Darrick J. Wong --- fs/xfs/xfs_inode.c | 293 ++++++++++++++++++++++----------------------- 1 file changed, 146 insertions(+), 147 deletions(-) diff --git a/fs/xfs/xfs_inode.c b/fs/xfs/xfs_inode.c index 4a95390486396..31e105f95739a 100644 --- a/fs/xfs/xfs_inode.c +++ b/fs/xfs/xfs_inode.c @@ -44,7 +44,6 @@ kmem_zone_t *xfs_inode_zone; */ #define XFS_ITRUNC_MAX_EXTENTS 2 -STATIC int xfs_iflush_int(struct xfs_inode *, struct xfs_buf *); STATIC int xfs_iunlink(struct xfs_trans *, struct xfs_inode *); STATIC int xfs_iunlink_remove(struct xfs_trans *, struct xfs_inode *); @@ -3450,152 +3449,8 @@ out_release_wip: return error; } -/* - * Non-blocking flush of dirty inode metadata into the backing buffer. - * - * The caller must have a reference to the inode and hold the cluster buffer - * locked. The function will walk across all the inodes on the cluster buffer it - * can find and lock without blocking, and flush them to the cluster buffer. - * - * On success, the caller must write out the buffer returned in *bp and - * release it. On failure, the filesystem will be shut down, the buffer will - * have been unlocked and released, and EFSCORRUPTED will be returned. - */ -int -xfs_iflush_cluster( - struct xfs_inode *ip, - struct xfs_buf *bp) -{ - struct xfs_mount *mp = ip->i_mount; - struct xfs_perag *pag; - unsigned long first_index, mask; - int cilist_size; - struct xfs_inode **cilist; - struct xfs_inode *cip; - struct xfs_ino_geometry *igeo = M_IGEO(mp); - int error = 0; - int nr_found; - int clcount = 0; - int i; - - pag = xfs_perag_get(mp, XFS_INO_TO_AGNO(mp, ip->i_ino)); - - cilist_size = igeo->inodes_per_cluster * sizeof(struct xfs_inode *); - cilist = kmem_alloc(cilist_size, KM_MAYFAIL|KM_NOFS); - if (!cilist) - goto out_put; - - mask = ~(igeo->inodes_per_cluster - 1); - first_index = XFS_INO_TO_AGINO(mp, ip->i_ino) & mask; - rcu_read_lock(); - /* really need a gang lookup range call here */ - nr_found = radix_tree_gang_lookup(&pag->pag_ici_root, (void**)cilist, - first_index, igeo->inodes_per_cluster); - if (nr_found == 0) - goto out_free; - - for (i = 0; i < nr_found; i++) { - cip = cilist[i]; - - /* - * because this is an RCU protected lookup, we could find a - * recently freed or even reallocated inode during the lookup. - * We need to check under the i_flags_lock for a valid inode - * here. Skip it if it is not valid or the wrong inode. - */ - spin_lock(&cip->i_flags_lock); - if (!cip->i_ino || - __xfs_iflags_test(cip, XFS_ISTALE)) { - spin_unlock(&cip->i_flags_lock); - continue; - } - - /* - * Once we fall off the end of the cluster, no point checking - * any more inodes in the list because they will also all be - * outside the cluster. - */ - if ((XFS_INO_TO_AGINO(mp, cip->i_ino) & mask) != first_index) { - spin_unlock(&cip->i_flags_lock); - break; - } - spin_unlock(&cip->i_flags_lock); - - /* - * Do an un-protected check to see if the inode is dirty and - * is a candidate for flushing. These checks will be repeated - * later after the appropriate locks are acquired. - */ - if (xfs_inode_clean(cip) && xfs_ipincount(cip) == 0) - continue; - - /* - * Try to get locks. If any are unavailable or it is pinned, - * then this inode cannot be flushed and is skipped. - */ - - if (!xfs_ilock_nowait(cip, XFS_ILOCK_SHARED)) - continue; - if (!xfs_iflock_nowait(cip)) { - xfs_iunlock(cip, XFS_ILOCK_SHARED); - continue; - } - if (xfs_ipincount(cip)) { - xfs_ifunlock(cip); - xfs_iunlock(cip, XFS_ILOCK_SHARED); - continue; - } - - - /* - * Check the inode number again, just to be certain we are not - * racing with freeing in xfs_reclaim_inode(). See the comments - * in that function for more information as to why the initial - * check is not sufficient. - */ - if (!cip->i_ino) { - xfs_ifunlock(cip); - xfs_iunlock(cip, XFS_ILOCK_SHARED); - continue; - } - - /* - * arriving here means that this inode can be flushed. First - * re-check that it's dirty before flushing. - */ - if (!xfs_inode_clean(cip)) { - error = xfs_iflush_int(cip, bp); - if (error) { - xfs_iunlock(cip, XFS_ILOCK_SHARED); - goto out_free; - } - clcount++; - } else { - xfs_ifunlock(cip); - } - xfs_iunlock(cip, XFS_ILOCK_SHARED); - } - - if (clcount) { - XFS_STATS_INC(mp, xs_icluster_flushcnt); - XFS_STATS_ADD(mp, xs_icluster_flushinode, clcount); - } - -out_free: - rcu_read_unlock(); - kmem_free(cilist); -out_put: - xfs_perag_put(pag); - if (error) { - bp->b_flags |= XBF_ASYNC; - xfs_buf_ioend_fail(bp); - xfs_force_shutdown(mp, SHUTDOWN_CORRUPT_INCORE); - } - return error; -} - -STATIC int -xfs_iflush_int( +static int +xfs_iflush( struct xfs_inode *ip, struct xfs_buf *bp) { @@ -3741,6 +3596,150 @@ flush_out: return error; } +/* + * Non-blocking flush of dirty inode metadata into the backing buffer. + * + * The caller must have a reference to the inode and hold the cluster buffer + * locked. The function will walk across all the inodes on the cluster buffer it + * can find and lock without blocking, and flush them to the cluster buffer. + * + * On success, the caller must write out the buffer returned in *bp and + * release it. On failure, the filesystem will be shut down, the buffer will + * have been unlocked and released, and EFSCORRUPTED will be returned. + */ +int +xfs_iflush_cluster( + struct xfs_inode *ip, + struct xfs_buf *bp) +{ + struct xfs_mount *mp = ip->i_mount; + struct xfs_perag *pag; + unsigned long first_index, mask; + int cilist_size; + struct xfs_inode **cilist; + struct xfs_inode *cip; + struct xfs_ino_geometry *igeo = M_IGEO(mp); + int error = 0; + int nr_found; + int clcount = 0; + int i; + + pag = xfs_perag_get(mp, XFS_INO_TO_AGNO(mp, ip->i_ino)); + + cilist_size = igeo->inodes_per_cluster * sizeof(struct xfs_inode *); + cilist = kmem_alloc(cilist_size, KM_MAYFAIL|KM_NOFS); + if (!cilist) + goto out_put; + + mask = ~(igeo->inodes_per_cluster - 1); + first_index = XFS_INO_TO_AGINO(mp, ip->i_ino) & mask; + rcu_read_lock(); + /* really need a gang lookup range call here */ + nr_found = radix_tree_gang_lookup(&pag->pag_ici_root, (void**)cilist, + first_index, igeo->inodes_per_cluster); + if (nr_found == 0) + goto out_free; + + for (i = 0; i < nr_found; i++) { + cip = cilist[i]; + + /* + * because this is an RCU protected lookup, we could find a + * recently freed or even reallocated inode during the lookup. + * We need to check under the i_flags_lock for a valid inode + * here. Skip it if it is not valid or the wrong inode. + */ + spin_lock(&cip->i_flags_lock); + if (!cip->i_ino || + __xfs_iflags_test(cip, XFS_ISTALE)) { + spin_unlock(&cip->i_flags_lock); + continue; + } + + /* + * Once we fall off the end of the cluster, no point checking + * any more inodes in the list because they will also all be + * outside the cluster. + */ + if ((XFS_INO_TO_AGINO(mp, cip->i_ino) & mask) != first_index) { + spin_unlock(&cip->i_flags_lock); + break; + } + spin_unlock(&cip->i_flags_lock); + + /* + * Do an un-protected check to see if the inode is dirty and + * is a candidate for flushing. These checks will be repeated + * later after the appropriate locks are acquired. + */ + if (xfs_inode_clean(cip) && xfs_ipincount(cip) == 0) + continue; + + /* + * Try to get locks. If any are unavailable or it is pinned, + * then this inode cannot be flushed and is skipped. + */ + + if (!xfs_ilock_nowait(cip, XFS_ILOCK_SHARED)) + continue; + if (!xfs_iflock_nowait(cip)) { + xfs_iunlock(cip, XFS_ILOCK_SHARED); + continue; + } + if (xfs_ipincount(cip)) { + xfs_ifunlock(cip); + xfs_iunlock(cip, XFS_ILOCK_SHARED); + continue; + } + + + /* + * Check the inode number again, just to be certain we are not + * racing with freeing in xfs_reclaim_inode(). See the comments + * in that function for more information as to why the initial + * check is not sufficient. + */ + if (!cip->i_ino) { + xfs_ifunlock(cip); + xfs_iunlock(cip, XFS_ILOCK_SHARED); + continue; + } + + /* + * arriving here means that this inode can be flushed. First + * re-check that it's dirty before flushing. + */ + if (!xfs_inode_clean(cip)) { + error = xfs_iflush(cip, bp); + if (error) { + xfs_iunlock(cip, XFS_ILOCK_SHARED); + goto out_free; + } + clcount++; + } else { + xfs_ifunlock(cip); + } + xfs_iunlock(cip, XFS_ILOCK_SHARED); + } + + if (clcount) { + XFS_STATS_INC(mp, xs_icluster_flushcnt); + XFS_STATS_ADD(mp, xs_icluster_flushinode, clcount); + } + +out_free: + rcu_read_unlock(); + kmem_free(cilist); +out_put: + xfs_perag_put(pag); + if (error) { + bp->b_flags |= XBF_ASYNC; + xfs_buf_ioend_fail(bp); + xfs_force_shutdown(mp, SHUTDOWN_CORRUPT_INCORE); + } + return error; +} + /* Release an inode. */ void xfs_irele( -- GitLab From 5717ea4d527acbec9300cb083b100dd0003ac777 Mon Sep 17 00:00:00 2001 From: Dave Chinner Date: Mon, 29 Jun 2020 14:49:20 -0700 Subject: [PATCH 0319/1476] xfs: rework xfs_iflush_cluster() dirty inode iteration Now that we have all the dirty inodes attached to the cluster buffer, we don't actually have to do radix tree lookups to find them. Sure, the radix tree is efficient, but walking a linked list of just the dirty inodes attached to the buffer is much better. We are also no longer dependent on having a locked inode passed into the function to determine where to start the lookup. This means we can drop it from the function call and treat all inodes the same. We also make xfs_iflush_cluster skip inodes marked with XFS_IRECLAIM. This we avoid races with inodes that reclaim is actively referencing or are being re-initialised by inode lookup. If they are actually dirty, they'll get written by a future cluster flush.... We also add a shutdown check after obtaining the flush lock so that we catch inodes that are dirty in memory and may have inconsistent state due to the shutdown in progress. We abort these inodes directly and so they remove themselves directly from the buffer list and the AIL rather than having to wait for the buffer to be failed and callbacks run to be processed correctly. Signed-off-by: Dave Chinner Reviewed-by: Darrick J. Wong Signed-off-by: Darrick J. Wong --- fs/xfs/xfs_inode.c | 171 ++++++++++++++++++---------------------- fs/xfs/xfs_inode.h | 2 +- fs/xfs/xfs_inode_item.c | 8 +- 3 files changed, 83 insertions(+), 98 deletions(-) diff --git a/fs/xfs/xfs_inode.c b/fs/xfs/xfs_inode.c index 31e105f95739a..ece3622f6d28f 100644 --- a/fs/xfs/xfs_inode.c +++ b/fs/xfs/xfs_inode.c @@ -3603,141 +3603,120 @@ flush_out: * locked. The function will walk across all the inodes on the cluster buffer it * can find and lock without blocking, and flush them to the cluster buffer. * - * On success, the caller must write out the buffer returned in *bp and - * release it. On failure, the filesystem will be shut down, the buffer will - * have been unlocked and released, and EFSCORRUPTED will be returned. + * On successful flushing of at least one inode, the caller must write out the + * buffer and release it. If no inodes are flushed, -EAGAIN will be returned and + * the caller needs to release the buffer. On failure, the filesystem will be + * shut down, the buffer will have been unlocked and released, and EFSCORRUPTED + * will be returned. */ int xfs_iflush_cluster( - struct xfs_inode *ip, struct xfs_buf *bp) { - struct xfs_mount *mp = ip->i_mount; - struct xfs_perag *pag; - unsigned long first_index, mask; - int cilist_size; - struct xfs_inode **cilist; - struct xfs_inode *cip; - struct xfs_ino_geometry *igeo = M_IGEO(mp); - int error = 0; - int nr_found; + struct xfs_mount *mp = bp->b_mount; + struct xfs_log_item *lip, *n; + struct xfs_inode *ip; + struct xfs_inode_log_item *iip; int clcount = 0; - int i; - - pag = xfs_perag_get(mp, XFS_INO_TO_AGNO(mp, ip->i_ino)); - - cilist_size = igeo->inodes_per_cluster * sizeof(struct xfs_inode *); - cilist = kmem_alloc(cilist_size, KM_MAYFAIL|KM_NOFS); - if (!cilist) - goto out_put; - - mask = ~(igeo->inodes_per_cluster - 1); - first_index = XFS_INO_TO_AGINO(mp, ip->i_ino) & mask; - rcu_read_lock(); - /* really need a gang lookup range call here */ - nr_found = radix_tree_gang_lookup(&pag->pag_ici_root, (void**)cilist, - first_index, igeo->inodes_per_cluster); - if (nr_found == 0) - goto out_free; + int error = 0; - for (i = 0; i < nr_found; i++) { - cip = cilist[i]; + /* + * We must use the safe variant here as on shutdown xfs_iflush_abort() + * can remove itself from the list. + */ + list_for_each_entry_safe(lip, n, &bp->b_li_list, li_bio_list) { + iip = (struct xfs_inode_log_item *)lip; + ip = iip->ili_inode; /* - * because this is an RCU protected lookup, we could find a - * recently freed or even reallocated inode during the lookup. - * We need to check under the i_flags_lock for a valid inode - * here. Skip it if it is not valid or the wrong inode. + * Quick and dirty check to avoid locks if possible. */ - spin_lock(&cip->i_flags_lock); - if (!cip->i_ino || - __xfs_iflags_test(cip, XFS_ISTALE)) { - spin_unlock(&cip->i_flags_lock); + if (__xfs_iflags_test(ip, XFS_IRECLAIM | XFS_IFLOCK)) + continue; + if (xfs_ipincount(ip)) continue; - } /* - * Once we fall off the end of the cluster, no point checking - * any more inodes in the list because they will also all be - * outside the cluster. + * The inode is still attached to the buffer, which means it is + * dirty but reclaim might try to grab it. Check carefully for + * that, and grab the ilock while still holding the i_flags_lock + * to guarantee reclaim will not be able to reclaim this inode + * once we drop the i_flags_lock. */ - if ((XFS_INO_TO_AGINO(mp, cip->i_ino) & mask) != first_index) { - spin_unlock(&cip->i_flags_lock); - break; + spin_lock(&ip->i_flags_lock); + ASSERT(!__xfs_iflags_test(ip, XFS_ISTALE)); + if (__xfs_iflags_test(ip, XFS_IRECLAIM | XFS_IFLOCK)) { + spin_unlock(&ip->i_flags_lock); + continue; } - spin_unlock(&cip->i_flags_lock); /* - * Do an un-protected check to see if the inode is dirty and - * is a candidate for flushing. These checks will be repeated - * later after the appropriate locks are acquired. + * ILOCK will pin the inode against reclaim and prevent + * concurrent transactions modifying the inode while we are + * flushing the inode. */ - if (xfs_inode_clean(cip) && xfs_ipincount(cip) == 0) + if (!xfs_ilock_nowait(ip, XFS_ILOCK_SHARED)) { + spin_unlock(&ip->i_flags_lock); continue; + } + spin_unlock(&ip->i_flags_lock); /* - * Try to get locks. If any are unavailable or it is pinned, - * then this inode cannot be flushed and is skipped. + * Skip inodes that are already flush locked as they have + * already been written to the buffer. */ - - if (!xfs_ilock_nowait(cip, XFS_ILOCK_SHARED)) - continue; - if (!xfs_iflock_nowait(cip)) { - xfs_iunlock(cip, XFS_ILOCK_SHARED); + if (!xfs_iflock_nowait(ip)) { + xfs_iunlock(ip, XFS_ILOCK_SHARED); continue; } - if (xfs_ipincount(cip)) { - xfs_ifunlock(cip); - xfs_iunlock(cip, XFS_ILOCK_SHARED); - continue; - } - /* - * Check the inode number again, just to be certain we are not - * racing with freeing in xfs_reclaim_inode(). See the comments - * in that function for more information as to why the initial - * check is not sufficient. + * Abort flushing this inode if we are shut down because the + * inode may not currently be in the AIL. This can occur when + * log I/O failure unpins the inode without inserting into the + * AIL, leaving a dirty/unpinned inode attached to the buffer + * that otherwise looks like it should be flushed. */ - if (!cip->i_ino) { - xfs_ifunlock(cip); - xfs_iunlock(cip, XFS_ILOCK_SHARED); + if (XFS_FORCED_SHUTDOWN(mp)) { + xfs_iunpin_wait(ip); + /* xfs_iflush_abort() drops the flush lock */ + xfs_iflush_abort(ip); + xfs_iunlock(ip, XFS_ILOCK_SHARED); + error = -EIO; continue; } - /* - * arriving here means that this inode can be flushed. First - * re-check that it's dirty before flushing. - */ - if (!xfs_inode_clean(cip)) { - error = xfs_iflush(cip, bp); - if (error) { - xfs_iunlock(cip, XFS_ILOCK_SHARED); - goto out_free; - } - clcount++; - } else { - xfs_ifunlock(cip); + /* don't block waiting on a log force to unpin dirty inodes */ + if (xfs_ipincount(ip)) { + xfs_ifunlock(ip); + xfs_iunlock(ip, XFS_ILOCK_SHARED); + continue; } - xfs_iunlock(cip, XFS_ILOCK_SHARED); - } - if (clcount) { - XFS_STATS_INC(mp, xs_icluster_flushcnt); - XFS_STATS_ADD(mp, xs_icluster_flushinode, clcount); + if (!xfs_inode_clean(ip)) + error = xfs_iflush(ip, bp); + else + xfs_ifunlock(ip); + xfs_iunlock(ip, XFS_ILOCK_SHARED); + if (error) + break; + clcount++; } -out_free: - rcu_read_unlock(); - kmem_free(cilist); -out_put: - xfs_perag_put(pag); if (error) { bp->b_flags |= XBF_ASYNC; xfs_buf_ioend_fail(bp); xfs_force_shutdown(mp, SHUTDOWN_CORRUPT_INCORE); + return error; } - return error; + + if (!clcount) + return -EAGAIN; + + XFS_STATS_INC(mp, xs_icluster_flushcnt); + XFS_STATS_ADD(mp, xs_icluster_flushinode, clcount); + return 0; + } /* Release an inode. */ diff --git a/fs/xfs/xfs_inode.h b/fs/xfs/xfs_inode.h index c482e7306fe00..e9a8bb184d1f9 100644 --- a/fs/xfs/xfs_inode.h +++ b/fs/xfs/xfs_inode.h @@ -426,7 +426,7 @@ int xfs_log_force_inode(struct xfs_inode *ip); void xfs_iunpin_wait(xfs_inode_t *); #define xfs_ipincount(ip) ((unsigned int) atomic_read(&ip->i_pincount)) -int xfs_iflush_cluster(struct xfs_inode *, struct xfs_buf *); +int xfs_iflush_cluster(struct xfs_buf *); void xfs_lock_two_inodes(struct xfs_inode *ip0, uint ip0_mode, struct xfs_inode *ip1, uint ip1_mode); diff --git a/fs/xfs/xfs_inode_item.c b/fs/xfs/xfs_inode_item.c index e8eda2ac25fb4..4e7fce8d4f7cb 100644 --- a/fs/xfs/xfs_inode_item.c +++ b/fs/xfs/xfs_inode_item.c @@ -507,12 +507,18 @@ xfs_inode_item_push( * reference for IO until we queue the buffer for delwri submission. */ xfs_buf_hold(bp); - error = xfs_iflush_cluster(ip, bp); + error = xfs_iflush_cluster(bp); if (!error) { if (!xfs_buf_delwri_queue(bp, buffer_list)) rval = XFS_ITEM_FLUSHING; xfs_buf_relse(bp); } else { + /* + * Release the buffer if we were unable to flush anything. On + * any other error, the buffer has already been released. + */ + if (error == -EAGAIN) + xfs_buf_relse(bp); rval = XFS_ITEM_LOCKED; } -- GitLab From a69a1dc2842e4548efca956c86e0816f2662ccb7 Mon Sep 17 00:00:00 2001 From: Dave Chinner Date: Mon, 29 Jun 2020 14:49:20 -0700 Subject: [PATCH 0320/1476] xfs: factor xfs_iflush_done xfs_iflush_done() does 3 distinct operations to the inodes attached to the buffer. Separate these operations out into functions so that it is easier to modify these operations independently in future. Signed-off-by: Dave Chinner Reviewed-by: Darrick J. Wong Signed-off-by: Darrick J. Wong --- fs/xfs/xfs_inode_item.c | 157 +++++++++++++++++++++------------------- 1 file changed, 82 insertions(+), 75 deletions(-) diff --git a/fs/xfs/xfs_inode_item.c b/fs/xfs/xfs_inode_item.c index 4e7fce8d4f7cb..3840117f8a5e2 100644 --- a/fs/xfs/xfs_inode_item.c +++ b/fs/xfs/xfs_inode_item.c @@ -641,101 +641,63 @@ xfs_inode_item_destroy( /* - * This is the inode flushing I/O completion routine. It is called - * from interrupt level when the buffer containing the inode is - * flushed to disk. It is responsible for removing the inode item - * from the AIL if it has not been re-logged, and unlocking the inode's - * flush lock. - * - * To reduce AIL lock traffic as much as possible, we scan the buffer log item - * list for other inodes that will run this function. We remove them from the - * buffer list so we can process all the inode IO completions in one AIL lock - * traversal. - * - * Note: Now that we attach the log item to the buffer when we first log the - * inode in memory, we can have unflushed inodes on the buffer list here. These - * inodes will have a zero ili_last_fields, so skip over them here. + * We only want to pull the item from the AIL if it is actually there + * and its location in the log has not changed since we started the + * flush. Thus, we only bother if the inode's lsn has not changed. */ -void -xfs_iflush_done( - struct xfs_buf *bp) +static void +xfs_iflush_ail_updates( + struct xfs_ail *ailp, + struct list_head *list) { - struct xfs_inode_log_item *iip; - struct xfs_log_item *lip, *n; - struct xfs_ail *ailp = bp->b_mount->m_ail; - int need_ail = 0; - LIST_HEAD(tmp); + struct xfs_log_item *lip; + xfs_lsn_t tail_lsn = 0; - /* - * Pull the attached inodes from the buffer one at a time and take the - * appropriate action on them. - */ - list_for_each_entry_safe(lip, n, &bp->b_li_list, li_bio_list) { - iip = INODE_ITEM(lip); - - if (xfs_iflags_test(iip->ili_inode, XFS_ISTALE)) { - xfs_iflush_abort(iip->ili_inode); - continue; - } + /* this is an opencoded batch version of xfs_trans_ail_delete */ + spin_lock(&ailp->ail_lock); + list_for_each_entry(lip, list, li_bio_list) { + xfs_lsn_t lsn; - if (!iip->ili_last_fields) + clear_bit(XFS_LI_FAILED, &lip->li_flags); + if (INODE_ITEM(lip)->ili_flush_lsn != lip->li_lsn) continue; - list_move_tail(&lip->li_bio_list, &tmp); - - /* Do an unlocked check for needing the AIL lock. */ - if (iip->ili_flush_lsn == lip->li_lsn || - test_bit(XFS_LI_FAILED, &lip->li_flags)) - need_ail++; + lsn = xfs_ail_delete_one(ailp, lip); + if (!tail_lsn && lsn) + tail_lsn = lsn; } + xfs_ail_update_finish(ailp, tail_lsn); +} - /* - * We only want to pull the item from the AIL if it is actually there - * and its location in the log has not changed since we started the - * flush. Thus, we only bother if the inode's lsn has not changed. - */ - if (need_ail) { - xfs_lsn_t tail_lsn = 0; - - /* this is an opencoded batch version of xfs_trans_ail_delete */ - spin_lock(&ailp->ail_lock); - list_for_each_entry(lip, &tmp, li_bio_list) { - clear_bit(XFS_LI_FAILED, &lip->li_flags); - if (lip->li_lsn == INODE_ITEM(lip)->ili_flush_lsn) { - xfs_lsn_t lsn = xfs_ail_delete_one(ailp, lip); - if (!tail_lsn && lsn) - tail_lsn = lsn; - } - } - xfs_ail_update_finish(ailp, tail_lsn); - } +/* + * Walk the list of inodes that have completed their IOs. If they are clean + * remove them from the list and dissociate them from the buffer. Buffers that + * are still dirty remain linked to the buffer and on the list. Caller must + * handle them appropriately. + */ +static void +xfs_iflush_finish( + struct xfs_buf *bp, + struct list_head *list) +{ + struct xfs_log_item *lip, *n; - /* - * Clean up and unlock the flush lock now we are done. We can clear the - * ili_last_fields bits now that we know that the data corresponding to - * them is safely on disk. - */ - list_for_each_entry_safe(lip, n, &tmp, li_bio_list) { + list_for_each_entry_safe(lip, n, list, li_bio_list) { + struct xfs_inode_log_item *iip = INODE_ITEM(lip); bool drop_buffer = false; - list_del_init(&lip->li_bio_list); - iip = INODE_ITEM(lip); - spin_lock(&iip->ili_lock); /* * Remove the reference to the cluster buffer if the inode is - * clean in memory. Drop the buffer reference once we've dropped - * the locks we hold. If the inode is dirty in memory, we need - * to put the inode item back on the buffer list for another - * pass through the flush machinery. + * clean in memory and drop the buffer reference once we've + * dropped the locks we hold. */ ASSERT(iip->ili_item.li_buf == bp); if (!iip->ili_fields) { iip->ili_item.li_buf = NULL; + list_del_init(&lip->li_bio_list); drop_buffer = true; - } else { - list_add(&lip->li_bio_list, &bp->b_li_list); } iip->ili_last_fields = 0; iip->ili_flush_lsn = 0; @@ -746,6 +708,51 @@ xfs_iflush_done( } } +/* + * Inode buffer IO completion routine. It is responsible for removing inodes + * attached to the buffer from the AIL if they have not been re-logged, as well + * as completing the flush and unlocking the inode. + */ +void +xfs_iflush_done( + struct xfs_buf *bp) +{ + struct xfs_log_item *lip, *n; + LIST_HEAD(flushed_inodes); + LIST_HEAD(ail_updates); + + /* + * Pull the attached inodes from the buffer one at a time and take the + * appropriate action on them. + */ + list_for_each_entry_safe(lip, n, &bp->b_li_list, li_bio_list) { + struct xfs_inode_log_item *iip = INODE_ITEM(lip); + + if (xfs_iflags_test(iip->ili_inode, XFS_ISTALE)) { + xfs_iflush_abort(iip->ili_inode); + continue; + } + if (!iip->ili_last_fields) + continue; + + /* Do an unlocked check for needing the AIL lock. */ + if (iip->ili_flush_lsn == lip->li_lsn || + test_bit(XFS_LI_FAILED, &lip->li_flags)) + list_move_tail(&lip->li_bio_list, &ail_updates); + else + list_move_tail(&lip->li_bio_list, &flushed_inodes); + } + + if (!list_empty(&ail_updates)) { + xfs_iflush_ail_updates(bp->b_mount->m_ail, &ail_updates); + list_splice_tail(&ail_updates, &flushed_inodes); + } + + xfs_iflush_finish(bp, &flushed_inodes); + if (!list_empty(&flushed_inodes)) + list_splice_tail(&flushed_inodes, &bp->b_li_list); +} + /* * This is the inode flushing abort routine. It is called from xfs_iflush when * the filesystem is shutting down to clean up the inode state. It is -- GitLab From e2705b0304778916db87831217ec642e34d9d9fa Mon Sep 17 00:00:00 2001 From: Dave Chinner Date: Mon, 29 Jun 2020 14:49:20 -0700 Subject: [PATCH 0321/1476] xfs: remove xfs_inobp_check() This debug code is called on every xfs_iflush() call, which then checks every inode in the buffer for non-zero unlinked list field. Hence it checks every inode in the cluster buffer every time a single inode on that cluster it flushed. This is resulting in: - 38.91% 5.33% [kernel] [k] xfs_iflush - 17.70% xfs_iflush - 9.93% xfs_inobp_check 4.36% xfs_buf_offset 10% of the CPU time spent flushing inodes is repeatedly checking unlinked fields in the buffer. We don't need to do this. The other place we call xfs_inobp_check() is xfs_iunlink_update_dinode(), and this is after we've done this assert for the agino we are about to write into that inode: ASSERT(xfs_verify_agino_or_null(mp, agno, next_agino)); which means we've already checked that the agino we are about to write is not 0 on debug kernels. The inode buffer verifiers do everything else we need, so let's just remove this debug code. Signed-off-by: Dave Chinner Reviewed-by: Christoph Hellwig Reviewed-by: Darrick J. Wong Signed-off-by: Darrick J. Wong --- fs/xfs/libxfs/xfs_inode_buf.c | 24 ------------------------ fs/xfs/libxfs/xfs_inode_buf.h | 6 ------ fs/xfs/xfs_inode.c | 2 -- 3 files changed, 32 deletions(-) diff --git a/fs/xfs/libxfs/xfs_inode_buf.c b/fs/xfs/libxfs/xfs_inode_buf.c index b4a6c091571e6..8d5dd08eab75f 100644 --- a/fs/xfs/libxfs/xfs_inode_buf.c +++ b/fs/xfs/libxfs/xfs_inode_buf.c @@ -20,30 +20,6 @@ #include -/* - * Check that none of the inode's in the buffer have a next - * unlinked field of 0. - */ -#if defined(DEBUG) -void -xfs_inobp_check( - xfs_mount_t *mp, - xfs_buf_t *bp) -{ - int i; - xfs_dinode_t *dip; - - for (i = 0; i < M_IGEO(mp)->inodes_per_cluster; i++) { - dip = xfs_buf_offset(bp, i * mp->m_sb.sb_inodesize); - if (!dip->di_next_unlinked) { - xfs_alert(mp, - "Detected bogus zero next_unlinked field in inode %d buffer 0x%llx.", - i, (long long)bp->b_bn); - } - } -} -#endif - /* * If we are doing readahead on an inode buffer, we might be in log recovery * reading an inode allocation buffer that hasn't yet been replayed, and hence diff --git a/fs/xfs/libxfs/xfs_inode_buf.h b/fs/xfs/libxfs/xfs_inode_buf.h index 865ac493c72a2..6b08b9d060c2e 100644 --- a/fs/xfs/libxfs/xfs_inode_buf.h +++ b/fs/xfs/libxfs/xfs_inode_buf.h @@ -52,12 +52,6 @@ int xfs_inode_from_disk(struct xfs_inode *ip, struct xfs_dinode *from); void xfs_log_dinode_to_disk(struct xfs_log_dinode *from, struct xfs_dinode *to); -#if defined(DEBUG) -void xfs_inobp_check(struct xfs_mount *, struct xfs_buf *); -#else -#define xfs_inobp_check(mp, bp) -#endif /* DEBUG */ - xfs_failaddr_t xfs_dinode_verify(struct xfs_mount *mp, xfs_ino_t ino, struct xfs_dinode *dip); xfs_failaddr_t xfs_inode_validate_extsize(struct xfs_mount *mp, diff --git a/fs/xfs/xfs_inode.c b/fs/xfs/xfs_inode.c index ece3622f6d28f..5c07bf491d9f5 100644 --- a/fs/xfs/xfs_inode.c +++ b/fs/xfs/xfs_inode.c @@ -2165,7 +2165,6 @@ xfs_iunlink_update_dinode( xfs_dinode_calc_crc(mp, dip); xfs_trans_inode_buf(tp, ibp); xfs_trans_log_buf(tp, ibp, offset, offset + sizeof(xfs_agino_t) - 1); - xfs_inobp_check(mp, ibp); } /* Set an in-core inode's unlinked pointer and return the old value. */ @@ -3558,7 +3557,6 @@ xfs_iflush( xfs_iflush_fork(ip, dip, iip, XFS_DATA_FORK); if (XFS_IFORK_Q(ip)) xfs_iflush_fork(ip, dip, iip, XFS_ATTR_FORK); - xfs_inobp_check(mp, bp); /* * We've recorded everything logged in the inode, so we'd like to clear -- GitLab From f866560be21966421c4a6e83079a6b76fadf667f Mon Sep 17 00:00:00 2001 From: "Darrick J. Wong" Date: Thu, 2 Jul 2020 08:42:12 -0700 Subject: [PATCH 0322/1476] xfs: rtbitmap scrubber should verify written extents Ensure that the realtime bitmap file is backed entirely by written extents. No holes, no unwritten blocks, etc. Signed-off-by: Darrick J. Wong Reviewed-by: Allison Collins --- fs/xfs/scrub/rtbitmap.c | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/fs/xfs/scrub/rtbitmap.c b/fs/xfs/scrub/rtbitmap.c index c642bc206c41d..c777c98c50c3c 100644 --- a/fs/xfs/scrub/rtbitmap.c +++ b/fs/xfs/scrub/rtbitmap.c @@ -13,6 +13,7 @@ #include "xfs_trans.h" #include "xfs_rtalloc.h" #include "xfs_inode.h" +#include "xfs_bmap.h" #include "scrub/scrub.h" #include "scrub/common.h" @@ -58,6 +59,41 @@ xchk_rtbitmap_rec( return 0; } +/* Make sure the entire rtbitmap file is mapped with written extents. */ +STATIC int +xchk_rtbitmap_check_extents( + struct xfs_scrub *sc) +{ + struct xfs_mount *mp = sc->mp; + struct xfs_bmbt_irec map; + xfs_rtblock_t off; + int nmap; + int error = 0; + + for (off = 0; off < mp->m_sb.sb_rbmblocks;) { + if (xchk_should_terminate(sc, &error) || + (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)) + break; + + /* Make sure we have a written extent. */ + nmap = 1; + error = xfs_bmapi_read(mp->m_rbmip, off, + mp->m_sb.sb_rbmblocks - off, &map, &nmap, + XFS_DATA_FORK); + if (!xchk_fblock_process_error(sc, XFS_DATA_FORK, off, &error)) + break; + + if (nmap != 1 || !xfs_bmap_is_written_extent(&map)) { + xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, off); + break; + } + + off += map.br_blockcount; + } + + return error; +} + /* Scrub the realtime bitmap. */ int xchk_rtbitmap( @@ -70,6 +106,10 @@ xchk_rtbitmap( if (error || (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)) return error; + error = xchk_rtbitmap_check_extents(sc); + if (error || (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)) + return error; + error = xfs_rtalloc_query_all(sc->tp, xchk_rtbitmap_rec, sc); if (!xchk_fblock_process_error(sc, XFS_DATA_FORK, 0, &error)) goto out; -- GitLab From 2fb94e36b6833332057043699d3338228f6e1e2b Mon Sep 17 00:00:00 2001 From: "Darrick J. Wong" Date: Thu, 2 Jul 2020 08:42:13 -0700 Subject: [PATCH 0323/1476] xfs: rtbitmap scrubber should check inode size Make sure the rtbitmap is large enough to store the entire bitmap. Signed-off-by: Darrick J. Wong Reviewed-by: Allison Collins --- fs/xfs/scrub/rtbitmap.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/fs/xfs/scrub/rtbitmap.c b/fs/xfs/scrub/rtbitmap.c index c777c98c50c3c..76e4ffe0315b1 100644 --- a/fs/xfs/scrub/rtbitmap.c +++ b/fs/xfs/scrub/rtbitmap.c @@ -101,6 +101,13 @@ xchk_rtbitmap( { int error; + /* Is the size of the rtbitmap correct? */ + if (sc->mp->m_rbmip->i_d.di_size != + XFS_FSB_TO_B(sc->mp, sc->mp->m_sb.sb_rbmblocks)) { + xchk_ino_set_corrupt(sc, sc->mp->m_rbmip->i_ino); + return 0; + } + /* Invoke the fork scrubber. */ error = xchk_metadata_inode_forks(sc); if (error || (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)) -- GitLab From b1bd5714472cc72e14409f5659b154c765a76c65 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pali=20Roh=C3=A1r?= Date: Mon, 1 Jun 2020 15:03:15 +0200 Subject: [PATCH 0324/1476] PCI: aardvark: Indicate error in 'val' when config read fails MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Most callers of config read do not check for return value. But most of the ones that do, checks for error indication in 'val' variable. This patch updates error handling in advk_pcie_rd_conf() function. If PIO transfer fails then 'val' variable is set to 0xffffffff which indicates failture. Link: https://lore.kernel.org/r/20200528162604.GA323482@bjorn-Precision-5520 Link: https://lore.kernel.org/r/20200601130315.18895-1-pali@kernel.org Reported-by: Bjorn Helgaas Signed-off-by: Pali Rohár Signed-off-by: Lorenzo Pieralisi --- drivers/pci/controller/pci-aardvark.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/pci/controller/pci-aardvark.c b/drivers/pci/controller/pci-aardvark.c index 90ff291c24f09..7872035dbb03c 100644 --- a/drivers/pci/controller/pci-aardvark.c +++ b/drivers/pci/controller/pci-aardvark.c @@ -688,8 +688,10 @@ static int advk_pcie_rd_conf(struct pci_bus *bus, u32 devfn, advk_writel(pcie, 1, PIO_START); ret = advk_pcie_wait_pio(pcie); - if (ret < 0) + if (ret < 0) { + *val = 0xffffffff; return PCIBIOS_SET_FAILED; + } advk_pcie_check_pio_status(pcie); -- GitLab From 8b6f0330b5f9a7543356bfa9e76d580f03aa2c1e Mon Sep 17 00:00:00 2001 From: Ansuel Smith Date: Mon, 15 Jun 2020 23:05:57 +0200 Subject: [PATCH 0325/1476] PCI: qcom: Add missing ipq806x clocks in PCIe driver Aux and Ref clk are missing in PCIe qcom driver. Add support for this optional clks for ipq8064/apq8064 SoC. Link: https://lore.kernel.org/r/20200615210608.21469-2-ansuelsmth@gmail.com Fixes: 82a823833f4e ("PCI: qcom: Add Qualcomm PCIe controller driver") Signed-off-by: Sham Muthayyan Signed-off-by: Ansuel Smith Signed-off-by: Lorenzo Pieralisi Reviewed-by: Rob Herring Acked-by: Stanimir Varbanov --- drivers/pci/controller/dwc/pcie-qcom.c | 38 ++++++++++++++++++++++---- 1 file changed, 33 insertions(+), 5 deletions(-) diff --git a/drivers/pci/controller/dwc/pcie-qcom.c b/drivers/pci/controller/dwc/pcie-qcom.c index 138e1a2d21ccd..fa0bf652ce0b1 100644 --- a/drivers/pci/controller/dwc/pcie-qcom.c +++ b/drivers/pci/controller/dwc/pcie-qcom.c @@ -88,6 +88,8 @@ struct qcom_pcie_resources_2_1_0 { struct clk *iface_clk; struct clk *core_clk; struct clk *phy_clk; + struct clk *aux_clk; + struct clk *ref_clk; struct reset_control *pci_reset; struct reset_control *axi_reset; struct reset_control *ahb_reset; @@ -246,6 +248,14 @@ static int qcom_pcie_get_resources_2_1_0(struct qcom_pcie *pcie) if (IS_ERR(res->phy_clk)) return PTR_ERR(res->phy_clk); + res->aux_clk = devm_clk_get_optional(dev, "aux"); + if (IS_ERR(res->aux_clk)) + return PTR_ERR(res->aux_clk); + + res->ref_clk = devm_clk_get_optional(dev, "ref"); + if (IS_ERR(res->ref_clk)) + return PTR_ERR(res->ref_clk); + res->pci_reset = devm_reset_control_get_exclusive(dev, "pci"); if (IS_ERR(res->pci_reset)) return PTR_ERR(res->pci_reset); @@ -278,6 +288,8 @@ static void qcom_pcie_deinit_2_1_0(struct qcom_pcie *pcie) clk_disable_unprepare(res->iface_clk); clk_disable_unprepare(res->core_clk); clk_disable_unprepare(res->phy_clk); + clk_disable_unprepare(res->aux_clk); + clk_disable_unprepare(res->ref_clk); regulator_bulk_disable(ARRAY_SIZE(res->supplies), res->supplies); } @@ -307,16 +319,28 @@ static int qcom_pcie_init_2_1_0(struct qcom_pcie *pcie) goto err_assert_ahb; } + ret = clk_prepare_enable(res->core_clk); + if (ret) { + dev_err(dev, "cannot prepare/enable core clock\n"); + goto err_clk_core; + } + ret = clk_prepare_enable(res->phy_clk); if (ret) { dev_err(dev, "cannot prepare/enable phy clock\n"); goto err_clk_phy; } - ret = clk_prepare_enable(res->core_clk); + ret = clk_prepare_enable(res->aux_clk); if (ret) { - dev_err(dev, "cannot prepare/enable core clock\n"); - goto err_clk_core; + dev_err(dev, "cannot prepare/enable aux clock\n"); + goto err_clk_aux; + } + + ret = clk_prepare_enable(res->ref_clk); + if (ret) { + dev_err(dev, "cannot prepare/enable ref clock\n"); + goto err_clk_ref; } ret = reset_control_deassert(res->ahb_reset); @@ -372,10 +396,14 @@ static int qcom_pcie_init_2_1_0(struct qcom_pcie *pcie) return 0; err_deassert_ahb: - clk_disable_unprepare(res->core_clk); -err_clk_core: + clk_disable_unprepare(res->ref_clk); +err_clk_ref: + clk_disable_unprepare(res->aux_clk); +err_clk_aux: clk_disable_unprepare(res->phy_clk); err_clk_phy: + clk_disable_unprepare(res->core_clk); +err_clk_core: clk_disable_unprepare(res->iface_clk); err_assert_ahb: regulator_bulk_disable(ARRAY_SIZE(res->supplies), res->supplies); -- GitLab From 736ae5c91712ba2686ea38f82d7c5eb716b625e3 Mon Sep 17 00:00:00 2001 From: Ansuel Smith Date: Mon, 15 Jun 2020 23:05:58 +0200 Subject: [PATCH 0326/1476] dt-bindings: PCI: qcom: Add missing clks Document missing clks used in ipq8064 SoC. Link: https://lore.kernel.org/r/20200615210608.21469-3-ansuelsmth@gmail.com Signed-off-by: Ansuel Smith Signed-off-by: Lorenzo Pieralisi Acked-by: Rob Herring Acked-by: Stanimir Varbanov --- Documentation/devicetree/bindings/pci/qcom,pcie.txt | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/Documentation/devicetree/bindings/pci/qcom,pcie.txt b/Documentation/devicetree/bindings/pci/qcom,pcie.txt index 981b4de128072..becdbdc0fffa8 100644 --- a/Documentation/devicetree/bindings/pci/qcom,pcie.txt +++ b/Documentation/devicetree/bindings/pci/qcom,pcie.txt @@ -90,6 +90,8 @@ Definition: Should contain the following entries - "core" Clocks the pcie hw block - "phy" Clocks the pcie PHY block + - "aux" Clocks the pcie AUX block + - "ref" Clocks the pcie ref block - clock-names: Usage: required for apq8084/ipq4019 Value type: @@ -277,8 +279,10 @@ <0 0 0 4 &intc 0 39 IRQ_TYPE_LEVEL_HIGH>; /* int_d */ clocks = <&gcc PCIE_A_CLK>, <&gcc PCIE_H_CLK>, - <&gcc PCIE_PHY_CLK>; - clock-names = "core", "iface", "phy"; + <&gcc PCIE_PHY_CLK>, + <&gcc PCIE_AUX_CLK>, + <&gcc PCIE_ALT_REF_CLK>; + clock-names = "core", "iface", "phy", "aux", "ref"; resets = <&gcc PCIE_ACLK_RESET>, <&gcc PCIE_HCLK_RESET>, <&gcc PCIE_POR_RESET>, -- GitLab From dd58318c019f10bc94db36df66af6c55d4c0cbba Mon Sep 17 00:00:00 2001 From: Abhishek Sahu Date: Mon, 15 Jun 2020 23:05:59 +0200 Subject: [PATCH 0327/1476] PCI: qcom: Change duplicate PCI reset to phy reset The deinit issues reset_control_assert for PCI twice and does not contain phy reset. Link: https://lore.kernel.org/r/20200615210608.21469-4-ansuelsmth@gmail.com Signed-off-by: Abhishek Sahu Signed-off-by: Ansuel Smith Signed-off-by: Lorenzo Pieralisi Reviewed-by: Rob Herring Acked-by: Stanimir Varbanov --- drivers/pci/controller/dwc/pcie-qcom.c | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/drivers/pci/controller/dwc/pcie-qcom.c b/drivers/pci/controller/dwc/pcie-qcom.c index fa0bf652ce0b1..bae5158a98549 100644 --- a/drivers/pci/controller/dwc/pcie-qcom.c +++ b/drivers/pci/controller/dwc/pcie-qcom.c @@ -280,14 +280,14 @@ static void qcom_pcie_deinit_2_1_0(struct qcom_pcie *pcie) { struct qcom_pcie_resources_2_1_0 *res = &pcie->res.v2_1_0; + clk_disable_unprepare(res->phy_clk); reset_control_assert(res->pci_reset); reset_control_assert(res->axi_reset); reset_control_assert(res->ahb_reset); reset_control_assert(res->por_reset); - reset_control_assert(res->pci_reset); + reset_control_assert(res->phy_reset); clk_disable_unprepare(res->iface_clk); clk_disable_unprepare(res->core_clk); - clk_disable_unprepare(res->phy_clk); clk_disable_unprepare(res->aux_clk); clk_disable_unprepare(res->ref_clk); regulator_bulk_disable(ARRAY_SIZE(res->supplies), res->supplies); @@ -325,12 +325,6 @@ static int qcom_pcie_init_2_1_0(struct qcom_pcie *pcie) goto err_clk_core; } - ret = clk_prepare_enable(res->phy_clk); - if (ret) { - dev_err(dev, "cannot prepare/enable phy clock\n"); - goto err_clk_phy; - } - ret = clk_prepare_enable(res->aux_clk); if (ret) { dev_err(dev, "cannot prepare/enable aux clock\n"); @@ -383,6 +377,12 @@ static int qcom_pcie_init_2_1_0(struct qcom_pcie *pcie) return ret; } + ret = clk_prepare_enable(res->phy_clk); + if (ret) { + dev_err(dev, "cannot prepare/enable phy clock\n"); + goto err_deassert_ahb; + } + /* wait for clock acquisition */ usleep_range(1000, 1500); @@ -400,8 +400,6 @@ err_deassert_ahb: err_clk_ref: clk_disable_unprepare(res->aux_clk); err_clk_aux: - clk_disable_unprepare(res->phy_clk); -err_clk_phy: clk_disable_unprepare(res->core_clk); err_clk_core: clk_disable_unprepare(res->iface_clk); -- GitLab From ee367e2cdd2202b5714982739e684543cd2cee0e Mon Sep 17 00:00:00 2001 From: Ansuel Smith Date: Mon, 15 Jun 2020 23:06:00 +0200 Subject: [PATCH 0328/1476] PCI: qcom: Add missing reset for ipq806x Add missing ext reset used by ipq8064 SoC in PCIe qcom driver. Link: https://lore.kernel.org/r/20200615210608.21469-5-ansuelsmth@gmail.com Fixes: 82a823833f4e ("PCI: qcom: Add Qualcomm PCIe controller driver") Signed-off-by: Sham Muthayyan Signed-off-by: Ansuel Smith Signed-off-by: Lorenzo Pieralisi Reviewed-by: Rob Herring Reviewed-by: Philipp Zabel Acked-by: Stanimir Varbanov Cc: stable@vger.kernel.org # v4.5+ --- drivers/pci/controller/dwc/pcie-qcom.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/drivers/pci/controller/dwc/pcie-qcom.c b/drivers/pci/controller/dwc/pcie-qcom.c index bae5158a98549..fcd265067f342 100644 --- a/drivers/pci/controller/dwc/pcie-qcom.c +++ b/drivers/pci/controller/dwc/pcie-qcom.c @@ -95,6 +95,7 @@ struct qcom_pcie_resources_2_1_0 { struct reset_control *ahb_reset; struct reset_control *por_reset; struct reset_control *phy_reset; + struct reset_control *ext_reset; struct regulator_bulk_data supplies[QCOM_PCIE_2_1_0_MAX_SUPPLY]; }; @@ -272,6 +273,10 @@ static int qcom_pcie_get_resources_2_1_0(struct qcom_pcie *pcie) if (IS_ERR(res->por_reset)) return PTR_ERR(res->por_reset); + res->ext_reset = devm_reset_control_get_optional_exclusive(dev, "ext"); + if (IS_ERR(res->ext_reset)) + return PTR_ERR(res->ext_reset); + res->phy_reset = devm_reset_control_get_exclusive(dev, "phy"); return PTR_ERR_OR_ZERO(res->phy_reset); } @@ -285,6 +290,7 @@ static void qcom_pcie_deinit_2_1_0(struct qcom_pcie *pcie) reset_control_assert(res->axi_reset); reset_control_assert(res->ahb_reset); reset_control_assert(res->por_reset); + reset_control_assert(res->ext_reset); reset_control_assert(res->phy_reset); clk_disable_unprepare(res->iface_clk); clk_disable_unprepare(res->core_clk); @@ -343,6 +349,12 @@ static int qcom_pcie_init_2_1_0(struct qcom_pcie *pcie) goto err_deassert_ahb; } + ret = reset_control_deassert(res->ext_reset); + if (ret) { + dev_err(dev, "cannot deassert ext reset\n"); + goto err_deassert_ahb; + } + /* enable PCIe clocks and resets */ val = readl(pcie->parf + PCIE20_PARF_PHY_CTRL); val &= ~BIT(0); -- GitLab From b11b8cc161de027343ba6ae0c88a2ad5231a430f Mon Sep 17 00:00:00 2001 From: Ansuel Smith Date: Mon, 15 Jun 2020 23:06:01 +0200 Subject: [PATCH 0329/1476] dt-bindings: PCI: qcom: Add ext reset Document ext reset used in ipq8064 SoC by qcom PCIe driver. Link: https://lore.kernel.org/r/20200615210608.21469-6-ansuelsmth@gmail.com Signed-off-by: Ansuel Smith Signed-off-by: Lorenzo Pieralisi Acked-by: Rob Herring Acked-by: Stanimir Varbanov --- Documentation/devicetree/bindings/pci/qcom,pcie.txt | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Documentation/devicetree/bindings/pci/qcom,pcie.txt b/Documentation/devicetree/bindings/pci/qcom,pcie.txt index becdbdc0fffa8..6efcef0407415 100644 --- a/Documentation/devicetree/bindings/pci/qcom,pcie.txt +++ b/Documentation/devicetree/bindings/pci/qcom,pcie.txt @@ -179,6 +179,7 @@ - "pwr" PWR reset - "ahb" AHB reset - "phy_ahb" PHY AHB reset + - "ext" EXT reset - reset-names: Usage: required for ipq8074 @@ -287,8 +288,9 @@ <&gcc PCIE_HCLK_RESET>, <&gcc PCIE_POR_RESET>, <&gcc PCIE_PCI_RESET>, - <&gcc PCIE_PHY_RESET>; - reset-names = "axi", "ahb", "por", "pci", "phy"; + <&gcc PCIE_PHY_RESET>, + <&gcc PCIE_EXT_RESET>; + reset-names = "axi", "ahb", "por", "pci", "phy", "ext"; pinctrl-0 = <&pcie_pins_default>; pinctrl-names = "default"; }; -- GitLab From 6a114526af4689938863bf34976c83bfd279f517 Mon Sep 17 00:00:00 2001 From: Ansuel Smith Date: Mon, 15 Jun 2020 23:06:02 +0200 Subject: [PATCH 0330/1476] PCI: qcom: Use bulk clk api and assert on error Rework 2.1.0 revision to use bulk clk api and fix missing assert on reset_control_deassert error. Link: https://lore.kernel.org/r/20200615210608.21469-7-ansuelsmth@gmail.com Signed-off-by: Ansuel Smith Signed-off-by: Lorenzo Pieralisi Reviewed-by: Rob Herring Acked-by: Stanimir Varbanov --- drivers/pci/controller/dwc/pcie-qcom.c | 131 +++++++++---------------- 1 file changed, 46 insertions(+), 85 deletions(-) diff --git a/drivers/pci/controller/dwc/pcie-qcom.c b/drivers/pci/controller/dwc/pcie-qcom.c index fcd265067f342..8cb36cbcde46a 100644 --- a/drivers/pci/controller/dwc/pcie-qcom.c +++ b/drivers/pci/controller/dwc/pcie-qcom.c @@ -84,12 +84,9 @@ #define DEVICE_TYPE_RC 0x4 #define QCOM_PCIE_2_1_0_MAX_SUPPLY 3 +#define QCOM_PCIE_2_1_0_MAX_CLOCKS 5 struct qcom_pcie_resources_2_1_0 { - struct clk *iface_clk; - struct clk *core_clk; - struct clk *phy_clk; - struct clk *aux_clk; - struct clk *ref_clk; + struct clk_bulk_data clks[QCOM_PCIE_2_1_0_MAX_CLOCKS]; struct reset_control *pci_reset; struct reset_control *axi_reset; struct reset_control *ahb_reset; @@ -237,25 +234,21 @@ static int qcom_pcie_get_resources_2_1_0(struct qcom_pcie *pcie) if (ret) return ret; - res->iface_clk = devm_clk_get(dev, "iface"); - if (IS_ERR(res->iface_clk)) - return PTR_ERR(res->iface_clk); - - res->core_clk = devm_clk_get(dev, "core"); - if (IS_ERR(res->core_clk)) - return PTR_ERR(res->core_clk); - - res->phy_clk = devm_clk_get(dev, "phy"); - if (IS_ERR(res->phy_clk)) - return PTR_ERR(res->phy_clk); + res->clks[0].id = "iface"; + res->clks[1].id = "core"; + res->clks[2].id = "phy"; + res->clks[3].id = "aux"; + res->clks[4].id = "ref"; - res->aux_clk = devm_clk_get_optional(dev, "aux"); - if (IS_ERR(res->aux_clk)) - return PTR_ERR(res->aux_clk); + /* iface, core, phy are required */ + ret = devm_clk_bulk_get(dev, 3, res->clks); + if (ret < 0) + return ret; - res->ref_clk = devm_clk_get_optional(dev, "ref"); - if (IS_ERR(res->ref_clk)) - return PTR_ERR(res->ref_clk); + /* aux, ref are optional */ + ret = devm_clk_bulk_get_optional(dev, 2, res->clks + 3); + if (ret < 0) + return ret; res->pci_reset = devm_reset_control_get_exclusive(dev, "pci"); if (IS_ERR(res->pci_reset)) @@ -285,17 +278,13 @@ static void qcom_pcie_deinit_2_1_0(struct qcom_pcie *pcie) { struct qcom_pcie_resources_2_1_0 *res = &pcie->res.v2_1_0; - clk_disable_unprepare(res->phy_clk); + clk_bulk_disable_unprepare(ARRAY_SIZE(res->clks), res->clks); reset_control_assert(res->pci_reset); reset_control_assert(res->axi_reset); reset_control_assert(res->ahb_reset); reset_control_assert(res->por_reset); reset_control_assert(res->ext_reset); reset_control_assert(res->phy_reset); - clk_disable_unprepare(res->iface_clk); - clk_disable_unprepare(res->core_clk); - clk_disable_unprepare(res->aux_clk); - clk_disable_unprepare(res->ref_clk); regulator_bulk_disable(ARRAY_SIZE(res->supplies), res->supplies); } @@ -313,36 +302,6 @@ static int qcom_pcie_init_2_1_0(struct qcom_pcie *pcie) return ret; } - ret = reset_control_assert(res->ahb_reset); - if (ret) { - dev_err(dev, "cannot assert ahb reset\n"); - goto err_assert_ahb; - } - - ret = clk_prepare_enable(res->iface_clk); - if (ret) { - dev_err(dev, "cannot prepare/enable iface clock\n"); - goto err_assert_ahb; - } - - ret = clk_prepare_enable(res->core_clk); - if (ret) { - dev_err(dev, "cannot prepare/enable core clock\n"); - goto err_clk_core; - } - - ret = clk_prepare_enable(res->aux_clk); - if (ret) { - dev_err(dev, "cannot prepare/enable aux clock\n"); - goto err_clk_aux; - } - - ret = clk_prepare_enable(res->ref_clk); - if (ret) { - dev_err(dev, "cannot prepare/enable ref clock\n"); - goto err_clk_ref; - } - ret = reset_control_deassert(res->ahb_reset); if (ret) { dev_err(dev, "cannot deassert ahb reset\n"); @@ -352,48 +311,46 @@ static int qcom_pcie_init_2_1_0(struct qcom_pcie *pcie) ret = reset_control_deassert(res->ext_reset); if (ret) { dev_err(dev, "cannot deassert ext reset\n"); - goto err_deassert_ahb; + goto err_deassert_ext; } - /* enable PCIe clocks and resets */ - val = readl(pcie->parf + PCIE20_PARF_PHY_CTRL); - val &= ~BIT(0); - writel(val, pcie->parf + PCIE20_PARF_PHY_CTRL); - - /* enable external reference clock */ - val = readl(pcie->parf + PCIE20_PARF_PHY_REFCLK); - val |= BIT(16); - writel(val, pcie->parf + PCIE20_PARF_PHY_REFCLK); - ret = reset_control_deassert(res->phy_reset); if (ret) { dev_err(dev, "cannot deassert phy reset\n"); - return ret; + goto err_deassert_phy; } ret = reset_control_deassert(res->pci_reset); if (ret) { dev_err(dev, "cannot deassert pci reset\n"); - return ret; + goto err_deassert_pci; } ret = reset_control_deassert(res->por_reset); if (ret) { dev_err(dev, "cannot deassert por reset\n"); - return ret; + goto err_deassert_por; } ret = reset_control_deassert(res->axi_reset); if (ret) { dev_err(dev, "cannot deassert axi reset\n"); - return ret; + goto err_deassert_axi; } - ret = clk_prepare_enable(res->phy_clk); - if (ret) { - dev_err(dev, "cannot prepare/enable phy clock\n"); - goto err_deassert_ahb; - } + ret = clk_bulk_prepare_enable(ARRAY_SIZE(res->clks), res->clks); + if (ret) + goto err_clks; + + /* enable PCIe clocks and resets */ + val = readl(pcie->parf + PCIE20_PARF_PHY_CTRL); + val &= ~BIT(0); + writel(val, pcie->parf + PCIE20_PARF_PHY_CTRL); + + /* enable external reference clock */ + val = readl(pcie->parf + PCIE20_PARF_PHY_REFCLK); + val |= BIT(16); + writel(val, pcie->parf + PCIE20_PARF_PHY_REFCLK); /* wait for clock acquisition */ usleep_range(1000, 1500); @@ -407,15 +364,19 @@ static int qcom_pcie_init_2_1_0(struct qcom_pcie *pcie) return 0; +err_clks: + reset_control_assert(res->axi_reset); +err_deassert_axi: + reset_control_assert(res->por_reset); +err_deassert_por: + reset_control_assert(res->pci_reset); +err_deassert_pci: + reset_control_assert(res->phy_reset); +err_deassert_phy: + reset_control_assert(res->ext_reset); +err_deassert_ext: + reset_control_assert(res->ahb_reset); err_deassert_ahb: - clk_disable_unprepare(res->ref_clk); -err_clk_ref: - clk_disable_unprepare(res->aux_clk); -err_clk_aux: - clk_disable_unprepare(res->core_clk); -err_clk_core: - clk_disable_unprepare(res->iface_clk); -err_assert_ahb: regulator_bulk_disable(ARRAY_SIZE(res->supplies), res->supplies); return ret; -- GitLab From 5149901e9e6deca487c01cc434a3ac4125c7b00b Mon Sep 17 00:00:00 2001 From: Ansuel Smith Date: Mon, 15 Jun 2020 23:06:03 +0200 Subject: [PATCH 0331/1476] PCI: qcom: Define some PARF params needed for ipq8064 SoC Set some specific value for Tx De-Emphasis, Tx Swing and Rx equalization needed on some ipq8064 based device (Netgear R7800 for example). Without this the system locks on kernel load. Link: https://lore.kernel.org/r/20200615210608.21469-8-ansuelsmth@gmail.com Fixes: 82a823833f4e ("PCI: qcom: Add Qualcomm PCIe controller driver") Signed-off-by: Ansuel Smith Signed-off-by: Lorenzo Pieralisi Reviewed-by: Rob Herring Acked-by: Stanimir Varbanov Cc: stable@vger.kernel.org # v4.5+ --- drivers/pci/controller/dwc/pcie-qcom.c | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/drivers/pci/controller/dwc/pcie-qcom.c b/drivers/pci/controller/dwc/pcie-qcom.c index 8cb36cbcde46a..69c7b119e81a2 100644 --- a/drivers/pci/controller/dwc/pcie-qcom.c +++ b/drivers/pci/controller/dwc/pcie-qcom.c @@ -77,6 +77,18 @@ #define DBI_RO_WR_EN 1 #define PERST_DELAY_US 1000 +/* PARF registers */ +#define PCIE20_PARF_PCS_DEEMPH 0x34 +#define PCS_DEEMPH_TX_DEEMPH_GEN1(x) ((x) << 16) +#define PCS_DEEMPH_TX_DEEMPH_GEN2_3_5DB(x) ((x) << 8) +#define PCS_DEEMPH_TX_DEEMPH_GEN2_6DB(x) ((x) << 0) + +#define PCIE20_PARF_PCS_SWING 0x38 +#define PCS_SWING_TX_SWING_FULL(x) ((x) << 8) +#define PCS_SWING_TX_SWING_LOW(x) ((x) << 0) + +#define PCIE20_PARF_CONFIG_BITS 0x50 +#define PHY_RX0_EQ(x) ((x) << 24) #define PCIE20_v3_PARF_SLV_ADDR_SPACE_SIZE 0x358 #define SLV_ADDR_SPACE_SZ 0x10000000 @@ -293,6 +305,7 @@ static int qcom_pcie_init_2_1_0(struct qcom_pcie *pcie) struct qcom_pcie_resources_2_1_0 *res = &pcie->res.v2_1_0; struct dw_pcie *pci = pcie->pci; struct device *dev = pci->dev; + struct device_node *node = dev->of_node; u32 val; int ret; @@ -347,6 +360,17 @@ static int qcom_pcie_init_2_1_0(struct qcom_pcie *pcie) val &= ~BIT(0); writel(val, pcie->parf + PCIE20_PARF_PHY_CTRL); + if (of_device_is_compatible(node, "qcom,pcie-ipq8064")) { + writel(PCS_DEEMPH_TX_DEEMPH_GEN1(24) | + PCS_DEEMPH_TX_DEEMPH_GEN2_3_5DB(24) | + PCS_DEEMPH_TX_DEEMPH_GEN2_6DB(34), + pcie->parf + PCIE20_PARF_PCS_DEEMPH); + writel(PCS_SWING_TX_SWING_FULL(120) | + PCS_SWING_TX_SWING_LOW(120), + pcie->parf + PCIE20_PARF_PCS_SWING); + writel(PHY_RX0_EQ(4), pcie->parf + PCIE20_PARF_CONFIG_BITS); + } + /* enable external reference clock */ val = readl(pcie->parf + PCIE20_PARF_PHY_REFCLK); val |= BIT(16); -- GitLab From de3c4bf648975ea0b1d344d811e9b0748907b47c Mon Sep 17 00:00:00 2001 From: Ansuel Smith Date: Mon, 15 Jun 2020 23:06:04 +0200 Subject: [PATCH 0332/1476] PCI: qcom: Add support for tx term offset for rev 2.1.0 Add tx term offset support to pcie qcom driver need in some revision of the ipq806x SoC. Ipq8064 needs tx term offset set to 7. Link: https://lore.kernel.org/r/20200615210608.21469-9-ansuelsmth@gmail.com Fixes: 82a823833f4e ("PCI: qcom: Add Qualcomm PCIe controller driver") Signed-off-by: Sham Muthayyan Signed-off-by: Ansuel Smith Signed-off-by: Lorenzo Pieralisi Acked-by: Stanimir Varbanov Cc: stable@vger.kernel.org # v4.5+ --- drivers/pci/controller/dwc/pcie-qcom.c | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/drivers/pci/controller/dwc/pcie-qcom.c b/drivers/pci/controller/dwc/pcie-qcom.c index 69c7b119e81a2..34d961e492fd9 100644 --- a/drivers/pci/controller/dwc/pcie-qcom.c +++ b/drivers/pci/controller/dwc/pcie-qcom.c @@ -45,7 +45,13 @@ #define PCIE_CAP_CPL_TIMEOUT_DISABLE 0x10 #define PCIE20_PARF_PHY_CTRL 0x40 +#define PHY_CTRL_PHY_TX0_TERM_OFFSET_MASK GENMASK(20, 16) +#define PHY_CTRL_PHY_TX0_TERM_OFFSET(x) ((x) << 16) + #define PCIE20_PARF_PHY_REFCLK 0x4C +#define PHY_REFCLK_SSP_EN BIT(16) +#define PHY_REFCLK_USE_PAD BIT(12) + #define PCIE20_PARF_DBI_BASE_ADDR 0x168 #define PCIE20_PARF_SLV_ADDR_SPACE_SIZE 0x16C #define PCIE20_PARF_MHI_CLOCK_RESET_CTRL 0x174 @@ -371,9 +377,18 @@ static int qcom_pcie_init_2_1_0(struct qcom_pcie *pcie) writel(PHY_RX0_EQ(4), pcie->parf + PCIE20_PARF_CONFIG_BITS); } + if (of_device_is_compatible(node, "qcom,pcie-ipq8064")) { + /* set TX termination offset */ + val = readl(pcie->parf + PCIE20_PARF_PHY_CTRL); + val &= ~PHY_CTRL_PHY_TX0_TERM_OFFSET_MASK; + val |= PHY_CTRL_PHY_TX0_TERM_OFFSET(7); + writel(val, pcie->parf + PCIE20_PARF_PHY_CTRL); + } + /* enable external reference clock */ val = readl(pcie->parf + PCIE20_PARF_PHY_REFCLK); - val |= BIT(16); + val &= ~PHY_REFCLK_USE_PAD; + val |= PHY_REFCLK_SSP_EN; writel(val, pcie->parf + PCIE20_PARF_PHY_REFCLK); /* wait for clock acquisition */ -- GitLab From 8df093fe2ae1717389df0dcdc620c02cc35abb21 Mon Sep 17 00:00:00 2001 From: Ansuel Smith Date: Mon, 15 Jun 2020 23:06:05 +0200 Subject: [PATCH 0333/1476] PCI: qcom: Add ipq8064 rev2 variant Ipq8064-v2 have tx term offset set to 0. Introduce this variant to permit different offset based on the revision. Link: https://lore.kernel.org/r/20200615210608.21469-10-ansuelsmth@gmail.com Signed-off-by: Ansuel Smith Signed-off-by: Lorenzo Pieralisi Acked-by: Stanimir Varbanov --- drivers/pci/controller/dwc/pcie-qcom.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/pci/controller/dwc/pcie-qcom.c b/drivers/pci/controller/dwc/pcie-qcom.c index 34d961e492fd9..6f37ab914fa3b 100644 --- a/drivers/pci/controller/dwc/pcie-qcom.c +++ b/drivers/pci/controller/dwc/pcie-qcom.c @@ -366,7 +366,8 @@ static int qcom_pcie_init_2_1_0(struct qcom_pcie *pcie) val &= ~BIT(0); writel(val, pcie->parf + PCIE20_PARF_PHY_CTRL); - if (of_device_is_compatible(node, "qcom,pcie-ipq8064")) { + if (of_device_is_compatible(node, "qcom,pcie-ipq8064") || + of_device_is_compatible(node, "qcom,pcie-ipq8064-v2")) { writel(PCS_DEEMPH_TX_DEEMPH_GEN1(24) | PCS_DEEMPH_TX_DEEMPH_GEN2_3_5DB(24) | PCS_DEEMPH_TX_DEEMPH_GEN2_6DB(34), @@ -1464,6 +1465,7 @@ err_pm_runtime_put: static const struct of_device_id qcom_pcie_match[] = { { .compatible = "qcom,pcie-apq8084", .data = &ops_1_0_0 }, { .compatible = "qcom,pcie-ipq8064", .data = &ops_2_1_0 }, + { .compatible = "qcom,pcie-ipq8064-v2", .data = &ops_2_1_0 }, { .compatible = "qcom,pcie-apq8064", .data = &ops_2_1_0 }, { .compatible = "qcom,pcie-msm8996", .data = &ops_2_3_2 }, { .compatible = "qcom,pcie-ipq8074", .data = &ops_2_3_3 }, -- GitLab From d511580ea9c275793224bc47471001b8dea64133 Mon Sep 17 00:00:00 2001 From: Ansuel Smith Date: Mon, 15 Jun 2020 23:06:06 +0200 Subject: [PATCH 0334/1476] dt-bindings: PCI: qcom: Add ipq8064 rev 2 variant Document qcom,pcie-ipq8064-v2 needed to use different phy_tx0_term_offset. In ipq8064 phy_tx0_term_offset is 7. In ipq8064 v2 other SoC it's set to 0 by default. Link: https://lore.kernel.org/r/20200615210608.21469-11-ansuelsmth@gmail.com Signed-off-by: Ansuel Smith Signed-off-by: Lorenzo Pieralisi Acked-by: Rob Herring Acked-by: Stanimir Varbanov --- Documentation/devicetree/bindings/pci/qcom,pcie.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/pci/qcom,pcie.txt b/Documentation/devicetree/bindings/pci/qcom,pcie.txt index 6efcef0407415..02bc81bb8b2d7 100644 --- a/Documentation/devicetree/bindings/pci/qcom,pcie.txt +++ b/Documentation/devicetree/bindings/pci/qcom,pcie.txt @@ -5,6 +5,7 @@ Value type: Definition: Value should contain - "qcom,pcie-ipq8064" for ipq8064 + - "qcom,pcie-ipq8064-v2" for ipq8064 rev 2 or ipq8065 - "qcom,pcie-apq8064" for apq8064 - "qcom,pcie-apq8084" for apq8084 - "qcom,pcie-msm8996" for msm8996 or apq8096 -- GitLab From 51ed2c2b60265006bde7531d10993cf24def0aee Mon Sep 17 00:00:00 2001 From: Sham Muthayyan Date: Mon, 15 Jun 2020 23:06:07 +0200 Subject: [PATCH 0335/1476] PCI: qcom: Support pci speed set for ipq806x Some SoC based on ipq8064/5 needs to be limited to pci GEN1 speed due to some hardware limitations. Add support for speed setting defined by the max-link-speed binding. If not defined the max speed is set to GEN2 by default. Link: https://lore.kernel.org/r/20200615210608.21469-12-ansuelsmth@gmail.com Signed-off-by: Sham Muthayyan Signed-off-by: Ansuel Smith Signed-off-by: Lorenzo Pieralisi Reviewed-by: Rob Herring Acked-by: Stanimir Varbanov --- drivers/pci/controller/dwc/pcie-qcom.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/drivers/pci/controller/dwc/pcie-qcom.c b/drivers/pci/controller/dwc/pcie-qcom.c index 6f37ab914fa3b..17498e66e2dfe 100644 --- a/drivers/pci/controller/dwc/pcie-qcom.c +++ b/drivers/pci/controller/dwc/pcie-qcom.c @@ -27,6 +27,7 @@ #include #include +#include "../../pci.h" #include "pcie-designware.h" #define PCIE20_PARF_SYS_CTRL 0x00 @@ -99,6 +100,8 @@ #define PCIE20_v3_PARF_SLV_ADDR_SPACE_SIZE 0x358 #define SLV_ADDR_SPACE_SZ 0x10000000 +#define PCIE20_LNK_CONTROL2_LINK_STATUS2 0xa0 + #define DEVICE_TYPE_RC 0x4 #define QCOM_PCIE_2_1_0_MAX_SUPPLY 3 @@ -195,6 +198,7 @@ struct qcom_pcie { struct phy *phy; struct gpio_desc *reset; const struct qcom_pcie_ops *ops; + int gen; }; #define to_qcom_pcie(x) dev_get_drvdata((x)->dev) @@ -395,6 +399,11 @@ static int qcom_pcie_init_2_1_0(struct qcom_pcie *pcie) /* wait for clock acquisition */ usleep_range(1000, 1500); + if (pcie->gen == 1) { + val = readl(pci->dbi_base + PCIE20_LNK_CONTROL2_LINK_STATUS2); + val |= PCI_EXP_LNKSTA_CLS_2_5GB; + writel(val, pci->dbi_base + PCIE20_LNK_CONTROL2_LINK_STATUS2); + } /* Set the Max TLP size to 2K, instead of using default of 4K */ writel(CFG_REMOTE_RD_REQ_BRIDGE_SIZE_2K, @@ -1397,6 +1406,10 @@ static int qcom_pcie_probe(struct platform_device *pdev) goto err_pm_runtime_put; } + pcie->gen = of_pci_get_max_link_speed(pdev->dev.of_node); + if (pcie->gen < 0) + pcie->gen = 2; + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "parf"); pcie->parf = devm_ioremap_resource(dev, res); if (IS_ERR(pcie->parf)) { -- GitLab From 824001cb64c007129cd8bb95649a3eea9542e870 Mon Sep 17 00:00:00 2001 From: Ansuel Smith Date: Mon, 15 Jun 2020 23:06:08 +0200 Subject: [PATCH 0336/1476] PCI: qcom: Replace define with standard value Lots of define are actually already defined in pci_regs.h, directly use the standard defines. Link: https://lore.kernel.org/r/20200615210608.21469-13-ansuelsmth@gmail.com Signed-off-by: Ansuel Smith Signed-off-by: Lorenzo Pieralisi Acked-by: Stanimir Varbanov --- drivers/pci/controller/dwc/pcie-qcom.c | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/drivers/pci/controller/dwc/pcie-qcom.c b/drivers/pci/controller/dwc/pcie-qcom.c index 17498e66e2dfe..73d4eed26f655 100644 --- a/drivers/pci/controller/dwc/pcie-qcom.c +++ b/drivers/pci/controller/dwc/pcie-qcom.c @@ -40,11 +40,6 @@ #define L23_CLK_RMV_DIS BIT(2) #define L1_CLK_RMV_DIS BIT(1) -#define PCIE20_COMMAND_STATUS 0x04 -#define CMD_BME_VAL 0x4 -#define PCIE20_DEVICE_CONTROL2_STATUS2 0x98 -#define PCIE_CAP_CPL_TIMEOUT_DISABLE 0x10 - #define PCIE20_PARF_PHY_CTRL 0x40 #define PHY_CTRL_PHY_TX0_TERM_OFFSET_MASK GENMASK(20, 16) #define PHY_CTRL_PHY_TX0_TERM_OFFSET(x) ((x) << 16) @@ -73,8 +68,8 @@ #define CFG_BRIDGE_SB_INIT BIT(0) #define PCIE20_CAP 0x70 -#define PCIE20_CAP_LINK_CAPABILITIES (PCIE20_CAP + 0xC) -#define PCIE20_CAP_ACTIVE_STATE_LINK_PM_SUPPORT (BIT(10) | BIT(11)) +#define PCIE20_DEVICE_CONTROL2_STATUS2 (PCIE20_CAP + PCI_EXP_DEVCTL2) +#define PCIE20_CAP_LINK_CAPABILITIES (PCIE20_CAP + PCI_EXP_LNKCAP) #define PCIE20_CAP_LINK_1 (PCIE20_CAP + 0x14) #define PCIE_CAP_LINK1_VAL 0x2FD7F @@ -1095,15 +1090,15 @@ static int qcom_pcie_init_2_3_3(struct qcom_pcie *pcie) pcie->parf + PCIE20_PARF_SYS_CTRL); writel(0, pcie->parf + PCIE20_PARF_Q2A_FLUSH); - writel(CMD_BME_VAL, pci->dbi_base + PCIE20_COMMAND_STATUS); + writel(PCI_COMMAND_MASTER, pci->dbi_base + PCI_COMMAND); writel(DBI_RO_WR_EN, pci->dbi_base + PCIE20_MISC_CONTROL_1_REG); writel(PCIE_CAP_LINK1_VAL, pci->dbi_base + PCIE20_CAP_LINK_1); val = readl(pci->dbi_base + PCIE20_CAP_LINK_CAPABILITIES); - val &= ~PCIE20_CAP_ACTIVE_STATE_LINK_PM_SUPPORT; + val &= ~PCI_EXP_LNKCAP_ASPMS; writel(val, pci->dbi_base + PCIE20_CAP_LINK_CAPABILITIES); - writel(PCIE_CAP_CPL_TIMEOUT_DISABLE, pci->dbi_base + + writel(PCI_EXP_DEVCTL2_COMP_TMOUT_DIS, pci->dbi_base + PCIE20_DEVICE_CONTROL2_STATUS2); return 0; -- GitLab From 2a7e32d0547f41c5ce244f84cf5d6ca7fccee7eb Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Thu, 25 Jun 2020 18:14:55 -0500 Subject: [PATCH 0337/1476] PCI: Fix pci_cfg_wait queue locking problem The pci_cfg_wait queue is used to prevent user-space config accesses to devices while they are recovering from reset. Previously we used these operations on pci_cfg_wait: __add_wait_queue(&pci_cfg_wait, ...) __remove_wait_queue(&pci_cfg_wait, ...) wake_up_all(&pci_cfg_wait) The wake_up acquires the wait queue lock, but the add and remove do not. Originally these were all protected by the pci_lock, but cdcb33f98244 ("PCI: Avoid possible deadlock on pci_lock and p->pi_lock"), moved wake_up_all() outside pci_lock, so it could race with add/remove operations, which caused occasional kernel panics, e.g., during vfio-pci hotplug/unplug testing: Unable to handle kernel read from unreadable memory at virtual address ffff802dac469000 Resolve this by using wait_event() instead of __add_wait_queue() and __remove_wait_queue(). The wait queue lock is held by both wait_event() and wake_up_all(), so it provides mutual exclusion. Fixes: cdcb33f98244 ("PCI: Avoid possible deadlock on pci_lock and p->pi_lock") Link: https://lore.kernel.org/linux-pci/79827f2f-9b43-4411-1376-b9063b67aee3@huawei.com/T/#u Based-on: https://lore.kernel.org/linux-pci/20191210031527.40136-1-zhengxiang9@huawei.com/ Based-on-patch-by: Xiang Zheng Signed-off-by: Bjorn Helgaas Tested-by: Xiang Zheng Cc: Heyi Guo Cc: Biaoxiang Ye --- drivers/pci/access.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/drivers/pci/access.c b/drivers/pci/access.c index 79c4a2ef269a7..9793f17fa1843 100644 --- a/drivers/pci/access.c +++ b/drivers/pci/access.c @@ -204,17 +204,13 @@ EXPORT_SYMBOL(pci_bus_set_ops); static DECLARE_WAIT_QUEUE_HEAD(pci_cfg_wait); static noinline void pci_wait_cfg(struct pci_dev *dev) + __must_hold(&pci_lock) { - DECLARE_WAITQUEUE(wait, current); - - __add_wait_queue(&pci_cfg_wait, &wait); do { - set_current_state(TASK_UNINTERRUPTIBLE); raw_spin_unlock_irq(&pci_lock); - schedule(); + wait_event(pci_cfg_wait, !dev->block_cfg_access); raw_spin_lock_irq(&pci_lock); } while (dev->block_cfg_access); - __remove_wait_queue(&pci_cfg_wait, &wait); } /* Returns 0 on success, negative values indicate error. */ -- GitLab From 16d79cd4e23b1964d36c041ab027505ceacbbeeb Mon Sep 17 00:00:00 2001 From: Luc Van Oostenryck Date: Thu, 2 Jul 2020 18:26:49 +0200 Subject: [PATCH 0338/1476] PCI: Use 'pci_channel_state_t' instead of 'enum pci_channel_state' The method struct pci_error_handlers.error_detected() is defined and documented as taking an 'enum pci_channel_state' for the second argument, but most drivers use 'pci_channel_state_t' instead. This 'pci_channel_state_t' is not a typedef for the enum but a typedef for a bitwise type in order to have better/stricter typechecking. Consolidate everything by using 'pci_channel_state_t' in the method's definition, in the related helpers and in the drivers. Enforce use of 'pci_channel_state_t' by replacing 'enum pci_channel_state' with an anonymous 'enum'. Note: Currently, from a typechecking point of view this patch changes nothing because only the constants defined by the enum are bitwise, not the enum itself (sparse doesn't have the notion of 'bitwise enum'). This may change in some not too far future, hence the patch. [bhelgaas: squash in https://lore.kernel.org/r/20200702162651.49526-3-luc.vanoostenryck@gmail.com https://lore.kernel.org/r/20200702162651.49526-4-luc.vanoostenryck@gmail.com] Link: https://lore.kernel.org/r/20200702162651.49526-2-luc.vanoostenryck@gmail.com Signed-off-by: Luc Van Oostenryck Signed-off-by: Bjorn Helgaas --- Documentation/PCI/pci-error-recovery.rst | 8 ++++---- arch/powerpc/kernel/eeh_driver.c | 2 +- drivers/block/rsxx/core.c | 2 +- drivers/dma/ioat/init.c | 2 +- drivers/media/pci/ngene/ngene-cards.c | 2 +- drivers/misc/genwqe/card_base.c | 2 +- drivers/net/ethernet/intel/i40e/i40e_main.c | 2 +- drivers/net/ethernet/intel/ice/ice_main.c | 2 +- drivers/net/ethernet/intel/ixgb/ixgb_main.c | 4 ++-- drivers/net/ethernet/sfc/efx.c | 2 +- drivers/net/ethernet/sfc/falcon/efx.c | 2 +- drivers/pci/pci.h | 2 +- drivers/pci/pcie/err.c | 4 ++-- drivers/pci/pcie/portdrv_pci.c | 2 +- drivers/scsi/aacraid/linit.c | 2 +- drivers/scsi/sym53c8xx_2/sym_glue.c | 2 +- drivers/staging/qlge/qlge_main.c | 2 +- include/linux/pci.h | 4 ++-- 18 files changed, 24 insertions(+), 24 deletions(-) diff --git a/Documentation/PCI/pci-error-recovery.rst b/Documentation/PCI/pci-error-recovery.rst index 13beee23cb04f..ccd7134231338 100644 --- a/Documentation/PCI/pci-error-recovery.rst +++ b/Documentation/PCI/pci-error-recovery.rst @@ -79,7 +79,7 @@ This structure has the form:: struct pci_error_handlers { - int (*error_detected)(struct pci_dev *dev, enum pci_channel_state); + int (*error_detected)(struct pci_dev *dev, pci_channel_state_t); int (*mmio_enabled)(struct pci_dev *dev); int (*slot_reset)(struct pci_dev *dev); void (*resume)(struct pci_dev *dev); @@ -87,11 +87,11 @@ This structure has the form:: The possible channel states are:: - enum pci_channel_state { + typedef enum { pci_channel_io_normal, /* I/O channel is in normal state */ pci_channel_io_frozen, /* I/O to channel is blocked */ pci_channel_io_perm_failure, /* PCI card is dead */ - }; + } pci_channel_state_t; Possible return values are:: @@ -348,7 +348,7 @@ STEP 6: Permanent Failure ------------------------- A "permanent failure" has occurred, and the platform cannot recover the device. The platform will call error_detected() with a -pci_channel_state value of pci_channel_io_perm_failure. +pci_channel_state_t value of pci_channel_io_perm_failure. The device driver should, at this point, assume the worst. It should cancel all pending I/O, refuse all new I/O, returning -EIO to diff --git a/arch/powerpc/kernel/eeh_driver.c b/arch/powerpc/kernel/eeh_driver.c index 7b048cee767c7..ab8806d2e03e6 100644 --- a/arch/powerpc/kernel/eeh_driver.c +++ b/arch/powerpc/kernel/eeh_driver.c @@ -214,7 +214,7 @@ static void eeh_dev_save_state(struct eeh_dev *edev, void *userdata) pci_save_state(pdev); } -static void eeh_set_channel_state(struct eeh_pe *root, enum pci_channel_state s) +static void eeh_set_channel_state(struct eeh_pe *root, pci_channel_state_t s) { struct eeh_pe *pe; struct eeh_dev *edev, *tmp; diff --git a/drivers/block/rsxx/core.c b/drivers/block/rsxx/core.c index 10f6368117d81..34e937dd6bca1 100644 --- a/drivers/block/rsxx/core.c +++ b/drivers/block/rsxx/core.c @@ -625,7 +625,7 @@ static int rsxx_eeh_fifo_flush_poll(struct rsxx_cardinfo *card) } static pci_ers_result_t rsxx_error_detected(struct pci_dev *dev, - enum pci_channel_state error) + pci_channel_state_t error) { int st; diff --git a/drivers/dma/ioat/init.c b/drivers/dma/ioat/init.c index 58d13564f88b4..089893f2bbb8b 100644 --- a/drivers/dma/ioat/init.c +++ b/drivers/dma/ioat/init.c @@ -1267,7 +1267,7 @@ static void ioat_resume(struct ioatdma_device *ioat_dma) #define DRV_NAME "ioatdma" static pci_ers_result_t ioat_pcie_error_detected(struct pci_dev *pdev, - enum pci_channel_state error) + pci_channel_state_t error) { dev_dbg(&pdev->dev, "%s: PCIe AER error %d\n", DRV_NAME, error); diff --git a/drivers/media/pci/ngene/ngene-cards.c b/drivers/media/pci/ngene/ngene-cards.c index 6185806a00e00..8bfb3d8ea6103 100644 --- a/drivers/media/pci/ngene/ngene-cards.c +++ b/drivers/media/pci/ngene/ngene-cards.c @@ -1186,7 +1186,7 @@ MODULE_DEVICE_TABLE(pci, ngene_id_tbl); /****************************************************************************/ static pci_ers_result_t ngene_error_detected(struct pci_dev *dev, - enum pci_channel_state state) + pci_channel_state_t state) { dev_err(&dev->dev, "PCI error\n"); if (state == pci_channel_io_perm_failure) diff --git a/drivers/misc/genwqe/card_base.c b/drivers/misc/genwqe/card_base.c index 1dc6c7c5cbce9..97b8ecc423833 100644 --- a/drivers/misc/genwqe/card_base.c +++ b/drivers/misc/genwqe/card_base.c @@ -1240,7 +1240,7 @@ static void genwqe_remove(struct pci_dev *pci_dev) * error is detected. */ static pci_ers_result_t genwqe_err_error_detected(struct pci_dev *pci_dev, - enum pci_channel_state state) + pci_channel_state_t state) { struct genwqe_dev *cd; diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c index 5d807c8004f85..f0de2d1842b41 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_main.c +++ b/drivers/net/ethernet/intel/i40e/i40e_main.c @@ -15465,7 +15465,7 @@ unmap: * remediation. **/ static pci_ers_result_t i40e_pci_error_detected(struct pci_dev *pdev, - enum pci_channel_state error) + pci_channel_state_t error) { struct i40e_pf *pf = pci_get_drvdata(pdev); diff --git a/drivers/net/ethernet/intel/ice/ice_main.c b/drivers/net/ethernet/intel/ice/ice_main.c index 082825e3cb395..4dd9226a12dfa 100644 --- a/drivers/net/ethernet/intel/ice/ice_main.c +++ b/drivers/net/ethernet/intel/ice/ice_main.c @@ -3586,7 +3586,7 @@ static void ice_remove(struct pci_dev *pdev) * is in progress. Allows the driver to gracefully prepare/handle PCI errors. */ static pci_ers_result_t -ice_pci_err_detected(struct pci_dev *pdev, enum pci_channel_state err) +ice_pci_err_detected(struct pci_dev *pdev, pci_channel_state_t err) { struct ice_pf *pf = pci_get_drvdata(pdev); diff --git a/drivers/net/ethernet/intel/ixgb/ixgb_main.c b/drivers/net/ethernet/intel/ixgb/ixgb_main.c index b64e91ea3465d..00db4b5863b1c 100644 --- a/drivers/net/ethernet/intel/ixgb/ixgb_main.c +++ b/drivers/net/ethernet/intel/ixgb/ixgb_main.c @@ -82,7 +82,7 @@ static int ixgb_vlan_rx_kill_vid(struct net_device *netdev, static void ixgb_restore_vlan(struct ixgb_adapter *adapter); static pci_ers_result_t ixgb_io_error_detected (struct pci_dev *pdev, - enum pci_channel_state state); + pci_channel_state_t state); static pci_ers_result_t ixgb_io_slot_reset (struct pci_dev *pdev); static void ixgb_io_resume (struct pci_dev *pdev); @@ -2194,7 +2194,7 @@ ixgb_restore_vlan(struct ixgb_adapter *adapter) * a PCI bus error is detected. */ static pci_ers_result_t ixgb_io_error_detected(struct pci_dev *pdev, - enum pci_channel_state state) + pci_channel_state_t state) { struct net_device *netdev = pci_get_drvdata(pdev); struct ixgb_adapter *adapter = netdev_priv(netdev); diff --git a/drivers/net/ethernet/sfc/efx.c b/drivers/net/ethernet/sfc/efx.c index 256807c28ff7c..ed627aff7b36f 100644 --- a/drivers/net/ethernet/sfc/efx.c +++ b/drivers/net/ethernet/sfc/efx.c @@ -1519,7 +1519,7 @@ static const struct dev_pm_ops efx_pm_ops = { * Stop the software path and request a slot reset. */ static pci_ers_result_t efx_io_error_detected(struct pci_dev *pdev, - enum pci_channel_state state) + pci_channel_state_t state) { pci_ers_result_t status = PCI_ERS_RESULT_RECOVERED; struct efx_nic *efx = pci_get_drvdata(pdev); diff --git a/drivers/net/ethernet/sfc/falcon/efx.c b/drivers/net/ethernet/sfc/falcon/efx.c index 42bcd34fc508e..f8979991970ef 100644 --- a/drivers/net/ethernet/sfc/falcon/efx.c +++ b/drivers/net/ethernet/sfc/falcon/efx.c @@ -3118,7 +3118,7 @@ static const struct dev_pm_ops ef4_pm_ops = { * Stop the software path and request a slot reset. */ static pci_ers_result_t ef4_io_error_detected(struct pci_dev *pdev, - enum pci_channel_state state) + pci_channel_state_t state) { pci_ers_result_t status = PCI_ERS_RESULT_RECOVERED; struct ef4_nic *efx = pci_get_drvdata(pdev); diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index 6d3f758671064..c6c0c455f59fc 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h @@ -555,7 +555,7 @@ static inline int pci_dev_specific_disable_acs_redir(struct pci_dev *dev) /* PCI error reporting and recovery */ pci_ers_result_t pcie_do_recovery(struct pci_dev *dev, - enum pci_channel_state state, + pci_channel_state_t state, pci_ers_result_t (*reset_link)(struct pci_dev *pdev)); bool pcie_wait_for_link(struct pci_dev *pdev, bool active); diff --git a/drivers/pci/pcie/err.c b/drivers/pci/pcie/err.c index 14bb8f54723ed..467686ee2d8be 100644 --- a/drivers/pci/pcie/err.c +++ b/drivers/pci/pcie/err.c @@ -46,7 +46,7 @@ static pci_ers_result_t merge_result(enum pci_ers_result orig, } static int report_error_detected(struct pci_dev *dev, - enum pci_channel_state state, + pci_channel_state_t state, enum pci_ers_result *result) { pci_ers_result_t vote; @@ -147,7 +147,7 @@ out: } pci_ers_result_t pcie_do_recovery(struct pci_dev *dev, - enum pci_channel_state state, + pci_channel_state_t state, pci_ers_result_t (*reset_link)(struct pci_dev *pdev)) { pci_ers_result_t status = PCI_ERS_RESULT_CAN_RECOVER; diff --git a/drivers/pci/pcie/portdrv_pci.c b/drivers/pci/pcie/portdrv_pci.c index 3acf151ae015a..3a3ce40ae1abd 100644 --- a/drivers/pci/pcie/portdrv_pci.c +++ b/drivers/pci/pcie/portdrv_pci.c @@ -146,7 +146,7 @@ static void pcie_portdrv_remove(struct pci_dev *dev) } static pci_ers_result_t pcie_portdrv_error_detected(struct pci_dev *dev, - enum pci_channel_state error) + pci_channel_state_t error) { /* Root Port has no impact. Always recovers. */ return PCI_ERS_RESULT_CAN_RECOVER; diff --git a/drivers/scsi/aacraid/linit.c b/drivers/scsi/aacraid/linit.c index a308e86a97f19..37f65602b0ec7 100644 --- a/drivers/scsi/aacraid/linit.c +++ b/drivers/scsi/aacraid/linit.c @@ -2002,7 +2002,7 @@ static void aac_remove_one(struct pci_dev *pdev) } static pci_ers_result_t aac_pci_error_detected(struct pci_dev *pdev, - enum pci_channel_state error) + pci_channel_state_t error) { struct Scsi_Host *shost = pci_get_drvdata(pdev); struct aac_dev *aac = shost_priv(shost); diff --git a/drivers/scsi/sym53c8xx_2/sym_glue.c b/drivers/scsi/sym53c8xx_2/sym_glue.c index 2ca018ce796f7..f455243bdb9be 100644 --- a/drivers/scsi/sym53c8xx_2/sym_glue.c +++ b/drivers/scsi/sym53c8xx_2/sym_glue.c @@ -1743,7 +1743,7 @@ static void sym2_remove(struct pci_dev *pdev) * @state: current state of the PCI slot */ static pci_ers_result_t sym2_io_error_detected(struct pci_dev *pdev, - enum pci_channel_state state) + pci_channel_state_t state) { /* If slot is permanently frozen, turn everything off */ if (state == pci_channel_io_perm_failure) { diff --git a/drivers/staging/qlge/qlge_main.c b/drivers/staging/qlge/qlge_main.c index 402edaeffe12d..ac30aefe49a11 100644 --- a/drivers/staging/qlge/qlge_main.c +++ b/drivers/staging/qlge/qlge_main.c @@ -4678,7 +4678,7 @@ static void ql_eeh_close(struct net_device *ndev) * a PCI bus error is detected. */ static pci_ers_result_t qlge_io_error_detected(struct pci_dev *pdev, - enum pci_channel_state state) + pci_channel_state_t state) { struct net_device *ndev = pci_get_drvdata(pdev); struct ql_adapter *qdev = netdev_priv(ndev); diff --git a/include/linux/pci.h b/include/linux/pci.h index c79d83304e529..adcee9e30bfa5 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -179,7 +179,7 @@ static inline const char *pci_power_name(pci_power_t state) */ typedef unsigned int __bitwise pci_channel_state_t; -enum pci_channel_state { +enum { /* I/O channel is in normal state */ pci_channel_io_normal = (__force pci_channel_state_t) 1, @@ -785,7 +785,7 @@ enum pci_ers_result { struct pci_error_handlers { /* PCI bus error detected on this device */ pci_ers_result_t (*error_detected)(struct pci_dev *dev, - enum pci_channel_state error); + pci_channel_state_t error); /* MMIO has been re-enabled, but not DMA */ pci_ers_result_t (*mmio_enabled)(struct pci_dev *dev); -- GitLab From 2194bc7c39610be7cabe7456c5f63a570604f015 Mon Sep 17 00:00:00 2001 From: Rajat Jain Date: Mon, 6 Jul 2020 16:32:40 -0700 Subject: [PATCH 0339/1476] PCI: Add device even if driver attach failed device_attach() returning failure indicates a driver error while trying to probe the device. In such a scenario, the PCI device should still be added in the system and be visible to the user. When device_attach() fails, merely warn about it and keep the PCI device in the system. This partially reverts ab1a187bba5c ("PCI: Check device_attach() return value always"). Link: https://lore.kernel.org/r/20200706233240.3245512-1-rajatja@google.com Signed-off-by: Rajat Jain Signed-off-by: Bjorn Helgaas Reviewed-by: Greg Kroah-Hartman Cc: stable@vger.kernel.org # v4.6+ --- drivers/pci/bus.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/drivers/pci/bus.c b/drivers/pci/bus.c index 8e40b3e6da77d..3cef835b375fd 100644 --- a/drivers/pci/bus.c +++ b/drivers/pci/bus.c @@ -322,12 +322,8 @@ void pci_bus_add_device(struct pci_dev *dev) dev->match_driver = true; retval = device_attach(&dev->dev); - if (retval < 0 && retval != -EPROBE_DEFER) { + if (retval < 0 && retval != -EPROBE_DEFER) pci_warn(dev, "device attach failed (%d)\n", retval); - pci_proc_detach_device(dev); - pci_remove_sysfs_dev_files(dev); - return; - } pci_dev_assign_added(dev, true); } -- GitLab From 0678e3109a3cf438a8d1c8e3c3e58a45a59df262 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Tue, 7 Jul 2020 18:48:17 -0500 Subject: [PATCH 0340/1476] PCI/AER: Simplify __aer_print_error() aer_correctable_error_string[] and aer_uncorrectable_error_string[] have descriptions of AER error status bits. Add NULL entries to these tables so all entries for bits 0-31 are defined. Then we don't have to check for ARRAY_SIZE() when decoding a status word, which simplifies __aer_print_error(). Signed-off-by: Bjorn Helgaas --- drivers/pci/pcie/aer.c | 48 ++++++++++++++++++++++++++++++------------ 1 file changed, 34 insertions(+), 14 deletions(-) diff --git a/drivers/pci/pcie/aer.c b/drivers/pci/pcie/aer.c index 3acf56683915e..9176c8a968b93 100644 --- a/drivers/pci/pcie/aer.c +++ b/drivers/pci/pcie/aer.c @@ -447,7 +447,7 @@ static const char *aer_error_layer[] = { "Transaction Layer" }; -static const char *aer_correctable_error_string[AER_MAX_TYPEOF_COR_ERRS] = { +static const char *aer_correctable_error_string[] = { "RxErr", /* Bit Position 0 */ NULL, NULL, @@ -464,9 +464,25 @@ static const char *aer_correctable_error_string[AER_MAX_TYPEOF_COR_ERRS] = { "NonFatalErr", /* Bit Position 13 */ "CorrIntErr", /* Bit Position 14 */ "HeaderOF", /* Bit Position 15 */ + NULL, /* Bit Position 16 */ + NULL, /* Bit Position 17 */ + NULL, /* Bit Position 18 */ + NULL, /* Bit Position 19 */ + NULL, /* Bit Position 20 */ + NULL, /* Bit Position 21 */ + NULL, /* Bit Position 22 */ + NULL, /* Bit Position 23 */ + NULL, /* Bit Position 24 */ + NULL, /* Bit Position 25 */ + NULL, /* Bit Position 26 */ + NULL, /* Bit Position 27 */ + NULL, /* Bit Position 28 */ + NULL, /* Bit Position 29 */ + NULL, /* Bit Position 30 */ + NULL, /* Bit Position 31 */ }; -static const char *aer_uncorrectable_error_string[AER_MAX_TYPEOF_UNCOR_ERRS] = { +static const char *aer_uncorrectable_error_string[] = { "Undefined", /* Bit Position 0 */ NULL, NULL, @@ -494,6 +510,11 @@ static const char *aer_uncorrectable_error_string[AER_MAX_TYPEOF_UNCOR_ERRS] = { "AtomicOpBlocked", /* Bit Position 24 */ "TLPBlockedErr", /* Bit Position 25 */ "PoisonTLPBlocked", /* Bit Position 26 */ + NULL, /* Bit Position 27 */ + NULL, /* Bit Position 28 */ + NULL, /* Bit Position 29 */ + NULL, /* Bit Position 30 */ + NULL, /* Bit Position 31 */ }; static const char *aer_agent_string[] = { @@ -650,24 +671,23 @@ static void __print_tlp_header(struct pci_dev *dev, static void __aer_print_error(struct pci_dev *dev, struct aer_err_info *info) { + const char **strings; unsigned long status = info->status & ~info->mask; - const char *errmsg = NULL; + const char *errmsg; int i; + if (info->severity == AER_CORRECTABLE) + strings = aer_correctable_error_string; + else + strings = aer_uncorrectable_error_string; + for_each_set_bit(i, &status, 32) { - if (info->severity == AER_CORRECTABLE) - errmsg = i < ARRAY_SIZE(aer_correctable_error_string) ? - aer_correctable_error_string[i] : NULL; - else - errmsg = i < ARRAY_SIZE(aer_uncorrectable_error_string) ? - aer_uncorrectable_error_string[i] : NULL; + errmsg = strings[i]; + if (!errmsg) + errmsg = "Unknown Error Bit"; - if (errmsg) - pci_err(dev, " [%2d] %-22s%s\n", i, errmsg, + pci_err(dev, " [%2d] %-22s%s\n", i, errmsg, info->first_error == i ? " (First)" : ""); - else - pci_err(dev, " [%2d] Unknown Error Bit%s\n", - i, info->first_error == i ? " (First)" : ""); } pci_dev_aer_stats_incr(dev, info); } -- GitLab From e83e2ca3c39553a9d0fd497d9c839b341e38c742 Mon Sep 17 00:00:00 2001 From: Matt Jolly Date: Fri, 19 Jun 2020 01:55:11 +1000 Subject: [PATCH 0341/1476] PCI/AER: Log correctable errors as warning, not error PCIe correctable errors are recovered by hardware with no need for software intervention (PCIe r5.0, sec 6.2.2.1). Reduce the log level of correctable errors from KERN_ERR to KERN_WARNING. The bug reports below are for correctable error logging. This doesn't fix the cause of those reports, but it may make the messages less alarming. [bhelgaas: commit log, use pci_printk() to avoid code duplication] Link: https://bugzilla.kernel.org/show_bug.cgi?id=201517 Link: https://bugzilla.kernel.org/show_bug.cgi?id=196183 Link: https://lore.kernel.org/r/20200618155511.16009-1-Kangie@footclan.ninja Signed-off-by: Matt Jolly Signed-off-by: Bjorn Helgaas --- drivers/pci/pcie/aer.c | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/drivers/pci/pcie/aer.c b/drivers/pci/pcie/aer.c index 9176c8a968b93..ca886bf91fd9f 100644 --- a/drivers/pci/pcie/aer.c +++ b/drivers/pci/pcie/aer.c @@ -673,20 +673,23 @@ static void __aer_print_error(struct pci_dev *dev, { const char **strings; unsigned long status = info->status & ~info->mask; - const char *errmsg; + const char *level, *errmsg; int i; - if (info->severity == AER_CORRECTABLE) + if (info->severity == AER_CORRECTABLE) { strings = aer_correctable_error_string; - else + level = KERN_WARNING; + } else { strings = aer_uncorrectable_error_string; + level = KERN_ERR; + } for_each_set_bit(i, &status, 32) { errmsg = strings[i]; if (!errmsg) errmsg = "Unknown Error Bit"; - pci_err(dev, " [%2d] %-22s%s\n", i, errmsg, + pci_printk(level, dev, " [%2d] %-22s%s\n", i, errmsg, info->first_error == i ? " (First)" : ""); } pci_dev_aer_stats_incr(dev, info); @@ -696,6 +699,7 @@ void aer_print_error(struct pci_dev *dev, struct aer_err_info *info) { int layer, agent; int id = ((dev->bus->number << 8) | dev->devfn); + const char *level; if (!info->status) { pci_err(dev, "PCIe Bus Error: severity=%s, type=Inaccessible, (Unregistered Agent ID)\n", @@ -706,13 +710,14 @@ void aer_print_error(struct pci_dev *dev, struct aer_err_info *info) layer = AER_GET_LAYER_ERROR(info->severity, info->status); agent = AER_GET_AGENT(info->severity, info->status); - pci_err(dev, "PCIe Bus Error: severity=%s, type=%s, (%s)\n", - aer_error_severity_string[info->severity], - aer_error_layer[layer], aer_agent_string[agent]); + level = (info->severity == AER_CORRECTABLE) ? KERN_WARNING : KERN_ERR; + + pci_printk(level, dev, "PCIe Bus Error: severity=%s, type=%s, (%s)\n", + aer_error_severity_string[info->severity], + aer_error_layer[layer], aer_agent_string[agent]); - pci_err(dev, " device [%04x:%04x] error status/mask=%08x/%08x\n", - dev->vendor, dev->device, - info->status, info->mask); + pci_printk(level, dev, " device [%04x:%04x] error status/mask=%08x/%08x\n", + dev->vendor, dev->device, info->status, info->mask); __aer_print_error(dev, info); -- GitLab From 3337bf41e0dd70b4064cdf60acdfcdc2d050066c Mon Sep 17 00:00:00 2001 From: "Desnes A. Nunes do Rosario" Date: Fri, 26 Jun 2020 13:47:37 -0300 Subject: [PATCH 0342/1476] selftests/powerpc: Purge extra count_pmc() calls of ebb selftests An extra count on ebb_state.stats.pmc_count[PMC_INDEX(pmc)] is being per- formed when count_pmc() is used to reset PMCs on a few selftests. This extra pmc_count can occasionally invalidate results, such as the ones from cycles_test shown hereafter. The ebb_check_count() failed with an above the upper limit error due to the extra value on ebb_state.stats.pmc_count. Furthermore, this extra count is also indicated by extra PMC1 trace_log on the output of the cycle test (as well as on pmc56_overflow_test): ========== ... [21]: counter = 8 [22]: register SPRN_MMCR0 = 0x0000000080000080 [23]: register SPRN_PMC1 = 0x0000000080000004 [24]: counter = 9 [25]: register SPRN_MMCR0 = 0x0000000080000080 [26]: register SPRN_PMC1 = 0x0000000080000004 [27]: counter = 10 [28]: register SPRN_MMCR0 = 0x0000000080000080 [29]: register SPRN_PMC1 = 0x0000000080000004 >> [30]: register SPRN_PMC1 = 0x000000004000051e PMC1 count (0x280000546) above upper limit 0x2800003e8 (+0x15e) [FAIL] Test FAILED on line 52 failure: cycles ========== Signed-off-by: Desnes A. Nunes do Rosario Tested-by: Sachin Sant Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20200626164737.21943-1-desnesn@linux.ibm.com --- .../selftests/powerpc/pmu/ebb/back_to_back_ebbs_test.c | 2 -- tools/testing/selftests/powerpc/pmu/ebb/cycles_test.c | 2 -- .../selftests/powerpc/pmu/ebb/cycles_with_freeze_test.c | 2 -- .../selftests/powerpc/pmu/ebb/cycles_with_mmcr2_test.c | 2 -- tools/testing/selftests/powerpc/pmu/ebb/ebb.c | 2 -- .../selftests/powerpc/pmu/ebb/ebb_on_willing_child_test.c | 2 -- .../selftests/powerpc/pmu/ebb/lost_exception_test.c | 1 - .../testing/selftests/powerpc/pmu/ebb/multi_counter_test.c | 7 ------- .../selftests/powerpc/pmu/ebb/multi_ebb_procs_test.c | 2 -- .../testing/selftests/powerpc/pmu/ebb/pmae_handling_test.c | 2 -- .../selftests/powerpc/pmu/ebb/pmc56_overflow_test.c | 2 -- 11 files changed, 26 deletions(-) diff --git a/tools/testing/selftests/powerpc/pmu/ebb/back_to_back_ebbs_test.c b/tools/testing/selftests/powerpc/pmu/ebb/back_to_back_ebbs_test.c index a2d7b0e3dca97..a26ac122c759f 100644 --- a/tools/testing/selftests/powerpc/pmu/ebb/back_to_back_ebbs_test.c +++ b/tools/testing/selftests/powerpc/pmu/ebb/back_to_back_ebbs_test.c @@ -91,8 +91,6 @@ int back_to_back_ebbs(void) ebb_global_disable(); ebb_freeze_pmcs(); - count_pmc(1, sample_period); - dump_ebb_state(); event_close(&event); diff --git a/tools/testing/selftests/powerpc/pmu/ebb/cycles_test.c b/tools/testing/selftests/powerpc/pmu/ebb/cycles_test.c index bc893813483ee..bb9f587fa76e8 100644 --- a/tools/testing/selftests/powerpc/pmu/ebb/cycles_test.c +++ b/tools/testing/selftests/powerpc/pmu/ebb/cycles_test.c @@ -42,8 +42,6 @@ int cycles(void) ebb_global_disable(); ebb_freeze_pmcs(); - count_pmc(1, sample_period); - dump_ebb_state(); event_close(&event); diff --git a/tools/testing/selftests/powerpc/pmu/ebb/cycles_with_freeze_test.c b/tools/testing/selftests/powerpc/pmu/ebb/cycles_with_freeze_test.c index dcd351d203289..9ae795ce314e6 100644 --- a/tools/testing/selftests/powerpc/pmu/ebb/cycles_with_freeze_test.c +++ b/tools/testing/selftests/powerpc/pmu/ebb/cycles_with_freeze_test.c @@ -99,8 +99,6 @@ int cycles_with_freeze(void) ebb_global_disable(); ebb_freeze_pmcs(); - count_pmc(1, sample_period); - dump_ebb_state(); printf("EBBs while frozen %d\n", ebbs_while_frozen); diff --git a/tools/testing/selftests/powerpc/pmu/ebb/cycles_with_mmcr2_test.c b/tools/testing/selftests/powerpc/pmu/ebb/cycles_with_mmcr2_test.c index 94c99c12c0f23..4b45a2e70f62b 100644 --- a/tools/testing/selftests/powerpc/pmu/ebb/cycles_with_mmcr2_test.c +++ b/tools/testing/selftests/powerpc/pmu/ebb/cycles_with_mmcr2_test.c @@ -71,8 +71,6 @@ int cycles_with_mmcr2(void) ebb_global_disable(); ebb_freeze_pmcs(); - count_pmc(1, sample_period); - dump_ebb_state(); event_close(&event); diff --git a/tools/testing/selftests/powerpc/pmu/ebb/ebb.c b/tools/testing/selftests/powerpc/pmu/ebb/ebb.c index dfbc5c3ad52d7..21537d6eb6b7d 100644 --- a/tools/testing/selftests/powerpc/pmu/ebb/ebb.c +++ b/tools/testing/selftests/powerpc/pmu/ebb/ebb.c @@ -396,8 +396,6 @@ int ebb_child(union pipe read_pipe, union pipe write_pipe) ebb_global_disable(); ebb_freeze_pmcs(); - count_pmc(1, sample_period); - dump_ebb_state(); event_close(&event); diff --git a/tools/testing/selftests/powerpc/pmu/ebb/ebb_on_willing_child_test.c b/tools/testing/selftests/powerpc/pmu/ebb/ebb_on_willing_child_test.c index ca2f7d729155b..b208bf6ad58d3 100644 --- a/tools/testing/selftests/powerpc/pmu/ebb/ebb_on_willing_child_test.c +++ b/tools/testing/selftests/powerpc/pmu/ebb/ebb_on_willing_child_test.c @@ -38,8 +38,6 @@ static int victim_child(union pipe read_pipe, union pipe write_pipe) ebb_global_disable(); ebb_freeze_pmcs(); - count_pmc(1, sample_period); - dump_ebb_state(); FAIL_IF(ebb_state.stats.ebb_count == 0); diff --git a/tools/testing/selftests/powerpc/pmu/ebb/lost_exception_test.c b/tools/testing/selftests/powerpc/pmu/ebb/lost_exception_test.c index ac3e6e182614a..ba2681a12cc7b 100644 --- a/tools/testing/selftests/powerpc/pmu/ebb/lost_exception_test.c +++ b/tools/testing/selftests/powerpc/pmu/ebb/lost_exception_test.c @@ -75,7 +75,6 @@ static int test_body(void) ebb_freeze_pmcs(); ebb_global_disable(); - count_pmc(4, sample_period); mtspr(SPRN_PMC4, 0xdead); dump_summary_ebb_state(); diff --git a/tools/testing/selftests/powerpc/pmu/ebb/multi_counter_test.c b/tools/testing/selftests/powerpc/pmu/ebb/multi_counter_test.c index b8242e9d97d2d..791d37ba327b5 100644 --- a/tools/testing/selftests/powerpc/pmu/ebb/multi_counter_test.c +++ b/tools/testing/selftests/powerpc/pmu/ebb/multi_counter_test.c @@ -70,13 +70,6 @@ int multi_counter(void) ebb_global_disable(); ebb_freeze_pmcs(); - count_pmc(1, sample_period); - count_pmc(2, sample_period); - count_pmc(3, sample_period); - count_pmc(4, sample_period); - count_pmc(5, sample_period); - count_pmc(6, sample_period); - dump_ebb_state(); for (i = 0; i < 6; i++) diff --git a/tools/testing/selftests/powerpc/pmu/ebb/multi_ebb_procs_test.c b/tools/testing/selftests/powerpc/pmu/ebb/multi_ebb_procs_test.c index a05c0e18ded63..9b0f70d597020 100644 --- a/tools/testing/selftests/powerpc/pmu/ebb/multi_ebb_procs_test.c +++ b/tools/testing/selftests/powerpc/pmu/ebb/multi_ebb_procs_test.c @@ -61,8 +61,6 @@ static int cycles_child(void) ebb_global_disable(); ebb_freeze_pmcs(); - count_pmc(1, sample_period); - dump_summary_ebb_state(); event_close(&event); diff --git a/tools/testing/selftests/powerpc/pmu/ebb/pmae_handling_test.c b/tools/testing/selftests/powerpc/pmu/ebb/pmae_handling_test.c index 153ebc92234fd..2904c741e04e5 100644 --- a/tools/testing/selftests/powerpc/pmu/ebb/pmae_handling_test.c +++ b/tools/testing/selftests/powerpc/pmu/ebb/pmae_handling_test.c @@ -82,8 +82,6 @@ static int test_body(void) ebb_global_disable(); ebb_freeze_pmcs(); - count_pmc(1, sample_period); - dump_ebb_state(); if (mmcr0_mismatch) diff --git a/tools/testing/selftests/powerpc/pmu/ebb/pmc56_overflow_test.c b/tools/testing/selftests/powerpc/pmu/ebb/pmc56_overflow_test.c index eadad75ed7e6f..b29f8ba22d1e6 100644 --- a/tools/testing/selftests/powerpc/pmu/ebb/pmc56_overflow_test.c +++ b/tools/testing/selftests/powerpc/pmu/ebb/pmc56_overflow_test.c @@ -76,8 +76,6 @@ int pmc56_overflow(void) ebb_global_disable(); ebb_freeze_pmcs(); - count_pmc(2, sample_period); - dump_ebb_state(); printf("PMC5/6 overflow %d\n", pmc56_overflowed); -- GitLab From e653b41131f60054dbfa0c7431613d6aeaee2212 Mon Sep 17 00:00:00 2001 From: Martin Blumenstingl Date: Mon, 29 Jun 2020 22:39:03 +0200 Subject: [PATCH 0343/1476] clk: meson: meson8b: add the vclk_en gate clock HHI_VID_CLK_CNTL[19] is documented as CLK_EN0. This description is the same in the public S912 datasheet and the GXBB driver calls this gate "vclk". Add this gate clock to the Meson8/Meson8b/Meson8m2 clock controller because it's needed to make the video output work. Signed-off-by: Martin Blumenstingl Signed-off-by: Jerome Brunet Link: https://lore.kernel.org/r/20200629203904.2989007-2-martin.blumenstingl@googlemail.com --- drivers/clk/meson/meson8b.c | 30 +++++++++++++++++++++++++----- drivers/clk/meson/meson8b.h | 3 ++- 2 files changed, 27 insertions(+), 6 deletions(-) diff --git a/drivers/clk/meson/meson8b.c b/drivers/clk/meson/meson8b.c index 3d826711c820b..d5b90d61a530c 100644 --- a/drivers/clk/meson/meson8b.c +++ b/drivers/clk/meson/meson8b.c @@ -1204,6 +1204,22 @@ static struct clk_regmap meson8b_vclk_in_en = { }, }; +static struct clk_regmap meson8b_vclk_en = { + .data = &(struct clk_regmap_gate_data){ + .offset = HHI_VID_CLK_CNTL, + .bit_idx = 19, + }, + .hw.init = &(struct clk_init_data){ + .name = "vclk_en", + .ops = &clk_regmap_gate_ro_ops, + .parent_hws = (const struct clk_hw *[]) { + &meson8b_vclk_in_en.hw + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + }, +}; + static struct clk_regmap meson8b_vclk_div1_gate = { .data = &(struct clk_regmap_gate_data){ .offset = HHI_VID_CLK_CNTL, @@ -1213,7 +1229,7 @@ static struct clk_regmap meson8b_vclk_div1_gate = { .name = "vclk_div1_en", .ops = &clk_regmap_gate_ro_ops, .parent_hws = (const struct clk_hw *[]) { - &meson8b_vclk_in_en.hw + &meson8b_vclk_en.hw }, .num_parents = 1, .flags = CLK_SET_RATE_PARENT, @@ -1227,7 +1243,7 @@ static struct clk_fixed_factor meson8b_vclk_div2_div = { .name = "vclk_div2", .ops = &clk_fixed_factor_ops, .parent_hws = (const struct clk_hw *[]) { - &meson8b_vclk_in_en.hw + &meson8b_vclk_en.hw }, .num_parents = 1, .flags = CLK_SET_RATE_PARENT, @@ -1257,7 +1273,7 @@ static struct clk_fixed_factor meson8b_vclk_div4_div = { .name = "vclk_div4", .ops = &clk_fixed_factor_ops, .parent_hws = (const struct clk_hw *[]) { - &meson8b_vclk_in_en.hw + &meson8b_vclk_en.hw }, .num_parents = 1, .flags = CLK_SET_RATE_PARENT, @@ -1287,7 +1303,7 @@ static struct clk_fixed_factor meson8b_vclk_div6_div = { .name = "vclk_div6", .ops = &clk_fixed_factor_ops, .parent_hws = (const struct clk_hw *[]) { - &meson8b_vclk_in_en.hw + &meson8b_vclk_en.hw }, .num_parents = 1, .flags = CLK_SET_RATE_PARENT, @@ -1317,7 +1333,7 @@ static struct clk_fixed_factor meson8b_vclk_div12_div = { .name = "vclk_div12", .ops = &clk_fixed_factor_ops, .parent_hws = (const struct clk_hw *[]) { - &meson8b_vclk_in_en.hw + &meson8b_vclk_en.hw }, .num_parents = 1, .flags = CLK_SET_RATE_PARENT, @@ -2820,6 +2836,7 @@ static struct clk_hw_onecell_data meson8_hw_onecell_data = { [CLKID_VID_PLL_FINAL_DIV] = &meson8b_vid_pll_final_div.hw, [CLKID_VCLK_IN_SEL] = &meson8b_vclk_in_sel.hw, [CLKID_VCLK_IN_EN] = &meson8b_vclk_in_en.hw, + [CLKID_VCLK_EN] = &meson8b_vclk_en.hw, [CLKID_VCLK_DIV1] = &meson8b_vclk_div1_gate.hw, [CLKID_VCLK_DIV2_DIV] = &meson8b_vclk_div2_div.hw, [CLKID_VCLK_DIV2] = &meson8b_vclk_div2_div_gate.hw, @@ -3025,6 +3042,7 @@ static struct clk_hw_onecell_data meson8b_hw_onecell_data = { [CLKID_VID_PLL_FINAL_DIV] = &meson8b_vid_pll_final_div.hw, [CLKID_VCLK_IN_SEL] = &meson8b_vclk_in_sel.hw, [CLKID_VCLK_IN_EN] = &meson8b_vclk_in_en.hw, + [CLKID_VCLK_EN] = &meson8b_vclk_en.hw, [CLKID_VCLK_DIV1] = &meson8b_vclk_div1_gate.hw, [CLKID_VCLK_DIV2_DIV] = &meson8b_vclk_div2_div.hw, [CLKID_VCLK_DIV2] = &meson8b_vclk_div2_div_gate.hw, @@ -3241,6 +3259,7 @@ static struct clk_hw_onecell_data meson8m2_hw_onecell_data = { [CLKID_VID_PLL_FINAL_DIV] = &meson8b_vid_pll_final_div.hw, [CLKID_VCLK_IN_SEL] = &meson8b_vclk_in_sel.hw, [CLKID_VCLK_IN_EN] = &meson8b_vclk_in_en.hw, + [CLKID_VCLK_EN] = &meson8b_vclk_en.hw, [CLKID_VCLK_DIV1] = &meson8b_vclk_div1_gate.hw, [CLKID_VCLK_DIV2_DIV] = &meson8b_vclk_div2_div.hw, [CLKID_VCLK_DIV2] = &meson8b_vclk_div2_div_gate.hw, @@ -3443,6 +3462,7 @@ static struct clk_regmap *const meson8b_clk_regmaps[] = { &meson8b_vid_pll_final_div, &meson8b_vclk_in_sel, &meson8b_vclk_in_en, + &meson8b_vclk_en, &meson8b_vclk_div1_gate, &meson8b_vclk_div2_div_gate, &meson8b_vclk_div4_div_gate, diff --git a/drivers/clk/meson/meson8b.h b/drivers/clk/meson/meson8b.h index cd38ae2a9cb5a..c8ab2a6322955 100644 --- a/drivers/clk/meson/meson8b.h +++ b/drivers/clk/meson/meson8b.h @@ -180,8 +180,9 @@ #define CLKID_CTS_AMCLK_DIV 208 #define CLKID_CTS_MCLK_I958_SEL 210 #define CLKID_CTS_MCLK_I958_DIV 211 +#define CLKID_VCLK_EN 214 -#define CLK_NR_CLKS 214 +#define CLK_NR_CLKS 215 /* * include the CLKID and RESETID that have -- GitLab From 2568528f55356a2f20f80a18244d3e235cbd2cab Mon Sep 17 00:00:00 2001 From: Martin Blumenstingl Date: Mon, 29 Jun 2020 22:39:04 +0200 Subject: [PATCH 0344/1476] clk: meson: meson8b: add the vclk2_en gate clock HHI_VIID_CLK_CNTL[19] is not part of the public S805 datasheet. However, the GXBB driver defines this bit as a gate called "vclk2" and in the 3.10 kernel GPL code dump the following line can found: WRITE_LCD_CBUS_REG_BITS(HHI_VIID_CLK_CNTL, 0, 19, 1); //disable vclk2_en Add this gate clock to the Meson8/Meson8b/Meson8m2 clock controller to complete the VCLK2 clock tree. Signed-off-by: Martin Blumenstingl Signed-off-by: Jerome Brunet Link: https://lore.kernel.org/r/20200629203904.2989007-3-martin.blumenstingl@googlemail.com --- drivers/clk/meson/meson8b.c | 30 +++++++++++++++++++++++++----- drivers/clk/meson/meson8b.h | 3 ++- 2 files changed, 27 insertions(+), 6 deletions(-) diff --git a/drivers/clk/meson/meson8b.c b/drivers/clk/meson/meson8b.c index d5b90d61a530c..862f0756b50f0 100644 --- a/drivers/clk/meson/meson8b.c +++ b/drivers/clk/meson/meson8b.c @@ -1387,6 +1387,22 @@ static struct clk_regmap meson8b_vclk2_clk_in_en = { }, }; +static struct clk_regmap meson8b_vclk2_clk_en = { + .data = &(struct clk_regmap_gate_data){ + .offset = HHI_VIID_CLK_DIV, + .bit_idx = 19, + }, + .hw.init = &(struct clk_init_data){ + .name = "vclk2_en", + .ops = &clk_regmap_gate_ro_ops, + .parent_hws = (const struct clk_hw *[]) { + &meson8b_vclk2_clk_in_en.hw + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + }, +}; + static struct clk_regmap meson8b_vclk2_div1_gate = { .data = &(struct clk_regmap_gate_data){ .offset = HHI_VIID_CLK_DIV, @@ -1396,7 +1412,7 @@ static struct clk_regmap meson8b_vclk2_div1_gate = { .name = "vclk2_div1_en", .ops = &clk_regmap_gate_ro_ops, .parent_hws = (const struct clk_hw *[]) { - &meson8b_vclk2_clk_in_en.hw + &meson8b_vclk2_clk_en.hw }, .num_parents = 1, .flags = CLK_SET_RATE_PARENT, @@ -1410,7 +1426,7 @@ static struct clk_fixed_factor meson8b_vclk2_div2_div = { .name = "vclk2_div2", .ops = &clk_fixed_factor_ops, .parent_hws = (const struct clk_hw *[]) { - &meson8b_vclk2_clk_in_en.hw + &meson8b_vclk2_clk_en.hw }, .num_parents = 1, .flags = CLK_SET_RATE_PARENT, @@ -1440,7 +1456,7 @@ static struct clk_fixed_factor meson8b_vclk2_div4_div = { .name = "vclk2_div4", .ops = &clk_fixed_factor_ops, .parent_hws = (const struct clk_hw *[]) { - &meson8b_vclk2_clk_in_en.hw + &meson8b_vclk2_clk_en.hw }, .num_parents = 1, .flags = CLK_SET_RATE_PARENT, @@ -1470,7 +1486,7 @@ static struct clk_fixed_factor meson8b_vclk2_div6_div = { .name = "vclk2_div6", .ops = &clk_fixed_factor_ops, .parent_hws = (const struct clk_hw *[]) { - &meson8b_vclk2_clk_in_en.hw + &meson8b_vclk2_clk_en.hw }, .num_parents = 1, .flags = CLK_SET_RATE_PARENT, @@ -1500,7 +1516,7 @@ static struct clk_fixed_factor meson8b_vclk2_div12_div = { .name = "vclk2_div12", .ops = &clk_fixed_factor_ops, .parent_hws = (const struct clk_hw *[]) { - &meson8b_vclk2_clk_in_en.hw + &meson8b_vclk2_clk_en.hw }, .num_parents = 1, .flags = CLK_SET_RATE_PARENT, @@ -2848,6 +2864,7 @@ static struct clk_hw_onecell_data meson8_hw_onecell_data = { [CLKID_VCLK_DIV12] = &meson8b_vclk_div12_div_gate.hw, [CLKID_VCLK2_IN_SEL] = &meson8b_vclk2_in_sel.hw, [CLKID_VCLK2_IN_EN] = &meson8b_vclk2_clk_in_en.hw, + [CLKID_VCLK2_EN] = &meson8b_vclk2_clk_en.hw, [CLKID_VCLK2_DIV1] = &meson8b_vclk2_div1_gate.hw, [CLKID_VCLK2_DIV2_DIV] = &meson8b_vclk2_div2_div.hw, [CLKID_VCLK2_DIV2] = &meson8b_vclk2_div2_div_gate.hw, @@ -3054,6 +3071,7 @@ static struct clk_hw_onecell_data meson8b_hw_onecell_data = { [CLKID_VCLK_DIV12] = &meson8b_vclk_div12_div_gate.hw, [CLKID_VCLK2_IN_SEL] = &meson8b_vclk2_in_sel.hw, [CLKID_VCLK2_IN_EN] = &meson8b_vclk2_clk_in_en.hw, + [CLKID_VCLK2_EN] = &meson8b_vclk2_clk_en.hw, [CLKID_VCLK2_DIV1] = &meson8b_vclk2_div1_gate.hw, [CLKID_VCLK2_DIV2_DIV] = &meson8b_vclk2_div2_div.hw, [CLKID_VCLK2_DIV2] = &meson8b_vclk2_div2_div_gate.hw, @@ -3271,6 +3289,7 @@ static struct clk_hw_onecell_data meson8m2_hw_onecell_data = { [CLKID_VCLK_DIV12] = &meson8b_vclk_div12_div_gate.hw, [CLKID_VCLK2_IN_SEL] = &meson8b_vclk2_in_sel.hw, [CLKID_VCLK2_IN_EN] = &meson8b_vclk2_clk_in_en.hw, + [CLKID_VCLK2_EN] = &meson8b_vclk2_clk_en.hw, [CLKID_VCLK2_DIV1] = &meson8b_vclk2_div1_gate.hw, [CLKID_VCLK2_DIV2_DIV] = &meson8b_vclk2_div2_div.hw, [CLKID_VCLK2_DIV2] = &meson8b_vclk2_div2_div_gate.hw, @@ -3470,6 +3489,7 @@ static struct clk_regmap *const meson8b_clk_regmaps[] = { &meson8b_vclk_div12_div_gate, &meson8b_vclk2_in_sel, &meson8b_vclk2_clk_in_en, + &meson8b_vclk2_clk_en, &meson8b_vclk2_div1_gate, &meson8b_vclk2_div2_div_gate, &meson8b_vclk2_div4_div_gate, diff --git a/drivers/clk/meson/meson8b.h b/drivers/clk/meson/meson8b.h index c8ab2a6322955..699c5bc7c817b 100644 --- a/drivers/clk/meson/meson8b.h +++ b/drivers/clk/meson/meson8b.h @@ -181,8 +181,9 @@ #define CLKID_CTS_MCLK_I958_SEL 210 #define CLKID_CTS_MCLK_I958_DIV 211 #define CLKID_VCLK_EN 214 +#define CLKID_VCLK2_EN 215 -#define CLK_NR_CLKS 215 +#define CLK_NR_CLKS 216 /* * include the CLKID and RESETID that have -- GitLab From c3f2375b90d0d26f257b926a0d236371df483ca1 Mon Sep 17 00:00:00 2001 From: Waiman Long Date: Wed, 8 Jul 2020 10:21:44 -0700 Subject: [PATCH 0345/1476] xfs: Fix false positive lockdep warning with sb_internal & fs_reclaim Depending on the workloads, the following circular locking dependency warning between sb_internal (a percpu rwsem) and fs_reclaim (a pseudo lock) may show up: ====================================================== WARNING: possible circular locking dependency detected 5.0.0-rc1+ #60 Tainted: G W ------------------------------------------------------ fsfreeze/4346 is trying to acquire lock: 0000000026f1d784 (fs_reclaim){+.+.}, at: fs_reclaim_acquire.part.19+0x5/0x30 but task is already holding lock: 0000000072bfc54b (sb_internal){++++}, at: percpu_down_write+0xb4/0x650 which lock already depends on the new lock. : Possible unsafe locking scenario: CPU0 CPU1 ---- ---- lock(sb_internal); lock(fs_reclaim); lock(sb_internal); lock(fs_reclaim); *** DEADLOCK *** 4 locks held by fsfreeze/4346: #0: 00000000b478ef56 (sb_writers#8){++++}, at: percpu_down_write+0xb4/0x650 #1: 000000001ec487a9 (&type->s_umount_key#28){++++}, at: freeze_super+0xda/0x290 #2: 000000003edbd5a0 (sb_pagefaults){++++}, at: percpu_down_write+0xb4/0x650 #3: 0000000072bfc54b (sb_internal){++++}, at: percpu_down_write+0xb4/0x650 stack backtrace: Call Trace: dump_stack+0xe0/0x19a print_circular_bug.isra.10.cold.34+0x2f4/0x435 check_prev_add.constprop.19+0xca1/0x15f0 validate_chain.isra.14+0x11af/0x3b50 __lock_acquire+0x728/0x1200 lock_acquire+0x269/0x5a0 fs_reclaim_acquire.part.19+0x29/0x30 fs_reclaim_acquire+0x19/0x20 kmem_cache_alloc+0x3e/0x3f0 kmem_zone_alloc+0x79/0x150 xfs_trans_alloc+0xfa/0x9d0 xfs_sync_sb+0x86/0x170 xfs_log_sbcount+0x10f/0x140 xfs_quiesce_attr+0x134/0x270 xfs_fs_freeze+0x4a/0x70 freeze_super+0x1af/0x290 do_vfs_ioctl+0xedc/0x16c0 ksys_ioctl+0x41/0x80 __x64_sys_ioctl+0x73/0xa9 do_syscall_64+0x18f/0xd23 entry_SYSCALL_64_after_hwframe+0x49/0xbe This is a false positive as all the dirty pages are flushed out before the filesystem can be frozen. One way to avoid this splat is to add GFP_NOFS to the affected allocation calls by using the memalloc_nofs_save()/memalloc_nofs_restore() pair. This shouldn't matter unless the system is really running out of memory. In that particular case, the filesystem freeze operation may fail while it was succeeding previously. Without this patch, the command sequence below will show that the lock dependency chain sb_internal -> fs_reclaim exists. # fsfreeze -f /home # fsfreeze --unfreeze /home # grep -i fs_reclaim -C 3 /proc/lockdep_chains | grep -C 5 sb_internal After applying the patch, such sb_internal -> fs_reclaim lock dependency chain can no longer be found. Because of that, the locking dependency warning will not be shown. Suggested-by: Dave Chinner Signed-off-by: Waiman Long Reviewed-by: Darrick J. Wong Signed-off-by: Darrick J. Wong Reviewed-by: Christoph Hellwig --- fs/xfs/xfs_super.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/fs/xfs/xfs_super.c b/fs/xfs/xfs_super.c index 5a5d9453cf51d..5ef5d8416f706 100644 --- a/fs/xfs/xfs_super.c +++ b/fs/xfs/xfs_super.c @@ -910,11 +910,21 @@ xfs_fs_freeze( struct super_block *sb) { struct xfs_mount *mp = XFS_M(sb); + unsigned int flags; + int ret; + /* + * The filesystem is now frozen far enough that memory reclaim + * cannot safely operate on the filesystem. Hence we need to + * set a GFP_NOFS context here to avoid recursion deadlocks. + */ + flags = memalloc_nofs_save(); xfs_stop_block_reaping(mp); xfs_save_resvblks(mp); xfs_quiesce_attr(mp); - return xfs_sync_sb(mp, true); + ret = xfs_sync_sb(mp, true); + memalloc_nofs_restore(flags); + return ret; } STATIC int -- GitLab From 526fbaed33e8eac89634a676e5f8e3b41dfae66e Mon Sep 17 00:00:00 2001 From: Zong Li Date: Tue, 16 Jun 2020 15:45:46 +0800 Subject: [PATCH 0346/1476] riscv: Register System RAM as iomem resources Add System RAM to /proc/iomem, various tools expect it such as kdump. It is also needed for page_is_ram API which checks the specified address whether registered as System RAM in iomem_resource list. Signed-off-by: Zong Li [Palmer: check MEMBLOCK_NOMAP] Signed-off-by: Palmer Dabbelt --- arch/riscv/mm/init.c | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/arch/riscv/mm/init.c b/arch/riscv/mm/init.c index f4adb3684f3db..92002952c621d 100644 --- a/arch/riscv/mm/init.c +++ b/arch/riscv/mm/init.c @@ -517,6 +517,32 @@ void mark_rodata_ro(void) } #endif +void __init resource_init(void) +{ + struct memblock_region *region; + + for_each_memblock(memory, region) { + struct resource *res; + + res = memblock_alloc(sizeof(struct resource), SMP_CACHE_BYTES); + if (!res) + panic("%s: Failed to allocate %zu bytes\n", __func__, + sizeof(struct resource)); + + if (memblock_is_nomap(region)) { + res->name = "reserved"; + res->flags = IORESOURCE_MEM; + } else { + res->name = "System RAM"; + res->flags = IORESOURCE_SYSTEM_RAM | IORESOURCE_BUSY; + } + res->start = __pfn_to_phys(memblock_region_memory_base_pfn(region)); + res->end = __pfn_to_phys(memblock_region_memory_end_pfn(region)) - 1; + + request_resource(&iomem_resource, res); + } +} + void __init paging_init(void) { setup_vm_final(); @@ -524,6 +550,7 @@ void __init paging_init(void) sparse_init(); setup_zero_page(); zone_sizes_init(); + resource_init(); } #ifdef CONFIG_SPARSEMEM_VMEMMAP -- GitLab From c7c337c5eded50a5a1265a0e8918db66e5982f47 Mon Sep 17 00:00:00 2001 From: Liao Pingfang Date: Thu, 9 Jul 2020 15:28:28 +0800 Subject: [PATCH 0347/1476] PCI: Fix error in panic message Use kzalloc() instead of kmalloc() in the panic message. [bhelgaas: drop similar ibmphp_pci.c change since it's not obviously correct] Link: https://lore.kernel.org/r/1594279708-34369-1-git-send-email-wang.yi59@zte.com.cn Signed-off-by: Liao Pingfang Signed-off-by: Yi Wang Signed-off-by: Bjorn Helgaas --- drivers/pci/setup-bus.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/pci/setup-bus.c b/drivers/pci/setup-bus.c index 9b94b1f16d80e..4d870ed893856 100644 --- a/drivers/pci/setup-bus.c +++ b/drivers/pci/setup-bus.c @@ -152,7 +152,7 @@ static void pdev_sort_resources(struct pci_dev *dev, struct list_head *head) tmp = kzalloc(sizeof(*tmp), GFP_KERNEL); if (!tmp) - panic("pdev_sort_resources(): kmalloc() failed!\n"); + panic("%s: kzalloc() failed!\n", __func__); tmp->res = r; tmp->dev = dev; -- GitLab From 746cf3459f118592a72ef42e7551777ff17b1684 Mon Sep 17 00:00:00 2001 From: Wei Yang Date: Fri, 3 Jul 2020 10:06:09 +0800 Subject: [PATCH 0348/1476] tracing: Simplify defining of the next event id The value to be used and compared in trace_search_list() is "last + 1". Let's just define next to be "last + 1" instead of doing the addition each time. Link: https://lkml.kernel.org/r/20200703020612.12930-2-richard.weiyang@linux.alibaba.com Signed-off-by: Wei Yang Signed-off-by: Steven Rostedt (VMware) --- kernel/trace/trace_output.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/kernel/trace/trace_output.c b/kernel/trace/trace_output.c index 73976de7f8cc8..a35232d616016 100644 --- a/kernel/trace/trace_output.c +++ b/kernel/trace/trace_output.c @@ -675,11 +675,11 @@ static LIST_HEAD(ftrace_event_list); static int trace_search_list(struct list_head **list) { struct trace_event *e; - int last = __TRACE_LAST_TYPE; + int next = __TRACE_LAST_TYPE + 1; if (list_empty(&ftrace_event_list)) { *list = &ftrace_event_list; - return last + 1; + return next; } /* @@ -687,17 +687,17 @@ static int trace_search_list(struct list_head **list) * lets see if somebody freed one. */ list_for_each_entry(e, &ftrace_event_list, list) { - if (e->type != last + 1) + if (e->type != next) break; - last++; + next++; } /* Did we used up all 65 thousand events??? */ - if ((last + 1) > TRACE_EVENT_TYPE_MAX) + if (next > TRACE_EVENT_TYPE_MAX) return 0; *list = &e->list; - return last + 1; + return next; } void trace_event_read_lock(void) -- GitLab From 2575b2f3ee711f4638e772e07a5146afcc704f30 Mon Sep 17 00:00:00 2001 From: Huacai Chen Date: Wed, 8 Jul 2020 15:59:30 +0800 Subject: [PATCH 0349/1476] PCI: Move PCI_VENDOR_ID_REDHAT definition to pci_ids.h Instead of duplicating the PCI_VENDOR_ID_REDHAT definition everywhere, move it to include/linux/pci_ids.h. [bhelgaas: also update MDPY_PCI_VENDOR_ID] Link: https://lore.kernel.org/r/1594195170-11119-1-git-send-email-chenhc@lemote.com Signed-off-by: Huacai Chen Signed-off-by: Bjorn Helgaas Acked-by: Gerd Hoffmann --- drivers/gpu/drm/qxl/qxl_dev.h | 2 -- drivers/net/ethernet/rocker/rocker_hw.h | 1 - include/linux/pci_ids.h | 2 ++ samples/vfio-mdev/mdpy-defs.h | 2 +- 4 files changed, 3 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/qxl/qxl_dev.h b/drivers/gpu/drm/qxl/qxl_dev.h index a0ee41632d7ef..a7bc31f6d565f 100644 --- a/drivers/gpu/drm/qxl/qxl_dev.h +++ b/drivers/gpu/drm/qxl/qxl_dev.h @@ -131,8 +131,6 @@ enum SpiceCursorType { #pragma pack(push, 1) -#define REDHAT_PCI_VENDOR_ID 0x1b36 - /* 0x100-0x11f reserved for spice, 0x1ff used for unstable work */ #define QXL_DEVICE_ID_STABLE 0x0100 diff --git a/drivers/net/ethernet/rocker/rocker_hw.h b/drivers/net/ethernet/rocker/rocker_hw.h index 59f1f8b690d2c..62fd84cf34353 100644 --- a/drivers/net/ethernet/rocker/rocker_hw.h +++ b/drivers/net/ethernet/rocker/rocker_hw.h @@ -25,7 +25,6 @@ enum { #define ROCKER_FP_PORTS_MAX 62 -#define PCI_VENDOR_ID_REDHAT 0x1b36 #define PCI_DEVICE_ID_REDHAT_ROCKER 0x0006 #define ROCKER_PCI_BAR0_SIZE 0x2000 diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h index 0ad57693f3926..5c709a1450b11 100644 --- a/include/linux/pci_ids.h +++ b/include/linux/pci_ids.h @@ -2585,6 +2585,8 @@ #define PCI_VENDOR_ID_ASMEDIA 0x1b21 +#define PCI_VENDOR_ID_REDHAT 0x1b36 + #define PCI_VENDOR_ID_AMAZON_ANNAPURNA_LABS 0x1c36 #define PCI_VENDOR_ID_CIRCUITCO 0x1cc8 diff --git a/samples/vfio-mdev/mdpy-defs.h b/samples/vfio-mdev/mdpy-defs.h index eb26421b6429d..961c55ec3ffde 100644 --- a/samples/vfio-mdev/mdpy-defs.h +++ b/samples/vfio-mdev/mdpy-defs.h @@ -9,7 +9,7 @@ */ /* pci ids */ -#define MDPY_PCI_VENDOR_ID 0x1b36 /* redhat */ +#define MDPY_PCI_VENDOR_ID PCI_VENDOR_ID_REDHAT #define MDPY_PCI_DEVICE_ID 0x000f #define MDPY_PCI_SUBVENDOR_ID PCI_SUBVENDOR_ID_REDHAT_QUMRANET #define MDPY_PCI_SUBDEVICE_ID PCI_SUBDEVICE_ID_QEMU -- GitLab From 36b8aacf2a483ba40fbe91c830d314e0bc133044 Mon Sep 17 00:00:00 2001 From: Wei Yang Date: Fri, 3 Jul 2020 10:06:10 +0800 Subject: [PATCH 0350/1476] tracing: Save one trace_event->type by using __TRACE_LAST_TYPE Static defined trace_event->type stops at (__TRACE_LAST_TYPE - 1) and dynamic trace_event->type starts from (__TRACE_LAST_TYPE + 1). To save one trace_event->type index, let's use __TRACE_LAST_TYPE. Link: https://lkml.kernel.org/r/20200703020612.12930-3-richard.weiyang@linux.alibaba.com Signed-off-by: Wei Yang Signed-off-by: Steven Rostedt (VMware) --- kernel/trace/trace_output.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/kernel/trace/trace_output.c b/kernel/trace/trace_output.c index a35232d616016..4d1893564912d 100644 --- a/kernel/trace/trace_output.c +++ b/kernel/trace/trace_output.c @@ -20,7 +20,7 @@ DECLARE_RWSEM(trace_event_sem); static struct hlist_head event_hash[EVENT_HASHSIZE] __read_mostly; -static int next_event_type = __TRACE_LAST_TYPE + 1; +static int next_event_type = __TRACE_LAST_TYPE; enum print_line_t trace_print_bputs_msg_only(struct trace_iterator *iter) { @@ -675,7 +675,7 @@ static LIST_HEAD(ftrace_event_list); static int trace_search_list(struct list_head **list) { struct trace_event *e; - int next = __TRACE_LAST_TYPE + 1; + int next = __TRACE_LAST_TYPE; if (list_empty(&ftrace_event_list)) { *list = &ftrace_event_list; -- GitLab From e63434f4cc0da0f3ffe60d932997842f60552517 Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Fri, 22 May 2020 17:48:20 -0600 Subject: [PATCH 0351/1476] PCI: host-common: Use struct pci_host_bridge.windows list directly There's no need to create a temporary resource list and then splice it to struct pci_host_bridge.windows list. Just use pci_host_bridge.windows directly. The necessary clean-up is already handled by the PCI core. Link: https://lore.kernel.org/r/20200522234832.954484-4-robh@kernel.org Signed-off-by: Rob Herring Signed-off-by: Lorenzo Pieralisi Cc: Will Deacon --- drivers/pci/controller/pci-host-common.c | 42 ++++++++---------------- 1 file changed, 14 insertions(+), 28 deletions(-) diff --git a/drivers/pci/controller/pci-host-common.c b/drivers/pci/controller/pci-host-common.c index 953de57f6c570..b76e55f495e47 100644 --- a/drivers/pci/controller/pci-host-common.c +++ b/drivers/pci/controller/pci-host-common.c @@ -21,7 +21,7 @@ static void gen_pci_unmap_cfg(void *ptr) } static struct pci_config_window *gen_pci_init(struct device *dev, - struct list_head *resources, const struct pci_ecam_ops *ops) + struct pci_host_bridge *bridge, const struct pci_ecam_ops *ops) { int err; struct resource cfgres; @@ -29,31 +29,25 @@ static struct pci_config_window *gen_pci_init(struct device *dev, struct pci_config_window *cfg; /* Parse our PCI ranges and request their resources */ - err = pci_parse_request_of_pci_ranges(dev, resources, NULL, &bus_range); + err = pci_parse_request_of_pci_ranges(dev, &bridge->windows, NULL, &bus_range); if (err) return ERR_PTR(err); err = of_address_to_resource(dev->of_node, 0, &cfgres); if (err) { dev_err(dev, "missing \"reg\" property\n"); - goto err_out; + return ERR_PTR(err); } cfg = pci_ecam_create(dev, &cfgres, bus_range, ops); - if (IS_ERR(cfg)) { - err = PTR_ERR(cfg); - goto err_out; - } + if (IS_ERR(cfg)) + return cfg; err = devm_add_action_or_reset(dev, gen_pci_unmap_cfg, cfg); - if (err) { - goto err_out; - } - return cfg; + if (err) + return ERR_PTR(err); -err_out: - pci_free_resource_list(resources); - return ERR_PTR(err); + return cfg; } int pci_host_common_probe(struct platform_device *pdev) @@ -61,9 +55,7 @@ int pci_host_common_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; struct pci_host_bridge *bridge; struct pci_config_window *cfg; - struct list_head resources; const struct pci_ecam_ops *ops; - int ret; ops = of_device_get_match_data(&pdev->dev); if (!ops) @@ -76,7 +68,7 @@ int pci_host_common_probe(struct platform_device *pdev) of_pci_check_probe_only(); /* Parse and map our Configuration Space windows */ - cfg = gen_pci_init(dev, &resources, ops); + cfg = gen_pci_init(dev, bridge, ops); if (IS_ERR(cfg)) return PTR_ERR(cfg); @@ -84,7 +76,6 @@ int pci_host_common_probe(struct platform_device *pdev) if (!pci_has_flag(PCI_PROBE_ONLY)) pci_add_flags(PCI_REASSIGN_ALL_BUS); - list_splice_init(&resources, &bridge->windows); bridge->dev.parent = dev; bridge->sysdata = cfg; bridge->busnr = cfg->busr.start; @@ -92,24 +83,19 @@ int pci_host_common_probe(struct platform_device *pdev) bridge->map_irq = of_irq_parse_and_map_pci; bridge->swizzle_irq = pci_common_swizzle; - ret = pci_host_probe(bridge); - if (ret < 0) { - pci_free_resource_list(&resources); - return ret; - } + platform_set_drvdata(pdev, bridge); - platform_set_drvdata(pdev, bridge->bus); - return 0; + return pci_host_probe(bridge); } EXPORT_SYMBOL_GPL(pci_host_common_probe); int pci_host_common_remove(struct platform_device *pdev) { - struct pci_bus *bus = platform_get_drvdata(pdev); + struct pci_host_bridge *bridge = platform_get_drvdata(pdev); pci_lock_rescan_remove(); - pci_stop_root_bus(bus); - pci_remove_root_bus(bus); + pci_stop_root_bus(bridge->bus); + pci_remove_root_bus(bridge->bus); pci_unlock_rescan_remove(); return 0; -- GitLab From a37571fa9c04a9efdd8f597cb29323a8fcea405a Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Fri, 22 May 2020 17:48:21 -0600 Subject: [PATCH 0352/1476] PCI: brcmstb: Use pci_host_probe() to register host The brcmstb host driver does the same host registration and bus scanning calls as pci_host_probe, so let's use it instead. Link: https://lore.kernel.org/r/20200522234832.954484-5-robh@kernel.org Tested-by: Nicolas Saenz Julienne Signed-off-by: Rob Herring Signed-off-by: Lorenzo Pieralisi Acked-by: Nicolas Saenz Julienne Cc: Nicolas Saenz Julienne Cc: bcm-kernel-feedback-list@broadcom.com Cc: linux-rpi-kernel@lists.infradead.org --- drivers/pci/controller/pcie-brcmstb.c | 20 ++++---------------- 1 file changed, 4 insertions(+), 16 deletions(-) diff --git a/drivers/pci/controller/pcie-brcmstb.c b/drivers/pci/controller/pcie-brcmstb.c index 7730ea845ff29..15c747c1390a2 100644 --- a/drivers/pci/controller/pcie-brcmstb.c +++ b/drivers/pci/controller/pcie-brcmstb.c @@ -172,7 +172,6 @@ struct brcm_pcie { struct device *dev; void __iomem *base; struct clk *clk; - struct pci_bus *root_bus; struct device_node *np; bool ssc; int gen; @@ -919,9 +918,10 @@ static void __brcm_pcie_remove(struct brcm_pcie *pcie) static int brcm_pcie_remove(struct platform_device *pdev) { struct brcm_pcie *pcie = platform_get_drvdata(pdev); + struct pci_host_bridge *bridge = pci_host_bridge_from_priv(pcie); - pci_stop_root_bus(pcie->root_bus); - pci_remove_root_bus(pcie->root_bus); + pci_stop_root_bus(bridge->bus); + pci_remove_root_bus(bridge->bus); __brcm_pcie_remove(pcie); return 0; @@ -933,7 +933,6 @@ static int brcm_pcie_probe(struct platform_device *pdev) struct pci_host_bridge *bridge; struct device_node *fw_np; struct brcm_pcie *pcie; - struct pci_bus *child; struct resource *res; int ret; @@ -1004,20 +1003,9 @@ static int brcm_pcie_probe(struct platform_device *pdev) bridge->map_irq = of_irq_parse_and_map_pci; bridge->swizzle_irq = pci_common_swizzle; - ret = pci_scan_root_bus_bridge(bridge); - if (ret < 0) { - dev_err(pcie->dev, "Scanning root bridge failed\n"); - goto fail; - } - - pci_assign_unassigned_bus_resources(bridge->bus); - list_for_each_entry(child, &bridge->bus->children, node) - pcie_bus_configure_settings(child); - pci_bus_add_devices(bridge->bus); platform_set_drvdata(pdev, pcie); - pcie->root_bus = bridge->bus; - return 0; + return pci_host_probe(bridge); fail: __brcm_pcie_remove(pcie); return ret; -- GitLab From 1f9b751220ce53d4ea7edf7dda90f495ce810494 Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Fri, 22 May 2020 17:48:22 -0600 Subject: [PATCH 0353/1476] PCI: mobiveil: Use pci_host_probe() to register host The mobiveil host driver does the same host registration and bus scanning calls as pci_host_probe, so let's use it instead. Link: https://lore.kernel.org/r/20200522234832.954484-6-robh@kernel.org Signed-off-by: Rob Herring Signed-off-by: Lorenzo Pieralisi Cc: Karthikeyan Mitran Cc: Hou Zhiqiang --- .../pci/controller/mobiveil/pcie-mobiveil-host.c | 16 +--------------- 1 file changed, 1 insertion(+), 15 deletions(-) diff --git a/drivers/pci/controller/mobiveil/pcie-mobiveil-host.c b/drivers/pci/controller/mobiveil/pcie-mobiveil-host.c index 5907baa9b1f21..5974619811ec2 100644 --- a/drivers/pci/controller/mobiveil/pcie-mobiveil-host.c +++ b/drivers/pci/controller/mobiveil/pcie-mobiveil-host.c @@ -569,8 +569,6 @@ int mobiveil_pcie_host_probe(struct mobiveil_pcie *pcie) struct mobiveil_root_port *rp = &pcie->rp; struct pci_host_bridge *bridge = rp->bridge; struct device *dev = &pcie->pdev->dev; - struct pci_bus *bus; - struct pci_bus *child; int ret; ret = mobiveil_pcie_parse_dt(pcie); @@ -620,17 +618,5 @@ int mobiveil_pcie_host_probe(struct mobiveil_pcie *pcie) return ret; } - /* setup the kernel resources for the newly added PCIe root bus */ - ret = pci_scan_root_bus_bridge(bridge); - if (ret) - return ret; - - bus = bridge->bus; - - pci_assign_unassigned_bus_resources(bus); - list_for_each_entry(child, &bus->children, node) - pcie_bus_configure_settings(child); - pci_bus_add_devices(bus); - - return 0; + return pci_host_probe(bridge); } -- GitLab From 67047e8bc8aa4b4d57c5e2bd00d48ac7c8b8757b Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Fri, 22 May 2020 17:48:23 -0600 Subject: [PATCH 0354/1476] PCI: tegra: Use pci_host_probe() to register host The tegra host driver does the same host registration and bus scanning calls as pci_host_probe, so let's use it instead. Link: https://lore.kernel.org/r/20200522234832.954484-7-robh@kernel.org Signed-off-by: Rob Herring Signed-off-by: Lorenzo Pieralisi Cc: Thierry Reding Cc: Jonathan Hunter Cc: linux-tegra@vger.kernel.org --- drivers/pci/controller/pci-tegra.c | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/drivers/pci/controller/pci-tegra.c b/drivers/pci/controller/pci-tegra.c index 235b456698fc7..82fb36ed6f52d 100644 --- a/drivers/pci/controller/pci-tegra.c +++ b/drivers/pci/controller/pci-tegra.c @@ -2670,7 +2670,6 @@ static int tegra_pcie_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; struct pci_host_bridge *host; struct tegra_pcie *pcie; - struct pci_bus *child; struct resource *bus; int err; @@ -2721,20 +2720,12 @@ static int tegra_pcie_probe(struct platform_device *pdev) host->map_irq = tegra_pcie_map_irq; host->swizzle_irq = pci_common_swizzle; - err = pci_scan_root_bus_bridge(host); + err = pci_host_probe(host); if (err < 0) { dev_err(dev, "failed to register host: %d\n", err); goto pm_runtime_put; } - pci_bus_size_bridges(host->bus); - pci_bus_assign_resources(host->bus); - - list_for_each_entry(child, &host->bus->children, node) - pcie_bus_configure_settings(child); - - pci_bus_add_devices(host->bus); - if (IS_ENABLED(CONFIG_DEBUG_FS)) { err = tegra_pcie_debugfs_init(pcie); if (err < 0) -- GitLab From d7169a94f1ba8c14db465fe3e410a4feceea35a5 Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Fri, 22 May 2020 17:48:24 -0600 Subject: [PATCH 0355/1476] PCI: v3: Use pci_host_probe() to register host The v3 host driver does the same host registration and bus scanning calls as pci_host_probe, so let's use it instead. Link: https://lore.kernel.org/r/20200522234832.954484-8-robh@kernel.org Signed-off-by: Rob Herring Signed-off-by: Lorenzo Pieralisi Reviewed-by: Linus Walleij Cc: Linus Walleij --- drivers/pci/controller/pci-v3-semi.c | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/drivers/pci/controller/pci-v3-semi.c b/drivers/pci/controller/pci-v3-semi.c index 3681e5af38785..198cf2c6ed924 100644 --- a/drivers/pci/controller/pci-v3-semi.c +++ b/drivers/pci/controller/pci-v3-semi.c @@ -239,7 +239,6 @@ struct v3_pci { struct device *dev; void __iomem *base; void __iomem *config_base; - struct pci_bus *bus; u32 config_mem; u32 non_pre_mem; u32 pre_mem; @@ -904,17 +903,7 @@ static int v3_pci_probe(struct platform_device *pdev) val |= V3_SYSTEM_M_LOCK; writew(val, v3->base + V3_SYSTEM); - ret = pci_scan_root_bus_bridge(host); - if (ret) { - dev_err(dev, "failed to register host: %d\n", ret); - return ret; - } - v3->bus = host->bus; - - pci_bus_assign_resources(v3->bus); - pci_bus_add_devices(v3->bus); - - return 0; + return pci_host_probe(host); } static const struct of_device_id v3_pci_of_match[] = { -- GitLab From a4b21b858b56d728021ad72ad7c1322d8772b2a6 Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Fri, 22 May 2020 17:48:25 -0600 Subject: [PATCH 0356/1476] PCI: versatile: Use pci_host_probe() to register host The versatile host driver does the same host registration and bus scanning calls as pci_host_probe, so let's use it instead. Link: https://lore.kernel.org/r/20200522234832.954484-9-robh@kernel.org Signed-off-by: Rob Herring Signed-off-by: Lorenzo Pieralisi Cc: Lorenzo Pieralisi --- drivers/pci/controller/pci-versatile.c | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/drivers/pci/controller/pci-versatile.c b/drivers/pci/controller/pci-versatile.c index b911359b6d812..e90f0cc65c734 100644 --- a/drivers/pci/controller/pci-versatile.c +++ b/drivers/pci/controller/pci-versatile.c @@ -70,7 +70,6 @@ static int versatile_pci_probe(struct platform_device *pdev) int ret, i, myslot = -1, mem = 1; u32 val; void __iomem *local_pci_cfg_base; - struct pci_bus *bus, *child; struct pci_host_bridge *bridge; bridge = devm_pci_alloc_host_bridge(dev, 0); @@ -164,18 +163,7 @@ static int versatile_pci_probe(struct platform_device *pdev) bridge->map_irq = of_irq_parse_and_map_pci; bridge->swizzle_irq = pci_common_swizzle; - ret = pci_scan_root_bus_bridge(bridge); - if (ret < 0) - return ret; - - bus = bridge->bus; - - pci_assign_unassigned_bus_resources(bus); - list_for_each_entry(child, &bus->children, node) - pcie_bus_configure_settings(child); - pci_bus_add_devices(bus); - - return 0; + return pci_host_probe(bridge); } static const struct of_device_id versatile_pci_of_match[] = { -- GitLab From 97c5372aeb5b43ab9d3f3db08652de8e8988b226 Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Fri, 22 May 2020 17:48:26 -0600 Subject: [PATCH 0357/1476] PCI: xgene: Use pci_host_probe() to register host The xgene host driver does the same host registration and bus scanning calls as pci_host_probe, so let's use it instead. Link: https://lore.kernel.org/r/20200522234832.954484-10-robh@kernel.org Signed-off-by: Rob Herring Signed-off-by: Lorenzo Pieralisi Cc: Toan Le --- drivers/pci/controller/pci-xgene.c | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/drivers/pci/controller/pci-xgene.c b/drivers/pci/controller/pci-xgene.c index d1efa8ffbae17..5aee802946cb4 100644 --- a/drivers/pci/controller/pci-xgene.c +++ b/drivers/pci/controller/pci-xgene.c @@ -591,7 +591,6 @@ static int xgene_pcie_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; struct device_node *dn = dev->of_node; struct xgene_pcie_port *port; - struct pci_bus *bus, *child; struct pci_host_bridge *bridge; int ret; @@ -632,17 +631,7 @@ static int xgene_pcie_probe(struct platform_device *pdev) bridge->map_irq = of_irq_parse_and_map_pci; bridge->swizzle_irq = pci_common_swizzle; - ret = pci_scan_root_bus_bridge(bridge); - if (ret < 0) - return ret; - - bus = bridge->bus; - - pci_assign_unassigned_bus_resources(bus); - list_for_each_entry(child, &bus->children, node) - pcie_bus_configure_settings(child); - pci_bus_add_devices(bus); - return 0; + return pci_host_probe(bridge); } static const struct of_device_id xgene_pcie_match_table[] = { -- GitLab From c63aed7334c21de8d626ff028ccad98cf5847a0e Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Fri, 22 May 2020 17:48:27 -0600 Subject: [PATCH 0358/1476] PCI: altera: Use pci_host_probe() to register host The altera host driver does the same host registration and bus scanning calls as pci_host_probe, so let's use it instead. The only difference is pci_assign_unassigned_bus_resources() was called instead of pci_bus_size_bridges() and pci_bus_assign_resources(). This should be the same. Link: https://lore.kernel.org/r/20200522234832.954484-11-robh@kernel.org Signed-off-by: Rob Herring Signed-off-by: Lorenzo Pieralisi Reviewed-by: Ley Foon Tan Cc: Ley Foon Tan Cc: rfi@lists.rocketboards.org --- drivers/pci/controller/pcie-altera.c | 17 +---------------- 1 file changed, 1 insertion(+), 16 deletions(-) diff --git a/drivers/pci/controller/pcie-altera.c b/drivers/pci/controller/pcie-altera.c index 24cb1c3310584..26ac3ad81de00 100644 --- a/drivers/pci/controller/pcie-altera.c +++ b/drivers/pci/controller/pcie-altera.c @@ -773,8 +773,6 @@ static int altera_pcie_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct altera_pcie *pcie; - struct pci_bus *bus; - struct pci_bus *child; struct pci_host_bridge *bridge; int ret; const struct of_device_id *match; @@ -825,20 +823,7 @@ static int altera_pcie_probe(struct platform_device *pdev) bridge->map_irq = of_irq_parse_and_map_pci; bridge->swizzle_irq = pci_common_swizzle; - ret = pci_scan_root_bus_bridge(bridge); - if (ret < 0) - return ret; - - bus = bridge->bus; - - pci_assign_unassigned_bus_resources(bus); - - /* Configure PCI Express setting. */ - list_for_each_entry(child, &bus->children, node) - pcie_bus_configure_settings(child); - - pci_bus_add_devices(bus); - return ret; + return pci_host_probe(bridge); } static int altera_pcie_remove(struct platform_device *pdev) -- GitLab From 81ce3cf4a246d1578ed7ae6b69e4331fd2e6587f Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Fri, 22 May 2020 17:48:28 -0600 Subject: [PATCH 0359/1476] PCI: iproc: Use pci_host_probe() to register host The iproc host driver does the same host registration and bus scanning calls as pci_host_probe, so let's use it instead. The only difference is pci_assign_unassigned_bus_resources() was called instead of pci_bus_size_bridges() and pci_bus_assign_resources(). This should be the same. Link: https://lore.kernel.org/r/20200522234832.954484-12-robh@kernel.org Signed-off-by: Rob Herring Signed-off-by: Lorenzo Pieralisi Acked-by: Scott Branden Cc: Lorenzo Pieralisi Cc: Ray Jui Cc: Scott Branden Cc: bcm-kernel-feedback-list@broadcom.com --- drivers/pci/controller/pcie-iproc.c | 18 +++++------------- drivers/pci/controller/pcie-iproc.h | 2 -- 2 files changed, 5 insertions(+), 15 deletions(-) diff --git a/drivers/pci/controller/pcie-iproc.c b/drivers/pci/controller/pcie-iproc.c index 8c7f875acf7fa..232fca0754e15 100644 --- a/drivers/pci/controller/pcie-iproc.c +++ b/drivers/pci/controller/pcie-iproc.c @@ -1470,7 +1470,6 @@ int iproc_pcie_setup(struct iproc_pcie *pcie, struct list_head *res) { struct device *dev; int ret; - struct pci_bus *child; struct pci_host_bridge *host = pci_host_bridge_from_priv(pcie); dev = pcie->dev; @@ -1531,21 +1530,12 @@ int iproc_pcie_setup(struct iproc_pcie *pcie, struct list_head *res) host->map_irq = pcie->map_irq; host->swizzle_irq = pci_common_swizzle; - ret = pci_scan_root_bus_bridge(host); + ret = pci_host_probe(host); if (ret < 0) { dev_err(dev, "failed to scan host: %d\n", ret); goto err_power_off_phy; } - pci_assign_unassigned_bus_resources(host->bus); - - pcie->root_bus = host->bus; - - list_for_each_entry(child, &host->bus->children, node) - pcie_bus_configure_settings(child); - - pci_bus_add_devices(host->bus); - return 0; err_power_off_phy: @@ -1558,8 +1548,10 @@ EXPORT_SYMBOL(iproc_pcie_setup); int iproc_pcie_remove(struct iproc_pcie *pcie) { - pci_stop_root_bus(pcie->root_bus); - pci_remove_root_bus(pcie->root_bus); + struct pci_host_bridge *host = pci_host_bridge_from_priv(pcie); + + pci_stop_root_bus(host->bus); + pci_remove_root_bus(host->bus); iproc_pcie_msi_disable(pcie); diff --git a/drivers/pci/controller/pcie-iproc.h b/drivers/pci/controller/pcie-iproc.h index 4f03ea5398056..c2676e442f556 100644 --- a/drivers/pci/controller/pcie-iproc.h +++ b/drivers/pci/controller/pcie-iproc.h @@ -54,7 +54,6 @@ struct iproc_msi; * @reg_offsets: register offsets * @base: PCIe host controller I/O register base * @base_addr: PCIe host controller register base physical address - * @root_bus: pointer to root bus * @phy: optional PHY device that controls the Serdes * @map_irq: function callback to map interrupts * @ep_is_internal: indicates an internal emulated endpoint device is connected @@ -85,7 +84,6 @@ struct iproc_pcie { void __iomem *base; phys_addr_t base_addr; struct resource mem; - struct pci_bus *root_bus; struct phy *phy; int (*map_irq)(const struct pci_dev *, u8, u8); bool ep_is_internal; -- GitLab From 56d2923484704a261f91f5fd0d4dd35f5b3ebb19 Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Fri, 22 May 2020 17:48:29 -0600 Subject: [PATCH 0360/1476] PCI: rcar: Use pci_host_probe() to register host The rcar host driver does the same host registration and bus scanning calls as pci_host_probe, so let's use it instead. Link: https://lore.kernel.org/r/20200522234832.954484-13-robh@kernel.org Signed-off-by: Rob Herring Signed-off-by: Lorenzo Pieralisi Reviewed-by: Geert Uytterhoeven Cc: Marek Vasut Cc: Yoshihiro Shimoda Cc: linux-renesas-soc@vger.kernel.org --- drivers/pci/controller/pcie-rcar-host.c | 18 +----------------- 1 file changed, 1 insertion(+), 17 deletions(-) diff --git a/drivers/pci/controller/pcie-rcar-host.c b/drivers/pci/controller/pcie-rcar-host.c index d210a36561be2..9069ad96fe95c 100644 --- a/drivers/pci/controller/pcie-rcar-host.c +++ b/drivers/pci/controller/pcie-rcar-host.c @@ -330,8 +330,6 @@ static int rcar_pcie_enable(struct rcar_pcie_host *host) struct pci_host_bridge *bridge = pci_host_bridge_from_priv(host); struct rcar_pcie *pcie = &host->pcie; struct device *dev = pcie->dev; - struct pci_bus *bus, *child; - int ret; /* Try setting 5 GT/s link speed */ rcar_pcie_force_speedup(pcie); @@ -349,21 +347,7 @@ static int rcar_pcie_enable(struct rcar_pcie_host *host) if (IS_ENABLED(CONFIG_PCI_MSI)) bridge->msi = &host->msi.chip; - ret = pci_scan_root_bus_bridge(bridge); - if (ret < 0) - return ret; - - bus = bridge->bus; - - pci_bus_size_bridges(bus); - pci_bus_assign_resources(bus); - - list_for_each_entry(child, &bus->children, node) - pcie_bus_configure_settings(child); - - pci_bus_add_devices(bus); - - return 0; + return pci_host_probe(bridge); } static int phy_wait_for_ack(struct rcar_pcie *pcie) -- GitLab From f473182c7524ddc04b5fc0aeb246ac102cf6edb5 Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Fri, 22 May 2020 17:48:30 -0600 Subject: [PATCH 0361/1476] PCI: rockchip: Use pci_host_probe() to register host The rockchip host driver does the same host registration and bus scanning calls as pci_host_probe, so let's use it instead. Link: https://lore.kernel.org/r/20200522234832.954484-14-robh@kernel.org Signed-off-by: Rob Herring Signed-off-by: Lorenzo Pieralisi Cc: Shawn Lin Cc: Heiko Stuebner Cc: linux-rockchip@lists.infradead.org --- drivers/pci/controller/pcie-rockchip-host.c | 18 ++++-------------- drivers/pci/controller/pcie-rockchip.h | 1 - 2 files changed, 4 insertions(+), 15 deletions(-) diff --git a/drivers/pci/controller/pcie-rockchip-host.c b/drivers/pci/controller/pcie-rockchip-host.c index 94af6f5828a3d..6a3c8442258b9 100644 --- a/drivers/pci/controller/pcie-rockchip-host.c +++ b/drivers/pci/controller/pcie-rockchip-host.c @@ -949,7 +949,6 @@ static int rockchip_pcie_probe(struct platform_device *pdev) { struct rockchip_pcie *rockchip; struct device *dev = &pdev->dev; - struct pci_bus *bus, *child; struct pci_host_bridge *bridge; struct resource *bus_res; int err; @@ -1015,20 +1014,10 @@ static int rockchip_pcie_probe(struct platform_device *pdev) bridge->map_irq = of_irq_parse_and_map_pci; bridge->swizzle_irq = pci_common_swizzle; - err = pci_scan_root_bus_bridge(bridge); + err = pci_host_probe(bridge); if (err < 0) goto err_remove_irq_domain; - bus = bridge->bus; - - rockchip->root_bus = bus; - - pci_bus_size_bridges(bus); - pci_bus_assign_resources(bus); - list_for_each_entry(child, &bus->children, node) - pcie_bus_configure_settings(child); - - pci_bus_add_devices(bus); return 0; err_remove_irq_domain: @@ -1051,9 +1040,10 @@ static int rockchip_pcie_remove(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct rockchip_pcie *rockchip = dev_get_drvdata(dev); + struct pci_host_bridge *bridge = pci_host_bridge_from_priv(rockchip); - pci_stop_root_bus(rockchip->root_bus); - pci_remove_root_bus(rockchip->root_bus); + pci_stop_root_bus(bridge->bus); + pci_remove_root_bus(bridge->bus); irq_domain_remove(rockchip->irq_domain); rockchip_pcie_deinit_phys(rockchip); diff --git a/drivers/pci/controller/pcie-rockchip.h b/drivers/pci/controller/pcie-rockchip.h index d90dfb3545732..4012543bafbee 100644 --- a/drivers/pci/controller/pcie-rockchip.h +++ b/drivers/pci/controller/pcie-rockchip.h @@ -303,7 +303,6 @@ struct rockchip_pcie { struct device *dev; struct irq_domain *irq_domain; int offset; - struct pci_bus *root_bus; void __iomem *msg_region; phys_addr_t msg_bus_addr; bool is_rc; -- GitLab From dd58950f9875ab33b5c2499a1add592800fe703d Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Fri, 22 May 2020 17:48:31 -0600 Subject: [PATCH 0362/1476] PCI: xilinx-nwl: Use pci_host_probe() to register host The xilinx-nwl host driver does the same host registration and bus scanning calls as pci_host_probe, so let's use it instead. The only difference is pci_assign_unassigned_bus_resources() was called instead of pci_bus_size_bridges() and pci_bus_assign_resources(). This should be the same. Link: https://lore.kernel.org/r/20200522234832.954484-15-robh@kernel.org Signed-off-by: Rob Herring Signed-off-by: Lorenzo Pieralisi Cc: Michal Simek --- drivers/pci/controller/pcie-xilinx-nwl.c | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/drivers/pci/controller/pcie-xilinx-nwl.c b/drivers/pci/controller/pcie-xilinx-nwl.c index 9bd1427f2fd6d..32a0b08d6da5f 100644 --- a/drivers/pci/controller/pcie-xilinx-nwl.c +++ b/drivers/pci/controller/pcie-xilinx-nwl.c @@ -817,8 +817,6 @@ static int nwl_pcie_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct nwl_pcie *pcie; - struct pci_bus *bus; - struct pci_bus *child; struct pci_host_bridge *bridge; int err; @@ -871,17 +869,7 @@ static int nwl_pcie_probe(struct platform_device *pdev) } } - err = pci_scan_root_bus_bridge(bridge); - if (err) - return err; - - bus = bridge->bus; - - pci_assign_unassigned_bus_resources(bus); - list_for_each_entry(child, &bus->children, node) - pcie_bus_configure_settings(child); - pci_bus_add_devices(bus); - return 0; + return pci_host_probe(bridge); } static struct platform_driver nwl_pcie_driver = { -- GitLab From 78aada4b05bdc63687b76a079eca327d3825add1 Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Fri, 22 May 2020 17:48:32 -0600 Subject: [PATCH 0363/1476] PCI: xilinx: Use pci_host_probe() to register host The xilinx host driver does the same host registration and bus scanning calls as pci_host_probe, so let's use it instead. The only difference is pci_assign_unassigned_bus_resources() was called instead of pci_bus_size_bridges() and pci_bus_assign_resources(). This should be the same. Link: https://lore.kernel.org/r/20200522234832.954484-16-robh@kernel.org Signed-off-by: Rob Herring Signed-off-by: Lorenzo Pieralisi Cc: Michal Simek --- drivers/pci/controller/pcie-xilinx.c | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/drivers/pci/controller/pcie-xilinx.c b/drivers/pci/controller/pcie-xilinx.c index 98e55297815b8..05547497f391d 100644 --- a/drivers/pci/controller/pcie-xilinx.c +++ b/drivers/pci/controller/pcie-xilinx.c @@ -616,7 +616,6 @@ static int xilinx_pcie_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct xilinx_pcie_port *port; - struct pci_bus *bus, *child; struct pci_host_bridge *bridge; int err; @@ -663,17 +662,7 @@ static int xilinx_pcie_probe(struct platform_device *pdev) xilinx_pcie_msi_chip.dev = dev; bridge->msi = &xilinx_pcie_msi_chip; #endif - err = pci_scan_root_bus_bridge(bridge); - if (err < 0) - return err; - - bus = bridge->bus; - - pci_assign_unassigned_bus_resources(bus); - list_for_each_entry(child, &bus->children, node) - pcie_bus_configure_settings(child); - pci_bus_add_devices(bus); - return 0; + return pci_host_probe(bridge); } static const struct of_device_id xilinx_pcie_of_match[] = { -- GitLab From e2dcd20b1645a7285b304bf6b9a1a6637f0f254a Mon Sep 17 00:00:00 2001 From: Dejin Zheng Date: Wed, 3 Jun 2020 01:16:01 +0800 Subject: [PATCH 0364/1476] PCI: controller: Convert to devm_platform_ioremap_resource_byname() Use devm_platform_ioremap_resource_byname() to simplify the code, since it calls respectively platform_get_resource_byname() and devm_ioremap_resource(). Link: https://lore.kernel.org/r/20200602171601.17630-1-zhengdejin5@gmail.com Signed-off-by: Dejin Zheng Signed-off-by: Lorenzo Pieralisi Reviewed-by: Rob Herring Reviewed-by: Gustavo Pimentel --- drivers/pci/controller/cadence/pcie-cadence-ep.c | 3 +-- drivers/pci/controller/cadence/pcie-cadence-host.c | 3 +-- drivers/pci/controller/pci-tegra.c | 8 +++----- drivers/pci/controller/pci-xgene.c | 3 +-- drivers/pci/controller/pcie-altera-msi.c | 3 +-- drivers/pci/controller/pcie-altera.c | 9 +++------ drivers/pci/controller/pcie-mediatek.c | 4 +--- drivers/pci/controller/pcie-rockchip.c | 5 ++--- 8 files changed, 13 insertions(+), 25 deletions(-) diff --git a/drivers/pci/controller/cadence/pcie-cadence-ep.c b/drivers/pci/controller/cadence/pcie-cadence-ep.c index 1c15c8352125e..74ffa03fde5f5 100644 --- a/drivers/pci/controller/cadence/pcie-cadence-ep.c +++ b/drivers/pci/controller/cadence/pcie-cadence-ep.c @@ -408,8 +408,7 @@ int cdns_pcie_ep_setup(struct cdns_pcie_ep *ep) pcie->is_rc = false; - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "reg"); - pcie->reg_base = devm_ioremap_resource(dev, res); + pcie->reg_base = devm_platform_ioremap_resource_byname(pdev, "reg"); if (IS_ERR(pcie->reg_base)) { dev_err(dev, "missing \"reg\"\n"); return PTR_ERR(pcie->reg_base); diff --git a/drivers/pci/controller/cadence/pcie-cadence-host.c b/drivers/pci/controller/cadence/pcie-cadence-host.c index 9f77e47983c3e..59b9b3df46404 100644 --- a/drivers/pci/controller/cadence/pcie-cadence-host.c +++ b/drivers/pci/controller/cadence/pcie-cadence-host.c @@ -217,8 +217,7 @@ int cdns_pcie_host_setup(struct cdns_pcie_rc *rc) rc->device_id = 0xffff; of_property_read_u32(np, "device-id", &rc->device_id); - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "reg"); - pcie->reg_base = devm_ioremap_resource(dev, res); + pcie->reg_base = devm_platform_ioremap_resource_byname(pdev, "reg"); if (IS_ERR(pcie->reg_base)) { dev_err(dev, "missing \"reg\"\n"); return PTR_ERR(pcie->reg_base); diff --git a/drivers/pci/controller/pci-tegra.c b/drivers/pci/controller/pci-tegra.c index 82fb36ed6f52d..71bf4b2684ac2 100644 --- a/drivers/pci/controller/pci-tegra.c +++ b/drivers/pci/controller/pci-tegra.c @@ -1462,7 +1462,7 @@ static int tegra_pcie_get_resources(struct tegra_pcie *pcie) { struct device *dev = pcie->dev; struct platform_device *pdev = to_platform_device(dev); - struct resource *pads, *afi, *res; + struct resource *res; const struct tegra_pcie_soc *soc = pcie->soc; int err; @@ -1486,15 +1486,13 @@ static int tegra_pcie_get_resources(struct tegra_pcie *pcie) } } - pads = platform_get_resource_byname(pdev, IORESOURCE_MEM, "pads"); - pcie->pads = devm_ioremap_resource(dev, pads); + pcie->pads = devm_platform_ioremap_resource_byname(pdev, "pads"); if (IS_ERR(pcie->pads)) { err = PTR_ERR(pcie->pads); goto phys_put; } - afi = platform_get_resource_byname(pdev, IORESOURCE_MEM, "afi"); - pcie->afi = devm_ioremap_resource(dev, afi); + pcie->afi = devm_platform_ioremap_resource_byname(pdev, "afi"); if (IS_ERR(pcie->afi)) { err = PTR_ERR(pcie->afi); goto phys_put; diff --git a/drivers/pci/controller/pci-xgene.c b/drivers/pci/controller/pci-xgene.c index 5aee802946cb4..4c783cfddb3a6 100644 --- a/drivers/pci/controller/pci-xgene.c +++ b/drivers/pci/controller/pci-xgene.c @@ -355,8 +355,7 @@ static int xgene_pcie_map_reg(struct xgene_pcie_port *port, if (IS_ERR(port->csr_base)) return PTR_ERR(port->csr_base); - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "cfg"); - port->cfg_base = devm_ioremap_resource(dev, res); + port->cfg_base = devm_platform_ioremap_resource_byname(pdev, "cfg"); if (IS_ERR(port->cfg_base)) return PTR_ERR(port->cfg_base); port->cfg_addr = res->start; diff --git a/drivers/pci/controller/pcie-altera-msi.c b/drivers/pci/controller/pcie-altera-msi.c index 16d938920ca57..613e19af71bd2 100644 --- a/drivers/pci/controller/pcie-altera-msi.c +++ b/drivers/pci/controller/pcie-altera-msi.c @@ -228,8 +228,7 @@ static int altera_msi_probe(struct platform_device *pdev) mutex_init(&msi->lock); msi->pdev = pdev; - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "csr"); - msi->csr_base = devm_ioremap_resource(&pdev->dev, res); + msi->csr_base = devm_platform_ioremap_resource_byname(pdev, "csr"); if (IS_ERR(msi->csr_base)) { dev_err(&pdev->dev, "failed to map csr memory\n"); return PTR_ERR(msi->csr_base); diff --git a/drivers/pci/controller/pcie-altera.c b/drivers/pci/controller/pcie-altera.c index 26ac3ad81de00..4ffe382f220f3 100644 --- a/drivers/pci/controller/pcie-altera.c +++ b/drivers/pci/controller/pcie-altera.c @@ -696,17 +696,14 @@ static int altera_pcie_parse_dt(struct altera_pcie *pcie) { struct device *dev = &pcie->pdev->dev; struct platform_device *pdev = pcie->pdev; - struct resource *cra; - struct resource *hip; - cra = platform_get_resource_byname(pdev, IORESOURCE_MEM, "Cra"); - pcie->cra_base = devm_ioremap_resource(dev, cra); + pcie->cra_base = devm_platform_ioremap_resource_byname(pdev, "Cra"); if (IS_ERR(pcie->cra_base)) return PTR_ERR(pcie->cra_base); if (pcie->pcie_data->version == ALTERA_PCIE_V2) { - hip = platform_get_resource_byname(pdev, IORESOURCE_MEM, "Hip"); - pcie->hip_base = devm_ioremap_resource(&pdev->dev, hip); + pcie->hip_base = + devm_platform_ioremap_resource_byname(pdev, "Hip"); if (IS_ERR(pcie->hip_base)) return PTR_ERR(pcie->hip_base); } diff --git a/drivers/pci/controller/pcie-mediatek.c b/drivers/pci/controller/pcie-mediatek.c index ebfa7d5a4e2db..d8e38276dbe34 100644 --- a/drivers/pci/controller/pcie-mediatek.c +++ b/drivers/pci/controller/pcie-mediatek.c @@ -905,7 +905,6 @@ static int mtk_pcie_parse_port(struct mtk_pcie *pcie, int slot) { struct mtk_pcie_port *port; - struct resource *regs; struct device *dev = pcie->dev; struct platform_device *pdev = to_platform_device(dev); char name[10]; @@ -916,8 +915,7 @@ static int mtk_pcie_parse_port(struct mtk_pcie *pcie, return -ENOMEM; snprintf(name, sizeof(name), "port%d", slot); - regs = platform_get_resource_byname(pdev, IORESOURCE_MEM, name); - port->base = devm_ioremap_resource(dev, regs); + port->base = devm_platform_ioremap_resource_byname(pdev, name); if (IS_ERR(port->base)) { dev_err(dev, "failed to map port%d base\n", slot); return PTR_ERR(port->base); diff --git a/drivers/pci/controller/pcie-rockchip.c b/drivers/pci/controller/pcie-rockchip.c index c53d1322a3d6c..904dec0d3a88f 100644 --- a/drivers/pci/controller/pcie-rockchip.c +++ b/drivers/pci/controller/pcie-rockchip.c @@ -45,9 +45,8 @@ int rockchip_pcie_parse_dt(struct rockchip_pcie *rockchip) return -EINVAL; } - regs = platform_get_resource_byname(pdev, IORESOURCE_MEM, - "apb-base"); - rockchip->apb_base = devm_ioremap_resource(dev, regs); + rockchip->apb_base = + devm_platform_ioremap_resource_byname(pdev, "apb-base"); if (IS_ERR(rockchip->apb_base)) return PTR_ERR(rockchip->apb_base); -- GitLab From 3cf0eead9fb895dd91fc1fea0114ca0068d1c1a3 Mon Sep 17 00:00:00 2001 From: Dejin Zheng Date: Wed, 8 Jul 2020 23:56:14 +0800 Subject: [PATCH 0365/1476] PCI: controller: Convert to devm_platform_ioremap_resource() Use devm_platform_ioremap_resource() to simplify the code, since t contains platform_get_resource() and devm_ioremap_resource() calls respectively. Link: https://lore.kernel.org/r/20200708155614.308-1-zhengdejin5@gmail.com Signed-off-by: Dejin Zheng Signed-off-by: Lorenzo Pieralisi Reviewed-by: Rob Herring --- drivers/pci/controller/dwc/pci-exynos.c | 4 +--- drivers/pci/controller/pci-aardvark.c | 5 ++--- drivers/pci/controller/pci-ftpci100.c | 4 +--- drivers/pci/controller/pci-versatile.c | 6 ++---- drivers/pci/controller/pcie-brcmstb.c | 4 +--- 5 files changed, 7 insertions(+), 16 deletions(-) diff --git a/drivers/pci/controller/dwc/pci-exynos.c b/drivers/pci/controller/dwc/pci-exynos.c index c5043d951e809..5791039d6a542 100644 --- a/drivers/pci/controller/dwc/pci-exynos.c +++ b/drivers/pci/controller/dwc/pci-exynos.c @@ -84,14 +84,12 @@ static int exynos5440_pcie_get_mem_resources(struct platform_device *pdev, { struct dw_pcie *pci = ep->pci; struct device *dev = pci->dev; - struct resource *res; ep->mem_res = devm_kzalloc(dev, sizeof(*ep->mem_res), GFP_KERNEL); if (!ep->mem_res) return -ENOMEM; - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - ep->mem_res->elbi_base = devm_ioremap_resource(dev, res); + ep->mem_res->elbi_base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(ep->mem_res->elbi_base)) return PTR_ERR(ep->mem_res->elbi_base); diff --git a/drivers/pci/controller/pci-aardvark.c b/drivers/pci/controller/pci-aardvark.c index 90ff291c24f09..0d98f9b04daac 100644 --- a/drivers/pci/controller/pci-aardvark.c +++ b/drivers/pci/controller/pci-aardvark.c @@ -1105,7 +1105,7 @@ static int advk_pcie_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct advk_pcie *pcie; - struct resource *res, *bus; + struct resource *bus; struct pci_host_bridge *bridge; int ret, irq; @@ -1116,8 +1116,7 @@ static int advk_pcie_probe(struct platform_device *pdev) pcie = pci_host_bridge_priv(bridge); pcie->pdev = pdev; - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - pcie->base = devm_ioremap_resource(dev, res); + pcie->base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(pcie->base)) return PTR_ERR(pcie->base); diff --git a/drivers/pci/controller/pci-ftpci100.c b/drivers/pci/controller/pci-ftpci100.c index 1b67564de7af5..221dfc9dc81b8 100644 --- a/drivers/pci/controller/pci-ftpci100.c +++ b/drivers/pci/controller/pci-ftpci100.c @@ -422,7 +422,6 @@ static int faraday_pci_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; const struct faraday_pci_variant *variant = of_device_get_match_data(dev); - struct resource *regs; struct resource_entry *win; struct faraday_pci *p; struct resource *io; @@ -465,8 +464,7 @@ static int faraday_pci_probe(struct platform_device *pdev) return ret; } - regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); - p->base = devm_ioremap_resource(dev, regs); + p->base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(p->base)) return PTR_ERR(p->base); diff --git a/drivers/pci/controller/pci-versatile.c b/drivers/pci/controller/pci-versatile.c index e90f0cc65c734..a8d361f6c5d9b 100644 --- a/drivers/pci/controller/pci-versatile.c +++ b/drivers/pci/controller/pci-versatile.c @@ -76,13 +76,11 @@ static int versatile_pci_probe(struct platform_device *pdev) if (!bridge) return -ENOMEM; - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - versatile_pci_base = devm_ioremap_resource(dev, res); + versatile_pci_base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(versatile_pci_base)) return PTR_ERR(versatile_pci_base); - res = platform_get_resource(pdev, IORESOURCE_MEM, 1); - versatile_cfg_base[0] = devm_ioremap_resource(dev, res); + versatile_cfg_base[0] = devm_platform_ioremap_resource(pdev, 1); if (IS_ERR(versatile_cfg_base[0])) return PTR_ERR(versatile_cfg_base[0]); diff --git a/drivers/pci/controller/pcie-brcmstb.c b/drivers/pci/controller/pcie-brcmstb.c index 15c747c1390a2..91a4b7f3ee45f 100644 --- a/drivers/pci/controller/pcie-brcmstb.c +++ b/drivers/pci/controller/pcie-brcmstb.c @@ -933,7 +933,6 @@ static int brcm_pcie_probe(struct platform_device *pdev) struct pci_host_bridge *bridge; struct device_node *fw_np; struct brcm_pcie *pcie; - struct resource *res; int ret; /* @@ -958,8 +957,7 @@ static int brcm_pcie_probe(struct platform_device *pdev) pcie->dev = &pdev->dev; pcie->np = np; - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - pcie->base = devm_ioremap_resource(&pdev->dev, res); + pcie->base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(pcie->base)) return PTR_ERR(pcie->base); -- GitLab From cbe420361f92a31694fb3773a6247fda9aba1db3 Mon Sep 17 00:00:00 2001 From: Rajat Jain Date: Tue, 7 Jul 2020 15:46:01 -0700 Subject: [PATCH 0366/1476] PCI: Reorder pci_enable_acs() and dependencies Move pci_enable_acs() and dependencies further up in the source code to avoid having to forward declare it when we make it static in near future. No functional changes intended. Link: https://lore.kernel.org/r/20200707224604.3737893-1-rajatja@google.com Signed-off-by: Rajat Jain Signed-off-by: Bjorn Helgaas --- drivers/pci/pci.c | 254 +++++++++++++++++++++++----------------------- 1 file changed, 127 insertions(+), 127 deletions(-) diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index ce096272f52b1..eec625f0e594e 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -777,6 +777,133 @@ int pci_wait_for_pending(struct pci_dev *dev, int pos, u16 mask) return 0; } +static int pci_acs_enable; + +/** + * pci_request_acs - ask for ACS to be enabled if supported + */ +void pci_request_acs(void) +{ + pci_acs_enable = 1; +} + +static const char *disable_acs_redir_param; + +/** + * pci_disable_acs_redir - disable ACS redirect capabilities + * @dev: the PCI device + * + * For only devices specified in the disable_acs_redir parameter. + */ +static void pci_disable_acs_redir(struct pci_dev *dev) +{ + int ret = 0; + const char *p; + int pos; + u16 ctrl; + + if (!disable_acs_redir_param) + return; + + p = disable_acs_redir_param; + while (*p) { + ret = pci_dev_str_match(dev, p, &p); + if (ret < 0) { + pr_info_once("PCI: Can't parse disable_acs_redir parameter: %s\n", + disable_acs_redir_param); + + break; + } else if (ret == 1) { + /* Found a match */ + break; + } + + if (*p != ';' && *p != ',') { + /* End of param or invalid format */ + break; + } + p++; + } + + if (ret != 1) + return; + + if (!pci_dev_specific_disable_acs_redir(dev)) + return; + + pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ACS); + if (!pos) { + pci_warn(dev, "cannot disable ACS redirect for this hardware as it does not have ACS capabilities\n"); + return; + } + + pci_read_config_word(dev, pos + PCI_ACS_CTRL, &ctrl); + + /* P2P Request & Completion Redirect */ + ctrl &= ~(PCI_ACS_RR | PCI_ACS_CR | PCI_ACS_EC); + + pci_write_config_word(dev, pos + PCI_ACS_CTRL, ctrl); + + pci_info(dev, "disabled ACS redirect\n"); +} + +/** + * pci_std_enable_acs - enable ACS on devices using standard ACS capabilities + * @dev: the PCI device + */ +static void pci_std_enable_acs(struct pci_dev *dev) +{ + int pos; + u16 cap; + u16 ctrl; + + pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ACS); + if (!pos) + return; + + pci_read_config_word(dev, pos + PCI_ACS_CAP, &cap); + pci_read_config_word(dev, pos + PCI_ACS_CTRL, &ctrl); + + /* Source Validation */ + ctrl |= (cap & PCI_ACS_SV); + + /* P2P Request Redirect */ + ctrl |= (cap & PCI_ACS_RR); + + /* P2P Completion Redirect */ + ctrl |= (cap & PCI_ACS_CR); + + /* Upstream Forwarding */ + ctrl |= (cap & PCI_ACS_UF); + + pci_write_config_word(dev, pos + PCI_ACS_CTRL, ctrl); +} + +/** + * pci_enable_acs - enable ACS if hardware support it + * @dev: the PCI device + */ +void pci_enable_acs(struct pci_dev *dev) +{ + if (!pci_acs_enable) + goto disable_acs_redir; + + if (!pci_dev_specific_enable_acs(dev)) + goto disable_acs_redir; + + pci_std_enable_acs(dev); + +disable_acs_redir: + /* + * Note: pci_disable_acs_redir() must be called even if ACS was not + * enabled by the kernel because it may have been enabled by + * platform firmware. So if we are told to disable it, we should + * always disable it after setting the kernel's default + * preferences. + */ + pci_disable_acs_redir(dev); +} + /** * pci_restore_bars - restore a device's BAR values (e.g. after wake-up) * @dev: PCI device to have its BARs restored @@ -3230,133 +3357,6 @@ void pci_configure_ari(struct pci_dev *dev) } } -static int pci_acs_enable; - -/** - * pci_request_acs - ask for ACS to be enabled if supported - */ -void pci_request_acs(void) -{ - pci_acs_enable = 1; -} - -static const char *disable_acs_redir_param; - -/** - * pci_disable_acs_redir - disable ACS redirect capabilities - * @dev: the PCI device - * - * For only devices specified in the disable_acs_redir parameter. - */ -static void pci_disable_acs_redir(struct pci_dev *dev) -{ - int ret = 0; - const char *p; - int pos; - u16 ctrl; - - if (!disable_acs_redir_param) - return; - - p = disable_acs_redir_param; - while (*p) { - ret = pci_dev_str_match(dev, p, &p); - if (ret < 0) { - pr_info_once("PCI: Can't parse disable_acs_redir parameter: %s\n", - disable_acs_redir_param); - - break; - } else if (ret == 1) { - /* Found a match */ - break; - } - - if (*p != ';' && *p != ',') { - /* End of param or invalid format */ - break; - } - p++; - } - - if (ret != 1) - return; - - if (!pci_dev_specific_disable_acs_redir(dev)) - return; - - pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ACS); - if (!pos) { - pci_warn(dev, "cannot disable ACS redirect for this hardware as it does not have ACS capabilities\n"); - return; - } - - pci_read_config_word(dev, pos + PCI_ACS_CTRL, &ctrl); - - /* P2P Request & Completion Redirect */ - ctrl &= ~(PCI_ACS_RR | PCI_ACS_CR | PCI_ACS_EC); - - pci_write_config_word(dev, pos + PCI_ACS_CTRL, ctrl); - - pci_info(dev, "disabled ACS redirect\n"); -} - -/** - * pci_std_enable_acs - enable ACS on devices using standard ACS capabilities - * @dev: the PCI device - */ -static void pci_std_enable_acs(struct pci_dev *dev) -{ - int pos; - u16 cap; - u16 ctrl; - - pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ACS); - if (!pos) - return; - - pci_read_config_word(dev, pos + PCI_ACS_CAP, &cap); - pci_read_config_word(dev, pos + PCI_ACS_CTRL, &ctrl); - - /* Source Validation */ - ctrl |= (cap & PCI_ACS_SV); - - /* P2P Request Redirect */ - ctrl |= (cap & PCI_ACS_RR); - - /* P2P Completion Redirect */ - ctrl |= (cap & PCI_ACS_CR); - - /* Upstream Forwarding */ - ctrl |= (cap & PCI_ACS_UF); - - pci_write_config_word(dev, pos + PCI_ACS_CTRL, ctrl); -} - -/** - * pci_enable_acs - enable ACS if hardware support it - * @dev: the PCI device - */ -void pci_enable_acs(struct pci_dev *dev) -{ - if (!pci_acs_enable) - goto disable_acs_redir; - - if (!pci_dev_specific_enable_acs(dev)) - goto disable_acs_redir; - - pci_std_enable_acs(dev); - -disable_acs_redir: - /* - * Note: pci_disable_acs_redir() must be called even if ACS was not - * enabled by the kernel because it may have been enabled by - * platform firmware. So if we are told to disable it, we should - * always disable it after setting the kernel's default - * preferences. - */ - pci_disable_acs_redir(dev); -} - static bool pci_acs_flags_enabled(struct pci_dev *pdev, u16 acs_flags) { int pos; -- GitLab From 52fbf5bdeeef415b28b8e6cdade1e48927927f60 Mon Sep 17 00:00:00 2001 From: Rajat Jain Date: Tue, 7 Jul 2020 15:46:02 -0700 Subject: [PATCH 0367/1476] PCI: Cache ACS capability offset in device Currently the ACS capability is being looked up at a number of places. Read and store it once at enumeration so that it can be used by all later. No functional change intended. Link: https://lore.kernel.org/r/20200707224604.3737893-2-rajatja@google.com Signed-off-by: Rajat Jain Signed-off-by: Bjorn Helgaas --- drivers/pci/p2pdma.c | 2 +- drivers/pci/pci.c | 20 ++++++++++++++++---- drivers/pci/pci.h | 2 +- drivers/pci/probe.c | 2 +- drivers/pci/quirks.c | 8 ++++---- include/linux/pci.h | 1 + 6 files changed, 24 insertions(+), 11 deletions(-) diff --git a/drivers/pci/p2pdma.c b/drivers/pci/p2pdma.c index e8e444eeb1cd2..f29a48f8fa594 100644 --- a/drivers/pci/p2pdma.c +++ b/drivers/pci/p2pdma.c @@ -253,7 +253,7 @@ static int pci_bridge_has_acs_redir(struct pci_dev *pdev) int pos; u16 ctrl; - pos = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_ACS); + pos = pdev->acs_cap; if (!pos) return 0; diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index eec625f0e594e..73a8627822140 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -831,7 +831,7 @@ static void pci_disable_acs_redir(struct pci_dev *dev) if (!pci_dev_specific_disable_acs_redir(dev)) return; - pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ACS); + pos = dev->acs_cap; if (!pos) { pci_warn(dev, "cannot disable ACS redirect for this hardware as it does not have ACS capabilities\n"); return; @@ -857,7 +857,7 @@ static void pci_std_enable_acs(struct pci_dev *dev) u16 cap; u16 ctrl; - pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ACS); + pos = dev->acs_cap; if (!pos) return; @@ -883,7 +883,7 @@ static void pci_std_enable_acs(struct pci_dev *dev) * pci_enable_acs - enable ACS if hardware support it * @dev: the PCI device */ -void pci_enable_acs(struct pci_dev *dev) +static void pci_enable_acs(struct pci_dev *dev) { if (!pci_acs_enable) goto disable_acs_redir; @@ -3362,7 +3362,7 @@ static bool pci_acs_flags_enabled(struct pci_dev *pdev, u16 acs_flags) int pos; u16 cap, ctrl; - pos = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_ACS); + pos = pdev->acs_cap; if (!pos) return false; @@ -3487,6 +3487,18 @@ bool pci_acs_path_enabled(struct pci_dev *start, return true; } +/** + * pci_acs_init - Initialize ACS if hardware supports it + * @dev: the PCI device + */ +void pci_acs_init(struct pci_dev *dev) +{ + dev->acs_cap = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ACS); + + if (dev->acs_cap) + pci_enable_acs(dev); +} + /** * pci_rebar_find_pos - find position of resize ctrl reg for BAR * @pdev: PCI device diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index 6d3f758671064..12fb79fbe29d3 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h @@ -532,7 +532,7 @@ static inline resource_size_t pci_resource_alignment(struct pci_dev *dev, return resource_alignment(res); } -void pci_enable_acs(struct pci_dev *dev); +void pci_acs_init(struct pci_dev *dev); #ifdef CONFIG_PCI_QUIRKS int pci_dev_specific_acs_enabled(struct pci_dev *dev, u16 acs_flags); int pci_dev_specific_enable_acs(struct pci_dev *dev); diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index 2f66988cea257..6d87066a5ecc5 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -2390,7 +2390,7 @@ static void pci_init_capabilities(struct pci_dev *dev) pci_ats_init(dev); /* Address Translation Services */ pci_pri_init(dev); /* Page Request Interface */ pci_pasid_init(dev); /* Process Address Space ID */ - pci_enable_acs(dev); /* Enable ACS P2P upstream forwarding */ + pci_acs_init(dev); /* Access Control Services */ pci_ptm_init(dev); /* Precision Time Measurement */ pci_aer_init(dev); /* Advanced Error Reporting */ pci_dpc_init(dev); /* Downstream Port Containment */ diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c index 812bfc32ecb82..b341628e47527 100644 --- a/drivers/pci/quirks.c +++ b/drivers/pci/quirks.c @@ -4653,7 +4653,7 @@ static int pci_quirk_intel_spt_pch_acs(struct pci_dev *dev, u16 acs_flags) if (!pci_quirk_intel_spt_pch_acs_match(dev)) return -ENOTTY; - pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ACS); + pos = dev->acs_cap; if (!pos) return -ENOTTY; @@ -4961,7 +4961,7 @@ static int pci_quirk_enable_intel_spt_pch_acs(struct pci_dev *dev) if (!pci_quirk_intel_spt_pch_acs_match(dev)) return -ENOTTY; - pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ACS); + pos = dev->acs_cap; if (!pos) return -ENOTTY; @@ -4988,7 +4988,7 @@ static int pci_quirk_disable_intel_spt_pch_acs_redir(struct pci_dev *dev) if (!pci_quirk_intel_spt_pch_acs_match(dev)) return -ENOTTY; - pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ACS); + pos = dev->acs_cap; if (!pos) return -ENOTTY; @@ -5355,7 +5355,7 @@ int pci_idt_bus_quirk(struct pci_bus *bus, int devfn, u32 *l, int timeout) bool found; struct pci_dev *bridge = bus->self; - pos = pci_find_ext_capability(bridge, PCI_EXT_CAP_ID_ACS); + pos = bridge->acs_cap; /* Disable ACS SV before initial config reads */ if (pos) { diff --git a/include/linux/pci.h b/include/linux/pci.h index c79d83304e529..a26be5332bba6 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -486,6 +486,7 @@ struct pci_dev { #ifdef CONFIG_PCI_P2PDMA struct pci_p2pdma *p2pdma; #endif + u16 acs_cap; /* ACS Capability offset */ phys_addr_t rom; /* Physical address if not from BAR */ size_t romlen; /* Length if not from BAR */ char *driver_override; /* Driver name to force a match */ -- GitLab From 99b50be9d8ec9ef319cc7d5de07f4d405fac7764 Mon Sep 17 00:00:00 2001 From: Rajat Jain Date: Tue, 7 Jul 2020 15:46:03 -0700 Subject: [PATCH 0368/1476] PCI: Treat "external-facing" devices themselves as internal "External-facing" devices are internal devices that expose PCIe hierarchies such as Thunderbolt outside the platform [1]. Previously these internal devices were marked as "untrusted" the same as devices downstream from them. Use the ACPI or DT information to identify external-facing devices, but only mark the devices *downstream* from them as "untrusted" [2]. The external-facing device itself is no longer marked as untrusted. [1] https://docs.microsoft.com/en-us/windows-hardware/drivers/pci/dsd-for-pcie-root-ports#identifying-externally-exposed-pcie-root-ports [2] https://lore.kernel.org/linux-pci/20200610230906.GA1528594@bjorn-Precision-5520/ Link: https://lore.kernel.org/r/20200707224604.3737893-3-rajatja@google.com Signed-off-by: Rajat Jain Signed-off-by: Bjorn Helgaas --- drivers/iommu/intel/iommu.c | 6 +++--- drivers/pci/of.c | 2 +- drivers/pci/pci-acpi.c | 9 ++++----- drivers/pci/probe.c | 2 +- include/linux/pci.h | 6 ++++++ 5 files changed, 15 insertions(+), 10 deletions(-) diff --git a/drivers/iommu/intel/iommu.c b/drivers/iommu/intel/iommu.c index 9129663a7406b..d23ce26b88335 100644 --- a/drivers/iommu/intel/iommu.c +++ b/drivers/iommu/intel/iommu.c @@ -4730,12 +4730,12 @@ const struct attribute_group *intel_iommu_groups[] = { NULL, }; -static inline bool has_untrusted_dev(void) +static inline bool has_external_pci(void) { struct pci_dev *pdev = NULL; for_each_pci_dev(pdev) - if (pdev->untrusted) + if (pdev->external_facing) return true; return false; @@ -4743,7 +4743,7 @@ static inline bool has_untrusted_dev(void) static int __init platform_optin_force_iommu(void) { - if (!dmar_platform_optin() || no_platform_optin || !has_untrusted_dev()) + if (!dmar_platform_optin() || no_platform_optin || !has_external_pci()) return 0; if (no_iommu || dmar_disabled) diff --git a/drivers/pci/of.c b/drivers/pci/of.c index 27839cd2459f6..22727fc9558df 100644 --- a/drivers/pci/of.c +++ b/drivers/pci/of.c @@ -42,7 +42,7 @@ void pci_set_bus_of_node(struct pci_bus *bus) } else { node = of_node_get(bus->self->dev.of_node); if (node && of_property_read_bool(node, "external-facing")) - bus->self->untrusted = true; + bus->self->external_facing = true; } bus->dev.of_node = node; diff --git a/drivers/pci/pci-acpi.c b/drivers/pci/pci-acpi.c index 7224b1e5f2a83..54520d34e27e4 100644 --- a/drivers/pci/pci-acpi.c +++ b/drivers/pci/pci-acpi.c @@ -1213,7 +1213,7 @@ static void pci_acpi_optimize_delay(struct pci_dev *pdev, ACPI_FREE(obj); } -static void pci_acpi_set_untrusted(struct pci_dev *dev) +static void pci_acpi_set_external_facing(struct pci_dev *dev) { u8 val; @@ -1224,11 +1224,10 @@ static void pci_acpi_set_untrusted(struct pci_dev *dev) /* * These root ports expose PCIe (including DMA) outside of the - * system so make sure we treat them and everything behind as - * untrusted. + * system. Everything downstream from them is external. */ if (val) - dev->untrusted = 1; + dev->external_facing = 1; } static void pci_acpi_setup(struct device *dev) @@ -1240,7 +1239,7 @@ static void pci_acpi_setup(struct device *dev) return; pci_acpi_optimize_delay(pci_dev, adev->handle); - pci_acpi_set_untrusted(pci_dev); + pci_acpi_set_external_facing(pci_dev); pci_acpi_add_edr_notifier(pci_dev); pci_acpi_add_pm_notifier(adev, pci_dev); diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index 6d87066a5ecc5..8c40c00413e74 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -1552,7 +1552,7 @@ static void set_pcie_untrusted(struct pci_dev *dev) * untrusted as well. */ parent = pci_upstream_bridge(dev); - if (parent && parent->untrusted) + if (parent && (parent->untrusted || parent->external_facing)) dev->untrusted = true; } diff --git a/include/linux/pci.h b/include/linux/pci.h index a26be5332bba6..7a40cd5caed03 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -432,6 +432,12 @@ struct pci_dev { * mappings to make sure they cannot access arbitrary memory. */ unsigned int untrusted:1; + /* + * Info from the platform, e.g., ACPI or device tree, may mark a + * device as "external-facing". An external-facing device is + * itself internal but devices downstream from it are external. + */ + unsigned int external_facing:1; unsigned int broken_intx_masking:1; /* INTx masking can't be used */ unsigned int io_window_1k:1; /* Intel bridge 1K I/O windows */ unsigned int irq_managed:1; -- GitLab From 8a94644b440eef5a7b9c104ac8aa7a7f413e35e5 Mon Sep 17 00:00:00 2001 From: Qiushi Wu Date: Wed, 27 May 2020 21:13:22 -0500 Subject: [PATCH 0369/1476] PCI: Fix pci_create_slot() reference count leak kobject_init_and_add() takes a reference even when it fails. If it returns an error, kobject_put() must be called to clean up the memory associated with the object. When kobject_init_and_add() fails, call kobject_put() instead of kfree(). b8eb718348b8 ("net-sysfs: Fix reference count leak in rx|netdev_queue_add_kobject") fixed a similar problem. Link: https://lore.kernel.org/r/20200528021322.1984-1-wu000273@umn.edu Signed-off-by: Qiushi Wu Signed-off-by: Bjorn Helgaas --- drivers/pci/slot.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/pci/slot.c b/drivers/pci/slot.c index cc386ef2fa122..3861505741e6d 100644 --- a/drivers/pci/slot.c +++ b/drivers/pci/slot.c @@ -268,13 +268,16 @@ placeholder: slot_name = make_slot_name(name); if (!slot_name) { err = -ENOMEM; + kfree(slot); goto err; } err = kobject_init_and_add(&slot->kobj, &pci_slot_ktype, NULL, "%s", slot_name); - if (err) + if (err) { + kobject_put(&slot->kobj); goto err; + } INIT_LIST_HEAD(&slot->list); list_add(&slot->list, &parent->slots); @@ -293,7 +296,6 @@ out: mutex_unlock(&pci_slot_mutex); return slot; err: - kfree(slot); slot = ERR_PTR(err); goto out; } -- GitLab From 6d0efeb14bbe2350a94ba07b403a686d731c5179 Mon Sep 17 00:00:00 2001 From: Ilia Lin Date: Fri, 3 Jul 2020 10:49:41 +0200 Subject: [PATCH 0370/1476] soc: qcom: Separate kryo l2 accessors from PMU driver The driver provides kernel level API for other drivers to access the MSM8996 L2 cache registers. Separating the L2 access code from the PMU driver and making it public to allow other drivers use it. The accesses must be separated with a single spinlock, maintained in this driver. Signed-off-by: Ilia Lin Signed-off-by: Loic Poulain Link: https://lore.kernel.org/r/1593766185-16346-2-git-send-email-loic.poulain@linaro.org Acked-by: Will Deacon Signed-off-by: Stephen Boyd --- drivers/perf/Kconfig | 1 + drivers/perf/qcom_l2_pmu.c | 90 ++++++++-------------------- drivers/soc/qcom/Kconfig | 4 ++ drivers/soc/qcom/Makefile | 1 + drivers/soc/qcom/kryo-l2-accessors.c | 57 ++++++++++++++++++ include/soc/qcom/kryo-l2-accessors.h | 12 ++++ 6 files changed, 99 insertions(+), 66 deletions(-) create mode 100644 drivers/soc/qcom/kryo-l2-accessors.c create mode 100644 include/soc/qcom/kryo-l2-accessors.h diff --git a/drivers/perf/Kconfig b/drivers/perf/Kconfig index a9261cf48293b..7305d57d18904 100644 --- a/drivers/perf/Kconfig +++ b/drivers/perf/Kconfig @@ -82,6 +82,7 @@ config FSL_IMX8_DDR_PMU config QCOM_L2_PMU bool "Qualcomm Technologies L2-cache PMU" depends on ARCH_QCOM && ARM64 && ACPI + select QCOM_KRYO_L2_ACCESSORS help Provides support for the L2 cache performance monitor unit (PMU) in Qualcomm Technologies processors. diff --git a/drivers/perf/qcom_l2_pmu.c b/drivers/perf/qcom_l2_pmu.c index 21d6991dbe0ba..02ca1fadbedd8 100644 --- a/drivers/perf/qcom_l2_pmu.c +++ b/drivers/perf/qcom_l2_pmu.c @@ -23,6 +23,7 @@ #include #include #include +#include #define MAX_L2_CTRS 9 @@ -79,8 +80,6 @@ #define L2_COUNTER_RELOAD BIT_ULL(31) #define L2_CYCLE_COUNTER_RELOAD BIT_ULL(63) -#define L2CPUSRSELR_EL1 sys_reg(3, 3, 15, 0, 6) -#define L2CPUSRDR_EL1 sys_reg(3, 3, 15, 0, 7) #define reg_idx(reg, i) (((i) * IA_L2_REG_OFFSET) + reg##_BASE) @@ -99,48 +98,7 @@ #define L2_EVENT_STREX 0x421 #define L2_EVENT_CLREX 0x422 -static DEFINE_RAW_SPINLOCK(l2_access_lock); -/** - * set_l2_indirect_reg: write value to an L2 register - * @reg: Address of L2 register. - * @value: Value to be written to register. - * - * Use architecturally required barriers for ordering between system register - * accesses - */ -static void set_l2_indirect_reg(u64 reg, u64 val) -{ - unsigned long flags; - - raw_spin_lock_irqsave(&l2_access_lock, flags); - write_sysreg_s(reg, L2CPUSRSELR_EL1); - isb(); - write_sysreg_s(val, L2CPUSRDR_EL1); - isb(); - raw_spin_unlock_irqrestore(&l2_access_lock, flags); -} - -/** - * get_l2_indirect_reg: read an L2 register value - * @reg: Address of L2 register. - * - * Use architecturally required barriers for ordering between system register - * accesses - */ -static u64 get_l2_indirect_reg(u64 reg) -{ - u64 val; - unsigned long flags; - - raw_spin_lock_irqsave(&l2_access_lock, flags); - write_sysreg_s(reg, L2CPUSRSELR_EL1); - isb(); - val = read_sysreg_s(L2CPUSRDR_EL1); - raw_spin_unlock_irqrestore(&l2_access_lock, flags); - - return val; -} struct cluster_pmu; @@ -211,28 +169,28 @@ static inline struct cluster_pmu *get_cluster_pmu( static void cluster_pmu_reset(void) { /* Reset all counters */ - set_l2_indirect_reg(L2PMCR, L2PMCR_RESET_ALL); - set_l2_indirect_reg(L2PMCNTENCLR, l2_counter_present_mask); - set_l2_indirect_reg(L2PMINTENCLR, l2_counter_present_mask); - set_l2_indirect_reg(L2PMOVSCLR, l2_counter_present_mask); + kryo_l2_set_indirect_reg(L2PMCR, L2PMCR_RESET_ALL); + kryo_l2_set_indirect_reg(L2PMCNTENCLR, l2_counter_present_mask); + kryo_l2_set_indirect_reg(L2PMINTENCLR, l2_counter_present_mask); + kryo_l2_set_indirect_reg(L2PMOVSCLR, l2_counter_present_mask); } static inline void cluster_pmu_enable(void) { - set_l2_indirect_reg(L2PMCR, L2PMCR_COUNTERS_ENABLE); + kryo_l2_set_indirect_reg(L2PMCR, L2PMCR_COUNTERS_ENABLE); } static inline void cluster_pmu_disable(void) { - set_l2_indirect_reg(L2PMCR, L2PMCR_COUNTERS_DISABLE); + kryo_l2_set_indirect_reg(L2PMCR, L2PMCR_COUNTERS_DISABLE); } static inline void cluster_pmu_counter_set_value(u32 idx, u64 value) { if (idx == l2_cycle_ctr_idx) - set_l2_indirect_reg(L2PMCCNTR, value); + kryo_l2_set_indirect_reg(L2PMCCNTR, value); else - set_l2_indirect_reg(reg_idx(IA_L2PMXEVCNTR, idx), value); + kryo_l2_set_indirect_reg(reg_idx(IA_L2PMXEVCNTR, idx), value); } static inline u64 cluster_pmu_counter_get_value(u32 idx) @@ -240,46 +198,46 @@ static inline u64 cluster_pmu_counter_get_value(u32 idx) u64 value; if (idx == l2_cycle_ctr_idx) - value = get_l2_indirect_reg(L2PMCCNTR); + value = kryo_l2_get_indirect_reg(L2PMCCNTR); else - value = get_l2_indirect_reg(reg_idx(IA_L2PMXEVCNTR, idx)); + value = kryo_l2_get_indirect_reg(reg_idx(IA_L2PMXEVCNTR, idx)); return value; } static inline void cluster_pmu_counter_enable(u32 idx) { - set_l2_indirect_reg(L2PMCNTENSET, idx_to_reg_bit(idx)); + kryo_l2_set_indirect_reg(L2PMCNTENSET, idx_to_reg_bit(idx)); } static inline void cluster_pmu_counter_disable(u32 idx) { - set_l2_indirect_reg(L2PMCNTENCLR, idx_to_reg_bit(idx)); + kryo_l2_set_indirect_reg(L2PMCNTENCLR, idx_to_reg_bit(idx)); } static inline void cluster_pmu_counter_enable_interrupt(u32 idx) { - set_l2_indirect_reg(L2PMINTENSET, idx_to_reg_bit(idx)); + kryo_l2_set_indirect_reg(L2PMINTENSET, idx_to_reg_bit(idx)); } static inline void cluster_pmu_counter_disable_interrupt(u32 idx) { - set_l2_indirect_reg(L2PMINTENCLR, idx_to_reg_bit(idx)); + kryo_l2_set_indirect_reg(L2PMINTENCLR, idx_to_reg_bit(idx)); } static inline void cluster_pmu_set_evccntcr(u32 val) { - set_l2_indirect_reg(L2PMCCNTCR, val); + kryo_l2_set_indirect_reg(L2PMCCNTCR, val); } static inline void cluster_pmu_set_evcntcr(u32 ctr, u32 val) { - set_l2_indirect_reg(reg_idx(IA_L2PMXEVCNTCR, ctr), val); + kryo_l2_set_indirect_reg(reg_idx(IA_L2PMXEVCNTCR, ctr), val); } static inline void cluster_pmu_set_evtyper(u32 ctr, u32 val) { - set_l2_indirect_reg(reg_idx(IA_L2PMXEVTYPER, ctr), val); + kryo_l2_set_indirect_reg(reg_idx(IA_L2PMXEVTYPER, ctr), val); } static void cluster_pmu_set_resr(struct cluster_pmu *cluster, @@ -295,11 +253,11 @@ static void cluster_pmu_set_resr(struct cluster_pmu *cluster, spin_lock_irqsave(&cluster->pmu_lock, flags); - resr_val = get_l2_indirect_reg(L2PMRESR); + resr_val = kryo_l2_get_indirect_reg(L2PMRESR); resr_val &= ~(L2PMRESR_GROUP_MASK << shift); resr_val |= field; resr_val |= L2PMRESR_EN; - set_l2_indirect_reg(L2PMRESR, resr_val); + kryo_l2_set_indirect_reg(L2PMRESR, resr_val); spin_unlock_irqrestore(&cluster->pmu_lock, flags); } @@ -315,14 +273,14 @@ static inline void cluster_pmu_set_evfilter_sys_mode(u32 ctr) L2PMXEVFILTER_ORGFILTER_IDINDEP | L2PMXEVFILTER_ORGFILTER_ALL; - set_l2_indirect_reg(reg_idx(IA_L2PMXEVFILTER, ctr), val); + kryo_l2_set_indirect_reg(reg_idx(IA_L2PMXEVFILTER, ctr), val); } static inline u32 cluster_pmu_getreset_ovsr(void) { - u32 result = get_l2_indirect_reg(L2PMOVSSET); + u32 result = kryo_l2_get_indirect_reg(L2PMOVSSET); - set_l2_indirect_reg(L2PMOVSCLR, result); + kryo_l2_set_indirect_reg(L2PMOVSCLR, result); return result; } @@ -767,7 +725,7 @@ static int get_num_counters(void) { int val; - val = get_l2_indirect_reg(L2PMCR); + val = kryo_l2_get_indirect_reg(L2PMCR); /* * Read number of counters from L2PMCR and add 1 diff --git a/drivers/soc/qcom/Kconfig b/drivers/soc/qcom/Kconfig index 07bb261a63d2d..cdc4f46d64ef1 100644 --- a/drivers/soc/qcom/Kconfig +++ b/drivers/soc/qcom/Kconfig @@ -53,6 +53,10 @@ config QCOM_LLCC SDM845. This provides interfaces to clients that use the LLCC. Say yes here to enable LLCC slice driver. +config QCOM_KRYO_L2_ACCESSORS + bool + depends on ARCH_QCOM && ARM64 || COMPILE_TEST + config QCOM_MDT_LOADER tristate select QCOM_SCM diff --git a/drivers/soc/qcom/Makefile b/drivers/soc/qcom/Makefile index 7d7e2ecbdce6d..93392d9dc7f7a 100644 --- a/drivers/soc/qcom/Makefile +++ b/drivers/soc/qcom/Makefile @@ -24,3 +24,4 @@ obj-$(CONFIG_QCOM_APR) += apr.o obj-$(CONFIG_QCOM_LLCC) += llcc-qcom.o obj-$(CONFIG_QCOM_RPMHPD) += rpmhpd.o obj-$(CONFIG_QCOM_RPMPD) += rpmpd.o +obj-$(CONFIG_QCOM_KRYO_L2_ACCESSORS) += kryo-l2-accessors.o diff --git a/drivers/soc/qcom/kryo-l2-accessors.c b/drivers/soc/qcom/kryo-l2-accessors.c new file mode 100644 index 0000000000000..c20cb92077c03 --- /dev/null +++ b/drivers/soc/qcom/kryo-l2-accessors.c @@ -0,0 +1,57 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2018, The Linux Foundation. All rights reserved. + */ + +#include +#include +#include +#include + +#define L2CPUSRSELR_EL1 sys_reg(3, 3, 15, 0, 6) +#define L2CPUSRDR_EL1 sys_reg(3, 3, 15, 0, 7) + +static DEFINE_RAW_SPINLOCK(l2_access_lock); + +/** + * kryo_l2_set_indirect_reg() - write value to an L2 register + * @reg: Address of L2 register. + * @value: Value to be written to register. + * + * Use architecturally required barriers for ordering between system register + * accesses, and system registers with respect to device memory + */ +void kryo_l2_set_indirect_reg(u64 reg, u64 val) +{ + unsigned long flags; + + raw_spin_lock_irqsave(&l2_access_lock, flags); + write_sysreg_s(reg, L2CPUSRSELR_EL1); + isb(); + write_sysreg_s(val, L2CPUSRDR_EL1); + isb(); + raw_spin_unlock_irqrestore(&l2_access_lock, flags); +} +EXPORT_SYMBOL(kryo_l2_set_indirect_reg); + +/** + * kryo_l2_get_indirect_reg() - read an L2 register value + * @reg: Address of L2 register. + * + * Use architecturally required barriers for ordering between system register + * accesses, and system registers with respect to device memory + */ +u64 kryo_l2_get_indirect_reg(u64 reg) +{ + u64 val; + unsigned long flags; + + raw_spin_lock_irqsave(&l2_access_lock, flags); + write_sysreg_s(reg, L2CPUSRSELR_EL1); + isb(); + val = read_sysreg_s(L2CPUSRDR_EL1); + raw_spin_unlock_irqrestore(&l2_access_lock, flags); + + return val; +} +EXPORT_SYMBOL(kryo_l2_get_indirect_reg); diff --git a/include/soc/qcom/kryo-l2-accessors.h b/include/soc/qcom/kryo-l2-accessors.h new file mode 100644 index 0000000000000..673c5344afe32 --- /dev/null +++ b/include/soc/qcom/kryo-l2-accessors.h @@ -0,0 +1,12 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2018, The Linux Foundation. All rights reserved. + */ + +#ifndef __SOC_ARCH_QCOM_KRYO_L2_ACCESSORS_H +#define __SOC_ARCH_QCOM_KRYO_L2_ACCESSORS_H + +void kryo_l2_set_indirect_reg(u64 reg, u64 val); +u64 kryo_l2_get_indirect_reg(u64 reg); + +#endif -- GitLab From 2283f9e03328b2437abddf446027931631af1031 Mon Sep 17 00:00:00 2001 From: Ilia Lin Date: Fri, 3 Jul 2020 10:49:43 +0200 Subject: [PATCH 0371/1476] dt-bindings: clk: qcom: Add bindings for CPU clock for msm8996 Each of the CPU clusters (Power and Perf) on msm8996 are clocked via 2 PLLs, a primary and alternate. There are also 2 Mux'es, a primary and secondary all connected together as shown below +-------+ XO | | +------------------>0 | | | PLL/2 | SMUX +----+ +------->1 | | | | | | | +-------+ | +-------+ | +---->0 | | | | +---------------+ | +----------->1 | CPU clk |Primary PLL +----+ PLL_EARLY | | +------> | +------+-----------+ +------>2 PMUX | +---------------+ | | | | | +------+ | +-->3 | +--^+ ACD +-----+ | +-------+ +---------------+ +------+ | |Alt PLL | | | +---------------------------+ +---------------+ PLL_EARLY The primary PLL is what drives the CPU clk, except for times when we are reprogramming the PLL itself (for rate changes) when we temporarily switch to an alternate PLL. A subsequent patch adds support to switch between primary and alternate PLL during rate changes. The primary PLL operates on a single VCO range, between 600MHz and 3GHz. However the CPUs do support OPPs with frequencies between 300MHz and 600MHz. In order to support running the CPUs at those frequencies we end up having to lock the PLL at twice the rate and drive the CPU clk via the PLL/2 output and SMUX. Signed-off-by: Ilia Lin Reviewed-by: Rob Herring Link: https://lore.kernel.org/r/1593766185-16346-4-git-send-email-loic.poulain@linaro.org Signed-off-by: Stephen Boyd --- .../bindings/clock/qcom,msm8996-apcc.yaml | 56 +++++++++++++++++++ 1 file changed, 56 insertions(+) create mode 100644 Documentation/devicetree/bindings/clock/qcom,msm8996-apcc.yaml diff --git a/Documentation/devicetree/bindings/clock/qcom,msm8996-apcc.yaml b/Documentation/devicetree/bindings/clock/qcom,msm8996-apcc.yaml new file mode 100644 index 0000000000000..d673edeed98d1 --- /dev/null +++ b/Documentation/devicetree/bindings/clock/qcom,msm8996-apcc.yaml @@ -0,0 +1,56 @@ +# SPDX-License-Identifier: GPL-2.0-only +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/clock/qcom,kryocc.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Qualcomm clock controller for MSM8996 CPUs + +maintainers: + - Loic Poulain + +description: | + Qualcomm CPU clock controller for MSM8996 CPUs, clock 0 is for Power cluster + and clock 1 is for Perf cluster. + +properties: + compatible: + enum: + - qcom,msm8996-apcc + + reg: + maxItems: 1 + + '#clock-cells': + const: 1 + + clocks: + items: + - description: Primary PLL clock for power cluster (little) + - description: Primary PLL clock for perf cluster (big) + - description: Alternate PLL clock for power cluster (little) + - description: Alternate PLL clock for perf cluster (big) + + clock-names: + items: + - const: pwrcl_pll + - const: perfcl_pll + - const: pwrcl_alt_pll + - const: perfcl_alt_pll + +required: + - compatible + - reg + - '#clock-cells' + +additionalProperties: false + +examples: + # Example for msm8996 + - | + kryocc: clock-controller@6400000 { + compatible = "qcom,msm8996-apcc"; + reg = <0x6400000 0x90000>; + #clock-cells = <1>; + }; +... -- GitLab From 03e342dc45c9ec07303953d4e4af11879be36609 Mon Sep 17 00:00:00 2001 From: Loic Poulain Date: Fri, 3 Jul 2020 10:49:42 +0200 Subject: [PATCH 0372/1476] clk: qcom: Add CPU clock driver for msm8996 Each of the CPU clusters (Power and Perf) on msm8996 are clocked via 2 PLLs, a primary and alternate. There are also 2 Mux'es, a primary and secondary all connected together as shown below +-------+ XO | | +------------------>0 | | | PLL/2 | SMUX +----+ +------->1 | | | | | | | +-------+ | +-------+ | +---->0 | | | | +---------------+ | +----------->1 | CPU clk |Primary PLL +----+ PLL_EARLY | | +------> | +------+-----------+ +------>2 PMUX | +---------------+ | | | | | +------+ | +-->3 | +--^+ ACD +-----+ | +-------+ +---------------+ +------+ | |Alt PLL | | | +---------------------------+ +---------------+ PLL_EARLY The primary PLL is what drives the CPU clk, except for times when we are reprogramming the PLL itself (for rate changes) when we temporarily switch to an alternate PLL. A subsequent patch adds support to switch between primary and alternate PLL during rate changes. The primary PLL operates on a single VCO range, between 600MHz and 3GHz. However the CPUs do support OPPs with frequencies between 300MHz and 600MHz. In order to support running the CPUs at those frequencies we end up having to lock the PLL at twice the rate and drive the CPU clk via the PLL/2 output and SMUX. So for frequencies above 600MHz we follow the following path Primary PLL --> PLL_EARLY --> PMUX(1) --> CPU clk and for frequencies between 300MHz and 600MHz we follow Primary PLL --> PLL/2 --> SMUX(1) --> PMUX(0) --> CPU clk ACD stands for Adaptive Clock Distribution and is used to detect voltage droops. Signed-off-by: Rajendra Nayak Rajendra Nayak: Initial RFC - https://lkml.org/lkml/2016/9/29/84 Signed-off-by: Ilia Lin Ilia Lin: - reworked clock registering - Added clock-tree diagram - non-builtin support - clock notifier on rate change - https://lkml.org/lkml/2018/5/24/123 Signed-off-by: Loic Poulain Loic Poulain: - fixed driver remove / clk deregistering - Removed useless memory barriers - devm usage when possible - Fixed Kconfig depends Link: https://lore.kernel.org/r/1593766185-16346-3-git-send-email-loic.poulain@linaro.org Signed-off-by: Stephen Boyd --- drivers/clk/qcom/Kconfig | 9 + drivers/clk/qcom/Makefile | 1 + drivers/clk/qcom/clk-alpha-pll.h | 6 + drivers/clk/qcom/clk-cpu-8996.c | 538 +++++++++++++++++++++++++++++++ 4 files changed, 554 insertions(+) create mode 100644 drivers/clk/qcom/clk-cpu-8996.c diff --git a/drivers/clk/qcom/Kconfig b/drivers/clk/qcom/Kconfig index f510ef61db697..318c0adfaae11 100644 --- a/drivers/clk/qcom/Kconfig +++ b/drivers/clk/qcom/Kconfig @@ -37,6 +37,15 @@ config QCOM_CLK_APCS_MSM8916 Say Y if you want to support CPU frequency scaling on devices such as msm8916. +config QCOM_CLK_APCC_MSM8996 + tristate "MSM8996 CPU Clock Controller" + select QCOM_KRYO_L2_ACCESSORS + depends on ARM64 + help + Support for the CPU clock controller on msm8996 devices. + Say Y if you want to support CPU clock scaling using CPUfreq + drivers for dyanmic power management. + config QCOM_CLK_RPM tristate "RPM based Clock Controller" depends on MFD_QCOM_RPM diff --git a/drivers/clk/qcom/Makefile b/drivers/clk/qcom/Makefile index 21439b94395a4..ae0979bebe188 100644 --- a/drivers/clk/qcom/Makefile +++ b/drivers/clk/qcom/Makefile @@ -44,6 +44,7 @@ obj-$(CONFIG_MSM_MMCC_8996) += mmcc-msm8996.o obj-$(CONFIG_MSM_MMCC_8998) += mmcc-msm8998.o obj-$(CONFIG_QCOM_A53PLL) += a53-pll.o obj-$(CONFIG_QCOM_CLK_APCS_MSM8916) += apcs-msm8916.o +obj-$(CONFIG_QCOM_CLK_APCC_MSM8996) += clk-cpu-8996.o obj-$(CONFIG_QCOM_CLK_RPM) += clk-rpm.o obj-$(CONFIG_QCOM_CLK_RPMH) += clk-rpmh.o obj-$(CONFIG_QCOM_CLK_SMD_RPM) += clk-smd-rpm.o diff --git a/drivers/clk/qcom/clk-alpha-pll.h b/drivers/clk/qcom/clk-alpha-pll.h index 704674a153b67..1ba82be93dd54 100644 --- a/drivers/clk/qcom/clk-alpha-pll.h +++ b/drivers/clk/qcom/clk-alpha-pll.h @@ -47,6 +47,12 @@ struct pll_vco { u32 val; }; +#define VCO(a, b, c) { \ + .val = a,\ + .min_freq = b,\ + .max_freq = c,\ +} + /** * struct clk_alpha_pll - phase locked loop (PLL) * @offset: base address of registers diff --git a/drivers/clk/qcom/clk-cpu-8996.c b/drivers/clk/qcom/clk-cpu-8996.c new file mode 100644 index 0000000000000..77a2d2806e581 --- /dev/null +++ b/drivers/clk/qcom/clk-cpu-8996.c @@ -0,0 +1,538 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2020, The Linux Foundation. All rights reserved. + */ + +/* + * Each of the CPU clusters (Power and Perf) on msm8996 are + * clocked via 2 PLLs, a primary and alternate. There are also + * 2 Mux'es, a primary and secondary all connected together + * as shown below + * + * +-------+ + * XO | | + * +------------------>0 | + * | | + * PLL/2 | SMUX +----+ + * +------->1 | | + * | | | | + * | +-------+ | +-------+ + * | +---->0 | + * | | | + * +---------------+ | +----------->1 | CPU clk + * |Primary PLL +----+ PLL_EARLY | | +------> + * | +------+-----------+ +------>2 PMUX | + * +---------------+ | | | | + * | +------+ | +-->3 | + * +--^+ ACD +-----+ | +-------+ + * +---------------+ +------+ | + * |Alt PLL | | + * | +---------------------------+ + * +---------------+ PLL_EARLY + * + * The primary PLL is what drives the CPU clk, except for times + * when we are reprogramming the PLL itself (for rate changes) when + * we temporarily switch to an alternate PLL. + * + * The primary PLL operates on a single VCO range, between 600MHz + * and 3GHz. However the CPUs do support OPPs with frequencies + * between 300MHz and 600MHz. In order to support running the CPUs + * at those frequencies we end up having to lock the PLL at twice + * the rate and drive the CPU clk via the PLL/2 output and SMUX. + * + * So for frequencies above 600MHz we follow the following path + * Primary PLL --> PLL_EARLY --> PMUX(1) --> CPU clk + * and for frequencies between 300MHz and 600MHz we follow + * Primary PLL --> PLL/2 --> SMUX(1) --> PMUX(0) --> CPU clk + * + * ACD stands for Adaptive Clock Distribution and is used to + * detect voltage droops. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "clk-alpha-pll.h" +#include "clk-regmap.h" + +enum _pmux_input { + DIV_2_INDEX = 0, + PLL_INDEX, + ACD_INDEX, + ALT_INDEX, + NUM_OF_PMUX_INPUTS +}; + +#define DIV_2_THRESHOLD 600000000 +#define PWRCL_REG_OFFSET 0x0 +#define PERFCL_REG_OFFSET 0x80000 +#define MUX_OFFSET 0x40 +#define ALT_PLL_OFFSET 0x100 +#define SSSCTL_OFFSET 0x160 + +static const u8 prim_pll_regs[PLL_OFF_MAX_REGS] = { + [PLL_OFF_L_VAL] = 0x04, + [PLL_OFF_ALPHA_VAL] = 0x08, + [PLL_OFF_USER_CTL] = 0x10, + [PLL_OFF_CONFIG_CTL] = 0x18, + [PLL_OFF_CONFIG_CTL_U] = 0x1c, + [PLL_OFF_TEST_CTL] = 0x20, + [PLL_OFF_TEST_CTL_U] = 0x24, + [PLL_OFF_STATUS] = 0x28, +}; + +static const u8 alt_pll_regs[PLL_OFF_MAX_REGS] = { + [PLL_OFF_L_VAL] = 0x04, + [PLL_OFF_ALPHA_VAL] = 0x08, + [PLL_OFF_ALPHA_VAL_U] = 0x0c, + [PLL_OFF_USER_CTL] = 0x10, + [PLL_OFF_USER_CTL_U] = 0x14, + [PLL_OFF_CONFIG_CTL] = 0x18, + [PLL_OFF_TEST_CTL] = 0x20, + [PLL_OFF_TEST_CTL_U] = 0x24, + [PLL_OFF_STATUS] = 0x28, +}; + +/* PLLs */ + +static const struct alpha_pll_config hfpll_config = { + .l = 60, + .config_ctl_val = 0x200d4aa8, + .config_ctl_hi_val = 0x006, + .pre_div_mask = BIT(12), + .post_div_mask = 0x3 << 8, + .post_div_val = 0x1 << 8, + .main_output_mask = BIT(0), + .early_output_mask = BIT(3), +}; + +static struct clk_alpha_pll perfcl_pll = { + .offset = PERFCL_REG_OFFSET, + .regs = prim_pll_regs, + .flags = SUPPORTS_DYNAMIC_UPDATE | SUPPORTS_FSM_MODE, + .clkr.hw.init = &(struct clk_init_data){ + .name = "perfcl_pll", + .parent_names = (const char *[]){ "xo" }, + .num_parents = 1, + .ops = &clk_alpha_pll_huayra_ops, + }, +}; + +static struct clk_alpha_pll pwrcl_pll = { + .offset = PWRCL_REG_OFFSET, + .regs = prim_pll_regs, + .flags = SUPPORTS_DYNAMIC_UPDATE | SUPPORTS_FSM_MODE, + .clkr.hw.init = &(struct clk_init_data){ + .name = "pwrcl_pll", + .parent_names = (const char *[]){ "xo" }, + .num_parents = 1, + .ops = &clk_alpha_pll_huayra_ops, + }, +}; + +static const struct pll_vco alt_pll_vco_modes[] = { + VCO(3, 250000000, 500000000), + VCO(2, 500000000, 750000000), + VCO(1, 750000000, 1000000000), + VCO(0, 1000000000, 2150400000), +}; + +static const struct alpha_pll_config altpll_config = { + .l = 16, + .vco_val = 0x3 << 20, + .vco_mask = 0x3 << 20, + .config_ctl_val = 0x4001051b, + .post_div_mask = 0x3 << 8, + .post_div_val = 0x1 << 8, + .main_output_mask = BIT(0), + .early_output_mask = BIT(3), +}; + +static struct clk_alpha_pll perfcl_alt_pll = { + .offset = PERFCL_REG_OFFSET + ALT_PLL_OFFSET, + .regs = alt_pll_regs, + .vco_table = alt_pll_vco_modes, + .num_vco = ARRAY_SIZE(alt_pll_vco_modes), + .flags = SUPPORTS_OFFLINE_REQ | SUPPORTS_FSM_MODE, + .clkr.hw.init = &(struct clk_init_data) { + .name = "perfcl_alt_pll", + .parent_names = (const char *[]){ "xo" }, + .num_parents = 1, + .ops = &clk_alpha_pll_hwfsm_ops, + }, +}; + +static struct clk_alpha_pll pwrcl_alt_pll = { + .offset = PWRCL_REG_OFFSET + ALT_PLL_OFFSET, + .regs = alt_pll_regs, + .vco_table = alt_pll_vco_modes, + .num_vco = ARRAY_SIZE(alt_pll_vco_modes), + .flags = SUPPORTS_OFFLINE_REQ | SUPPORTS_FSM_MODE, + .clkr.hw.init = &(struct clk_init_data) { + .name = "pwrcl_alt_pll", + .parent_names = (const char *[]){ "xo" }, + .num_parents = 1, + .ops = &clk_alpha_pll_hwfsm_ops, + }, +}; + +struct clk_cpu_8996_mux { + u32 reg; + u8 shift; + u8 width; + struct notifier_block nb; + struct clk_hw *pll; + struct clk_hw *pll_div_2; + struct clk_regmap clkr; +}; + +static int cpu_clk_notifier_cb(struct notifier_block *nb, unsigned long event, + void *data); + +#define to_clk_cpu_8996_mux_nb(_nb) \ + container_of(_nb, struct clk_cpu_8996_mux, nb) + +static inline struct clk_cpu_8996_mux *to_clk_cpu_8996_mux_hw(struct clk_hw *hw) +{ + return container_of(to_clk_regmap(hw), struct clk_cpu_8996_mux, clkr); +} + +static u8 clk_cpu_8996_mux_get_parent(struct clk_hw *hw) +{ + struct clk_regmap *clkr = to_clk_regmap(hw); + struct clk_cpu_8996_mux *cpuclk = to_clk_cpu_8996_mux_hw(hw); + u32 mask = GENMASK(cpuclk->width - 1, 0); + u32 val; + + regmap_read(clkr->regmap, cpuclk->reg, &val); + val >>= cpuclk->shift; + + return val & mask; +} + +static int clk_cpu_8996_mux_set_parent(struct clk_hw *hw, u8 index) +{ + struct clk_regmap *clkr = to_clk_regmap(hw); + struct clk_cpu_8996_mux *cpuclk = to_clk_cpu_8996_mux_hw(hw); + u32 mask = GENMASK(cpuclk->width + cpuclk->shift - 1, cpuclk->shift); + u32 val; + + val = index; + val <<= cpuclk->shift; + + return regmap_update_bits(clkr->regmap, cpuclk->reg, mask, val); +} + +static int clk_cpu_8996_mux_determine_rate(struct clk_hw *hw, + struct clk_rate_request *req) +{ + struct clk_cpu_8996_mux *cpuclk = to_clk_cpu_8996_mux_hw(hw); + struct clk_hw *parent = cpuclk->pll; + + if (cpuclk->pll_div_2 && req->rate < DIV_2_THRESHOLD) { + if (req->rate < (DIV_2_THRESHOLD / 2)) + return -EINVAL; + + parent = cpuclk->pll_div_2; + } + + req->best_parent_rate = clk_hw_round_rate(parent, req->rate); + req->best_parent_hw = parent; + + return 0; +} + +static const struct clk_ops clk_cpu_8996_mux_ops = { + .set_parent = clk_cpu_8996_mux_set_parent, + .get_parent = clk_cpu_8996_mux_get_parent, + .determine_rate = clk_cpu_8996_mux_determine_rate, +}; + +static struct clk_cpu_8996_mux pwrcl_smux = { + .reg = PWRCL_REG_OFFSET + MUX_OFFSET, + .shift = 2, + .width = 2, + .clkr.hw.init = &(struct clk_init_data) { + .name = "pwrcl_smux", + .parent_names = (const char *[]){ + "xo", + "pwrcl_pll_main", + }, + .num_parents = 2, + .ops = &clk_cpu_8996_mux_ops, + .flags = CLK_SET_RATE_PARENT, + }, +}; + +static struct clk_cpu_8996_mux perfcl_smux = { + .reg = PERFCL_REG_OFFSET + MUX_OFFSET, + .shift = 2, + .width = 2, + .clkr.hw.init = &(struct clk_init_data) { + .name = "perfcl_smux", + .parent_names = (const char *[]){ + "xo", + "perfcl_pll_main", + }, + .num_parents = 2, + .ops = &clk_cpu_8996_mux_ops, + .flags = CLK_SET_RATE_PARENT, + }, +}; + +static struct clk_cpu_8996_mux pwrcl_pmux = { + .reg = PWRCL_REG_OFFSET + MUX_OFFSET, + .shift = 0, + .width = 2, + .pll = &pwrcl_pll.clkr.hw, + .pll_div_2 = &pwrcl_smux.clkr.hw, + .nb.notifier_call = cpu_clk_notifier_cb, + .clkr.hw.init = &(struct clk_init_data) { + .name = "pwrcl_pmux", + .parent_names = (const char *[]){ + "pwrcl_smux", + "pwrcl_pll", + "pwrcl_pll_acd", + "pwrcl_alt_pll", + }, + .num_parents = 4, + .ops = &clk_cpu_8996_mux_ops, + /* CPU clock is critical and should never be gated */ + .flags = CLK_SET_RATE_PARENT | CLK_IS_CRITICAL, + }, +}; + +static struct clk_cpu_8996_mux perfcl_pmux = { + .reg = PERFCL_REG_OFFSET + MUX_OFFSET, + .shift = 0, + .width = 2, + .pll = &perfcl_pll.clkr.hw, + .pll_div_2 = &perfcl_smux.clkr.hw, + .nb.notifier_call = cpu_clk_notifier_cb, + .clkr.hw.init = &(struct clk_init_data) { + .name = "perfcl_pmux", + .parent_names = (const char *[]){ + "perfcl_smux", + "perfcl_pll", + "perfcl_pll_acd", + "perfcl_alt_pll", + }, + .num_parents = 4, + .ops = &clk_cpu_8996_mux_ops, + /* CPU clock is critical and should never be gated */ + .flags = CLK_SET_RATE_PARENT | CLK_IS_CRITICAL, + }, +}; + +static const struct regmap_config cpu_msm8996_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x80210, + .fast_io = true, + .val_format_endian = REGMAP_ENDIAN_LITTLE, +}; + +struct clk_regmap *cpu_msm8996_clks[] = { + &perfcl_pll.clkr, + &pwrcl_pll.clkr, + &perfcl_alt_pll.clkr, + &pwrcl_alt_pll.clkr, + &perfcl_smux.clkr, + &pwrcl_smux.clkr, + &perfcl_pmux.clkr, + &pwrcl_pmux.clkr, +}; + +static int qcom_cpu_clk_msm8996_register_clks(struct device *dev, + struct regmap *regmap) +{ + int i, ret; + + perfcl_smux.pll = clk_hw_register_fixed_factor(dev, "perfcl_pll_main", + "perfcl_pll", + CLK_SET_RATE_PARENT, + 1, 2); + if (IS_ERR(perfcl_smux.pll)) { + dev_err(dev, "Failed to initialize perfcl_pll_main\n"); + return PTR_ERR(perfcl_smux.pll); + } + + pwrcl_smux.pll = clk_hw_register_fixed_factor(dev, "pwrcl_pll_main", + "pwrcl_pll", + CLK_SET_RATE_PARENT, + 1, 2); + if (IS_ERR(pwrcl_smux.pll)) { + dev_err(dev, "Failed to initialize pwrcl_pll_main\n"); + clk_hw_unregister(perfcl_smux.pll); + return PTR_ERR(pwrcl_smux.pll); + } + + for (i = 0; i < ARRAY_SIZE(cpu_msm8996_clks); i++) { + ret = devm_clk_register_regmap(dev, cpu_msm8996_clks[i]); + if (ret) { + clk_hw_unregister(perfcl_smux.pll); + clk_hw_unregister(pwrcl_smux.pll); + return ret; + } + } + + clk_alpha_pll_configure(&perfcl_pll, regmap, &hfpll_config); + clk_alpha_pll_configure(&pwrcl_pll, regmap, &hfpll_config); + clk_alpha_pll_configure(&perfcl_alt_pll, regmap, &altpll_config); + clk_alpha_pll_configure(&pwrcl_alt_pll, regmap, &altpll_config); + + /* Enable alt PLLs */ + clk_prepare_enable(pwrcl_alt_pll.clkr.hw.clk); + clk_prepare_enable(perfcl_alt_pll.clkr.hw.clk); + + clk_notifier_register(pwrcl_pmux.clkr.hw.clk, &pwrcl_pmux.nb); + clk_notifier_register(perfcl_pmux.clkr.hw.clk, &perfcl_pmux.nb); + + return ret; +} + +static int qcom_cpu_clk_msm8996_unregister_clks(void) +{ + int ret = 0; + + ret = clk_notifier_unregister(pwrcl_pmux.clkr.hw.clk, &pwrcl_pmux.nb); + if (ret) + return ret; + + ret = clk_notifier_unregister(perfcl_pmux.clkr.hw.clk, &perfcl_pmux.nb); + if (ret) + return ret; + + clk_hw_unregister(perfcl_smux.pll); + clk_hw_unregister(pwrcl_smux.pll); + + return 0; +} + +#define CPU_AFINITY_MASK 0xFFF +#define PWRCL_CPU_REG_MASK 0x3 +#define PERFCL_CPU_REG_MASK 0x103 + +#define L2ACDCR_REG 0x580ULL +#define L2ACDTD_REG 0x581ULL +#define L2ACDDVMRC_REG 0x584ULL +#define L2ACDSSCR_REG 0x589ULL + +static DEFINE_SPINLOCK(qcom_clk_acd_lock); +static void __iomem *base; + +static void qcom_cpu_clk_msm8996_acd_init(void __iomem *base) +{ + u64 hwid; + unsigned long flags; + + spin_lock_irqsave(&qcom_clk_acd_lock, flags); + + hwid = read_cpuid_mpidr() & CPU_AFINITY_MASK; + + kryo_l2_set_indirect_reg(L2ACDTD_REG, 0x00006a11); + kryo_l2_set_indirect_reg(L2ACDDVMRC_REG, 0x000e0f0f); + kryo_l2_set_indirect_reg(L2ACDSSCR_REG, 0x00000601); + + if (PWRCL_CPU_REG_MASK == (hwid | PWRCL_CPU_REG_MASK)) { + writel(0xf, base + PWRCL_REG_OFFSET + SSSCTL_OFFSET); + kryo_l2_set_indirect_reg(L2ACDCR_REG, 0x002c5ffd); + } + + if (PERFCL_CPU_REG_MASK == (hwid | PERFCL_CPU_REG_MASK)) { + kryo_l2_set_indirect_reg(L2ACDCR_REG, 0x002c5ffd); + writel(0xf, base + PERFCL_REG_OFFSET + SSSCTL_OFFSET); + } + + spin_unlock_irqrestore(&qcom_clk_acd_lock, flags); +} + +static int cpu_clk_notifier_cb(struct notifier_block *nb, unsigned long event, + void *data) +{ + struct clk_cpu_8996_mux *cpuclk = to_clk_cpu_8996_mux_nb(nb); + struct clk_notifier_data *cnd = data; + int ret; + + switch (event) { + case PRE_RATE_CHANGE: + ret = clk_cpu_8996_mux_set_parent(&cpuclk->clkr.hw, ALT_INDEX); + qcom_cpu_clk_msm8996_acd_init(base); + break; + case POST_RATE_CHANGE: + if (cnd->new_rate < DIV_2_THRESHOLD) + ret = clk_cpu_8996_mux_set_parent(&cpuclk->clkr.hw, + DIV_2_INDEX); + else + ret = clk_cpu_8996_mux_set_parent(&cpuclk->clkr.hw, + ACD_INDEX); + break; + default: + ret = 0; + break; + } + + return notifier_from_errno(ret); +}; + +static int qcom_cpu_clk_msm8996_driver_probe(struct platform_device *pdev) +{ + struct regmap *regmap; + struct clk_hw_onecell_data *data; + struct device *dev = &pdev->dev; + int ret; + + data = devm_kzalloc(dev, struct_size(data, hws, 2), GFP_KERNEL); + if (!data) + return -ENOMEM; + + base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(base)) + return PTR_ERR(base); + + regmap = devm_regmap_init_mmio(dev, base, &cpu_msm8996_regmap_config); + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + + ret = qcom_cpu_clk_msm8996_register_clks(dev, regmap); + if (ret) + return ret; + + qcom_cpu_clk_msm8996_acd_init(base); + + data->hws[0] = &pwrcl_pmux.clkr.hw; + data->hws[1] = &perfcl_pmux.clkr.hw; + data->num = 2; + + return devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get, data); +} + +static int qcom_cpu_clk_msm8996_driver_remove(struct platform_device *pdev) +{ + return qcom_cpu_clk_msm8996_unregister_clks(); +} + +static const struct of_device_id qcom_cpu_clk_msm8996_match_table[] = { + { .compatible = "qcom,msm8996-apcc" }, + {} +}; +MODULE_DEVICE_TABLE(of, qcom_cpu_clk_msm8996_match_table); + +static struct platform_driver qcom_cpu_clk_msm8996_driver = { + .probe = qcom_cpu_clk_msm8996_driver_probe, + .remove = qcom_cpu_clk_msm8996_driver_remove, + .driver = { + .name = "qcom-msm8996-apcc", + .of_match_table = qcom_cpu_clk_msm8996_match_table, + }, +}; +module_platform_driver(qcom_cpu_clk_msm8996_driver); + +MODULE_DESCRIPTION("QCOM MSM8996 CPU Clock Driver"); +MODULE_LICENSE("GPL v2"); -- GitLab From 5f1d8970d4cdba6ef3b2f3f11ef15edd62ce551a Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Fri, 3 Jul 2020 19:51:14 +0200 Subject: [PATCH 0373/1476] Replace HTTP links with HTTPS ones: Common CLK framework Rationale: Reduces attack surface on kernel devs opening the links for MITM as HTTPS traffic is much harder to manipulate. Deterministic algorithm: For each file: If not .svg: For each line: If doesn't contain `\bxmlns\b`: For each link, `\bhttp://[^# \t\r\n]*(?:\w|/)`: If both the HTTP and HTTPS versions return 200 OK and serve the same content: Replace HTTP with HTTPS. Signed-off-by: Alexander A. Klimov Link: https://lore.kernel.org/r/20200703175114.15027-1-grandmaster@al2klimov.de Signed-off-by: Stephen Boyd --- .../devicetree/bindings/clock/clock-bindings.txt | 2 +- Documentation/devicetree/bindings/clock/silabs,si514.txt | 2 +- Documentation/devicetree/bindings/clock/silabs,si5351.txt | 2 +- Documentation/devicetree/bindings/clock/silabs,si570.txt | 4 ++-- Documentation/devicetree/bindings/clock/ti,cdce706.txt | 2 +- Documentation/devicetree/bindings/clock/ti,cdce925.txt | 8 ++++---- drivers/clk/clk-cdce706.c | 2 +- drivers/clk/clk-gpio.c | 2 +- drivers/clk/clk-si5351.c | 4 ++-- drivers/clk/keystone/sci-clk.c | 2 +- drivers/clk/keystone/syscon-clk.c | 2 +- drivers/clk/meson/meson8b.h | 2 +- drivers/clk/versatile/icst.c | 2 +- drivers/clk/versatile/icst.h | 2 +- 14 files changed, 19 insertions(+), 19 deletions(-) diff --git a/Documentation/devicetree/bindings/clock/clock-bindings.txt b/Documentation/devicetree/bindings/clock/clock-bindings.txt index 8a55fdcf96eea..f2ea53832ac63 100644 --- a/Documentation/devicetree/bindings/clock/clock-bindings.txt +++ b/Documentation/devicetree/bindings/clock/clock-bindings.txt @@ -9,7 +9,7 @@ specifier is an array of zero, one or more cells identifying the clock output on a device. The length of a clock specifier is defined by the value of a #clock-cells property in the clock provider node. -[1] http://patchwork.ozlabs.org/patch/31551/ +[1] https://patchwork.ozlabs.org/patch/31551/ ==Clock providers== diff --git a/Documentation/devicetree/bindings/clock/silabs,si514.txt b/Documentation/devicetree/bindings/clock/silabs,si514.txt index ea1a9dbc63b6a..a4f28ec86f355 100644 --- a/Documentation/devicetree/bindings/clock/silabs,si514.txt +++ b/Documentation/devicetree/bindings/clock/silabs,si514.txt @@ -6,7 +6,7 @@ found in the datasheet[2]. [1] Documentation/devicetree/bindings/clock/clock-bindings.txt [2] Si514 datasheet - http://www.silabs.com/Support%20Documents/TechnicalDocs/si514.pdf + https://www.silabs.com/Support%20Documents/TechnicalDocs/si514.pdf Required properties: - compatible: Shall be "silabs,si514" diff --git a/Documentation/devicetree/bindings/clock/silabs,si5351.txt b/Documentation/devicetree/bindings/clock/silabs,si5351.txt index f00191cad8cdf..8fe6f80afadee 100644 --- a/Documentation/devicetree/bindings/clock/silabs,si5351.txt +++ b/Documentation/devicetree/bindings/clock/silabs,si5351.txt @@ -2,7 +2,7 @@ Binding for Silicon Labs Si5351a/b/c programmable i2c clock generator. Reference [1] Si5351A/B/C Data Sheet - http://www.silabs.com/Support%20Documents/TechnicalDocs/Si5351.pdf + https://www.silabs.com/Support%20Documents/TechnicalDocs/Si5351.pdf The Si5351a/b/c are programmable i2c clock generators with up to 8 output clocks. Si5351a also has a reduced pin-count package (MSOP10) where only diff --git a/Documentation/devicetree/bindings/clock/silabs,si570.txt b/Documentation/devicetree/bindings/clock/silabs,si570.txt index c09f21e1d98f0..901935e929d2f 100644 --- a/Documentation/devicetree/bindings/clock/silabs,si570.txt +++ b/Documentation/devicetree/bindings/clock/silabs,si570.txt @@ -7,9 +7,9 @@ found in the data sheets[2][3]. [1] Documentation/devicetree/bindings/clock/clock-bindings.txt [2] Si570/571 Data Sheet - http://www.silabs.com/Support%20Documents/TechnicalDocs/si570.pdf + https://www.silabs.com/Support%20Documents/TechnicalDocs/si570.pdf [3] Si598/599 Data Sheet - http://www.silabs.com/Support%20Documents/TechnicalDocs/si598-99.pdf + https://www.silabs.com/Support%20Documents/TechnicalDocs/si598-99.pdf Required properties: - compatible: Shall be one of "silabs,si570", "silabs,si571", diff --git a/Documentation/devicetree/bindings/clock/ti,cdce706.txt b/Documentation/devicetree/bindings/clock/ti,cdce706.txt index 959d96632f5d2..21c3ff7647887 100644 --- a/Documentation/devicetree/bindings/clock/ti,cdce706.txt +++ b/Documentation/devicetree/bindings/clock/ti,cdce706.txt @@ -1,7 +1,7 @@ Bindings for Texas Instruments CDCE706 programmable 3-PLL clock synthesizer/multiplier/divider. -Reference: http://www.ti.com/lit/ds/symlink/cdce706.pdf +Reference: https://www.ti.com/lit/ds/symlink/cdce706.pdf I2C device node required properties: - compatible: shall be "ti,cdce706". diff --git a/Documentation/devicetree/bindings/clock/ti,cdce925.txt b/Documentation/devicetree/bindings/clock/ti,cdce925.txt index 26544c85202af..df42ab72718ff 100644 --- a/Documentation/devicetree/bindings/clock/ti,cdce925.txt +++ b/Documentation/devicetree/bindings/clock/ti,cdce925.txt @@ -4,10 +4,10 @@ Reference This binding uses the common clock binding[1]. [1] Documentation/devicetree/bindings/clock/clock-bindings.txt -[2] http://www.ti.com/product/cdce913 -[3] http://www.ti.com/product/cdce925 -[4] http://www.ti.com/product/cdce937 -[5] http://www.ti.com/product/cdce949 +[2] https://www.ti.com/product/cdce913 +[3] https://www.ti.com/product/cdce925 +[4] https://www.ti.com/product/cdce937 +[5] https://www.ti.com/product/cdce949 The driver provides clock sources for each output Y1 through Y5. diff --git a/drivers/clk/clk-cdce706.c b/drivers/clk/clk-cdce706.c index 239102e37e2ff..c91e9096b070e 100644 --- a/drivers/clk/clk-cdce706.c +++ b/drivers/clk/clk-cdce706.c @@ -4,7 +4,7 @@ * * Copyright (c) 2014 Cadence Design Systems Inc. * - * Reference: http://www.ti.com/lit/ds/symlink/cdce706.pdf + * Reference: https://www.ti.com/lit/ds/symlink/cdce706.pdf */ #include diff --git a/drivers/clk/clk-gpio.c b/drivers/clk/clk-gpio.c index 70397b4b5ffea..38755a241ab76 100644 --- a/drivers/clk/clk-gpio.c +++ b/drivers/clk/clk-gpio.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 /* - * Copyright (C) 2013 - 2014 Texas Instruments Incorporated - http://www.ti.com + * Copyright (C) 2013 - 2014 Texas Instruments Incorporated - https://www.ti.com * * Authors: * Jyri Sarha diff --git a/drivers/clk/clk-si5351.c b/drivers/clk/clk-si5351.c index 71de3618e5084..1e1702e609cbb 100644 --- a/drivers/clk/clk-si5351.c +++ b/drivers/clk/clk-si5351.c @@ -7,9 +7,9 @@ * * References: * [1] "Si5351A/B/C Data Sheet" - * http://www.silabs.com/Support%20Documents/TechnicalDocs/Si5351.pdf + * https://www.silabs.com/Support%20Documents/TechnicalDocs/Si5351.pdf * [2] "Manually Generating an Si5351 Register Map" - * http://www.silabs.com/Support%20Documents/TechnicalDocs/AN619.pdf + * https://www.silabs.com/Support%20Documents/TechnicalDocs/AN619.pdf */ #include diff --git a/drivers/clk/keystone/sci-clk.c b/drivers/clk/keystone/sci-clk.c index 7edf8c8432b67..2ad26cb927fdb 100644 --- a/drivers/clk/keystone/sci-clk.c +++ b/drivers/clk/keystone/sci-clk.c @@ -1,7 +1,7 @@ /* * SCI Clock driver for keystone based devices * - * Copyright (C) 2015-2016 Texas Instruments Incorporated - http://www.ti.com/ + * Copyright (C) 2015-2016 Texas Instruments Incorporated - https://www.ti.com/ * Tero Kristo * * This program is free software; you can redistribute it and/or modify diff --git a/drivers/clk/keystone/syscon-clk.c b/drivers/clk/keystone/syscon-clk.c index 8d7dbea3bd300..5b3d364621747 100644 --- a/drivers/clk/keystone/syscon-clk.c +++ b/drivers/clk/keystone/syscon-clk.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 /* - * Copyright (C) 2020 Texas Instruments Incorporated - http://www.ti.com/ + * Copyright (C) 2020 Texas Instruments Incorporated - https://www.ti.com/ */ #include diff --git a/drivers/clk/meson/meson8b.h b/drivers/clk/meson/meson8b.h index cd38ae2a9cb5a..3958b580d9426 100644 --- a/drivers/clk/meson/meson8b.h +++ b/drivers/clk/meson/meson8b.h @@ -17,7 +17,7 @@ * blocks below. Those offsets must be multiplied by 4 before adding them to * the base address to get the right value * - * [0] http://dn.odroid.com/S805/Datasheet/S805_Datasheet%20V0.8%2020150126.pdf + * [0] https://dn.odroid.com/S805/Datasheet/S805_Datasheet%20V0.8%2020150126.pdf */ #define HHI_GP_PLL_CNTL 0x40 /* 0x10 offset in data sheet */ #define HHI_GP_PLL_CNTL2 0x44 /* 0x11 offset in data sheet */ diff --git a/drivers/clk/versatile/icst.c b/drivers/clk/versatile/icst.c index ba4b2d22ec97b..307cb3774f877 100644 --- a/drivers/clk/versatile/icst.c +++ b/drivers/clk/versatile/icst.c @@ -5,7 +5,7 @@ * Copyright (C) 2003 Deep Blue Solutions, Ltd, All Rights Reserved. * * Support functions for calculating clocks/divisors for the ICST307 - * clock generators. See http://www.idt.com/ for more information + * clock generators. See https://www.idt.com/ for more information * on these devices. * * This is an almost identical implementation to the ICST525 clock generator. diff --git a/drivers/clk/versatile/icst.h b/drivers/clk/versatile/icst.h index 73a3062b45355..29622768b02a0 100644 --- a/drivers/clk/versatile/icst.h +++ b/drivers/clk/versatile/icst.h @@ -3,7 +3,7 @@ * Copyright (C) 2003 Deep Blue Solutions, Ltd, All Rights Reserved. * * Support functions for calculating clocks/divisors for the ICST - * clock generators. See http://www.idt.com/ for more information + * clock generators. See https://www.idt.com/ for more information * on these devices. */ #ifndef ICST_H -- GitLab From e7fb524cfccaf649b257d517f437392f50b3931f Mon Sep 17 00:00:00 2001 From: Sivaprakash Murugesan Date: Sun, 5 Jul 2020 14:47:54 +0530 Subject: [PATCH 0374/1476] dt-bindings: clock: qcom: ipq8074: Add missing bindings for PCIe Add missing clock bindings for PCIe port0 of ipq8074. Co-developed-by: Selvam Sathappan Periakaruppan Signed-off-by: Selvam Sathappan Periakaruppan Signed-off-by: Sivaprakash Murugesan Link: https://lore.kernel.org/r/1593940680-2363-4-git-send-email-sivaprak@codeaurora.org [sboyd@kernel.org: Clean up commit text subject] Signed-off-by: Stephen Boyd --- include/dt-bindings/clock/qcom,gcc-ipq8074.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/include/dt-bindings/clock/qcom,gcc-ipq8074.h b/include/dt-bindings/clock/qcom,gcc-ipq8074.h index 4de4811a3540e..e3e018565addf 100644 --- a/include/dt-bindings/clock/qcom,gcc-ipq8074.h +++ b/include/dt-bindings/clock/qcom,gcc-ipq8074.h @@ -362,5 +362,9 @@ #define GCC_PCIE1_AXI_SLAVE_ARES 128 #define GCC_PCIE1_AHB_ARES 129 #define GCC_PCIE1_AXI_MASTER_STICKY_ARES 130 +#define GCC_PCIE0_AXI_SLAVE_STICKY_ARES 131 +#define GCC_PCIE0_AXI_S_BRIDGE_CLK 132 +#define GCC_PCIE0_RCHNG_CLK_SRC 133 +#define GCC_PCIE0_RCHNG_CLK 134 #endif -- GitLab From f0cfcf1ade201dcfd3365f231efc90e846fa46df Mon Sep 17 00:00:00 2001 From: Sivaprakash Murugesan Date: Sun, 5 Jul 2020 14:47:55 +0530 Subject: [PATCH 0375/1476] clk: qcom: ipq8074: Add missing clocks for pcie Add missing clocks and resets for pcie port0 of ipq8074 devices. Co-developed-by: Selvam Sathappan Periakaruppan Signed-off-by: Selvam Sathappan Periakaruppan Signed-off-by: Sivaprakash Murugesan Link: https://lore.kernel.org/r/1593940680-2363-5-git-send-email-sivaprak@codeaurora.org [sboyd@kernel.org: Make freq table static const] Signed-off-by: Stephen Boyd --- drivers/clk/qcom/gcc-ipq8074.c | 60 ++++++++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) diff --git a/drivers/clk/qcom/gcc-ipq8074.c b/drivers/clk/qcom/gcc-ipq8074.c index e01f5f591d1e2..ef2c9c4cf9abb 100644 --- a/drivers/clk/qcom/gcc-ipq8074.c +++ b/drivers/clk/qcom/gcc-ipq8074.c @@ -4316,6 +4316,62 @@ static struct clk_branch gcc_gp3_clk = { }, }; +static const struct freq_tbl ftbl_pcie_rchng_clk_src[] = { + F(19200000, P_XO, 1, 0, 0), + F(100000000, P_GPLL0, 8, 0, 0), + { } +}; + +struct clk_rcg2 pcie0_rchng_clk_src = { + .cmd_rcgr = 0x75070, + .freq_tbl = ftbl_pcie_rchng_clk_src, + .hid_width = 5, + .parent_map = gcc_xo_gpll0_map, + .clkr.hw.init = &(struct clk_init_data){ + .name = "pcie0_rchng_clk_src", + .parent_hws = (const struct clk_hw *[]) { + &gpll0.clkr.hw }, + .num_parents = 2, + .ops = &clk_rcg2_ops, + }, +}; + +static struct clk_branch gcc_pcie0_rchng_clk = { + .halt_reg = 0x75070, + .halt_bit = 31, + .clkr = { + .enable_reg = 0x75070, + .enable_mask = BIT(1), + .hw.init = &(struct clk_init_data){ + .name = "gcc_pcie0_rchng_clk", + .parent_hws = (const struct clk_hw *[]){ + &pcie0_rchng_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_pcie0_axi_s_bridge_clk = { + .halt_reg = 0x75048, + .halt_bit = 31, + .clkr = { + .enable_reg = 0x75048, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_pcie0_axi_s_bridge_clk", + .parent_hws = (const struct clk_hw *[]){ + &pcie0_axi_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + static struct clk_hw *gcc_ipq8074_hws[] = { &gpll0_out_main_div2.hw, &gpll6_out_main_div2.hw, @@ -4551,6 +4607,9 @@ static struct clk_regmap *gcc_ipq8074_clks[] = { [GCC_GP1_CLK] = &gcc_gp1_clk.clkr, [GCC_GP2_CLK] = &gcc_gp2_clk.clkr, [GCC_GP3_CLK] = &gcc_gp3_clk.clkr, + [GCC_PCIE0_RCHNG_CLK_SRC] = &pcie0_rchng_clk_src.clkr, + [GCC_PCIE0_RCHNG_CLK] = &gcc_pcie0_rchng_clk.clkr, + [GCC_PCIE0_AXI_S_BRIDGE_CLK] = &gcc_pcie0_axi_s_bridge_clk.clkr, }; static const struct qcom_reset_map gcc_ipq8074_resets[] = { @@ -4678,6 +4737,7 @@ static const struct qcom_reset_map gcc_ipq8074_resets[] = { [GCC_PCIE0_AXI_SLAVE_ARES] = { 0x75040, 4 }, [GCC_PCIE0_AHB_ARES] = { 0x75040, 5 }, [GCC_PCIE0_AXI_MASTER_STICKY_ARES] = { 0x75040, 6 }, + [GCC_PCIE0_AXI_SLAVE_STICKY_ARES] = { 0x75040, 7 }, [GCC_PCIE1_PIPE_ARES] = { 0x76040, 0 }, [GCC_PCIE1_SLEEP_ARES] = { 0x76040, 1 }, [GCC_PCIE1_CORE_STICKY_ARES] = { 0x76040, 2 }, -- GitLab From b4297844995f380588e6f935a2f98c399129a9b2 Mon Sep 17 00:00:00 2001 From: Konrad Dybcio Date: Wed, 24 Jun 2020 01:00:18 +0200 Subject: [PATCH 0376/1476] clk: qcom: smd: Add support for MSM8992/4 rpm clocks Add rpm smd clocks, PMIC and bus clocks which are required on MSM8992, MSM8994 (and APQ variants) for clients to vote on. Signed-off-by: Konrad Dybcio Link: https://lore.kernel.org/r/20200623230018.303776-1-konradybcio@gmail.com [sboyd@kernel.org: Fixed up binding numbers] Signed-off-by: Stephen Boyd --- .../devicetree/bindings/clock/qcom,rpmcc.txt | 2 + drivers/clk/qcom/clk-smd-rpm.c | 171 ++++++++++++++++++ include/dt-bindings/clock/qcom,rpmcc.h | 4 + 3 files changed, 177 insertions(+) diff --git a/Documentation/devicetree/bindings/clock/qcom,rpmcc.txt b/Documentation/devicetree/bindings/clock/qcom,rpmcc.txt index 8786d19ffe173..b44a0622fb3ac 100644 --- a/Documentation/devicetree/bindings/clock/qcom,rpmcc.txt +++ b/Documentation/devicetree/bindings/clock/qcom,rpmcc.txt @@ -18,6 +18,8 @@ Required properties : "qcom,rpmcc-msm8976", "qcom,rpmcc" "qcom,rpmcc-apq8064", "qcom,rpmcc" "qcom,rpmcc-ipq806x", "qcom,rpmcc" + "qcom,rpmcc-msm8992",·"qcom,rpmcc" + "qcom,rpmcc-msm8994",·"qcom,rpmcc" "qcom,rpmcc-msm8996", "qcom,rpmcc" "qcom,rpmcc-msm8998", "qcom,rpmcc" "qcom,rpmcc-qcs404", "qcom,rpmcc" diff --git a/drivers/clk/qcom/clk-smd-rpm.c b/drivers/clk/qcom/clk-smd-rpm.c index 083399affc8e0..0e1dfa89489e8 100644 --- a/drivers/clk/qcom/clk-smd-rpm.c +++ b/drivers/clk/qcom/clk-smd-rpm.c @@ -623,6 +623,175 @@ static const struct rpm_smd_clk_desc rpm_clk_msm8976 = { .num_clks = ARRAY_SIZE(msm8976_clks), }; +/* msm8992 */ +DEFINE_CLK_SMD_RPM(msm8992, pnoc_clk, pnoc_a_clk, QCOM_SMD_RPM_BUS_CLK, 0); +DEFINE_CLK_SMD_RPM(msm8992, ocmemgx_clk, ocmemgx_a_clk, QCOM_SMD_RPM_MEM_CLK, 2); +DEFINE_CLK_SMD_RPM(msm8992, bimc_clk, bimc_a_clk, QCOM_SMD_RPM_MEM_CLK, 0); +DEFINE_CLK_SMD_RPM(msm8992, cnoc_clk, cnoc_a_clk, QCOM_SMD_RPM_BUS_CLK, 2); +DEFINE_CLK_SMD_RPM(msm8992, gfx3d_clk_src, gfx3d_a_clk_src, QCOM_SMD_RPM_MEM_CLK, 1); +DEFINE_CLK_SMD_RPM(msm8992, snoc_clk, snoc_a_clk, QCOM_SMD_RPM_BUS_CLK, 1); +DEFINE_CLK_SMD_RPM_XO_BUFFER(msm8992, bb_clk1, bb_clk1_a, 1); +DEFINE_CLK_SMD_RPM_XO_BUFFER_PINCTRL(msm8992, bb_clk1_pin, bb_clk1_a_pin, 1); +DEFINE_CLK_SMD_RPM_XO_BUFFER(msm8992, bb_clk2, bb_clk2_a, 2); +DEFINE_CLK_SMD_RPM_XO_BUFFER_PINCTRL(msm8992, bb_clk2_pin, bb_clk2_a_pin, 2); + +DEFINE_CLK_SMD_RPM_XO_BUFFER(msm8992, div_clk1, div_clk1_a, 11); +DEFINE_CLK_SMD_RPM_XO_BUFFER(msm8992, div_clk2, div_clk2_a, 12); +DEFINE_CLK_SMD_RPM_XO_BUFFER(msm8992, div_clk3, div_clk3_a, 13); +DEFINE_CLK_SMD_RPM(msm8992, ipa_clk, ipa_a_clk, QCOM_SMD_RPM_IPA_CLK, 0); +DEFINE_CLK_SMD_RPM_XO_BUFFER(msm8992, ln_bb_clk, ln_bb_a_clk, 8); +DEFINE_CLK_SMD_RPM(msm8992, mmssnoc_ahb_clk, mmssnoc_ahb_a_clk, + QCOM_SMD_RPM_BUS_CLK, 3); +DEFINE_CLK_SMD_RPM_QDSS(msm8992, qdss_clk, qdss_a_clk, + QCOM_SMD_RPM_MISC_CLK, 1); +DEFINE_CLK_SMD_RPM_XO_BUFFER(msm8992, rf_clk1, rf_clk1_a, 4); +DEFINE_CLK_SMD_RPM_XO_BUFFER(msm8992, rf_clk2, rf_clk2_a, 5); +DEFINE_CLK_SMD_RPM_XO_BUFFER_PINCTRL(msm8992, rf_clk1_pin, rf_clk1_a_pin, 4); +DEFINE_CLK_SMD_RPM_XO_BUFFER_PINCTRL(msm8992, rf_clk2_pin, rf_clk2_a_pin, 5); + +DEFINE_CLK_SMD_RPM(msm8992, ce1_clk, ce1_a_clk, QCOM_SMD_RPM_CE_CLK, 0); +DEFINE_CLK_SMD_RPM(msm8992, ce2_clk, ce2_a_clk, QCOM_SMD_RPM_CE_CLK, 1); + +static struct clk_smd_rpm *msm8992_clks[] = { + [RPM_SMD_PNOC_CLK] = &msm8992_pnoc_clk, + [RPM_SMD_PNOC_A_CLK] = &msm8992_pnoc_a_clk, + [RPM_SMD_OCMEMGX_CLK] = &msm8992_ocmemgx_clk, + [RPM_SMD_OCMEMGX_A_CLK] = &msm8992_ocmemgx_a_clk, + [RPM_SMD_BIMC_CLK] = &msm8992_bimc_clk, + [RPM_SMD_BIMC_A_CLK] = &msm8992_bimc_a_clk, + [RPM_SMD_CNOC_CLK] = &msm8992_cnoc_clk, + [RPM_SMD_CNOC_A_CLK] = &msm8992_cnoc_a_clk, + [RPM_SMD_GFX3D_CLK_SRC] = &msm8992_gfx3d_clk_src, + [RPM_SMD_GFX3D_A_CLK_SRC] = &msm8992_gfx3d_a_clk_src, + [RPM_SMD_SNOC_CLK] = &msm8992_snoc_clk, + [RPM_SMD_SNOC_A_CLK] = &msm8992_snoc_a_clk, + [RPM_SMD_BB_CLK1] = &msm8992_bb_clk1, + [RPM_SMD_BB_CLK1_A] = &msm8992_bb_clk1_a, + [RPM_SMD_BB_CLK1_PIN] = &msm8992_bb_clk1_pin, + [RPM_SMD_BB_CLK1_A_PIN] = &msm8992_bb_clk1_a_pin, + [RPM_SMD_BB_CLK2] = &msm8992_bb_clk2, + [RPM_SMD_BB_CLK2_A] = &msm8992_bb_clk2_a, + [RPM_SMD_BB_CLK2_PIN] = &msm8992_bb_clk2_pin, + [RPM_SMD_BB_CLK2_A_PIN] = &msm8992_bb_clk2_a_pin, + [RPM_SMD_DIV_CLK1] = &msm8992_div_clk1, + [RPM_SMD_DIV_A_CLK1] = &msm8992_div_clk1_a, + [RPM_SMD_DIV_CLK2] = &msm8992_div_clk2, + [RPM_SMD_DIV_A_CLK2] = &msm8992_div_clk2_a, + [RPM_SMD_DIV_CLK3] = &msm8992_div_clk3, + [RPM_SMD_DIV_A_CLK3] = &msm8992_div_clk3_a, + [RPM_SMD_IPA_CLK] = &msm8992_ipa_clk, + [RPM_SMD_IPA_A_CLK] = &msm8992_ipa_a_clk, + [RPM_SMD_LN_BB_CLK] = &msm8992_ln_bb_clk, + [RPM_SMD_LN_BB_A_CLK] = &msm8992_ln_bb_a_clk, + [RPM_SMD_MMSSNOC_AHB_CLK] = &msm8992_mmssnoc_ahb_clk, + [RPM_SMD_MMSSNOC_AHB_A_CLK] = &msm8992_mmssnoc_ahb_a_clk, + [RPM_SMD_QDSS_CLK] = &msm8992_qdss_clk, + [RPM_SMD_QDSS_A_CLK] = &msm8992_qdss_a_clk, + [RPM_SMD_RF_CLK1] = &msm8992_rf_clk1, + [RPM_SMD_RF_CLK1_A] = &msm8992_rf_clk1_a, + [RPM_SMD_RF_CLK2] = &msm8992_rf_clk2, + [RPM_SMD_RF_CLK2_A] = &msm8992_rf_clk2_a, + [RPM_SMD_RF_CLK1_PIN] = &msm8992_rf_clk1_pin, + [RPM_SMD_RF_CLK1_A_PIN] = &msm8992_rf_clk1_a_pin, + [RPM_SMD_RF_CLK2_PIN] = &msm8992_rf_clk2_pin, + [RPM_SMD_RF_CLK2_A_PIN] = &msm8992_rf_clk2_a_pin, + [RPM_SMD_CE1_CLK] = &msm8992_ce1_clk, + [RPM_SMD_CE1_A_CLK] = &msm8992_ce1_a_clk, + [RPM_SMD_CE2_CLK] = &msm8992_ce2_clk, + [RPM_SMD_CE2_A_CLK] = &msm8992_ce2_a_clk, +}; + +static const struct rpm_smd_clk_desc rpm_clk_msm8992 = { + .clks = msm8992_clks, + .num_clks = ARRAY_SIZE(msm8992_clks), +}; + +/* msm8994 */ +DEFINE_CLK_SMD_RPM(msm8994, pnoc_clk, pnoc_a_clk, QCOM_SMD_RPM_BUS_CLK, 0); +DEFINE_CLK_SMD_RPM(msm8994, ocmemgx_clk, ocmemgx_a_clk, QCOM_SMD_RPM_MEM_CLK, 2); +DEFINE_CLK_SMD_RPM(msm8994, bimc_clk, bimc_a_clk, QCOM_SMD_RPM_MEM_CLK, 0); +DEFINE_CLK_SMD_RPM(msm8994, cnoc_clk, cnoc_a_clk, QCOM_SMD_RPM_BUS_CLK, 2); +DEFINE_CLK_SMD_RPM(msm8994, gfx3d_clk_src, gfx3d_a_clk_src, QCOM_SMD_RPM_MEM_CLK, 1); +DEFINE_CLK_SMD_RPM(msm8994, snoc_clk, snoc_a_clk, QCOM_SMD_RPM_BUS_CLK, 1); +DEFINE_CLK_SMD_RPM_XO_BUFFER(msm8994, bb_clk1, bb_clk1_a, 1); +DEFINE_CLK_SMD_RPM_XO_BUFFER_PINCTRL(msm8994, bb_clk1_pin, bb_clk1_a_pin, 1); +DEFINE_CLK_SMD_RPM_XO_BUFFER(msm8994, bb_clk2, bb_clk2_a, 2); +DEFINE_CLK_SMD_RPM_XO_BUFFER_PINCTRL(msm8994, bb_clk2_pin, bb_clk2_a_pin, 2); + +DEFINE_CLK_SMD_RPM_XO_BUFFER(msm8994, div_clk1, div_clk1_a, 11); +DEFINE_CLK_SMD_RPM_XO_BUFFER(msm8994, div_clk2, div_clk2_a, 12); +DEFINE_CLK_SMD_RPM_XO_BUFFER(msm8994, div_clk3, div_clk3_a, 13); +DEFINE_CLK_SMD_RPM(msm8994, ipa_clk, ipa_a_clk, QCOM_SMD_RPM_IPA_CLK, 0); +DEFINE_CLK_SMD_RPM_XO_BUFFER(msm8994, ln_bb_clk, ln_bb_a_clk, 8); +DEFINE_CLK_SMD_RPM(msm8994, mmssnoc_ahb_clk, mmssnoc_ahb_a_clk, + QCOM_SMD_RPM_BUS_CLK, 3); +DEFINE_CLK_SMD_RPM_QDSS(msm8994, qdss_clk, qdss_a_clk, + QCOM_SMD_RPM_MISC_CLK, 1); +DEFINE_CLK_SMD_RPM_XO_BUFFER(msm8994, rf_clk1, rf_clk1_a, 4); +DEFINE_CLK_SMD_RPM_XO_BUFFER(msm8994, rf_clk2, rf_clk2_a, 5); +DEFINE_CLK_SMD_RPM_XO_BUFFER_PINCTRL(msm8994, rf_clk1_pin, rf_clk1_a_pin, 4); +DEFINE_CLK_SMD_RPM_XO_BUFFER_PINCTRL(msm8994, rf_clk2_pin, rf_clk2_a_pin, 5); + +DEFINE_CLK_SMD_RPM(msm8994, ce1_clk, ce1_a_clk, QCOM_SMD_RPM_CE_CLK, 0); +DEFINE_CLK_SMD_RPM(msm8994, ce2_clk, ce2_a_clk, QCOM_SMD_RPM_CE_CLK, 1); +DEFINE_CLK_SMD_RPM(msm8994, ce3_clk, ce3_a_clk, QCOM_SMD_RPM_CE_CLK, 2); + +static struct clk_smd_rpm *msm8994_clks[] = { + [RPM_SMD_PNOC_CLK] = &msm8994_pnoc_clk, + [RPM_SMD_PNOC_A_CLK] = &msm8994_pnoc_a_clk, + [RPM_SMD_OCMEMGX_CLK] = &msm8994_ocmemgx_clk, + [RPM_SMD_OCMEMGX_A_CLK] = &msm8994_ocmemgx_a_clk, + [RPM_SMD_BIMC_CLK] = &msm8994_bimc_clk, + [RPM_SMD_BIMC_A_CLK] = &msm8994_bimc_a_clk, + [RPM_SMD_CNOC_CLK] = &msm8994_cnoc_clk, + [RPM_SMD_CNOC_A_CLK] = &msm8994_cnoc_a_clk, + [RPM_SMD_GFX3D_CLK_SRC] = &msm8994_gfx3d_clk_src, + [RPM_SMD_GFX3D_A_CLK_SRC] = &msm8994_gfx3d_a_clk_src, + [RPM_SMD_SNOC_CLK] = &msm8994_snoc_clk, + [RPM_SMD_SNOC_A_CLK] = &msm8994_snoc_a_clk, + [RPM_SMD_BB_CLK1] = &msm8994_bb_clk1, + [RPM_SMD_BB_CLK1_A] = &msm8994_bb_clk1_a, + [RPM_SMD_BB_CLK1_PIN] = &msm8994_bb_clk1_pin, + [RPM_SMD_BB_CLK1_A_PIN] = &msm8994_bb_clk1_a_pin, + [RPM_SMD_BB_CLK2] = &msm8994_bb_clk2, + [RPM_SMD_BB_CLK2_A] = &msm8994_bb_clk2_a, + [RPM_SMD_BB_CLK2_PIN] = &msm8994_bb_clk2_pin, + [RPM_SMD_BB_CLK2_A_PIN] = &msm8994_bb_clk2_a_pin, + [RPM_SMD_DIV_CLK1] = &msm8994_div_clk1, + [RPM_SMD_DIV_A_CLK1] = &msm8994_div_clk1_a, + [RPM_SMD_DIV_CLK2] = &msm8994_div_clk2, + [RPM_SMD_DIV_A_CLK2] = &msm8994_div_clk2_a, + [RPM_SMD_DIV_CLK3] = &msm8994_div_clk3, + [RPM_SMD_DIV_A_CLK3] = &msm8994_div_clk3_a, + [RPM_SMD_IPA_CLK] = &msm8994_ipa_clk, + [RPM_SMD_IPA_A_CLK] = &msm8994_ipa_a_clk, + [RPM_SMD_LN_BB_CLK] = &msm8994_ln_bb_clk, + [RPM_SMD_LN_BB_A_CLK] = &msm8994_ln_bb_a_clk, + [RPM_SMD_MMSSNOC_AHB_CLK] = &msm8994_mmssnoc_ahb_clk, + [RPM_SMD_MMSSNOC_AHB_A_CLK] = &msm8994_mmssnoc_ahb_a_clk, + [RPM_SMD_QDSS_CLK] = &msm8994_qdss_clk, + [RPM_SMD_QDSS_A_CLK] = &msm8994_qdss_a_clk, + [RPM_SMD_RF_CLK1] = &msm8994_rf_clk1, + [RPM_SMD_RF_CLK1_A] = &msm8994_rf_clk1_a, + [RPM_SMD_RF_CLK2] = &msm8994_rf_clk2, + [RPM_SMD_RF_CLK2_A] = &msm8994_rf_clk2_a, + [RPM_SMD_RF_CLK1_PIN] = &msm8994_rf_clk1_pin, + [RPM_SMD_RF_CLK1_A_PIN] = &msm8994_rf_clk1_a_pin, + [RPM_SMD_RF_CLK2_PIN] = &msm8994_rf_clk2_pin, + [RPM_SMD_RF_CLK2_A_PIN] = &msm8994_rf_clk2_a_pin, + [RPM_SMD_CE1_CLK] = &msm8994_ce1_clk, + [RPM_SMD_CE1_A_CLK] = &msm8994_ce1_a_clk, + [RPM_SMD_CE2_CLK] = &msm8994_ce2_clk, + [RPM_SMD_CE2_A_CLK] = &msm8994_ce2_a_clk, + [RPM_SMD_CE3_CLK] = &msm8994_ce3_clk, + [RPM_SMD_CE3_A_CLK] = &msm8994_ce3_a_clk, +}; + +static const struct rpm_smd_clk_desc rpm_clk_msm8994 = { + .clks = msm8994_clks, + .num_clks = ARRAY_SIZE(msm8994_clks), +}; + /* msm8996 */ DEFINE_CLK_SMD_RPM(msm8996, pcnoc_clk, pcnoc_a_clk, QCOM_SMD_RPM_BUS_CLK, 0); DEFINE_CLK_SMD_RPM(msm8996, snoc_clk, snoc_a_clk, QCOM_SMD_RPM_BUS_CLK, 1); @@ -895,6 +1064,8 @@ static const struct of_device_id rpm_smd_clk_match_table[] = { { .compatible = "qcom,rpmcc-msm8936", .data = &rpm_clk_msm8936 }, { .compatible = "qcom,rpmcc-msm8974", .data = &rpm_clk_msm8974 }, { .compatible = "qcom,rpmcc-msm8976", .data = &rpm_clk_msm8976 }, + { .compatible = "qcom,rpmcc-msm8992", .data = &rpm_clk_msm8992 }, + { .compatible = "qcom,rpmcc-msm8994", .data = &rpm_clk_msm8994 }, { .compatible = "qcom,rpmcc-msm8996", .data = &rpm_clk_msm8996 }, { .compatible = "qcom,rpmcc-msm8998", .data = &rpm_clk_msm8998 }, { .compatible = "qcom,rpmcc-qcs404", .data = &rpm_clk_qcs404 }, diff --git a/include/dt-bindings/clock/qcom,rpmcc.h b/include/dt-bindings/clock/qcom,rpmcc.h index e98ed70d91b38..8aaba7cd95895 100644 --- a/include/dt-bindings/clock/qcom,rpmcc.h +++ b/include/dt-bindings/clock/qcom,rpmcc.h @@ -145,5 +145,9 @@ #define RPM_SMD_LN_BB_CLK2_A_PIN 99 #define RPM_SMD_SYSMMNOC_CLK 100 #define RPM_SMD_SYSMMNOC_A_CLK 101 +#define RPM_SMD_CE2_CLK 102 +#define RPM_SMD_CE2_A_CLK 103 +#define RPM_SMD_CE3_CLK 104 +#define RPM_SMD_CE3_A_CLK 105 #endif -- GitLab From b545b9c393cb4486a7b70ef2fd8d0c8451872342 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Sun, 14 Jun 2020 14:51:40 +0300 Subject: [PATCH 0377/1476] clk: Drop duplicate selection in Kconfig There is no need to select RATIONAL in individual drivers, since common clock symbol does it already. Signed-off-by: Andy Shevchenko Link: https://lore.kernel.org/r/20200614115140.41262-1-andriy.shevchenko@linux.intel.com Signed-off-by: Stephen Boyd --- drivers/clk/Kconfig | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig index 69934c0c3dd85..42301ebf06e96 100644 --- a/drivers/clk/Kconfig +++ b/drivers/clk/Kconfig @@ -120,7 +120,6 @@ config COMMON_CLK_SI5351 tristate "Clock driver for SiLabs 5351A/B/C" depends on I2C select REGMAP_I2C - select RATIONAL help This driver supports Silicon Labs 5351A/B/C programmable clock generators. @@ -162,7 +161,6 @@ config COMMON_CLK_CDCE706 tristate "Clock driver for TI CDCE706 clock synthesizer" depends on I2C select REGMAP_I2C - select RATIONAL help This driver supports TI CDCE706 programmable 3-PLL clock synthesizer. -- GitLab From 668a8187e8b7875d515eacab88cef7f97fb4a1b3 Mon Sep 17 00:00:00 2001 From: Marian-Cristian Rotariu Date: Tue, 7 Jul 2020 17:18:08 +0100 Subject: [PATCH 0378/1476] dt-bindings: clock: renesas,cpg-mssr: Document r8a774e1 Add binding documentation for the RZ/G2H (R8A774E1) Clock Pulse Generator driver. Signed-off-by: Marian-Cristian Rotariu Signed-off-by: Lad Prabhakar Link: https://lore.kernel.org/r/1594138692-16816-9-git-send-email-prabhakar.mahadev-lad.rj@bp.renesas.com Signed-off-by: Geert Uytterhoeven --- Documentation/devicetree/bindings/clock/renesas,cpg-mssr.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/clock/renesas,cpg-mssr.yaml b/Documentation/devicetree/bindings/clock/renesas,cpg-mssr.yaml index c745bd60719ab..e13aee8ab61ad 100644 --- a/Documentation/devicetree/bindings/clock/renesas,cpg-mssr.yaml +++ b/Documentation/devicetree/bindings/clock/renesas,cpg-mssr.yaml @@ -33,6 +33,7 @@ properties: - renesas,r8a774a1-cpg-mssr # RZ/G2M - renesas,r8a774b1-cpg-mssr # RZ/G2N - renesas,r8a774c0-cpg-mssr # RZ/G2E + - renesas,r8a774e1-cpg-mssr # RZ/G2H - renesas,r8a7790-cpg-mssr # R-Car H2 - renesas,r8a7791-cpg-mssr # R-Car M2-W - renesas,r8a7792-cpg-mssr # R-Car V2H -- GitLab From c8a53fa1d211936ddcabf82ff991c18672b3d63a Mon Sep 17 00:00:00 2001 From: Marian-Cristian Rotariu Date: Tue, 7 Jul 2020 17:18:10 +0100 Subject: [PATCH 0379/1476] clk: renesas: cpg-mssr: Add r8a774e1 support Add support for the RZ/G2H (R8A774E1) SoC to the Renesas Clock Pulse Generator / Module Standby and Software Reset driver. Signed-off-by: Marian-Cristian Rotariu Signed-off-by: Lad Prabhakar Link: https://lore.kernel.org/r/1594138692-16816-11-git-send-email-prabhakar.mahadev-lad.rj@bp.renesas.com [geert: Mark RWDT critical, cfr. commit f23f1101ad0ef1ac ("clk: renesas: rcar-gen3: Mark RWDT clocks as critical")] Signed-off-by: Geert Uytterhoeven --- drivers/clk/renesas/Kconfig | 5 + drivers/clk/renesas/Makefile | 1 + drivers/clk/renesas/r8a774e1-cpg-mssr.c | 349 ++++++++++++++++++++++++ drivers/clk/renesas/renesas-cpg-mssr.c | 6 + drivers/clk/renesas/renesas-cpg-mssr.h | 1 + 5 files changed, 362 insertions(+) create mode 100644 drivers/clk/renesas/r8a774e1-cpg-mssr.c diff --git a/drivers/clk/renesas/Kconfig b/drivers/clk/renesas/Kconfig index 9eb79bf906430..28e8730ce2632 100644 --- a/drivers/clk/renesas/Kconfig +++ b/drivers/clk/renesas/Kconfig @@ -15,6 +15,7 @@ config CLK_RENESAS select CLK_R8A774A1 if ARCH_R8A774A1 select CLK_R8A774B1 if ARCH_R8A774B1 select CLK_R8A774C0 if ARCH_R8A774C0 + select CLK_R8A774E1 if ARCH_R8A774E1 select CLK_R8A7778 if ARCH_R8A7778 select CLK_R8A7779 if ARCH_R8A7779 select CLK_R8A7790 if ARCH_R8A7790 @@ -84,6 +85,10 @@ config CLK_R8A774C0 bool "RZ/G2E clock support" if COMPILE_TEST select CLK_RCAR_GEN3_CPG +config CLK_R8A774E1 + bool "RZ/G2H clock support" if COMPILE_TEST + select CLK_RCAR_GEN3_CPG + config CLK_R8A7778 bool "R-Car M1A clock support" if COMPILE_TEST select CLK_RENESAS_CPG_MSTP diff --git a/drivers/clk/renesas/Makefile b/drivers/clk/renesas/Makefile index a4066f9b34ef3..c7c03ab9a6a3f 100644 --- a/drivers/clk/renesas/Makefile +++ b/drivers/clk/renesas/Makefile @@ -12,6 +12,7 @@ obj-$(CONFIG_CLK_R8A77470) += r8a77470-cpg-mssr.o obj-$(CONFIG_CLK_R8A774A1) += r8a774a1-cpg-mssr.o obj-$(CONFIG_CLK_R8A774B1) += r8a774b1-cpg-mssr.o obj-$(CONFIG_CLK_R8A774C0) += r8a774c0-cpg-mssr.o +obj-$(CONFIG_CLK_R8A774E1) += r8a774e1-cpg-mssr.o obj-$(CONFIG_CLK_R8A7778) += clk-r8a7778.o obj-$(CONFIG_CLK_R8A7779) += clk-r8a7779.o obj-$(CONFIG_CLK_R8A7790) += r8a7790-cpg-mssr.o diff --git a/drivers/clk/renesas/r8a774e1-cpg-mssr.c b/drivers/clk/renesas/r8a774e1-cpg-mssr.c new file mode 100644 index 0000000000000..b96c486abb448 --- /dev/null +++ b/drivers/clk/renesas/r8a774e1-cpg-mssr.c @@ -0,0 +1,349 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * r8a774e1 Clock Pulse Generator / Module Standby and Software Reset + * + * Copyright (C) 2020 Renesas Electronics Corp. + * + * Based on r8a7795-cpg-mssr.c + * + * Copyright (C) 2015 Glider bvba + */ + +#include +#include +#include +#include + +#include + +#include "renesas-cpg-mssr.h" +#include "rcar-gen3-cpg.h" + +enum clk_ids { + /* Core Clock Outputs exported to DT */ + LAST_DT_CORE_CLK = R8A774E1_CLK_CANFD, + + /* External Input Clocks */ + CLK_EXTAL, + CLK_EXTALR, + + /* Internal Core Clocks */ + CLK_MAIN, + CLK_PLL0, + CLK_PLL1, + CLK_PLL2, + CLK_PLL3, + CLK_PLL4, + CLK_PLL1_DIV2, + CLK_PLL1_DIV4, + CLK_S0, + CLK_S1, + CLK_S2, + CLK_S3, + CLK_SDSRC, + CLK_RPCSRC, + CLK_RINT, + + /* Module Clocks */ + MOD_CLK_BASE +}; + +static const struct cpg_core_clk r8a774e1_core_clks[] __initconst = { + /* External Clock Inputs */ + DEF_INPUT("extal", CLK_EXTAL), + DEF_INPUT("extalr", CLK_EXTALR), + + /* Internal Core Clocks */ + DEF_BASE(".main", CLK_MAIN, CLK_TYPE_GEN3_MAIN, CLK_EXTAL), + DEF_BASE(".pll0", CLK_PLL0, CLK_TYPE_GEN3_PLL0, CLK_MAIN), + DEF_BASE(".pll1", CLK_PLL1, CLK_TYPE_GEN3_PLL1, CLK_MAIN), + DEF_BASE(".pll2", CLK_PLL2, CLK_TYPE_GEN3_PLL2, CLK_MAIN), + DEF_BASE(".pll3", CLK_PLL3, CLK_TYPE_GEN3_PLL3, CLK_MAIN), + DEF_BASE(".pll4", CLK_PLL4, CLK_TYPE_GEN3_PLL4, CLK_MAIN), + + DEF_FIXED(".pll1_div2", CLK_PLL1_DIV2, CLK_PLL1, 2, 1), + DEF_FIXED(".pll1_div4", CLK_PLL1_DIV4, CLK_PLL1_DIV2, 2, 1), + DEF_FIXED(".s0", CLK_S0, CLK_PLL1_DIV2, 2, 1), + DEF_FIXED(".s1", CLK_S1, CLK_PLL1_DIV2, 3, 1), + DEF_FIXED(".s2", CLK_S2, CLK_PLL1_DIV2, 4, 1), + DEF_FIXED(".s3", CLK_S3, CLK_PLL1_DIV2, 6, 1), + DEF_FIXED(".sdsrc", CLK_SDSRC, CLK_PLL1_DIV2, 2, 1), + DEF_BASE(".rpcsrc", CLK_RPCSRC, CLK_TYPE_GEN3_RPCSRC, CLK_PLL1), + + DEF_BASE("rpc", R8A774E1_CLK_RPC, CLK_TYPE_GEN3_RPC, + CLK_RPCSRC), + DEF_BASE("rpcd2", R8A774E1_CLK_RPCD2, CLK_TYPE_GEN3_RPCD2, + R8A774E1_CLK_RPC), + + DEF_GEN3_OSC(".r", CLK_RINT, CLK_EXTAL, 32), + + /* Core Clock Outputs */ + DEF_GEN3_Z("z", R8A774E1_CLK_Z, CLK_TYPE_GEN3_Z, CLK_PLL0, 2, 8), + DEF_GEN3_Z("z2", R8A774E1_CLK_Z2, CLK_TYPE_GEN3_Z, CLK_PLL2, 2, 0), + DEF_FIXED("ztr", R8A774E1_CLK_ZTR, CLK_PLL1_DIV2, 6, 1), + DEF_FIXED("ztrd2", R8A774E1_CLK_ZTRD2, CLK_PLL1_DIV2, 12, 1), + DEF_FIXED("zt", R8A774E1_CLK_ZT, CLK_PLL1_DIV2, 4, 1), + DEF_FIXED("zx", R8A774E1_CLK_ZX, CLK_PLL1_DIV2, 2, 1), + DEF_FIXED("s0d1", R8A774E1_CLK_S0D1, CLK_S0, 1, 1), + DEF_FIXED("s0d2", R8A774E1_CLK_S0D2, CLK_S0, 2, 1), + DEF_FIXED("s0d3", R8A774E1_CLK_S0D3, CLK_S0, 3, 1), + DEF_FIXED("s0d4", R8A774E1_CLK_S0D4, CLK_S0, 4, 1), + DEF_FIXED("s0d6", R8A774E1_CLK_S0D6, CLK_S0, 6, 1), + DEF_FIXED("s0d8", R8A774E1_CLK_S0D8, CLK_S0, 8, 1), + DEF_FIXED("s0d12", R8A774E1_CLK_S0D12, CLK_S0, 12, 1), + DEF_FIXED("s1d2", R8A774E1_CLK_S1D2, CLK_S1, 2, 1), + DEF_FIXED("s1d4", R8A774E1_CLK_S1D4, CLK_S1, 4, 1), + DEF_FIXED("s2d1", R8A774E1_CLK_S2D1, CLK_S2, 1, 1), + DEF_FIXED("s2d2", R8A774E1_CLK_S2D2, CLK_S2, 2, 1), + DEF_FIXED("s2d4", R8A774E1_CLK_S2D4, CLK_S2, 4, 1), + DEF_FIXED("s3d1", R8A774E1_CLK_S3D1, CLK_S3, 1, 1), + DEF_FIXED("s3d2", R8A774E1_CLK_S3D2, CLK_S3, 2, 1), + DEF_FIXED("s3d4", R8A774E1_CLK_S3D4, CLK_S3, 4, 1), + + DEF_GEN3_SD("sd0", R8A774E1_CLK_SD0, CLK_SDSRC, 0x074), + DEF_GEN3_SD("sd1", R8A774E1_CLK_SD1, CLK_SDSRC, 0x078), + DEF_GEN3_SD("sd2", R8A774E1_CLK_SD2, CLK_SDSRC, 0x268), + DEF_GEN3_SD("sd3", R8A774E1_CLK_SD3, CLK_SDSRC, 0x26c), + + DEF_FIXED("cl", R8A774E1_CLK_CL, CLK_PLL1_DIV2, 48, 1), + DEF_FIXED("cr", R8A774E1_CLK_CR, CLK_PLL1_DIV4, 2, 1), + DEF_FIXED("cp", R8A774E1_CLK_CP, CLK_EXTAL, 2, 1), + DEF_FIXED("cpex", R8A774E1_CLK_CPEX, CLK_EXTAL, 2, 1), + + DEF_DIV6P1("canfd", R8A774E1_CLK_CANFD, CLK_PLL1_DIV4, 0x244), + DEF_DIV6P1("csi0", R8A774E1_CLK_CSI0, CLK_PLL1_DIV4, 0x00c), + DEF_DIV6P1("mso", R8A774E1_CLK_MSO, CLK_PLL1_DIV4, 0x014), + DEF_DIV6P1("hdmi", R8A774E1_CLK_HDMI, CLK_PLL1_DIV4, 0x250), + + DEF_GEN3_OSC("osc", R8A774E1_CLK_OSC, CLK_EXTAL, 8), + + DEF_BASE("r", R8A774E1_CLK_R, CLK_TYPE_GEN3_R, CLK_RINT), +}; + +static const struct mssr_mod_clk r8a774e1_mod_clks[] __initconst = { + DEF_MOD("fdp1-1", 118, R8A774E1_CLK_S0D1), + DEF_MOD("fdp1-0", 119, R8A774E1_CLK_S0D1), + DEF_MOD("tmu4", 121, R8A774E1_CLK_S0D6), + DEF_MOD("tmu3", 122, R8A774E1_CLK_S3D2), + DEF_MOD("tmu2", 123, R8A774E1_CLK_S3D2), + DEF_MOD("tmu1", 124, R8A774E1_CLK_S3D2), + DEF_MOD("tmu0", 125, R8A774E1_CLK_CP), + DEF_MOD("vcplf", 130, R8A774E1_CLK_S2D1), + DEF_MOD("vdpb", 131, R8A774E1_CLK_S2D1), + DEF_MOD("scif5", 202, R8A774E1_CLK_S3D4), + DEF_MOD("scif4", 203, R8A774E1_CLK_S3D4), + DEF_MOD("scif3", 204, R8A774E1_CLK_S3D4), + DEF_MOD("scif1", 206, R8A774E1_CLK_S3D4), + DEF_MOD("scif0", 207, R8A774E1_CLK_S3D4), + DEF_MOD("msiof3", 208, R8A774E1_CLK_MSO), + DEF_MOD("msiof2", 209, R8A774E1_CLK_MSO), + DEF_MOD("msiof1", 210, R8A774E1_CLK_MSO), + DEF_MOD("msiof0", 211, R8A774E1_CLK_MSO), + DEF_MOD("sys-dmac2", 217, R8A774E1_CLK_S3D1), + DEF_MOD("sys-dmac1", 218, R8A774E1_CLK_S3D1), + DEF_MOD("sys-dmac0", 219, R8A774E1_CLK_S0D3), + DEF_MOD("cmt3", 300, R8A774E1_CLK_R), + DEF_MOD("cmt2", 301, R8A774E1_CLK_R), + DEF_MOD("cmt1", 302, R8A774E1_CLK_R), + DEF_MOD("cmt0", 303, R8A774E1_CLK_R), + DEF_MOD("tpu0", 304, R8A774E1_CLK_S3D4), + DEF_MOD("scif2", 310, R8A774E1_CLK_S3D4), + DEF_MOD("sdif3", 311, R8A774E1_CLK_SD3), + DEF_MOD("sdif2", 312, R8A774E1_CLK_SD2), + DEF_MOD("sdif1", 313, R8A774E1_CLK_SD1), + DEF_MOD("sdif0", 314, R8A774E1_CLK_SD0), + DEF_MOD("pcie1", 318, R8A774E1_CLK_S3D1), + DEF_MOD("pcie0", 319, R8A774E1_CLK_S3D1), + DEF_MOD("usb3-if0", 328, R8A774E1_CLK_S3D1), + DEF_MOD("usb-dmac0", 330, R8A774E1_CLK_S3D1), + DEF_MOD("usb-dmac1", 331, R8A774E1_CLK_S3D1), + DEF_MOD("rwdt", 402, R8A774E1_CLK_R), + DEF_MOD("intc-ex", 407, R8A774E1_CLK_CP), + DEF_MOD("intc-ap", 408, R8A774E1_CLK_S0D3), + DEF_MOD("audmac1", 501, R8A774E1_CLK_S1D2), + DEF_MOD("audmac0", 502, R8A774E1_CLK_S1D2), + DEF_MOD("hscif4", 516, R8A774E1_CLK_S3D1), + DEF_MOD("hscif3", 517, R8A774E1_CLK_S3D1), + DEF_MOD("hscif2", 518, R8A774E1_CLK_S3D1), + DEF_MOD("hscif1", 519, R8A774E1_CLK_S3D1), + DEF_MOD("hscif0", 520, R8A774E1_CLK_S3D1), + DEF_MOD("thermal", 522, R8A774E1_CLK_CP), + DEF_MOD("pwm", 523, R8A774E1_CLK_S0D12), + DEF_MOD("fcpvd1", 602, R8A774E1_CLK_S0D2), + DEF_MOD("fcpvd0", 603, R8A774E1_CLK_S0D2), + DEF_MOD("fcpvb1", 606, R8A774E1_CLK_S0D1), + DEF_MOD("fcpvb0", 607, R8A774E1_CLK_S0D1), + DEF_MOD("fcpvi1", 610, R8A774E1_CLK_S0D1), + DEF_MOD("fcpvi0", 611, R8A774E1_CLK_S0D1), + DEF_MOD("fcpf1", 614, R8A774E1_CLK_S0D1), + DEF_MOD("fcpf0", 615, R8A774E1_CLK_S0D1), + DEF_MOD("fcpcs", 619, R8A774E1_CLK_S0D1), + DEF_MOD("vspd1", 622, R8A774E1_CLK_S0D2), + DEF_MOD("vspd0", 623, R8A774E1_CLK_S0D2), + DEF_MOD("vspbc", 624, R8A774E1_CLK_S0D1), + DEF_MOD("vspbd", 626, R8A774E1_CLK_S0D1), + DEF_MOD("vspi1", 630, R8A774E1_CLK_S0D1), + DEF_MOD("vspi0", 631, R8A774E1_CLK_S0D1), + DEF_MOD("ehci1", 702, R8A774E1_CLK_S3D2), + DEF_MOD("ehci0", 703, R8A774E1_CLK_S3D2), + DEF_MOD("hsusb", 704, R8A774E1_CLK_S3D2), + DEF_MOD("csi20", 714, R8A774E1_CLK_CSI0), + DEF_MOD("csi40", 716, R8A774E1_CLK_CSI0), + DEF_MOD("du3", 721, R8A774E1_CLK_S2D1), + DEF_MOD("du1", 723, R8A774E1_CLK_S2D1), + DEF_MOD("du0", 724, R8A774E1_CLK_S2D1), + DEF_MOD("lvds", 727, R8A774E1_CLK_S0D4), + DEF_MOD("hdmi0", 729, R8A774E1_CLK_HDMI), + DEF_MOD("vin7", 804, R8A774E1_CLK_S0D2), + DEF_MOD("vin6", 805, R8A774E1_CLK_S0D2), + DEF_MOD("vin5", 806, R8A774E1_CLK_S0D2), + DEF_MOD("vin4", 807, R8A774E1_CLK_S0D2), + DEF_MOD("vin3", 808, R8A774E1_CLK_S0D2), + DEF_MOD("vin2", 809, R8A774E1_CLK_S0D2), + DEF_MOD("vin1", 810, R8A774E1_CLK_S0D2), + DEF_MOD("vin0", 811, R8A774E1_CLK_S0D2), + DEF_MOD("etheravb", 812, R8A774E1_CLK_S0D6), + DEF_MOD("sata0", 815, R8A774E1_CLK_S3D2), + DEF_MOD("gpio7", 905, R8A774E1_CLK_S3D4), + DEF_MOD("gpio6", 906, R8A774E1_CLK_S3D4), + DEF_MOD("gpio5", 907, R8A774E1_CLK_S3D4), + DEF_MOD("gpio4", 908, R8A774E1_CLK_S3D4), + DEF_MOD("gpio3", 909, R8A774E1_CLK_S3D4), + DEF_MOD("gpio2", 910, R8A774E1_CLK_S3D4), + DEF_MOD("gpio1", 911, R8A774E1_CLK_S3D4), + DEF_MOD("gpio0", 912, R8A774E1_CLK_S3D4), + DEF_MOD("can-fd", 914, R8A774E1_CLK_S3D2), + DEF_MOD("can-if1", 915, R8A774E1_CLK_S3D4), + DEF_MOD("can-if0", 916, R8A774E1_CLK_S3D4), + DEF_MOD("rpc-if", 917, R8A774E1_CLK_RPCD2), + DEF_MOD("i2c6", 918, R8A774E1_CLK_S0D6), + DEF_MOD("i2c5", 919, R8A774E1_CLK_S0D6), + DEF_MOD("adg", 922, R8A774E1_CLK_S0D1), + DEF_MOD("i2c-dvfs", 926, R8A774E1_CLK_CP), + DEF_MOD("i2c4", 927, R8A774E1_CLK_S0D6), + DEF_MOD("i2c3", 928, R8A774E1_CLK_S0D6), + DEF_MOD("i2c2", 929, R8A774E1_CLK_S3D2), + DEF_MOD("i2c1", 930, R8A774E1_CLK_S3D2), + DEF_MOD("i2c0", 931, R8A774E1_CLK_S3D2), + DEF_MOD("ssi-all", 1005, R8A774E1_CLK_S3D4), + DEF_MOD("ssi9", 1006, MOD_CLK_ID(1005)), + DEF_MOD("ssi8", 1007, MOD_CLK_ID(1005)), + DEF_MOD("ssi7", 1008, MOD_CLK_ID(1005)), + DEF_MOD("ssi6", 1009, MOD_CLK_ID(1005)), + DEF_MOD("ssi5", 1010, MOD_CLK_ID(1005)), + DEF_MOD("ssi4", 1011, MOD_CLK_ID(1005)), + DEF_MOD("ssi3", 1012, MOD_CLK_ID(1005)), + DEF_MOD("ssi2", 1013, MOD_CLK_ID(1005)), + DEF_MOD("ssi1", 1014, MOD_CLK_ID(1005)), + DEF_MOD("ssi0", 1015, MOD_CLK_ID(1005)), + DEF_MOD("scu-all", 1017, R8A774E1_CLK_S3D4), + DEF_MOD("scu-dvc1", 1018, MOD_CLK_ID(1017)), + DEF_MOD("scu-dvc0", 1019, MOD_CLK_ID(1017)), + DEF_MOD("scu-ctu1-mix1", 1020, MOD_CLK_ID(1017)), + DEF_MOD("scu-ctu0-mix0", 1021, MOD_CLK_ID(1017)), + DEF_MOD("scu-src9", 1022, MOD_CLK_ID(1017)), + DEF_MOD("scu-src8", 1023, MOD_CLK_ID(1017)), + DEF_MOD("scu-src7", 1024, MOD_CLK_ID(1017)), + DEF_MOD("scu-src6", 1025, MOD_CLK_ID(1017)), + DEF_MOD("scu-src5", 1026, MOD_CLK_ID(1017)), + DEF_MOD("scu-src4", 1027, MOD_CLK_ID(1017)), + DEF_MOD("scu-src3", 1028, MOD_CLK_ID(1017)), + DEF_MOD("scu-src2", 1029, MOD_CLK_ID(1017)), + DEF_MOD("scu-src1", 1030, MOD_CLK_ID(1017)), + DEF_MOD("scu-src0", 1031, MOD_CLK_ID(1017)), +}; + +static const unsigned int r8a774e1_crit_mod_clks[] __initconst = { + MOD_CLK_ID(402), /* RWDT */ + MOD_CLK_ID(408), /* INTC-AP (GIC) */ +}; + +/* + * CPG Clock Data + */ + +/* + * MD EXTAL PLL0 PLL1 PLL2 PLL3 PLL4 OSC + * 14 13 19 17 (MHz) + *------------------------------------------------------------------------- + * 0 0 0 0 16.66 x 1 x180 x192 x144 x192 x144 /16 + * 0 0 0 1 16.66 x 1 x180 x192 x144 x128 x144 /16 + * 0 0 1 0 Prohibited setting + * 0 0 1 1 16.66 x 1 x180 x192 x144 x192 x144 /16 + * 0 1 0 0 20 x 1 x150 x160 x120 x160 x120 /19 + * 0 1 0 1 20 x 1 x150 x160 x120 x106 x120 /19 + * 0 1 1 0 Prohibited setting + * 0 1 1 1 20 x 1 x150 x160 x120 x160 x120 /19 + * 1 0 0 0 25 x 1 x120 x128 x96 x128 x96 /24 + * 1 0 0 1 25 x 1 x120 x128 x96 x84 x96 /24 + * 1 0 1 0 Prohibited setting + * 1 0 1 1 25 x 1 x120 x128 x96 x128 x96 /24 + * 1 1 0 0 33.33 / 2 x180 x192 x144 x192 x144 /32 + * 1 1 0 1 33.33 / 2 x180 x192 x144 x128 x144 /32 + * 1 1 1 0 Prohibited setting + * 1 1 1 1 33.33 / 2 x180 x192 x144 x192 x144 /32 + */ +#define CPG_PLL_CONFIG_INDEX(md) ((((md) & BIT(14)) >> 11) | \ + (((md) & BIT(13)) >> 11) | \ + (((md) & BIT(19)) >> 18) | \ + (((md) & BIT(17)) >> 17)) + +static const struct rcar_gen3_cpg_pll_config cpg_pll_configs[16] __initconst = { + /* EXTAL div PLL1 mult/div PLL3 mult/div OSC prediv */ + { 1, 192, 1, 192, 1, 16, }, + { 1, 192, 1, 128, 1, 16, }, + { 0, /* Prohibited setting */ }, + { 1, 192, 1, 192, 1, 16, }, + { 1, 160, 1, 160, 1, 19, }, + { 1, 160, 1, 106, 1, 19, }, + { 0, /* Prohibited setting */ }, + { 1, 160, 1, 160, 1, 19, }, + { 1, 128, 1, 128, 1, 24, }, + { 1, 128, 1, 84, 1, 24, }, + { 0, /* Prohibited setting */ }, + { 1, 128, 1, 128, 1, 24, }, + { 2, 192, 1, 192, 1, 32, }, + { 2, 192, 1, 128, 1, 32, }, + { 0, /* Prohibited setting */ }, + { 2, 192, 1, 192, 1, 32, }, +}; + +static int __init r8a774e1_cpg_mssr_init(struct device *dev) +{ + const struct rcar_gen3_cpg_pll_config *cpg_pll_config; + u32 cpg_mode; + int error; + + error = rcar_rst_read_mode_pins(&cpg_mode); + if (error) + return error; + + cpg_pll_config = &cpg_pll_configs[CPG_PLL_CONFIG_INDEX(cpg_mode)]; + if (!cpg_pll_config->extal_div) { + dev_err(dev, "Prohibited setting (cpg_mode=0x%x)\n", cpg_mode); + return -EINVAL; + } + + return rcar_gen3_cpg_init(cpg_pll_config, CLK_EXTALR, cpg_mode); +} + +const struct cpg_mssr_info r8a774e1_cpg_mssr_info __initconst = { + /* Core Clocks */ + .core_clks = r8a774e1_core_clks, + .num_core_clks = ARRAY_SIZE(r8a774e1_core_clks), + .last_dt_core_clk = LAST_DT_CORE_CLK, + .num_total_core_clks = MOD_CLK_BASE, + + /* Module Clocks */ + .mod_clks = r8a774e1_mod_clks, + .num_mod_clks = ARRAY_SIZE(r8a774e1_mod_clks), + .num_hw_mod_clks = 12 * 32, + + /* Critical Module Clocks */ + .crit_mod_clks = r8a774e1_crit_mod_clks, + .num_crit_mod_clks = ARRAY_SIZE(r8a774e1_crit_mod_clks), + + /* Callbacks */ + .init = r8a774e1_cpg_mssr_init, + .cpg_clk_register = rcar_gen3_cpg_clk_register, +}; diff --git a/drivers/clk/renesas/renesas-cpg-mssr.c b/drivers/clk/renesas/renesas-cpg-mssr.c index 4648a829db66d..5a306d28738c2 100644 --- a/drivers/clk/renesas/renesas-cpg-mssr.c +++ b/drivers/clk/renesas/renesas-cpg-mssr.c @@ -721,6 +721,12 @@ static const struct of_device_id cpg_mssr_match[] = { .data = &r8a774c0_cpg_mssr_info, }, #endif +#ifdef CONFIG_CLK_R8A774E1 + { + .compatible = "renesas,r8a774e1-cpg-mssr", + .data = &r8a774e1_cpg_mssr_info, + }, +#endif #ifdef CONFIG_CLK_R8A7790 { .compatible = "renesas,r8a7790-cpg-mssr", diff --git a/drivers/clk/renesas/renesas-cpg-mssr.h b/drivers/clk/renesas/renesas-cpg-mssr.h index 55a18ef0efafc..1cc569484250a 100644 --- a/drivers/clk/renesas/renesas-cpg-mssr.h +++ b/drivers/clk/renesas/renesas-cpg-mssr.h @@ -162,6 +162,7 @@ extern const struct cpg_mssr_info r8a77470_cpg_mssr_info; extern const struct cpg_mssr_info r8a774a1_cpg_mssr_info; extern const struct cpg_mssr_info r8a774b1_cpg_mssr_info; extern const struct cpg_mssr_info r8a774c0_cpg_mssr_info; +extern const struct cpg_mssr_info r8a774e1_cpg_mssr_info; extern const struct cpg_mssr_info r8a7790_cpg_mssr_info; extern const struct cpg_mssr_info r8a7791_cpg_mssr_info; extern const struct cpg_mssr_info r8a7792_cpg_mssr_info; -- GitLab From 0d5c10b4c84d6ae6255129e5f16a0d2119c74334 Mon Sep 17 00:00:00 2001 From: Dave Jiang Date: Fri, 26 Jun 2020 11:11:18 -0700 Subject: [PATCH 0380/1476] dmaengine: idxd: add work queue drain support Add wq drain support. When a wq is being released, it needs to wait for all in-flight operation to complete. A device control function idxd_wq_drain() has been added to facilitate this. A wq drain call is added to the char dev on release to make sure all user operations are complete. A wq drain is also added before the wq is being disabled. A drain command can take an unpredictable period of time. Interrupt support for device commands is added to allow waiting on the command to finish. If a previous command is in progress, the new submitter can block until the current command is finished before proceeding. The interrupt based submission will submit the command and then wait until a command completion interrupt happens to complete. All commands are moved to the interrupt based command submission except for the device reset during probe, which will be polled. Fixes: 42d279f9137a ("dmaengine: idxd: add char driver to expose submission portal to userland") Signed-off-by: Dave Jiang Reviewed-by: Tony Luck Reviewed-by: Dan Williams Link: https://lore.kernel.org/r/159319502515.69593.13451647706946040301.stgit@djiang5-desk3.ch.intel.com Signed-off-by: Vinod Koul --- drivers/dma/idxd/cdev.c | 3 + drivers/dma/idxd/device.c | 155 +++++++++++++++++--------------------- drivers/dma/idxd/idxd.h | 11 ++- drivers/dma/idxd/init.c | 14 ++-- drivers/dma/idxd/irq.c | 41 +++++----- drivers/dma/idxd/sysfs.c | 20 +---- 6 files changed, 112 insertions(+), 132 deletions(-) diff --git a/drivers/dma/idxd/cdev.c b/drivers/dma/idxd/cdev.c index ff49847e37a86..f9fc589662073 100644 --- a/drivers/dma/idxd/cdev.c +++ b/drivers/dma/idxd/cdev.c @@ -104,6 +104,9 @@ static int idxd_cdev_release(struct inode *node, struct file *filep) dev_dbg(dev, "%s called\n", __func__); filep->private_data = NULL; + /* Wait for in-flight operations to complete. */ + idxd_wq_drain(wq); + kfree(ctx); idxd_wq_put(wq); return 0; diff --git a/drivers/dma/idxd/device.c b/drivers/dma/idxd/device.c index 8f05b29e78913..1d8d64508a28b 100644 --- a/drivers/dma/idxd/device.c +++ b/drivers/dma/idxd/device.c @@ -11,8 +11,8 @@ #include "idxd.h" #include "registers.h" -static int idxd_cmd_wait(struct idxd_device *idxd, u32 *status, int timeout); -static int idxd_cmd_send(struct idxd_device *idxd, int cmd_code, u32 operand); +static void idxd_cmd_exec(struct idxd_device *idxd, int cmd_code, u32 operand, + u32 *status); /* Interrupt control bits */ int idxd_mask_msix_vector(struct idxd_device *idxd, int vec_id) @@ -233,21 +233,13 @@ int idxd_wq_enable(struct idxd_wq *wq) struct idxd_device *idxd = wq->idxd; struct device *dev = &idxd->pdev->dev; u32 status; - int rc; - - lockdep_assert_held(&idxd->dev_lock); if (wq->state == IDXD_WQ_ENABLED) { dev_dbg(dev, "WQ %d already enabled\n", wq->id); return -ENXIO; } - rc = idxd_cmd_send(idxd, IDXD_CMD_ENABLE_WQ, wq->id); - if (rc < 0) - return rc; - rc = idxd_cmd_wait(idxd, &status, IDXD_REG_TIMEOUT); - if (rc < 0) - return rc; + idxd_cmd_exec(idxd, IDXD_CMD_ENABLE_WQ, wq->id, &status); if (status != IDXD_CMDSTS_SUCCESS && status != IDXD_CMDSTS_ERR_WQ_ENABLED) { @@ -265,9 +257,7 @@ int idxd_wq_disable(struct idxd_wq *wq) struct idxd_device *idxd = wq->idxd; struct device *dev = &idxd->pdev->dev; u32 status, operand; - int rc; - lockdep_assert_held(&idxd->dev_lock); dev_dbg(dev, "Disabling WQ %d\n", wq->id); if (wq->state != IDXD_WQ_ENABLED) { @@ -276,12 +266,7 @@ int idxd_wq_disable(struct idxd_wq *wq) } operand = BIT(wq->id % 16) | ((wq->id / 16) << 16); - rc = idxd_cmd_send(idxd, IDXD_CMD_DISABLE_WQ, operand); - if (rc < 0) - return rc; - rc = idxd_cmd_wait(idxd, &status, IDXD_REG_TIMEOUT); - if (rc < 0) - return rc; + idxd_cmd_exec(idxd, IDXD_CMD_DISABLE_WQ, operand, &status); if (status != IDXD_CMDSTS_SUCCESS) { dev_dbg(dev, "WQ disable failed: %#x\n", status); @@ -293,6 +278,22 @@ int idxd_wq_disable(struct idxd_wq *wq) return 0; } +void idxd_wq_drain(struct idxd_wq *wq) +{ + struct idxd_device *idxd = wq->idxd; + struct device *dev = &idxd->pdev->dev; + u32 operand; + + if (wq->state != IDXD_WQ_ENABLED) { + dev_dbg(dev, "WQ %d in wrong state: %d\n", wq->id, wq->state); + return; + } + + dev_dbg(dev, "Draining WQ %d\n", wq->id); + operand = BIT(wq->id % 16) | ((wq->id / 16) << 16); + idxd_cmd_exec(idxd, IDXD_CMD_DRAIN_WQ, operand, NULL); +} + int idxd_wq_map_portal(struct idxd_wq *wq) { struct idxd_device *idxd = wq->idxd; @@ -330,66 +331,79 @@ static inline bool idxd_is_enabled(struct idxd_device *idxd) return false; } -static int idxd_cmd_wait(struct idxd_device *idxd, u32 *status, int timeout) +/* + * This is function is only used for reset during probe and will + * poll for completion. Once the device is setup with interrupts, + * all commands will be done via interrupt completion. + */ +void idxd_device_init_reset(struct idxd_device *idxd) { - u32 sts, to = timeout; - - lockdep_assert_held(&idxd->dev_lock); - sts = ioread32(idxd->reg_base + IDXD_CMDSTS_OFFSET); - while (sts & IDXD_CMDSTS_ACTIVE && --to) { - cpu_relax(); - sts = ioread32(idxd->reg_base + IDXD_CMDSTS_OFFSET); - } + struct device *dev = &idxd->pdev->dev; + union idxd_command_reg cmd; + unsigned long flags; - if (to == 0 && sts & IDXD_CMDSTS_ACTIVE) { - dev_warn(&idxd->pdev->dev, "%s timed out!\n", __func__); - *status = 0; - return -EBUSY; - } + memset(&cmd, 0, sizeof(cmd)); + cmd.cmd = IDXD_CMD_RESET_DEVICE; + dev_dbg(dev, "%s: sending reset for init.\n", __func__); + spin_lock_irqsave(&idxd->dev_lock, flags); + iowrite32(cmd.bits, idxd->reg_base + IDXD_CMD_OFFSET); - *status = sts; - return 0; + while (ioread32(idxd->reg_base + IDXD_CMDSTS_OFFSET) & + IDXD_CMDSTS_ACTIVE) + cpu_relax(); + spin_unlock_irqrestore(&idxd->dev_lock, flags); } -static int idxd_cmd_send(struct idxd_device *idxd, int cmd_code, u32 operand) +static void idxd_cmd_exec(struct idxd_device *idxd, int cmd_code, u32 operand, + u32 *status) { union idxd_command_reg cmd; - int rc; - u32 status; - - lockdep_assert_held(&idxd->dev_lock); - rc = idxd_cmd_wait(idxd, &status, IDXD_REG_TIMEOUT); - if (rc < 0) - return rc; + DECLARE_COMPLETION_ONSTACK(done); + unsigned long flags; memset(&cmd, 0, sizeof(cmd)); cmd.cmd = cmd_code; cmd.operand = operand; + cmd.int_req = 1; + + spin_lock_irqsave(&idxd->dev_lock, flags); + wait_event_lock_irq(idxd->cmd_waitq, + !test_bit(IDXD_FLAG_CMD_RUNNING, &idxd->flags), + idxd->dev_lock); + dev_dbg(&idxd->pdev->dev, "%s: sending cmd: %#x op: %#x\n", __func__, cmd_code, operand); + + __set_bit(IDXD_FLAG_CMD_RUNNING, &idxd->flags); + idxd->cmd_done = &done; iowrite32(cmd.bits, idxd->reg_base + IDXD_CMD_OFFSET); - return 0; + /* + * After command submitted, release lock and go to sleep until + * the command completes via interrupt. + */ + spin_unlock_irqrestore(&idxd->dev_lock, flags); + wait_for_completion(&done); + spin_lock_irqsave(&idxd->dev_lock, flags); + if (status) + *status = ioread32(idxd->reg_base + IDXD_CMDSTS_OFFSET); + __clear_bit(IDXD_FLAG_CMD_RUNNING, &idxd->flags); + /* Wake up other pending commands */ + wake_up(&idxd->cmd_waitq); + spin_unlock_irqrestore(&idxd->dev_lock, flags); } int idxd_device_enable(struct idxd_device *idxd) { struct device *dev = &idxd->pdev->dev; - int rc; u32 status; - lockdep_assert_held(&idxd->dev_lock); if (idxd_is_enabled(idxd)) { dev_dbg(dev, "Device already enabled\n"); return -ENXIO; } - rc = idxd_cmd_send(idxd, IDXD_CMD_ENABLE_DEVICE, 0); - if (rc < 0) - return rc; - rc = idxd_cmd_wait(idxd, &status, IDXD_REG_TIMEOUT); - if (rc < 0) - return rc; + idxd_cmd_exec(idxd, IDXD_CMD_ENABLE_DEVICE, 0, &status); /* If the command is successful or if the device was enabled */ if (status != IDXD_CMDSTS_SUCCESS && @@ -405,58 +419,29 @@ int idxd_device_enable(struct idxd_device *idxd) int idxd_device_disable(struct idxd_device *idxd) { struct device *dev = &idxd->pdev->dev; - int rc; u32 status; - lockdep_assert_held(&idxd->dev_lock); if (!idxd_is_enabled(idxd)) { dev_dbg(dev, "Device is not enabled\n"); return 0; } - rc = idxd_cmd_send(idxd, IDXD_CMD_DISABLE_DEVICE, 0); - if (rc < 0) - return rc; - rc = idxd_cmd_wait(idxd, &status, IDXD_REG_TIMEOUT); - if (rc < 0) - return rc; + idxd_cmd_exec(idxd, IDXD_CMD_DISABLE_DEVICE, 0, &status); /* If the command is successful or if the device was disabled */ if (status != IDXD_CMDSTS_SUCCESS && !(status & IDXD_CMDSTS_ERR_DIS_DEV_EN)) { dev_dbg(dev, "%s: err_code: %#x\n", __func__, status); - rc = -ENXIO; - return rc; + return -ENXIO; } idxd->state = IDXD_DEV_CONF_READY; return 0; } -int __idxd_device_reset(struct idxd_device *idxd) -{ - u32 status; - int rc; - - rc = idxd_cmd_send(idxd, IDXD_CMD_RESET_DEVICE, 0); - if (rc < 0) - return rc; - rc = idxd_cmd_wait(idxd, &status, IDXD_REG_TIMEOUT); - if (rc < 0) - return rc; - - return 0; -} - -int idxd_device_reset(struct idxd_device *idxd) +void idxd_device_reset(struct idxd_device *idxd) { - unsigned long flags; - int rc; - - spin_lock_irqsave(&idxd->dev_lock, flags); - rc = __idxd_device_reset(idxd); - spin_unlock_irqrestore(&idxd->dev_lock, flags); - return rc; + idxd_cmd_exec(idxd, IDXD_CMD_RESET_DEVICE, 0, NULL); } /* Device configuration bits */ diff --git a/drivers/dma/idxd/idxd.h b/drivers/dma/idxd/idxd.h index b03a754918ef4..83214e902dd26 100644 --- a/drivers/dma/idxd/idxd.h +++ b/drivers/dma/idxd/idxd.h @@ -142,6 +142,7 @@ enum idxd_device_state { enum idxd_device_flag { IDXD_FLAG_CONFIGURABLE = 0, + IDXD_FLAG_CMD_RUNNING, }; struct idxd_device { @@ -158,6 +159,7 @@ struct idxd_device { void __iomem *reg_base; spinlock_t dev_lock; /* spinlock for device */ + struct completion *cmd_done; struct idxd_group *groups; struct idxd_wq *wqs; struct idxd_engine *engines; @@ -180,12 +182,14 @@ struct idxd_device { int nr_tokens; /* non-reserved tokens */ union sw_err_reg sw_err; - + wait_queue_head_t cmd_waitq; struct msix_entry *msix_entries; int num_wq_irqs; struct idxd_irq_entry *irq_entries; struct dma_device dma_dev; + struct workqueue_struct *wq; + struct work_struct work; }; /* IDXD software descriptor */ @@ -273,10 +277,10 @@ int idxd_mask_msix_vector(struct idxd_device *idxd, int vec_id); int idxd_unmask_msix_vector(struct idxd_device *idxd, int vec_id); /* device control */ +void idxd_device_init_reset(struct idxd_device *idxd); int idxd_device_enable(struct idxd_device *idxd); int idxd_device_disable(struct idxd_device *idxd); -int idxd_device_reset(struct idxd_device *idxd); -int __idxd_device_reset(struct idxd_device *idxd); +void idxd_device_reset(struct idxd_device *idxd); void idxd_device_cleanup(struct idxd_device *idxd); int idxd_device_config(struct idxd_device *idxd); void idxd_device_wqs_clear_state(struct idxd_device *idxd); @@ -286,6 +290,7 @@ int idxd_wq_alloc_resources(struct idxd_wq *wq); void idxd_wq_free_resources(struct idxd_wq *wq); int idxd_wq_enable(struct idxd_wq *wq); int idxd_wq_disable(struct idxd_wq *wq); +void idxd_wq_drain(struct idxd_wq *wq); int idxd_wq_map_portal(struct idxd_wq *wq); void idxd_wq_unmap_portal(struct idxd_wq *wq); diff --git a/drivers/dma/idxd/init.c b/drivers/dma/idxd/init.c index b69839a8ac2c0..c7c61974f20f6 100644 --- a/drivers/dma/idxd/init.c +++ b/drivers/dma/idxd/init.c @@ -146,6 +146,7 @@ static int idxd_setup_internals(struct idxd_device *idxd) struct device *dev = &idxd->pdev->dev; int i; + init_waitqueue_head(&idxd->cmd_waitq); idxd->groups = devm_kcalloc(dev, idxd->max_groups, sizeof(struct idxd_group), GFP_KERNEL); if (!idxd->groups) @@ -182,6 +183,10 @@ static int idxd_setup_internals(struct idxd_device *idxd) idxd->engines[i].id = i; } + idxd->wq = create_workqueue(dev_name(dev)); + if (!idxd->wq) + return -ENOMEM; + return 0; } @@ -277,9 +282,7 @@ static int idxd_probe(struct idxd_device *idxd) int rc; dev_dbg(dev, "%s entered and resetting device\n", __func__); - rc = idxd_device_reset(idxd); - if (rc < 0) - return rc; + idxd_device_init_reset(idxd); dev_dbg(dev, "IDXD reset complete\n"); idxd_read_caps(idxd); @@ -414,11 +417,8 @@ static void idxd_shutdown(struct pci_dev *pdev) int rc, i; struct idxd_irq_entry *irq_entry; int msixcnt = pci_msix_vec_count(pdev); - unsigned long flags; - spin_lock_irqsave(&idxd->dev_lock, flags); rc = idxd_device_disable(idxd); - spin_unlock_irqrestore(&idxd->dev_lock, flags); if (rc) dev_err(&pdev->dev, "Disabling device failed\n"); @@ -434,6 +434,8 @@ static void idxd_shutdown(struct pci_dev *pdev) idxd_flush_pending_llist(irq_entry); idxd_flush_work_list(irq_entry); } + + destroy_workqueue(idxd->wq); } static void idxd_remove(struct pci_dev *pdev) diff --git a/drivers/dma/idxd/irq.c b/drivers/dma/idxd/irq.c index 6510791b9921b..6052765ca3c89 100644 --- a/drivers/dma/idxd/irq.c +++ b/drivers/dma/idxd/irq.c @@ -23,16 +23,13 @@ void idxd_device_wqs_clear_state(struct idxd_device *idxd) } } -static int idxd_restart(struct idxd_device *idxd) +static void idxd_device_reinit(struct work_struct *work) { - int i, rc; - - lockdep_assert_held(&idxd->dev_lock); - - rc = __idxd_device_reset(idxd); - if (rc < 0) - goto out; + struct idxd_device *idxd = container_of(work, struct idxd_device, work); + struct device *dev = &idxd->pdev->dev; + int rc, i; + idxd_device_reset(idxd); rc = idxd_device_config(idxd); if (rc < 0) goto out; @@ -47,19 +44,16 @@ static int idxd_restart(struct idxd_device *idxd) if (wq->state == IDXD_WQ_ENABLED) { rc = idxd_wq_enable(wq); if (rc < 0) { - dev_warn(&idxd->pdev->dev, - "Unable to re-enable wq %s\n", + dev_warn(dev, "Unable to re-enable wq %s\n", dev_name(&wq->conf_dev)); } } } - return 0; + return; out: idxd_device_wqs_clear_state(idxd); - idxd->state = IDXD_DEV_HALTED; - return rc; } irqreturn_t idxd_irq_handler(int vec, void *data) @@ -78,7 +72,7 @@ irqreturn_t idxd_misc_thread(int vec, void *data) struct device *dev = &idxd->pdev->dev; union gensts_reg gensts; u32 cause, val = 0; - int i, rc; + int i; bool err = false; cause = ioread32(idxd->reg_base + IDXD_INTCAUSE_OFFSET); @@ -117,8 +111,8 @@ irqreturn_t idxd_misc_thread(int vec, void *data) } if (cause & IDXD_INTC_CMD) { - /* Driver does use command interrupts */ val |= IDXD_INTC_CMD; + complete(idxd->cmd_done); } if (cause & IDXD_INTC_OCCUPY) { @@ -145,21 +139,24 @@ irqreturn_t idxd_misc_thread(int vec, void *data) gensts.bits = ioread32(idxd->reg_base + IDXD_GENSTATS_OFFSET); if (gensts.state == IDXD_DEVICE_STATE_HALT) { - spin_lock_bh(&idxd->dev_lock); + idxd->state = IDXD_DEV_HALTED; if (gensts.reset_type == IDXD_DEVICE_RESET_SOFTWARE) { - rc = idxd_restart(idxd); - if (rc < 0) - dev_err(&idxd->pdev->dev, - "idxd restart failed, device halt."); + /* + * If we need a software reset, we will throw the work + * on a system workqueue in order to allow interrupts + * for the device command completions. + */ + INIT_WORK(&idxd->work, idxd_device_reinit); + queue_work(idxd->wq, &idxd->work); } else { + spin_lock_bh(&idxd->dev_lock); idxd_device_wqs_clear_state(idxd); - idxd->state = IDXD_DEV_HALTED; dev_err(&idxd->pdev->dev, "idxd halted, need %s.\n", gensts.reset_type == IDXD_DEVICE_RESET_FLR ? "FLR" : "system reset"); + spin_unlock_bh(&idxd->dev_lock); } - spin_unlock_bh(&idxd->dev_lock); } idxd_unmask_msix_vector(idxd, irq_entry->id); diff --git a/drivers/dma/idxd/sysfs.c b/drivers/dma/idxd/sysfs.c index 052dae5d6dddb..6f0711a822a12 100644 --- a/drivers/dma/idxd/sysfs.c +++ b/drivers/dma/idxd/sysfs.c @@ -118,12 +118,11 @@ static int idxd_config_bus_probe(struct device *dev) if (!try_module_get(THIS_MODULE)) return -ENXIO; - spin_lock_irqsave(&idxd->dev_lock, flags); - /* Perform IDXD configuration and enabling */ + spin_lock_irqsave(&idxd->dev_lock, flags); rc = idxd_device_config(idxd); + spin_unlock_irqrestore(&idxd->dev_lock, flags); if (rc < 0) { - spin_unlock_irqrestore(&idxd->dev_lock, flags); module_put(THIS_MODULE); dev_warn(dev, "Device config failed: %d\n", rc); return rc; @@ -132,18 +131,15 @@ static int idxd_config_bus_probe(struct device *dev) /* start device */ rc = idxd_device_enable(idxd); if (rc < 0) { - spin_unlock_irqrestore(&idxd->dev_lock, flags); module_put(THIS_MODULE); dev_warn(dev, "Device enable failed: %d\n", rc); return rc; } - spin_unlock_irqrestore(&idxd->dev_lock, flags); dev_info(dev, "Device %s enabled\n", dev_name(dev)); rc = idxd_register_dma_device(idxd); if (rc < 0) { - spin_unlock_irqrestore(&idxd->dev_lock, flags); module_put(THIS_MODULE); dev_dbg(dev, "Failed to register dmaengine device\n"); return rc; @@ -188,8 +184,8 @@ static int idxd_config_bus_probe(struct device *dev) spin_lock_irqsave(&idxd->dev_lock, flags); rc = idxd_device_config(idxd); + spin_unlock_irqrestore(&idxd->dev_lock, flags); if (rc < 0) { - spin_unlock_irqrestore(&idxd->dev_lock, flags); mutex_unlock(&wq->wq_lock); dev_warn(dev, "Writing WQ %d config failed: %d\n", wq->id, rc); @@ -198,13 +194,11 @@ static int idxd_config_bus_probe(struct device *dev) rc = idxd_wq_enable(wq); if (rc < 0) { - spin_unlock_irqrestore(&idxd->dev_lock, flags); mutex_unlock(&wq->wq_lock); dev_warn(dev, "WQ %d enabling failed: %d\n", wq->id, rc); return rc; } - spin_unlock_irqrestore(&idxd->dev_lock, flags); rc = idxd_wq_map_portal(wq); if (rc < 0) { @@ -212,7 +206,6 @@ static int idxd_config_bus_probe(struct device *dev) rc = idxd_wq_disable(wq); if (rc < 0) dev_warn(dev, "IDXD wq disable failed\n"); - spin_unlock_irqrestore(&idxd->dev_lock, flags); mutex_unlock(&wq->wq_lock); return rc; } @@ -248,7 +241,6 @@ static void disable_wq(struct idxd_wq *wq) { struct idxd_device *idxd = wq->idxd; struct device *dev = &idxd->pdev->dev; - unsigned long flags; int rc; mutex_lock(&wq->wq_lock); @@ -269,9 +261,8 @@ static void disable_wq(struct idxd_wq *wq) idxd_wq_unmap_portal(wq); - spin_lock_irqsave(&idxd->dev_lock, flags); + idxd_wq_drain(wq); rc = idxd_wq_disable(wq); - spin_unlock_irqrestore(&idxd->dev_lock, flags); idxd_wq_free_resources(wq); wq->client_count = 0; @@ -287,7 +278,6 @@ static void disable_wq(struct idxd_wq *wq) static int idxd_config_bus_remove(struct device *dev) { int rc; - unsigned long flags; dev_dbg(dev, "%s called for %s\n", __func__, dev_name(dev)); @@ -313,9 +303,7 @@ static int idxd_config_bus_remove(struct device *dev) } idxd_unregister_dma_device(idxd); - spin_lock_irqsave(&idxd->dev_lock, flags); rc = idxd_device_disable(idxd); - spin_unlock_irqrestore(&idxd->dev_lock, flags); module_put(THIS_MODULE); if (rc < 0) dev_warn(dev, "Device disable failed\n"); -- GitLab From 4548a6ad3d50c398aa12fa3ad45dd0611328f13b Mon Sep 17 00:00:00 2001 From: Dave Jiang Date: Fri, 26 Jun 2020 11:12:56 -0700 Subject: [PATCH 0381/1476] dmaengine: idxd: move idxd interrupt handling to mask instead of ignore Switch driver to use MSIX mask and unmask instead of the ignore bit. When ignore bit is cleared, we must issue an MMIO read to ensure writes have all arrived and check and process any additional completions. The ignore bit does not queue up any pending MSIX interrupts. The mask bit however does. Use API call from interrupt subsystem to mask MSIX interrupt since the hardware does not have convenient mask bit register. Suggested-by: Ashok Raj Signed-off-by: Dave Jiang Link: https://lore.kernel.org/r/159319517621.70410.11816465052708900506.stgit@djiang5-desk3.ch.intel.com Signed-off-by: Vinod Koul --- drivers/dma/idxd/device.c | 53 ++++++++------------------------------- drivers/dma/idxd/idxd.h | 4 +-- drivers/dma/idxd/irq.c | 2 -- 3 files changed, 13 insertions(+), 46 deletions(-) diff --git a/drivers/dma/idxd/device.c b/drivers/dma/idxd/device.c index 1d8d64508a28b..26e9a51de94e2 100644 --- a/drivers/dma/idxd/device.c +++ b/drivers/dma/idxd/device.c @@ -6,6 +6,8 @@ #include #include #include +#include +#include #include #include "../dmaengine.h" #include "idxd.h" @@ -15,61 +17,28 @@ static void idxd_cmd_exec(struct idxd_device *idxd, int cmd_code, u32 operand, u32 *status); /* Interrupt control bits */ -int idxd_mask_msix_vector(struct idxd_device *idxd, int vec_id) +void idxd_mask_msix_vector(struct idxd_device *idxd, int vec_id) { - struct pci_dev *pdev = idxd->pdev; - int msixcnt = pci_msix_vec_count(pdev); - union msix_perm perm; - u32 offset; - - if (vec_id < 0 || vec_id >= msixcnt) - return -EINVAL; - - offset = idxd->msix_perm_offset + vec_id * 8; - perm.bits = ioread32(idxd->reg_base + offset); - perm.ignore = 1; - iowrite32(perm.bits, idxd->reg_base + offset); + struct irq_data *data = irq_get_irq_data(idxd->msix_entries[vec_id].vector); - return 0; + pci_msi_mask_irq(data); } void idxd_mask_msix_vectors(struct idxd_device *idxd) { struct pci_dev *pdev = idxd->pdev; int msixcnt = pci_msix_vec_count(pdev); - int i, rc; + int i; - for (i = 0; i < msixcnt; i++) { - rc = idxd_mask_msix_vector(idxd, i); - if (rc < 0) - dev_warn(&pdev->dev, - "Failed disabling msix vec %d\n", i); - } + for (i = 0; i < msixcnt; i++) + idxd_mask_msix_vector(idxd, i); } -int idxd_unmask_msix_vector(struct idxd_device *idxd, int vec_id) +void idxd_unmask_msix_vector(struct idxd_device *idxd, int vec_id) { - struct pci_dev *pdev = idxd->pdev; - int msixcnt = pci_msix_vec_count(pdev); - union msix_perm perm; - u32 offset; - - if (vec_id < 0 || vec_id >= msixcnt) - return -EINVAL; - - offset = idxd->msix_perm_offset + vec_id * 8; - perm.bits = ioread32(idxd->reg_base + offset); - perm.ignore = 0; - iowrite32(perm.bits, idxd->reg_base + offset); + struct irq_data *data = irq_get_irq_data(idxd->msix_entries[vec_id].vector); - /* - * A readback from the device ensures that any previously generated - * completion record writes are visible to software based on PCI - * ordering rules. - */ - perm.bits = ioread32(idxd->reg_base + offset); - - return 0; + pci_msi_unmask_irq(data); } void idxd_unmask_error_interrupts(struct idxd_device *idxd) diff --git a/drivers/dma/idxd/idxd.h b/drivers/dma/idxd/idxd.h index 83214e902dd26..5f50bb830ca4e 100644 --- a/drivers/dma/idxd/idxd.h +++ b/drivers/dma/idxd/idxd.h @@ -273,8 +273,8 @@ irqreturn_t idxd_wq_thread(int irq, void *data); void idxd_mask_error_interrupts(struct idxd_device *idxd); void idxd_unmask_error_interrupts(struct idxd_device *idxd); void idxd_mask_msix_vectors(struct idxd_device *idxd); -int idxd_mask_msix_vector(struct idxd_device *idxd, int vec_id); -int idxd_unmask_msix_vector(struct idxd_device *idxd, int vec_id); +void idxd_mask_msix_vector(struct idxd_device *idxd, int vec_id); +void idxd_unmask_msix_vector(struct idxd_device *idxd, int vec_id); /* device control */ void idxd_device_init_reset(struct idxd_device *idxd); diff --git a/drivers/dma/idxd/irq.c b/drivers/dma/idxd/irq.c index 6052765ca3c89..f3c1d9ae8b563 100644 --- a/drivers/dma/idxd/irq.c +++ b/drivers/dma/idxd/irq.c @@ -260,8 +260,6 @@ irqreturn_t idxd_wq_thread(int irq, void *data) processed = idxd_desc_process(irq_entry); idxd_unmask_msix_vector(irq_entry->idxd, irq_entry->id); - /* catch anything unprocessed after unmasking */ - processed += idxd_desc_process(irq_entry); if (processed == 0) return IRQ_NONE; -- GitLab From 3621d3e57d9ec4677085bd11ebbf99bb405685b2 Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Fri, 3 Jul 2020 20:44:46 -0700 Subject: [PATCH 0382/1476] Documentation/driver-api: dmaengine/provider: drop doubled word Drop the doubled word "has". Signed-off-by: Randy Dunlap Cc: Jonathan Corbet Cc: linux-doc@vger.kernel.org Cc: Vinod Koul Cc: dmaengine@vger.kernel.org Link: https://lore.kernel.org/r/20200704034502.17199-2-rdunlap@infradead.org Signed-off-by: Vinod Koul --- Documentation/driver-api/dmaengine/provider.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/driver-api/dmaengine/provider.rst b/Documentation/driver-api/dmaengine/provider.rst index ce68315482b1f..5773421fe9556 100644 --- a/Documentation/driver-api/dmaengine/provider.rst +++ b/Documentation/driver-api/dmaengine/provider.rst @@ -507,7 +507,7 @@ dma_cookie_t DMA_CTRL_ACK - If clear, the descriptor cannot be reused by provider until the - client acknowledges receipt, i.e. has has a chance to establish any + client acknowledges receipt, i.e. has a chance to establish any dependency chains - This can be acked by invoking async_tx_ack() -- GitLab From 69cea0d45a618ad4ae74f36386ef1af5128b2b19 Mon Sep 17 00:00:00 2001 From: Mike Snitzer Date: Wed, 10 Jun 2020 15:02:37 -0400 Subject: [PATCH 0383/1476] dm mpath: changes from initial m->flags locking audit Fix locking in slow-paths where m->lock should be taken. Signed-off-by: Mike Snitzer --- drivers/md/dm-mpath.c | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/drivers/md/dm-mpath.c b/drivers/md/dm-mpath.c index 78cff42d987ee..d7bb74bded8c9 100644 --- a/drivers/md/dm-mpath.c +++ b/drivers/md/dm-mpath.c @@ -335,6 +335,8 @@ static int pg_init_all_paths(struct multipath *m) static void __switch_pg(struct multipath *m, struct priority_group *pg) { + lockdep_assert_held(&m->lock); + m->current_pg = pg; /* Must we initialise the PG first, and queue I/O till it's ready? */ @@ -382,7 +384,9 @@ static struct pgpath *choose_pgpath(struct multipath *m, size_t nr_bytes) unsigned bypassed = 1; if (!atomic_read(&m->nr_valid_paths)) { + spin_lock_irqsave(&m->lock, flags); clear_bit(MPATHF_QUEUE_IO, &m->flags); + spin_unlock_irqrestore(&m->lock, flags); goto failed; } @@ -422,8 +426,11 @@ check_current_pg: continue; pgpath = choose_path_in_pg(m, pg, nr_bytes); if (!IS_ERR_OR_NULL(pgpath)) { - if (!bypassed) + if (!bypassed) { + spin_lock_irqsave(&m->lock, flags); set_bit(MPATHF_PG_INIT_DELAY_RETRY, &m->flags); + spin_unlock_irqrestore(&m->lock, flags); + } return pgpath; } } @@ -1662,9 +1669,9 @@ static int multipath_end_io_bio(struct dm_target *ti, struct bio *clone, spin_lock_irqsave(&m->lock, flags); bio_list_add(&m->queued_bios, clone); - spin_unlock_irqrestore(&m->lock, flags); if (!test_bit(MPATHF_QUEUE_IO, &m->flags)) queue_work(kmultipathd, &m->process_queued_bios); + spin_unlock_irqrestore(&m->lock, flags); r = DM_ENDIO_INCOMPLETE; done: @@ -1938,6 +1945,7 @@ static int multipath_prepare_ioctl(struct dm_target *ti, { struct multipath *m = ti->private; struct pgpath *current_pgpath; + unsigned long flags; int r; current_pgpath = READ_ONCE(m->current_pgpath); @@ -1965,8 +1973,10 @@ static int multipath_prepare_ioctl(struct dm_target *ti, /* Path status changed, redo selection */ (void) choose_pgpath(m, 0); } + spin_lock_irqsave(&m->lock, flags); if (test_bit(MPATHF_PG_INIT_REQUIRED, &m->flags)) - pg_init_all_paths(m); + (void) __pg_init_all_paths(m); + spin_unlock_irqrestore(&m->lock, flags); dm_table_run_md_queue_async(m->ti->table); process_queued_io_list(m); } -- GitLab From a271a89c6e4773478b1c4f8213dfe8351ea66723 Mon Sep 17 00:00:00 2001 From: Mike Snitzer Date: Wed, 10 Jun 2020 16:07:57 -0400 Subject: [PATCH 0384/1476] dm mpath: take m->lock spinlock when testing QUEUE_IF_NO_PATH Fix multipath_end_io, multipath_end_io_bio and multipath_busy to take m->lock while testing if MPATHF_QUEUE_IF_NO_PATH bit is set. These are all slow-path cases when no paths are available so extra locking isn't a performance hit. Correctness matters most. Signed-off-by: Mike Snitzer --- drivers/md/dm-mpath.c | 52 ++++++++++++++++++++++++++++--------------- 1 file changed, 34 insertions(+), 18 deletions(-) diff --git a/drivers/md/dm-mpath.c b/drivers/md/dm-mpath.c index d7bb74bded8c9..bf5175805b1df 100644 --- a/drivers/md/dm-mpath.c +++ b/drivers/md/dm-mpath.c @@ -1621,12 +1621,16 @@ static int multipath_end_io(struct dm_target *ti, struct request *clone, if (pgpath) fail_path(pgpath); - if (atomic_read(&m->nr_valid_paths) == 0 && - !must_push_back_rq(m)) { - if (error == BLK_STS_IOERR) - dm_report_EIO(m); - /* complete with the original error */ - r = DM_ENDIO_DONE; + if (!atomic_read(&m->nr_valid_paths)) { + unsigned long flags; + spin_lock_irqsave(&m->lock, flags); + if (!must_push_back_rq(m)) { + if (error == BLK_STS_IOERR) + dm_report_EIO(m); + /* complete with the original error */ + r = DM_ENDIO_DONE; + } + spin_unlock_irqrestore(&m->lock, flags); } } @@ -1656,15 +1660,19 @@ static int multipath_end_io_bio(struct dm_target *ti, struct bio *clone, if (pgpath) fail_path(pgpath); - if (atomic_read(&m->nr_valid_paths) == 0 && - !test_bit(MPATHF_QUEUE_IF_NO_PATH, &m->flags)) { - if (__must_push_back(m)) { - r = DM_ENDIO_REQUEUE; - } else { - dm_report_EIO(m); - *error = BLK_STS_IOERR; + if (!atomic_read(&m->nr_valid_paths)) { + spin_lock_irqsave(&m->lock, flags); + if (!test_bit(MPATHF_QUEUE_IF_NO_PATH, &m->flags)) { + if (__must_push_back(m)) { + r = DM_ENDIO_REQUEUE; + } else { + dm_report_EIO(m); + *error = BLK_STS_IOERR; + } + spin_unlock_irqrestore(&m->lock, flags); + goto done; } - goto done; + spin_unlock_irqrestore(&m->lock, flags); } spin_lock_irqsave(&m->lock, flags); @@ -1962,10 +1970,11 @@ static int multipath_prepare_ioctl(struct dm_target *ti, } } else { /* No path is available */ + r = -EIO; + spin_lock_irqsave(&m->lock, flags); if (test_bit(MPATHF_QUEUE_IF_NO_PATH, &m->flags)) r = -ENOTCONN; - else - r = -EIO; + spin_unlock_irqrestore(&m->lock, flags); } if (r == -ENOTCONN) { @@ -2036,8 +2045,15 @@ static int multipath_busy(struct dm_target *ti) return true; /* no paths available, for blk-mq: rely on IO mapping to delay requeue */ - if (!atomic_read(&m->nr_valid_paths) && test_bit(MPATHF_QUEUE_IF_NO_PATH, &m->flags)) - return (m->queue_mode != DM_TYPE_REQUEST_BASED); + if (!atomic_read(&m->nr_valid_paths)) { + unsigned long flags; + spin_lock_irqsave(&m->lock, flags); + if (test_bit(MPATHF_QUEUE_IF_NO_PATH, &m->flags)) { + spin_unlock_irqrestore(&m->lock, flags); + return (m->queue_mode != DM_TYPE_REQUEST_BASED); + } + spin_unlock_irqrestore(&m->lock, flags); + } /* Guess which priority_group will be used at next mapping time */ pg = READ_ONCE(m->current_pg); -- GitLab From 73265f3ffdc90f0e27b79fbcba8530c75c74ef12 Mon Sep 17 00:00:00 2001 From: Mike Snitzer Date: Wed, 10 Jun 2020 16:51:32 -0400 Subject: [PATCH 0385/1476] dm mpath: push locking down to must_push_back_rq() Signed-off-by: Mike Snitzer --- drivers/md/dm-mpath.c | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/drivers/md/dm-mpath.c b/drivers/md/dm-mpath.c index bf5175805b1df..87b6a2c98c739 100644 --- a/drivers/md/dm-mpath.c +++ b/drivers/md/dm-mpath.c @@ -472,7 +472,14 @@ static bool __must_push_back(struct multipath *m) static bool must_push_back_rq(struct multipath *m) { - return test_bit(MPATHF_QUEUE_IF_NO_PATH, &m->flags) || __must_push_back(m); + unsigned long flags; + bool ret; + + spin_lock_irqsave(&m->lock, flags); + ret = (test_bit(MPATHF_QUEUE_IF_NO_PATH, &m->flags) || __must_push_back(m)); + spin_unlock_irqrestore(&m->lock, flags); + + return ret; } /* @@ -1621,16 +1628,12 @@ static int multipath_end_io(struct dm_target *ti, struct request *clone, if (pgpath) fail_path(pgpath); - if (!atomic_read(&m->nr_valid_paths)) { - unsigned long flags; - spin_lock_irqsave(&m->lock, flags); - if (!must_push_back_rq(m)) { - if (error == BLK_STS_IOERR) - dm_report_EIO(m); - /* complete with the original error */ - r = DM_ENDIO_DONE; - } - spin_unlock_irqrestore(&m->lock, flags); + if (!atomic_read(&m->nr_valid_paths) && + !must_push_back_rq(m)) { + if (error == BLK_STS_IOERR) + dm_report_EIO(m); + /* complete with the original error */ + r = DM_ENDIO_DONE; } } -- GitLab From f45f11868e0efeccfc1c695e56864f9032f18bdc Mon Sep 17 00:00:00 2001 From: Mike Snitzer Date: Wed, 10 Jun 2020 20:03:19 -0400 Subject: [PATCH 0386/1476] dm mpath: factor out multipath_queue_bio Enables further cleanup. Signed-off-by: Mike Snitzer --- drivers/md/dm-mpath.c | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/drivers/md/dm-mpath.c b/drivers/md/dm-mpath.c index 87b6a2c98c739..4b48183c6b5c4 100644 --- a/drivers/md/dm-mpath.c +++ b/drivers/md/dm-mpath.c @@ -574,6 +574,18 @@ static void multipath_release_clone(struct request *clone, * Map cloned bios (bio-based multipath) */ +static void multipath_queue_bio(struct multipath *m, struct bio *bio) +{ + unsigned long flags; + + /* Queue for the daemon to resubmit */ + spin_lock_irqsave(&m->lock, flags); + bio_list_add(&m->queued_bios, bio); + if (!test_bit(MPATHF_QUEUE_IO, &m->flags)) + queue_work(kmultipathd, &m->process_queued_bios); + spin_unlock_irqrestore(&m->lock, flags); +} + static struct pgpath *__map_bio(struct multipath *m, struct bio *bio) { struct pgpath *pgpath; @@ -590,16 +602,11 @@ static struct pgpath *__map_bio(struct multipath *m, struct bio *bio) if ((pgpath && queue_io) || (!pgpath && test_bit(MPATHF_QUEUE_IF_NO_PATH, &m->flags))) { - /* Queue for the daemon to resubmit */ - spin_lock_irqsave(&m->lock, flags); - bio_list_add(&m->queued_bios, bio); - spin_unlock_irqrestore(&m->lock, flags); + multipath_queue_bio(m, bio); /* PG_INIT_REQUIRED cannot be set without QUEUE_IO */ if (queue_io || test_bit(MPATHF_PG_INIT_REQUIRED, &m->flags)) pg_init_all_paths(m); - else if (!queue_io) - queue_work(kmultipathd, &m->process_queued_bios); return ERR_PTR(-EAGAIN); } @@ -1678,12 +1685,7 @@ static int multipath_end_io_bio(struct dm_target *ti, struct bio *clone, spin_unlock_irqrestore(&m->lock, flags); } - spin_lock_irqsave(&m->lock, flags); - bio_list_add(&m->queued_bios, clone); - if (!test_bit(MPATHF_QUEUE_IO, &m->flags)) - queue_work(kmultipathd, &m->process_queued_bios); - spin_unlock_irqrestore(&m->lock, flags); - + multipath_queue_bio(m, clone); r = DM_ENDIO_INCOMPLETE; done: if (pgpath) { -- GitLab From 17213ec1806199ab528655946af144abc37d89fb Mon Sep 17 00:00:00 2001 From: Mike Snitzer Date: Wed, 10 Jun 2020 20:19:56 -0400 Subject: [PATCH 0387/1476] dm mpath: rework __map_bio() so that it follows same pattern as request-based multipath_clone_and_map() Signed-off-by: Mike Snitzer --- drivers/md/dm-mpath.c | 33 +++++++++++++++++++-------------- 1 file changed, 19 insertions(+), 14 deletions(-) diff --git a/drivers/md/dm-mpath.c b/drivers/md/dm-mpath.c index 4b48183c6b5c4..ab6ccd619573a 100644 --- a/drivers/md/dm-mpath.c +++ b/drivers/md/dm-mpath.c @@ -574,15 +574,20 @@ static void multipath_release_clone(struct request *clone, * Map cloned bios (bio-based multipath) */ -static void multipath_queue_bio(struct multipath *m, struct bio *bio) +static void __multipath_queue_bio(struct multipath *m, struct bio *bio) { - unsigned long flags; - /* Queue for the daemon to resubmit */ - spin_lock_irqsave(&m->lock, flags); bio_list_add(&m->queued_bios, bio); if (!test_bit(MPATHF_QUEUE_IO, &m->flags)) queue_work(kmultipathd, &m->process_queued_bios); +} + +static void multipath_queue_bio(struct multipath *m, struct bio *bio) +{ + unsigned long flags; + + spin_lock_irqsave(&m->lock, flags); + __multipath_queue_bio(m, bio); spin_unlock_irqrestore(&m->lock, flags); } @@ -590,24 +595,24 @@ static struct pgpath *__map_bio(struct multipath *m, struct bio *bio) { struct pgpath *pgpath; unsigned long flags; - bool queue_io; /* Do we need to select a new pgpath? */ pgpath = READ_ONCE(m->current_pgpath); if (!pgpath || !test_bit(MPATHF_QUEUE_IO, &m->flags)) pgpath = choose_pgpath(m, bio->bi_iter.bi_size); - /* MPATHF_QUEUE_IO might have been cleared by choose_pgpath. */ - queue_io = test_bit(MPATHF_QUEUE_IO, &m->flags); + if (!pgpath) { + spin_lock_irqsave(&m->lock, flags); + if (test_bit(MPATHF_QUEUE_IF_NO_PATH, &m->flags)) { + __multipath_queue_bio(m, bio); + pgpath = ERR_PTR(-EAGAIN); + } + spin_unlock_irqrestore(&m->lock, flags); - if ((pgpath && queue_io) || - (!pgpath && test_bit(MPATHF_QUEUE_IF_NO_PATH, &m->flags))) { + } else if (test_bit(MPATHF_QUEUE_IO, &m->flags) || + test_bit(MPATHF_PG_INIT_REQUIRED, &m->flags)) { multipath_queue_bio(m, bio); - - /* PG_INIT_REQUIRED cannot be set without QUEUE_IO */ - if (queue_io || test_bit(MPATHF_PG_INIT_REQUIRED, &m->flags)) - pg_init_all_paths(m); - + pg_init_all_paths(m); return ERR_PTR(-EAGAIN); } -- GitLab From 564dbb130b3f8ab39719a42c130505bf75610fbf Mon Sep 17 00:00:00 2001 From: Mike Snitzer Date: Wed, 10 Jun 2020 21:25:39 -0400 Subject: [PATCH 0388/1476] dm mpath: rename current_pgpath to pgpath in multipath_prepare_ioctl Makes consistent with __map_bio() and multipath_clone_and_map(). Signed-off-by: Mike Snitzer --- drivers/md/dm-mpath.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/md/dm-mpath.c b/drivers/md/dm-mpath.c index ab6ccd619573a..f71bb4e5eaf7e 100644 --- a/drivers/md/dm-mpath.c +++ b/drivers/md/dm-mpath.c @@ -1962,17 +1962,17 @@ static int multipath_prepare_ioctl(struct dm_target *ti, struct block_device **bdev) { struct multipath *m = ti->private; - struct pgpath *current_pgpath; + struct pgpath *pgpath; unsigned long flags; int r; - current_pgpath = READ_ONCE(m->current_pgpath); - if (!current_pgpath || !test_bit(MPATHF_QUEUE_IO, &m->flags)) - current_pgpath = choose_pgpath(m, 0); + pgpath = READ_ONCE(m->current_pgpath); + if (!pgpath || !test_bit(MPATHF_QUEUE_IO, &m->flags)) + pgpath = choose_pgpath(m, 0); - if (current_pgpath) { + if (pgpath) { if (!test_bit(MPATHF_QUEUE_IO, &m->flags)) { - *bdev = current_pgpath->path.dev->bdev; + *bdev = pgpath->path.dev->bdev; r = 0; } else { /* pg_init has not started or completed */ -- GitLab From 374117ad4736c5a4f8012cfe59fc07d9d58191d5 Mon Sep 17 00:00:00 2001 From: Mike Snitzer Date: Mon, 15 Jun 2020 11:31:04 -0400 Subject: [PATCH 0389/1476] dm mpath: use double checked locking in fast path Fast-path code biased toward lazy acknowledgement of bit being set (primarily only for initialization). Multipath code is very retry oriented so even if state is missed it'll recover. Signed-off-by: Mike Snitzer --- drivers/md/dm-mpath.c | 32 +++++++++++++++++++++++--------- 1 file changed, 23 insertions(+), 9 deletions(-) diff --git a/drivers/md/dm-mpath.c b/drivers/md/dm-mpath.c index f71bb4e5eaf7e..88dc7b41b1c61 100644 --- a/drivers/md/dm-mpath.c +++ b/drivers/md/dm-mpath.c @@ -128,6 +128,20 @@ static void queue_if_no_path_timeout_work(struct timer_list *t); #define MPATHF_PG_INIT_REQUIRED 5 /* pg_init needs calling? */ #define MPATHF_PG_INIT_DELAY_RETRY 6 /* Delay pg_init retry? */ +static bool mpath_double_check_test_bit(int MPATHF_bit, struct multipath *m) +{ + bool r = test_bit(MPATHF_bit, &m->flags); + + if (r) { + unsigned long flags; + spin_lock_irqsave(&m->lock, flags); + r = test_bit(MPATHF_bit, &m->flags); + spin_unlock_irqrestore(&m->lock, flags); + } + + return r; +} + /*----------------------------------------------- * Allocation routines *-----------------------------------------------*/ @@ -499,7 +513,7 @@ static int multipath_clone_and_map(struct dm_target *ti, struct request *rq, /* Do we need to select a new pgpath? */ pgpath = READ_ONCE(m->current_pgpath); - if (!pgpath || !test_bit(MPATHF_QUEUE_IO, &m->flags)) + if (!pgpath || !mpath_double_check_test_bit(MPATHF_QUEUE_IO, m)) pgpath = choose_pgpath(m, nr_bytes); if (!pgpath) { @@ -507,8 +521,8 @@ static int multipath_clone_and_map(struct dm_target *ti, struct request *rq, return DM_MAPIO_DELAY_REQUEUE; dm_report_EIO(m); /* Failed */ return DM_MAPIO_KILL; - } else if (test_bit(MPATHF_QUEUE_IO, &m->flags) || - test_bit(MPATHF_PG_INIT_REQUIRED, &m->flags)) { + } else if (mpath_double_check_test_bit(MPATHF_QUEUE_IO, m) || + mpath_double_check_test_bit(MPATHF_PG_INIT_REQUIRED, m)) { pg_init_all_paths(m); return DM_MAPIO_DELAY_REQUEUE; } @@ -598,7 +612,7 @@ static struct pgpath *__map_bio(struct multipath *m, struct bio *bio) /* Do we need to select a new pgpath? */ pgpath = READ_ONCE(m->current_pgpath); - if (!pgpath || !test_bit(MPATHF_QUEUE_IO, &m->flags)) + if (!pgpath || !mpath_double_check_test_bit(MPATHF_QUEUE_IO, m)) pgpath = choose_pgpath(m, bio->bi_iter.bi_size); if (!pgpath) { @@ -609,8 +623,8 @@ static struct pgpath *__map_bio(struct multipath *m, struct bio *bio) } spin_unlock_irqrestore(&m->lock, flags); - } else if (test_bit(MPATHF_QUEUE_IO, &m->flags) || - test_bit(MPATHF_PG_INIT_REQUIRED, &m->flags)) { + } else if (mpath_double_check_test_bit(MPATHF_QUEUE_IO, m) || + mpath_double_check_test_bit(MPATHF_PG_INIT_REQUIRED, m)) { multipath_queue_bio(m, bio); pg_init_all_paths(m); return ERR_PTR(-EAGAIN); @@ -861,7 +875,7 @@ static int setup_scsi_dh(struct block_device *bdev, struct multipath *m, struct request_queue *q = bdev_get_queue(bdev); int r; - if (test_bit(MPATHF_RETAIN_ATTACHED_HW_HANDLER, &m->flags)) { + if (mpath_double_check_test_bit(MPATHF_RETAIN_ATTACHED_HW_HANDLER, m)) { retain: if (*attached_handler_name) { /* @@ -1967,11 +1981,11 @@ static int multipath_prepare_ioctl(struct dm_target *ti, int r; pgpath = READ_ONCE(m->current_pgpath); - if (!pgpath || !test_bit(MPATHF_QUEUE_IO, &m->flags)) + if (!pgpath || !mpath_double_check_test_bit(MPATHF_QUEUE_IO, m)) pgpath = choose_pgpath(m, 0); if (pgpath) { - if (!test_bit(MPATHF_QUEUE_IO, &m->flags)) { + if (!mpath_double_check_test_bit(MPATHF_QUEUE_IO, m)) { *bdev = pgpath->path.dev->bdev; r = 0; } else { -- GitLab From e1fef0b08e06dcce275bd585bc6a900dff395096 Mon Sep 17 00:00:00 2001 From: JeongHyeon Lee Date: Thu, 18 Jun 2020 15:56:50 +0900 Subject: [PATCH 0390/1476] dm verity: add "panic_on_corruption" error handling mode Samsung smart phones may need the ability to panic on corruption. Not all devices provide the bootloader support needed to use the existing "restart_on_corruption" mode. Additional details for why Samsung needs this new mode can be found here: https://www.redhat.com/archives/dm-devel/2020-June/msg00235.html Signed-off-by: jhs2.lee Signed-off-by: Mike Snitzer --- Documentation/admin-guide/device-mapper/verity.rst | 4 ++++ drivers/md/dm-verity-target.c | 13 ++++++++++++- drivers/md/dm-verity.h | 3 ++- 3 files changed, 18 insertions(+), 2 deletions(-) diff --git a/Documentation/admin-guide/device-mapper/verity.rst b/Documentation/admin-guide/device-mapper/verity.rst index bb02caa452894..66f71f0dab1b3 100644 --- a/Documentation/admin-guide/device-mapper/verity.rst +++ b/Documentation/admin-guide/device-mapper/verity.rst @@ -83,6 +83,10 @@ restart_on_corruption not compatible with ignore_corruption and requires user space support to avoid restart loops. +panic_on_corruption + Panic the device when a corrupted block is discovered. This option is + not compatible with ignore_corruption and restart_on_corruption. + ignore_zero_blocks Do not verify blocks that are expected to contain zeroes and always return zeroes instead. This may be useful if the partition contains unused blocks diff --git a/drivers/md/dm-verity-target.c b/drivers/md/dm-verity-target.c index eec9f252e9354..ddcd1d03beb99 100644 --- a/drivers/md/dm-verity-target.c +++ b/drivers/md/dm-verity-target.c @@ -30,6 +30,7 @@ #define DM_VERITY_OPT_LOGGING "ignore_corruption" #define DM_VERITY_OPT_RESTART "restart_on_corruption" +#define DM_VERITY_OPT_PANIC "panic_on_corruption" #define DM_VERITY_OPT_IGN_ZEROES "ignore_zero_blocks" #define DM_VERITY_OPT_AT_MOST_ONCE "check_at_most_once" @@ -254,6 +255,9 @@ out: if (v->mode == DM_VERITY_MODE_RESTART) kernel_restart("dm-verity device corrupted"); + if (v->mode == DM_VERITY_MODE_PANIC) + panic("dm-verity device corrupted"); + return 1; } @@ -742,6 +746,9 @@ static void verity_status(struct dm_target *ti, status_type_t type, case DM_VERITY_MODE_RESTART: DMEMIT(DM_VERITY_OPT_RESTART); break; + case DM_VERITY_MODE_PANIC: + DMEMIT(DM_VERITY_OPT_PANIC); + break; default: BUG(); } @@ -907,6 +914,10 @@ static int verity_parse_opt_args(struct dm_arg_set *as, struct dm_verity *v, v->mode = DM_VERITY_MODE_RESTART; continue; + } else if (!strcasecmp(arg_name, DM_VERITY_OPT_PANIC)) { + v->mode = DM_VERITY_MODE_PANIC; + continue; + } else if (!strcasecmp(arg_name, DM_VERITY_OPT_IGN_ZEROES)) { r = verity_alloc_zero_digest(v); if (r) { @@ -1221,7 +1232,7 @@ bad: static struct target_type verity_target = { .name = "verity", - .version = {1, 6, 0}, + .version = {1, 7, 0}, .module = THIS_MODULE, .ctr = verity_ctr, .dtr = verity_dtr, diff --git a/drivers/md/dm-verity.h b/drivers/md/dm-verity.h index 641b9e3a399b7..4e769d13473a9 100644 --- a/drivers/md/dm-verity.h +++ b/drivers/md/dm-verity.h @@ -20,7 +20,8 @@ enum verity_mode { DM_VERITY_MODE_EIO, DM_VERITY_MODE_LOGGING, - DM_VERITY_MODE_RESTART + DM_VERITY_MODE_RESTART, + DM_VERITY_MODE_PANIC }; enum verity_block_type { -- GitLab From 216f8e95aacc8e9690d8e2286c472671b65f4128 Mon Sep 17 00:00:00 2001 From: Shmuel Hazan Date: Tue, 23 Jun 2020 09:03:35 +0300 Subject: [PATCH 0391/1476] PCI: mvebu: Setup BAR0 in order to fix MSI According to the Armada XP datasheet, section 10.2.6: "in order for the device to do a write to the MSI doorbell address, it needs to write to a register in the internal registers space". As a result of the requirement above, without this patch, MSI won't function and therefore some devices won't operate properly without pci=nomsi. This requirement was not present at the time of writing this driver since the vendor u-boot always initializes all PCIe controllers (incl. BAR0 initialization) and for some time, the vendor u-boot was the only available bootloader for this driver's SoCs (e.g. A38x,A37x, etc). Tested on an Armada 385 board on mainline u-boot (2020.4), without u-boot PCI initialization and the following PCIe devices: - Wilocity Wil6200 rev 2 (wil6210) - Qualcomm Atheros QCA6174 (ath10k_pci) Both failed to get a response from the device after loading the firmware and seem to operate properly with this patch. Link: https://lore.kernel.org/r/20200623060334.108444-1-sh@tkos.co.il Signed-off-by: Shmuel Hazan Signed-off-by: Lorenzo Pieralisi Acked-by: Thomas Petazzoni --- drivers/pci/controller/pci-mvebu.c | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/drivers/pci/controller/pci-mvebu.c b/drivers/pci/controller/pci-mvebu.c index 153a64676bc90..101c06602aa1b 100644 --- a/drivers/pci/controller/pci-mvebu.c +++ b/drivers/pci/controller/pci-mvebu.c @@ -105,6 +105,7 @@ struct mvebu_pcie_port { struct mvebu_pcie_window memwin; struct mvebu_pcie_window iowin; u32 saved_pcie_stat; + struct resource regs; }; static inline void mvebu_writel(struct mvebu_pcie_port *port, u32 val, u32 reg) @@ -149,7 +150,9 @@ static void mvebu_pcie_set_local_dev_nr(struct mvebu_pcie_port *port, int nr) /* * Setup PCIE BARs and Address Decode Wins: - * BAR[0,2] -> disabled, BAR[1] -> covers all DRAM banks + * BAR[0] -> internal registers (needed for MSI) + * BAR[1] -> covers all DRAM banks + * BAR[2] -> Disabled * WIN[0-3] -> DRAM bank[0-3] */ static void mvebu_pcie_setup_wins(struct mvebu_pcie_port *port) @@ -203,6 +206,12 @@ static void mvebu_pcie_setup_wins(struct mvebu_pcie_port *port) mvebu_writel(port, 0, PCIE_BAR_HI_OFF(1)); mvebu_writel(port, ((size - 1) & 0xffff0000) | 1, PCIE_BAR_CTRL_OFF(1)); + + /* + * Point BAR[0] to the device's internal registers. + */ + mvebu_writel(port, round_down(port->regs.start, SZ_1M), PCIE_BAR_LO_OFF(0)); + mvebu_writel(port, 0, PCIE_BAR_HI_OFF(0)); } static void mvebu_pcie_setup_hw(struct mvebu_pcie_port *port) @@ -708,14 +717,13 @@ static void __iomem *mvebu_pcie_map_registers(struct platform_device *pdev, struct device_node *np, struct mvebu_pcie_port *port) { - struct resource regs; int ret = 0; - ret = of_address_to_resource(np, 0, ®s); + ret = of_address_to_resource(np, 0, &port->regs); if (ret) return (void __iomem *)ERR_PTR(ret); - return devm_ioremap_resource(&pdev->dev, ®s); + return devm_ioremap_resource(&pdev->dev, &port->regs); } #define DT_FLAGS_TO_TYPE(flags) (((flags) >> 24) & 0x03) -- GitLab From 92a005448f6fed70b5e7a9f29a1f930118449f1b Mon Sep 17 00:00:00 2001 From: Gao Xiang Date: Mon, 13 Jul 2020 09:13:00 -0700 Subject: [PATCH 0392/1476] xfs: get rid of unnecessary xfs_perag_{get,put} pairs In the course of some operations, we look up the perag from the mount multiple times to get or change perag information. These are often very short pieces of code, so while the lookup cost is generally low, the cost of the lookup is far higher than the cost of the operation we are doing on the perag. Since we changed buffers to hold references to the perag they are cached in, many modification contexts already hold active references to the perag that are held across these operations. This is especially true for any operation that is serialised by an allocation group header buffer. In these cases, we can just use the buffer's reference to the perag to avoid needing to do lookups to access the perag. This means that many operations don't need to do perag lookups at all to access the perag because they've already looked up objects that own persistent references and hence can use that reference instead. Cc: Dave Chinner Cc: "Darrick J. Wong" Signed-off-by: Gao Xiang Reviewed-by: Darrick J. Wong Signed-off-by: Darrick J. Wong --- fs/xfs/libxfs/xfs_ag.c | 4 ++-- fs/xfs/libxfs/xfs_ag_resv.h | 12 ---------- fs/xfs/libxfs/xfs_alloc.c | 22 ++++++----------- fs/xfs/libxfs/xfs_alloc_btree.c | 8 ++----- fs/xfs/libxfs/xfs_ialloc.c | 28 ++++++---------------- fs/xfs/libxfs/xfs_refcount_btree.c | 4 +--- fs/xfs/libxfs/xfs_rmap_btree.c | 9 ++++--- fs/xfs/xfs_inode.c | 38 +++++++++--------------------- 8 files changed, 34 insertions(+), 91 deletions(-) diff --git a/fs/xfs/libxfs/xfs_ag.c b/fs/xfs/libxfs/xfs_ag.c index 9d84007a5c65e..8cf73fe4338e0 100644 --- a/fs/xfs/libxfs/xfs_ag.c +++ b/fs/xfs/libxfs/xfs_ag.c @@ -563,7 +563,8 @@ xfs_ag_get_geometry( error = xfs_alloc_read_agf(mp, NULL, agno, 0, &agf_bp); if (error) goto out_agi; - pag = xfs_perag_get(mp, agno); + + pag = agi_bp->b_pag; /* Fill out form. */ memset(ageo, 0, sizeof(*ageo)); @@ -583,7 +584,6 @@ xfs_ag_get_geometry( xfs_ag_geom_health(pag, ageo); /* Release resources. */ - xfs_perag_put(pag); xfs_buf_relse(agf_bp); out_agi: xfs_buf_relse(agi_bp); diff --git a/fs/xfs/libxfs/xfs_ag_resv.h b/fs/xfs/libxfs/xfs_ag_resv.h index f3fd0ee9a7f78..8a8eb4bc48bb6 100644 --- a/fs/xfs/libxfs/xfs_ag_resv.h +++ b/fs/xfs/libxfs/xfs_ag_resv.h @@ -37,16 +37,4 @@ xfs_ag_resv_rmapbt_alloc( xfs_perag_put(pag); } -static inline void -xfs_ag_resv_rmapbt_free( - struct xfs_mount *mp, - xfs_agnumber_t agno) -{ - struct xfs_perag *pag; - - pag = xfs_perag_get(mp, agno); - xfs_ag_resv_free_extent(pag, XFS_AG_RESV_RMAPBT, NULL, 1); - xfs_perag_put(pag); -} - #endif /* __XFS_AG_RESV_H__ */ diff --git a/fs/xfs/libxfs/xfs_alloc.c b/fs/xfs/libxfs/xfs_alloc.c index 203e74fa64aa6..bf4d07e5c73ff 100644 --- a/fs/xfs/libxfs/xfs_alloc.c +++ b/fs/xfs/libxfs/xfs_alloc.c @@ -710,13 +710,12 @@ xfs_alloc_read_agfl( STATIC int xfs_alloc_update_counters( struct xfs_trans *tp, - struct xfs_perag *pag, struct xfs_buf *agbp, long len) { struct xfs_agf *agf = agbp->b_addr; - pag->pagf_freeblks += len; + agbp->b_pag->pagf_freeblks += len; be32_add_cpu(&agf->agf_freeblks, len); xfs_trans_agblocks_delta(tp, len); @@ -1175,8 +1174,7 @@ xfs_alloc_ag_vextent( } if (!args->wasfromfl) { - error = xfs_alloc_update_counters(args->tp, args->pag, - args->agbp, + error = xfs_alloc_update_counters(args->tp, args->agbp, -((long)(args->len))); if (error) return error; @@ -1887,7 +1885,6 @@ xfs_free_ag_extent( enum xfs_ag_resv_type type) { struct xfs_mount *mp; - struct xfs_perag *pag; struct xfs_btree_cur *bno_cur; struct xfs_btree_cur *cnt_cur; xfs_agblock_t gtbno; /* start of right neighbor */ @@ -2167,10 +2164,8 @@ xfs_free_ag_extent( /* * Update the freespace totals in the ag and superblock. */ - pag = xfs_perag_get(mp, agno); - error = xfs_alloc_update_counters(tp, pag, agbp, len); - xfs_ag_resv_free_extent(pag, type, tp, len); - xfs_perag_put(pag); + error = xfs_alloc_update_counters(tp, agbp, len); + xfs_ag_resv_free_extent(agbp->b_pag, type, tp, len); if (error) goto error0; @@ -2689,7 +2684,7 @@ xfs_alloc_get_freelist( if (be32_to_cpu(agf->agf_flfirst) == xfs_agfl_size(mp)) agf->agf_flfirst = 0; - pag = xfs_perag_get(mp, be32_to_cpu(agf->agf_seqno)); + pag = agbp->b_pag; ASSERT(!pag->pagf_agflreset); be32_add_cpu(&agf->agf_flcount, -1); xfs_trans_agflist_delta(tp, -1); @@ -2701,7 +2696,6 @@ xfs_alloc_get_freelist( pag->pagf_btreeblks++; logflags |= XFS_AGF_BTREEBLKS; } - xfs_perag_put(pag); xfs_alloc_log_agf(tp, agbp, logflags); *bnop = bno; @@ -2797,7 +2791,7 @@ xfs_alloc_put_freelist( if (be32_to_cpu(agf->agf_fllast) == xfs_agfl_size(mp)) agf->agf_fllast = 0; - pag = xfs_perag_get(mp, be32_to_cpu(agf->agf_seqno)); + pag = agbp->b_pag; ASSERT(!pag->pagf_agflreset); be32_add_cpu(&agf->agf_flcount, 1); xfs_trans_agflist_delta(tp, 1); @@ -2809,7 +2803,6 @@ xfs_alloc_put_freelist( pag->pagf_btreeblks--; logflags |= XFS_AGF_BTREEBLKS; } - xfs_perag_put(pag); xfs_alloc_log_agf(tp, agbp, logflags); @@ -3006,7 +2999,7 @@ xfs_alloc_read_agf( ASSERT(!(*bpp)->b_error); agf = (*bpp)->b_addr; - pag = xfs_perag_get(mp, agno); + pag = (*bpp)->b_pag; if (!pag->pagf_init) { pag->pagf_freeblks = be32_to_cpu(agf->agf_freeblks); pag->pagf_btreeblks = be32_to_cpu(agf->agf_btreeblks); @@ -3034,7 +3027,6 @@ xfs_alloc_read_agf( be32_to_cpu(agf->agf_levels[XFS_BTNUM_CNTi])); } #endif - xfs_perag_put(pag); return 0; } diff --git a/fs/xfs/libxfs/xfs_alloc_btree.c b/fs/xfs/libxfs/xfs_alloc_btree.c index 60c453cb3ee37..3d1226aa2eb52 100644 --- a/fs/xfs/libxfs/xfs_alloc_btree.c +++ b/fs/xfs/libxfs/xfs_alloc_btree.c @@ -38,16 +38,14 @@ xfs_allocbt_set_root( { struct xfs_buf *agbp = cur->bc_ag.agbp; struct xfs_agf *agf = agbp->b_addr; - xfs_agnumber_t seqno = be32_to_cpu(agf->agf_seqno); int btnum = cur->bc_btnum; - struct xfs_perag *pag = xfs_perag_get(cur->bc_mp, seqno); + struct xfs_perag *pag = agbp->b_pag; ASSERT(ptr->s != 0); agf->agf_roots[btnum] = ptr->s; be32_add_cpu(&agf->agf_levels[btnum], inc); pag->pagf_levels[btnum] += inc; - xfs_perag_put(pag); xfs_alloc_log_agf(cur->bc_tp, agbp, XFS_AGF_ROOTS | XFS_AGF_LEVELS); } @@ -115,7 +113,6 @@ xfs_allocbt_update_lastrec( int reason) { struct xfs_agf *agf = cur->bc_ag.agbp->b_addr; - xfs_agnumber_t seqno = be32_to_cpu(agf->agf_seqno); struct xfs_perag *pag; __be32 len; int numrecs; @@ -160,9 +157,8 @@ xfs_allocbt_update_lastrec( } agf->agf_longest = len; - pag = xfs_perag_get(cur->bc_mp, seqno); + pag = cur->bc_ag.agbp->b_pag; pag->pagf_longest = be32_to_cpu(len); - xfs_perag_put(pag); xfs_alloc_log_agf(cur->bc_tp, cur->bc_ag.agbp, XFS_AGF_LONGEST); } diff --git a/fs/xfs/libxfs/xfs_ialloc.c b/fs/xfs/libxfs/xfs_ialloc.c index 7fcf62b324b0d..f742a96a2fe1f 100644 --- a/fs/xfs/libxfs/xfs_ialloc.c +++ b/fs/xfs/libxfs/xfs_ialloc.c @@ -888,10 +888,9 @@ sparse_alloc: */ be32_add_cpu(&agi->agi_count, newlen); be32_add_cpu(&agi->agi_freecount, newlen); - pag = xfs_perag_get(args.mp, agno); + pag = agbp->b_pag; pag->pagi_freecount += newlen; pag->pagi_count += newlen; - xfs_perag_put(pag); agi->agi_newino = cpu_to_be32(newino); /* @@ -1134,7 +1133,7 @@ xfs_dialloc_ag_inobt( xfs_agnumber_t agno = be32_to_cpu(agi->agi_seqno); xfs_agnumber_t pagno = XFS_INO_TO_AGNO(mp, parent); xfs_agino_t pagino = XFS_INO_TO_AGINO(mp, parent); - struct xfs_perag *pag; + struct xfs_perag *pag = agbp->b_pag; struct xfs_btree_cur *cur, *tcur; struct xfs_inobt_rec_incore rec, trec; xfs_ino_t ino; @@ -1143,8 +1142,6 @@ xfs_dialloc_ag_inobt( int i, j; int searchdistance = 10; - pag = xfs_perag_get(mp, agno); - ASSERT(pag->pagi_init); ASSERT(pag->pagi_inodeok); ASSERT(pag->pagi_freecount > 0); @@ -1384,14 +1381,12 @@ alloc_inode: xfs_btree_del_cursor(cur, XFS_BTREE_NOERROR); xfs_trans_mod_sb(tp, XFS_TRANS_SB_IFREE, -1); - xfs_perag_put(pag); *inop = ino; return 0; error1: xfs_btree_del_cursor(tcur, XFS_BTREE_ERROR); error0: xfs_btree_del_cursor(cur, XFS_BTREE_ERROR); - xfs_perag_put(pag); return error; } @@ -1587,7 +1582,6 @@ xfs_dialloc_ag( xfs_agnumber_t agno = be32_to_cpu(agi->agi_seqno); xfs_agnumber_t pagno = XFS_INO_TO_AGNO(mp, parent); xfs_agino_t pagino = XFS_INO_TO_AGINO(mp, parent); - struct xfs_perag *pag; struct xfs_btree_cur *cur; /* finobt cursor */ struct xfs_btree_cur *icur; /* inobt cursor */ struct xfs_inobt_rec_incore rec; @@ -1599,8 +1593,6 @@ xfs_dialloc_ag( if (!xfs_sb_version_hasfinobt(&mp->m_sb)) return xfs_dialloc_ag_inobt(tp, agbp, parent, inop); - pag = xfs_perag_get(mp, agno); - /* * If pagino is 0 (this is the root inode allocation) use newino. * This must work because we've just allocated some. @@ -1667,7 +1659,7 @@ xfs_dialloc_ag( */ be32_add_cpu(&agi->agi_freecount, -1); xfs_ialloc_log_agi(tp, agbp, XFS_AGI_FREECOUNT); - pag->pagi_freecount--; + agbp->b_pag->pagi_freecount--; xfs_trans_mod_sb(tp, XFS_TRANS_SB_IFREE, -1); @@ -1680,7 +1672,6 @@ xfs_dialloc_ag( xfs_btree_del_cursor(icur, XFS_BTREE_NOERROR); xfs_btree_del_cursor(cur, XFS_BTREE_NOERROR); - xfs_perag_put(pag); *inop = ino; return 0; @@ -1688,7 +1679,6 @@ error_icur: xfs_btree_del_cursor(icur, XFS_BTREE_ERROR); error_cur: xfs_btree_del_cursor(cur, XFS_BTREE_ERROR); - xfs_perag_put(pag); return error; } @@ -1945,7 +1935,6 @@ xfs_difree_inobt( { struct xfs_agi *agi = agbp->b_addr; xfs_agnumber_t agno = be32_to_cpu(agi->agi_seqno); - struct xfs_perag *pag; struct xfs_btree_cur *cur; struct xfs_inobt_rec_incore rec; int ilen; @@ -2007,6 +1996,8 @@ xfs_difree_inobt( if (!(mp->m_flags & XFS_MOUNT_IKEEP) && rec.ir_free == XFS_INOBT_ALL_FREE && mp->m_sb.sb_inopblock <= XFS_INODES_PER_CHUNK) { + struct xfs_perag *pag = agbp->b_pag; + xic->deleted = true; xic->first_ino = XFS_AGINO_TO_INO(mp, agno, rec.ir_startino); xic->alloc = xfs_inobt_irec_to_allocmask(&rec); @@ -2020,10 +2011,8 @@ xfs_difree_inobt( be32_add_cpu(&agi->agi_count, -ilen); be32_add_cpu(&agi->agi_freecount, -(ilen - 1)); xfs_ialloc_log_agi(tp, agbp, XFS_AGI_COUNT | XFS_AGI_FREECOUNT); - pag = xfs_perag_get(mp, agno); pag->pagi_freecount -= ilen - 1; pag->pagi_count -= ilen; - xfs_perag_put(pag); xfs_trans_mod_sb(tp, XFS_TRANS_SB_ICOUNT, -ilen); xfs_trans_mod_sb(tp, XFS_TRANS_SB_IFREE, -(ilen - 1)); @@ -2049,9 +2038,7 @@ xfs_difree_inobt( */ be32_add_cpu(&agi->agi_freecount, 1); xfs_ialloc_log_agi(tp, agbp, XFS_AGI_FREECOUNT); - pag = xfs_perag_get(mp, agno); - pag->pagi_freecount++; - xfs_perag_put(pag); + agbp->b_pag->pagi_freecount++; xfs_trans_mod_sb(tp, XFS_TRANS_SB_IFREE, 1); } @@ -2661,7 +2648,7 @@ xfs_ialloc_read_agi( return error; agi = (*bpp)->b_addr; - pag = xfs_perag_get(mp, agno); + pag = (*bpp)->b_pag; if (!pag->pagi_init) { pag->pagi_freecount = be32_to_cpu(agi->agi_freecount); pag->pagi_count = be32_to_cpu(agi->agi_count); @@ -2674,7 +2661,6 @@ xfs_ialloc_read_agi( */ ASSERT(pag->pagi_freecount == be32_to_cpu(agi->agi_freecount) || XFS_FORCED_SHUTDOWN(mp)); - xfs_perag_put(pag); return 0; } diff --git a/fs/xfs/libxfs/xfs_refcount_btree.c b/fs/xfs/libxfs/xfs_refcount_btree.c index 7fd6044a4f780..c5296c124a4c4 100644 --- a/fs/xfs/libxfs/xfs_refcount_btree.c +++ b/fs/xfs/libxfs/xfs_refcount_btree.c @@ -37,15 +37,13 @@ xfs_refcountbt_set_root( { struct xfs_buf *agbp = cur->bc_ag.agbp; struct xfs_agf *agf = agbp->b_addr; - xfs_agnumber_t seqno = be32_to_cpu(agf->agf_seqno); - struct xfs_perag *pag = xfs_perag_get(cur->bc_mp, seqno); + struct xfs_perag *pag = agbp->b_pag; ASSERT(ptr->s != 0); agf->agf_refcount_root = ptr->s; be32_add_cpu(&agf->agf_refcount_level, inc); pag->pagf_refcount_level += inc; - xfs_perag_put(pag); xfs_alloc_log_agf(cur->bc_tp, agbp, XFS_AGF_REFCOUNT_ROOT | XFS_AGF_REFCOUNT_LEVEL); diff --git a/fs/xfs/libxfs/xfs_rmap_btree.c b/fs/xfs/libxfs/xfs_rmap_btree.c index b7c05314d07c9..94948a53569f4 100644 --- a/fs/xfs/libxfs/xfs_rmap_btree.c +++ b/fs/xfs/libxfs/xfs_rmap_btree.c @@ -63,16 +63,14 @@ xfs_rmapbt_set_root( { struct xfs_buf *agbp = cur->bc_ag.agbp; struct xfs_agf *agf = agbp->b_addr; - xfs_agnumber_t seqno = be32_to_cpu(agf->agf_seqno); int btnum = cur->bc_btnum; - struct xfs_perag *pag = xfs_perag_get(cur->bc_mp, seqno); + struct xfs_perag *pag = agbp->b_pag; ASSERT(ptr->s != 0); agf->agf_roots[btnum] = ptr->s; be32_add_cpu(&agf->agf_levels[btnum], inc); pag->pagf_levels[btnum] += inc; - xfs_perag_put(pag); xfs_alloc_log_agf(cur->bc_tp, agbp, XFS_AGF_ROOTS | XFS_AGF_LEVELS); } @@ -123,6 +121,7 @@ xfs_rmapbt_free_block( { struct xfs_buf *agbp = cur->bc_ag.agbp; struct xfs_agf *agf = agbp->b_addr; + struct xfs_perag *pag; xfs_agblock_t bno; int error; @@ -139,8 +138,8 @@ xfs_rmapbt_free_block( XFS_EXTENT_BUSY_SKIP_DISCARD); xfs_trans_agbtree_delta(cur->bc_tp, -1); - xfs_ag_resv_rmapbt_free(cur->bc_mp, cur->bc_ag.agno); - + pag = cur->bc_ag.agbp->b_pag; + xfs_ag_resv_free_extent(pag, XFS_AG_RESV_RMAPBT, NULL, 1); return 0; } diff --git a/fs/xfs/xfs_inode.c b/fs/xfs/xfs_inode.c index 5c07bf491d9f5..407d6299606d1 100644 --- a/fs/xfs/xfs_inode.c +++ b/fs/xfs/xfs_inode.c @@ -2265,7 +2265,6 @@ xfs_iunlink( } if (next_agino != NULLAGINO) { - struct xfs_perag *pag; xfs_agino_t old_agino; /* @@ -2282,9 +2281,7 @@ xfs_iunlink( * agino has been unlinked, add a backref from the next inode * back to agino. */ - pag = xfs_perag_get(mp, agno); - error = xfs_iunlink_add_backref(pag, agino, next_agino); - xfs_perag_put(pag); + error = xfs_iunlink_add_backref(agibp->b_pag, agino, next_agino); if (error) return error; } @@ -2420,7 +2417,6 @@ xfs_iunlink_remove( struct xfs_buf *agibp; struct xfs_buf *last_ibp; struct xfs_dinode *last_dip = NULL; - struct xfs_perag *pag = NULL; xfs_agnumber_t agno = XFS_INO_TO_AGNO(mp, ip->i_ino); xfs_agino_t agino = XFS_INO_TO_AGINO(mp, ip->i_ino); xfs_agino_t next_agino; @@ -2464,32 +2460,22 @@ xfs_iunlink_remove( * this inode's backref to point from the next inode. */ if (next_agino != NULLAGINO) { - pag = xfs_perag_get(mp, agno); - error = xfs_iunlink_change_backref(pag, next_agino, + error = xfs_iunlink_change_backref(agibp->b_pag, next_agino, NULLAGINO); if (error) - goto out; + return error; } - if (head_agino == agino) { - /* Point the head of the list to the next unlinked inode. */ - error = xfs_iunlink_update_bucket(tp, agno, agibp, bucket_index, - next_agino); - if (error) - goto out; - } else { + if (head_agino != agino) { struct xfs_imap imap; xfs_agino_t prev_agino; - if (!pag) - pag = xfs_perag_get(mp, agno); - /* We need to search the list for the inode being freed. */ error = xfs_iunlink_map_prev(tp, agno, head_agino, agino, &prev_agino, &imap, &last_dip, &last_ibp, - pag); + agibp->b_pag); if (error) - goto out; + return error; /* Point the previous inode on the list to the next inode. */ xfs_iunlink_update_dinode(tp, agno, prev_agino, last_ibp, @@ -2503,15 +2489,13 @@ xfs_iunlink_remove( * change_backref takes care of deleting the backref if * next_agino is NULLAGINO. */ - error = xfs_iunlink_change_backref(pag, agino, next_agino); - if (error) - goto out; + return xfs_iunlink_change_backref(agibp->b_pag, agino, + next_agino); } -out: - if (pag) - xfs_perag_put(pag); - return error; + /* Point the head of the list to the next unlinked inode. */ + return xfs_iunlink_update_bucket(tp, agno, agibp, bucket_index, + next_agino); } /* -- GitLab From 76622c88c2ce89d9ce494a0f656a2ad0abd67a69 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Mon, 13 Jul 2020 09:14:50 -0700 Subject: [PATCH 0393/1476] xfs: remove SYNC_WAIT and SYNC_TRYLOCK These two definitions are unused now. Signed-off-by: Christoph Hellwig Reviewed-by: Darrick J. Wong Signed-off-by: Darrick J. Wong Reviewed-by: Dave Chinner Reviewed-by: Chaitanya Kulkarni --- fs/xfs/xfs_icache.h | 3 --- 1 file changed, 3 deletions(-) diff --git a/fs/xfs/xfs_icache.h b/fs/xfs/xfs_icache.h index ae92ca53de423..3a4c8b382cd0f 100644 --- a/fs/xfs/xfs_icache.h +++ b/fs/xfs/xfs_icache.h @@ -17,9 +17,6 @@ struct xfs_eofblocks { __u64 eof_min_file_size; }; -#define SYNC_WAIT 0x0001 /* wait for i/o to complete */ -#define SYNC_TRYLOCK 0x0002 /* only try to lock inodes */ - /* * tags for inode radix tree */ -- GitLab From 8464e650b957888e3fb4789b4b16bf66840a287a Mon Sep 17 00:00:00 2001 From: YueHaibing Date: Mon, 13 Jul 2020 09:17:32 -0700 Subject: [PATCH 0394/1476] xfs: remove duplicated include from xfs_buf_item.c Remove duplicated include. Signed-off-by: YueHaibing Reviewed-by: Darrick J. Wong Signed-off-by: Darrick J. Wong Reviewed-by: Chaitanya Kulkarni --- fs/xfs/xfs_buf_item.c | 1 - 1 file changed, 1 deletion(-) diff --git a/fs/xfs/xfs_buf_item.c b/fs/xfs/xfs_buf_item.c index e9428c30862a9..ed1bf1d99483a 100644 --- a/fs/xfs/xfs_buf_item.c +++ b/fs/xfs/xfs_buf_item.c @@ -19,7 +19,6 @@ #include "xfs_quota.h" #include "xfs_dquot_item.h" #include "xfs_dquot.h" -#include "xfs_trans_priv.h" #include "xfs_trace.h" #include "xfs_log.h" -- GitLab From 5c699396f5f6cf6d67055af7b82c270d31fd831a Mon Sep 17 00:00:00 2001 From: Anton Blanchard Date: Tue, 30 Jun 2020 10:02:18 +1000 Subject: [PATCH 0395/1476] powerpc/xmon: Reset RCU and soft lockup watchdogs I'm seeing RCU warnings when exiting xmon. xmon resets the NMI watchdog, but does nothing with the RCU stall or soft lockup watchdogs. Add a helper function that handles all three. Signed-off-by: Anton Blanchard Acked-by: Nicholas Piggin Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20200630100218.62a3c3fb@kryten.localdomain --- arch/powerpc/xmon/xmon.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/arch/powerpc/xmon/xmon.c b/arch/powerpc/xmon/xmon.c index 7efe4bc3ccf63..d27944e38b044 100644 --- a/arch/powerpc/xmon/xmon.c +++ b/arch/powerpc/xmon/xmon.c @@ -481,6 +481,13 @@ static inline int unrecoverable_excp(struct pt_regs *regs) #endif } +static void xmon_touch_watchdogs(void) +{ + touch_softlockup_watchdog_sync(); + rcu_cpu_stall_reset(); + touch_nmi_watchdog(); +} + static int xmon_core(struct pt_regs *regs, int fromipi) { int cmd = 0; @@ -718,7 +725,7 @@ static int xmon_core(struct pt_regs *regs, int fromipi) else insert_cpu_bpts(); - touch_nmi_watchdog(); + xmon_touch_watchdogs(); local_irq_restore(flags); return cmd != 'X' && cmd != EOF; -- GitLab From acccc984c1f2e49225b40f1d0d20d383ec27d4d0 Mon Sep 17 00:00:00 2001 From: Sam Bobroff Date: Tue, 30 Jun 2020 08:50:44 +1000 Subject: [PATCH 0396/1476] MAINTAINERS: Remove self from powerpc EEH I'm sorry to say I can no longer maintain this position. Signed-off-by: Sam Bobroff Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/aec7d729c28e35c7fa9969ec50229080c771195c.1593471043.git.sbobroff@linux.ibm.com --- MAINTAINERS | 1 - 1 file changed, 1 deletion(-) diff --git a/MAINTAINERS b/MAINTAINERS index 7b5ffd646c6b9..7de395dfbdd50 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -13187,7 +13187,6 @@ F: tools/pci/ PCI ENHANCED ERROR HANDLING (EEH) FOR POWERPC M: Russell Currey -M: Sam Bobroff M: Oliver O'Halloran L: linuxppc-dev@lists.ozlabs.org S: Supported -- GitLab From 87db7579ebd5ded337056eb765542eb2608f16e3 Mon Sep 17 00:00:00 2001 From: Philippe Bergheaud Date: Fri, 19 Jun 2020 16:04:39 +0200 Subject: [PATCH 0397/1476] ocxl: control via sysfs whether the FPGA is reloaded on a link reset Some opencapi FPGA images allow to control if the FPGA should be reloaded on the next adapter reset. If it is supported, the image specifies it through a Vendor Specific DVSEC in the config space of function 0. Signed-off-by: Philippe Bergheaud Signed-off-by: Frederic Barrat Reviewed-by: Andrew Donnellan Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20200619140439.153962-1-fbarrat@linux.ibm.com --- Documentation/ABI/testing/sysfs-class-ocxl | 11 +++ drivers/misc/ocxl/config.c | 81 ++++++++++++++++++++-- drivers/misc/ocxl/ocxl_internal.h | 6 ++ drivers/misc/ocxl/sysfs.c | 35 ++++++++++ include/misc/ocxl-config.h | 1 + 5 files changed, 129 insertions(+), 5 deletions(-) diff --git a/Documentation/ABI/testing/sysfs-class-ocxl b/Documentation/ABI/testing/sysfs-class-ocxl index b5b1fa197592e..ae1276efa45a2 100644 --- a/Documentation/ABI/testing/sysfs-class-ocxl +++ b/Documentation/ABI/testing/sysfs-class-ocxl @@ -33,3 +33,14 @@ Date: January 2018 Contact: linuxppc-dev@lists.ozlabs.org Description: read/write Give access the global mmio area for the AFU + +What: /sys/class/ocxl//reload_on_reset +Date: February 2020 +Contact: linuxppc-dev@lists.ozlabs.org +Description: read/write + Control whether the FPGA is reloaded on a link reset. Enabled + through a vendor-specific logic block on the FPGA. + 0 Do not reload FPGA image from flash + 1 Reload FPGA image from flash + unavailable + The device does not support this capability diff --git a/drivers/misc/ocxl/config.c b/drivers/misc/ocxl/config.c index c8e19bfb5ef90..42f7a1298775b 100644 --- a/drivers/misc/ocxl/config.c +++ b/drivers/misc/ocxl/config.c @@ -71,6 +71,20 @@ static int find_dvsec_afu_ctrl(struct pci_dev *dev, u8 afu_idx) return 0; } +/** + * get_function_0() - Find a related PCI device (function 0) + * @device: PCI device to match + * + * Returns a pointer to the related device, or null if not found + */ +static struct pci_dev *get_function_0(struct pci_dev *dev) +{ + unsigned int devfn = PCI_DEVFN(PCI_SLOT(dev->devfn), 0); + + return pci_get_domain_bus_and_slot(pci_domain_nr(dev->bus), + dev->bus->number, devfn); +} + static void read_pasid(struct pci_dev *dev, struct ocxl_fn_config *fn) { u16 val; @@ -159,14 +173,15 @@ static int read_dvsec_afu_info(struct pci_dev *dev, struct ocxl_fn_config *fn) static int read_dvsec_vendor(struct pci_dev *dev) { int pos; - u32 cfg, tlx, dlx; + u32 cfg, tlx, dlx, reset_reload; /* - * vendor specific DVSEC is optional + * vendor specific DVSEC, for IBM images only. Some older + * images may not have it * - * It's currently only used on function 0 to specify the - * version of some logic blocks. Some older images may not - * even have it so we ignore any errors + * It's only used on function 0 to specify the version of some + * logic blocks and to give access to special registers to + * enable host-based flashing. */ if (PCI_FUNC(dev->devfn) != 0) return 0; @@ -178,11 +193,67 @@ static int read_dvsec_vendor(struct pci_dev *dev) pci_read_config_dword(dev, pos + OCXL_DVSEC_VENDOR_CFG_VERS, &cfg); pci_read_config_dword(dev, pos + OCXL_DVSEC_VENDOR_TLX_VERS, &tlx); pci_read_config_dword(dev, pos + OCXL_DVSEC_VENDOR_DLX_VERS, &dlx); + pci_read_config_dword(dev, pos + OCXL_DVSEC_VENDOR_RESET_RELOAD, + &reset_reload); dev_dbg(&dev->dev, "Vendor specific DVSEC:\n"); dev_dbg(&dev->dev, " CFG version = 0x%x\n", cfg); dev_dbg(&dev->dev, " TLX version = 0x%x\n", tlx); dev_dbg(&dev->dev, " DLX version = 0x%x\n", dlx); + dev_dbg(&dev->dev, " ResetReload = 0x%x\n", reset_reload); + return 0; +} + +static int get_dvsec_vendor0(struct pci_dev *dev, struct pci_dev **dev0, + int *out_pos) +{ + int pos; + + if (PCI_FUNC(dev->devfn) != 0) { + dev = get_function_0(dev); + if (!dev) + return -1; + } + pos = find_dvsec(dev, OCXL_DVSEC_VENDOR_ID); + if (!pos) + return -1; + *dev0 = dev; + *out_pos = pos; + return 0; +} + +int ocxl_config_get_reset_reload(struct pci_dev *dev, int *val) +{ + struct pci_dev *dev0; + u32 reset_reload; + int pos; + + if (get_dvsec_vendor0(dev, &dev0, &pos)) + return -1; + + pci_read_config_dword(dev0, pos + OCXL_DVSEC_VENDOR_RESET_RELOAD, + &reset_reload); + *val = !!(reset_reload & BIT(0)); + return 0; +} + +int ocxl_config_set_reset_reload(struct pci_dev *dev, int val) +{ + struct pci_dev *dev0; + u32 reset_reload; + int pos; + + if (get_dvsec_vendor0(dev, &dev0, &pos)) + return -1; + + pci_read_config_dword(dev0, pos + OCXL_DVSEC_VENDOR_RESET_RELOAD, + &reset_reload); + if (val) + reset_reload |= BIT(0); + else + reset_reload &= ~BIT(0); + pci_write_config_dword(dev0, pos + OCXL_DVSEC_VENDOR_RESET_RELOAD, + reset_reload); return 0; } diff --git a/drivers/misc/ocxl/ocxl_internal.h b/drivers/misc/ocxl/ocxl_internal.h index 345bf843a38e9..af9a84aeee6f9 100644 --- a/drivers/misc/ocxl/ocxl_internal.h +++ b/drivers/misc/ocxl/ocxl_internal.h @@ -112,6 +112,12 @@ void ocxl_actag_afu_free(struct ocxl_fn *fn, u32 start, u32 size); */ int ocxl_config_get_pasid_info(struct pci_dev *dev, int *count); +/* + * Control whether the FPGA is reloaded on a link reset + */ +int ocxl_config_get_reset_reload(struct pci_dev *dev, int *val); +int ocxl_config_set_reset_reload(struct pci_dev *dev, int val); + /* * Check if an AFU index is valid for the given function. * diff --git a/drivers/misc/ocxl/sysfs.c b/drivers/misc/ocxl/sysfs.c index 58f1ba2642068..25c78df8055d3 100644 --- a/drivers/misc/ocxl/sysfs.c +++ b/drivers/misc/ocxl/sysfs.c @@ -51,11 +51,46 @@ static ssize_t contexts_show(struct device *device, afu->pasid_count, afu->pasid_max); } +static ssize_t reload_on_reset_show(struct device *device, + struct device_attribute *attr, + char *buf) +{ + struct ocxl_afu *afu = to_afu(device); + struct ocxl_fn *fn = afu->fn; + struct pci_dev *pci_dev = to_pci_dev(fn->dev.parent); + int val; + + if (ocxl_config_get_reset_reload(pci_dev, &val)) + return scnprintf(buf, PAGE_SIZE, "unavailable\n"); + + return scnprintf(buf, PAGE_SIZE, "%d\n", val); +} + +static ssize_t reload_on_reset_store(struct device *device, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct ocxl_afu *afu = to_afu(device); + struct ocxl_fn *fn = afu->fn; + struct pci_dev *pci_dev = to_pci_dev(fn->dev.parent); + int rc, val; + + rc = kstrtoint(buf, 0, &val); + if (rc || (val != 0 && val != 1)) + return -EINVAL; + + if (ocxl_config_set_reset_reload(pci_dev, val)) + return -ENODEV; + + return count; +} + static struct device_attribute afu_attrs[] = { __ATTR_RO(global_mmio_size), __ATTR_RO(pp_mmio_size), __ATTR_RO(afu_version), __ATTR_RO(contexts), + __ATTR_RW(reload_on_reset), }; static ssize_t global_mmio_read(struct file *filp, struct kobject *kobj, diff --git a/include/misc/ocxl-config.h b/include/misc/ocxl-config.h index 3526fa996a220..ccfd3b463517e 100644 --- a/include/misc/ocxl-config.h +++ b/include/misc/ocxl-config.h @@ -41,5 +41,6 @@ #define OCXL_DVSEC_VENDOR_CFG_VERS 0x0C #define OCXL_DVSEC_VENDOR_TLX_VERS 0x10 #define OCXL_DVSEC_VENDOR_DLX_VERS 0x20 +#define OCXL_DVSEC_VENDOR_RESET_RELOAD 0x38 #endif /* _OCXL_CONFIG_H_ */ -- GitLab From 5658cf085ba3c3f3c24ac0f7210f0473794df506 Mon Sep 17 00:00:00 2001 From: Srikar Dronamraju Date: Mon, 29 Jun 2020 16:07:01 +0530 Subject: [PATCH 0398/1476] powerpc/cacheinfo: Use cpumap_print to print cpumap Tejun Heo had modified shared_cpu_map_show() to use scnprintf instead of cpumap_print during support for *pb[l] format. Refer commit 0c118b7bd09a ("powerpc: use %*pb[l] to print bitmaps including cpumasks and nodemasks"). cpumap_print_to_pagebuf() is a standard function to print cpumap. With commit 9cf79d115f0d ("bitmap: remove explicit newline handling using scnprintf format string"), there is no need to print explicit newline and trailing null character. cpumap_print_to_pagebuf() internally uses scnprintf(). Hence replace scnprintf() with cpumap_print_to_pagebuf(). Note: shared_cpu_map_show() in drivers/base/cacheinfo.c already uses cpumap_print_to_pagebuf(). Before this patch: # cat /sys/devices/system/cpu0/cache/index1/shared_cpu_map 00ff # (Notice the extra blank line). After this patch: # cat /sys/devices/system/cpu0/cache/index1/shared_cpu_map 00ff # Signed-off-by: Srikar Dronamraju Acked-by: Tejun Heo Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20200629103703.4538-2-srikar@linux.vnet.ibm.com --- arch/powerpc/kernel/cacheinfo.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/arch/powerpc/kernel/cacheinfo.c b/arch/powerpc/kernel/cacheinfo.c index 470336277c672..0d3c45e2fccdf 100644 --- a/arch/powerpc/kernel/cacheinfo.c +++ b/arch/powerpc/kernel/cacheinfo.c @@ -652,7 +652,7 @@ static ssize_t shared_cpu_map_show(struct kobject *k, struct kobj_attribute *att struct cache_index_dir *index; struct cache *cache; const struct cpumask *mask; - int ret, cpu; + int cpu; index = kobj_to_cache_index_dir(k); cache = index->cache; @@ -664,11 +664,7 @@ static ssize_t shared_cpu_map_show(struct kobject *k, struct kobj_attribute *att mask = &cache->shared_cpu_map; } - ret = scnprintf(buf, PAGE_SIZE - 1, "%*pb\n", - cpumask_pr_args(mask)); - buf[ret++] = '\n'; - buf[ret] = '\0'; - return ret; + return cpumap_print_to_pagebuf(false, buf, mask); } static struct kobj_attribute cache_shared_cpu_map_attr = -- GitLab From 74b7492e417812ea0f5002e210e2ac07a5728d17 Mon Sep 17 00:00:00 2001 From: Srikar Dronamraju Date: Mon, 29 Jun 2020 16:07:02 +0530 Subject: [PATCH 0399/1476] powerpc/cacheinfo: Make cpumap_show code reusable In anticipation of implementing shared_cpu_list, move code under shared_cpu_map_show() to a common function. No functional changes. Signed-off-by: Srikar Dronamraju Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20200629103703.4538-3-srikar@linux.vnet.ibm.com --- arch/powerpc/kernel/cacheinfo.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/arch/powerpc/kernel/cacheinfo.c b/arch/powerpc/kernel/cacheinfo.c index 0d3c45e2fccdf..5be870f996239 100644 --- a/arch/powerpc/kernel/cacheinfo.c +++ b/arch/powerpc/kernel/cacheinfo.c @@ -647,7 +647,8 @@ static const struct cpumask *get_big_core_shared_cpu_map(int cpu, struct cache * return &cache->shared_cpu_map; } -static ssize_t shared_cpu_map_show(struct kobject *k, struct kobj_attribute *attr, char *buf) +static ssize_t +show_shared_cpumap(struct kobject *k, struct kobj_attribute *attr, char *buf, bool list) { struct cache_index_dir *index; struct cache *cache; @@ -664,7 +665,12 @@ static ssize_t shared_cpu_map_show(struct kobject *k, struct kobj_attribute *att mask = &cache->shared_cpu_map; } - return cpumap_print_to_pagebuf(false, buf, mask); + return cpumap_print_to_pagebuf(list, buf, mask); +} + +static ssize_t shared_cpu_map_show(struct kobject *k, struct kobj_attribute *attr, char *buf) +{ + return show_shared_cpumap(k, attr, buf, false) } static struct kobj_attribute cache_shared_cpu_map_attr = -- GitLab From a87a77cb947cc9fc89f0dad51aeee66a61cc7fc4 Mon Sep 17 00:00:00 2001 From: Srikar Dronamraju Date: Mon, 29 Jun 2020 16:07:03 +0530 Subject: [PATCH 0400/1476] powerpc/cacheinfo: Add per cpu per index shared_cpu_list Unlike drivers/base/cacheinfo, powerpc cacheinfo code is not exposing shared_cpu_list under /sys/devices/system/cpu/cpu/cache/index Add shared_cpu_list to per cpu per index directory to maintain parity with x86. Some scripts (example: mmtests https://github.com/gormanm/mmtests) seem to be looking for shared_cpu_list instead of shared_cpu_map. Before this patch: # ls /sys/devices/system/cpu0/cache/index1 coherency_line_size number_of_sets size ways_of_associativity level shared_cpu_map type # cat /sys/devices/system/cpu0/cache/index1/shared_cpu_map 00ff # After this patch: # ls /sys/devices/system/cpu0/cache/index1 coherency_line_size number_of_sets shared_cpu_map type level shared_cpu_list size ways_of_associativity # cat /sys/devices/system/cpu0/cache/index1/shared_cpu_map 00ff # cat /sys/devices/system/cpu0/cache/index1/shared_cpu_list 0-7 # Signed-off-by: Srikar Dronamraju Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20200629103703.4538-4-srikar@linux.vnet.ibm.com --- arch/powerpc/kernel/cacheinfo.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/arch/powerpc/kernel/cacheinfo.c b/arch/powerpc/kernel/cacheinfo.c index 5be870f996239..d8d4552af30ad 100644 --- a/arch/powerpc/kernel/cacheinfo.c +++ b/arch/powerpc/kernel/cacheinfo.c @@ -670,12 +670,20 @@ show_shared_cpumap(struct kobject *k, struct kobj_attribute *attr, char *buf, bo static ssize_t shared_cpu_map_show(struct kobject *k, struct kobj_attribute *attr, char *buf) { - return show_shared_cpumap(k, attr, buf, false) + return show_shared_cpumap(k, attr, buf, false); +} + +static ssize_t shared_cpu_list_show(struct kobject *k, struct kobj_attribute *attr, char *buf) +{ + return show_shared_cpumap(k, attr, buf, true); } static struct kobj_attribute cache_shared_cpu_map_attr = __ATTR(shared_cpu_map, 0444, shared_cpu_map_show, NULL); +static struct kobj_attribute cache_shared_cpu_list_attr = + __ATTR(shared_cpu_list, 0444, shared_cpu_list_show, NULL); + /* Attributes which should always be created -- the kobject/sysfs core * does this automatically via kobj_type->default_attrs. This is the * minimum data required to uniquely identify a cache. @@ -684,6 +692,7 @@ static struct attribute *cache_index_default_attrs[] = { &cache_type_attr.attr, &cache_level_attr.attr, &cache_shared_cpu_map_attr.attr, + &cache_shared_cpu_list_attr.attr, NULL, }; -- GitLab From c339f9be304c21da1c42899a824f84a2cc9ced30 Mon Sep 17 00:00:00 2001 From: Abhishek Goel Date: Mon, 6 Jul 2020 00:32:58 -0500 Subject: [PATCH 0401/1476] cpuidle/powernv : Remove dead code block Commit 1961acad2f88559c2cdd2ef67c58c3627f1f6e54 removes usage of function "validate_dt_prop_sizes". This patch removes this unused function. Signed-off-by: Abhishek Goel Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20200706053258.121475-1-huntbag@linux.vnet.ibm.com --- drivers/cpuidle/cpuidle-powernv.c | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/drivers/cpuidle/cpuidle-powernv.c b/drivers/cpuidle/cpuidle-powernv.c index 1b299e801f749..addaa6e6718be 100644 --- a/drivers/cpuidle/cpuidle-powernv.c +++ b/drivers/cpuidle/cpuidle-powernv.c @@ -244,20 +244,6 @@ static inline void add_powernv_state(int index, const char *name, stop_psscr_table[index].mask = psscr_mask; } -/* - * Returns 0 if prop1_len == prop2_len. Else returns -1 - */ -static inline int validate_dt_prop_sizes(const char *prop1, int prop1_len, - const char *prop2, int prop2_len) -{ - if (prop1_len == prop2_len) - return 0; - - pr_warn("cpuidle-powernv: array sizes don't match for %s and %s\n", - prop1, prop2); - return -1; -} - extern u32 pnv_get_supported_cpuidle_states(void); static int powernv_add_idle_states(void) { -- GitLab From 93eacd94e09db2b1bb0343f8115385e5c34abf0a Mon Sep 17 00:00:00 2001 From: Oliver O'Halloran Date: Sun, 5 Jul 2020 23:35:56 +1000 Subject: [PATCH 0402/1476] powerpc/powernv: Make pnv_pci_sriov_enable() and friends static The kernel test robot noticed these are non-static which causes Clang to print some warnings. These are called via ppc_md function pointers so there's no need for them to be non-static. Reported-by: kernel test robot Signed-off-by: Oliver O'Halloran Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20200705133557.443607-1-oohall@gmail.com --- arch/powerpc/platforms/powernv/pci-ioda.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/arch/powerpc/platforms/powernv/pci-ioda.c b/arch/powerpc/platforms/powernv/pci-ioda.c index 73a63efcf8558..c2d46d28114b5 100644 --- a/arch/powerpc/platforms/powernv/pci-ioda.c +++ b/arch/powerpc/platforms/powernv/pci-ioda.c @@ -1490,7 +1490,7 @@ static void pnv_ioda_release_vf_PE(struct pci_dev *pdev) } } -void pnv_pci_sriov_disable(struct pci_dev *pdev) +static void pnv_pci_sriov_disable(struct pci_dev *pdev) { struct pci_bus *bus; struct pci_controller *hose; @@ -1600,7 +1600,7 @@ static void pnv_ioda_setup_vf_PE(struct pci_dev *pdev, u16 num_vfs) } } -int pnv_pci_sriov_enable(struct pci_dev *pdev, u16 num_vfs) +static int pnv_pci_sriov_enable(struct pci_dev *pdev, u16 num_vfs) { struct pci_bus *bus; struct pci_controller *hose; @@ -1715,7 +1715,7 @@ m64_failed: return ret; } -int pnv_pcibios_sriov_disable(struct pci_dev *pdev) +static int pnv_pcibios_sriov_disable(struct pci_dev *pdev) { pnv_pci_sriov_disable(pdev); @@ -1724,7 +1724,7 @@ int pnv_pcibios_sriov_disable(struct pci_dev *pdev) return 0; } -int pnv_pcibios_sriov_enable(struct pci_dev *pdev, u16 num_vfs) +static int pnv_pcibios_sriov_enable(struct pci_dev *pdev, u16 num_vfs) { /* Allocate PCI data */ add_sriov_vf_pdns(pdev); -- GitLab From e3417faec526cbf97773dca691dcd743f5bfeb64 Mon Sep 17 00:00:00 2001 From: Oliver O'Halloran Date: Sun, 5 Jul 2020 23:35:57 +1000 Subject: [PATCH 0403/1476] powerpc/powernv: Move pnv_ioda_setup_bus_dma under CONFIG_IOMMU_API pnv_ioda_setup_bus_dma() is only used when a passed through PE is returned to the host. If the kernel is built without IOMMU support this is dead code. Move it under the #ifdef with the rest of the IOMMU API support. Reported-by: kernel test robot Signed-off-by: Oliver O'Halloran Reviewed-by: Alexey Kardashevskiy Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20200705133557.443607-2-oohall@gmail.com --- arch/powerpc/platforms/powernv/pci-ioda.c | 26 +++++++++++------------ 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/arch/powerpc/platforms/powernv/pci-ioda.c b/arch/powerpc/platforms/powernv/pci-ioda.c index c2d46d28114b5..31c3e6d58c410 100644 --- a/arch/powerpc/platforms/powernv/pci-ioda.c +++ b/arch/powerpc/platforms/powernv/pci-ioda.c @@ -1885,19 +1885,6 @@ static bool pnv_pci_ioda_iommu_bypass_supported(struct pci_dev *pdev, return false; } -static void pnv_ioda_setup_bus_dma(struct pnv_ioda_pe *pe, struct pci_bus *bus) -{ - struct pci_dev *dev; - - list_for_each_entry(dev, &bus->devices, bus_list) { - set_iommu_table_base(&dev->dev, pe->table_group.tables[0]); - dev->dev.archdata.dma_offset = pe->tce_bypass_base; - - if ((pe->flags & PNV_IODA_PE_BUS_ALL) && dev->subordinate) - pnv_ioda_setup_bus_dma(pe, dev->subordinate); - } -} - static inline __be64 __iomem *pnv_ioda_get_inval_reg(struct pnv_phb *phb, bool real_mode) { @@ -2547,6 +2534,19 @@ static long pnv_pci_ioda2_create_table_userspace( return ret; } +static void pnv_ioda_setup_bus_dma(struct pnv_ioda_pe *pe, struct pci_bus *bus) +{ + struct pci_dev *dev; + + list_for_each_entry(dev, &bus->devices, bus_list) { + set_iommu_table_base(&dev->dev, pe->table_group.tables[0]); + dev->dev.archdata.dma_offset = pe->tce_bypass_base; + + if ((pe->flags & PNV_IODA_PE_BUS_ALL) && dev->subordinate) + pnv_ioda_setup_bus_dma(pe, dev->subordinate); + } +} + static void pnv_ioda2_take_ownership(struct iommu_table_group *table_group) { struct pnv_ioda_pe *pe = container_of(table_group, struct pnv_ioda_pe, -- GitLab From b648a5132ca3237a0f1ce5d871fff342b0efcf8a Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Mon, 6 Jul 2020 15:22:46 +0200 Subject: [PATCH 0404/1476] powerpc/spufs: add CONFIG_COREDUMP dependency The kernel test robot pointed out a slightly different error message after recent commit 5456ffdee666 ("powerpc/spufs: simplify spufs core dumping") to spufs for a configuration that never worked: powerpc64-linux-ld: arch/powerpc/platforms/cell/spufs/file.o: in function `.spufs_proxydma_info_dump': >> file.c:(.text+0x4c68): undefined reference to `.dump_emit' powerpc64-linux-ld: arch/powerpc/platforms/cell/spufs/file.o: in function `.spufs_dma_info_dump': file.c:(.text+0x4d70): undefined reference to `.dump_emit' powerpc64-linux-ld: arch/powerpc/platforms/cell/spufs/file.o: in function `.spufs_wbox_info_dump': file.c:(.text+0x4df4): undefined reference to `.dump_emit' Add a Kconfig dependency to prevent this from happening again. Reported-by: kernel test robot Signed-off-by: Arnd Bergmann Acked-by: Jeremy Kerr Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20200706132302.3885935-1-arnd@arndb.de --- arch/powerpc/platforms/cell/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/powerpc/platforms/cell/Kconfig b/arch/powerpc/platforms/cell/Kconfig index 0f7c8241912b9..f2ff359041eec 100644 --- a/arch/powerpc/platforms/cell/Kconfig +++ b/arch/powerpc/platforms/cell/Kconfig @@ -44,6 +44,7 @@ config SPU_FS tristate "SPU file system" default m depends on PPC_CELL + depends on COREDUMP select SPU_BASE help The SPU file system is used to access Synergistic Processing -- GitLab From 0138ba5783ae0dcc799ad401a1e8ac8333790df9 Mon Sep 17 00:00:00 2001 From: Nicholas Piggin Date: Mon, 11 May 2020 20:19:52 +1000 Subject: [PATCH 0405/1476] powerpc/64/signal: Balance return predictor stack in signal trampoline Returning from an interrupt or syscall to a signal handler currently begins execution directly at the handler's entry point, with LR set to the address of the sigreturn trampoline. When the signal handler function returns, it runs the trampoline. It looks like this: # interrupt at user address xyz # kernel stuff... signal is raised rfid # void handler(int sig) addis 2,12,.TOC.-.LCF0@ha addi 2,2,.TOC.-.LCF0@l mflr 0 std 0,16(1) stdu 1,-96(1) # handler stuff ld 0,16(1) mtlr 0 blr # __kernel_sigtramp_rt64 addi r1,r1,__SIGNAL_FRAMESIZE li r0,__NR_rt_sigreturn sc # kernel executes rt_sigreturn rfid # back to user address xyz Note the blr with no matching bl. This can corrupt the return predictor. Solve this by instead resuming execution at the signal trampoline which then calls the signal handler. qtrace-tools link_stack checker confirms the entire user/kernel/vdso cycle is balanced after this patch, whereas it's not upstream. Alan confirms the dwarf unwind info still looks good. gdb still recognises the signal frame and can step into parent frames if it break inside a signal handler. Performance is pretty noisy, not a very significant change on a POWER9 here, but branch misses are consistently a lot lower on a microbenchmark: Performance counter stats for './signal': 13,085.72 msec task-clock # 1.000 CPUs utilized 45,024,760,101 cycles # 3.441 GHz 65,102,895,542 instructions # 1.45 insn per cycle 11,271,673,787 branches # 861.372 M/sec 59,468,979 branch-misses # 0.53% of all branches 12,989.09 msec task-clock # 1.000 CPUs utilized 44,692,719,559 cycles # 3.441 GHz 65,109,984,964 instructions # 1.46 insn per cycle 11,282,136,057 branches # 868.585 M/sec 39,786,942 branch-misses # 0.35% of all branches Signed-off-by: Nicholas Piggin Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20200511101952.1463138-1-npiggin@gmail.com --- arch/powerpc/include/asm/ppc-opcode.h | 1 + arch/powerpc/kernel/signal_64.c | 22 ++++++++++++---------- arch/powerpc/kernel/vdso64/sigtramp.S | 13 +++++-------- 3 files changed, 18 insertions(+), 18 deletions(-) diff --git a/arch/powerpc/include/asm/ppc-opcode.h b/arch/powerpc/include/asm/ppc-opcode.h index 2a39c716c3436..bce879fb9afd8 100644 --- a/arch/powerpc/include/asm/ppc-opcode.h +++ b/arch/powerpc/include/asm/ppc-opcode.h @@ -332,6 +332,7 @@ #define PPC_INST_BLR 0x4e800020 #define PPC_INST_BLRL 0x4e800021 #define PPC_INST_BCTR 0x4e800420 +#define PPC_INST_BCTRL 0x4e800421 #define PPC_INST_MULLD 0x7c0001d2 #define PPC_INST_MULLW 0x7c0001d6 #define PPC_INST_MULHWU 0x7c000016 diff --git a/arch/powerpc/kernel/signal_64.c b/arch/powerpc/kernel/signal_64.c index 55e5f76554da6..97729642f9556 100644 --- a/arch/powerpc/kernel/signal_64.c +++ b/arch/powerpc/kernel/signal_64.c @@ -39,8 +39,8 @@ #define GP_REGS_SIZE min(sizeof(elf_gregset_t), sizeof(struct pt_regs)) #define FP_REGS_SIZE sizeof(elf_fpregset_t) -#define TRAMP_TRACEBACK 3 -#define TRAMP_SIZE 6 +#define TRAMP_TRACEBACK 4 +#define TRAMP_SIZE 7 /* * When we have signals to deliver, we set up on the user stack, @@ -600,13 +600,15 @@ static long setup_trampoline(unsigned int syscall, unsigned int __user *tramp) int i; long err = 0; + /* bctrl # call the handler */ + err |= __put_user(PPC_INST_BCTRL, &tramp[0]); /* addi r1, r1, __SIGNAL_FRAMESIZE # Pop the dummy stackframe */ err |= __put_user(PPC_INST_ADDI | __PPC_RT(R1) | __PPC_RA(R1) | - (__SIGNAL_FRAMESIZE & 0xffff), &tramp[0]); + (__SIGNAL_FRAMESIZE & 0xffff), &tramp[1]); /* li r0, __NR_[rt_]sigreturn| */ - err |= __put_user(PPC_INST_ADDI | (syscall & 0xffff), &tramp[1]); + err |= __put_user(PPC_INST_ADDI | (syscall & 0xffff), &tramp[2]); /* sc */ - err |= __put_user(PPC_INST_SC, &tramp[2]); + err |= __put_user(PPC_INST_SC, &tramp[3]); /* Minimal traceback info */ for (i=TRAMP_TRACEBACK; i < TRAMP_SIZE ;i++) @@ -864,12 +866,12 @@ int handle_rt_signal64(struct ksignal *ksig, sigset_t *set, /* Set up to return from userspace. */ if (vdso64_rt_sigtramp && tsk->mm->context.vdso_base) { - regs->link = tsk->mm->context.vdso_base + vdso64_rt_sigtramp; + regs->nip = tsk->mm->context.vdso_base + vdso64_rt_sigtramp; } else { err |= setup_trampoline(__NR_rt_sigreturn, &frame->tramp[0]); if (err) goto badframe; - regs->link = (unsigned long) &frame->tramp[0]; + regs->nip = (unsigned long) &frame->tramp[0]; } /* Allocate a dummy caller frame for the signal handler. */ @@ -878,8 +880,8 @@ int handle_rt_signal64(struct ksignal *ksig, sigset_t *set, /* Set up "regs" so we "return" to the signal handler. */ if (is_elf2_task()) { - regs->nip = (unsigned long) ksig->ka.sa.sa_handler; - regs->gpr[12] = regs->nip; + regs->ctr = (unsigned long) ksig->ka.sa.sa_handler; + regs->gpr[12] = regs->ctr; } else { /* Handler is *really* a pointer to the function descriptor for * the signal routine. The first entry in the function @@ -889,7 +891,7 @@ int handle_rt_signal64(struct ksignal *ksig, sigset_t *set, func_descr_t __user *funct_desc_ptr = (func_descr_t __user *) ksig->ka.sa.sa_handler; - err |= get_user(regs->nip, &funct_desc_ptr->entry); + err |= get_user(regs->ctr, &funct_desc_ptr->entry); err |= get_user(regs->gpr[2], &funct_desc_ptr->toc); } diff --git a/arch/powerpc/kernel/vdso64/sigtramp.S b/arch/powerpc/kernel/vdso64/sigtramp.S index a8cc0409d7d25..bbf68cd01088b 100644 --- a/arch/powerpc/kernel/vdso64/sigtramp.S +++ b/arch/powerpc/kernel/vdso64/sigtramp.S @@ -6,6 +6,7 @@ * Copyright (C) 2004 Benjamin Herrenschmuidt (benh@kernel.crashing.org), IBM Corp. * Copyright (C) 2004 Alan Modra (amodra@au.ibm.com)), IBM Corp. */ +#include /* IFETCH_ALIGN_BYTES */ #include #include #include @@ -14,21 +15,17 @@ .text -/* The nop here is a hack. The dwarf2 unwind routines subtract 1 from - the return address to get an address in the middle of the presumed - call instruction. Since we don't have a call here, we artificially - extend the range covered by the unwind info by padding before the - real start. */ - nop .balign 8 + .balign IFETCH_ALIGN_BYTES V_FUNCTION_BEGIN(__kernel_sigtramp_rt64) -.Lsigrt_start = . - 4 +.Lsigrt_start: + bctrl /* call the handler */ addi r1, r1, __SIGNAL_FRAMESIZE li r0,__NR_rt_sigreturn sc .Lsigrt_end: V_FUNCTION_END(__kernel_sigtramp_rt64) -/* The ".balign 8" above and the following zeros mimic the old stack +/* The .balign 8 above and the following zeros mimic the old stack trampoline layout. The last magic value is the ucontext pointer, chosen in such a way that older libgcc unwind code returns a zero for a sigcontext pointer. */ -- GitLab From 1f9bb31e58118e14ab66239796f2fbe633e1ad32 Mon Sep 17 00:00:00 2001 From: Nicholas Piggin Date: Wed, 8 Jul 2020 17:49:42 +1000 Subject: [PATCH 0406/1476] selftests/powerpc: Add FPU denormal test Add a testcase that tries to trigger the FPU denormal exception on Power8 or earlier CPUs. Prior to commit 4557ac6b344b ("powerpc/64s/exception: Fix 0x1500 interrupt handler crash") this would trigger a crash such as: Oops: Exception in kernel mode, sig: 5 [#1] LE PAGE_SIZE=64K MMU=Hash SMP NR_CPUS=2048 NUMA PowerNV Modules linked in: iptable_mangle xt_MASQUERADE iptable_nat nf_nat xt_conntrack nf_conntrack nf_defrag_ipv6 nf_defrag_ipv4 ipt_REJECT nf_reject_ipv4 xt_tcpudp tun bridge stp llc ip6table_filter ip6_tables iptable_filter fuse kvm_hv binfmt_misc squashfs mlx4_ib ib_uverbs dm_multipath scsi_dh_rdac scsi_dh_alua ib_core mlx4_en sr_mod cdrom bnx2x lpfc mlx4_core crc_t10dif scsi_transport_fc sg mdio vmx_crypto crct10dif_vpmsum leds_powernv powernv_rng rng_core led_class powernv_op_panel sunrpc ip_tables x_tables autofs4 CPU: 159 PID: 6854 Comm: fpu_denormal Not tainted 5.8.0-rc2-gcc-8.2.0-00092-g4ec7aaab0828 #192 NIP: c0000000000100ec LR: c00000000001b85c CTR: 0000000000000000 REGS: c000001dd818f770 TRAP: 1500 Not tainted (5.8.0-rc2-gcc-8.2.0-00092-g4ec7aaab0828) MSR: 900000000290b033 CR: 24002884 XER: 20000000 CFAR: c00000000001005c IRQMASK: 1 GPR00: c00000000001c4c8 c000001dd818fa00 c00000000171c200 c000001dd8101570 GPR04: 0000000000000000 c000001dd818fe90 c000001dd8101590 000000000000001d GPR08: 0000000000000010 0000000000002000 c000001dd818fe90 fffffffffc48ac60 GPR12: 0000000000002200 c000001ffff4f480 0000000000000000 0000000000000000 GPR16: 0000000000000000 0000000000000000 0000000000000000 0000000000000000 GPR20: 0000000000000000 00007fffab225b40 0000000000000001 c000000001757168 GPR24: c000001dd8101570 c0000018027b00f0 c000001dd8101570 c000000001496098 GPR28: c00000000174ad05 c000001dd8100000 c000001dd8100000 c000001dd8100000 NIP save_fpu+0xa8/0x2ac LR __giveup_fpu+0x2c/0xd0 Call Trace: 0xc000001dd818fa80 (unreliable) giveup_all+0x118/0x120 __switch_to+0x124/0x6c0 __schedule+0x390/0xaf0 do_task_dead+0x70/0x80 do_exit+0x8fc/0xe10 do_group_exit+0x64/0xd0 sys_exit_group+0x24/0x30 system_call_exception+0x164/0x270 system_call_common+0xf0/0x278 Signed-off-by: Nicholas Piggin [mpe: Split out of fix patch, add oops log] Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20200708074942.1713396-1-npiggin@gmail.com --- .../testing/selftests/powerpc/math/.gitignore | 1 + tools/testing/selftests/powerpc/math/Makefile | 2 +- .../selftests/powerpc/math/fpu_denormal.c | 38 +++++++++++++++++++ 3 files changed, 40 insertions(+), 1 deletion(-) create mode 100644 tools/testing/selftests/powerpc/math/fpu_denormal.c diff --git a/tools/testing/selftests/powerpc/math/.gitignore b/tools/testing/selftests/powerpc/math/.gitignore index e31ca6f453ed4..d0c23b2e4b609 100644 --- a/tools/testing/selftests/powerpc/math/.gitignore +++ b/tools/testing/selftests/powerpc/math/.gitignore @@ -6,3 +6,4 @@ vmx_preempt fpu_signal vmx_signal vsx_preempt +fpu_denormal diff --git a/tools/testing/selftests/powerpc/math/Makefile b/tools/testing/selftests/powerpc/math/Makefile index 11a10d7a2bbd9..4e2049d2fd8db 100644 --- a/tools/testing/selftests/powerpc/math/Makefile +++ b/tools/testing/selftests/powerpc/math/Makefile @@ -1,5 +1,5 @@ # SPDX-License-Identifier: GPL-2.0 -TEST_GEN_PROGS := fpu_syscall fpu_preempt fpu_signal vmx_syscall vmx_preempt vmx_signal vsx_preempt +TEST_GEN_PROGS := fpu_syscall fpu_preempt fpu_signal fpu_denormal vmx_syscall vmx_preempt vmx_signal vsx_preempt top_srcdir = ../../../../.. include ../../lib.mk diff --git a/tools/testing/selftests/powerpc/math/fpu_denormal.c b/tools/testing/selftests/powerpc/math/fpu_denormal.c new file mode 100644 index 0000000000000..5f96682abaa8f --- /dev/null +++ b/tools/testing/selftests/powerpc/math/fpu_denormal.c @@ -0,0 +1,38 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright IBM Corp. 2020 + * + * This test attempts to cause a FP denormal exception on POWER8 CPUs. Unfortunately + * if the denormal handler is not configured or working properly, this can cause a bad + * crash in kernel mode when the kernel tries to save FP registers when the process + * exits. + */ + +#include +#include + +#include "utils.h" + +static int test_denormal_fpu(void) +{ + unsigned int m32; + unsigned long m64; + volatile float f; + volatile double d; + + /* try to induce lfs ; stfd */ + + m32 = 0x00715fcf; /* random denormal */ + memcpy((float *)&f, &m32, sizeof(f)); + d = f; + memcpy(&m64, (double *)&d, sizeof(d)); + + FAIL_IF((long)(m64 != 0x380c57f3c0000000)); /* renormalised value */ + + return 0; +} + +int main(int argc, char *argv[]) +{ + return test_harness(test_denormal_fpu, "fpu_denormal"); +} -- GitLab From 7d38f089731fe129a49e254028caec6f05420f18 Mon Sep 17 00:00:00 2001 From: Christophe Leroy Date: Thu, 2 Jul 2020 14:09:21 +0000 Subject: [PATCH 0407/1476] docs: powerpc: Clarify book3s/32 MMU families Documentation wrongly tells that book3s/32 CPU have hash MMU. 603 and e300 core only have software loaded TLB. 755, 7450 family and e600 core have both hash MMU and software loaded TLB. This can be selected by setting a bit in HID2 (755) or HID0 (others). At the time being this is not supported by the kernel. Make this explicit in the documentation. Signed-off-by: Christophe Leroy Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/261923c075d1cb49d02493685e8585d4ea2a5197.1593698951.git.christophe.leroy@csgroup.eu --- Documentation/powerpc/cpu_families.rst | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/Documentation/powerpc/cpu_families.rst b/Documentation/powerpc/cpu_families.rst index 1e063c5440c39..9b84e045e7134 100644 --- a/Documentation/powerpc/cpu_families.rst +++ b/Documentation/powerpc/cpu_families.rst @@ -9,7 +9,9 @@ and are supported by arch/powerpc. Book3S (aka sPAPR) ------------------ -- Hash MMU +- Hash MMU (except 603 and e300) +- Software loaded TLB (603 and e300) +- Selectable Software loaded TLB in addition to hash MMU (755, 7450, e600) - Mix of 32 & 64 bit:: +--------------+ +----------------+ @@ -24,9 +26,9 @@ Book3S (aka sPAPR) | | | | v v - +--------------+ +----------------+ +-------+ - | 604 | | 750 (G3) | ---> | 750CX | - +--------------+ +----------------+ +-------+ + +--------------+ +-----+ +----------------+ +-------+ + | 604 | | 755 | <--- | 750 (G3) | ---> | 750CX | + +--------------+ +-----+ +----------------+ +-------+ | | | | | | v v v -- GitLab From b506923ee44ae87fc9f4de16b53feb313623e146 Mon Sep 17 00:00:00 2001 From: Christophe Leroy Date: Thu, 2 Jul 2020 11:52:02 +0000 Subject: [PATCH 0408/1476] Revert "powerpc/kasan: Fix shadow pages allocation failure" This reverts commit d2a91cef9bbdeb87b7449fdab1a6be6000930210. This commit moved too much work in kasan_init(). The allocation of shadow pages has to be moved for the reason explained in that patch, but the allocation of page tables still need to be done before switching to the final hash table. First revert the incorrect commit, following patch redoes it properly. Fixes: d2a91cef9bbd ("powerpc/kasan: Fix shadow pages allocation failure") Cc: stable@vger.kernel.org Reported-by: Erhard F. Signed-off-by: Christophe Leroy Signed-off-by: Michael Ellerman Link: https://bugzilla.kernel.org/show_bug.cgi?id=208181 Link: https://lore.kernel.org/r/3667deb0911affbf999b99f87c31c77d5e870cd2.1593690707.git.christophe.leroy@csgroup.eu --- arch/powerpc/include/asm/kasan.h | 2 ++ arch/powerpc/mm/init_32.c | 2 ++ arch/powerpc/mm/kasan/kasan_init_32.c | 4 +--- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/arch/powerpc/include/asm/kasan.h b/arch/powerpc/include/asm/kasan.h index be85c7005fb15..d635b96c7ea69 100644 --- a/arch/powerpc/include/asm/kasan.h +++ b/arch/powerpc/include/asm/kasan.h @@ -27,10 +27,12 @@ #ifdef CONFIG_KASAN void kasan_early_init(void); +void kasan_mmu_init(void); void kasan_init(void); void kasan_late_init(void); #else static inline void kasan_init(void) { } +static inline void kasan_mmu_init(void) { } static inline void kasan_late_init(void) { } #endif diff --git a/arch/powerpc/mm/init_32.c b/arch/powerpc/mm/init_32.c index 5a5469eb3174f..bf1717f8d5f4f 100644 --- a/arch/powerpc/mm/init_32.c +++ b/arch/powerpc/mm/init_32.c @@ -171,6 +171,8 @@ void __init MMU_init(void) btext_unmap(); #endif + kasan_mmu_init(); + setup_kup(); /* Shortly after that, the entire linear mapping will be available */ diff --git a/arch/powerpc/mm/kasan/kasan_init_32.c b/arch/powerpc/mm/kasan/kasan_init_32.c index 0760e1e754e4f..4813c6d508898 100644 --- a/arch/powerpc/mm/kasan/kasan_init_32.c +++ b/arch/powerpc/mm/kasan/kasan_init_32.c @@ -117,7 +117,7 @@ static void __init kasan_unmap_early_shadow_vmalloc(void) kasan_update_early_region(k_start, k_end, __pte(0)); } -static void __init kasan_mmu_init(void) +void __init kasan_mmu_init(void) { int ret; struct memblock_region *reg; @@ -146,8 +146,6 @@ static void __init kasan_mmu_init(void) void __init kasan_init(void) { - kasan_mmu_init(); - kasan_remap_early_shadow_ro(); clear_page(kasan_early_shadow_page); -- GitLab From 41ea93cf7ba4e0f0cc46ebfdda8b6ff27c67bc91 Mon Sep 17 00:00:00 2001 From: Christophe Leroy Date: Thu, 2 Jul 2020 11:52:03 +0000 Subject: [PATCH 0409/1476] powerpc/kasan: Fix shadow pages allocation failure Doing kasan pages allocation in MMU_init is too early, kernel doesn't have access yet to the entire memory space and memblock_alloc() fails when the kernel is a bit big. Do it from kasan_init() instead. Fixes: 2edb16efc899 ("powerpc/32: Add KASAN support") Fixes: d2a91cef9bbd ("powerpc/kasan: Fix shadow pages allocation failure") Cc: stable@vger.kernel.org Reported-by: Erhard F. Signed-off-by: Christophe Leroy Signed-off-by: Michael Ellerman Link: https://bugzilla.kernel.org/show_bug.cgi?id=208181 Link: https://lore.kernel.org/r/63048fcea8a1c02f75429ba3152f80f7853f87fc.1593690707.git.christophe.leroy@csgroup.eu --- arch/powerpc/mm/kasan/kasan_init_32.c | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/arch/powerpc/mm/kasan/kasan_init_32.c b/arch/powerpc/mm/kasan/kasan_init_32.c index 4813c6d508898..019b0c0bbbf31 100644 --- a/arch/powerpc/mm/kasan/kasan_init_32.c +++ b/arch/powerpc/mm/kasan/kasan_init_32.c @@ -120,11 +120,24 @@ static void __init kasan_unmap_early_shadow_vmalloc(void) void __init kasan_mmu_init(void) { int ret; + + if (early_mmu_has_feature(MMU_FTR_HPTE_TABLE) || + IS_ENABLED(CONFIG_KASAN_VMALLOC)) { + ret = kasan_init_shadow_page_tables(KASAN_SHADOW_START, KASAN_SHADOW_END); + + if (ret) + panic("kasan: kasan_init_shadow_page_tables() failed"); + } +} + +void __init kasan_init(void) +{ struct memblock_region *reg; for_each_memblock(memory, reg) { phys_addr_t base = reg->base; phys_addr_t top = min(base + reg->size, total_lowmem); + int ret; if (base >= top) continue; @@ -134,18 +147,6 @@ void __init kasan_mmu_init(void) panic("kasan: kasan_init_region() failed"); } - if (early_mmu_has_feature(MMU_FTR_HPTE_TABLE) || - IS_ENABLED(CONFIG_KASAN_VMALLOC)) { - ret = kasan_init_shadow_page_tables(KASAN_SHADOW_START, KASAN_SHADOW_END); - - if (ret) - panic("kasan: kasan_init_shadow_page_tables() failed"); - } - -} - -void __init kasan_init(void) -{ kasan_remap_early_shadow_ro(); clear_page(kasan_early_shadow_page); -- GitLab From 667e3c413ecf20371692fd2dc37e06dc14d0b140 Mon Sep 17 00:00:00 2001 From: Christophe Leroy Date: Tue, 7 Jul 2020 12:33:35 +0000 Subject: [PATCH 0410/1476] powerpc/signal_32: Remove !FULL_REGS() special handling in PPC64 save_general_regs() Since commit ("1bd79336a426 powerpc: Fix various syscall/signal/swapcontext bugs"), getting save_general_regs() called without FULL_REGS() is very unlikely and generates a warning. The 32-bit version of save_general_regs() doesn't take care of it at all and copies all registers anyway since that commit. Moreover, commit 965dd3ad3076 ("powerpc/64/syscall: Remove non-volatile GPR save optimisation") is another reason why it would never happen. So the same with 64-bit, don't worry about FULL_REGS() and copy all registers all the time. Signed-off-by: Christophe Leroy Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/173de3b659fa3a5f126a0eb170522cccd909950f.1594125164.git.christophe.leroy@csgroup.eu --- arch/powerpc/kernel/signal_32.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/arch/powerpc/kernel/signal_32.c b/arch/powerpc/kernel/signal_32.c index 1415c16ab6280..0fd59fbc6d49d 100644 --- a/arch/powerpc/kernel/signal_32.c +++ b/arch/powerpc/kernel/signal_32.c @@ -109,8 +109,6 @@ static inline int save_general_regs(struct pt_regs *regs, WARN_ON(!FULL_REGS(regs)); for (i = 0; i <= PT_RESULT; i ++) { - if (i == 14 && !FULL_REGS(regs)) - i = 32; if ( i == PT_SOFTE) { if(__put_user((unsigned int)softe, &frame->mc_gregs[i])) return -EFAULT; -- GitLab From 020c4831e01264f8b62af6ca9e669b7c51881a56 Mon Sep 17 00:00:00 2001 From: Christophe Leroy Date: Tue, 7 Jul 2020 12:33:36 +0000 Subject: [PATCH 0411/1476] powerpc/signal_32: Simplify loop in PPC64 save_general_regs() save_general_regs() which does special handling when i == PT_SOFTE. Rewrite it to minimise the specific part, especially the __put_user() and associated error handling is the same so make it common. Signed-off-by: Christophe Leroy [mpe: Use a regular if rather than ternary operator] Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/47a38df46cae5a5a88a558a64d71f75e9c4d9950.1594125164.git.christophe.leroy@csgroup.eu --- arch/powerpc/kernel/signal_32.c | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/arch/powerpc/kernel/signal_32.c b/arch/powerpc/kernel/signal_32.c index 0fd59fbc6d49d..96950f189b5a1 100644 --- a/arch/powerpc/kernel/signal_32.c +++ b/arch/powerpc/kernel/signal_32.c @@ -102,20 +102,18 @@ static inline int save_general_regs(struct pt_regs *regs, struct mcontext __user *frame) { elf_greg_t64 *gregs = (elf_greg_t64 *)regs; - int i; - /* Force usr to alway see softe as 1 (interrupts enabled) */ - elf_greg_t64 softe = 0x1; + int val, i; WARN_ON(!FULL_REGS(regs)); for (i = 0; i <= PT_RESULT; i ++) { - if ( i == PT_SOFTE) { - if(__put_user((unsigned int)softe, &frame->mc_gregs[i])) - return -EFAULT; - else - continue; - } - if (__put_user((unsigned int)gregs[i], &frame->mc_gregs[i])) + /* Force usr to alway see softe as 1 (interrupts enabled) */ + if (i == PT_SOFTE) + val = 1; + else + val = gregs[i]; + + if (__put_user(val, &frame->mc_gregs[i])) return -EFAULT; } return 0; -- GitLab From 96032f983ca32ad1d43c73da922dbc7022754c3c Mon Sep 17 00:00:00 2001 From: Christophe Leroy Date: Tue, 7 Jul 2020 18:32:25 +0000 Subject: [PATCH 0412/1476] powerpc/signal64: Don't opencode page prefaulting Instead of doing a __get_user() from the first and last location into a tmp var which won't be used, use fault_in_pages_readable() Signed-off-by: Christophe Leroy Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/810bd8840ef990a200f58c9dea9abe767ca02a3a.1594146723.git.christophe.leroy@csgroup.eu --- arch/powerpc/kernel/signal_64.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/arch/powerpc/kernel/signal_64.c b/arch/powerpc/kernel/signal_64.c index 97729642f9556..bfc939360bad3 100644 --- a/arch/powerpc/kernel/signal_64.c +++ b/arch/powerpc/kernel/signal_64.c @@ -21,6 +21,7 @@ #include #include #include +#include #include #include @@ -634,7 +635,6 @@ static long setup_trampoline(unsigned int syscall, unsigned int __user *tramp) SYSCALL_DEFINE3(swapcontext, struct ucontext __user *, old_ctx, struct ucontext __user *, new_ctx, long, ctx_size) { - unsigned char tmp; sigset_t set; unsigned long new_msr = 0; int ctx_has_vsx_region = 0; @@ -669,9 +669,8 @@ SYSCALL_DEFINE3(swapcontext, struct ucontext __user *, old_ctx, } if (new_ctx == NULL) return 0; - if (!access_ok(new_ctx, ctx_size) - || __get_user(tmp, (u8 __user *) new_ctx) - || __get_user(tmp, (u8 __user *) new_ctx + ctx_size - 1)) + if (!access_ok(new_ctx, ctx_size) || + fault_in_pages_readable((u8 __user *)new_ctx, ctx_size)) return -EFAULT; /* -- GitLab From 793d74a8c78e05d6833bfcf582e24e40bd92518f Mon Sep 17 00:00:00 2001 From: Christophe Leroy Date: Tue, 28 Apr 2020 13:16:47 +0000 Subject: [PATCH 0413/1476] powerpc/vdso64: Switch from __get_datapage() to get_datapage inline macro On the same way as already done on PPC32, drop __get_datapage() function and use get_datapage inline macro instead. See commit ec0895f08f99 ("powerpc/vdso32: inline __get_datapage()") Signed-off-by: Christophe Leroy Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/e13d95312e0b9792556b19b4bb8955cc1ff19fc7.1588079622.git.christophe.leroy@c-s.fr --- arch/powerpc/kernel/vdso64/cacheflush.S | 9 ++++---- arch/powerpc/kernel/vdso64/datapage.S | 28 +++-------------------- arch/powerpc/kernel/vdso64/gettimeofday.S | 9 ++++---- 3 files changed, 12 insertions(+), 34 deletions(-) diff --git a/arch/powerpc/kernel/vdso64/cacheflush.S b/arch/powerpc/kernel/vdso64/cacheflush.S index 526f5ba2593e2..cab14324242bf 100644 --- a/arch/powerpc/kernel/vdso64/cacheflush.S +++ b/arch/powerpc/kernel/vdso64/cacheflush.S @@ -8,6 +8,7 @@ #include #include #include +#include #include .text @@ -24,14 +25,12 @@ V_FUNCTION_BEGIN(__kernel_sync_dicache) .cfi_startproc mflr r12 .cfi_register lr,r12 - mr r11,r3 - bl V_LOCAL_FUNC(__get_datapage) + get_datapage r10, r0 mtlr r12 - mr r10,r3 lwz r7,CFG_DCACHE_BLOCKSZ(r10) addi r5,r7,-1 - andc r6,r11,r5 /* round low to line bdy */ + andc r6,r3,r5 /* round low to line bdy */ subf r8,r6,r4 /* compute length */ add r8,r8,r5 /* ensure we get enough */ lwz r9,CFG_DCACHE_LOGBLOCKSZ(r10) @@ -48,7 +47,7 @@ V_FUNCTION_BEGIN(__kernel_sync_dicache) lwz r7,CFG_ICACHE_BLOCKSZ(r10) addi r5,r7,-1 - andc r6,r11,r5 /* round low to line bdy */ + andc r6,r3,r5 /* round low to line bdy */ subf r8,r6,r4 /* compute length */ add r8,r8,r5 lwz r9,CFG_ICACHE_LOGBLOCKSZ(r10) diff --git a/arch/powerpc/kernel/vdso64/datapage.S b/arch/powerpc/kernel/vdso64/datapage.S index dc84f5ae3802b..067247d3efb9e 100644 --- a/arch/powerpc/kernel/vdso64/datapage.S +++ b/arch/powerpc/kernel/vdso64/datapage.S @@ -10,35 +10,13 @@ #include #include #include +#include .text .global __kernel_datapage_offset; __kernel_datapage_offset: .long 0 -V_FUNCTION_BEGIN(__get_datapage) - .cfi_startproc - /* We don't want that exposed or overridable as we want other objects - * to be able to bl directly to here - */ - .protected __get_datapage - .hidden __get_datapage - - mflr r0 - .cfi_register lr,r0 - - bcl 20,31,data_page_branch -data_page_branch: - mflr r3 - mtlr r0 - addi r3, r3, __kernel_datapage_offset-data_page_branch - lwz r0,0(r3) - .cfi_restore lr - add r3,r0,r3 - blr - .cfi_endproc -V_FUNCTION_END(__get_datapage) - /* * void *__kernel_get_syscall_map(unsigned int *syscall_count) ; * @@ -53,7 +31,7 @@ V_FUNCTION_BEGIN(__kernel_get_syscall_map) mflr r12 .cfi_register lr,r12 mr r4,r3 - bl V_LOCAL_FUNC(__get_datapage) + get_datapage r3, r0 mtlr r12 addi r3,r3,CFG_SYSCALL_MAP64 cmpldi cr0,r4,0 @@ -75,7 +53,7 @@ V_FUNCTION_BEGIN(__kernel_get_tbfreq) .cfi_startproc mflr r12 .cfi_register lr,r12 - bl V_LOCAL_FUNC(__get_datapage) + get_datapage r3, r0 ld r3,CFG_TB_TICKS_PER_SEC(r3) mtlr r12 crclr cr0*4+so diff --git a/arch/powerpc/kernel/vdso64/gettimeofday.S b/arch/powerpc/kernel/vdso64/gettimeofday.S index 1c9a047032504..20f8be40c6537 100644 --- a/arch/powerpc/kernel/vdso64/gettimeofday.S +++ b/arch/powerpc/kernel/vdso64/gettimeofday.S @@ -9,6 +9,7 @@ #include #include #include +#include #include #include @@ -26,7 +27,7 @@ V_FUNCTION_BEGIN(__kernel_gettimeofday) mr r11,r3 /* r11 holds tv */ mr r10,r4 /* r10 holds tz */ - bl V_LOCAL_FUNC(__get_datapage) /* get data page */ + get_datapage r3, r0 cmpldi r11,0 /* check if tv is NULL */ beq 2f lis r7,1000000@ha /* load up USEC_PER_SEC */ @@ -71,7 +72,7 @@ V_FUNCTION_BEGIN(__kernel_clock_gettime) mflr r12 /* r12 saves lr */ .cfi_register lr,r12 mr r11,r4 /* r11 saves tp */ - bl V_LOCAL_FUNC(__get_datapage) /* get data page */ + get_datapage r3, r0 lis r7,NSEC_PER_SEC@h /* want nanoseconds */ ori r7,r7,NSEC_PER_SEC@l beq cr5,70f @@ -188,7 +189,7 @@ V_FUNCTION_BEGIN(__kernel_clock_getres) mflr r12 .cfi_register lr,r12 - bl V_LOCAL_FUNC(__get_datapage) + get_datapage r3, r0 lwz r5, CLOCK_HRTIMER_RES(r3) mtlr r12 li r3,0 @@ -221,7 +222,7 @@ V_FUNCTION_BEGIN(__kernel_time) .cfi_register lr,r12 mr r11,r3 /* r11 holds t */ - bl V_LOCAL_FUNC(__get_datapage) + get_datapage r3, r0 ld r4,STAMP_XTIME_SEC(r3) -- GitLab From 05611a93b8ffa3fe7d2eb43dd6c11e37ead5908a Mon Sep 17 00:00:00 2001 From: Sugar Zhang Date: Mon, 29 Jun 2020 22:05:42 +0800 Subject: [PATCH 0414/1476] dmaengine: pl330: Remove the burst limit for quirk 'NO-FLUSHP' There is no reason to limit the performance on the 'NO-FLUSHP' SoCs, because 'FLUSHP' instruction is broken on these platforms, so remove the limit to improve the efficiency. Signed-off-by: Sugar Zhang Link: https://lore.kernel.org/r/1593439555-68130-2-git-send-email-sugar.zhang@rock-chips.com Signed-off-by: Vinod Koul --- drivers/dma/pl330.c | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/drivers/dma/pl330.c b/drivers/dma/pl330.c index 6a158eef6b8ad..7686292bc1dbd 100644 --- a/drivers/dma/pl330.c +++ b/drivers/dma/pl330.c @@ -1183,9 +1183,6 @@ static inline int _ldst_peripheral(struct pl330_dmac *pl330, { int off = 0; - if (pl330->quirks & PL330_QUIRK_BROKEN_NO_FLUSHP) - cond = BURST; - /* * do FLUSHP at beginning to clear any stale dma requests before the * first WFP. @@ -2221,9 +2218,7 @@ static bool pl330_prep_slave_fifo(struct dma_pl330_chan *pch, static int fixup_burst_len(int max_burst_len, int quirks) { - if (quirks & PL330_QUIRK_BROKEN_NO_FLUSHP) - return 1; - else if (max_burst_len > PL330_MAX_BURST) + if (max_burst_len > PL330_MAX_BURST) return PL330_MAX_BURST; else if (max_burst_len < 1) return 1; @@ -3128,8 +3123,7 @@ pl330_probe(struct amba_device *adev, const struct amba_id *id) pd->dst_addr_widths = PL330_DMA_BUSWIDTHS; pd->directions = BIT(DMA_DEV_TO_MEM) | BIT(DMA_MEM_TO_DEV); pd->residue_granularity = DMA_RESIDUE_GRANULARITY_BURST; - pd->max_burst = ((pl330->quirks & PL330_QUIRK_BROKEN_NO_FLUSHP) ? - 1 : PL330_MAX_BURST); + pd->max_burst = PL330_MAX_BURST; ret = dma_async_device_register(pd); if (ret) { -- GitLab From 3e7f0bd872087bf4653eeee9a83050f91baae907 Mon Sep 17 00:00:00 2001 From: Sugar Zhang Date: Mon, 29 Jun 2020 22:05:43 +0800 Subject: [PATCH 0415/1476] dmaengine: pl330: Improve transfer efficiency for the dregs Only the unaligned burst transfers have the dregs. so, still use BURST transfer with a reduced size for better performance. Signed-off-by: Sugar Zhang Link: https://lore.kernel.org/r/1593439555-68130-3-git-send-email-sugar.zhang@rock-chips.com Signed-off-by: Vinod Koul --- drivers/dma/pl330.c | 32 +++++++++++++++++++++----------- 1 file changed, 21 insertions(+), 11 deletions(-) diff --git a/drivers/dma/pl330.c b/drivers/dma/pl330.c index 7686292bc1dbd..f1f0176c6c057 100644 --- a/drivers/dma/pl330.c +++ b/drivers/dma/pl330.c @@ -1228,8 +1228,9 @@ static int _bursts(struct pl330_dmac *pl330, unsigned dry_run, u8 buf[], } /* - * transfer dregs with single transfers to peripheral, or a reduced size burst - * for mem-to-mem. + * only the unaligned burst transfers have the dregs. + * so, still transfer dregs with a reduced size burst + * for mem-to-mem, mem-to-dev or dev-to-mem. */ static int _dregs(struct pl330_dmac *pl330, unsigned int dry_run, u8 buf[], const struct _xfer_spec *pxs, int transfer_length) @@ -1240,22 +1241,31 @@ static int _dregs(struct pl330_dmac *pl330, unsigned int dry_run, u8 buf[], if (transfer_length == 0) return off; + /* + * dregs_len = (total bytes - BURST_TO_BYTE(bursts, ccr)) / + * BRST_SIZE(ccr) + * the dregs len must be smaller than burst len, + * so, for higher efficiency, we can modify CCR + * to use a reduced size burst len for the dregs. + */ + dregs_ccr = pxs->ccr; + dregs_ccr &= ~((0xf << CC_SRCBRSTLEN_SHFT) | + (0xf << CC_DSTBRSTLEN_SHFT)); + dregs_ccr |= (((transfer_length - 1) & 0xf) << + CC_SRCBRSTLEN_SHFT); + dregs_ccr |= (((transfer_length - 1) & 0xf) << + CC_DSTBRSTLEN_SHFT); + switch (pxs->desc->rqtype) { case DMA_MEM_TO_DEV: /* fall through */ case DMA_DEV_TO_MEM: - off += _ldst_peripheral(pl330, dry_run, &buf[off], pxs, - transfer_length, SINGLE); + off += _emit_MOV(dry_run, &buf[off], CCR, dregs_ccr); + off += _ldst_peripheral(pl330, dry_run, &buf[off], pxs, 1, + BURST); break; case DMA_MEM_TO_MEM: - dregs_ccr = pxs->ccr; - dregs_ccr &= ~((0xf << CC_SRCBRSTLEN_SHFT) | - (0xf << CC_DSTBRSTLEN_SHFT)); - dregs_ccr |= (((transfer_length - 1) & 0xf) << - CC_SRCBRSTLEN_SHFT); - dregs_ccr |= (((transfer_length - 1) & 0xf) << - CC_DSTBRSTLEN_SHFT); off += _emit_MOV(dry_run, &buf[off], CCR, dregs_ccr); off += _ldst_memtomem(dry_run, &buf[off], pxs, 1); break; -- GitLab From 211138e680dc7597b79c10f8ff6c6a1e73d3408d Mon Sep 17 00:00:00 2001 From: Sugar Zhang Date: Mon, 29 Jun 2020 22:05:44 +0800 Subject: [PATCH 0416/1476] dt-bindings: dma: pl330: Document the quirk 'arm,pl330-periph-burst' This patch Adds the quirk 'arm,pl330-periph-burst' for pl330. Signed-off-by: Sugar Zhang Acked-by: Rob Herring Link: https://lore.kernel.org/r/1593439555-68130-4-git-send-email-sugar.zhang@rock-chips.com Signed-off-by: Vinod Koul --- Documentation/devicetree/bindings/dma/arm-pl330.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/dma/arm-pl330.txt b/Documentation/devicetree/bindings/dma/arm-pl330.txt index 2c7fd1941abbb..315e90122afaf 100644 --- a/Documentation/devicetree/bindings/dma/arm-pl330.txt +++ b/Documentation/devicetree/bindings/dma/arm-pl330.txt @@ -16,6 +16,7 @@ Optional properties: - dma-channels: contains the total number of DMA channels supported by the DMAC - dma-requests: contains the total number of DMA requests supported by the DMAC - arm,pl330-broken-no-flushp: quirk for avoiding to execute DMAFLUSHP + - arm,pl330-periph-burst: quirk for performing burst transfer only - resets: contains an entry for each entry in reset-names. See ../reset/reset.txt for details. - reset-names: must contain at least "dma", and optional is "dma-ocp". -- GitLab From 5fb9e3a3423313fe6169d5069e471bfdab6e0b79 Mon Sep 17 00:00:00 2001 From: Sugar Zhang Date: Mon, 29 Jun 2020 22:05:45 +0800 Subject: [PATCH 0417/1476] dmaengine: pl330: Add quirk 'arm,pl330-periph-burst' This patch adds the qurik to use burst transfers only for pl330 controller, even for request with a length of 1. Although, the correct way should be: if the peripheral request length is 1, the peripheral should use SINGLE request, and then notify the dmac using SINGLE mode by src/dst_maxburst with 1. For example, on the Rockchip SoCs, all the peripherals can use SINGLE or BURST request by setting GRF registers. it is possible that if these peripheral drivers are used only for Rockchip SoCs. Unfortunately, it's not, such as dw uart, which is used so widely, and we can't set src/dst_maxburst according to the SoCs' specific to compatible with all the other SoCs. So, for convenience, all the peripherals are set as BURST request by default on the Rockchip SoCs. even for request with a length of 1. the current pl330 driver will perform SINGLE transfer if the client's maxburst is 1, which still should be working according to chapter 2.6.6 of datasheet which describe how DMAC performs SINGLE transfers for a BURST request. Unfortunately, it's broken on the Rockchip SoCs, which support only matching transfers, such as BURST transfer for BURST request, SINGLE transfer for SINGLE request. Finally, we add the quirk to specify pl330 to use burst transfers only. Signed-off-by: Sugar Zhang Link: https://lore.kernel.org/r/1593439555-68130-5-git-send-email-sugar.zhang@rock-chips.com Signed-off-by: Vinod Koul --- drivers/dma/pl330.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/drivers/dma/pl330.c b/drivers/dma/pl330.c index f1f0176c6c057..3be8d462eab41 100644 --- a/drivers/dma/pl330.c +++ b/drivers/dma/pl330.c @@ -33,7 +33,8 @@ #define PL330_MAX_PERI 32 #define PL330_MAX_BURST 16 -#define PL330_QUIRK_BROKEN_NO_FLUSHP BIT(0) +#define PL330_QUIRK_BROKEN_NO_FLUSHP BIT(0) +#define PL330_QUIRK_PERIPH_BURST BIT(1) enum pl330_cachectrl { CCTRL0, /* Noncacheable and nonbufferable */ @@ -509,6 +510,10 @@ static struct pl330_of_quirks { { .quirk = "arm,pl330-broken-no-flushp", .id = PL330_QUIRK_BROKEN_NO_FLUSHP, + }, + { + .quirk = "arm,pl330-periph-burst", + .id = PL330_QUIRK_PERIPH_BURST, } }; @@ -1206,6 +1211,9 @@ static int _bursts(struct pl330_dmac *pl330, unsigned dry_run, u8 buf[], int off = 0; enum pl330_cond cond = BRST_LEN(pxs->ccr) > 1 ? BURST : SINGLE; + if (pl330->quirks & PL330_QUIRK_PERIPH_BURST) + cond = BURST; + switch (pxs->desc->rqtype) { case DMA_MEM_TO_DEV: /* fall through */ -- GitLab From d6a7bb869dd8a516901591136a9a895fd829d6c6 Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Mon, 13 Jul 2020 23:35:39 -0700 Subject: [PATCH 0418/1476] dmaengine: idxd: fix PCI_MSI build errors Fix build errors when CONFIG_PCI_MSI is not enabled by making the driver depend on PCI_MSI: ld: drivers/dma/idxd/device.o: in function `idxd_mask_msix_vector': device.c:(.text+0x26f): undefined reference to `pci_msi_mask_irq' ld: drivers/dma/idxd/device.o: in function `idxd_unmask_msix_vector': device.c:(.text+0x2af): undefined reference to `pci_msi_unmask_irq' Signed-off-by: Randy Dunlap Cc: Dave Jiang Cc: dmaengine@vger.kernel.org Cc: Vinod Koul Link: https://lore.kernel.org/r/9dee3f46-70d9-ea75-10cb-5527ab297d1d@infradead.org Signed-off-by: Vinod Koul --- drivers/dma/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig index b70e90765ad35..bfe0731f37642 100644 --- a/drivers/dma/Kconfig +++ b/drivers/dma/Kconfig @@ -285,6 +285,7 @@ config INTEL_IDMA64 config INTEL_IDXD tristate "Intel Data Accelerators support" depends on PCI && X86_64 + depends on PCI_MSI depends on SBITMAP select DMA_ENGINE help -- GitLab From 2166d96029bc88a90f431aca7f51e714c653e027 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Tue, 7 Jul 2020 13:23:48 +0300 Subject: [PATCH 0419/1476] dmaengine: ti: k3-udma: Remove dma_sync_single calls for descriptors The descriptors are allocated via wither dma_pool or dma_alloc_coherent. There is no need for the dma_sync_singel_* calls. Signed-off-by: Peter Ujfalusi Reviewed-by: Grygorii Strashko Link: https://lore.kernel.org/r/20200707102352.28773-2-peter.ujfalusi@ti.com Signed-off-by: Vinod Koul --- drivers/dma/ti/k3-udma.c | 37 ++----------------------------------- 1 file changed, 2 insertions(+), 35 deletions(-) diff --git a/drivers/dma/ti/k3-udma.c b/drivers/dma/ti/k3-udma.c index 945b7c604f917..f5c1373e9225d 100644 --- a/drivers/dma/ti/k3-udma.c +++ b/drivers/dma/ti/k3-udma.c @@ -539,30 +539,6 @@ static bool udma_is_chan_paused(struct udma_chan *uc) return false; } -static void udma_sync_for_device(struct udma_chan *uc, int idx) -{ - struct udma_desc *d = uc->desc; - - if (uc->cyclic && uc->config.pkt_mode) { - dma_sync_single_for_device(uc->ud->dev, - d->hwdesc[idx].cppi5_desc_paddr, - d->hwdesc[idx].cppi5_desc_size, - DMA_TO_DEVICE); - } else { - int i; - - for (i = 0; i < d->hwdesc_count; i++) { - if (!d->hwdesc[i].cppi5_desc_vaddr) - continue; - - dma_sync_single_for_device(uc->ud->dev, - d->hwdesc[i].cppi5_desc_paddr, - d->hwdesc[i].cppi5_desc_size, - DMA_TO_DEVICE); - } - } -} - static inline dma_addr_t udma_get_rx_flush_hwdesc_paddr(struct udma_chan *uc) { return uc->ud->rx_flush.hwdescs[uc->config.pkt_mode].cppi5_desc_paddr; @@ -593,7 +569,6 @@ static int udma_push_to_ring(struct udma_chan *uc, int idx) paddr = udma_curr_cppi5_desc_paddr(d, idx); wmb(); /* Ensure that writes are not moved over this point */ - udma_sync_for_device(uc, idx); } return k3_ringacc_ring_push(ring, &paddr); @@ -628,12 +603,12 @@ static int udma_pop_from_ring(struct udma_chan *uc, dma_addr_t *addr) } if (ring && k3_ringacc_ring_get_occ(ring)) { - struct udma_desc *d = NULL; - ret = k3_ringacc_ring_pop(ring, addr); if (ret) return ret; + rmb(); /* Ensure that reads are not moved before this point */ + /* Teardown completion */ if (cppi5_desc_is_tdcm(*addr)) return ret; @@ -641,14 +616,6 @@ static int udma_pop_from_ring(struct udma_chan *uc, dma_addr_t *addr) /* Check for flush descriptor */ if (udma_desc_is_rx_flush(uc, *addr)) return -ENOENT; - - d = udma_udma_desc_from_paddr(uc, *addr); - - if (d) - dma_sync_single_for_cpu(uc->ud->dev, *addr, - d->hwdesc[0].cppi5_desc_size, - DMA_FROM_DEVICE); - rmb(); /* Ensure that reads are not moved before this point */ } return ret; -- GitLab From 3b8bee2a96ce7a5e47239f3a0cb39caffa8358a4 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Tue, 7 Jul 2020 13:23:49 +0300 Subject: [PATCH 0420/1476] dmaengine: ti: k3-udma: Do not use ring_get_occ in udma_pop_from_ring The ring_get_occ is redundant as the k3_ringacc_ring_pop() is also checking the occ of the ring. With removing the ring_get_occ, the function can be simplified as well. Signed-off-by: Peter Ujfalusi Reviewed-by: Grygorii Strashko Link: https://lore.kernel.org/r/20200707102352.28773-3-peter.ujfalusi@ti.com Signed-off-by: Vinod Koul --- drivers/dma/ti/k3-udma.c | 28 +++++++++++++--------------- 1 file changed, 13 insertions(+), 15 deletions(-) diff --git a/drivers/dma/ti/k3-udma.c b/drivers/dma/ti/k3-udma.c index f5c1373e9225d..08f58d7ed8d50 100644 --- a/drivers/dma/ti/k3-udma.c +++ b/drivers/dma/ti/k3-udma.c @@ -588,7 +588,7 @@ static bool udma_desc_is_rx_flush(struct udma_chan *uc, dma_addr_t addr) static int udma_pop_from_ring(struct udma_chan *uc, dma_addr_t *addr) { struct k3_ring *ring = NULL; - int ret = -ENOENT; + int ret; switch (uc->config.dir) { case DMA_DEV_TO_MEM: @@ -599,26 +599,24 @@ static int udma_pop_from_ring(struct udma_chan *uc, dma_addr_t *addr) ring = uc->tchan->tc_ring; break; default: - break; + return -ENOENT; } - if (ring && k3_ringacc_ring_get_occ(ring)) { - ret = k3_ringacc_ring_pop(ring, addr); - if (ret) - return ret; + ret = k3_ringacc_ring_pop(ring, addr); + if (ret) + return ret; - rmb(); /* Ensure that reads are not moved before this point */ + rmb(); /* Ensure that reads are not moved before this point */ - /* Teardown completion */ - if (cppi5_desc_is_tdcm(*addr)) - return ret; + /* Teardown completion */ + if (cppi5_desc_is_tdcm(*addr)) + return 0; - /* Check for flush descriptor */ - if (udma_desc_is_rx_flush(uc, *addr)) - return -ENOENT; - } + /* Check for flush descriptor */ + if (udma_desc_is_rx_flush(uc, *addr)) + return -ENOENT; - return ret; + return 0; } static void udma_reset_rings(struct udma_chan *uc) -- GitLab From bc7e55239c7aecfb2896c4c08638fc5682e8de01 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Tue, 7 Jul 2020 13:23:50 +0300 Subject: [PATCH 0421/1476] dmaengine: ti: k3-udma: Use common defines for TCHANRT/RCHANRT registers The register offsets and functions are the same among TCHAN and RCHAN. Use generic, common names for them. Signed-off-by: Peter Ujfalusi Reviewed-by: Grygorii Strashko Link: https://lore.kernel.org/r/20200707102352.28773-4-peter.ujfalusi@ti.com Signed-off-by: Vinod Koul --- drivers/dma/ti/k3-udma-glue.c | 79 ++++++++++++----------- drivers/dma/ti/k3-udma.c | 114 +++++++++++++++++----------------- drivers/dma/ti/k3-udma.h | 61 +++++++----------- 3 files changed, 115 insertions(+), 139 deletions(-) diff --git a/drivers/dma/ti/k3-udma-glue.c b/drivers/dma/ti/k3-udma-glue.c index 64c8955e0cf13..d66ed18303a40 100644 --- a/drivers/dma/ti/k3-udma-glue.c +++ b/drivers/dma/ti/k3-udma-glue.c @@ -186,17 +186,17 @@ static void k3_udma_glue_dump_tx_rt_chn(struct k3_udma_glue_tx_channel *chn, struct device *dev = chn->common.dev; dev_dbg(dev, "=== dump ===> %s\n", mark); - dev_dbg(dev, "0x%08X: %08X\n", UDMA_TCHAN_RT_CTL_REG, - xudma_tchanrt_read(chn->udma_tchanx, UDMA_TCHAN_RT_CTL_REG)); - dev_dbg(dev, "0x%08X: %08X\n", UDMA_TCHAN_RT_PEER_RT_EN_REG, + dev_dbg(dev, "0x%08X: %08X\n", UDMA_CHAN_RT_CTL_REG, + xudma_tchanrt_read(chn->udma_tchanx, UDMA_CHAN_RT_CTL_REG)); + dev_dbg(dev, "0x%08X: %08X\n", UDMA_CHAN_RT_PEER_RT_EN_REG, xudma_tchanrt_read(chn->udma_tchanx, - UDMA_TCHAN_RT_PEER_RT_EN_REG)); - dev_dbg(dev, "0x%08X: %08X\n", UDMA_TCHAN_RT_PCNT_REG, - xudma_tchanrt_read(chn->udma_tchanx, UDMA_TCHAN_RT_PCNT_REG)); - dev_dbg(dev, "0x%08X: %08X\n", UDMA_TCHAN_RT_BCNT_REG, - xudma_tchanrt_read(chn->udma_tchanx, UDMA_TCHAN_RT_BCNT_REG)); - dev_dbg(dev, "0x%08X: %08X\n", UDMA_TCHAN_RT_SBCNT_REG, - xudma_tchanrt_read(chn->udma_tchanx, UDMA_TCHAN_RT_SBCNT_REG)); + UDMA_CHAN_RT_PEER_RT_EN_REG)); + dev_dbg(dev, "0x%08X: %08X\n", UDMA_CHAN_RT_PCNT_REG, + xudma_tchanrt_read(chn->udma_tchanx, UDMA_CHAN_RT_PCNT_REG)); + dev_dbg(dev, "0x%08X: %08X\n", UDMA_CHAN_RT_BCNT_REG, + xudma_tchanrt_read(chn->udma_tchanx, UDMA_CHAN_RT_BCNT_REG)); + dev_dbg(dev, "0x%08X: %08X\n", UDMA_CHAN_RT_SBCNT_REG, + xudma_tchanrt_read(chn->udma_tchanx, UDMA_CHAN_RT_SBCNT_REG)); } static int k3_udma_glue_cfg_tx_chn(struct k3_udma_glue_tx_channel *tx_chn) @@ -389,14 +389,13 @@ int k3_udma_glue_enable_tx_chn(struct k3_udma_glue_tx_channel *tx_chn) u32 txrt_ctl; txrt_ctl = UDMA_PEER_RT_EN_ENABLE; - xudma_tchanrt_write(tx_chn->udma_tchanx, - UDMA_TCHAN_RT_PEER_RT_EN_REG, + xudma_tchanrt_write(tx_chn->udma_tchanx, UDMA_CHAN_RT_PEER_RT_EN_REG, txrt_ctl); txrt_ctl = xudma_tchanrt_read(tx_chn->udma_tchanx, - UDMA_TCHAN_RT_CTL_REG); + UDMA_CHAN_RT_CTL_REG); txrt_ctl |= UDMA_CHAN_RT_CTL_EN; - xudma_tchanrt_write(tx_chn->udma_tchanx, UDMA_TCHAN_RT_CTL_REG, + xudma_tchanrt_write(tx_chn->udma_tchanx, UDMA_CHAN_RT_CTL_REG, txrt_ctl); k3_udma_glue_dump_tx_rt_chn(tx_chn, "txchn en"); @@ -408,10 +407,10 @@ void k3_udma_glue_disable_tx_chn(struct k3_udma_glue_tx_channel *tx_chn) { k3_udma_glue_dump_tx_rt_chn(tx_chn, "txchn dis1"); - xudma_tchanrt_write(tx_chn->udma_tchanx, UDMA_TCHAN_RT_CTL_REG, 0); + xudma_tchanrt_write(tx_chn->udma_tchanx, UDMA_CHAN_RT_CTL_REG, 0); xudma_tchanrt_write(tx_chn->udma_tchanx, - UDMA_TCHAN_RT_PEER_RT_EN_REG, 0); + UDMA_CHAN_RT_PEER_RT_EN_REG, 0); k3_udma_glue_dump_tx_rt_chn(tx_chn, "txchn dis2"); } EXPORT_SYMBOL_GPL(k3_udma_glue_disable_tx_chn); @@ -424,14 +423,14 @@ void k3_udma_glue_tdown_tx_chn(struct k3_udma_glue_tx_channel *tx_chn, k3_udma_glue_dump_tx_rt_chn(tx_chn, "txchn tdown1"); - xudma_tchanrt_write(tx_chn->udma_tchanx, UDMA_TCHAN_RT_CTL_REG, + xudma_tchanrt_write(tx_chn->udma_tchanx, UDMA_CHAN_RT_CTL_REG, UDMA_CHAN_RT_CTL_EN | UDMA_CHAN_RT_CTL_TDOWN); - val = xudma_tchanrt_read(tx_chn->udma_tchanx, UDMA_TCHAN_RT_CTL_REG); + val = xudma_tchanrt_read(tx_chn->udma_tchanx, UDMA_CHAN_RT_CTL_REG); while (sync && (val & UDMA_CHAN_RT_CTL_EN)) { val = xudma_tchanrt_read(tx_chn->udma_tchanx, - UDMA_TCHAN_RT_CTL_REG); + UDMA_CHAN_RT_CTL_REG); udelay(1); if (i > K3_UDMAX_TDOWN_TIMEOUT_US) { dev_err(tx_chn->common.dev, "TX tdown timeout\n"); @@ -441,7 +440,7 @@ void k3_udma_glue_tdown_tx_chn(struct k3_udma_glue_tx_channel *tx_chn, } val = xudma_tchanrt_read(tx_chn->udma_tchanx, - UDMA_TCHAN_RT_PEER_RT_EN_REG); + UDMA_CHAN_RT_PEER_RT_EN_REG); if (sync && (val & UDMA_PEER_RT_EN_ENABLE)) dev_err(tx_chn->common.dev, "TX tdown peer not stopped\n"); k3_udma_glue_dump_tx_rt_chn(tx_chn, "txchn tdown2"); @@ -716,17 +715,17 @@ static void k3_udma_glue_dump_rx_rt_chn(struct k3_udma_glue_rx_channel *chn, dev_dbg(dev, "=== dump ===> %s\n", mark); - dev_dbg(dev, "0x%08X: %08X\n", UDMA_RCHAN_RT_CTL_REG, - xudma_rchanrt_read(chn->udma_rchanx, UDMA_RCHAN_RT_CTL_REG)); - dev_dbg(dev, "0x%08X: %08X\n", UDMA_RCHAN_RT_PEER_RT_EN_REG, + dev_dbg(dev, "0x%08X: %08X\n", UDMA_CHAN_RT_CTL_REG, + xudma_rchanrt_read(chn->udma_rchanx, UDMA_CHAN_RT_CTL_REG)); + dev_dbg(dev, "0x%08X: %08X\n", UDMA_CHAN_RT_PEER_RT_EN_REG, xudma_rchanrt_read(chn->udma_rchanx, - UDMA_RCHAN_RT_PEER_RT_EN_REG)); - dev_dbg(dev, "0x%08X: %08X\n", UDMA_RCHAN_RT_PCNT_REG, - xudma_rchanrt_read(chn->udma_rchanx, UDMA_RCHAN_RT_PCNT_REG)); - dev_dbg(dev, "0x%08X: %08X\n", UDMA_RCHAN_RT_BCNT_REG, - xudma_rchanrt_read(chn->udma_rchanx, UDMA_RCHAN_RT_BCNT_REG)); - dev_dbg(dev, "0x%08X: %08X\n", UDMA_RCHAN_RT_SBCNT_REG, - xudma_rchanrt_read(chn->udma_rchanx, UDMA_RCHAN_RT_SBCNT_REG)); + UDMA_CHAN_RT_PEER_RT_EN_REG)); + dev_dbg(dev, "0x%08X: %08X\n", UDMA_CHAN_RT_PCNT_REG, + xudma_rchanrt_read(chn->udma_rchanx, UDMA_CHAN_RT_PCNT_REG)); + dev_dbg(dev, "0x%08X: %08X\n", UDMA_CHAN_RT_BCNT_REG, + xudma_rchanrt_read(chn->udma_rchanx, UDMA_CHAN_RT_BCNT_REG)); + dev_dbg(dev, "0x%08X: %08X\n", UDMA_CHAN_RT_SBCNT_REG, + xudma_rchanrt_read(chn->udma_rchanx, UDMA_CHAN_RT_SBCNT_REG)); } static int @@ -1084,13 +1083,12 @@ int k3_udma_glue_enable_rx_chn(struct k3_udma_glue_rx_channel *rx_chn) return -EINVAL; rxrt_ctl = xudma_rchanrt_read(rx_chn->udma_rchanx, - UDMA_RCHAN_RT_CTL_REG); + UDMA_CHAN_RT_CTL_REG); rxrt_ctl |= UDMA_CHAN_RT_CTL_EN; - xudma_rchanrt_write(rx_chn->udma_rchanx, UDMA_RCHAN_RT_CTL_REG, + xudma_rchanrt_write(rx_chn->udma_rchanx, UDMA_CHAN_RT_CTL_REG, rxrt_ctl); - xudma_rchanrt_write(rx_chn->udma_rchanx, - UDMA_RCHAN_RT_PEER_RT_EN_REG, + xudma_rchanrt_write(rx_chn->udma_rchanx, UDMA_CHAN_RT_PEER_RT_EN_REG, UDMA_PEER_RT_EN_ENABLE); k3_udma_glue_dump_rx_rt_chn(rx_chn, "rxrt en"); @@ -1103,9 +1101,8 @@ void k3_udma_glue_disable_rx_chn(struct k3_udma_glue_rx_channel *rx_chn) k3_udma_glue_dump_rx_rt_chn(rx_chn, "rxrt dis1"); xudma_rchanrt_write(rx_chn->udma_rchanx, - UDMA_RCHAN_RT_PEER_RT_EN_REG, - 0); - xudma_rchanrt_write(rx_chn->udma_rchanx, UDMA_RCHAN_RT_CTL_REG, 0); + UDMA_CHAN_RT_PEER_RT_EN_REG, 0); + xudma_rchanrt_write(rx_chn->udma_rchanx, UDMA_CHAN_RT_CTL_REG, 0); k3_udma_glue_dump_rx_rt_chn(rx_chn, "rxrt dis2"); } @@ -1122,14 +1119,14 @@ void k3_udma_glue_tdown_rx_chn(struct k3_udma_glue_rx_channel *rx_chn, k3_udma_glue_dump_rx_rt_chn(rx_chn, "rxrt tdown1"); - xudma_rchanrt_write(rx_chn->udma_rchanx, UDMA_RCHAN_RT_PEER_RT_EN_REG, + xudma_rchanrt_write(rx_chn->udma_rchanx, UDMA_CHAN_RT_PEER_RT_EN_REG, UDMA_PEER_RT_EN_ENABLE | UDMA_PEER_RT_EN_TEARDOWN); - val = xudma_rchanrt_read(rx_chn->udma_rchanx, UDMA_RCHAN_RT_CTL_REG); + val = xudma_rchanrt_read(rx_chn->udma_rchanx, UDMA_CHAN_RT_CTL_REG); while (sync && (val & UDMA_CHAN_RT_CTL_EN)) { val = xudma_rchanrt_read(rx_chn->udma_rchanx, - UDMA_RCHAN_RT_CTL_REG); + UDMA_CHAN_RT_CTL_REG); udelay(1); if (i > K3_UDMAX_TDOWN_TIMEOUT_US) { dev_err(rx_chn->common.dev, "RX tdown timeout\n"); @@ -1139,7 +1136,7 @@ void k3_udma_glue_tdown_rx_chn(struct k3_udma_glue_rx_channel *rx_chn, } val = xudma_rchanrt_read(rx_chn->udma_rchanx, - UDMA_RCHAN_RT_PEER_RT_EN_REG); + UDMA_CHAN_RT_PEER_RT_EN_REG); if (sync && (val & UDMA_PEER_RT_EN_ENABLE)) dev_err(rx_chn->common.dev, "TX tdown peer not stopped\n"); k3_udma_glue_dump_rx_rt_chn(rx_chn, "rxrt tdown2"); diff --git a/drivers/dma/ti/k3-udma.c b/drivers/dma/ti/k3-udma.c index 08f58d7ed8d50..2fb294c975642 100644 --- a/drivers/dma/ti/k3-udma.c +++ b/drivers/dma/ti/k3-udma.c @@ -366,7 +366,7 @@ static void udma_dump_chan_stdata(struct udma_chan *uc) if (uc->config.dir == DMA_MEM_TO_DEV || uc->config.dir == DMA_MEM_TO_MEM) { dev_dbg(dev, "TCHAN State data:\n"); for (i = 0; i < 32; i++) { - offset = UDMA_TCHAN_RT_STDATA_REG + i * 4; + offset = UDMA_CHAN_RT_STDATA_REG + i * 4; dev_dbg(dev, "TRT_STDATA[%02d]: 0x%08x\n", i, udma_tchanrt_read(uc->tchan, offset)); } @@ -375,7 +375,7 @@ static void udma_dump_chan_stdata(struct udma_chan *uc) if (uc->config.dir == DMA_DEV_TO_MEM || uc->config.dir == DMA_MEM_TO_MEM) { dev_dbg(dev, "RCHAN State data:\n"); for (i = 0; i < 32; i++) { - offset = UDMA_RCHAN_RT_STDATA_REG + i * 4; + offset = UDMA_CHAN_RT_STDATA_REG + i * 4; dev_dbg(dev, "RRT_STDATA[%02d]: 0x%08x\n", i, udma_rchanrt_read(uc->rchan, offset)); } @@ -500,9 +500,9 @@ static bool udma_is_chan_running(struct udma_chan *uc) u32 rrt_ctl = 0; if (uc->tchan) - trt_ctl = udma_tchanrt_read(uc->tchan, UDMA_TCHAN_RT_CTL_REG); + trt_ctl = udma_tchanrt_read(uc->tchan, UDMA_CHAN_RT_CTL_REG); if (uc->rchan) - rrt_ctl = udma_rchanrt_read(uc->rchan, UDMA_RCHAN_RT_CTL_REG); + rrt_ctl = udma_rchanrt_read(uc->rchan, UDMA_CHAN_RT_CTL_REG); if (trt_ctl & UDMA_CHAN_RT_CTL_EN || rrt_ctl & UDMA_CHAN_RT_CTL_EN) return true; @@ -516,17 +516,15 @@ static bool udma_is_chan_paused(struct udma_chan *uc) switch (uc->config.dir) { case DMA_DEV_TO_MEM: - val = udma_rchanrt_read(uc->rchan, - UDMA_RCHAN_RT_PEER_RT_EN_REG); + val = udma_rchanrt_read(uc->rchan, UDMA_CHAN_RT_PEER_RT_EN_REG); pause_mask = UDMA_PEER_RT_EN_PAUSE; break; case DMA_MEM_TO_DEV: - val = udma_tchanrt_read(uc->tchan, - UDMA_TCHAN_RT_PEER_RT_EN_REG); + val = udma_tchanrt_read(uc->tchan, UDMA_CHAN_RT_PEER_RT_EN_REG); pause_mask = UDMA_PEER_RT_EN_PAUSE; break; case DMA_MEM_TO_MEM: - val = udma_tchanrt_read(uc->tchan, UDMA_TCHAN_RT_CTL_REG); + val = udma_tchanrt_read(uc->tchan, UDMA_CHAN_RT_CTL_REG); pause_mask = UDMA_CHAN_RT_CTL_PAUSE; break; default: @@ -660,31 +658,31 @@ static void udma_reset_counters(struct udma_chan *uc) u32 val; if (uc->tchan) { - val = udma_tchanrt_read(uc->tchan, UDMA_TCHAN_RT_BCNT_REG); - udma_tchanrt_write(uc->tchan, UDMA_TCHAN_RT_BCNT_REG, val); + val = udma_tchanrt_read(uc->tchan, UDMA_CHAN_RT_BCNT_REG); + udma_tchanrt_write(uc->tchan, UDMA_CHAN_RT_BCNT_REG, val); - val = udma_tchanrt_read(uc->tchan, UDMA_TCHAN_RT_SBCNT_REG); - udma_tchanrt_write(uc->tchan, UDMA_TCHAN_RT_SBCNT_REG, val); + val = udma_tchanrt_read(uc->tchan, UDMA_CHAN_RT_SBCNT_REG); + udma_tchanrt_write(uc->tchan, UDMA_CHAN_RT_SBCNT_REG, val); - val = udma_tchanrt_read(uc->tchan, UDMA_TCHAN_RT_PCNT_REG); - udma_tchanrt_write(uc->tchan, UDMA_TCHAN_RT_PCNT_REG, val); + val = udma_tchanrt_read(uc->tchan, UDMA_CHAN_RT_PCNT_REG); + udma_tchanrt_write(uc->tchan, UDMA_CHAN_RT_PCNT_REG, val); - val = udma_tchanrt_read(uc->tchan, UDMA_TCHAN_RT_PEER_BCNT_REG); - udma_tchanrt_write(uc->tchan, UDMA_TCHAN_RT_PEER_BCNT_REG, val); + val = udma_tchanrt_read(uc->tchan, UDMA_CHAN_RT_PEER_BCNT_REG); + udma_tchanrt_write(uc->tchan, UDMA_CHAN_RT_PEER_BCNT_REG, val); } if (uc->rchan) { - val = udma_rchanrt_read(uc->rchan, UDMA_RCHAN_RT_BCNT_REG); - udma_rchanrt_write(uc->rchan, UDMA_RCHAN_RT_BCNT_REG, val); + val = udma_rchanrt_read(uc->rchan, UDMA_CHAN_RT_BCNT_REG); + udma_rchanrt_write(uc->rchan, UDMA_CHAN_RT_BCNT_REG, val); - val = udma_rchanrt_read(uc->rchan, UDMA_RCHAN_RT_SBCNT_REG); - udma_rchanrt_write(uc->rchan, UDMA_RCHAN_RT_SBCNT_REG, val); + val = udma_rchanrt_read(uc->rchan, UDMA_CHAN_RT_SBCNT_REG); + udma_rchanrt_write(uc->rchan, UDMA_CHAN_RT_SBCNT_REG, val); - val = udma_rchanrt_read(uc->rchan, UDMA_RCHAN_RT_PCNT_REG); - udma_rchanrt_write(uc->rchan, UDMA_RCHAN_RT_PCNT_REG, val); + val = udma_rchanrt_read(uc->rchan, UDMA_CHAN_RT_PCNT_REG); + udma_rchanrt_write(uc->rchan, UDMA_CHAN_RT_PCNT_REG, val); - val = udma_rchanrt_read(uc->rchan, UDMA_RCHAN_RT_PEER_BCNT_REG); - udma_rchanrt_write(uc->rchan, UDMA_RCHAN_RT_PEER_BCNT_REG, val); + val = udma_rchanrt_read(uc->rchan, UDMA_CHAN_RT_PEER_BCNT_REG); + udma_rchanrt_write(uc->rchan, UDMA_CHAN_RT_PEER_BCNT_REG, val); } uc->bcnt = 0; @@ -694,16 +692,16 @@ static int udma_reset_chan(struct udma_chan *uc, bool hard) { switch (uc->config.dir) { case DMA_DEV_TO_MEM: - udma_rchanrt_write(uc->rchan, UDMA_RCHAN_RT_PEER_RT_EN_REG, 0); - udma_rchanrt_write(uc->rchan, UDMA_RCHAN_RT_CTL_REG, 0); + udma_rchanrt_write(uc->rchan, UDMA_CHAN_RT_PEER_RT_EN_REG, 0); + udma_rchanrt_write(uc->rchan, UDMA_CHAN_RT_CTL_REG, 0); break; case DMA_MEM_TO_DEV: - udma_tchanrt_write(uc->tchan, UDMA_TCHAN_RT_CTL_REG, 0); - udma_tchanrt_write(uc->tchan, UDMA_TCHAN_RT_PEER_RT_EN_REG, 0); + udma_tchanrt_write(uc->tchan, UDMA_CHAN_RT_CTL_REG, 0); + udma_tchanrt_write(uc->tchan, UDMA_CHAN_RT_PEER_RT_EN_REG, 0); break; case DMA_MEM_TO_MEM: - udma_rchanrt_write(uc->rchan, UDMA_RCHAN_RT_CTL_REG, 0); - udma_tchanrt_write(uc->tchan, UDMA_TCHAN_RT_CTL_REG, 0); + udma_rchanrt_write(uc->rchan, UDMA_CHAN_RT_CTL_REG, 0); + udma_tchanrt_write(uc->tchan, UDMA_CHAN_RT_CTL_REG, 0); break; default: return -EINVAL; @@ -731,7 +729,7 @@ static int udma_reset_chan(struct udma_chan *uc, bool hard) * the rchan. */ if (uc->config.dir == DMA_DEV_TO_MEM) - udma_rchanrt_write(uc->rchan, UDMA_RCHAN_RT_CTL_REG, + udma_rchanrt_write(uc->rchan, UDMA_CHAN_RT_CTL_REG, UDMA_CHAN_RT_CTL_EN | UDMA_CHAN_RT_CTL_TDOWN | UDMA_CHAN_RT_CTL_FTDOWN); @@ -809,10 +807,10 @@ static int udma_start(struct udma_chan *uc) val |= PDMA_STATIC_TR_XY_BURST; udma_rchanrt_write(uc->rchan, - UDMA_RCHAN_RT_PEER_STATIC_TR_XY_REG, val); + UDMA_CHAN_RT_PEER_STATIC_TR_XY_REG, val); udma_rchanrt_write(uc->rchan, - UDMA_RCHAN_RT_PEER_STATIC_TR_Z_REG, + UDMA_CHAN_RT_PEER_STATIC_TR_Z_REG, PDMA_STATIC_TR_Z(uc->desc->static_tr.bstcnt, match_data->statictr_z_mask)); @@ -821,11 +819,11 @@ static int udma_start(struct udma_chan *uc) sizeof(uc->static_tr)); } - udma_rchanrt_write(uc->rchan, UDMA_RCHAN_RT_CTL_REG, + udma_rchanrt_write(uc->rchan, UDMA_CHAN_RT_CTL_REG, UDMA_CHAN_RT_CTL_EN); /* Enable remote */ - udma_rchanrt_write(uc->rchan, UDMA_RCHAN_RT_PEER_RT_EN_REG, + udma_rchanrt_write(uc->rchan, UDMA_CHAN_RT_PEER_RT_EN_REG, UDMA_PEER_RT_EN_ENABLE); break; @@ -841,7 +839,7 @@ static int udma_start(struct udma_chan *uc) val |= PDMA_STATIC_TR_XY_BURST; udma_tchanrt_write(uc->tchan, - UDMA_TCHAN_RT_PEER_STATIC_TR_XY_REG, val); + UDMA_CHAN_RT_PEER_STATIC_TR_XY_REG, val); /* save the current staticTR configuration */ memcpy(&uc->static_tr, &uc->desc->static_tr, @@ -849,17 +847,17 @@ static int udma_start(struct udma_chan *uc) } /* Enable remote */ - udma_tchanrt_write(uc->tchan, UDMA_TCHAN_RT_PEER_RT_EN_REG, + udma_tchanrt_write(uc->tchan, UDMA_CHAN_RT_PEER_RT_EN_REG, UDMA_PEER_RT_EN_ENABLE); - udma_tchanrt_write(uc->tchan, UDMA_TCHAN_RT_CTL_REG, + udma_tchanrt_write(uc->tchan, UDMA_CHAN_RT_CTL_REG, UDMA_CHAN_RT_CTL_EN); break; case DMA_MEM_TO_MEM: - udma_rchanrt_write(uc->rchan, UDMA_RCHAN_RT_CTL_REG, + udma_rchanrt_write(uc->rchan, UDMA_CHAN_RT_CTL_REG, UDMA_CHAN_RT_CTL_EN); - udma_tchanrt_write(uc->tchan, UDMA_TCHAN_RT_CTL_REG, + udma_tchanrt_write(uc->tchan, UDMA_CHAN_RT_CTL_REG, UDMA_CHAN_RT_CTL_EN); break; @@ -885,20 +883,20 @@ static int udma_stop(struct udma_chan *uc) if (!uc->cyclic && !uc->desc) udma_push_to_ring(uc, -1); - udma_rchanrt_write(uc->rchan, UDMA_RCHAN_RT_PEER_RT_EN_REG, + udma_rchanrt_write(uc->rchan, UDMA_CHAN_RT_PEER_RT_EN_REG, UDMA_PEER_RT_EN_ENABLE | UDMA_PEER_RT_EN_TEARDOWN); break; case DMA_MEM_TO_DEV: - udma_tchanrt_write(uc->tchan, UDMA_TCHAN_RT_PEER_RT_EN_REG, + udma_tchanrt_write(uc->tchan, UDMA_CHAN_RT_PEER_RT_EN_REG, UDMA_PEER_RT_EN_ENABLE | UDMA_PEER_RT_EN_FLUSH); - udma_tchanrt_write(uc->tchan, UDMA_TCHAN_RT_CTL_REG, + udma_tchanrt_write(uc->tchan, UDMA_CHAN_RT_CTL_REG, UDMA_CHAN_RT_CTL_EN | UDMA_CHAN_RT_CTL_TDOWN); break; case DMA_MEM_TO_MEM: - udma_tchanrt_write(uc->tchan, UDMA_TCHAN_RT_CTL_REG, + udma_tchanrt_write(uc->tchan, UDMA_CHAN_RT_CTL_REG, UDMA_CHAN_RT_CTL_EN | UDMA_CHAN_RT_CTL_TDOWN); break; @@ -938,8 +936,8 @@ static bool udma_is_desc_really_done(struct udma_chan *uc, struct udma_desc *d) uc->config.dir != DMA_MEM_TO_DEV) return true; - peer_bcnt = udma_tchanrt_read(uc->tchan, UDMA_TCHAN_RT_PEER_BCNT_REG); - bcnt = udma_tchanrt_read(uc->tchan, UDMA_TCHAN_RT_BCNT_REG); + peer_bcnt = udma_tchanrt_read(uc->tchan, UDMA_CHAN_RT_PEER_BCNT_REG); + bcnt = udma_tchanrt_read(uc->tchan, UDMA_CHAN_RT_BCNT_REG); /* Transfer is incomplete, store current residue and time stamp */ if (peer_bcnt < bcnt) { @@ -2740,29 +2738,29 @@ static enum dma_status udma_tx_status(struct dma_chan *chan, if (uc->desc->dir == DMA_MEM_TO_DEV) { bcnt = udma_tchanrt_read(uc->tchan, - UDMA_TCHAN_RT_SBCNT_REG); + UDMA_CHAN_RT_SBCNT_REG); if (uc->config.ep_type != PSIL_EP_NATIVE) { peer_bcnt = udma_tchanrt_read(uc->tchan, - UDMA_TCHAN_RT_PEER_BCNT_REG); + UDMA_CHAN_RT_PEER_BCNT_REG); if (bcnt > peer_bcnt) delay = bcnt - peer_bcnt; } } else if (uc->desc->dir == DMA_DEV_TO_MEM) { bcnt = udma_rchanrt_read(uc->rchan, - UDMA_RCHAN_RT_BCNT_REG); + UDMA_CHAN_RT_BCNT_REG); if (uc->config.ep_type != PSIL_EP_NATIVE) { peer_bcnt = udma_rchanrt_read(uc->rchan, - UDMA_RCHAN_RT_PEER_BCNT_REG); + UDMA_CHAN_RT_PEER_BCNT_REG); if (peer_bcnt > bcnt) delay = peer_bcnt - bcnt; } } else { bcnt = udma_tchanrt_read(uc->tchan, - UDMA_TCHAN_RT_BCNT_REG); + UDMA_CHAN_RT_BCNT_REG); } bcnt -= uc->bcnt; @@ -2796,18 +2794,18 @@ static int udma_pause(struct dma_chan *chan) switch (uc->config.dir) { case DMA_DEV_TO_MEM: udma_rchanrt_update_bits(uc->rchan, - UDMA_RCHAN_RT_PEER_RT_EN_REG, + UDMA_CHAN_RT_PEER_RT_EN_REG, UDMA_PEER_RT_EN_PAUSE, UDMA_PEER_RT_EN_PAUSE); break; case DMA_MEM_TO_DEV: udma_tchanrt_update_bits(uc->tchan, - UDMA_TCHAN_RT_PEER_RT_EN_REG, + UDMA_CHAN_RT_PEER_RT_EN_REG, UDMA_PEER_RT_EN_PAUSE, UDMA_PEER_RT_EN_PAUSE); break; case DMA_MEM_TO_MEM: - udma_tchanrt_update_bits(uc->tchan, UDMA_TCHAN_RT_CTL_REG, + udma_tchanrt_update_bits(uc->tchan, UDMA_CHAN_RT_CTL_REG, UDMA_CHAN_RT_CTL_PAUSE, UDMA_CHAN_RT_CTL_PAUSE); break; @@ -2826,17 +2824,17 @@ static int udma_resume(struct dma_chan *chan) switch (uc->config.dir) { case DMA_DEV_TO_MEM: udma_rchanrt_update_bits(uc->rchan, - UDMA_RCHAN_RT_PEER_RT_EN_REG, + UDMA_CHAN_RT_PEER_RT_EN_REG, UDMA_PEER_RT_EN_PAUSE, 0); break; case DMA_MEM_TO_DEV: udma_tchanrt_update_bits(uc->tchan, - UDMA_TCHAN_RT_PEER_RT_EN_REG, + UDMA_CHAN_RT_PEER_RT_EN_REG, UDMA_PEER_RT_EN_PAUSE, 0); break; case DMA_MEM_TO_MEM: - udma_tchanrt_update_bits(uc->tchan, UDMA_TCHAN_RT_CTL_REG, + udma_tchanrt_update_bits(uc->tchan, UDMA_CHAN_RT_CTL_REG, UDMA_CHAN_RT_CTL_PAUSE, 0); break; default: diff --git a/drivers/dma/ti/k3-udma.h b/drivers/dma/ti/k3-udma.h index 128d8744a4355..a8ea1138e1a5b 100644 --- a/drivers/dma/ti/k3-udma.h +++ b/drivers/dma/ti/k3-udma.h @@ -18,52 +18,33 @@ #define UDMA_RX_FLOW_ID_FW_OES_REG 0x80 #define UDMA_RX_FLOW_ID_FW_STATUS_REG 0x88 -/* TX chan RT regs */ -#define UDMA_TCHAN_RT_CTL_REG 0x0 -#define UDMA_TCHAN_RT_SWTRIG_REG 0x8 -#define UDMA_TCHAN_RT_STDATA_REG 0x80 - -#define UDMA_TCHAN_RT_PEER_REG(i) (0x200 + ((i) * 0x4)) -#define UDMA_TCHAN_RT_PEER_STATIC_TR_XY_REG \ - UDMA_TCHAN_RT_PEER_REG(0) /* PSI-L: 0x400 */ -#define UDMA_TCHAN_RT_PEER_STATIC_TR_Z_REG \ - UDMA_TCHAN_RT_PEER_REG(1) /* PSI-L: 0x401 */ -#define UDMA_TCHAN_RT_PEER_BCNT_REG \ - UDMA_TCHAN_RT_PEER_REG(4) /* PSI-L: 0x404 */ -#define UDMA_TCHAN_RT_PEER_RT_EN_REG \ - UDMA_TCHAN_RT_PEER_REG(8) /* PSI-L: 0x408 */ - -#define UDMA_TCHAN_RT_PCNT_REG 0x400 -#define UDMA_TCHAN_RT_BCNT_REG 0x408 -#define UDMA_TCHAN_RT_SBCNT_REG 0x410 - -/* RX chan RT regs */ -#define UDMA_RCHAN_RT_CTL_REG 0x0 -#define UDMA_RCHAN_RT_SWTRIG_REG 0x8 -#define UDMA_RCHAN_RT_STDATA_REG 0x80 - -#define UDMA_RCHAN_RT_PEER_REG(i) (0x200 + ((i) * 0x4)) -#define UDMA_RCHAN_RT_PEER_STATIC_TR_XY_REG \ - UDMA_RCHAN_RT_PEER_REG(0) /* PSI-L: 0x400 */ -#define UDMA_RCHAN_RT_PEER_STATIC_TR_Z_REG \ - UDMA_RCHAN_RT_PEER_REG(1) /* PSI-L: 0x401 */ -#define UDMA_RCHAN_RT_PEER_BCNT_REG \ - UDMA_RCHAN_RT_PEER_REG(4) /* PSI-L: 0x404 */ -#define UDMA_RCHAN_RT_PEER_RT_EN_REG \ - UDMA_RCHAN_RT_PEER_REG(8) /* PSI-L: 0x408 */ - -#define UDMA_RCHAN_RT_PCNT_REG 0x400 -#define UDMA_RCHAN_RT_BCNT_REG 0x408 -#define UDMA_RCHAN_RT_SBCNT_REG 0x410 - -/* UDMA_TCHAN_RT_CTL_REG/UDMA_RCHAN_RT_CTL_REG */ +/* TCHANRT/RCHANRT registers */ +#define UDMA_CHAN_RT_CTL_REG 0x0 +#define UDMA_CHAN_RT_SWTRIG_REG 0x8 +#define UDMA_CHAN_RT_STDATA_REG 0x80 + +#define UDMA_CHAN_RT_PEER_REG(i) (0x200 + ((i) * 0x4)) +#define UDMA_CHAN_RT_PEER_STATIC_TR_XY_REG \ + UDMA_CHAN_RT_PEER_REG(0) /* PSI-L: 0x400 */ +#define UDMA_CHAN_RT_PEER_STATIC_TR_Z_REG \ + UDMA_CHAN_RT_PEER_REG(1) /* PSI-L: 0x401 */ +#define UDMA_CHAN_RT_PEER_BCNT_REG \ + UDMA_CHAN_RT_PEER_REG(4) /* PSI-L: 0x404 */ +#define UDMA_CHAN_RT_PEER_RT_EN_REG \ + UDMA_CHAN_RT_PEER_REG(8) /* PSI-L: 0x408 */ + +#define UDMA_CHAN_RT_PCNT_REG 0x400 +#define UDMA_CHAN_RT_BCNT_REG 0x408 +#define UDMA_CHAN_RT_SBCNT_REG 0x410 + +/* UDMA_CHAN_RT_CTL_REG */ #define UDMA_CHAN_RT_CTL_EN BIT(31) #define UDMA_CHAN_RT_CTL_TDOWN BIT(30) #define UDMA_CHAN_RT_CTL_PAUSE BIT(29) #define UDMA_CHAN_RT_CTL_FTDOWN BIT(28) #define UDMA_CHAN_RT_CTL_ERROR BIT(0) -/* UDMA_TCHAN_RT_PEER_RT_EN_REG/UDMA_RCHAN_RT_PEER_RT_EN_REG (PSI-L: 0x408) */ +/* UDMA_CHAN_RT_PEER_RT_EN_REG */ #define UDMA_PEER_RT_EN_ENABLE BIT(31) #define UDMA_PEER_RT_EN_TEARDOWN BIT(30) #define UDMA_PEER_RT_EN_PAUSE BIT(29) -- GitLab From 67414f8db560e5d8dd621f451fb5b50796c8fe6d Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Tue, 7 Jul 2020 13:23:51 +0300 Subject: [PATCH 0422/1476] dmaengine: ti: k3-udma-private: Use udma_read/write for register access Instead of using higher level wrappers (udma_rchanrt/tchanrt read/write), use the underlying register access functions directly. This will allow changes in the higher level wrappers within the DMAengine driver. Signed-off-by: Peter Ujfalusi Reviewed-by: Grygorii Strashko Link: https://lore.kernel.org/r/20200707102352.28773-5-peter.ujfalusi@ti.com Signed-off-by: Vinod Koul --- drivers/dma/ti/k3-udma-private.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/dma/ti/k3-udma-private.c b/drivers/dma/ti/k3-udma-private.c index 0b8f3dd6b1463..c8100d1716ee6 100644 --- a/drivers/dma/ti/k3-udma-private.c +++ b/drivers/dma/ti/k3-udma-private.c @@ -120,13 +120,17 @@ XUDMA_GET_RESOURCE_ID(rflow); #define XUDMA_RT_IO_FUNCTIONS(res) \ u32 xudma_##res##rt_read(struct udma_##res *p, int reg) \ { \ - return udma_##res##rt_read(p, reg); \ + if (!p) \ + return 0; \ + return udma_read(p->reg_rt, reg); \ } \ EXPORT_SYMBOL(xudma_##res##rt_read); \ \ void xudma_##res##rt_write(struct udma_##res *p, int reg, u32 val) \ { \ - udma_##res##rt_write(p, reg, val); \ + if (!p) \ + return; \ + udma_write(p->reg_rt, reg, val); \ } \ EXPORT_SYMBOL(xudma_##res##rt_write) XUDMA_RT_IO_FUNCTIONS(tchan); -- GitLab From db375dcb12250bbc148aef0fa8da747aaa8cf6ec Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Tue, 7 Jul 2020 13:23:52 +0300 Subject: [PATCH 0423/1476] dmaengine: ti: k3-udma: Use udma_chan instead of tchan/rchan for IO functions Move the uc->tchan/rchan checks to the IO wrappers itself instead of calling the functions with tchan/rchan directly. Signed-off-by: Peter Ujfalusi Reviewed-by: Grygorii Strashko Link: https://lore.kernel.org/r/20200707102352.28773-6-peter.ujfalusi@ti.com Signed-off-by: Vinod Koul --- drivers/dma/ti/k3-udma.c | 163 +++++++++++++++++++-------------------- 1 file changed, 78 insertions(+), 85 deletions(-) diff --git a/drivers/dma/ti/k3-udma.c b/drivers/dma/ti/k3-udma.c index 2fb294c975642..1b38d1f2032d6 100644 --- a/drivers/dma/ti/k3-udma.c +++ b/drivers/dma/ti/k3-udma.c @@ -282,51 +282,49 @@ static inline void udma_update_bits(void __iomem *base, int reg, } /* TCHANRT */ -static inline u32 udma_tchanrt_read(struct udma_tchan *tchan, int reg) +static inline u32 udma_tchanrt_read(struct udma_chan *uc, int reg) { - if (!tchan) + if (!uc->tchan) return 0; - return udma_read(tchan->reg_rt, reg); + return udma_read(uc->tchan->reg_rt, reg); } -static inline void udma_tchanrt_write(struct udma_tchan *tchan, int reg, - u32 val) +static inline void udma_tchanrt_write(struct udma_chan *uc, int reg, u32 val) { - if (!tchan) + if (!uc->tchan) return; - udma_write(tchan->reg_rt, reg, val); + udma_write(uc->tchan->reg_rt, reg, val); } -static inline void udma_tchanrt_update_bits(struct udma_tchan *tchan, int reg, +static inline void udma_tchanrt_update_bits(struct udma_chan *uc, int reg, u32 mask, u32 val) { - if (!tchan) + if (!uc->tchan) return; - udma_update_bits(tchan->reg_rt, reg, mask, val); + udma_update_bits(uc->tchan->reg_rt, reg, mask, val); } /* RCHANRT */ -static inline u32 udma_rchanrt_read(struct udma_rchan *rchan, int reg) +static inline u32 udma_rchanrt_read(struct udma_chan *uc, int reg) { - if (!rchan) + if (!uc->rchan) return 0; - return udma_read(rchan->reg_rt, reg); + return udma_read(uc->rchan->reg_rt, reg); } -static inline void udma_rchanrt_write(struct udma_rchan *rchan, int reg, - u32 val) +static inline void udma_rchanrt_write(struct udma_chan *uc, int reg, u32 val) { - if (!rchan) + if (!uc->rchan) return; - udma_write(rchan->reg_rt, reg, val); + udma_write(uc->rchan->reg_rt, reg, val); } -static inline void udma_rchanrt_update_bits(struct udma_rchan *rchan, int reg, +static inline void udma_rchanrt_update_bits(struct udma_chan *uc, int reg, u32 mask, u32 val) { - if (!rchan) + if (!uc->rchan) return; - udma_update_bits(rchan->reg_rt, reg, mask, val); + udma_update_bits(uc->rchan->reg_rt, reg, mask, val); } static int navss_psil_pair(struct udma_dev *ud, u32 src_thread, u32 dst_thread) @@ -368,7 +366,7 @@ static void udma_dump_chan_stdata(struct udma_chan *uc) for (i = 0; i < 32; i++) { offset = UDMA_CHAN_RT_STDATA_REG + i * 4; dev_dbg(dev, "TRT_STDATA[%02d]: 0x%08x\n", i, - udma_tchanrt_read(uc->tchan, offset)); + udma_tchanrt_read(uc, offset)); } } @@ -377,7 +375,7 @@ static void udma_dump_chan_stdata(struct udma_chan *uc) for (i = 0; i < 32; i++) { offset = UDMA_CHAN_RT_STDATA_REG + i * 4; dev_dbg(dev, "RRT_STDATA[%02d]: 0x%08x\n", i, - udma_rchanrt_read(uc->rchan, offset)); + udma_rchanrt_read(uc, offset)); } } } @@ -500,9 +498,9 @@ static bool udma_is_chan_running(struct udma_chan *uc) u32 rrt_ctl = 0; if (uc->tchan) - trt_ctl = udma_tchanrt_read(uc->tchan, UDMA_CHAN_RT_CTL_REG); + trt_ctl = udma_tchanrt_read(uc, UDMA_CHAN_RT_CTL_REG); if (uc->rchan) - rrt_ctl = udma_rchanrt_read(uc->rchan, UDMA_CHAN_RT_CTL_REG); + rrt_ctl = udma_rchanrt_read(uc, UDMA_CHAN_RT_CTL_REG); if (trt_ctl & UDMA_CHAN_RT_CTL_EN || rrt_ctl & UDMA_CHAN_RT_CTL_EN) return true; @@ -516,15 +514,15 @@ static bool udma_is_chan_paused(struct udma_chan *uc) switch (uc->config.dir) { case DMA_DEV_TO_MEM: - val = udma_rchanrt_read(uc->rchan, UDMA_CHAN_RT_PEER_RT_EN_REG); + val = udma_rchanrt_read(uc, UDMA_CHAN_RT_PEER_RT_EN_REG); pause_mask = UDMA_PEER_RT_EN_PAUSE; break; case DMA_MEM_TO_DEV: - val = udma_tchanrt_read(uc->tchan, UDMA_CHAN_RT_PEER_RT_EN_REG); + val = udma_tchanrt_read(uc, UDMA_CHAN_RT_PEER_RT_EN_REG); pause_mask = UDMA_PEER_RT_EN_PAUSE; break; case DMA_MEM_TO_MEM: - val = udma_tchanrt_read(uc->tchan, UDMA_CHAN_RT_CTL_REG); + val = udma_tchanrt_read(uc, UDMA_CHAN_RT_CTL_REG); pause_mask = UDMA_CHAN_RT_CTL_PAUSE; break; default: @@ -658,31 +656,31 @@ static void udma_reset_counters(struct udma_chan *uc) u32 val; if (uc->tchan) { - val = udma_tchanrt_read(uc->tchan, UDMA_CHAN_RT_BCNT_REG); - udma_tchanrt_write(uc->tchan, UDMA_CHAN_RT_BCNT_REG, val); + val = udma_tchanrt_read(uc, UDMA_CHAN_RT_BCNT_REG); + udma_tchanrt_write(uc, UDMA_CHAN_RT_BCNT_REG, val); - val = udma_tchanrt_read(uc->tchan, UDMA_CHAN_RT_SBCNT_REG); - udma_tchanrt_write(uc->tchan, UDMA_CHAN_RT_SBCNT_REG, val); + val = udma_tchanrt_read(uc, UDMA_CHAN_RT_SBCNT_REG); + udma_tchanrt_write(uc, UDMA_CHAN_RT_SBCNT_REG, val); - val = udma_tchanrt_read(uc->tchan, UDMA_CHAN_RT_PCNT_REG); - udma_tchanrt_write(uc->tchan, UDMA_CHAN_RT_PCNT_REG, val); + val = udma_tchanrt_read(uc, UDMA_CHAN_RT_PCNT_REG); + udma_tchanrt_write(uc, UDMA_CHAN_RT_PCNT_REG, val); - val = udma_tchanrt_read(uc->tchan, UDMA_CHAN_RT_PEER_BCNT_REG); - udma_tchanrt_write(uc->tchan, UDMA_CHAN_RT_PEER_BCNT_REG, val); + val = udma_tchanrt_read(uc, UDMA_CHAN_RT_PEER_BCNT_REG); + udma_tchanrt_write(uc, UDMA_CHAN_RT_PEER_BCNT_REG, val); } if (uc->rchan) { - val = udma_rchanrt_read(uc->rchan, UDMA_CHAN_RT_BCNT_REG); - udma_rchanrt_write(uc->rchan, UDMA_CHAN_RT_BCNT_REG, val); + val = udma_rchanrt_read(uc, UDMA_CHAN_RT_BCNT_REG); + udma_rchanrt_write(uc, UDMA_CHAN_RT_BCNT_REG, val); - val = udma_rchanrt_read(uc->rchan, UDMA_CHAN_RT_SBCNT_REG); - udma_rchanrt_write(uc->rchan, UDMA_CHAN_RT_SBCNT_REG, val); + val = udma_rchanrt_read(uc, UDMA_CHAN_RT_SBCNT_REG); + udma_rchanrt_write(uc, UDMA_CHAN_RT_SBCNT_REG, val); - val = udma_rchanrt_read(uc->rchan, UDMA_CHAN_RT_PCNT_REG); - udma_rchanrt_write(uc->rchan, UDMA_CHAN_RT_PCNT_REG, val); + val = udma_rchanrt_read(uc, UDMA_CHAN_RT_PCNT_REG); + udma_rchanrt_write(uc, UDMA_CHAN_RT_PCNT_REG, val); - val = udma_rchanrt_read(uc->rchan, UDMA_CHAN_RT_PEER_BCNT_REG); - udma_rchanrt_write(uc->rchan, UDMA_CHAN_RT_PEER_BCNT_REG, val); + val = udma_rchanrt_read(uc, UDMA_CHAN_RT_PEER_BCNT_REG); + udma_rchanrt_write(uc, UDMA_CHAN_RT_PEER_BCNT_REG, val); } uc->bcnt = 0; @@ -692,16 +690,16 @@ static int udma_reset_chan(struct udma_chan *uc, bool hard) { switch (uc->config.dir) { case DMA_DEV_TO_MEM: - udma_rchanrt_write(uc->rchan, UDMA_CHAN_RT_PEER_RT_EN_REG, 0); - udma_rchanrt_write(uc->rchan, UDMA_CHAN_RT_CTL_REG, 0); + udma_rchanrt_write(uc, UDMA_CHAN_RT_PEER_RT_EN_REG, 0); + udma_rchanrt_write(uc, UDMA_CHAN_RT_CTL_REG, 0); break; case DMA_MEM_TO_DEV: - udma_tchanrt_write(uc->tchan, UDMA_CHAN_RT_CTL_REG, 0); - udma_tchanrt_write(uc->tchan, UDMA_CHAN_RT_PEER_RT_EN_REG, 0); + udma_tchanrt_write(uc, UDMA_CHAN_RT_CTL_REG, 0); + udma_tchanrt_write(uc, UDMA_CHAN_RT_PEER_RT_EN_REG, 0); break; case DMA_MEM_TO_MEM: - udma_rchanrt_write(uc->rchan, UDMA_CHAN_RT_CTL_REG, 0); - udma_tchanrt_write(uc->tchan, UDMA_CHAN_RT_CTL_REG, 0); + udma_rchanrt_write(uc, UDMA_CHAN_RT_CTL_REG, 0); + udma_tchanrt_write(uc, UDMA_CHAN_RT_CTL_REG, 0); break; default: return -EINVAL; @@ -729,7 +727,7 @@ static int udma_reset_chan(struct udma_chan *uc, bool hard) * the rchan. */ if (uc->config.dir == DMA_DEV_TO_MEM) - udma_rchanrt_write(uc->rchan, UDMA_CHAN_RT_CTL_REG, + udma_rchanrt_write(uc, UDMA_CHAN_RT_CTL_REG, UDMA_CHAN_RT_CTL_EN | UDMA_CHAN_RT_CTL_TDOWN | UDMA_CHAN_RT_CTL_FTDOWN); @@ -806,10 +804,11 @@ static int udma_start(struct udma_chan *uc) if (uc->config.enable_burst) val |= PDMA_STATIC_TR_XY_BURST; - udma_rchanrt_write(uc->rchan, - UDMA_CHAN_RT_PEER_STATIC_TR_XY_REG, val); + udma_rchanrt_write(uc, + UDMA_CHAN_RT_PEER_STATIC_TR_XY_REG, + val); - udma_rchanrt_write(uc->rchan, + udma_rchanrt_write(uc, UDMA_CHAN_RT_PEER_STATIC_TR_Z_REG, PDMA_STATIC_TR_Z(uc->desc->static_tr.bstcnt, match_data->statictr_z_mask)); @@ -819,11 +818,11 @@ static int udma_start(struct udma_chan *uc) sizeof(uc->static_tr)); } - udma_rchanrt_write(uc->rchan, UDMA_CHAN_RT_CTL_REG, + udma_rchanrt_write(uc, UDMA_CHAN_RT_CTL_REG, UDMA_CHAN_RT_CTL_EN); /* Enable remote */ - udma_rchanrt_write(uc->rchan, UDMA_CHAN_RT_PEER_RT_EN_REG, + udma_rchanrt_write(uc, UDMA_CHAN_RT_PEER_RT_EN_REG, UDMA_PEER_RT_EN_ENABLE); break; @@ -838,8 +837,9 @@ static int udma_start(struct udma_chan *uc) if (uc->config.enable_burst) val |= PDMA_STATIC_TR_XY_BURST; - udma_tchanrt_write(uc->tchan, - UDMA_CHAN_RT_PEER_STATIC_TR_XY_REG, val); + udma_tchanrt_write(uc, + UDMA_CHAN_RT_PEER_STATIC_TR_XY_REG, + val); /* save the current staticTR configuration */ memcpy(&uc->static_tr, &uc->desc->static_tr, @@ -847,17 +847,17 @@ static int udma_start(struct udma_chan *uc) } /* Enable remote */ - udma_tchanrt_write(uc->tchan, UDMA_CHAN_RT_PEER_RT_EN_REG, + udma_tchanrt_write(uc, UDMA_CHAN_RT_PEER_RT_EN_REG, UDMA_PEER_RT_EN_ENABLE); - udma_tchanrt_write(uc->tchan, UDMA_CHAN_RT_CTL_REG, + udma_tchanrt_write(uc, UDMA_CHAN_RT_CTL_REG, UDMA_CHAN_RT_CTL_EN); break; case DMA_MEM_TO_MEM: - udma_rchanrt_write(uc->rchan, UDMA_CHAN_RT_CTL_REG, + udma_rchanrt_write(uc, UDMA_CHAN_RT_CTL_REG, UDMA_CHAN_RT_CTL_EN); - udma_tchanrt_write(uc->tchan, UDMA_CHAN_RT_CTL_REG, + udma_tchanrt_write(uc, UDMA_CHAN_RT_CTL_REG, UDMA_CHAN_RT_CTL_EN); break; @@ -883,20 +883,20 @@ static int udma_stop(struct udma_chan *uc) if (!uc->cyclic && !uc->desc) udma_push_to_ring(uc, -1); - udma_rchanrt_write(uc->rchan, UDMA_CHAN_RT_PEER_RT_EN_REG, + udma_rchanrt_write(uc, UDMA_CHAN_RT_PEER_RT_EN_REG, UDMA_PEER_RT_EN_ENABLE | UDMA_PEER_RT_EN_TEARDOWN); break; case DMA_MEM_TO_DEV: - udma_tchanrt_write(uc->tchan, UDMA_CHAN_RT_PEER_RT_EN_REG, + udma_tchanrt_write(uc, UDMA_CHAN_RT_PEER_RT_EN_REG, UDMA_PEER_RT_EN_ENABLE | UDMA_PEER_RT_EN_FLUSH); - udma_tchanrt_write(uc->tchan, UDMA_CHAN_RT_CTL_REG, + udma_tchanrt_write(uc, UDMA_CHAN_RT_CTL_REG, UDMA_CHAN_RT_CTL_EN | UDMA_CHAN_RT_CTL_TDOWN); break; case DMA_MEM_TO_MEM: - udma_tchanrt_write(uc->tchan, UDMA_CHAN_RT_CTL_REG, + udma_tchanrt_write(uc, UDMA_CHAN_RT_CTL_REG, UDMA_CHAN_RT_CTL_EN | UDMA_CHAN_RT_CTL_TDOWN); break; @@ -936,8 +936,8 @@ static bool udma_is_desc_really_done(struct udma_chan *uc, struct udma_desc *d) uc->config.dir != DMA_MEM_TO_DEV) return true; - peer_bcnt = udma_tchanrt_read(uc->tchan, UDMA_CHAN_RT_PEER_BCNT_REG); - bcnt = udma_tchanrt_read(uc->tchan, UDMA_CHAN_RT_BCNT_REG); + peer_bcnt = udma_tchanrt_read(uc, UDMA_CHAN_RT_PEER_BCNT_REG); + bcnt = udma_tchanrt_read(uc, UDMA_CHAN_RT_BCNT_REG); /* Transfer is incomplete, store current residue and time stamp */ if (peer_bcnt < bcnt) { @@ -2737,30 +2737,27 @@ static enum dma_status udma_tx_status(struct dma_chan *chan, u32 delay = 0; if (uc->desc->dir == DMA_MEM_TO_DEV) { - bcnt = udma_tchanrt_read(uc->tchan, - UDMA_CHAN_RT_SBCNT_REG); + bcnt = udma_tchanrt_read(uc, UDMA_CHAN_RT_SBCNT_REG); if (uc->config.ep_type != PSIL_EP_NATIVE) { - peer_bcnt = udma_tchanrt_read(uc->tchan, + peer_bcnt = udma_tchanrt_read(uc, UDMA_CHAN_RT_PEER_BCNT_REG); if (bcnt > peer_bcnt) delay = bcnt - peer_bcnt; } } else if (uc->desc->dir == DMA_DEV_TO_MEM) { - bcnt = udma_rchanrt_read(uc->rchan, - UDMA_CHAN_RT_BCNT_REG); + bcnt = udma_rchanrt_read(uc, UDMA_CHAN_RT_BCNT_REG); if (uc->config.ep_type != PSIL_EP_NATIVE) { - peer_bcnt = udma_rchanrt_read(uc->rchan, + peer_bcnt = udma_rchanrt_read(uc, UDMA_CHAN_RT_PEER_BCNT_REG); if (peer_bcnt > bcnt) delay = peer_bcnt - bcnt; } } else { - bcnt = udma_tchanrt_read(uc->tchan, - UDMA_CHAN_RT_BCNT_REG); + bcnt = udma_tchanrt_read(uc, UDMA_CHAN_RT_BCNT_REG); } bcnt -= uc->bcnt; @@ -2793,19 +2790,17 @@ static int udma_pause(struct dma_chan *chan) /* pause the channel */ switch (uc->config.dir) { case DMA_DEV_TO_MEM: - udma_rchanrt_update_bits(uc->rchan, - UDMA_CHAN_RT_PEER_RT_EN_REG, + udma_rchanrt_update_bits(uc, UDMA_CHAN_RT_PEER_RT_EN_REG, UDMA_PEER_RT_EN_PAUSE, UDMA_PEER_RT_EN_PAUSE); break; case DMA_MEM_TO_DEV: - udma_tchanrt_update_bits(uc->tchan, - UDMA_CHAN_RT_PEER_RT_EN_REG, + udma_tchanrt_update_bits(uc, UDMA_CHAN_RT_PEER_RT_EN_REG, UDMA_PEER_RT_EN_PAUSE, UDMA_PEER_RT_EN_PAUSE); break; case DMA_MEM_TO_MEM: - udma_tchanrt_update_bits(uc->tchan, UDMA_CHAN_RT_CTL_REG, + udma_tchanrt_update_bits(uc, UDMA_CHAN_RT_CTL_REG, UDMA_CHAN_RT_CTL_PAUSE, UDMA_CHAN_RT_CTL_PAUSE); break; @@ -2823,18 +2818,16 @@ static int udma_resume(struct dma_chan *chan) /* resume the channel */ switch (uc->config.dir) { case DMA_DEV_TO_MEM: - udma_rchanrt_update_bits(uc->rchan, - UDMA_CHAN_RT_PEER_RT_EN_REG, + udma_rchanrt_update_bits(uc, UDMA_CHAN_RT_PEER_RT_EN_REG, UDMA_PEER_RT_EN_PAUSE, 0); break; case DMA_MEM_TO_DEV: - udma_tchanrt_update_bits(uc->tchan, - UDMA_CHAN_RT_PEER_RT_EN_REG, + udma_tchanrt_update_bits(uc, UDMA_CHAN_RT_PEER_RT_EN_REG, UDMA_PEER_RT_EN_PAUSE, 0); break; case DMA_MEM_TO_MEM: - udma_tchanrt_update_bits(uc->tchan, UDMA_CHAN_RT_CTL_REG, + udma_tchanrt_update_bits(uc, UDMA_CHAN_RT_CTL_REG, UDMA_CHAN_RT_CTL_PAUSE, 0); break; default: -- GitLab From ace52a8c2dd59e3c79fc2a442659ea7c5a52df7f Mon Sep 17 00:00:00 2001 From: "Gustavo A. R. Silva" Date: Fri, 19 Jun 2020 17:43:34 -0500 Subject: [PATCH 0424/1476] dmaengine: ti: k3-udma: Use struct_size() in kzalloc() Make use of the struct_size() helper instead of an open-coded version in order to avoid any potential type mistakes. This code was detected with the help of Coccinelle and, audited and fixed manually. Signed-off-by: Gustavo A. R. Silva Acked-by: Peter Ujfalusi Addresses-KSPP-ID: https://github.com/KSPP/linux/issues/83 Link: https://lore.kernel.org/r/20200619224334.GA7857@embeddedor Signed-off-by: Vinod Koul --- drivers/dma/ti/k3-udma.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/dma/ti/k3-udma.c b/drivers/dma/ti/k3-udma.c index 1b38d1f2032d6..677541434ad18 100644 --- a/drivers/dma/ti/k3-udma.c +++ b/drivers/dma/ti/k3-udma.c @@ -2171,7 +2171,7 @@ udma_prep_slave_sg_pkt(struct udma_chan *uc, struct scatterlist *sgl, u32 ring_id; unsigned int i; - d = kzalloc(sizeof(*d) + sglen * sizeof(d->hwdesc[0]), GFP_NOWAIT); + d = kzalloc(struct_size(d, hwdesc, sglen), GFP_NOWAIT); if (!d) return NULL; @@ -2487,7 +2487,7 @@ udma_prep_dma_cyclic_pkt(struct udma_chan *uc, dma_addr_t buf_addr, if (period_len >= SZ_4M) return NULL; - d = kzalloc(sizeof(*d) + periods * sizeof(d->hwdesc[0]), GFP_NOWAIT); + d = kzalloc(struct_size(d, hwdesc, periods), GFP_NOWAIT); if (!d) return NULL; -- GitLab From 09b4db279758dd3d5bb235605e985b99e7bc1a93 Mon Sep 17 00:00:00 2001 From: Lad Prabhakar Date: Mon, 13 Jul 2020 22:35:15 +0100 Subject: [PATCH 0425/1476] dt-bindings: dma: renesas,rcar-dmac: Document R8A774E1 bindings Renesas RZ/G2H (R8A774E1) SoC also has the R-Car gen3 compatible DMA controllers, therefore document RZ/G2H specific bindings. Signed-off-by: Lad Prabhakar Reviewed-by: Geert Uytterhoeven Link: https://lore.kernel.org/r/1594676120-5862-5-git-send-email-prabhakar.mahadev-lad.rj@bp.renesas.com Signed-off-by: Vinod Koul --- Documentation/devicetree/bindings/dma/renesas,rcar-dmac.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/dma/renesas,rcar-dmac.yaml b/Documentation/devicetree/bindings/dma/renesas,rcar-dmac.yaml index b842dfd96a897..13f1a46be40dc 100644 --- a/Documentation/devicetree/bindings/dma/renesas,rcar-dmac.yaml +++ b/Documentation/devicetree/bindings/dma/renesas,rcar-dmac.yaml @@ -23,6 +23,7 @@ properties: - renesas,dmac-r8a774a1 # RZ/G2M - renesas,dmac-r8a774b1 # RZ/G2N - renesas,dmac-r8a774c0 # RZ/G2E + - renesas,dmac-r8a774e1 # RZ/G2H - renesas,dmac-r8a7790 # R-Car H2 - renesas,dmac-r8a7791 # R-Car M2-W - renesas,dmac-r8a7792 # R-Car V2H -- GitLab From f4c20f084d4007017c9606f47e2c454916b1baec Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Tue, 14 Jul 2020 12:15:30 +0100 Subject: [PATCH 0426/1476] dmaengine: mediatek: mtk-hsdma: Fix formatting in 'struct mtk_hsdma_pdesc' doc block Struct attribute names must be an exact match or the kerneldoc checker gets confused. Fixes the following W=1 kernel build warning(s): drivers/dma/mediatek/mtk-hsdma.c:120: warning: Function parameter or member 'desc1' not described in 'mtk_hsdma_pdesc' drivers/dma/mediatek/mtk-hsdma.c:120: warning: Function parameter or member 'desc2' not described in 'mtk_hsdma_pdesc' drivers/dma/mediatek/mtk-hsdma.c:120: warning: Function parameter or member 'desc3' not described in 'mtk_hsdma_pdesc' drivers/dma/mediatek/mtk-hsdma.c:120: warning: Function parameter or member 'desc4' not described in 'mtk_hsdma_pdesc' Signed-off-by: Lee Jones Cc: Sean Wang Cc: Matthias Brugger Cc: linux-mediatek@lists.infradead.org Link: https://lore.kernel.org/r/20200714111546.1755231-2-lee.jones@linaro.org Signed-off-by: Vinod Koul --- drivers/dma/mediatek/mtk-hsdma.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/dma/mediatek/mtk-hsdma.c b/drivers/dma/mediatek/mtk-hsdma.c index 4c58da7421432..f133ae8dece16 100644 --- a/drivers/dma/mediatek/mtk-hsdma.c +++ b/drivers/dma/mediatek/mtk-hsdma.c @@ -107,10 +107,10 @@ enum mtk_hsdma_vdesc_flag { * struct mtk_hsdma_pdesc - This is the struct holding info describing physical * descriptor (PD) and its placement must be kept at * 4-bytes alignment in little endian order. - * @desc[1-4]: The control pad used to indicate hardware how to - * deal with the descriptor such as source and - * destination address and data length. The maximum - * data length each pdesc can handle is 0x3f80 bytes + * @desc1: | The control pad used to indicate hardware how to + * @desc2: | deal with the descriptor such as source and + * @desc3: | destination address and data length. The maximum + * @desc4: | data length each pdesc can handle is 0x3f80 bytes */ struct mtk_hsdma_pdesc { __le32 desc1; -- GitLab From 7d8c91487f77c024ff4309d1dff66232ed6a07c5 Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Tue, 14 Jul 2020 12:15:31 +0100 Subject: [PATCH 0427/1476] dmaengine: of-dma: Fix misspellings/formatting issues in some function headers Fixes the following W=1 kernel build warning(s): drivers/dma/of-dma.c:57: warning: Function parameter or member 'ofdma' not described in 'of_dma_router_xlate' drivers/dma/of-dma.c:57: warning: Excess function parameter 'of_dma' description in 'of_dma_router_xlate' drivers/dma/of-dma.c:107: warning: Function parameter or member 'data' not described in 'of_dma_controller_register' drivers/dma/of-dma.c:308: warning: Function parameter or member 'ofdma' not described in 'of_dma_simple_xlate' drivers/dma/of-dma.c:308: warning: Excess function parameter 'of_dma' description in 'of_dma_simple_xlate' drivers/dma/of-dma.c:338: warning: Function parameter or member 'ofdma' not described in 'of_dma_xlate_by_chan_id' drivers/dma/of-dma.c:338: warning: Excess function parameter 'of_dma' description in 'of_dma_xlate_by_chan_id' Signed-off-by: Lee Jones Link: https://lore.kernel.org/r/20200714111546.1755231-3-lee.jones@linaro.org Signed-off-by: Vinod Koul --- drivers/dma/of-dma.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/dma/of-dma.c b/drivers/dma/of-dma.c index b2c2b5e8093cf..863f2aaf5c8f7 100644 --- a/drivers/dma/of-dma.c +++ b/drivers/dma/of-dma.c @@ -46,7 +46,7 @@ static struct of_dma *of_dma_find_controller(struct of_phandle_args *dma_spec) /** * of_dma_router_xlate - translation function for router devices * @dma_spec: pointer to DMA specifier as found in the device tree - * @of_dma: pointer to DMA controller data (router information) + * @ofdma: pointer to DMA controller data (router information) * * The function creates new dma_spec to be passed to the router driver's * of_dma_route_allocate() function to prepare a dma_spec which will be used @@ -92,7 +92,7 @@ static struct dma_chan *of_dma_router_xlate(struct of_phandle_args *dma_spec, * @np: device node of DMA controller * @of_dma_xlate: translation function which converts a phandle * arguments list into a dma_chan structure - * @data pointer to controller specific data to be used by + * @data: pointer to controller specific data to be used by * translation function * * Returns 0 on success or appropriate errno value on error. @@ -295,7 +295,7 @@ EXPORT_SYMBOL_GPL(of_dma_request_slave_channel); /** * of_dma_simple_xlate - Simple DMA engine translation function * @dma_spec: pointer to DMA specifier as found in the device tree - * @of_dma: pointer to DMA controller data + * @ofdma: pointer to DMA controller data * * A simple translation function for devices that use a 32-bit value for the * filter_param when calling the DMA engine dma_request_channel() function. @@ -323,7 +323,7 @@ EXPORT_SYMBOL_GPL(of_dma_simple_xlate); /** * of_dma_xlate_by_chan_id - Translate dt property to DMA channel by channel id * @dma_spec: pointer to DMA specifier as found in the device tree - * @of_dma: pointer to DMA controller data + * @ofdma: pointer to DMA controller data * * This function can be used as the of xlate callback for DMA driver which wants * to match the channel based on the channel id. When using this xlate function -- GitLab From ae70f785e5c9711dd4fdbd185ea81711c659a7e5 Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Tue, 14 Jul 2020 12:15:32 +0100 Subject: [PATCH 0428/1476] dmaengine: ep93xx_dma: Provide some missing struct attribute documentation A couple of entries were missed, causing kerneldoc to complain. Fixes the following W=1 kernel build warning(s): drivers/dma/ep93xx_dma.c:183: warning: Function parameter or member 'slave_config' not described in 'ep93xx_dma_chan' drivers/dma/ep93xx_dma.c:215: warning: Function parameter or member 'hw_synchronize' not described in 'ep93xx_dma_engine' Signed-off-by: Lee Jones Cc: Lennert Buytenhek Cc: Ryan Mallon Cc: Mika Westerberg Link: https://lore.kernel.org/r/20200714111546.1755231-4-lee.jones@linaro.org Signed-off-by: Vinod Koul --- drivers/dma/ep93xx_dma.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/dma/ep93xx_dma.c b/drivers/dma/ep93xx_dma.c index 9c8b4d35cf03d..87a246012629f 100644 --- a/drivers/dma/ep93xx_dma.c +++ b/drivers/dma/ep93xx_dma.c @@ -147,6 +147,7 @@ struct ep93xx_dma_desc { * is set via .device_config before slave operation is * prepared * @runtime_ctrl: M2M runtime values for the control register. + * @slave_config: slave configuration * * As EP93xx DMA controller doesn't support real chained DMA descriptors we * will have slightly different scheme here: @active points to a head of @@ -187,6 +188,7 @@ struct ep93xx_dma_chan { * @dma_dev: holds the dmaengine device * @m2m: is this an M2M or M2P device * @hw_setup: method which sets the channel up for operation + * @hw_synchronize: synchronizes DMA channel termination to current context * @hw_shutdown: shuts the channel down and flushes whatever is left * @hw_submit: pushes active descriptor(s) to the hardware * @hw_interrupt: handle the interrupt -- GitLab From 6cfb8321cfb5a50a2a9a0bbcf61a1bb55f13ace8 Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Tue, 14 Jul 2020 12:15:33 +0100 Subject: [PATCH 0429/1476] dmaengine: mmp_pdma: Demote obvious misuse of kerneldoc to standard comment blocks No attempt has been made to document any of the demoted functions here. Fixes the following W=1 kernel build warning(s): drivers/dma/mmp_pdma.c:298: warning: Function parameter or member 'chan' not described in 'start_pending_queue' drivers/dma/mmp_pdma.c:393: warning: Function parameter or member 'dchan' not described in 'mmp_pdma_alloc_chan_resources' drivers/dma/mmp_pdma.c:862: warning: Function parameter or member 'dchan' not described in 'mmp_pdma_issue_pending' Signed-off-by: Lee Jones Link: https://lore.kernel.org/r/20200714111546.1755231-5-lee.jones@linaro.org Signed-off-by: Vinod Koul --- drivers/dma/mmp_pdma.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/dma/mmp_pdma.c b/drivers/dma/mmp_pdma.c index 41c542eaa23a5..f42f792db277e 100644 --- a/drivers/dma/mmp_pdma.c +++ b/drivers/dma/mmp_pdma.c @@ -290,7 +290,7 @@ static void mmp_pdma_free_phy(struct mmp_pdma_chan *pchan) spin_unlock_irqrestore(&pdev->phy_lock, flags); } -/** +/* * start_pending_queue - transfer any pending transactions * pending list ==> running list */ @@ -381,7 +381,7 @@ mmp_pdma_alloc_descriptor(struct mmp_pdma_chan *chan) return desc; } -/** +/* * mmp_pdma_alloc_chan_resources - Allocate resources for DMA channel. * * This function will create a dma pool for descriptor allocation. @@ -854,7 +854,7 @@ static enum dma_status mmp_pdma_tx_status(struct dma_chan *dchan, return ret; } -/** +/* * mmp_pdma_issue_pending - Issue the DMA start command * pending list ==> running list */ -- GitLab From f9e036df575d8efce6fd469acd9df3148c2adf6e Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Tue, 14 Jul 2020 12:15:34 +0100 Subject: [PATCH 0430/1476] dmaengine: pl330: Demote obvious misuse of kerneldoc to standard comment block No 'struct' title is provided. Nor are any attribute descriptions. Fixes the following W=1 kernel build warning(s): drivers/dma/pl330.c:295: warning: cannot understand function prototype: 'struct pl330_reqcfg ' Signed-off-by: Lee Jones Cc: Philipp Zabel Cc: Jaswinder Singh Link: https://lore.kernel.org/r/20200714111546.1755231-6-lee.jones@linaro.org Signed-off-by: Vinod Koul --- drivers/dma/pl330.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/dma/pl330.c b/drivers/dma/pl330.c index 3be8d462eab41..2c508ee672b90 100644 --- a/drivers/dma/pl330.c +++ b/drivers/dma/pl330.c @@ -285,7 +285,7 @@ struct pl330_config { u32 irq_ns; }; -/** +/* * Request Configuration. * The PL330 core does not modify this and uses the last * working configuration if the request doesn't provide any. -- GitLab From 0b851134f63bb8bfc42c9babd032d6a800debddb Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Tue, 14 Jul 2020 12:15:35 +0100 Subject: [PATCH 0431/1476] dmaengine: ste_dma40: Supply 2 missing struct attribute descriptions Fixes the following W=1 kernel build warning(s): drivers/dma/ste_dma40.c:398: warning: Function parameter or member 'dma_addr' not described in 'd40_lcla_pool' drivers/dma/ste_dma40.c:601: warning: Function parameter or member 'dma_parms' not described in 'd40_base' Signed-off-by: Lee Jones Cc: Per Forlin Cc: Jonas Aaberg Link: https://lore.kernel.org/r/20200714111546.1755231-7-lee.jones@linaro.org Signed-off-by: Vinod Koul --- drivers/dma/ste_dma40.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/dma/ste_dma40.c b/drivers/dma/ste_dma40.c index de8bfd9a76e9e..21e2f1d0c2109 100644 --- a/drivers/dma/ste_dma40.c +++ b/drivers/dma/ste_dma40.c @@ -381,6 +381,7 @@ struct d40_desc { * struct d40_lcla_pool - LCLA pool settings and data. * * @base: The virtual address of LCLA. 18 bit aligned. + * @dma_addr: DMA address, if mapped * @base_unaligned: The orignal kmalloc pointer, if kmalloc is used. * This pointer is only there for clean-up on error. * @pages: The number of pages needed for all physical channels. @@ -534,6 +535,7 @@ struct d40_gen_dmac { * mode" allocated physical channels. * @num_log_chans: The number of logical channels. Calculated from * num_phy_chans. + * @dma_parms: DMA parameters for the channel * @dma_both: dma_device channels that can do both memcpy and slave transfers. * @dma_slave: dma_device channels that can do only do slave transfers. * @dma_memcpy: dma_device channels that can do only do memcpy transfers. -- GitLab From f15f720b82c45ed4efc6a3353c36f57ae38047ee Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Tue, 14 Jul 2020 12:15:36 +0100 Subject: [PATCH 0432/1476] dmaengine: altera-msgdma: Fix struct documentation blocks Fix some misspelling/description issues, demote non-kerneldoc header to standard comment block and provide a new description for msgdma_desc_config()'s 'stride' parameter. Fixes the following W=1 kernel build warning(s): drivers/dma/altera-msgdma.c:163: warning: Function parameter or member 'node' not described in 'msgdma_sw_desc' drivers/dma/altera-msgdma.c:163: warning: Function parameter or member 'tx_list' not described in 'msgdma_sw_desc' drivers/dma/altera-msgdma.c:197: warning: Function parameter or member 'lock' not described in 'msgdma_device' drivers/dma/altera-msgdma.c:197: warning: Function parameter or member 'dev' not described in 'msgdma_device' drivers/dma/altera-msgdma.c:197: warning: Function parameter or member 'irq_tasklet' not described in 'msgdma_device' drivers/dma/altera-msgdma.c:197: warning: Function parameter or member 'pending_list' not described in 'msgdma_device' drivers/dma/altera-msgdma.c:197: warning: Function parameter or member 'free_list' not described in 'msgdma_device' drivers/dma/altera-msgdma.c:197: warning: Function parameter or member 'active_list' not described in 'msgdma_device' drivers/dma/altera-msgdma.c:197: warning: Function parameter or member 'done_list' not described in 'msgdma_device' drivers/dma/altera-msgdma.c:197: warning: Function parameter or member 'desc_free_cnt' not described in 'msgdma_device' drivers/dma/altera-msgdma.c:197: warning: Function parameter or member 'idle' not described in 'msgdma_device' drivers/dma/altera-msgdma.c:197: warning: Function parameter or member 'dmadev' not described in 'msgdma_device' drivers/dma/altera-msgdma.c:197: warning: Function parameter or member 'dmachan' not described in 'msgdma_device' drivers/dma/altera-msgdma.c:197: warning: Function parameter or member 'hw_desq' not described in 'msgdma_device' drivers/dma/altera-msgdma.c:197: warning: Function parameter or member 'sw_desq' not described in 'msgdma_device' drivers/dma/altera-msgdma.c:197: warning: Function parameter or member 'npendings' not described in 'msgdma_device' drivers/dma/altera-msgdma.c:197: warning: Function parameter or member 'slave_cfg' not described in 'msgdma_device' drivers/dma/altera-msgdma.c:197: warning: Function parameter or member 'irq' not described in 'msgdma_device' drivers/dma/altera-msgdma.c:197: warning: Function parameter or member 'csr' not described in 'msgdma_device' drivers/dma/altera-msgdma.c:197: warning: Function parameter or member 'desc' not described in 'msgdma_device' drivers/dma/altera-msgdma.c:197: warning: Function parameter or member 'resp' not described in 'msgdma_device' drivers/dma/altera-msgdma.c:265: warning: Function parameter or member 'stride' not described in 'msgdma_desc_config' Signed-off-by: Lee Jones Cc: Stefan Roese Link: https://lore.kernel.org/r/20200714111546.1755231-8-lee.jones@linaro.org Signed-off-by: Vinod Koul --- drivers/dma/altera-msgdma.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/dma/altera-msgdma.c b/drivers/dma/altera-msgdma.c index 539e785039cac..321ac3a7aa418 100644 --- a/drivers/dma/altera-msgdma.c +++ b/drivers/dma/altera-msgdma.c @@ -153,7 +153,8 @@ struct msgdma_extended_desc { * struct msgdma_sw_desc - implements a sw descriptor * @async_tx: support for the async_tx api * @hw_desc: assosiated HW descriptor - * @free_list: node of the free SW descriprots list + * @node: node to move from the free list to the tx list + * @tx_list: transmit list node */ struct msgdma_sw_desc { struct dma_async_tx_descriptor async_tx; @@ -162,7 +163,7 @@ struct msgdma_sw_desc { struct list_head tx_list; }; -/** +/* * struct msgdma_device - DMA device structure */ struct msgdma_device { @@ -258,6 +259,7 @@ static void msgdma_free_desc_list(struct msgdma_device *mdev, * @dst: Destination buffer address * @src: Source buffer address * @len: Transfer length + * @stride: Read/write stride value to set */ static void msgdma_desc_config(struct msgdma_extended_desc *desc, dma_addr_t dst, dma_addr_t src, size_t len, -- GitLab From 21e3cdb0234f7a687a624ab6d9eb92e19f60f743 Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Tue, 14 Jul 2020 12:15:37 +0100 Subject: [PATCH 0433/1476] dmaengine: at_hdmac: Repair parameter misspelling and demote non-kerneldoc headers Fixes the following W=1 kernel build warning(s): drivers/dma/at_hdmac.c:666: warning: Function parameter or member 'tx' not described in 'atc_tx_submit' drivers/dma/at_hdmac.c:666: warning: Excess function parameter 'desc' description in 'atc_tx_submit' drivers/dma/at_hdmac.c:1206: warning: Function parameter or member 'reg_width' not described in 'atc_dma_cyclic_check_values' drivers/dma/at_hdmac.c:1206: warning: Function parameter or member 'buf_addr' not described in 'atc_dma_cyclic_check_values' drivers/dma/at_hdmac.c:1206: warning: Function parameter or member 'period_len' not described in 'atc_dma_cyclic_check_values' drivers/dma/at_hdmac.c:1228: warning: Function parameter or member 'chan' not described in 'atc_dma_cyclic_fill_desc' drivers/dma/at_hdmac.c:1228: warning: Function parameter or member 'desc' not described in 'atc_dma_cyclic_fill_desc' drivers/dma/at_hdmac.c:1228: warning: Function parameter or member 'period_index' not described in 'atc_dma_cyclic_fill_desc' drivers/dma/at_hdmac.c:1228: warning: Function parameter or member 'buf_addr' not described in 'atc_dma_cyclic_fill_desc' drivers/dma/at_hdmac.c:1228: warning: Function parameter or member 'reg_width' not described in 'atc_dma_cyclic_fill_desc' drivers/dma/at_hdmac.c:1228: warning: Function parameter or member 'period_len' not described in 'atc_dma_cyclic_fill_desc' drivers/dma/at_hdmac.c:1228: warning: Function parameter or member 'direction' not described in 'atc_dma_cyclic_fill_desc' Signed-off-by: Lee Jones Cc: Ludovic Desroches Cc: Nicolas Ferre Link: https://lore.kernel.org/r/20200714111546.1755231-9-lee.jones@linaro.org Signed-off-by: Vinod Koul --- drivers/dma/at_hdmac.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/dma/at_hdmac.c b/drivers/dma/at_hdmac.c index 73a20780744bf..45bbcd6146fd2 100644 --- a/drivers/dma/at_hdmac.c +++ b/drivers/dma/at_hdmac.c @@ -656,7 +656,7 @@ static irqreturn_t at_dma_interrupt(int irq, void *dev_id) /** * atc_tx_submit - set the prepared descriptor(s) to be executed by the engine - * @desc: descriptor at the head of the transaction chain + * @tx: descriptor at the head of the transaction chain * * Queue chain if DMA engine is working already * @@ -1196,7 +1196,7 @@ err: return NULL; } -/** +/* * atc_dma_cyclic_check_values * Check for too big/unaligned periods and unaligned DMA buffer */ @@ -1217,7 +1217,7 @@ err_out: return -EINVAL; } -/** +/* * atc_dma_cyclic_fill_desc - Fill one period descriptor */ static int -- GitLab From 023069ba3f440778ec0d7e6158e00ccea61933dc Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Tue, 14 Jul 2020 12:15:38 +0100 Subject: [PATCH 0434/1476] dmaengine: sun4i-dma: Demote obvious misuse of kerneldoc to standard comment blocks MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit No attempt has been made to document any of the demoted functions here. Fixes the following W=1 kernel build warning(s): drivers/dma/sun4i-dma.c:321: warning: Function parameter or member 'priv' not described in '__execute_vchan_pending' drivers/dma/sun4i-dma.c:321: warning: Function parameter or member 'vchan' not described in '__execute_vchan_pending' drivers/dma/sun4i-dma.c:435: warning: Function parameter or member 'chan' not described in 'generate_ndma_promise' drivers/dma/sun4i-dma.c:435: warning: Function parameter or member 'src' not described in 'generate_ndma_promise' drivers/dma/sun4i-dma.c:435: warning: Function parameter or member 'dest' not described in 'generate_ndma_promise' drivers/dma/sun4i-dma.c:435: warning: Function parameter or member 'len' not described in 'generate_ndma_promise' drivers/dma/sun4i-dma.c:435: warning: Function parameter or member 'sconfig' not described in 'generate_ndma_promise' drivers/dma/sun4i-dma.c:435: warning: Function parameter or member 'direction' not described in 'generate_ndma_promise' drivers/dma/sun4i-dma.c:501: warning: Function parameter or member 'chan' not described in 'generate_ddma_promise' drivers/dma/sun4i-dma.c:501: warning: Function parameter or member 'src' not described in 'generate_ddma_promise' drivers/dma/sun4i-dma.c:501: warning: Function parameter or member 'dest' not described in 'generate_ddma_promise' drivers/dma/sun4i-dma.c:501: warning: Function parameter or member 'len' not described in 'generate_ddma_promise' drivers/dma/sun4i-dma.c:501: warning: Function parameter or member 'sconfig' not described in 'generate_ddma_promise' drivers/dma/sun4i-dma.c:577: warning: Function parameter or member 'contract' not described in 'get_next_cyclic_promise' drivers/dma/sun4i-dma.c:596: warning: Function parameter or member 'vd' not described in 'sun4i_dma_free_contract' Signed-off-by: Lee Jones Acked-by: Chen-Yu Tsai Cc: Maxime Ripard Cc: Chen-Yu Tsai Cc: "Emilio López" Link: https://lore.kernel.org/r/20200714111546.1755231-10-lee.jones@linaro.org Signed-off-by: Vinod Koul --- drivers/dma/sun4i-dma.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/dma/sun4i-dma.c b/drivers/dma/sun4i-dma.c index e7ff09a5031db..e8b6633ae6612 100644 --- a/drivers/dma/sun4i-dma.c +++ b/drivers/dma/sun4i-dma.c @@ -307,7 +307,7 @@ static void set_pchan_interrupt(struct sun4i_dma_dev *priv, spin_unlock_irqrestore(&priv->lock, flags); } -/** +/* * Execute pending operations on a vchan * * When given a vchan, this function will try to acquire a suitable @@ -419,7 +419,7 @@ static int sanitize_config(struct dma_slave_config *sconfig, return 0; } -/** +/* * Generate a promise, to be used in a normal DMA contract. * * A NDMA promise contains all the information required to program the @@ -486,7 +486,7 @@ fail: return NULL; } -/** +/* * Generate a promise, to be used in a dedicated DMA contract. * * A DDMA promise contains all the information required to program the @@ -543,7 +543,7 @@ fail: return NULL; } -/** +/* * Generate a contract * * Contracts function as DMA descriptors. As our hardware does not support @@ -565,7 +565,7 @@ static struct sun4i_dma_contract *generate_dma_contract(void) return contract; } -/** +/* * Get next promise on a cyclic transfer * * Cyclic contracts contain a series of promises which are executed on a @@ -589,7 +589,7 @@ get_next_cyclic_promise(struct sun4i_dma_contract *contract) return promise; } -/** +/* * Free a contract and all its associated promises */ static void sun4i_dma_free_contract(struct virt_dma_desc *vd) -- GitLab From 041c4646003e237d2fa0ce625309661d2c3bf2ae Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Tue, 14 Jul 2020 12:15:39 +0100 Subject: [PATCH 0435/1476] dmaengine: fsl-qdma: Fix 'struct fsl_qdma_format' formatting issue Kerneldoc formatting for attributes should be '@.*: '. Fixes the following W=1 kernel build warning(s): drivers/dma/fsl-qdma.c:154: warning: Function parameter or member 'data' not described in 'fsl_qdma_format' Signed-off-by: Lee Jones Cc: Wen He Cc: Jiaheng Fan Link: https://lore.kernel.org/r/20200714111546.1755231-11-lee.jones@linaro.org Signed-off-by: Vinod Koul --- drivers/dma/fsl-qdma.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/dma/fsl-qdma.c b/drivers/dma/fsl-qdma.c index 95cc0256b3878..c33f491fae3e5 100644 --- a/drivers/dma/fsl-qdma.c +++ b/drivers/dma/fsl-qdma.c @@ -136,7 +136,7 @@ * @__reserved1: Reserved field. * @cfg8b_w1: Compound descriptor command queue origin produced * by qDMA and dynamic debug field. - * @data Pointer to the memory 40-bit address, describes DMA + * @data: Pointer to the memory 40-bit address, describes DMA * source information and DMA destination information. */ struct fsl_qdma_format { -- GitLab From d0c4a1492810f61e6a982731d06f52917a577876 Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Tue, 14 Jul 2020 12:15:40 +0100 Subject: [PATCH 0436/1476] dmaengine: imx-sdma: Correct formatting issue and provide 2 new descriptions Fixes the following W=1 kernel build warning(s): drivers/dma/imx-sdma.c:383: warning: Function parameter or member 'slave_config' not described in 'sdma_channel' drivers/dma/imx-sdma.c:383: warning: Function parameter or member 'context_loaded' not described in 'sdma_channel' drivers/dma/imx-sdma.c:383: warning: Function parameter or member 'terminate_worker' not described in 'sdma_channel' Signed-off-by: Lee Jones Cc: Shawn Guo Cc: Sascha Hauer Cc: Pengutronix Kernel Team Cc: Fabio Estevam Cc: NXP Linux Team Link: https://lore.kernel.org/r/20200714111546.1755231-12-lee.jones@linaro.org Signed-off-by: Vinod Koul --- drivers/dma/imx-sdma.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/dma/imx-sdma.c b/drivers/dma/imx-sdma.c index 91774039ae5d6..b8cfc9d5f1a25 100644 --- a/drivers/dma/imx-sdma.c +++ b/drivers/dma/imx-sdma.c @@ -335,7 +335,7 @@ struct sdma_desc { * @sdma: pointer to the SDMA engine for this channel * @channel: the channel number, matches dmaengine chan_id + 1 * @direction: transfer type. Needed for setting SDMA script - * @slave_config Slave configuration + * @slave_config: Slave configuration * @peripheral_type: Peripheral type. Needed for setting SDMA script * @event_id0: aka dma request line * @event_id1: for channels that use 2 events @@ -354,8 +354,10 @@ struct sdma_desc { * @shp_addr: value for gReg[6] * @per_addr: value for gReg[2] * @status: status of dma channel + * @context_loaded: ensure context is only loaded once * @data: specific sdma interface structure * @bd_pool: dma_pool for bd + * @terminate_worker: used to call back into terminate work function */ struct sdma_channel { struct virt_dma_chan vc; -- GitLab From 1c5933c5a98cc82e5baca48a45c06ffc79e4fb7e Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Tue, 14 Jul 2020 12:15:41 +0100 Subject: [PATCH 0437/1476] dmaengine: iop-adma: Function parameter documentation must adhere to correct formatting Also remove superfluous entry. Fixes the following W=1 kernel build warning(s): drivers/dma/iop-adma.c:418: warning: Function parameter or member 'chan' not described in 'iop_adma_alloc_chan_resources' Signed-off-by: Lee Jones Link: https://lore.kernel.org/r/20200714111546.1755231-13-lee.jones@linaro.org Signed-off-by: Vinod Koul --- drivers/dma/iop-adma.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/dma/iop-adma.c b/drivers/dma/iop-adma.c index db0e274126fb7..3350bffb2e937 100644 --- a/drivers/dma/iop-adma.c +++ b/drivers/dma/iop-adma.c @@ -406,8 +406,7 @@ static void iop_chan_start_null_xor(struct iop_adma_chan *iop_chan); /** * iop_adma_alloc_chan_resources - returns the number of allocated descriptors - * @chan - allocate descriptor resources for this channel - * @client - current client requesting the channel be ready for requests + * @chan: allocate descriptor resources for this channel * * Note: We keep the slots for 1 operation on iop_chan->chain at all times. To * avoid deadlock, via async_xor, num_descs_in_pool must at a minimum be -- GitLab From 8702624bd9472d85372d679fbdd705b91d9c4bbc Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Tue, 14 Jul 2020 12:15:42 +0100 Subject: [PATCH 0438/1476] dmaengine: nbpfaxi: Provide some missing attribute docs and split out slave info Fixes the following W=1 kernel build warning(s): drivers/dma/nbpfaxi.c:157: warning: Function parameter or member 'chan' not described in 'nbpf_desc' drivers/dma/nbpfaxi.c:220: warning: Function parameter or member 'tasklet' not described in 'nbpf_channel' drivers/dma/nbpfaxi.c:220: warning: Function parameter or member 'slave_src_addr' not described in 'nbpf_channel' drivers/dma/nbpfaxi.c:220: warning: Function parameter or member 'slave_src_width' not described in 'nbpf_channel' drivers/dma/nbpfaxi.c:220: warning: Function parameter or member 'slave_src_burst' not described in 'nbpf_channel' drivers/dma/nbpfaxi.c:220: warning: Function parameter or member 'slave_dst_addr' not described in 'nbpf_channel' drivers/dma/nbpfaxi.c:220: warning: Function parameter or member 'slave_dst_width' not described in 'nbpf_channel' drivers/dma/nbpfaxi.c:220: warning: Function parameter or member 'slave_dst_burst' not described in 'nbpf_channel' drivers/dma/nbpfaxi.c:220: warning: Function parameter or member 'running' not described in 'nbpf_channel' drivers/dma/nbpfaxi.c:220: warning: Function parameter or member 'paused' not described in 'nbpf_channel' Signed-off-by: Lee Jones Cc: Alexei Starovoitov Cc: Daniel Borkmann Cc: Martin KaFai Lau Cc: Song Liu Cc: Yonghong Song Cc: Andrii Nakryiko Cc: John Fastabend Cc: KP Singh Cc: Guennadi Liakhovetski Cc: netdev@vger.kernel.org Cc: bpf@vger.kernel.org Link: https://lore.kernel.org/r/20200714111546.1755231-14-lee.jones@linaro.org Signed-off-by: Vinod Koul --- drivers/dma/nbpfaxi.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/drivers/dma/nbpfaxi.c b/drivers/dma/nbpfaxi.c index 594409a6e9752..74df621402e10 100644 --- a/drivers/dma/nbpfaxi.c +++ b/drivers/dma/nbpfaxi.c @@ -144,6 +144,7 @@ struct nbpf_link_desc { * @async_tx: dmaengine object * @user_wait: waiting for a user ack * @length: total transfer length + * @chan: associated DMAC channel * @sg: list of hardware descriptors, represented by struct nbpf_link_desc * @node: member in channel descriptor lists */ @@ -174,13 +175,17 @@ struct nbpf_desc_page { /** * struct nbpf_channel - one DMAC channel * @dma_chan: standard dmaengine channel object + * @tasklet: channel specific tasklet used for callbacks * @base: register address base * @nbpf: DMAC * @name: IRQ name * @irq: IRQ number - * @slave_addr: address for slave DMA - * @slave_width:slave data size in bytes - * @slave_burst:maximum slave burst size in bytes + * @slave_src_addr: source address for slave DMA + * @slave_src_width: source slave data size in bytes + * @slave_src_burst: maximum source slave burst size in bytes + * @slave_dst_addr: destination address for slave DMA + * @slave_dst_width: destination slave data size in bytes + * @slave_dst_burst: maximum destination slave burst size in bytes * @terminal: DMA terminal, assigned to this channel * @dmarq_cfg: DMA request line configuration - high / low, edge / level for NBPF_CHAN_CFG * @flags: configuration flags from DT @@ -191,6 +196,8 @@ struct nbpf_desc_page { * @active: list of descriptors, scheduled for processing * @done: list of completed descriptors, waiting post-processing * @desc_page: list of additionally allocated descriptor pages - if any + * @running: linked descriptor of running transaction + * @paused: are translations on this channel paused? */ struct nbpf_channel { struct dma_chan dma_chan; -- GitLab From 5726164fa4862f266cbdaa52e251343dcc9d3ea2 Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Tue, 14 Jul 2020 12:15:43 +0100 Subject: [PATCH 0439/1476] dmaengine: xgene-dma: Provide descriptions for 'dev' and 'clk' in device's ddata Fixes the following W=1 kernel build warning(s): drivers/dma/xgene-dma.c:310: warning: Function parameter or member 'dev' not described in 'xgene_dma' drivers/dma/xgene-dma.c:310: warning: Function parameter or member 'clk' not described in 'xgene_dma' Signed-off-by: Lee Jones Cc: Prasad Sahu Cc: Loc Ho Link: https://lore.kernel.org/r/20200714111546.1755231-15-lee.jones@linaro.org Signed-off-by: Vinod Koul --- drivers/dma/xgene-dma.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/dma/xgene-dma.c b/drivers/dma/xgene-dma.c index cd60fa6d67501..4f733d37a22e2 100644 --- a/drivers/dma/xgene-dma.c +++ b/drivers/dma/xgene-dma.c @@ -287,6 +287,8 @@ struct xgene_dma_chan { /** * struct xgene_dma - internal representation of an X-Gene DMA device + * @dev: reference to this device's struct device + * @clk: reference to this device's clock * @err_irq: DMA error irq number * @ring_num: start id number for DMA ring * @csr_dma: base for DMA register access -- GitLab From e6fe333c862b29978fdf625f7ff2f9a9cbc91935 Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Tue, 14 Jul 2020 12:15:44 +0100 Subject: [PATCH 0440/1476] dmaengine: mv_xor_v2: Supply some missing 'struct mv_xor_v2_device' attribute docs Fixes the following W=1 kernel build warning(s): drivers/dma/mv_xor_v2.c:168: warning: Function parameter or member 'clk' not described in 'mv_xor_v2_device' drivers/dma/mv_xor_v2.c:168: warning: Function parameter or member 'reg_clk' not described in 'mv_xor_v2_device' drivers/dma/mv_xor_v2.c:168: warning: Function parameter or member 'irq_tasklet' not described in 'mv_xor_v2_device' drivers/dma/mv_xor_v2.c:168: warning: Function parameter or member 'hw_queue_idx' not described in 'mv_xor_v2_device' drivers/dma/mv_xor_v2.c:168: warning: Function parameter or member 'msi_desc' not described in 'mv_xor_v2_device' Signed-off-by: Lee Jones Link: https://lore.kernel.org/r/20200714111546.1755231-16-lee.jones@linaro.org Signed-off-by: Vinod Koul --- drivers/dma/mv_xor_v2.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/dma/mv_xor_v2.c b/drivers/dma/mv_xor_v2.c index 157c959311eaf..9225f08dfee92 100644 --- a/drivers/dma/mv_xor_v2.c +++ b/drivers/dma/mv_xor_v2.c @@ -135,9 +135,11 @@ struct mv_xor_v2_descriptor { /** * struct mv_xor_v2_device - implements a xor device * @lock: lock for the engine + * @clk: reference to the 'core' clock + * @reg_clk: reference to the 'reg' clock * @dma_base: memory mapped DMA register base * @glob_base: memory mapped global register base - * @irq_tasklet: + * @irq_tasklet: tasklet used for IRQ handling call-backs * @free_sw_desc: linked list of free SW descriptors * @dmadev: dma device * @dmachan: dma channel @@ -146,6 +148,8 @@ struct mv_xor_v2_descriptor { * @sw_desq: SW descriptors queue * @desc_size: HW descriptor size * @npendings: number of pending descriptors (for which tx_submit has + * @hw_queue_idx: HW queue index + * @msi_desc: local interrupt descriptor information * been called, but not yet issue_pending) */ struct mv_xor_v2_device { -- GitLab From af49b016c0f97515ee193991af338c624271d839 Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Tue, 14 Jul 2020 12:15:45 +0100 Subject: [PATCH 0441/1476] dmaengine: ioat: init: Correct misspelling of function parameter 'c' for channel Fixes the following W=1 kernel build warning(s): drivers/dma/ioat/init.c:608: warning: Function parameter or member 'c' not described in 'ioat_free_chan_resources' drivers/dma/ioat/init.c:608: warning: Excess function parameter 'chan' description in 'ioat_free_chan_resources' Signed-off-by: Lee Jones Cc: Logan Gunthorpe Link: https://lore.kernel.org/r/20200714111546.1755231-17-lee.jones@linaro.org Signed-off-by: Vinod Koul --- drivers/dma/ioat/init.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/dma/ioat/init.c b/drivers/dma/ioat/init.c index 58d13564f88b4..df0935a0021ad 100644 --- a/drivers/dma/ioat/init.c +++ b/drivers/dma/ioat/init.c @@ -602,7 +602,7 @@ static void ioat_enumerate_channels(struct ioatdma_device *ioat_dma) /** * ioat_free_chan_resources - release all the descriptors - * @chan: the channel to be cleaned + * @c: the channel to be cleaned */ static void ioat_free_chan_resources(struct dma_chan *c) { -- GitLab From c1309fd09bfe98cd767452bcdbdf1442086cc93a Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Tue, 14 Jul 2020 12:15:46 +0100 Subject: [PATCH 0442/1476] dmaengine: ioat: Fix some parameter misspelling and provide description for phys_complete Fixes the following W=1 kernel build warning(s): drivers/dma/ioat/dma.c:202: warning: Function parameter or member 'ioat_chan' not described in 'ioat_update_pending' drivers/dma/ioat/dma.c:202: warning: Excess function parameter 'ioat' description in 'ioat_update_pending' drivers/dma/ioat/dma.c:465: warning: Function parameter or member 'ioat_chan' not described in 'ioat_check_space_lock' drivers/dma/ioat/dma.c:465: warning: Excess function parameter 'ioat' description in 'ioat_check_space_lock' drivers/dma/ioat/dma.c:591: warning: Function parameter or member 'ioat_chan' not described in '__cleanup' drivers/dma/ioat/dma.c:591: warning: Function parameter or member 'phys_complete' not described in '__cleanup' drivers/dma/ioat/dma.c:591: warning: Excess function parameter 'ioat' description in '__cleanup' Signed-off-by: Lee Jones Cc: Dave Jiang Cc: Leonid Ravich Link: https://lore.kernel.org/r/20200714111546.1755231-18-lee.jones@linaro.org Signed-off-by: Vinod Koul --- drivers/dma/ioat/dma.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/dma/ioat/dma.c b/drivers/dma/ioat/dma.c index 8ad0ad861c861..9d5744e880608 100644 --- a/drivers/dma/ioat/dma.c +++ b/drivers/dma/ioat/dma.c @@ -181,7 +181,7 @@ void ioat_issue_pending(struct dma_chan *c) /** * ioat_update_pending - log pending descriptors - * @ioat: ioat+ channel + * @ioat_chan: ioat+ channel * * Check if the number of unsubmitted descriptors has exceeded the * watermark. Called with prep_lock held @@ -445,7 +445,7 @@ ioat_alloc_ring(struct dma_chan *c, int order, gfp_t flags) /** * ioat_check_space_lock - verify space and grab ring producer lock - * @ioat: ioat,3 channel (ring) to operate on + * @ioat_chan: ioat,3 channel (ring) to operate on * @num_descs: allocation length */ int ioat_check_space_lock(struct ioatdma_chan *ioat_chan, int num_descs) @@ -573,7 +573,8 @@ desc_get_errstat(struct ioatdma_chan *ioat_chan, struct ioat_ring_ent *desc) /** * __cleanup - reclaim used descriptors - * @ioat: channel (ring) to clean + * @ioat_chan: channel (ring) to clean + * @phys_complete: zeroed (or not) completion address (from status) */ static void __cleanup(struct ioatdma_chan *ioat_chan, dma_addr_t phys_complete) { -- GitLab From 029ab30b4c0a7ec587eece1ec07c3981fdff2bed Mon Sep 17 00:00:00 2001 From: Bharata B Rao Date: Fri, 3 Jul 2020 11:06:06 +0530 Subject: [PATCH 0443/1476] powerpc/mm: Enable radix GTSE only if supported. Make GTSE an MMU feature and enable it by default for radix. However for guest, conditionally enable it if hypervisor supports it via OV5 vector. Let prom_init ask for radix GTSE only if the support exists. Having GTSE as an MMU feature will make it easy to enable radix without GTSE. Currently radix assumes GTSE is enabled by default. Signed-off-by: Bharata B Rao Reviewed-by: Aneesh Kumar K.V Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20200703053608.12884-2-bharata@linux.ibm.com --- arch/powerpc/include/asm/mmu.h | 4 ++++ arch/powerpc/kernel/dt_cpu_ftrs.c | 1 + arch/powerpc/kernel/prom_init.c | 13 ++++++++----- arch/powerpc/mm/init_64.c | 5 ++++- 4 files changed, 17 insertions(+), 6 deletions(-) diff --git a/arch/powerpc/include/asm/mmu.h b/arch/powerpc/include/asm/mmu.h index f4ac25d4df05a..884d519959346 100644 --- a/arch/powerpc/include/asm/mmu.h +++ b/arch/powerpc/include/asm/mmu.h @@ -28,6 +28,9 @@ * Individual features below. */ +/* Guest Translation Shootdown Enable */ +#define MMU_FTR_GTSE ASM_CONST(0x00001000) + /* * Support for 68 bit VA space. We added that from ISA 2.05 */ @@ -173,6 +176,7 @@ enum { #endif #ifdef CONFIG_PPC_RADIX_MMU MMU_FTR_TYPE_RADIX | + MMU_FTR_GTSE | #ifdef CONFIG_PPC_KUAP MMU_FTR_RADIX_KUAP | #endif /* CONFIG_PPC_KUAP */ diff --git a/arch/powerpc/kernel/dt_cpu_ftrs.c b/arch/powerpc/kernel/dt_cpu_ftrs.c index a0edeb391e3e4..ac650c233cd97 100644 --- a/arch/powerpc/kernel/dt_cpu_ftrs.c +++ b/arch/powerpc/kernel/dt_cpu_ftrs.c @@ -336,6 +336,7 @@ static int __init feat_enable_mmu_radix(struct dt_cpu_feature *f) #ifdef CONFIG_PPC_RADIX_MMU cur_cpu_spec->mmu_features |= MMU_FTR_TYPE_RADIX; cur_cpu_spec->mmu_features |= MMU_FTRS_HASH_BASE; + cur_cpu_spec->mmu_features |= MMU_FTR_GTSE; cur_cpu_spec->cpu_user_features |= PPC_FEATURE_HAS_MMU; return 1; diff --git a/arch/powerpc/kernel/prom_init.c b/arch/powerpc/kernel/prom_init.c index 90c604d00b7d0..cbc605cfdec06 100644 --- a/arch/powerpc/kernel/prom_init.c +++ b/arch/powerpc/kernel/prom_init.c @@ -1336,12 +1336,15 @@ static void __init prom_check_platform_support(void) } } - if (supported.radix_mmu && supported.radix_gtse && - IS_ENABLED(CONFIG_PPC_RADIX_MMU)) { - /* Radix preferred - but we require GTSE for now */ - prom_debug("Asking for radix with GTSE\n"); + if (supported.radix_mmu && IS_ENABLED(CONFIG_PPC_RADIX_MMU)) { + /* Radix preferred - Check if GTSE is also supported */ + prom_debug("Asking for radix\n"); ibm_architecture_vec.vec5.mmu = OV5_FEAT(OV5_MMU_RADIX); - ibm_architecture_vec.vec5.radix_ext = OV5_FEAT(OV5_RADIX_GTSE); + if (supported.radix_gtse) + ibm_architecture_vec.vec5.radix_ext = + OV5_FEAT(OV5_RADIX_GTSE); + else + prom_debug("Radix GTSE isn't supported\n"); } else if (supported.hash_mmu) { /* Default to hash mmu (if we can) */ prom_debug("Asking for hash\n"); diff --git a/arch/powerpc/mm/init_64.c b/arch/powerpc/mm/init_64.c index bc73abf0bc25e..152aa0200cef7 100644 --- a/arch/powerpc/mm/init_64.c +++ b/arch/powerpc/mm/init_64.c @@ -407,12 +407,15 @@ static void __init early_check_vec5(void) if (!(vec5[OV5_INDX(OV5_RADIX_GTSE)] & OV5_FEAT(OV5_RADIX_GTSE))) { pr_warn("WARNING: Hypervisor doesn't support RADIX with GTSE\n"); - } + cur_cpu_spec->mmu_features &= ~MMU_FTR_GTSE; + } else + cur_cpu_spec->mmu_features |= MMU_FTR_GTSE; /* Do radix anyway - the hypervisor said we had to */ cur_cpu_spec->mmu_features |= MMU_FTR_TYPE_RADIX; } else if (mmu_supported == OV5_FEAT(OV5_MMU_HASH)) { /* Hypervisor only supports hash - disable radix */ cur_cpu_spec->mmu_features &= ~MMU_FTR_TYPE_RADIX; + cur_cpu_spec->mmu_features &= ~MMU_FTR_GTSE; } } -- GitLab From b6c84175078ff022b343b7b0737aeb33001ca90c Mon Sep 17 00:00:00 2001 From: Bharata B Rao Date: Fri, 3 Jul 2020 11:06:07 +0530 Subject: [PATCH 0444/1476] powerpc/pseries: H_REGISTER_PROC_TBL should ask for GTSE only if enabled H_REGISTER_PROC_TBL asks for GTSE by default. GTSE flag bit should be set only when GTSE is supported. Signed-off-by: Bharata B Rao Reviewed-by: Aneesh Kumar K.V Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20200703053608.12884-3-bharata@linux.ibm.com --- arch/powerpc/platforms/pseries/lpar.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/arch/powerpc/platforms/pseries/lpar.c b/arch/powerpc/platforms/pseries/lpar.c index fd26f3d21d7b4..f82569a505f11 100644 --- a/arch/powerpc/platforms/pseries/lpar.c +++ b/arch/powerpc/platforms/pseries/lpar.c @@ -1680,9 +1680,11 @@ static int pseries_lpar_register_process_table(unsigned long base, if (table_size) flags |= PROC_TABLE_NEW; - if (radix_enabled()) - flags |= PROC_TABLE_RADIX | PROC_TABLE_GTSE; - else + if (radix_enabled()) { + flags |= PROC_TABLE_RADIX; + if (mmu_has_feature(MMU_FTR_GTSE)) + flags |= PROC_TABLE_GTSE; + } else flags |= PROC_TABLE_HPT_SLB; for (;;) { rc = plpar_hcall_norets(H_REGISTER_PROC_TBL, flags, base, -- GitLab From dd3d9aa5589c52efaec12ffeb84f0f5f8208fbc3 Mon Sep 17 00:00:00 2001 From: Nicholas Piggin Date: Fri, 3 Jul 2020 11:06:08 +0530 Subject: [PATCH 0445/1476] powerpc/mm/book3s64/radix: Off-load TLB invalidations to host when !GTSE When platform doesn't support GTSE, let TLB invalidation requests for radix guests be off-loaded to the host using H_RPT_INVALIDATE hcall. [hcall wrapper, error path handling and renames] Signed-off-by: Nicholas Piggin Signed-off-by: Bharata B Rao Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20200703053608.12884-4-bharata@linux.ibm.com --- .../include/asm/book3s/64/tlbflush-radix.h | 15 ++++ arch/powerpc/include/asm/hvcall.h | 34 +++++++- arch/powerpc/include/asm/plpar_wrappers.h | 52 ++++++++++++ arch/powerpc/mm/book3s64/radix_tlb.c | 82 +++++++++++++++++-- 4 files changed, 175 insertions(+), 8 deletions(-) diff --git a/arch/powerpc/include/asm/book3s/64/tlbflush-radix.h b/arch/powerpc/include/asm/book3s/64/tlbflush-radix.h index ca8db193ae38a..94439e0cefc9c 100644 --- a/arch/powerpc/include/asm/book3s/64/tlbflush-radix.h +++ b/arch/powerpc/include/asm/book3s/64/tlbflush-radix.h @@ -2,10 +2,25 @@ #ifndef _ASM_POWERPC_TLBFLUSH_RADIX_H #define _ASM_POWERPC_TLBFLUSH_RADIX_H +#include + struct vm_area_struct; struct mm_struct; struct mmu_gather; +static inline u64 psize_to_rpti_pgsize(unsigned long psize) +{ + if (psize == MMU_PAGE_4K) + return H_RPTI_PAGE_4K; + if (psize == MMU_PAGE_64K) + return H_RPTI_PAGE_64K; + if (psize == MMU_PAGE_2M) + return H_RPTI_PAGE_2M; + if (psize == MMU_PAGE_1G) + return H_RPTI_PAGE_1G; + return H_RPTI_PAGE_ALL; +} + static inline int mmu_get_ap(int psize) { return mmu_psize_defs[psize].ap; diff --git a/arch/powerpc/include/asm/hvcall.h b/arch/powerpc/include/asm/hvcall.h index e90c073e437ea..43486e773bd6b 100644 --- a/arch/powerpc/include/asm/hvcall.h +++ b/arch/powerpc/include/asm/hvcall.h @@ -305,7 +305,8 @@ #define H_SCM_UNBIND_ALL 0x3FC #define H_SCM_HEALTH 0x400 #define H_SCM_PERFORMANCE_STATS 0x418 -#define MAX_HCALL_OPCODE H_SCM_PERFORMANCE_STATS +#define H_RPT_INVALIDATE 0x448 +#define MAX_HCALL_OPCODE H_RPT_INVALIDATE /* Scope args for H_SCM_UNBIND_ALL */ #define H_UNBIND_SCOPE_ALL (0x1) @@ -389,6 +390,37 @@ #define PROC_TABLE_RADIX 0x04 #define PROC_TABLE_GTSE 0x01 +/* + * Defines for + * H_RPT_INVALIDATE - Invalidate RPT translation lookaside information. + */ + +/* Type of translation to invalidate (type) */ +#define H_RPTI_TYPE_NESTED 0x0001 /* Invalidate nested guest partition-scope */ +#define H_RPTI_TYPE_TLB 0x0002 /* Invalidate TLB */ +#define H_RPTI_TYPE_PWC 0x0004 /* Invalidate Page Walk Cache */ +/* Invalidate Process Table Entries if H_RPTI_TYPE_NESTED is clear */ +#define H_RPTI_TYPE_PRT 0x0008 +/* Invalidate Partition Table Entries if H_RPTI_TYPE_NESTED is set */ +#define H_RPTI_TYPE_PAT 0x0008 +#define H_RPTI_TYPE_ALL (H_RPTI_TYPE_TLB | H_RPTI_TYPE_PWC | \ + H_RPTI_TYPE_PRT) +#define H_RPTI_TYPE_NESTED_ALL (H_RPTI_TYPE_TLB | H_RPTI_TYPE_PWC | \ + H_RPTI_TYPE_PAT) + +/* Invalidation targets (target) */ +#define H_RPTI_TARGET_CMMU 0x01 /* All virtual processors in the partition */ +#define H_RPTI_TARGET_CMMU_LOCAL 0x02 /* Current virtual processor */ +/* All nest/accelerator agents in use by the partition */ +#define H_RPTI_TARGET_NMMU 0x04 + +/* Page size mask (page sizes) */ +#define H_RPTI_PAGE_4K 0x01 +#define H_RPTI_PAGE_64K 0x02 +#define H_RPTI_PAGE_2M 0x04 +#define H_RPTI_PAGE_1G 0x08 +#define H_RPTI_PAGE_ALL (-1UL) + #ifndef __ASSEMBLY__ #include diff --git a/arch/powerpc/include/asm/plpar_wrappers.h b/arch/powerpc/include/asm/plpar_wrappers.h index 4497c8afb573b..4293c5d2ddf4a 100644 --- a/arch/powerpc/include/asm/plpar_wrappers.h +++ b/arch/powerpc/include/asm/plpar_wrappers.h @@ -334,6 +334,51 @@ static inline long plpar_get_cpu_characteristics(struct h_cpu_char_result *p) return rc; } +/* + * Wrapper to H_RPT_INVALIDATE hcall that handles return values appropriately + * + * - Returns H_SUCCESS on success + * - For H_BUSY return value, we retry the hcall. + * - For any other hcall failures, attempt a full flush once before + * resorting to BUG(). + * + * Note: This hcall is expected to fail only very rarely. The correct + * error recovery of killing the process/guest will be eventually + * needed. + */ +static inline long pseries_rpt_invalidate(u32 pid, u64 target, u64 type, + u64 page_sizes, u64 start, u64 end) +{ + long rc; + unsigned long all; + + while (true) { + rc = plpar_hcall_norets(H_RPT_INVALIDATE, pid, target, type, + page_sizes, start, end); + if (rc == H_BUSY) { + cpu_relax(); + continue; + } else if (rc == H_SUCCESS) + return rc; + + /* Flush request failed, try with a full flush once */ + if (type & H_RPTI_TYPE_NESTED) + all = H_RPTI_TYPE_NESTED | H_RPTI_TYPE_NESTED_ALL; + else + all = H_RPTI_TYPE_ALL; +retry: + rc = plpar_hcall_norets(H_RPT_INVALIDATE, pid, target, + all, page_sizes, 0, -1UL); + if (rc == H_BUSY) { + cpu_relax(); + goto retry; + } else if (rc == H_SUCCESS) + return rc; + + BUG(); + } +} + #else /* !CONFIG_PPC_PSERIES */ static inline long plpar_set_ciabr(unsigned long ciabr) @@ -346,6 +391,13 @@ static inline long plpar_pte_read_4(unsigned long flags, unsigned long ptex, { return 0; } + +static inline long pseries_rpt_invalidate(u32 pid, u64 target, u64 type, + u64 page_sizes, u64 start, u64 end) +{ + return 0; +} + #endif /* CONFIG_PPC_PSERIES */ #endif /* _ASM_POWERPC_PLPAR_WRAPPERS_H */ diff --git a/arch/powerpc/mm/book3s64/radix_tlb.c b/arch/powerpc/mm/book3s64/radix_tlb.c index b5cc9b23cf024..0d233763441fd 100644 --- a/arch/powerpc/mm/book3s64/radix_tlb.c +++ b/arch/powerpc/mm/book3s64/radix_tlb.c @@ -16,6 +16,7 @@ #include #include #include +#include #define RIC_FLUSH_TLB 0 #define RIC_FLUSH_PWC 1 @@ -694,7 +695,14 @@ void radix__flush_tlb_mm(struct mm_struct *mm) goto local; } - if (cputlb_use_tlbie()) { + if (!mmu_has_feature(MMU_FTR_GTSE)) { + unsigned long tgt = H_RPTI_TARGET_CMMU; + + if (atomic_read(&mm->context.copros) > 0) + tgt |= H_RPTI_TARGET_NMMU; + pseries_rpt_invalidate(pid, tgt, H_RPTI_TYPE_TLB, + H_RPTI_PAGE_ALL, 0, -1UL); + } else if (cputlb_use_tlbie()) { if (mm_needs_flush_escalation(mm)) _tlbie_pid(pid, RIC_FLUSH_ALL); else @@ -727,7 +735,16 @@ static void __flush_all_mm(struct mm_struct *mm, bool fullmm) goto local; } } - if (cputlb_use_tlbie()) + if (!mmu_has_feature(MMU_FTR_GTSE)) { + unsigned long tgt = H_RPTI_TARGET_CMMU; + unsigned long type = H_RPTI_TYPE_TLB | H_RPTI_TYPE_PWC | + H_RPTI_TYPE_PRT; + + if (atomic_read(&mm->context.copros) > 0) + tgt |= H_RPTI_TARGET_NMMU; + pseries_rpt_invalidate(pid, tgt, type, + H_RPTI_PAGE_ALL, 0, -1UL); + } else if (cputlb_use_tlbie()) _tlbie_pid(pid, RIC_FLUSH_ALL); else _tlbiel_pid_multicast(mm, pid, RIC_FLUSH_ALL); @@ -760,7 +777,19 @@ void radix__flush_tlb_page_psize(struct mm_struct *mm, unsigned long vmaddr, exit_flush_lazy_tlbs(mm); goto local; } - if (cputlb_use_tlbie()) + if (!mmu_has_feature(MMU_FTR_GTSE)) { + unsigned long tgt, pg_sizes, size; + + tgt = H_RPTI_TARGET_CMMU; + pg_sizes = psize_to_rpti_pgsize(psize); + size = 1UL << mmu_psize_to_shift(psize); + + if (atomic_read(&mm->context.copros) > 0) + tgt |= H_RPTI_TARGET_NMMU; + pseries_rpt_invalidate(pid, tgt, H_RPTI_TYPE_TLB, + pg_sizes, vmaddr, + vmaddr + size); + } else if (cputlb_use_tlbie()) _tlbie_va(vmaddr, pid, psize, RIC_FLUSH_TLB); else _tlbiel_va_multicast(mm, vmaddr, pid, psize, RIC_FLUSH_TLB); @@ -810,7 +839,14 @@ static inline void _tlbiel_kernel_broadcast(void) */ void radix__flush_tlb_kernel_range(unsigned long start, unsigned long end) { - if (cputlb_use_tlbie()) + if (!mmu_has_feature(MMU_FTR_GTSE)) { + unsigned long tgt = H_RPTI_TARGET_CMMU | H_RPTI_TARGET_NMMU; + unsigned long type = H_RPTI_TYPE_TLB | H_RPTI_TYPE_PWC | + H_RPTI_TYPE_PRT; + + pseries_rpt_invalidate(0, tgt, type, H_RPTI_PAGE_ALL, + start, end); + } else if (cputlb_use_tlbie()) _tlbie_pid(0, RIC_FLUSH_ALL); else _tlbiel_kernel_broadcast(); @@ -864,7 +900,17 @@ is_local: nr_pages > tlb_local_single_page_flush_ceiling); } - if (full) { + if (!mmu_has_feature(MMU_FTR_GTSE) && !local) { + unsigned long tgt = H_RPTI_TARGET_CMMU; + unsigned long pg_sizes = psize_to_rpti_pgsize(mmu_virtual_psize); + + if (IS_ENABLED(CONFIG_TRANSPARENT_HUGEPAGE)) + pg_sizes |= psize_to_rpti_pgsize(MMU_PAGE_2M); + if (atomic_read(&mm->context.copros) > 0) + tgt |= H_RPTI_TARGET_NMMU; + pseries_rpt_invalidate(pid, tgt, H_RPTI_TYPE_TLB, pg_sizes, + start, end); + } else if (full) { if (local) { _tlbiel_pid(pid, RIC_FLUSH_TLB); } else { @@ -1046,7 +1092,17 @@ is_local: nr_pages > tlb_local_single_page_flush_ceiling); } - if (full) { + if (!mmu_has_feature(MMU_FTR_GTSE) && !local) { + unsigned long tgt = H_RPTI_TARGET_CMMU; + unsigned long type = H_RPTI_TYPE_TLB; + unsigned long pg_sizes = psize_to_rpti_pgsize(psize); + + if (also_pwc) + type |= H_RPTI_TYPE_PWC; + if (atomic_read(&mm->context.copros) > 0) + tgt |= H_RPTI_TARGET_NMMU; + pseries_rpt_invalidate(pid, tgt, type, pg_sizes, start, end); + } else if (full) { if (local) { _tlbiel_pid(pid, also_pwc ? RIC_FLUSH_ALL : RIC_FLUSH_TLB); } else { @@ -1111,7 +1167,19 @@ void radix__flush_tlb_collapsed_pmd(struct mm_struct *mm, unsigned long addr) exit_flush_lazy_tlbs(mm); goto local; } - if (cputlb_use_tlbie()) + if (!mmu_has_feature(MMU_FTR_GTSE)) { + unsigned long tgt, type, pg_sizes; + + tgt = H_RPTI_TARGET_CMMU; + type = H_RPTI_TYPE_TLB | H_RPTI_TYPE_PWC | + H_RPTI_TYPE_PRT; + pg_sizes = psize_to_rpti_pgsize(mmu_virtual_psize); + + if (atomic_read(&mm->context.copros) > 0) + tgt |= H_RPTI_TARGET_NMMU; + pseries_rpt_invalidate(pid, tgt, type, pg_sizes, + addr, end); + } else if (cputlb_use_tlbie()) _tlbie_va_range(addr, end, pid, PAGE_SIZE, mmu_virtual_psize, true); else _tlbiel_va_range_multicast(mm, -- GitLab From c83040192f3763b243ece26073d61a895b4a230f Mon Sep 17 00:00:00 2001 From: "Aneesh Kumar K.V" Date: Wed, 1 Jul 2020 12:52:29 +0530 Subject: [PATCH 0446/1476] powerpc/pmem: Restrict papr_scm to P8 and above. The PAPR based virtualized persistent memory devices are only supported on POWER9 and above. In the followup patch, the kernel will switch the persistent memory cache flush functions to use a new `dcbf` variant instruction. The new instructions even though added in ISA 3.1 works even on P8 and P9 because these are implemented as a variant of existing `dcbf` and `hwsync` and on P8 and P9 behaves as such. Considering these devices are only supported on P8 and above, update the driver to prevent a P7-compat guest from using persistent memory devices. We don't update of_pmem driver with the same condition, because, on bare-metal, the firmware enables pmem support only on P9 and above. There the kernel depends on OPAL firmware to restrict exposing persistent memory related device tree entries on older hardware. of_pmem.ko is written without any arch dependency and we don't want to add ppc64 specific cpu feature check in of_pmem driver. Signed-off-by: Aneesh Kumar K.V Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20200701072235.223558-2-aneesh.kumar@linux.ibm.com --- arch/powerpc/platforms/pseries/pmem.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/arch/powerpc/platforms/pseries/pmem.c b/arch/powerpc/platforms/pseries/pmem.c index f860a897a9e05..2347e1038f58b 100644 --- a/arch/powerpc/platforms/pseries/pmem.c +++ b/arch/powerpc/platforms/pseries/pmem.c @@ -147,6 +147,12 @@ const struct of_device_id drc_pmem_match[] = { static int pseries_pmem_init(void) { + /* + * Only supported on POWER8 and above. + */ + if (!cpu_has_feature(CPU_FTR_ARCH_207S)) + return 0; + pmem_node = of_find_node_by_type(NULL, "ibm,persistent-memory"); if (!pmem_node) return 0; -- GitLab From 32db09d992ddc7d145595cff49cccfe14e018266 Mon Sep 17 00:00:00 2001 From: "Aneesh Kumar K.V" Date: Wed, 1 Jul 2020 12:52:30 +0530 Subject: [PATCH 0447/1476] powerpc/pmem: Add new instructions for persistent storage and sync POWER10 introduces two new variants of dcbf instructions (dcbstps and dcbfps) that can be used to write modified locations back to persistent storage. Additionally, POWER10 also introduce phwsync and plwsync which can be used to establish order of these writes to persistent storage. This patch exposes these instructions to the rest of the kernel. The existing dcbf and hwsync instructions in P8 and P9 are adequate to enable appropriate synchronization with OpenCAPI-hosted persistent storage. Hence the new instructions are added as a variant of the old ones that old hardware won't differentiate. Signed-off-by: Aneesh Kumar K.V Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20200701072235.223558-3-aneesh.kumar@linux.ibm.com --- arch/powerpc/include/asm/ppc-opcode.h | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/arch/powerpc/include/asm/ppc-opcode.h b/arch/powerpc/include/asm/ppc-opcode.h index bce879fb9afd8..777d5056a71c4 100644 --- a/arch/powerpc/include/asm/ppc-opcode.h +++ b/arch/powerpc/include/asm/ppc-opcode.h @@ -219,6 +219,8 @@ #define PPC_INST_STWCX 0x7c00012d #define PPC_INST_LWSYNC 0x7c2004ac #define PPC_INST_SYNC 0x7c0004ac +#define PPC_INST_PHWSYNC 0x7c8004ac +#define PPC_INST_PLWSYNC 0x7ca004ac #define PPC_INST_SYNC_MASK 0xfc0007fe #define PPC_INST_ISYNC 0x4c00012c #define PPC_INST_LXVD2X 0x7c000698 @@ -284,6 +286,8 @@ #define PPC_INST_TABORT 0x7c00071d #define PPC_INST_TSR 0x7c0005dd +#define PPC_INST_DCBF 0x7c0000ac + #define PPC_INST_NAP 0x4c000364 #define PPC_INST_SLEEP 0x4c0003a4 #define PPC_INST_WINKLE 0x4c0003e4 @@ -533,6 +537,14 @@ #define STBCIX(s,a,b) stringify_in_c(.long PPC_INST_STBCIX | \ __PPC_RS(s) | __PPC_RA(a) | __PPC_RB(b)) +#define PPC_DCBFPS(a, b) stringify_in_c(.long PPC_INST_DCBF | \ + ___PPC_RA(a) | ___PPC_RB(b) | (4 << 21)) +#define PPC_DCBSTPS(a, b) stringify_in_c(.long PPC_INST_DCBF | \ + ___PPC_RA(a) | ___PPC_RB(b) | (6 << 21)) + +#define PPC_PHWSYNC stringify_in_c(.long PPC_INST_PHWSYNC) +#define PPC_PLWSYNC stringify_in_c(.long PPC_INST_PLWSYNC) + /* * Define what the VSX XX1 form instructions will look like, then add * the 128 bit load store instructions based on that. -- GitLab From d358042793183a57094dac45a44116e1165ac593 Mon Sep 17 00:00:00 2001 From: "Aneesh Kumar K.V" Date: Wed, 1 Jul 2020 12:52:31 +0530 Subject: [PATCH 0448/1476] powerpc/pmem: Add flush routines using new pmem store and sync instruction Start using dcbstps; phwsync; sequence for flushing persistent memory range. The new instructions are implemented as a variant of dcbf and hwsync and on P8 and P9 they will be executed as those instructions. We avoid using them on older hardware. This helps to avoid difficult to debug bugs. Signed-off-by: Aneesh Kumar K.V Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20200701072235.223558-4-aneesh.kumar@linux.ibm.com --- arch/powerpc/include/asm/cacheflush.h | 1 + arch/powerpc/lib/pmem.c | 50 ++++++++++++++++++++++++--- 2 files changed, 47 insertions(+), 4 deletions(-) diff --git a/arch/powerpc/include/asm/cacheflush.h b/arch/powerpc/include/asm/cacheflush.h index de600b915a3c5..54764c6e922de 100644 --- a/arch/powerpc/include/asm/cacheflush.h +++ b/arch/powerpc/include/asm/cacheflush.h @@ -6,6 +6,7 @@ #include #include +#include #ifdef CONFIG_PPC_BOOK3S_64 /* diff --git a/arch/powerpc/lib/pmem.c b/arch/powerpc/lib/pmem.c index 0666a8d295962..5a61aaeb69302 100644 --- a/arch/powerpc/lib/pmem.c +++ b/arch/powerpc/lib/pmem.c @@ -9,20 +9,62 @@ #include +static inline void __clean_pmem_range(unsigned long start, unsigned long stop) +{ + unsigned long shift = l1_dcache_shift(); + unsigned long bytes = l1_dcache_bytes(); + void *addr = (void *)(start & ~(bytes - 1)); + unsigned long size = stop - (unsigned long)addr + (bytes - 1); + unsigned long i; + + for (i = 0; i < size >> shift; i++, addr += bytes) + asm volatile(PPC_DCBSTPS(%0, %1): :"i"(0), "r"(addr): "memory"); + + + asm volatile(PPC_PHWSYNC ::: "memory"); +} + +static inline void __flush_pmem_range(unsigned long start, unsigned long stop) +{ + unsigned long shift = l1_dcache_shift(); + unsigned long bytes = l1_dcache_bytes(); + void *addr = (void *)(start & ~(bytes - 1)); + unsigned long size = stop - (unsigned long)addr + (bytes - 1); + unsigned long i; + + for (i = 0; i < size >> shift; i++, addr += bytes) + asm volatile(PPC_DCBFPS(%0, %1): :"i"(0), "r"(addr): "memory"); + + + asm volatile(PPC_PHWSYNC ::: "memory"); +} + +static inline void clean_pmem_range(unsigned long start, unsigned long stop) +{ + if (cpu_has_feature(CPU_FTR_ARCH_207S)) + return __clean_pmem_range(start, stop); +} + +static inline void flush_pmem_range(unsigned long start, unsigned long stop) +{ + if (cpu_has_feature(CPU_FTR_ARCH_207S)) + return __flush_pmem_range(start, stop); +} + /* * CONFIG_ARCH_HAS_PMEM_API symbols */ void arch_wb_cache_pmem(void *addr, size_t size) { unsigned long start = (unsigned long) addr; - flush_dcache_range(start, start + size); + clean_pmem_range(start, start + size); } EXPORT_SYMBOL_GPL(arch_wb_cache_pmem); void arch_invalidate_pmem(void *addr, size_t size) { unsigned long start = (unsigned long) addr; - flush_dcache_range(start, start + size); + flush_pmem_range(start, start + size); } EXPORT_SYMBOL_GPL(arch_invalidate_pmem); @@ -35,7 +77,7 @@ long __copy_from_user_flushcache(void *dest, const void __user *src, unsigned long copied, start = (unsigned long) dest; copied = __copy_from_user(dest, src, size); - flush_dcache_range(start, start + size); + clean_pmem_range(start, start + size); return copied; } @@ -45,7 +87,7 @@ void *memcpy_flushcache(void *dest, const void *src, size_t size) unsigned long start = (unsigned long) dest; memcpy(dest, src, size); - flush_dcache_range(start, start + size); + clean_pmem_range(start, start + size); return dest; } -- GitLab From 3e79f082ebfc130360bcee23e4dd74729dcafdf4 Mon Sep 17 00:00:00 2001 From: "Aneesh Kumar K.V" Date: Wed, 1 Jul 2020 12:52:32 +0530 Subject: [PATCH 0449/1476] libnvdimm/nvdimm/flush: Allow architecture to override the flush barrier Architectures like ppc64 provide persistent memory specific barriers that will ensure that all stores for which the modifications are written to persistent storage by preceding dcbfps and dcbstps instructions have updated persistent storage before any data access or data transfer caused by subsequent instructions is initiated. This is in addition to the ordering done by wmb() Update nvdimm core such that architecture can use barriers other than wmb to ensure all previous writes are architecturally visible for the platform buffer flush. Signed-off-by: Aneesh Kumar K.V Reviewed-by: Dan Williams Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20200701072235.223558-5-aneesh.kumar@linux.ibm.com --- Documentation/memory-barriers.txt | 14 ++++++++++++++ drivers/md/dm-writecache.c | 2 +- drivers/nvdimm/region_devs.c | 8 ++++---- include/asm-generic/barrier.h | 10 ++++++++++ 4 files changed, 29 insertions(+), 5 deletions(-) diff --git a/Documentation/memory-barriers.txt b/Documentation/memory-barriers.txt index eaabc31342949..ff07cd3b2f82b 100644 --- a/Documentation/memory-barriers.txt +++ b/Documentation/memory-barriers.txt @@ -1935,6 +1935,20 @@ There are some more advanced barrier functions: relaxed I/O accessors and the Documentation/DMA-API.txt file for more information on consistent memory. + (*) pmem_wmb(); + + This is for use with persistent memory to ensure that stores for which + modifications are written to persistent storage reached a platform + durability domain. + + For example, after a non-temporal write to pmem region, we use pmem_wmb() + to ensure that stores have reached a platform durability domain. This ensures + that stores have updated persistent storage before any data access or + data transfer caused by subsequent instructions is initiated. This is + in addition to the ordering done by wmb(). + + For load from persistent memory, existing read memory barriers are sufficient + to ensure read ordering. =============================== IMPLICIT KERNEL MEMORY BARRIERS diff --git a/drivers/md/dm-writecache.c b/drivers/md/dm-writecache.c index 74f3c506f0848..00534fa4a3844 100644 --- a/drivers/md/dm-writecache.c +++ b/drivers/md/dm-writecache.c @@ -536,7 +536,7 @@ static void ssd_commit_superblock(struct dm_writecache *wc) static void writecache_commit_flushed(struct dm_writecache *wc, bool wait_for_ios) { if (WC_MODE_PMEM(wc)) - wmb(); + pmem_wmb(); else ssd_commit_flushed(wc, wait_for_ios); } diff --git a/drivers/nvdimm/region_devs.c b/drivers/nvdimm/region_devs.c index 4502f9c4708d0..c3237c2b03a6b 100644 --- a/drivers/nvdimm/region_devs.c +++ b/drivers/nvdimm/region_devs.c @@ -1206,13 +1206,13 @@ int generic_nvdimm_flush(struct nd_region *nd_region) idx = this_cpu_add_return(flush_idx, hash_32(current->pid + idx, 8)); /* - * The first wmb() is needed to 'sfence' all previous writes - * such that they are architecturally visible for the platform - * buffer flush. Note that we've already arranged for pmem + * The pmem_wmb() is needed to 'sfence' all + * previous writes such that they are architecturally visible for + * the platform buffer flush. Note that we've already arranged for pmem * writes to avoid the cache via memcpy_flushcache(). The final * wmb() ensures ordering for the NVDIMM flush write. */ - wmb(); + pmem_wmb(); for (i = 0; i < nd_region->ndr_mappings; i++) if (ndrd_get_flush_wpq(ndrd, i, 0)) writeq(1, ndrd_get_flush_wpq(ndrd, i, idx)); diff --git a/include/asm-generic/barrier.h b/include/asm-generic/barrier.h index 2eacaf7d62f61..b589bb216ee57 100644 --- a/include/asm-generic/barrier.h +++ b/include/asm-generic/barrier.h @@ -257,5 +257,15 @@ do { \ }) #endif +/* + * pmem_wmb() ensures that all stores for which the modification + * are written to persistent storage by preceding instructions have + * updated persistent storage before any data access or data transfer + * caused by subsequent instructions is initiated. + */ +#ifndef pmem_wmb +#define pmem_wmb() wmb() +#endif + #endif /* !__ASSEMBLY__ */ #endif /* __ASM_GENERIC_BARRIER_H */ -- GitLab From 76e6c73f33d4e1cc4de4f25c0bf66d59e42113c4 Mon Sep 17 00:00:00 2001 From: "Aneesh Kumar K.V" Date: Wed, 1 Jul 2020 12:52:33 +0530 Subject: [PATCH 0450/1476] powerpc/pmem: Update ppc64 to use the new barrier instruction. pmem on POWER10 can now use phwsync instead of hwsync to ensure all previous writes are architecturally visible for the platform buffer flush. Signed-off-by: Aneesh Kumar K.V Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20200701072235.223558-6-aneesh.kumar@linux.ibm.com --- arch/powerpc/include/asm/barrier.h | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/arch/powerpc/include/asm/barrier.h b/arch/powerpc/include/asm/barrier.h index 123adcefd40f6..35c1b8f3aa683 100644 --- a/arch/powerpc/include/asm/barrier.h +++ b/arch/powerpc/include/asm/barrier.h @@ -7,6 +7,10 @@ #include +#ifndef __ASSEMBLY__ +#include +#endif + /* * Memory barrier. * The sync instruction guarantees that all memory accesses initiated @@ -97,6 +101,15 @@ do { \ #define barrier_nospec() #endif /* CONFIG_PPC_BARRIER_NOSPEC */ +/* + * pmem_wmb() ensures that all stores for which the modification + * are written to persistent storage by preceding dcbfps/dcbstps + * instructions have updated persistent storage before any data + * access or data transfer caused by subsequent instructions is + * initiated. + */ +#define pmem_wmb() __asm__ __volatile__(PPC_PHWSYNC ::: "memory") + #include #endif /* _ASM_POWERPC_BARRIER_H */ -- GitLab From 436499ab868f1a9e497cfdbf641affe8a122c571 Mon Sep 17 00:00:00 2001 From: "Aneesh Kumar K.V" Date: Wed, 1 Jul 2020 12:52:34 +0530 Subject: [PATCH 0451/1476] powerpc/pmem: Avoid the barrier in flush routines nvdimm expect the flush routines to just mark the cache clean. The barrier that mark the store globally visible is done in nvdimm_flush(). Signed-off-by: Aneesh Kumar K.V Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20200701072235.223558-7-aneesh.kumar@linux.ibm.com --- arch/powerpc/lib/pmem.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/arch/powerpc/lib/pmem.c b/arch/powerpc/lib/pmem.c index 5a61aaeb69302..21210fa676e50 100644 --- a/arch/powerpc/lib/pmem.c +++ b/arch/powerpc/lib/pmem.c @@ -19,9 +19,6 @@ static inline void __clean_pmem_range(unsigned long start, unsigned long stop) for (i = 0; i < size >> shift; i++, addr += bytes) asm volatile(PPC_DCBSTPS(%0, %1): :"i"(0), "r"(addr): "memory"); - - - asm volatile(PPC_PHWSYNC ::: "memory"); } static inline void __flush_pmem_range(unsigned long start, unsigned long stop) @@ -34,9 +31,6 @@ static inline void __flush_pmem_range(unsigned long start, unsigned long stop) for (i = 0; i < size >> shift; i++, addr += bytes) asm volatile(PPC_DCBFPS(%0, %1): :"i"(0), "r"(addr): "memory"); - - - asm volatile(PPC_PHWSYNC ::: "memory"); } static inline void clean_pmem_range(unsigned long start, unsigned long stop) -- GitLab From 8c26ab72663b4affc31e47cdf77d61d0172d1033 Mon Sep 17 00:00:00 2001 From: "Aneesh Kumar K.V" Date: Wed, 1 Jul 2020 12:52:35 +0530 Subject: [PATCH 0452/1476] powerpc/pmem: Initialize pmem device on newer hardware With kernel now supporting new pmem flush/sync instructions, we can now enable the kernel to initialize the device. On P10 these devices would appear with a new compatible string. For PAPR device we have compatible "ibm,pmemory-v2" and for OF pmem device we have compatible "pmem-region-v2" Signed-off-by: Aneesh Kumar K.V Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20200701072235.223558-8-aneesh.kumar@linux.ibm.com --- arch/powerpc/platforms/pseries/papr_scm.c | 1 + drivers/nvdimm/of_pmem.c | 1 + 2 files changed, 2 insertions(+) diff --git a/arch/powerpc/platforms/pseries/papr_scm.c b/arch/powerpc/platforms/pseries/papr_scm.c index 9c569078a09fd..66c19c0fe5667 100644 --- a/arch/powerpc/platforms/pseries/papr_scm.c +++ b/arch/powerpc/platforms/pseries/papr_scm.c @@ -876,6 +876,7 @@ static int papr_scm_remove(struct platform_device *pdev) static const struct of_device_id papr_scm_match[] = { { .compatible = "ibm,pmemory" }, + { .compatible = "ibm,pmemory-v2" }, { }, }; diff --git a/drivers/nvdimm/of_pmem.c b/drivers/nvdimm/of_pmem.c index 6826a274a1f10..10dbdcdfb9ce9 100644 --- a/drivers/nvdimm/of_pmem.c +++ b/drivers/nvdimm/of_pmem.c @@ -90,6 +90,7 @@ static int of_pmem_region_remove(struct platform_device *pdev) static const struct of_device_id of_pmem_region_match[] = { { .compatible = "pmem-region" }, + { .compatible = "pmem-region-v2" }, { }, }; -- GitLab From 891b4fe8fe3d09f20948b391f24c9fc5b7580a2b Mon Sep 17 00:00:00 2001 From: Nicholas Piggin Date: Wed, 24 Jun 2020 09:41:37 +1000 Subject: [PATCH 0453/1476] powerpc/64s: restore_math remove TM test The TM test in restore_math added by commit dc16b553c949e ("powerpc: Always restore FPU/VEC/VSX if hardware transactional memory in use") is no longer necessary after commit a8318c13e79ba ("powerpc/tm: Fix restoring FP/VMX facility incorrectly on interrupts"), which removed the cases where restore_math has to restore if TM is active. Signed-off-by: Nicholas Piggin Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20200623234139.2262227-1-npiggin@gmail.com --- arch/powerpc/kernel/process.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/arch/powerpc/kernel/process.c b/arch/powerpc/kernel/process.c index 4650b9bb217fb..02d691bda9b60 100644 --- a/arch/powerpc/kernel/process.c +++ b/arch/powerpc/kernel/process.c @@ -530,8 +530,7 @@ void notrace restore_math(struct pt_regs *regs) { unsigned long msr; - if (!MSR_TM_ACTIVE(regs->msr) && - !current->thread.load_fp && !loadvec(current->thread)) + if (!current->thread.load_fp && !loadvec(current->thread)) return; msr = regs->msr; -- GitLab From 01eb01877f3386d4bd5de75909abdd0af45a5fa2 Mon Sep 17 00:00:00 2001 From: Nicholas Piggin Date: Wed, 24 Jun 2020 09:41:38 +1000 Subject: [PATCH 0454/1476] powerpc/64s: Fix restore_math unnecessarily changing MSR Before returning to user, if there are missing FP/VEC/VSX bits from the user MSR then those registers had been saved and must be restored again before use. restore_math will decide whether to restore immediately, or skip the restore and let fp/vec/vsx unavailable faults demand load the registers. Each time restore_math restores one of the FP/VSX or VEC register sets is loaded, an 8-bit counter is incremented (load_fp and load_vec). When these wrap to zero, restore_math no longer restores that register set until after they are next demand faulted. It's quite usual for those counters to have different values, so if one wraps to zero and restore_math no longer restores its registers or user MSR bit but the other is not zero yet does not need to be restored (because the kernel is not frequently using the FPU), then restore_math will be called and it will also not return in the early exit check. This causes msr_check_and_set to test and set the MSR at every kernel exit despite having no work to do. This can cause workloads (e.g., a NULL syscall microbenchmark) to run fast for a time while both counters are non-zero, then slow down when one of the counters reaches zero, then speed up again after the second counter reaches zero. The cost is significant, about 10% slowdown on a NULL syscall benchmark, and the jittery behaviour is very undesirable. Fix this by having restore_math test all conditions first, and only update MSR if we will be loading registers. Signed-off-by: Nicholas Piggin Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20200623234139.2262227-2-npiggin@gmail.com --- arch/powerpc/kernel/process.c | 100 ++++++++++++++++++------------- arch/powerpc/kernel/syscall_64.c | 8 +++ 2 files changed, 68 insertions(+), 40 deletions(-) diff --git a/arch/powerpc/kernel/process.c b/arch/powerpc/kernel/process.c index 02d691bda9b60..9be4759f3bbb8 100644 --- a/arch/powerpc/kernel/process.c +++ b/arch/powerpc/kernel/process.c @@ -471,49 +471,58 @@ EXPORT_SYMBOL(giveup_all); #ifdef CONFIG_PPC_BOOK3S_64 #ifdef CONFIG_PPC_FPU -static int restore_fp(struct task_struct *tsk) +static bool should_restore_fp(void) { - if (tsk->thread.load_fp) { - load_fp_state(¤t->thread.fp_state); + if (current->thread.load_fp) { current->thread.load_fp++; - return 1; + return true; } - return 0; + return false; +} + +static void do_restore_fp(void) +{ + load_fp_state(¤t->thread.fp_state); } #else -static int restore_fp(struct task_struct *tsk) { return 0; } +static bool should_restore_fp(void) { return false; } +static void do_restore_fp(void) { } #endif /* CONFIG_PPC_FPU */ #ifdef CONFIG_ALTIVEC -#define loadvec(thr) ((thr).load_vec) -static int restore_altivec(struct task_struct *tsk) +static bool should_restore_altivec(void) { - if (cpu_has_feature(CPU_FTR_ALTIVEC) && (tsk->thread.load_vec)) { - load_vr_state(&tsk->thread.vr_state); - tsk->thread.used_vr = 1; - tsk->thread.load_vec++; - - return 1; + if (cpu_has_feature(CPU_FTR_ALTIVEC) && (current->thread.load_vec)) { + current->thread.load_vec++; + return true; } - return 0; + return false; +} + +static void do_restore_altivec(void) +{ + load_vr_state(¤t->thread.vr_state); + current->thread.used_vr = 1; } #else -#define loadvec(thr) 0 -static inline int restore_altivec(struct task_struct *tsk) { return 0; } +static bool should_restore_altivec(void) { return false; } +static void do_restore_altivec(void) { } #endif /* CONFIG_ALTIVEC */ #ifdef CONFIG_VSX -static int restore_vsx(struct task_struct *tsk) +static bool should_restore_vsx(void) { - if (cpu_has_feature(CPU_FTR_VSX)) { - tsk->thread.used_vsr = 1; - return 1; - } - - return 0; + if (cpu_has_feature(CPU_FTR_VSX)) + return true; + return false; +} +static void do_restore_vsx(void) +{ + current->thread.used_vsr = 1; } #else -static inline int restore_vsx(struct task_struct *tsk) { return 0; } +static bool should_restore_vsx(void) { return false; } +static void do_restore_vsx(void) { } #endif /* CONFIG_VSX */ /* @@ -529,31 +538,42 @@ static inline int restore_vsx(struct task_struct *tsk) { return 0; } void notrace restore_math(struct pt_regs *regs) { unsigned long msr; - - if (!current->thread.load_fp && !loadvec(current->thread)) - return; + unsigned long new_msr = 0; msr = regs->msr; - msr_check_and_set(msr_all_available); /* - * Only reload if the bit is not set in the user MSR, the bit BEING set - * indicates that the registers are hot + * new_msr tracks the facilities that are to be restored. Only reload + * if the bit is not set in the user MSR (if it is set, the registers + * are live for the user thread). */ - if ((!(msr & MSR_FP)) && restore_fp(current)) - msr |= MSR_FP | current->thread.fpexc_mode; + if ((!(msr & MSR_FP)) && should_restore_fp()) + new_msr |= MSR_FP | current->thread.fpexc_mode; - if ((!(msr & MSR_VEC)) && restore_altivec(current)) - msr |= MSR_VEC; + if ((!(msr & MSR_VEC)) && should_restore_altivec()) + new_msr |= MSR_VEC; - if ((msr & (MSR_FP | MSR_VEC)) == (MSR_FP | MSR_VEC) && - restore_vsx(current)) { - msr |= MSR_VSX; + if ((!(msr & MSR_VSX)) && should_restore_vsx()) { + if (((msr | new_msr) & (MSR_FP | MSR_VEC)) == (MSR_FP | MSR_VEC)) + new_msr |= MSR_VSX; } - msr_check_and_clear(msr_all_available); + if (new_msr) { + msr_check_and_set(new_msr); + + if (new_msr & MSR_FP) + do_restore_fp(); + + if (new_msr & MSR_VEC) + do_restore_altivec(); - regs->msr = msr; + if (new_msr & MSR_VSX) + do_restore_vsx(); + + msr_check_and_clear(new_msr); + + regs->msr |= new_msr; + } } #endif diff --git a/arch/powerpc/kernel/syscall_64.c b/arch/powerpc/kernel/syscall_64.c index 79edba3ab3124..5126f1d3184a0 100644 --- a/arch/powerpc/kernel/syscall_64.c +++ b/arch/powerpc/kernel/syscall_64.c @@ -206,6 +206,13 @@ again: else if (cpu_has_feature(CPU_FTR_ALTIVEC)) mathflags |= MSR_VEC; + /* + * If userspace MSR has all available FP bits set, + * then they are live and no need to restore. If not, + * it means the regs were given up and restore_math + * may decide to restore them (to avoid taking an FP + * fault). + */ if ((regs->msr & mathflags) != mathflags) restore_math(regs); } @@ -277,6 +284,7 @@ again: else if (cpu_has_feature(CPU_FTR_ALTIVEC)) mathflags |= MSR_VEC; + /* See above restore_math comment */ if ((regs->msr & mathflags) != mathflags) restore_math(regs); } -- GitLab From b2b46304e9360f3dda49c9d8ba4a1478b9eecf1d Mon Sep 17 00:00:00 2001 From: Nicholas Piggin Date: Wed, 24 Jun 2020 09:41:39 +1000 Subject: [PATCH 0455/1476] powerpc: re-initialise lazy FPU/VEC counters on every fault When a FP/VEC/VSX unavailable fault loads registers and enables the facility in the MSR, re-set the lazy restore counters to 1 rather than incrementing them so every fault gets the same number of restores before the next fault. This probably shouldn't be a practical change because if a lazy counter was non-zero then it should have been restored and would not cause a fault when userspace tries to access it. However the code and comment implies otherwise so that's misleading and unnecessary. Signed-off-by: Nicholas Piggin Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20200623234139.2262227-3-npiggin@gmail.com --- arch/powerpc/kernel/fpu.S | 4 +--- arch/powerpc/kernel/vector.S | 4 +--- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/arch/powerpc/kernel/fpu.S b/arch/powerpc/kernel/fpu.S index cac22cb97a8c4..4ae39db70044e 100644 --- a/arch/powerpc/kernel/fpu.S +++ b/arch/powerpc/kernel/fpu.S @@ -107,9 +107,7 @@ END_FTR_SECTION_IFSET(CPU_FTR_VSX) or r12,r12,r4 std r12,_MSR(r1) #endif - /* Don't care if r4 overflows, this is desired behaviour */ - lbz r4,THREAD_LOAD_FP(r5) - addi r4,r4,1 + li r4,1 stb r4,THREAD_LOAD_FP(r5) addi r10,r5,THREAD_FPSTATE lfd fr0,FPSTATE_FPSCR(r10) diff --git a/arch/powerpc/kernel/vector.S b/arch/powerpc/kernel/vector.S index efc5b52f95d29..801dc28fdcca5 100644 --- a/arch/powerpc/kernel/vector.S +++ b/arch/powerpc/kernel/vector.S @@ -76,9 +76,7 @@ _GLOBAL(load_up_altivec) oris r12,r12,MSR_VEC@h std r12,_MSR(r1) #endif - /* Don't care if r4 overflows, this is desired behaviour */ - lbz r4,THREAD_LOAD_VEC(r5) - addi r4,r4,1 + li r4,1 stb r4,THREAD_LOAD_VEC(r5) addi r6,r5,THREAD_VRSTATE li r4,1 -- GitLab From 1026798c644bfd3115fc4e32fd5e767cfc30ccf1 Mon Sep 17 00:00:00 2001 From: Nicholas Piggin Date: Tue, 9 Jun 2020 17:06:04 +1000 Subject: [PATCH 0456/1476] powerpc/security: re-name count cache flush to branch cache flush The count cache flush mostly refers to both count cache and link stack flushing. As a first step to untangling these a bit, re-name the bits that apply to both. Signed-off-by: Nicholas Piggin Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20200609070610.846703-2-npiggin@gmail.com --- arch/powerpc/include/asm/asm-prototypes.h | 4 +-- arch/powerpc/kernel/entry_64.S | 7 ++--- arch/powerpc/kernel/security.c | 36 +++++++++++------------ 3 files changed, 23 insertions(+), 24 deletions(-) diff --git a/arch/powerpc/include/asm/asm-prototypes.h b/arch/powerpc/include/asm/asm-prototypes.h index 7d81e86a1e5dd..fa9057360e88c 100644 --- a/arch/powerpc/include/asm/asm-prototypes.h +++ b/arch/powerpc/include/asm/asm-prototypes.h @@ -144,13 +144,13 @@ void _kvmppc_restore_tm_pr(struct kvm_vcpu *vcpu, u64 guest_msr); void _kvmppc_save_tm_pr(struct kvm_vcpu *vcpu, u64 guest_msr); /* Patch sites */ -extern s32 patch__call_flush_count_cache; +extern s32 patch__call_flush_branch_caches; extern s32 patch__flush_count_cache_return; extern s32 patch__flush_link_stack_return; extern s32 patch__call_kvm_flush_link_stack; extern s32 patch__memset_nocache, patch__memcpy_nocache; -extern long flush_count_cache; +extern long flush_branch_caches; extern long kvm_flush_link_stack; #ifdef CONFIG_PPC_TRANSACTIONAL_MEM diff --git a/arch/powerpc/kernel/entry_64.S b/arch/powerpc/kernel/entry_64.S index 1013adc9acfbf..bdef9c11bad22 100644 --- a/arch/powerpc/kernel/entry_64.S +++ b/arch/powerpc/kernel/entry_64.S @@ -259,8 +259,7 @@ _ASM_NOKPROBE_SYMBOL(save_nvgprs); #define FLUSH_COUNT_CACHE \ 1: nop; \ - patch_site 1b, patch__call_flush_count_cache - + patch_site 1b, patch__call_flush_branch_caches #define BCCTR_FLUSH .long 0x4c400420 @@ -271,8 +270,8 @@ _ASM_NOKPROBE_SYMBOL(save_nvgprs); .endm .balign 32 -.global flush_count_cache -flush_count_cache: +.global flush_branch_caches +flush_branch_caches: /* Save LR into r9 */ mflr r9 diff --git a/arch/powerpc/kernel/security.c b/arch/powerpc/kernel/security.c index d86701ce116ba..df2a3eff950b7 100644 --- a/arch/powerpc/kernel/security.c +++ b/arch/powerpc/kernel/security.c @@ -21,12 +21,12 @@ u64 powerpc_security_features __read_mostly = SEC_FTR_DEFAULT; -enum count_cache_flush_type { - COUNT_CACHE_FLUSH_NONE = 0x1, - COUNT_CACHE_FLUSH_SW = 0x2, - COUNT_CACHE_FLUSH_HW = 0x4, +enum branch_cache_flush_type { + BRANCH_CACHE_FLUSH_NONE = 0x1, + BRANCH_CACHE_FLUSH_SW = 0x2, + BRANCH_CACHE_FLUSH_HW = 0x4, }; -static enum count_cache_flush_type count_cache_flush_type = COUNT_CACHE_FLUSH_NONE; +static enum branch_cache_flush_type count_cache_flush_type = BRANCH_CACHE_FLUSH_NONE; static bool link_stack_flush_enabled; bool barrier_nospec_enabled; @@ -222,10 +222,10 @@ ssize_t cpu_show_spectre_v2(struct device *dev, struct device_attribute *attr, c if (link_stack_flush_enabled) seq_buf_printf(&s, ", Software link stack flush"); - } else if (count_cache_flush_type != COUNT_CACHE_FLUSH_NONE) { + } else if (count_cache_flush_type != BRANCH_CACHE_FLUSH_NONE) { seq_buf_printf(&s, "Mitigation: Software count cache flush"); - if (count_cache_flush_type == COUNT_CACHE_FLUSH_HW) + if (count_cache_flush_type == BRANCH_CACHE_FLUSH_HW) seq_buf_printf(&s, " (hardware accelerated)"); if (link_stack_flush_enabled) @@ -429,18 +429,18 @@ device_initcall(stf_barrier_debugfs_init); static void no_count_cache_flush(void) { - count_cache_flush_type = COUNT_CACHE_FLUSH_NONE; + count_cache_flush_type = BRANCH_CACHE_FLUSH_NONE; pr_info("count-cache-flush: software flush disabled.\n"); } -static void toggle_count_cache_flush(bool enable) +static void toggle_branch_cache_flush(bool enable) { if (!security_ftr_enabled(SEC_FTR_FLUSH_COUNT_CACHE) && !security_ftr_enabled(SEC_FTR_FLUSH_LINK_STACK)) enable = false; if (!enable) { - patch_instruction_site(&patch__call_flush_count_cache, + patch_instruction_site(&patch__call_flush_branch_caches, ppc_inst(PPC_INST_NOP)); #ifdef CONFIG_KVM_BOOK3S_HV_POSSIBLE patch_instruction_site(&patch__call_kvm_flush_link_stack, @@ -452,9 +452,9 @@ static void toggle_count_cache_flush(bool enable) return; } - // This enables the branch from _switch to flush_count_cache - patch_branch_site(&patch__call_flush_count_cache, - (u64)&flush_count_cache, BRANCH_SET_LINK); + // This enables the branch from _switch to flush_branch_caches + patch_branch_site(&patch__call_flush_branch_caches, + (u64)&flush_branch_caches, BRANCH_SET_LINK); #ifdef CONFIG_KVM_BOOK3S_HV_POSSIBLE // This enables the branch from guest_exit_cont to kvm_flush_link_stack @@ -474,13 +474,13 @@ static void toggle_count_cache_flush(bool enable) } if (!security_ftr_enabled(SEC_FTR_BCCTR_FLUSH_ASSIST)) { - count_cache_flush_type = COUNT_CACHE_FLUSH_SW; + count_cache_flush_type = BRANCH_CACHE_FLUSH_SW; pr_info("count-cache-flush: full software flush sequence enabled.\n"); return; } patch_instruction_site(&patch__flush_count_cache_return, ppc_inst(PPC_INST_BLR)); - count_cache_flush_type = COUNT_CACHE_FLUSH_HW; + count_cache_flush_type = BRANCH_CACHE_FLUSH_HW; pr_info("count-cache-flush: hardware assisted flush sequence enabled\n"); } @@ -505,7 +505,7 @@ void setup_count_cache_flush(void) security_ftr_enabled(SEC_FTR_FLUSH_COUNT_CACHE)) security_ftr_set(SEC_FTR_FLUSH_LINK_STACK); - toggle_count_cache_flush(enable); + toggle_branch_cache_flush(enable); } #ifdef CONFIG_DEBUG_FS @@ -520,14 +520,14 @@ static int count_cache_flush_set(void *data, u64 val) else return -EINVAL; - toggle_count_cache_flush(enable); + toggle_branch_cache_flush(enable); return 0; } static int count_cache_flush_get(void *data, u64 *val) { - if (count_cache_flush_type == COUNT_CACHE_FLUSH_NONE) + if (count_cache_flush_type == BRANCH_CACHE_FLUSH_NONE) *val = 0; else *val = 1; -- GitLab From c06ac2771070f465076e87bba262c64fb0b3aca3 Mon Sep 17 00:00:00 2001 From: Nicholas Piggin Date: Tue, 9 Jun 2020 17:06:05 +1000 Subject: [PATCH 0457/1476] powerpc/security: change link stack flush state to the flush type enum Prepare to allow for hardware link stack flushing by using the none/sw/hw type, same as the count cache state. Signed-off-by: Nicholas Piggin Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20200609070610.846703-3-npiggin@gmail.com --- arch/powerpc/kernel/security.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/arch/powerpc/kernel/security.c b/arch/powerpc/kernel/security.c index df2a3eff950b7..28f4cb062f691 100644 --- a/arch/powerpc/kernel/security.c +++ b/arch/powerpc/kernel/security.c @@ -27,7 +27,7 @@ enum branch_cache_flush_type { BRANCH_CACHE_FLUSH_HW = 0x4, }; static enum branch_cache_flush_type count_cache_flush_type = BRANCH_CACHE_FLUSH_NONE; -static bool link_stack_flush_enabled; +static enum branch_cache_flush_type link_stack_flush_type = BRANCH_CACHE_FLUSH_NONE; bool barrier_nospec_enabled; static bool no_nospec; @@ -219,7 +219,7 @@ ssize_t cpu_show_spectre_v2(struct device *dev, struct device_attribute *attr, c if (ccd) seq_buf_printf(&s, "Indirect branch cache disabled"); - if (link_stack_flush_enabled) + if (link_stack_flush_type == BRANCH_CACHE_FLUSH_SW) seq_buf_printf(&s, ", Software link stack flush"); } else if (count_cache_flush_type != BRANCH_CACHE_FLUSH_NONE) { @@ -228,7 +228,7 @@ ssize_t cpu_show_spectre_v2(struct device *dev, struct device_attribute *attr, c if (count_cache_flush_type == BRANCH_CACHE_FLUSH_HW) seq_buf_printf(&s, " (hardware accelerated)"); - if (link_stack_flush_enabled) + if (link_stack_flush_type == BRANCH_CACHE_FLUSH_SW) seq_buf_printf(&s, ", Software link stack flush"); } else if (btb_flush_enabled) { @@ -447,7 +447,7 @@ static void toggle_branch_cache_flush(bool enable) ppc_inst(PPC_INST_NOP)); #endif pr_info("link-stack-flush: software flush disabled.\n"); - link_stack_flush_enabled = false; + link_stack_flush_type = BRANCH_CACHE_FLUSH_NONE; no_count_cache_flush(); return; } @@ -463,7 +463,7 @@ static void toggle_branch_cache_flush(bool enable) #endif pr_info("link-stack-flush: software flush enabled.\n"); - link_stack_flush_enabled = true; + link_stack_flush_type = BRANCH_CACHE_FLUSH_SW; // If we just need to flush the link stack, patch an early return if (!security_ftr_enabled(SEC_FTR_FLUSH_COUNT_CACHE)) { -- GitLab From 1afe00c74ffe6d502bffa81c7d849cb4640d7ae5 Mon Sep 17 00:00:00 2001 From: Nicholas Piggin Date: Tue, 9 Jun 2020 17:06:06 +1000 Subject: [PATCH 0458/1476] powerpc/security: make display of branch cache flush more consistent Make the count-cache and link-stack messages look the same Signed-off-by: Nicholas Piggin Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20200609070610.846703-4-npiggin@gmail.com --- arch/powerpc/kernel/security.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/arch/powerpc/kernel/security.c b/arch/powerpc/kernel/security.c index 28f4cb062f691..659ef6a92bb94 100644 --- a/arch/powerpc/kernel/security.c +++ b/arch/powerpc/kernel/security.c @@ -430,7 +430,7 @@ device_initcall(stf_barrier_debugfs_init); static void no_count_cache_flush(void) { count_cache_flush_type = BRANCH_CACHE_FLUSH_NONE; - pr_info("count-cache-flush: software flush disabled.\n"); + pr_info("count-cache-flush: flush disabled.\n"); } static void toggle_branch_cache_flush(bool enable) @@ -446,7 +446,7 @@ static void toggle_branch_cache_flush(bool enable) patch_instruction_site(&patch__call_kvm_flush_link_stack, ppc_inst(PPC_INST_NOP)); #endif - pr_info("link-stack-flush: software flush disabled.\n"); + pr_info("link-stack-flush: flush disabled.\n"); link_stack_flush_type = BRANCH_CACHE_FLUSH_NONE; no_count_cache_flush(); return; @@ -475,13 +475,13 @@ static void toggle_branch_cache_flush(bool enable) if (!security_ftr_enabled(SEC_FTR_BCCTR_FLUSH_ASSIST)) { count_cache_flush_type = BRANCH_CACHE_FLUSH_SW; - pr_info("count-cache-flush: full software flush sequence enabled.\n"); + pr_info("count-cache-flush: software flush enabled.\n"); return; } patch_instruction_site(&patch__flush_count_cache_return, ppc_inst(PPC_INST_BLR)); count_cache_flush_type = BRANCH_CACHE_FLUSH_HW; - pr_info("count-cache-flush: hardware assisted flush sequence enabled\n"); + pr_info("count-cache-flush: hardware flush enabled.\n"); } void setup_count_cache_flush(void) -- GitLab From c0036549a9d9a060fa8bc24e31f85503ce08ad5e Mon Sep 17 00:00:00 2001 From: Nicholas Piggin Date: Tue, 9 Jun 2020 17:06:07 +1000 Subject: [PATCH 0459/1476] powerpc/security: split branch cache flush toggle from code patching Branch cache flushing code patching has inter-dependencies on both the link stack and the count cache flushing state. To make the code clearer and to separate the link stack and count cache handling, split the "toggle" (setting up variables and printing enable/disable) from the code patching. Signed-off-by: Nicholas Piggin [mpe: Always print something, even if the flush is disabled] Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20200609070610.846703-5-npiggin@gmail.com --- arch/powerpc/kernel/security.c | 94 ++++++++++++++++++---------------- 1 file changed, 51 insertions(+), 43 deletions(-) diff --git a/arch/powerpc/kernel/security.c b/arch/powerpc/kernel/security.c index 659ef6a92bb94..05eeb22b3b978 100644 --- a/arch/powerpc/kernel/security.c +++ b/arch/powerpc/kernel/security.c @@ -427,61 +427,69 @@ static __init int stf_barrier_debugfs_init(void) device_initcall(stf_barrier_debugfs_init); #endif /* CONFIG_DEBUG_FS */ -static void no_count_cache_flush(void) +static void update_branch_cache_flush(void) { - count_cache_flush_type = BRANCH_CACHE_FLUSH_NONE; - pr_info("count-cache-flush: flush disabled.\n"); -} - -static void toggle_branch_cache_flush(bool enable) -{ - if (!security_ftr_enabled(SEC_FTR_FLUSH_COUNT_CACHE) && - !security_ftr_enabled(SEC_FTR_FLUSH_LINK_STACK)) - enable = false; - - if (!enable) { - patch_instruction_site(&patch__call_flush_branch_caches, - ppc_inst(PPC_INST_NOP)); #ifdef CONFIG_KVM_BOOK3S_HV_POSSIBLE + // This controls the branch from guest_exit_cont to kvm_flush_link_stack + if (link_stack_flush_type == BRANCH_CACHE_FLUSH_NONE) { patch_instruction_site(&patch__call_kvm_flush_link_stack, ppc_inst(PPC_INST_NOP)); -#endif - pr_info("link-stack-flush: flush disabled.\n"); - link_stack_flush_type = BRANCH_CACHE_FLUSH_NONE; - no_count_cache_flush(); - return; + } else { + patch_branch_site(&patch__call_kvm_flush_link_stack, + (u64)&kvm_flush_link_stack, BRANCH_SET_LINK); } - - // This enables the branch from _switch to flush_branch_caches - patch_branch_site(&patch__call_flush_branch_caches, - (u64)&flush_branch_caches, BRANCH_SET_LINK); - -#ifdef CONFIG_KVM_BOOK3S_HV_POSSIBLE - // This enables the branch from guest_exit_cont to kvm_flush_link_stack - patch_branch_site(&patch__call_kvm_flush_link_stack, - (u64)&kvm_flush_link_stack, BRANCH_SET_LINK); #endif - pr_info("link-stack-flush: software flush enabled.\n"); - link_stack_flush_type = BRANCH_CACHE_FLUSH_SW; + // This controls the branch from _switch to flush_branch_caches + if (count_cache_flush_type == BRANCH_CACHE_FLUSH_NONE && + link_stack_flush_type == BRANCH_CACHE_FLUSH_NONE) { + patch_instruction_site(&patch__call_flush_branch_caches, + ppc_inst(PPC_INST_NOP)); + } else { + patch_branch_site(&patch__call_flush_branch_caches, + (u64)&flush_branch_caches, BRANCH_SET_LINK); + + // If we just need to flush the link stack, early return + if (count_cache_flush_type == BRANCH_CACHE_FLUSH_NONE) { + patch_instruction_site(&patch__flush_link_stack_return, + ppc_inst(PPC_INST_BLR)); + + // If we have flush instruction, early return + } else if (count_cache_flush_type == BRANCH_CACHE_FLUSH_HW) { + patch_instruction_site(&patch__flush_count_cache_return, + ppc_inst(PPC_INST_BLR)); + } + } +} + +static void toggle_branch_cache_flush(bool enable) +{ + if (!enable || !security_ftr_enabled(SEC_FTR_FLUSH_COUNT_CACHE)) { + if (count_cache_flush_type != BRANCH_CACHE_FLUSH_NONE) + count_cache_flush_type = BRANCH_CACHE_FLUSH_NONE; - // If we just need to flush the link stack, patch an early return - if (!security_ftr_enabled(SEC_FTR_FLUSH_COUNT_CACHE)) { - patch_instruction_site(&patch__flush_link_stack_return, - ppc_inst(PPC_INST_BLR)); - no_count_cache_flush(); - return; + pr_info("count-cache-flush: flush disabled.\n"); + } else { + if (security_ftr_enabled(SEC_FTR_BCCTR_FLUSH_ASSIST)) { + count_cache_flush_type = BRANCH_CACHE_FLUSH_HW; + pr_info("count-cache-flush: hardware flush enabled.\n"); + } else { + count_cache_flush_type = BRANCH_CACHE_FLUSH_SW; + pr_info("count-cache-flush: software flush enabled.\n"); + } } - if (!security_ftr_enabled(SEC_FTR_BCCTR_FLUSH_ASSIST)) { - count_cache_flush_type = BRANCH_CACHE_FLUSH_SW; - pr_info("count-cache-flush: software flush enabled.\n"); - return; + if (!enable || !security_ftr_enabled(SEC_FTR_FLUSH_LINK_STACK)) { + if (link_stack_flush_type != BRANCH_CACHE_FLUSH_NONE) + link_stack_flush_type = BRANCH_CACHE_FLUSH_NONE; + + pr_info("link-stack-flush: flush disabled.\n"); + } else { + link_stack_flush_type = BRANCH_CACHE_FLUSH_SW; + pr_info("link-stack-flush: software flush enabled.\n"); } - patch_instruction_site(&patch__flush_count_cache_return, ppc_inst(PPC_INST_BLR)); - count_cache_flush_type = BRANCH_CACHE_FLUSH_HW; - pr_info("count-cache-flush: hardware flush enabled.\n"); + update_branch_cache_flush(); } void setup_count_cache_flush(void) -- GitLab From 70d7cdaf0548ec95fa7204dcdd39cd8e63cee24d Mon Sep 17 00:00:00 2001 From: Nicholas Piggin Date: Tue, 9 Jun 2020 17:06:08 +1000 Subject: [PATCH 0460/1476] powerpc/64s: Move branch cache flushing bcctr variant to ppc-ops.h Signed-off-by: Nicholas Piggin Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20200609070610.846703-6-npiggin@gmail.com --- arch/powerpc/include/asm/ppc-opcode.h | 2 ++ arch/powerpc/kernel/entry_64.S | 6 ++---- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/arch/powerpc/include/asm/ppc-opcode.h b/arch/powerpc/include/asm/ppc-opcode.h index 777d5056a71c4..3ee6fad902bc2 100644 --- a/arch/powerpc/include/asm/ppc-opcode.h +++ b/arch/powerpc/include/asm/ppc-opcode.h @@ -195,6 +195,7 @@ #define OP_LQ 56 /* sorted alphabetically */ +#define PPC_INST_BCCTR_FLUSH 0x4c400420 #define PPC_INST_BHRBE 0x7c00025c #define PPC_INST_CLRBHRB 0x7c00035c #define PPC_INST_COPY 0x7c20060c @@ -437,6 +438,7 @@ #endif /* Deal with instructions that older assemblers aren't aware of */ +#define PPC_BCCTR_FLUSH stringify_in_c(.long PPC_INST_BCCTR_FLUSH) #define PPC_CP_ABORT stringify_in_c(.long PPC_INST_CP_ABORT) #define PPC_COPY(a, b) stringify_in_c(.long PPC_INST_COPY | \ ___PPC_RA(a) | ___PPC_RB(b)) diff --git a/arch/powerpc/kernel/entry_64.S b/arch/powerpc/kernel/entry_64.S index bdef9c11bad22..da85c2511e57d 100644 --- a/arch/powerpc/kernel/entry_64.S +++ b/arch/powerpc/kernel/entry_64.S @@ -261,8 +261,6 @@ _ASM_NOKPROBE_SYMBOL(save_nvgprs); 1: nop; \ patch_site 1b, patch__call_flush_branch_caches -#define BCCTR_FLUSH .long 0x4c400420 - .macro nops number .rept \number nop @@ -293,7 +291,7 @@ flush_branch_caches: li r9,0x7fff mtctr r9 - BCCTR_FLUSH + PPC_BCCTR_FLUSH 2: nop patch_site 2b patch__flush_count_cache_return @@ -302,7 +300,7 @@ flush_branch_caches: .rept 278 .balign 32 - BCCTR_FLUSH + PPC_BCCTR_FLUSH nops 7 .endr -- GitLab From 4d24e21cc694e7253a532fe5a9bde12b284f1317 Mon Sep 17 00:00:00 2001 From: Nicholas Piggin Date: Tue, 9 Jun 2020 17:06:09 +1000 Subject: [PATCH 0461/1476] powerpc/security: Allow for processors that flush the link stack using the special bcctr If both count cache and link stack are to be flushed, and can be flushed with the special bcctr, patch that in directly to the flush/branch nop site. Signed-off-by: Nicholas Piggin Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20200609070610.846703-7-npiggin@gmail.com --- arch/powerpc/include/asm/security_features.h | 2 ++ arch/powerpc/kernel/security.c | 27 ++++++++++++++------ 2 files changed, 21 insertions(+), 8 deletions(-) diff --git a/arch/powerpc/include/asm/security_features.h b/arch/powerpc/include/asm/security_features.h index 7c05e95a5c444..fbb8fa32150fd 100644 --- a/arch/powerpc/include/asm/security_features.h +++ b/arch/powerpc/include/asm/security_features.h @@ -63,6 +63,8 @@ static inline bool security_ftr_enabled(u64 feature) // bcctr 2,0,0 triggers a hardware assisted count cache flush #define SEC_FTR_BCCTR_FLUSH_ASSIST 0x0000000000000800ull +// bcctr 2,0,0 triggers a hardware assisted link stack flush +#define SEC_FTR_BCCTR_LINK_FLUSH_ASSIST 0x0000000000002000ull // Features indicating need for Spectre/Meltdown mitigations diff --git a/arch/powerpc/kernel/security.c b/arch/powerpc/kernel/security.c index 05eeb22b3b978..c9876aab31421 100644 --- a/arch/powerpc/kernel/security.c +++ b/arch/powerpc/kernel/security.c @@ -219,24 +219,25 @@ ssize_t cpu_show_spectre_v2(struct device *dev, struct device_attribute *attr, c if (ccd) seq_buf_printf(&s, "Indirect branch cache disabled"); - if (link_stack_flush_type == BRANCH_CACHE_FLUSH_SW) - seq_buf_printf(&s, ", Software link stack flush"); - } else if (count_cache_flush_type != BRANCH_CACHE_FLUSH_NONE) { seq_buf_printf(&s, "Mitigation: Software count cache flush"); if (count_cache_flush_type == BRANCH_CACHE_FLUSH_HW) seq_buf_printf(&s, " (hardware accelerated)"); - if (link_stack_flush_type == BRANCH_CACHE_FLUSH_SW) - seq_buf_printf(&s, ", Software link stack flush"); - } else if (btb_flush_enabled) { seq_buf_printf(&s, "Mitigation: Branch predictor state flush"); } else { seq_buf_printf(&s, "Vulnerable"); } + if (bcs || ccd || count_cache_flush_type != BRANCH_CACHE_FLUSH_NONE) { + if (link_stack_flush_type != BRANCH_CACHE_FLUSH_NONE) + seq_buf_printf(&s, ", Software link stack flush"); + if (link_stack_flush_type == BRANCH_CACHE_FLUSH_HW) + seq_buf_printf(&s, " (hardware accelerated)"); + } + seq_buf_printf(&s, "\n"); return s.len; @@ -435,6 +436,7 @@ static void update_branch_cache_flush(void) patch_instruction_site(&patch__call_kvm_flush_link_stack, ppc_inst(PPC_INST_NOP)); } else { + // Could use HW flush, but that could also flush count cache patch_branch_site(&patch__call_kvm_flush_link_stack, (u64)&kvm_flush_link_stack, BRANCH_SET_LINK); } @@ -445,6 +447,10 @@ static void update_branch_cache_flush(void) link_stack_flush_type == BRANCH_CACHE_FLUSH_NONE) { patch_instruction_site(&patch__call_flush_branch_caches, ppc_inst(PPC_INST_NOP)); + } else if (count_cache_flush_type == BRANCH_CACHE_FLUSH_HW && + link_stack_flush_type == BRANCH_CACHE_FLUSH_HW) { + patch_instruction_site(&patch__call_flush_branch_caches, + ppc_inst(PPC_INST_BCCTR_FLUSH)); } else { patch_branch_site(&patch__call_flush_branch_caches, (u64)&flush_branch_caches, BRANCH_SET_LINK); @@ -485,8 +491,13 @@ static void toggle_branch_cache_flush(bool enable) pr_info("link-stack-flush: flush disabled.\n"); } else { - link_stack_flush_type = BRANCH_CACHE_FLUSH_SW; - pr_info("link-stack-flush: software flush enabled.\n"); + if (security_ftr_enabled(SEC_FTR_BCCTR_LINK_FLUSH_ASSIST)) { + link_stack_flush_type = BRANCH_CACHE_FLUSH_HW; + pr_info("link-stack-flush: hardware flush enabled.\n"); + } else { + link_stack_flush_type = BRANCH_CACHE_FLUSH_SW; + pr_info("link-stack-flush: software flush enabled.\n"); + } } update_branch_cache_flush(); -- GitLab From 48f6e7f6d948b56489da027bc3284c709b939d28 Mon Sep 17 00:00:00 2001 From: Nathan Lynch Date: Fri, 12 Jun 2020 00:12:21 -0500 Subject: [PATCH 0462/1476] powerpc/pseries: remove cede offline state for CPUs This effectively reverts commit 3aa565f53c39 ("powerpc/pseries: Add hooks to put the CPU into an appropriate offline state"), which added an offline mode for CPUs which uses the H_CEDE hcall instead of the architected stop-self RTAS function in order to facilitate "folding" of dedicated mode processors on PowerVM platforms to achieve energy savings. This has been the default offline mode since its introduction. There's nothing about stop-self that would prevent the hypervisor from achieving the energy savings available via H_CEDE, so the original premise of this change appears to be flawed. I also have encountered the claim that the transition to and from ceded state is much faster than stop-self/start-cpu. Certainly we would not want to use stop-self as an *idle* mode. That is what H_CEDE is for. However, this difference is insignificant in the context of Linux CPU hotplug, where the latency of an offline or online operation on current systems is on the order of 100ms, mainly attributable to all the various subsystems' cpuhp callbacks. The cede offline mode also prevents accurate accounting, as discussed before: https://lore.kernel.org/linuxppc-dev/1571740391-3251-1-git-send-email-ego@linux.vnet.ibm.com/ Unconditionally use stop-self to offline processor threads. This is the architected method for offlining CPUs on PAPR systems. The "cede_offline" boot parameter is rendered obsolete. Removing this code enables the removal of the partition suspend code which temporarily onlines all present CPUs. Fixes: 3aa565f53c39 ("powerpc/pseries: Add hooks to put the CPU into an appropriate offline state") Signed-off-by: Nathan Lynch Reviewed-by: Gautham R. Shenoy Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20200612051238.1007764-2-nathanl@linux.ibm.com --- Documentation/core-api/cpu_hotplug.rst | 7 - arch/powerpc/platforms/pseries/hotplug-cpu.c | 170 ++---------------- .../platforms/pseries/offline_states.h | 38 ---- arch/powerpc/platforms/pseries/pmem.c | 1 - arch/powerpc/platforms/pseries/smp.c | 28 +-- 5 files changed, 15 insertions(+), 229 deletions(-) delete mode 100644 arch/powerpc/platforms/pseries/offline_states.h diff --git a/Documentation/core-api/cpu_hotplug.rst b/Documentation/core-api/cpu_hotplug.rst index 4a50ab7817f77..b1ae1ac159cf9 100644 --- a/Documentation/core-api/cpu_hotplug.rst +++ b/Documentation/core-api/cpu_hotplug.rst @@ -50,13 +50,6 @@ Command Line Switches This option is limited to the X86 and S390 architecture. -``cede_offline={"off","on"}`` - Use this option to disable/enable putting offlined processors to an extended - ``H_CEDE`` state on supported pseries platforms. If nothing is specified, - ``cede_offline`` is set to "on". - - This option is limited to the PowerPC architecture. - ``cpu0_hotplug`` Allow to shutdown CPU0. diff --git a/arch/powerpc/platforms/pseries/hotplug-cpu.c b/arch/powerpc/platforms/pseries/hotplug-cpu.c index 3e8cbfe7a80fd..d4b346355bb9e 100644 --- a/arch/powerpc/platforms/pseries/hotplug-cpu.c +++ b/arch/powerpc/platforms/pseries/hotplug-cpu.c @@ -35,54 +35,10 @@ #include #include "pseries.h" -#include "offline_states.h" /* This version can't take the spinlock, because it never returns */ static int rtas_stop_self_token = RTAS_UNKNOWN_SERVICE; -static DEFINE_PER_CPU(enum cpu_state_vals, preferred_offline_state) = - CPU_STATE_OFFLINE; -static DEFINE_PER_CPU(enum cpu_state_vals, current_state) = CPU_STATE_OFFLINE; - -static enum cpu_state_vals default_offline_state = CPU_STATE_OFFLINE; - -static bool cede_offline_enabled __read_mostly = true; - -/* - * Enable/disable cede_offline when available. - */ -static int __init setup_cede_offline(char *str) -{ - return (kstrtobool(str, &cede_offline_enabled) == 0); -} - -__setup("cede_offline=", setup_cede_offline); - -enum cpu_state_vals get_cpu_current_state(int cpu) -{ - return per_cpu(current_state, cpu); -} - -void set_cpu_current_state(int cpu, enum cpu_state_vals state) -{ - per_cpu(current_state, cpu) = state; -} - -enum cpu_state_vals get_preferred_offline_state(int cpu) -{ - return per_cpu(preferred_offline_state, cpu); -} - -void set_preferred_offline_state(int cpu, enum cpu_state_vals state) -{ - per_cpu(preferred_offline_state, cpu) = state; -} - -void set_default_offline_state(int cpu) -{ - per_cpu(preferred_offline_state, cpu) = default_offline_state; -} - static void rtas_stop_self(void) { static struct rtas_args args; @@ -101,9 +57,7 @@ static void rtas_stop_self(void) static void pseries_mach_cpu_die(void) { - unsigned int cpu = smp_processor_id(); unsigned int hwcpu = hard_smp_processor_id(); - u8 cede_latency_hint = 0; local_irq_disable(); idle_task_exit(); @@ -112,49 +66,6 @@ static void pseries_mach_cpu_die(void) else xics_teardown_cpu(); - if (get_preferred_offline_state(cpu) == CPU_STATE_INACTIVE) { - set_cpu_current_state(cpu, CPU_STATE_INACTIVE); - if (ppc_md.suspend_disable_cpu) - ppc_md.suspend_disable_cpu(); - - cede_latency_hint = 2; - - get_lppaca()->idle = 1; - if (!lppaca_shared_proc(get_lppaca())) - get_lppaca()->donate_dedicated_cpu = 1; - - while (get_preferred_offline_state(cpu) == CPU_STATE_INACTIVE) { - while (!prep_irq_for_idle()) { - local_irq_enable(); - local_irq_disable(); - } - - extended_cede_processor(cede_latency_hint); - } - - local_irq_disable(); - - if (!lppaca_shared_proc(get_lppaca())) - get_lppaca()->donate_dedicated_cpu = 0; - get_lppaca()->idle = 0; - - if (get_preferred_offline_state(cpu) == CPU_STATE_ONLINE) { - unregister_slb_shadow(hwcpu); - - hard_irq_disable(); - /* - * Call to start_secondary_resume() will not return. - * Kernel stack will be reset and start_secondary() - * will be called to continue the online operation. - */ - start_secondary_resume(); - } - } - - /* Requested state is CPU_STATE_OFFLINE at this point */ - WARN_ON(get_preferred_offline_state(cpu) != CPU_STATE_OFFLINE); - - set_cpu_current_state(cpu, CPU_STATE_OFFLINE); unregister_slb_shadow(hwcpu); rtas_stop_self(); @@ -200,24 +111,13 @@ static void pseries_cpu_die(unsigned int cpu) int cpu_status = 1; unsigned int pcpu = get_hard_smp_processor_id(cpu); - if (get_preferred_offline_state(cpu) == CPU_STATE_INACTIVE) { - cpu_status = 1; - for (tries = 0; tries < 5000; tries++) { - if (get_cpu_current_state(cpu) == CPU_STATE_INACTIVE) { - cpu_status = 0; - break; - } - msleep(1); - } - } else if (get_preferred_offline_state(cpu) == CPU_STATE_OFFLINE) { + for (tries = 0; tries < 25; tries++) { + cpu_status = smp_query_cpu_stopped(pcpu); + if (cpu_status == QCSS_STOPPED || + cpu_status == QCSS_HARDWARE_ERROR) + break; + cpu_relax(); - for (tries = 0; tries < 25; tries++) { - cpu_status = smp_query_cpu_stopped(pcpu); - if (cpu_status == QCSS_STOPPED || - cpu_status == QCSS_HARDWARE_ERROR) - break; - cpu_relax(); - } } if (cpu_status != 0) { @@ -359,28 +259,15 @@ static int dlpar_offline_cpu(struct device_node *dn) if (get_hard_smp_processor_id(cpu) != thread) continue; - if (get_cpu_current_state(cpu) == CPU_STATE_OFFLINE) + if (!cpu_online(cpu)) break; - if (get_cpu_current_state(cpu) == CPU_STATE_ONLINE) { - set_preferred_offline_state(cpu, - CPU_STATE_OFFLINE); - cpu_maps_update_done(); - timed_topology_update(1); - rc = device_offline(get_cpu_device(cpu)); - if (rc) - goto out; - cpu_maps_update_begin(); - break; - } - - /* - * The cpu is in CPU_STATE_INACTIVE. - * Upgrade it's state to CPU_STATE_OFFLINE. - */ - set_preferred_offline_state(cpu, CPU_STATE_OFFLINE); - WARN_ON(plpar_hcall_norets(H_PROD, thread) != H_SUCCESS); - __cpu_die(cpu); + cpu_maps_update_done(); + timed_topology_update(1); + rc = device_offline(get_cpu_device(cpu)); + if (rc) + goto out; + cpu_maps_update_begin(); break; } if (cpu == num_possible_cpus()) { @@ -414,8 +301,6 @@ static int dlpar_online_cpu(struct device_node *dn) for_each_present_cpu(cpu) { if (get_hard_smp_processor_id(cpu) != thread) continue; - BUG_ON(get_cpu_current_state(cpu) - != CPU_STATE_OFFLINE); cpu_maps_update_done(); timed_topology_update(1); find_and_online_cpu_nid(cpu); @@ -1013,27 +898,8 @@ static struct notifier_block pseries_smp_nb = { .notifier_call = pseries_smp_notifier, }; -#define MAX_CEDE_LATENCY_LEVELS 4 -#define CEDE_LATENCY_PARAM_LENGTH 10 -#define CEDE_LATENCY_PARAM_MAX_LENGTH \ - (MAX_CEDE_LATENCY_LEVELS * CEDE_LATENCY_PARAM_LENGTH * sizeof(char)) -#define CEDE_LATENCY_TOKEN 45 - -static char cede_parameters[CEDE_LATENCY_PARAM_MAX_LENGTH]; - -static int parse_cede_parameters(void) -{ - memset(cede_parameters, 0, CEDE_LATENCY_PARAM_MAX_LENGTH); - return rtas_call(rtas_token("ibm,get-system-parameter"), 3, 1, - NULL, - CEDE_LATENCY_TOKEN, - __pa(cede_parameters), - CEDE_LATENCY_PARAM_MAX_LENGTH); -} - static int __init pseries_cpu_hotplug_init(void) { - int cpu; int qcss_tok; #ifdef CONFIG_ARCH_CPU_PROBE_RELEASE @@ -1056,16 +922,8 @@ static int __init pseries_cpu_hotplug_init(void) smp_ops->cpu_die = pseries_cpu_die; /* Processors can be added/removed only on LPAR */ - if (firmware_has_feature(FW_FEATURE_LPAR)) { + if (firmware_has_feature(FW_FEATURE_LPAR)) of_reconfig_notifier_register(&pseries_smp_nb); - cpu_maps_update_begin(); - if (cede_offline_enabled && parse_cede_parameters() == 0) { - default_offline_state = CPU_STATE_INACTIVE; - for_each_online_cpu(cpu) - set_default_offline_state(cpu); - } - cpu_maps_update_done(); - } return 0; } diff --git a/arch/powerpc/platforms/pseries/offline_states.h b/arch/powerpc/platforms/pseries/offline_states.h deleted file mode 100644 index 51414aee28625..0000000000000 --- a/arch/powerpc/platforms/pseries/offline_states.h +++ /dev/null @@ -1,38 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -#ifndef _OFFLINE_STATES_H_ -#define _OFFLINE_STATES_H_ - -/* Cpu offline states go here */ -enum cpu_state_vals { - CPU_STATE_OFFLINE, - CPU_STATE_INACTIVE, - CPU_STATE_ONLINE, - CPU_MAX_OFFLINE_STATES -}; - -#ifdef CONFIG_HOTPLUG_CPU -extern enum cpu_state_vals get_cpu_current_state(int cpu); -extern void set_cpu_current_state(int cpu, enum cpu_state_vals state); -extern void set_preferred_offline_state(int cpu, enum cpu_state_vals state); -extern void set_default_offline_state(int cpu); -#else -static inline enum cpu_state_vals get_cpu_current_state(int cpu) -{ - return CPU_STATE_ONLINE; -} - -static inline void set_cpu_current_state(int cpu, enum cpu_state_vals state) -{ -} - -static inline void set_preferred_offline_state(int cpu, enum cpu_state_vals state) -{ -} - -static inline void set_default_offline_state(int cpu) -{ -} -#endif - -extern enum cpu_state_vals get_preferred_offline_state(int cpu); -#endif diff --git a/arch/powerpc/platforms/pseries/pmem.c b/arch/powerpc/platforms/pseries/pmem.c index 2347e1038f58b..e1dc5d3254df9 100644 --- a/arch/powerpc/platforms/pseries/pmem.c +++ b/arch/powerpc/platforms/pseries/pmem.c @@ -24,7 +24,6 @@ #include #include "pseries.h" -#include "offline_states.h" static struct device_node *pmem_node; diff --git a/arch/powerpc/platforms/pseries/smp.c b/arch/powerpc/platforms/pseries/smp.c index 6891710833be5..7ebacac03dc36 100644 --- a/arch/powerpc/platforms/pseries/smp.c +++ b/arch/powerpc/platforms/pseries/smp.c @@ -44,8 +44,6 @@ #include #include "pseries.h" -#include "offline_states.h" - /* * The Primary thread of each non-boot processor was started from the OF client @@ -108,10 +106,7 @@ static inline int smp_startup_cpu(unsigned int lcpu) /* Fixup atomic count: it exited inside IRQ handler. */ task_thread_info(paca_ptrs[lcpu]->__current)->preempt_count = 0; -#ifdef CONFIG_HOTPLUG_CPU - if (get_cpu_current_state(lcpu) == CPU_STATE_INACTIVE) - goto out; -#endif + /* * If the RTAS start-cpu token does not exist then presume the * cpu is already spinning. @@ -126,9 +121,6 @@ static inline int smp_startup_cpu(unsigned int lcpu) return 0; } -#ifdef CONFIG_HOTPLUG_CPU -out: -#endif return 1; } @@ -143,10 +135,6 @@ static void smp_setup_cpu(int cpu) vpa_init(cpu); cpumask_clear_cpu(cpu, of_spin_mask); -#ifdef CONFIG_HOTPLUG_CPU - set_cpu_current_state(cpu, CPU_STATE_ONLINE); - set_default_offline_state(cpu); -#endif } static int smp_pSeries_kick_cpu(int nr) @@ -163,20 +151,6 @@ static int smp_pSeries_kick_cpu(int nr) * the processor will continue on to secondary_start */ paca_ptrs[nr]->cpu_start = 1; -#ifdef CONFIG_HOTPLUG_CPU - set_preferred_offline_state(nr, CPU_STATE_ONLINE); - - if (get_cpu_current_state(nr) == CPU_STATE_INACTIVE) { - long rc; - unsigned long hcpuid; - - hcpuid = get_hard_smp_processor_id(nr); - rc = plpar_hcall_norets(H_PROD, hcpuid); - if (rc != H_SUCCESS) - printk(KERN_ERR "Error: Prod to wake up processor %d " - "Ret= %ld\n", nr, rc); - } -#endif return 0; } -- GitLab From ec2fc2a9e9bbad9023aab65bc472ce7a3ca8608f Mon Sep 17 00:00:00 2001 From: Nathan Lynch Date: Fri, 12 Jun 2020 00:12:22 -0500 Subject: [PATCH 0463/1476] powerpc/rtas: don't online CPUs for partition suspend Partition suspension, used for hibernation and migration, requires that the OS place all but one of the LPAR's processor threads into one of two states prior to calling the ibm,suspend-me RTAS function: * the architected offline state (via RTAS stop-self); or * the H_JOIN hcall, which does not return until the partition resumes execution Using H_CEDE as the offline mode, introduced by commit 3aa565f53c39 ("powerpc/pseries: Add hooks to put the CPU into an appropriate offline state"), means that any threads which are offline from Linux's point of view must be moved to one of those two states before a partition suspension can proceed. This was eventually addressed in commit 120496ac2d2d ("powerpc: Bring all threads online prior to migration/hibernation"), which added code to temporarily bring up any offline processor threads so they can call H_JOIN. Conceptually this is fine, but the implementation has had multiple races with cpu hotplug operations initiated from user space[1][2][3], the error handling is fragile, and it generates user-visible cpu hotplug events which is a lot of noise for a platform feature that's supposed to minimize disruption to workloads. With commit 3aa565f53c39 ("powerpc/pseries: Add hooks to put the CPU into an appropriate offline state") reverted, this code becomes unnecessary, so remove it. Since any offline CPUs now are truly offline from the platform's point of view, it is no longer necessary to bring up CPUs only to have them call H_JOIN and then go offline again upon resuming. Only active threads are required to call H_JOIN; stopped threads can be left alone. [1] commit a6717c01ddc2 ("powerpc/rtas: use device model APIs and serialization during LPM") [2] commit 9fb603050ffd ("powerpc/rtas: retry when cpu offline races with suspend/migration") [3] commit dfd718a2ed1f ("powerpc/rtas: Fix a potential race between CPU-Offline & Migration") Fixes: 120496ac2d2d ("powerpc: Bring all threads online prior to migration/hibernation") Signed-off-by: Nathan Lynch Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20200612051238.1007764-3-nathanl@linux.ibm.com --- arch/powerpc/include/asm/rtas.h | 2 - arch/powerpc/kernel/rtas.c | 122 +---------------------- arch/powerpc/platforms/pseries/suspend.c | 22 +--- 3 files changed, 3 insertions(+), 143 deletions(-) diff --git a/arch/powerpc/include/asm/rtas.h b/arch/powerpc/include/asm/rtas.h index 014968f25f7e1..0107d724e9daa 100644 --- a/arch/powerpc/include/asm/rtas.h +++ b/arch/powerpc/include/asm/rtas.h @@ -253,8 +253,6 @@ extern int rtas_set_indicator_fast(int indicator, int index, int new_value); extern void rtas_progress(char *s, unsigned short hex); extern int rtas_suspend_cpu(struct rtas_suspend_me_data *data); extern int rtas_suspend_last_cpu(struct rtas_suspend_me_data *data); -extern int rtas_online_cpus_mask(cpumask_var_t cpus); -extern int rtas_offline_cpus_mask(cpumask_var_t cpus); extern int rtas_ibm_suspend_me(u64 handle); struct rtc_time; diff --git a/arch/powerpc/kernel/rtas.c b/arch/powerpc/kernel/rtas.c index a09eba03f1809..806d554ce3577 100644 --- a/arch/powerpc/kernel/rtas.c +++ b/arch/powerpc/kernel/rtas.c @@ -843,96 +843,6 @@ static void rtas_percpu_suspend_me(void *info) __rtas_suspend_cpu((struct rtas_suspend_me_data *)info, 1); } -enum rtas_cpu_state { - DOWN, - UP, -}; - -#ifndef CONFIG_SMP -static int rtas_cpu_state_change_mask(enum rtas_cpu_state state, - cpumask_var_t cpus) -{ - if (!cpumask_empty(cpus)) { - cpumask_clear(cpus); - return -EINVAL; - } else - return 0; -} -#else -/* On return cpumask will be altered to indicate CPUs changed. - * CPUs with states changed will be set in the mask, - * CPUs with status unchanged will be unset in the mask. */ -static int rtas_cpu_state_change_mask(enum rtas_cpu_state state, - cpumask_var_t cpus) -{ - int cpu; - int cpuret = 0; - int ret = 0; - - if (cpumask_empty(cpus)) - return 0; - - for_each_cpu(cpu, cpus) { - struct device *dev = get_cpu_device(cpu); - - switch (state) { - case DOWN: - cpuret = device_offline(dev); - break; - case UP: - cpuret = device_online(dev); - break; - } - if (cpuret < 0) { - pr_debug("%s: cpu_%s for cpu#%d returned %d.\n", - __func__, - ((state == UP) ? "up" : "down"), - cpu, cpuret); - if (!ret) - ret = cpuret; - if (state == UP) { - /* clear bits for unchanged cpus, return */ - cpumask_shift_right(cpus, cpus, cpu); - cpumask_shift_left(cpus, cpus, cpu); - break; - } else { - /* clear bit for unchanged cpu, continue */ - cpumask_clear_cpu(cpu, cpus); - } - } - cond_resched(); - } - - return ret; -} -#endif - -int rtas_online_cpus_mask(cpumask_var_t cpus) -{ - int ret; - - ret = rtas_cpu_state_change_mask(UP, cpus); - - if (ret) { - cpumask_var_t tmp_mask; - - if (!alloc_cpumask_var(&tmp_mask, GFP_KERNEL)) - return ret; - - /* Use tmp_mask to preserve cpus mask from first failure */ - cpumask_copy(tmp_mask, cpus); - rtas_offline_cpus_mask(tmp_mask); - free_cpumask_var(tmp_mask); - } - - return ret; -} - -int rtas_offline_cpus_mask(cpumask_var_t cpus) -{ - return rtas_cpu_state_change_mask(DOWN, cpus); -} - int rtas_ibm_suspend_me(u64 handle) { long state; @@ -940,8 +850,6 @@ int rtas_ibm_suspend_me(u64 handle) unsigned long retbuf[PLPAR_HCALL_BUFSIZE]; struct rtas_suspend_me_data data; DECLARE_COMPLETION_ONSTACK(done); - cpumask_var_t offline_mask; - int cpuret; if (!rtas_service_present("ibm,suspend-me")) return -ENOSYS; @@ -962,9 +870,6 @@ int rtas_ibm_suspend_me(u64 handle) return -EIO; } - if (!alloc_cpumask_var(&offline_mask, GFP_KERNEL)) - return -ENOMEM; - atomic_set(&data.working, 0); atomic_set(&data.done, 0); atomic_set(&data.error, 0); @@ -973,24 +878,8 @@ int rtas_ibm_suspend_me(u64 handle) lock_device_hotplug(); - /* All present CPUs must be online */ - cpumask_andnot(offline_mask, cpu_present_mask, cpu_online_mask); - cpuret = rtas_online_cpus_mask(offline_mask); - if (cpuret) { - pr_err("%s: Could not bring present CPUs online.\n", __func__); - atomic_set(&data.error, cpuret); - goto out; - } - cpu_hotplug_disable(); - /* Check if we raced with a CPU-Offline Operation */ - if (!cpumask_equal(cpu_present_mask, cpu_online_mask)) { - pr_info("%s: Raced against a concurrent CPU-Offline\n", __func__); - atomic_set(&data.error, -EAGAIN); - goto out_hotplug_enable; - } - /* Call function on all CPUs. One of us will make the * rtas call */ @@ -1001,18 +890,11 @@ int rtas_ibm_suspend_me(u64 handle) if (atomic_read(&data.error) != 0) printk(KERN_ERR "Error doing global join\n"); -out_hotplug_enable: - cpu_hotplug_enable(); - /* Take down CPUs not online prior to suspend */ - cpuret = rtas_offline_cpus_mask(offline_mask); - if (cpuret) - pr_warn("%s: Could not restore CPUs to offline state.\n", - __func__); + cpu_hotplug_enable(); -out: unlock_device_hotplug(); - free_cpumask_var(offline_mask); + return atomic_read(&data.error); } diff --git a/arch/powerpc/platforms/pseries/suspend.c b/arch/powerpc/platforms/pseries/suspend.c index 0a24a5a185f02..f789693f61f40 100644 --- a/arch/powerpc/platforms/pseries/suspend.c +++ b/arch/powerpc/platforms/pseries/suspend.c @@ -132,15 +132,11 @@ static ssize_t store_hibernate(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { - cpumask_var_t offline_mask; int rc; if (!capable(CAP_SYS_ADMIN)) return -EPERM; - if (!alloc_cpumask_var(&offline_mask, GFP_KERNEL)) - return -ENOMEM; - stream_id = simple_strtoul(buf, NULL, 16); do { @@ -150,32 +146,16 @@ static ssize_t store_hibernate(struct device *dev, } while (rc == -EAGAIN); if (!rc) { - /* All present CPUs must be online */ - cpumask_andnot(offline_mask, cpu_present_mask, - cpu_online_mask); - rc = rtas_online_cpus_mask(offline_mask); - if (rc) { - pr_err("%s: Could not bring present CPUs online.\n", - __func__); - goto out; - } - stop_topology_update(); rc = pm_suspend(PM_SUSPEND_MEM); start_topology_update(); - - /* Take down CPUs not online prior to suspend */ - if (!rtas_offline_cpus_mask(offline_mask)) - pr_warn("%s: Could not restore CPUs to offline " - "state.\n", __func__); } stream_id = 0; if (!rc) rc = count; -out: - free_cpumask_var(offline_mask); + return rc; } -- GitLab From c30f931e891eb0a32885ecd79984e1e7366fceda Mon Sep 17 00:00:00 2001 From: Nathan Lynch Date: Fri, 12 Jun 2020 00:12:23 -0500 Subject: [PATCH 0464/1476] powerpc/numa: remove ability to enable topology updates Remove the /proc/powerpc/topology_updates interface and the topology_updates=on/off command line argument. The internal topology_updates_enabled flag remains for now, but always false. Signed-off-by: Nathan Lynch Reviewed-by: Srikar Dronamraju Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20200612051238.1007764-4-nathanl@linux.ibm.com --- arch/powerpc/mm/numa.c | 71 +----------------------------------------- 1 file changed, 1 insertion(+), 70 deletions(-) diff --git a/arch/powerpc/mm/numa.c b/arch/powerpc/mm/numa.c index 9fcf2d1958300..34d95de77bdde 100644 --- a/arch/powerpc/mm/numa.c +++ b/arch/powerpc/mm/numa.c @@ -984,27 +984,7 @@ static int __init early_numa(char *p) } early_param("numa", early_numa); -/* - * The platform can inform us through one of several mechanisms - * (post-migration device tree updates, PRRN or VPHN) that the NUMA - * assignment of a resource has changed. This controls whether we act - * on that. Disabled by default. - */ -static bool topology_updates_enabled; - -static int __init early_topology_updates(char *p) -{ - if (!p) - return 0; - - if (!strcmp(p, "on")) { - pr_warn("Caution: enabling topology updates\n"); - topology_updates_enabled = true; - } - - return 0; -} -early_param("topology_updates", early_topology_updates); +static const bool topology_updates_enabled; #ifdef CONFIG_MEMORY_HOTPLUG /* @@ -1632,52 +1612,6 @@ int prrn_is_enabled(void) return prrn_enabled; } -static int topology_read(struct seq_file *file, void *v) -{ - if (vphn_enabled || prrn_enabled) - seq_puts(file, "on\n"); - else - seq_puts(file, "off\n"); - - return 0; -} - -static int topology_open(struct inode *inode, struct file *file) -{ - return single_open(file, topology_read, NULL); -} - -static ssize_t topology_write(struct file *file, const char __user *buf, - size_t count, loff_t *off) -{ - char kbuf[4]; /* "on" or "off" plus null. */ - int read_len; - - read_len = count < 3 ? count : 3; - if (copy_from_user(kbuf, buf, read_len)) - return -EINVAL; - - kbuf[read_len] = '\0'; - - if (!strncmp(kbuf, "on", 2)) { - topology_updates_enabled = true; - start_topology_update(); - } else if (!strncmp(kbuf, "off", 3)) { - stop_topology_update(); - topology_updates_enabled = false; - } else - return -EINVAL; - - return count; -} - -static const struct proc_ops topology_proc_ops = { - .proc_read = seq_read, - .proc_write = topology_write, - .proc_open = topology_open, - .proc_release = single_release, -}; - static int topology_update_init(void) { start_topology_update(); @@ -1685,9 +1619,6 @@ static int topology_update_init(void) if (vphn_enabled) topology_schedule_update(); - if (!proc_create("powerpc/topology_updates", 0644, NULL, &topology_proc_ops)) - return -ENOMEM; - topology_inited = 1; return 0; } -- GitLab From 7d35bef96a46f7e9e167bb25258c0bd389aeab1b Mon Sep 17 00:00:00 2001 From: Nathan Lynch Date: Fri, 12 Jun 2020 00:12:24 -0500 Subject: [PATCH 0465/1476] powerpc/numa: remove unreachable topology update code Since the topology_updates_enabled flag is now always false, remove it and the code which has become unreachable. This is the minimum change that prevents 'defined but unused' warnings emitted by the compiler after stubbing out the start/stop_topology_updates() functions. Signed-off-by: Nathan Lynch Reviewed-by: Srikar Dronamraju Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20200612051238.1007764-5-nathanl@linux.ibm.com --- arch/powerpc/mm/numa.c | 149 +---------------------------------------- 1 file changed, 2 insertions(+), 147 deletions(-) diff --git a/arch/powerpc/mm/numa.c b/arch/powerpc/mm/numa.c index 34d95de77bdde..9e20f12e6cafc 100644 --- a/arch/powerpc/mm/numa.c +++ b/arch/powerpc/mm/numa.c @@ -984,8 +984,6 @@ static int __init early_numa(char *p) } early_param("numa", early_numa); -static const bool topology_updates_enabled; - #ifdef CONFIG_MEMORY_HOTPLUG /* * Find the node associated with a hot added memory section for @@ -1133,7 +1131,6 @@ struct topology_update_data { #define TOPOLOGY_DEF_TIMER_SECS 60 -static u8 vphn_cpu_change_counts[NR_CPUS][MAX_DISTANCE_REF_POINTS]; static cpumask_t cpu_associativity_changes_mask; static int vphn_enabled; static int prrn_enabled; @@ -1158,63 +1155,6 @@ int timed_topology_update(int nsecs) return 0; } -/* - * Store the current values of the associativity change counters in the - * hypervisor. - */ -static void setup_cpu_associativity_change_counters(void) -{ - int cpu; - - /* The VPHN feature supports a maximum of 8 reference points */ - BUILD_BUG_ON(MAX_DISTANCE_REF_POINTS > 8); - - for_each_possible_cpu(cpu) { - int i; - u8 *counts = vphn_cpu_change_counts[cpu]; - volatile u8 *hypervisor_counts = lppaca_of(cpu).vphn_assoc_counts; - - for (i = 0; i < distance_ref_points_depth; i++) - counts[i] = hypervisor_counts[i]; - } -} - -/* - * The hypervisor maintains a set of 8 associativity change counters in - * the VPA of each cpu that correspond to the associativity levels in the - * ibm,associativity-reference-points property. When an associativity - * level changes, the corresponding counter is incremented. - * - * Set a bit in cpu_associativity_changes_mask for each cpu whose home - * node associativity levels have changed. - * - * Returns the number of cpus with unhandled associativity changes. - */ -static int update_cpu_associativity_changes_mask(void) -{ - int cpu; - cpumask_t *changes = &cpu_associativity_changes_mask; - - for_each_possible_cpu(cpu) { - int i, changed = 0; - u8 *counts = vphn_cpu_change_counts[cpu]; - volatile u8 *hypervisor_counts = lppaca_of(cpu).vphn_assoc_counts; - - for (i = 0; i < distance_ref_points_depth; i++) { - if (hypervisor_counts[i] != counts[i]) { - counts[i] = hypervisor_counts[i]; - changed = 1; - } - } - if (changed) { - cpumask_or(changes, changes, cpu_sibling_mask(cpu)); - cpu = cpu_last_thread_sibling(cpu); - } - } - - return cpumask_weight(changes); -} - /* * Retrieve the new associativity information for a virtual processor's * home node. @@ -1498,16 +1438,6 @@ static void topology_schedule_update(void) schedule_work(&topology_work); } -static void topology_timer_fn(struct timer_list *unused) -{ - if (prrn_enabled && cpumask_weight(&cpu_associativity_changes_mask)) - topology_schedule_update(); - else if (vphn_enabled) { - if (update_cpu_associativity_changes_mask() > 0) - topology_schedule_update(); - reset_topology_timer(); - } -} static struct timer_list topology_timer; static void reset_topology_timer(void) @@ -1516,69 +1446,12 @@ static void reset_topology_timer(void) mod_timer(&topology_timer, jiffies + topology_timer_secs * HZ); } -#ifdef CONFIG_SMP - -static int dt_update_callback(struct notifier_block *nb, - unsigned long action, void *data) -{ - struct of_reconfig_data *update = data; - int rc = NOTIFY_DONE; - - switch (action) { - case OF_RECONFIG_UPDATE_PROPERTY: - if (of_node_is_type(update->dn, "cpu") && - !of_prop_cmp(update->prop->name, "ibm,associativity")) { - u32 core_id; - of_property_read_u32(update->dn, "reg", &core_id); - rc = dlpar_cpu_readd(core_id); - rc = NOTIFY_OK; - } - break; - } - - return rc; -} - -static struct notifier_block dt_update_nb = { - .notifier_call = dt_update_callback, -}; - -#endif - /* * Start polling for associativity changes. */ int start_topology_update(void) { - int rc = 0; - - if (!topology_updates_enabled) - return 0; - - if (firmware_has_feature(FW_FEATURE_PRRN)) { - if (!prrn_enabled) { - prrn_enabled = 1; -#ifdef CONFIG_SMP - rc = of_reconfig_notifier_register(&dt_update_nb); -#endif - } - } - if (firmware_has_feature(FW_FEATURE_VPHN) && - lppaca_shared_proc(get_lppaca())) { - if (!vphn_enabled) { - vphn_enabled = 1; - setup_cpu_associativity_change_counters(); - timer_setup(&topology_timer, topology_timer_fn, - TIMER_DEFERRABLE); - reset_topology_timer(); - } - } - - pr_info("Starting topology update%s%s\n", - (prrn_enabled ? " prrn_enabled" : ""), - (vphn_enabled ? " vphn_enabled" : "")); - - return rc; + return 0; } /* @@ -1586,25 +1459,7 @@ int start_topology_update(void) */ int stop_topology_update(void) { - int rc = 0; - - if (!topology_updates_enabled) - return 0; - - if (prrn_enabled) { - prrn_enabled = 0; -#ifdef CONFIG_SMP - rc = of_reconfig_notifier_unregister(&dt_update_nb); -#endif - } - if (vphn_enabled) { - vphn_enabled = 0; - rc = del_timer_sync(&topology_timer); - } - - pr_info("Stopping topology update\n"); - - return rc; + return 0; } int prrn_is_enabled(void) -- GitLab From e6eacf8eb4dee7bc7021c837666e3ebf1b0ec3b5 Mon Sep 17 00:00:00 2001 From: Nathan Lynch Date: Fri, 12 Jun 2020 00:12:25 -0500 Subject: [PATCH 0466/1476] powerpc/numa: make vphn_enabled, prrn_enabled flags const Previous changes have made it so these flags are never changed; enforce this by making them const. Signed-off-by: Nathan Lynch Reviewed-by: Srikar Dronamraju Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20200612051238.1007764-6-nathanl@linux.ibm.com --- arch/powerpc/mm/numa.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/powerpc/mm/numa.c b/arch/powerpc/mm/numa.c index 9e20f12e6cafc..1b89bacb89756 100644 --- a/arch/powerpc/mm/numa.c +++ b/arch/powerpc/mm/numa.c @@ -1132,8 +1132,8 @@ struct topology_update_data { #define TOPOLOGY_DEF_TIMER_SECS 60 static cpumask_t cpu_associativity_changes_mask; -static int vphn_enabled; -static int prrn_enabled; +static const int vphn_enabled; +static const int prrn_enabled; static void reset_topology_timer(void); static int topology_timer_secs = 1; static int topology_inited; -- GitLab From 50e0cf3742a01e72f4ea4a8fe9221b152e22871b Mon Sep 17 00:00:00 2001 From: Nathan Lynch Date: Fri, 12 Jun 2020 00:12:26 -0500 Subject: [PATCH 0467/1476] powerpc/numa: remove unreachable topology timer code Since vphn_enabled is always 0, we can stub out timed_topology_update() and remove the code which becomes unreachable. Signed-off-by: Nathan Lynch Reviewed-by: Srikar Dronamraju Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20200612051238.1007764-7-nathanl@linux.ibm.com --- arch/powerpc/mm/numa.c | 21 --------------------- 1 file changed, 21 deletions(-) diff --git a/arch/powerpc/mm/numa.c b/arch/powerpc/mm/numa.c index 1b89bacb89756..6207297490a81 100644 --- a/arch/powerpc/mm/numa.c +++ b/arch/powerpc/mm/numa.c @@ -1129,13 +1129,9 @@ struct topology_update_data { int new_nid; }; -#define TOPOLOGY_DEF_TIMER_SECS 60 - static cpumask_t cpu_associativity_changes_mask; static const int vphn_enabled; static const int prrn_enabled; -static void reset_topology_timer(void); -static int topology_timer_secs = 1; static int topology_inited; /* @@ -1143,15 +1139,6 @@ static int topology_inited; */ int timed_topology_update(int nsecs) { - if (vphn_enabled) { - if (nsecs > 0) - topology_timer_secs = nsecs; - else - topology_timer_secs = TOPOLOGY_DEF_TIMER_SECS; - - reset_topology_timer(); - } - return 0; } @@ -1438,14 +1425,6 @@ static void topology_schedule_update(void) schedule_work(&topology_work); } -static struct timer_list topology_timer; - -static void reset_topology_timer(void) -{ - if (vphn_enabled) - mod_timer(&topology_timer, jiffies + topology_timer_secs * HZ); -} - /* * Start polling for associativity changes. */ -- GitLab From 6325cb4a4ea8f4af8515b923650dd8f709694b44 Mon Sep 17 00:00:00 2001 From: Nathan Lynch Date: Fri, 12 Jun 2020 00:12:27 -0500 Subject: [PATCH 0468/1476] powerpc/numa: remove unreachable topology workqueue code Since vphn_enabled is always 0, we can remove the call to topology_schedule_update() and remove the code which becomes unreachable as a result. Signed-off-by: Nathan Lynch Reviewed-by: Srikar Dronamraju Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20200612051238.1007764-8-nathanl@linux.ibm.com --- arch/powerpc/mm/numa.c | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/arch/powerpc/mm/numa.c b/arch/powerpc/mm/numa.c index 6207297490a81..8415481a7f137 100644 --- a/arch/powerpc/mm/numa.c +++ b/arch/powerpc/mm/numa.c @@ -1414,17 +1414,6 @@ int arch_update_cpu_topology(void) return numa_update_cpu_topology(true); } -static void topology_work_fn(struct work_struct *work) -{ - rebuild_sched_domains(); -} -static DECLARE_WORK(topology_work, topology_work_fn); - -static void topology_schedule_update(void) -{ - schedule_work(&topology_work); -} - /* * Start polling for associativity changes. */ @@ -1450,9 +1439,6 @@ static int topology_update_init(void) { start_topology_update(); - if (vphn_enabled) - topology_schedule_update(); - topology_inited = 1; return 0; } -- GitLab From 9fb8b5fd1bf782a8257506ad5198237f4124d556 Mon Sep 17 00:00:00 2001 From: Nathan Lynch Date: Fri, 12 Jun 2020 00:12:28 -0500 Subject: [PATCH 0469/1476] powerpc/numa: remove vphn_enabled and prrn_enabled internal flags These flags are always zero now; remove them and suitably adjust the remaining references to them. Signed-off-by: Nathan Lynch Reviewed-by: Srikar Dronamraju Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20200612051238.1007764-9-nathanl@linux.ibm.com --- arch/powerpc/mm/numa.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/arch/powerpc/mm/numa.c b/arch/powerpc/mm/numa.c index 8415481a7f137..8749d7f2b1a63 100644 --- a/arch/powerpc/mm/numa.c +++ b/arch/powerpc/mm/numa.c @@ -1130,8 +1130,6 @@ struct topology_update_data { }; static cpumask_t cpu_associativity_changes_mask; -static const int vphn_enabled; -static const int prrn_enabled; static int topology_inited; /* @@ -1292,7 +1290,7 @@ int numa_update_cpu_topology(bool cpus_locked) struct device *dev; int weight, new_nid, i = 0; - if (!prrn_enabled && !vphn_enabled && topology_inited) + if (topology_inited) return 0; weight = cpumask_weight(&cpu_associativity_changes_mask); @@ -1432,7 +1430,7 @@ int stop_topology_update(void) int prrn_is_enabled(void) { - return prrn_enabled; + return 0; } static int topology_update_init(void) -- GitLab From 893ec6461f46c91487d914e6d467d2e804b9a883 Mon Sep 17 00:00:00 2001 From: Nathan Lynch Date: Fri, 12 Jun 2020 00:12:29 -0500 Subject: [PATCH 0470/1476] powerpc/numa: stub out numa_update_cpu_topology() Previous changes have removed the code which sets bits in cpu_associativity_changes_mask and thus it is never modifed at runtime. From this we can reason that numa_update_cpu_topology() always returns 0 without doing anything. Remove the body of numa_update_cpu_topology() and remove all code which becomes unreachable as a result. Signed-off-by: Nathan Lynch Reviewed-by: Srikar Dronamraju Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20200612051238.1007764-10-nathanl@linux.ibm.com --- arch/powerpc/mm/numa.c | 193 +---------------------------------------- 1 file changed, 1 insertion(+), 192 deletions(-) diff --git a/arch/powerpc/mm/numa.c b/arch/powerpc/mm/numa.c index 8749d7f2b1a63..b220e5b60140f 100644 --- a/arch/powerpc/mm/numa.c +++ b/arch/powerpc/mm/numa.c @@ -1122,14 +1122,6 @@ u64 memory_hotplug_max(void) /* Virtual Processor Home Node (VPHN) support */ #ifdef CONFIG_PPC_SPLPAR -struct topology_update_data { - struct topology_update_data *next; - unsigned int cpu; - int old_nid; - int new_nid; -}; - -static cpumask_t cpu_associativity_changes_mask; static int topology_inited; /* @@ -1219,192 +1211,9 @@ int find_and_online_cpu_nid(int cpu) return new_nid; } -/* - * Update the CPU maps and sysfs entries for a single CPU when its NUMA - * characteristics change. This function doesn't perform any locking and is - * only safe to call from stop_machine(). - */ -static int update_cpu_topology(void *data) -{ - struct topology_update_data *update; - unsigned long cpu; - - if (!data) - return -EINVAL; - - cpu = smp_processor_id(); - - for (update = data; update; update = update->next) { - int new_nid = update->new_nid; - if (cpu != update->cpu) - continue; - - unmap_cpu_from_node(cpu); - map_cpu_to_node(cpu, new_nid); - set_cpu_numa_node(cpu, new_nid); - set_cpu_numa_mem(cpu, local_memory_node(new_nid)); - vdso_getcpu_init(); - } - - return 0; -} - -static int update_lookup_table(void *data) -{ - struct topology_update_data *update; - - if (!data) - return -EINVAL; - - /* - * Upon topology update, the numa-cpu lookup table needs to be updated - * for all threads in the core, including offline CPUs, to ensure that - * future hotplug operations respect the cpu-to-node associativity - * properly. - */ - for (update = data; update; update = update->next) { - int nid, base, j; - - nid = update->new_nid; - base = cpu_first_thread_sibling(update->cpu); - - for (j = 0; j < threads_per_core; j++) { - update_numa_cpu_lookup_table(base + j, nid); - } - } - - return 0; -} - -/* - * Update the node maps and sysfs entries for each cpu whose home node - * has changed. Returns 1 when the topology has changed, and 0 otherwise. - * - * cpus_locked says whether we already hold cpu_hotplug_lock. - */ int numa_update_cpu_topology(bool cpus_locked) { - unsigned int cpu, sibling, changed = 0; - struct topology_update_data *updates, *ud; - cpumask_t updated_cpus; - struct device *dev; - int weight, new_nid, i = 0; - - if (topology_inited) - return 0; - - weight = cpumask_weight(&cpu_associativity_changes_mask); - if (!weight) - return 0; - - updates = kcalloc(weight, sizeof(*updates), GFP_KERNEL); - if (!updates) - return 0; - - cpumask_clear(&updated_cpus); - - for_each_cpu(cpu, &cpu_associativity_changes_mask) { - /* - * If siblings aren't flagged for changes, updates list - * will be too short. Skip on this update and set for next - * update. - */ - if (!cpumask_subset(cpu_sibling_mask(cpu), - &cpu_associativity_changes_mask)) { - pr_info("Sibling bits not set for associativity " - "change, cpu%d\n", cpu); - cpumask_or(&cpu_associativity_changes_mask, - &cpu_associativity_changes_mask, - cpu_sibling_mask(cpu)); - cpu = cpu_last_thread_sibling(cpu); - continue; - } - - new_nid = find_and_online_cpu_nid(cpu); - - if (new_nid == numa_cpu_lookup_table[cpu]) { - cpumask_andnot(&cpu_associativity_changes_mask, - &cpu_associativity_changes_mask, - cpu_sibling_mask(cpu)); - dbg("Assoc chg gives same node %d for cpu%d\n", - new_nid, cpu); - cpu = cpu_last_thread_sibling(cpu); - continue; - } - - for_each_cpu(sibling, cpu_sibling_mask(cpu)) { - ud = &updates[i++]; - ud->next = &updates[i]; - ud->cpu = sibling; - ud->new_nid = new_nid; - ud->old_nid = numa_cpu_lookup_table[sibling]; - cpumask_set_cpu(sibling, &updated_cpus); - } - cpu = cpu_last_thread_sibling(cpu); - } - - /* - * Prevent processing of 'updates' from overflowing array - * where last entry filled in a 'next' pointer. - */ - if (i) - updates[i-1].next = NULL; - - pr_debug("Topology update for the following CPUs:\n"); - if (cpumask_weight(&updated_cpus)) { - for (ud = &updates[0]; ud; ud = ud->next) { - pr_debug("cpu %d moving from node %d " - "to %d\n", ud->cpu, - ud->old_nid, ud->new_nid); - } - } - - /* - * In cases where we have nothing to update (because the updates list - * is too short or because the new topology is same as the old one), - * skip invoking update_cpu_topology() via stop-machine(). This is - * necessary (and not just a fast-path optimization) since stop-machine - * can end up electing a random CPU to run update_cpu_topology(), and - * thus trick us into setting up incorrect cpu-node mappings (since - * 'updates' is kzalloc()'ed). - * - * And for the similar reason, we will skip all the following updating. - */ - if (!cpumask_weight(&updated_cpus)) - goto out; - - if (cpus_locked) - stop_machine_cpuslocked(update_cpu_topology, &updates[0], - &updated_cpus); - else - stop_machine(update_cpu_topology, &updates[0], &updated_cpus); - - /* - * Update the numa-cpu lookup table with the new mappings, even for - * offline CPUs. It is best to perform this update from the stop- - * machine context. - */ - if (cpus_locked) - stop_machine_cpuslocked(update_lookup_table, &updates[0], - cpumask_of(raw_smp_processor_id())); - else - stop_machine(update_lookup_table, &updates[0], - cpumask_of(raw_smp_processor_id())); - - for (ud = &updates[0]; ud; ud = ud->next) { - unregister_cpu_under_node(ud->cpu, ud->old_nid); - register_cpu_under_node(ud->cpu, ud->new_nid); - - dev = get_cpu_device(ud->cpu); - if (dev) - kobject_uevent(&dev->kobj, KOBJ_CHANGE); - cpumask_clear_cpu(ud->cpu, &cpu_associativity_changes_mask); - changed = 1; - } - -out: - kfree(updates); - return changed; + return 0; } int arch_update_cpu_topology(void) -- GitLab From b1815aeac7fde2dc3412daf2efaededd21cd58e0 Mon Sep 17 00:00:00 2001 From: Nathan Lynch Date: Fri, 12 Jun 2020 00:12:30 -0500 Subject: [PATCH 0471/1476] powerpc/numa: remove timed_topology_update() timed_topology_update is a no-op now, so remove it and all call sites. Signed-off-by: Nathan Lynch Reviewed-by: Srikar Dronamraju Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20200612051238.1007764-11-nathanl@linux.ibm.com --- arch/powerpc/include/asm/topology.h | 5 ----- arch/powerpc/mm/numa.c | 9 --------- arch/powerpc/platforms/pseries/hotplug-cpu.c | 2 -- 3 files changed, 16 deletions(-) diff --git a/arch/powerpc/include/asm/topology.h b/arch/powerpc/include/asm/topology.h index 2db7ba7897200..379e2cc3789fd 100644 --- a/arch/powerpc/include/asm/topology.h +++ b/arch/powerpc/include/asm/topology.h @@ -97,7 +97,6 @@ extern int start_topology_update(void); extern int stop_topology_update(void); extern int prrn_is_enabled(void); extern int find_and_online_cpu_nid(int cpu); -extern int timed_topology_update(int nsecs); #else static inline int start_topology_update(void) { @@ -115,10 +114,6 @@ static inline int find_and_online_cpu_nid(int cpu) { return 0; } -static inline int timed_topology_update(int nsecs) -{ - return 0; -} #endif /* CONFIG_NUMA && CONFIG_PPC_SPLPAR */ diff --git a/arch/powerpc/mm/numa.c b/arch/powerpc/mm/numa.c index b220e5b60140f..6c579ac3e6796 100644 --- a/arch/powerpc/mm/numa.c +++ b/arch/powerpc/mm/numa.c @@ -1124,14 +1124,6 @@ u64 memory_hotplug_max(void) #ifdef CONFIG_PPC_SPLPAR static int topology_inited; -/* - * Change polling interval for associativity changes. - */ -int timed_topology_update(int nsecs) -{ - return 0; -} - /* * Retrieve the new associativity information for a virtual processor's * home node. @@ -1147,7 +1139,6 @@ static long vphn_get_associativity(unsigned long cpu, switch (rc) { case H_SUCCESS: dbg("VPHN hcall succeeded. Reset polling...\n"); - timed_topology_update(0); goto out; case H_FUNCTION: diff --git a/arch/powerpc/platforms/pseries/hotplug-cpu.c b/arch/powerpc/platforms/pseries/hotplug-cpu.c index d4b346355bb9e..dbfabb185eb52 100644 --- a/arch/powerpc/platforms/pseries/hotplug-cpu.c +++ b/arch/powerpc/platforms/pseries/hotplug-cpu.c @@ -263,7 +263,6 @@ static int dlpar_offline_cpu(struct device_node *dn) break; cpu_maps_update_done(); - timed_topology_update(1); rc = device_offline(get_cpu_device(cpu)); if (rc) goto out; @@ -302,7 +301,6 @@ static int dlpar_online_cpu(struct device_node *dn) if (get_hard_smp_processor_id(cpu) != thread) continue; cpu_maps_update_done(); - timed_topology_update(1); find_and_online_cpu_nid(cpu); rc = device_online(get_cpu_device(cpu)); if (rc) { -- GitLab From 1835303e5690cbeef2c07a9a5416045475ddaa13 Mon Sep 17 00:00:00 2001 From: Nathan Lynch Date: Fri, 12 Jun 2020 00:12:31 -0500 Subject: [PATCH 0472/1476] powerpc/numa: remove start/stop_topology_update() These APIs have become no-ops, so remove them and all call sites. Signed-off-by: Nathan Lynch Reviewed-by: Srikar Dronamraju Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20200612051238.1007764-12-nathanl@linux.ibm.com --- arch/powerpc/include/asm/topology.h | 10 ---------- arch/powerpc/mm/numa.c | 20 -------------------- arch/powerpc/platforms/pseries/mobility.c | 4 ---- arch/powerpc/platforms/pseries/suspend.c | 5 +---- 4 files changed, 1 insertion(+), 38 deletions(-) diff --git a/arch/powerpc/include/asm/topology.h b/arch/powerpc/include/asm/topology.h index 379e2cc3789fd..537c638582ebf 100644 --- a/arch/powerpc/include/asm/topology.h +++ b/arch/powerpc/include/asm/topology.h @@ -93,19 +93,9 @@ static inline int cpu_distance(__be32 *cpu1_assoc, __be32 *cpu2_assoc) #endif /* CONFIG_NUMA */ #if defined(CONFIG_NUMA) && defined(CONFIG_PPC_SPLPAR) -extern int start_topology_update(void); -extern int stop_topology_update(void); extern int prrn_is_enabled(void); extern int find_and_online_cpu_nid(int cpu); #else -static inline int start_topology_update(void) -{ - return 0; -} -static inline int stop_topology_update(void) -{ - return 0; -} static inline int prrn_is_enabled(void) { return 0; diff --git a/arch/powerpc/mm/numa.c b/arch/powerpc/mm/numa.c index 6c579ac3e6796..dec7ce3b5e673 100644 --- a/arch/powerpc/mm/numa.c +++ b/arch/powerpc/mm/numa.c @@ -1157,8 +1157,6 @@ static long vphn_get_associativity(unsigned long cpu, , rc); break; } - - stop_topology_update(); out: return rc; } @@ -1212,22 +1210,6 @@ int arch_update_cpu_topology(void) return numa_update_cpu_topology(true); } -/* - * Start polling for associativity changes. - */ -int start_topology_update(void) -{ - return 0; -} - -/* - * Disable polling for VPHN associativity changes. - */ -int stop_topology_update(void) -{ - return 0; -} - int prrn_is_enabled(void) { return 0; @@ -1235,8 +1217,6 @@ int prrn_is_enabled(void) static int topology_update_init(void) { - start_topology_update(); - topology_inited = 1; return 0; } diff --git a/arch/powerpc/platforms/pseries/mobility.c b/arch/powerpc/platforms/pseries/mobility.c index 10d9829977363..c0b09f6f0ae3a 100644 --- a/arch/powerpc/platforms/pseries/mobility.c +++ b/arch/powerpc/platforms/pseries/mobility.c @@ -388,8 +388,6 @@ static ssize_t migration_store(struct class *class, if (rc) return rc; - stop_topology_update(); - do { rc = rtas_ibm_suspend_me(streamid); if (rc == -EAGAIN) @@ -401,8 +399,6 @@ static ssize_t migration_store(struct class *class, post_mobility_fixup(); - start_topology_update(); - return count; } diff --git a/arch/powerpc/platforms/pseries/suspend.c b/arch/powerpc/platforms/pseries/suspend.c index f789693f61f40..81e0ac58d6204 100644 --- a/arch/powerpc/platforms/pseries/suspend.c +++ b/arch/powerpc/platforms/pseries/suspend.c @@ -145,11 +145,8 @@ static ssize_t store_hibernate(struct device *dev, ssleep(1); } while (rc == -EAGAIN); - if (!rc) { - stop_topology_update(); + if (!rc) rc = pm_suspend(PM_SUSPEND_MEM); - start_topology_update(); - } stream_id = 0; -- GitLab From 91713ac377859893a7798999cb2e3a388d8ae710 Mon Sep 17 00:00:00 2001 From: Nathan Lynch Date: Fri, 12 Jun 2020 00:12:32 -0500 Subject: [PATCH 0473/1476] powerpc/rtasd: simplify handle_rtas_event(), emit message on events prrn_is_enabled() always returns false/0, so handle_rtas_event() can be simplified and some dead code can be removed. Use machine_is() instead of #ifdef to run this code only on pseries, and add an informational ratelimited message that we are ignoring the events. PRRN events are relatively rare in normal operation and usually arise from operator-initiated actions such as a DPO (Dynamic Platform Optimizer) run. Eventually we do want to consume these events and update the device tree, but that needs more care to be safe vs LPM and DLPAR. Signed-off-by: Nathan Lynch Reviewed-by: Srikar Dronamraju Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20200612051238.1007764-13-nathanl@linux.ibm.com --- arch/powerpc/kernel/rtasd.c | 28 +++------------------------- 1 file changed, 3 insertions(+), 25 deletions(-) diff --git a/arch/powerpc/kernel/rtasd.c b/arch/powerpc/kernel/rtasd.c index 89b798f8f6564..8561dfb33f241 100644 --- a/arch/powerpc/kernel/rtasd.c +++ b/arch/powerpc/kernel/rtasd.c @@ -273,37 +273,15 @@ void pSeries_log_error(char *buf, unsigned int err_type, int fatal) } } -#ifdef CONFIG_PPC_PSERIES -static void handle_prrn_event(s32 scope) -{ - /* - * For PRRN, we must pass the negative of the scope value in - * the RTAS event. - */ - pseries_devicetree_update(-scope); - numa_update_cpu_topology(false); -} - static void handle_rtas_event(const struct rtas_error_log *log) { - if (rtas_error_type(log) != RTAS_TYPE_PRRN || !prrn_is_enabled()) + if (!machine_is(pseries)) return; - /* For PRRN Events the extended log length is used to denote - * the scope for calling rtas update-nodes. - */ - handle_prrn_event(rtas_error_extended_log_length(log)); + if (rtas_error_type(log) == RTAS_TYPE_PRRN) + pr_info_ratelimited("Platform resource reassignment ignored.\n"); } -#else - -static void handle_rtas_event(const struct rtas_error_log *log) -{ - return; -} - -#endif - static int rtas_log_open(struct inode * inode, struct file * file) { return 0; -- GitLab From 042ef7cc43f4571d8cbe44a7c735ab6622809142 Mon Sep 17 00:00:00 2001 From: Nathan Lynch Date: Fri, 12 Jun 2020 00:12:33 -0500 Subject: [PATCH 0474/1476] powerpc/numa: remove prrn_is_enabled() All users of this prrn_is_enabled() are gone; remove it. Signed-off-by: Nathan Lynch Reviewed-by: Srikar Dronamraju Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20200612051238.1007764-14-nathanl@linux.ibm.com --- arch/powerpc/include/asm/topology.h | 5 ----- arch/powerpc/mm/numa.c | 5 ----- 2 files changed, 10 deletions(-) diff --git a/arch/powerpc/include/asm/topology.h b/arch/powerpc/include/asm/topology.h index 537c638582ebf..658aad65912b6 100644 --- a/arch/powerpc/include/asm/topology.h +++ b/arch/powerpc/include/asm/topology.h @@ -93,13 +93,8 @@ static inline int cpu_distance(__be32 *cpu1_assoc, __be32 *cpu2_assoc) #endif /* CONFIG_NUMA */ #if defined(CONFIG_NUMA) && defined(CONFIG_PPC_SPLPAR) -extern int prrn_is_enabled(void); extern int find_and_online_cpu_nid(int cpu); #else -static inline int prrn_is_enabled(void) -{ - return 0; -} static inline int find_and_online_cpu_nid(int cpu) { return 0; diff --git a/arch/powerpc/mm/numa.c b/arch/powerpc/mm/numa.c index dec7ce3b5e673..26fcc947dd2d7 100644 --- a/arch/powerpc/mm/numa.c +++ b/arch/powerpc/mm/numa.c @@ -1210,11 +1210,6 @@ int arch_update_cpu_topology(void) return numa_update_cpu_topology(true); } -int prrn_is_enabled(void) -{ - return 0; -} - static int topology_update_init(void) { topology_inited = 1; -- GitLab From cdf082c4570f186d608aca688f2cc872b014558a Mon Sep 17 00:00:00 2001 From: Nathan Lynch Date: Fri, 12 Jun 2020 00:12:34 -0500 Subject: [PATCH 0475/1476] powerpc/numa: remove arch_update_cpu_topology Since arch_update_cpu_topology() doesn't do anything on powerpc now, remove it and associated dead code. Signed-off-by: Nathan Lynch Reviewed-by: Srikar Dronamraju Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20200612051238.1007764-15-nathanl@linux.ibm.com --- arch/powerpc/include/asm/topology.h | 6 ------ arch/powerpc/mm/numa.c | 10 ---------- 2 files changed, 16 deletions(-) diff --git a/arch/powerpc/include/asm/topology.h b/arch/powerpc/include/asm/topology.h index 658aad65912b6..b2c346c5e16f8 100644 --- a/arch/powerpc/include/asm/topology.h +++ b/arch/powerpc/include/asm/topology.h @@ -43,7 +43,6 @@ extern void __init dump_numa_cpu_topology(void); extern int sysfs_add_device_to_node(struct device *dev, int nid); extern void sysfs_remove_device_from_node(struct device *dev, int nid); -extern int numa_update_cpu_topology(bool cpus_locked); static inline void update_numa_cpu_lookup_table(unsigned int cpu, int node) { @@ -78,11 +77,6 @@ static inline void sysfs_remove_device_from_node(struct device *dev, { } -static inline int numa_update_cpu_topology(bool cpus_locked) -{ - return 0; -} - static inline void update_numa_cpu_lookup_table(unsigned int cpu, int node) {} static inline int cpu_distance(__be32 *cpu1_assoc, __be32 *cpu2_assoc) diff --git a/arch/powerpc/mm/numa.c b/arch/powerpc/mm/numa.c index 26fcc947dd2d7..e437a9ac49564 100644 --- a/arch/powerpc/mm/numa.c +++ b/arch/powerpc/mm/numa.c @@ -1200,16 +1200,6 @@ int find_and_online_cpu_nid(int cpu) return new_nid; } -int numa_update_cpu_topology(bool cpus_locked) -{ - return 0; -} - -int arch_update_cpu_topology(void) -{ - return numa_update_cpu_topology(true); -} - static int topology_update_init(void) { topology_inited = 1; -- GitLab From bb7c3d36e3b18aa02d34358ae75e1b91f69a968b Mon Sep 17 00:00:00 2001 From: Nathan Lynch Date: Fri, 12 Jun 2020 00:12:35 -0500 Subject: [PATCH 0476/1476] powerpc/pseries: remove prrn special case from DT update path pseries_devicetree_update() is no longer called with PRRN_SCOPE. The purpose of prrn_update_node() was to remove and then add back a LMB whose NUMA assignment had changed. This has never been reliable, and this codepath has been default-disabled for several releases. Remove prrn_update_node(). Signed-off-by: Nathan Lynch Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20200612051238.1007764-16-nathanl@linux.ibm.com --- arch/powerpc/platforms/pseries/mobility.c | 27 ----------------------- 1 file changed, 27 deletions(-) diff --git a/arch/powerpc/platforms/pseries/mobility.c b/arch/powerpc/platforms/pseries/mobility.c index c0b09f6f0ae3a..78cd772a579b0 100644 --- a/arch/powerpc/platforms/pseries/mobility.c +++ b/arch/powerpc/platforms/pseries/mobility.c @@ -244,29 +244,6 @@ static int add_dt_node(__be32 parent_phandle, __be32 drc_index) return rc; } -static void prrn_update_node(__be32 phandle) -{ - struct pseries_hp_errorlog hp_elog; - struct device_node *dn; - - /* - * If a node is found from a the given phandle, the phandle does not - * represent the drc index of an LMB and we can ignore. - */ - dn = of_find_node_by_phandle(be32_to_cpu(phandle)); - if (dn) { - of_node_put(dn); - return; - } - - hp_elog.resource = PSERIES_HP_ELOG_RESOURCE_MEM; - hp_elog.action = PSERIES_HP_ELOG_ACTION_READD; - hp_elog.id_type = PSERIES_HP_ELOG_ID_DRC_INDEX; - hp_elog._drc_u.drc_index = phandle; - - handle_dlpar_errorlog(&hp_elog); -} - int pseries_devicetree_update(s32 scope) { char *rtas_buf; @@ -305,10 +282,6 @@ int pseries_devicetree_update(s32 scope) break; case UPDATE_DT_NODE: update_dt_node(phandle, scope); - - if (scope == PRRN_SCOPE) - prrn_update_node(phandle); - break; case ADD_DT_NODE: drc_index = *data++; -- GitLab From 4abe60c6448bf1dba48689450ad1348e5fc6f7b7 Mon Sep 17 00:00:00 2001 From: Nathan Lynch Date: Fri, 12 Jun 2020 00:12:36 -0500 Subject: [PATCH 0477/1476] powerpc/pseries: remove memory "re-add" implementation dlpar_memory() no longer has any callers which pass PSERIES_HP_ELOG_ACTION_READD. Remove this case and the corresponding unreachable code. Signed-off-by: Nathan Lynch Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20200612051238.1007764-17-nathanl@linux.ibm.com --- arch/powerpc/include/asm/rtas.h | 1 - .../platforms/pseries/hotplug-memory.c | 42 ------------------- 2 files changed, 43 deletions(-) diff --git a/arch/powerpc/include/asm/rtas.h b/arch/powerpc/include/asm/rtas.h index 0107d724e9daa..55f9a154c95d8 100644 --- a/arch/powerpc/include/asm/rtas.h +++ b/arch/powerpc/include/asm/rtas.h @@ -215,7 +215,6 @@ inline uint16_t pseries_errorlog_length(struct pseries_errorlog *sect) #define PSERIES_HP_ELOG_ACTION_ADD 1 #define PSERIES_HP_ELOG_ACTION_REMOVE 2 -#define PSERIES_HP_ELOG_ACTION_READD 3 #define PSERIES_HP_ELOG_ID_DRC_NAME 1 #define PSERIES_HP_ELOG_ID_DRC_INDEX 2 diff --git a/arch/powerpc/platforms/pseries/hotplug-memory.c b/arch/powerpc/platforms/pseries/hotplug-memory.c index 5ace2f9a277e9..67ece3ac9ac29 100644 --- a/arch/powerpc/platforms/pseries/hotplug-memory.c +++ b/arch/powerpc/platforms/pseries/hotplug-memory.c @@ -487,40 +487,6 @@ static int dlpar_memory_remove_by_index(u32 drc_index) return rc; } -static int dlpar_memory_readd_by_index(u32 drc_index) -{ - struct drmem_lmb *lmb; - int lmb_found; - int rc; - - pr_info("Attempting to update LMB, drc index %x\n", drc_index); - - lmb_found = 0; - for_each_drmem_lmb(lmb) { - if (lmb->drc_index == drc_index) { - lmb_found = 1; - rc = dlpar_remove_lmb(lmb); - if (!rc) { - rc = dlpar_add_lmb(lmb); - if (rc) - dlpar_release_drc(lmb->drc_index); - } - break; - } - } - - if (!lmb_found) - rc = -EINVAL; - - if (rc) - pr_info("Failed to update memory at %llx\n", - lmb->base_addr); - else - pr_info("Memory at %llx was updated\n", lmb->base_addr); - - return rc; -} - static int dlpar_memory_remove_by_ic(u32 lmbs_to_remove, u32 drc_index) { struct drmem_lmb *lmb, *start_lmb, *end_lmb; @@ -617,10 +583,6 @@ static int dlpar_memory_remove_by_index(u32 drc_index) { return -EOPNOTSUPP; } -static int dlpar_memory_readd_by_index(u32 drc_index) -{ - return -EOPNOTSUPP; -} static int dlpar_memory_remove_by_ic(u32 lmbs_to_remove, u32 drc_index) { @@ -902,10 +864,6 @@ int dlpar_memory(struct pseries_hp_errorlog *hp_elog) break; } - break; - case PSERIES_HP_ELOG_ACTION_READD: - drc_index = hp_elog->_drc_u.drc_index; - rc = dlpar_memory_readd_by_index(drc_index); break; default: pr_err("Invalid action (%d) specified\n", hp_elog->action); -- GitLab From 38c392cef19019457ddcfb197ff3d9c5267698e6 Mon Sep 17 00:00:00 2001 From: Nathan Lynch Date: Fri, 12 Jun 2020 00:12:37 -0500 Subject: [PATCH 0478/1476] powerpc/pseries: remove dlpar_cpu_readd() dlpar_cpu_readd() is unused now. Signed-off-by: Nathan Lynch Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20200612051238.1007764-18-nathanl@linux.ibm.com --- arch/powerpc/include/asm/topology.h | 1 - arch/powerpc/platforms/pseries/hotplug-cpu.c | 19 ------------------- 2 files changed, 20 deletions(-) diff --git a/arch/powerpc/include/asm/topology.h b/arch/powerpc/include/asm/topology.h index b2c346c5e16f8..f0b6300e7dd33 100644 --- a/arch/powerpc/include/asm/topology.h +++ b/arch/powerpc/include/asm/topology.h @@ -115,7 +115,6 @@ int get_physical_package_id(int cpu); #define topology_core_cpumask(cpu) (per_cpu(cpu_core_map, cpu)) #define topology_core_id(cpu) (cpu_to_core_id(cpu)) -int dlpar_cpu_readd(int cpu); #endif #endif diff --git a/arch/powerpc/platforms/pseries/hotplug-cpu.c b/arch/powerpc/platforms/pseries/hotplug-cpu.c index dbfabb185eb52..4bad7a83addc1 100644 --- a/arch/powerpc/platforms/pseries/hotplug-cpu.c +++ b/arch/powerpc/platforms/pseries/hotplug-cpu.c @@ -779,25 +779,6 @@ static int dlpar_cpu_add_by_count(u32 cpus_to_add) return rc; } -int dlpar_cpu_readd(int cpu) -{ - struct device_node *dn; - struct device *dev; - u32 drc_index; - int rc; - - dev = get_cpu_device(cpu); - dn = dev->of_node; - - rc = of_property_read_u32(dn, "ibm,my-drc-index", &drc_index); - - rc = dlpar_cpu_remove_by_index(drc_index); - if (!rc) - rc = dlpar_cpu_add(drc_index); - - return rc; -} - int dlpar_cpu(struct pseries_hp_errorlog *hp_elog) { u32 count, drc_index; -- GitLab From e978a3ccaa714b5ff125857d2cbecbb6fdf6c094 Mon Sep 17 00:00:00 2001 From: Nathan Lynch Date: Fri, 12 Jun 2020 00:12:38 -0500 Subject: [PATCH 0479/1476] powerpc/pseries: remove obsolete memory hotplug DT notifier code pseries_update_drconf_memory() runs from a DT notifier in response to an update to the ibm,dynamic-memory property of the /ibm,dynamic-reconfiguration-memory node. This property is an older less compact format than the ibm,dynamic-memory-v2 property used in most currently supported firmwares. There has never been an equivalent function for the v2 property. pseries_update_drconf_memory() compares the 'assigned' flag for each LMB in the old vs new properties and adds or removes the block accordingly. However it appears to be of no actual utility: * Partition suspension and PRRNs are specified only to change LMBs' NUMA affinity information. This notifier should be a no-op for those scenarios since the assigned flags should not change. * The memory hotplug/DLPAR path has a hack which short-circuits execution of the notifier: dlpar_memory() ... rtas_hp_event = true; drmem_update_dt() of_update_property() pseries_memory_notifier() pseries_update_drconf_memory() if (rtas_hp_event) return; So this code only makes sense as a relic of the time when more of the DLPAR workflow took place in user space. I don't see a purpose for it now. Signed-off-by: Nathan Lynch Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20200612051238.1007764-19-nathanl@linux.ibm.com --- .../platforms/pseries/hotplug-memory.c | 65 +------------------ 1 file changed, 1 insertion(+), 64 deletions(-) diff --git a/arch/powerpc/platforms/pseries/hotplug-memory.c b/arch/powerpc/platforms/pseries/hotplug-memory.c index 67ece3ac9ac29..73a5dcd977e11 100644 --- a/arch/powerpc/platforms/pseries/hotplug-memory.c +++ b/arch/powerpc/platforms/pseries/hotplug-memory.c @@ -22,8 +22,6 @@ #include #include "pseries.h" -static bool rtas_hp_event; - unsigned long pseries_memory_block_size(void) { struct device_node *np; @@ -871,11 +869,8 @@ int dlpar_memory(struct pseries_hp_errorlog *hp_elog) break; } - if (!rc) { - rtas_hp_event = true; + if (!rc) rc = drmem_update_dt(); - rtas_hp_event = false; - } unlock_device_hotplug(); return rc; @@ -911,60 +906,6 @@ static int pseries_add_mem_node(struct device_node *np) return (ret < 0) ? -EINVAL : 0; } -static int pseries_update_drconf_memory(struct of_reconfig_data *pr) -{ - struct of_drconf_cell_v1 *new_drmem, *old_drmem; - unsigned long memblock_size; - u32 entries; - __be32 *p; - int i, rc = -EINVAL; - - if (rtas_hp_event) - return 0; - - memblock_size = pseries_memory_block_size(); - if (!memblock_size) - return -EINVAL; - - if (!pr->old_prop) - return 0; - - p = (__be32 *) pr->old_prop->value; - if (!p) - return -EINVAL; - - /* The first int of the property is the number of lmb's described - * by the property. This is followed by an array of of_drconf_cell - * entries. Get the number of entries and skip to the array of - * of_drconf_cell's. - */ - entries = be32_to_cpu(*p++); - old_drmem = (struct of_drconf_cell_v1 *)p; - - p = (__be32 *)pr->prop->value; - p++; - new_drmem = (struct of_drconf_cell_v1 *)p; - - for (i = 0; i < entries; i++) { - if ((be32_to_cpu(old_drmem[i].flags) & DRCONF_MEM_ASSIGNED) && - (!(be32_to_cpu(new_drmem[i].flags) & DRCONF_MEM_ASSIGNED))) { - rc = pseries_remove_memblock( - be64_to_cpu(old_drmem[i].base_addr), - memblock_size); - break; - } else if ((!(be32_to_cpu(old_drmem[i].flags) & - DRCONF_MEM_ASSIGNED)) && - (be32_to_cpu(new_drmem[i].flags) & - DRCONF_MEM_ASSIGNED)) { - rc = memblock_add(be64_to_cpu(old_drmem[i].base_addr), - memblock_size); - rc = (rc < 0) ? -EINVAL : 0; - break; - } - } - return rc; -} - static int pseries_memory_notifier(struct notifier_block *nb, unsigned long action, void *data) { @@ -978,10 +919,6 @@ static int pseries_memory_notifier(struct notifier_block *nb, case OF_RECONFIG_DETACH_NODE: err = pseries_remove_mem_node(rd->dn); break; - case OF_RECONFIG_UPDATE_PROPERTY: - if (!strcmp(rd->prop->name, "ibm,dynamic-memory")) - err = pseries_update_drconf_memory(rd); - break; } return notifier_from_errno(err); } -- GitLab From 1a8f0886a6008c98a926bdeca49f2ef33015a491 Mon Sep 17 00:00:00 2001 From: Kajol Jain Date: Thu, 9 Jul 2020 10:48:35 +0530 Subject: [PATCH 0480/1476] powerpc/perf/hv-24x7: Add cpu hotplug support Patch here adds cpu hotplug functions to hv_24x7 pmu. A new cpuhp_state "CPUHP_AP_PERF_POWERPC_HV_24x7_ONLINE" enum is added. The online callback function updates the cpumask only if its empty. As the primary intention of adding hotplug support is to designate a CPU to make HCALL to collect the counter data. The offline function test and clear corresponding cpu in a cpumask and update cpumask to any other active cpu. Signed-off-by: Kajol Jain Reviewed-by: Gautham R. Shenoy Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20200709051836.723765-2-kjain@linux.ibm.com --- arch/powerpc/perf/hv-24x7.c | 46 +++++++++++++++++++++++++++++++++++++ include/linux/cpuhotplug.h | 1 + 2 files changed, 47 insertions(+) diff --git a/arch/powerpc/perf/hv-24x7.c b/arch/powerpc/perf/hv-24x7.c index db213eb7cb028..93b4700dcf8c1 100644 --- a/arch/powerpc/perf/hv-24x7.c +++ b/arch/powerpc/perf/hv-24x7.c @@ -31,6 +31,8 @@ static int interface_version; /* Whether we have to aggregate result data for some domains. */ static bool aggregate_result_elements; +static cpumask_t hv_24x7_cpumask; + static bool domain_is_valid(unsigned domain) { switch (domain) { @@ -1641,6 +1643,45 @@ static struct pmu h_24x7_pmu = { .capabilities = PERF_PMU_CAP_NO_EXCLUDE, }; +static int ppc_hv_24x7_cpu_online(unsigned int cpu) +{ + if (cpumask_empty(&hv_24x7_cpumask)) + cpumask_set_cpu(cpu, &hv_24x7_cpumask); + + return 0; +} + +static int ppc_hv_24x7_cpu_offline(unsigned int cpu) +{ + int target; + + /* Check if exiting cpu is used for collecting 24x7 events */ + if (!cpumask_test_and_clear_cpu(cpu, &hv_24x7_cpumask)) + return 0; + + /* Find a new cpu to collect 24x7 events */ + target = cpumask_last(cpu_active_mask); + + if (target < 0 || target >= nr_cpu_ids) { + pr_err("hv_24x7: CPU hotplug init failed\n"); + return -1; + } + + /* Migrate 24x7 events to the new target */ + cpumask_set_cpu(target, &hv_24x7_cpumask); + perf_pmu_migrate_context(&h_24x7_pmu, cpu, target); + + return 0; +} + +static int hv_24x7_cpu_hotplug_init(void) +{ + return cpuhp_setup_state(CPUHP_AP_PERF_POWERPC_HV_24x7_ONLINE, + "perf/powerpc/hv_24x7:online", + ppc_hv_24x7_cpu_online, + ppc_hv_24x7_cpu_offline); +} + static int hv_24x7_init(void) { int r; @@ -1685,6 +1726,11 @@ static int hv_24x7_init(void) if (r) return r; + /* init cpuhotplug */ + r = hv_24x7_cpu_hotplug_init(); + if (r) + return r; + r = perf_pmu_register(&h_24x7_pmu, h_24x7_pmu.name, -1); if (r) return r; diff --git a/include/linux/cpuhotplug.h b/include/linux/cpuhotplug.h index 191772d4a4d7d..a2710e654b642 100644 --- a/include/linux/cpuhotplug.h +++ b/include/linux/cpuhotplug.h @@ -181,6 +181,7 @@ enum cpuhp_state { CPUHP_AP_PERF_POWERPC_CORE_IMC_ONLINE, CPUHP_AP_PERF_POWERPC_THREAD_IMC_ONLINE, CPUHP_AP_PERF_POWERPC_TRACE_IMC_ONLINE, + CPUHP_AP_PERF_POWERPC_HV_24x7_ONLINE, CPUHP_AP_WATCHDOG_ONLINE, CPUHP_AP_WORKQUEUE_ONLINE, CPUHP_AP_RCUTREE_ONLINE, -- GitLab From 792f73f747b82f6cb191a323e1f5755d33149b50 Mon Sep 17 00:00:00 2001 From: Kajol Jain Date: Thu, 9 Jul 2020 10:48:36 +0530 Subject: [PATCH 0481/1476] powerpc/hv-24x7: Add sysfs files inside hv-24x7 device to show cpumask Patch here adds a cpumask attr to hv_24x7 pmu along with ABI documentation. Primary use to expose the cpumask is for the perf tool which has the capability to parse the driver sysfs folder and understand the cpumask file. Having cpumask file will reduce the number of perf command line parameters (will avoid "-C" option in the perf tool command line). It can also notify the user which is the current cpu used to retrieve the counter data. command:# cat /sys/devices/hv_24x7/interface/cpumask 0 Signed-off-by: Kajol Jain Reviewed-by: Madhavan Srinivasan Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20200709051836.723765-3-kjain@linux.ibm.com --- .../ABI/testing/sysfs-bus-event_source-devices-hv_24x7 | 7 +++++++ arch/powerpc/perf/hv-24x7.c | 8 ++++++++ 2 files changed, 15 insertions(+) diff --git a/Documentation/ABI/testing/sysfs-bus-event_source-devices-hv_24x7 b/Documentation/ABI/testing/sysfs-bus-event_source-devices-hv_24x7 index e8698afcd952a..f7e32f218f737 100644 --- a/Documentation/ABI/testing/sysfs-bus-event_source-devices-hv_24x7 +++ b/Documentation/ABI/testing/sysfs-bus-event_source-devices-hv_24x7 @@ -43,6 +43,13 @@ Description: read only This sysfs interface exposes the number of cores per chip present in the system. +What: /sys/devices/hv_24x7/interface/cpumask +Date: July 2020 +Contact: Linux on PowerPC Developer List +Description: read only + This sysfs file exposes the cpumask which is designated to make + HCALLs to retrieve hv-24x7 pmu event counter data. + What: /sys/bus/event_source/devices/hv_24x7/event_descs/ Date: February 2014 Contact: Linux on PowerPC Developer List diff --git a/arch/powerpc/perf/hv-24x7.c b/arch/powerpc/perf/hv-24x7.c index 93b4700dcf8c1..cdb7bfbd157e0 100644 --- a/arch/powerpc/perf/hv-24x7.c +++ b/arch/powerpc/perf/hv-24x7.c @@ -448,6 +448,12 @@ static ssize_t device_show_string(struct device *dev, return sprintf(buf, "%s\n", (char *)d->var); } +static ssize_t cpumask_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return cpumap_print_to_pagebuf(true, buf, &hv_24x7_cpumask); +} + static ssize_t sockets_show(struct device *dev, struct device_attribute *attr, char *buf) { @@ -1115,6 +1121,7 @@ static DEVICE_ATTR_RO(domains); static DEVICE_ATTR_RO(sockets); static DEVICE_ATTR_RO(chipspersocket); static DEVICE_ATTR_RO(coresperchip); +static DEVICE_ATTR_RO(cpumask); static struct bin_attribute *if_bin_attrs[] = { &bin_attr_catalog, @@ -1128,6 +1135,7 @@ static struct attribute *if_attrs[] = { &dev_attr_sockets.attr, &dev_attr_chipspersocket.attr, &dev_attr_coresperchip.attr, + &dev_attr_cpumask.attr, NULL, }; -- GitLab From db551f8cc6a33f79cd2d2a6cfd1903f044e828a8 Mon Sep 17 00:00:00 2001 From: Balamuruhan S Date: Wed, 24 Jun 2020 17:00:33 +0530 Subject: [PATCH 0482/1476] powerpc/ppc-opcode: Introduce PPC_RAW_* macros for base instruction encoding Introduce PPC_RAW_* macros to have all the bare encoding of ppc instructions. Move `VSX_XX*()` and `TMRN()` macros up to reuse it. Signed-off-by: Balamuruhan S Tested-by: Naveen N. Rao Acked-by: Naveen N. Rao [mpe: Add DCBFPS, DCBSTPS, PHWSYNC, PLWSYNC] Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20200624113038.908074-2-bala24@linux.ibm.com --- arch/powerpc/include/asm/ppc-opcode.h | 93 +++++++++++++++++++++++++-- 1 file changed, 86 insertions(+), 7 deletions(-) diff --git a/arch/powerpc/include/asm/ppc-opcode.h b/arch/powerpc/include/asm/ppc-opcode.h index 3ee6fad902bc2..4aac2165da213 100644 --- a/arch/powerpc/include/asm/ppc-opcode.h +++ b/arch/powerpc/include/asm/ppc-opcode.h @@ -437,6 +437,92 @@ #define __PPC_EH(eh) 0 #endif +/* Base instruction encoding */ +#define PPC_RAW_CP_ABORT (PPC_INST_CP_ABORT) +#define PPC_RAW_COPY(a, b) (PPC_INST_COPY | ___PPC_RA(a) | ___PPC_RB(b)) +#define PPC_RAW_DARN(t, l) (PPC_INST_DARN | ___PPC_RT(t) | (((l) & 0x3) << 16)) +#define PPC_RAW_DCBAL(a, b) (PPC_INST_DCBAL | __PPC_RA(a) | __PPC_RB(b)) +#define PPC_RAW_DCBZL(a, b) (PPC_INST_DCBZL | __PPC_RA(a) | __PPC_RB(b)) +#define PPC_RAW_LQARX(t, a, b, eh) (PPC_INST_LQARX | ___PPC_RT(t) | ___PPC_RA(a) | ___PPC_RB(b) | __PPC_EH(eh)) +#define PPC_RAW_LDARX(t, a, b, eh) (PPC_INST_LDARX | ___PPC_RT(t) | ___PPC_RA(a) | ___PPC_RB(b) | __PPC_EH(eh)) +#define PPC_RAW_LWARX(t, a, b, eh) (PPC_INST_LWARX | ___PPC_RT(t) | ___PPC_RA(a) | ___PPC_RB(b) | __PPC_EH(eh)) +#define PPC_RAW_PHWSYNC (PPC_INST_PHWSYNC) +#define PPC_RAW_PLWSYNC (PPC_INST_PLWSYNC) +#define PPC_RAW_STQCX(t, a, b) (PPC_INST_STQCX | ___PPC_RT(t) | ___PPC_RA(a) | ___PPC_RB(b)) +#define PPC_RAW_MADDHD(t, a, b, c) (PPC_INST_MADDHD | ___PPC_RT(t) | ___PPC_RA(a) | ___PPC_RB(b) | ___PPC_RC(c)) +#define PPC_RAW_MADDHDU(t, a, b, c) (PPC_INST_MADDHDU | ___PPC_RT(t) | ___PPC_RA(a) | ___PPC_RB(b) | ___PPC_RC(c)) +#define PPC_RAW_MADDLD(t, a, b, c) (PPC_INST_MADDLD | ___PPC_RT(t) | ___PPC_RA(a) | ___PPC_RB(b) | ___PPC_RC(c)) +#define PPC_RAW_MSGSND(b) (PPC_INST_MSGSND | ___PPC_RB(b)) +#define PPC_RAW_MSGSYNC (PPC_INST_MSGSYNC) +#define PPC_RAW_MSGCLR(b) (PPC_INST_MSGCLR | ___PPC_RB(b)) +#define PPC_RAW_MSGSNDP(b) (PPC_INST_MSGSNDP | ___PPC_RB(b)) +#define PPC_RAW_MSGCLRP(b) (PPC_INST_MSGCLRP | ___PPC_RB(b)) +#define PPC_RAW_PASTE(a, b) (PPC_INST_PASTE | ___PPC_RA(a) | ___PPC_RB(b)) +#define PPC_RAW_POPCNTB(a, s) (PPC_INST_POPCNTB | __PPC_RA(a) | __PPC_RS(s)) +#define PPC_RAW_POPCNTD(a, s) (PPC_INST_POPCNTD | __PPC_RA(a) | __PPC_RS(s)) +#define PPC_RAW_POPCNTW(a, s) (PPC_INST_POPCNTW | __PPC_RA(a) | __PPC_RS(s)) +#define PPC_RAW_RFCI (PPC_INST_RFCI) +#define PPC_RAW_RFDI (PPC_INST_RFDI) +#define PPC_RAW_RFMCI (PPC_INST_RFMCI) +#define PPC_RAW_TLBILX(t, a, b) (PPC_INST_TLBILX | __PPC_T_TLB(t) | __PPC_RA0(a) | __PPC_RB(b)) +#define PPC_RAW_WAIT(w) (PPC_INST_WAIT | __PPC_WC(w)) +#define PPC_RAW_TLBIE(lp, a) (PPC_INST_TLBIE | ___PPC_RB(a) | ___PPC_RS(lp)) +#define PPC_RAW_TLBIE_5(rb, rs, ric, prs, r) \ + (PPC_INST_TLBIE | ___PPC_RB(rb) | ___PPC_RS(rs) | ___PPC_RIC(ric) | ___PPC_PRS(prs) | ___PPC_R(r)) +#define PPC_RAW_TLBIEL(rb, rs, ric, prs, r) \ + (PPC_INST_TLBIEL | ___PPC_RB(rb) | ___PPC_RS(rs) | ___PPC_RIC(ric) | ___PPC_PRS(prs) | ___PPC_R(r)) +#define PPC_RAW_TLBSRX_DOT(a, b) (PPC_INST_TLBSRX_DOT | __PPC_RA0(a) | __PPC_RB(b)) +#define PPC_RAW_TLBIVAX(a, b) (PPC_INST_TLBIVAX | __PPC_RA0(a) | __PPC_RB(b)) +#define PPC_RAW_ERATWE(s, a, w) (PPC_INST_ERATWE | __PPC_RS(s) | __PPC_RA(a) | __PPC_WS(w)) +#define PPC_RAW_ERATRE(s, a, w) (PPC_INST_ERATRE | __PPC_RS(s) | __PPC_RA(a) | __PPC_WS(w)) +#define PPC_RAW_ERATILX(t, a, b) (PPC_INST_ERATILX | __PPC_T_TLB(t) | __PPC_RA0(a) | __PPC_RB(b)) +#define PPC_RAW_ERATIVAX(s, a, b) (PPC_INST_ERATIVAX | __PPC_RS(s) | __PPC_RA0(a) | __PPC_RB(b)) +#define PPC_RAW_ERATSX(t, a, w) (PPC_INST_ERATSX | __PPC_RS(t) | __PPC_RA0(a) | __PPC_RB(b)) +#define PPC_RAW_ERATSX_DOT(t, a, w) (PPC_INST_ERATSX_DOT | __PPC_RS(t) | __PPC_RA0(a) | __PPC_RB(b)) +#define PPC_RAW_SLBFEE_DOT(t, b) (PPC_INST_SLBFEE | __PPC_RT(t) | __PPC_RB(b)) +#define __PPC_RAW_SLBFEE_DOT(t, b) (PPC_INST_SLBFEE | ___PPC_RT(t) | ___PPC_RB(b)) +#define PPC_RAW_ICBT(c, a, b) (PPC_INST_ICBT | __PPC_CT(c) | __PPC_RA0(a) | __PPC_RB(b)) +#define PPC_RAW_LBZCIX(t, a, b) (PPC_INST_LBZCIX | __PPC_RT(t) | __PPC_RA(a) | __PPC_RB(b)) +#define PPC_RAW_STBCIX(s, a, b) (PPC_INST_STBCIX | __PPC_RS(s) | __PPC_RA(a) | __PPC_RB(b)) +#define PPC_RAW_DCBFPS(a, b) (PPC_INST_DCBF | ___PPC_RA(a) | ___PPC_RB(b) | (4 << 21)) +#define PPC_RAW_DCBSTPS(a, b) (PPC_INST_DCBF | ___PPC_RA(a) | ___PPC_RB(b) | (6 << 21)) +/* + * Define what the VSX XX1 form instructions will look like, then add + * the 128 bit load store instructions based on that. + */ +#define VSX_XX1(s, a, b) (__PPC_XS(s) | __PPC_RA(a) | __PPC_RB(b)) +#define VSX_XX3(t, a, b) (__PPC_XT(t) | __PPC_XA(a) | __PPC_XB(b)) +#define PPC_RAW_STXVD2X(s, a, b) (PPC_INST_STXVD2X | VSX_XX1((s), a, b)) +#define PPC_RAW_LXVD2X(s, a, b) (PPC_INST_LXVD2X | VSX_XX1((s), a, b)) +#define PPC_RAW_MFVRD(a, t) (PPC_INST_MFVSRD | VSX_XX1((t) + 32, a, R0)) +#define PPC_RAW_MTVRD(t, a) (PPC_INST_MTVSRD | VSX_XX1((t) + 32, a, R0)) +#define PPC_RAW_VPMSUMW(t, a, b) (PPC_INST_VPMSUMW | VSX_XX3((t), a, b)) +#define PPC_RAW_VPMSUMD(t, a, b) (PPC_INST_VPMSUMD | VSX_XX3((t), a, b)) +#define PPC_RAW_XXLOR(t, a, b) (PPC_INST_XXLOR | VSX_XX3((t), a, b)) +#define PPC_RAW_XXSWAPD(t, a) (PPC_INST_XXSWAPD | VSX_XX3((t), a, a)) +#define PPC_RAW_XVCPSGNDP(t, a, b) ((PPC_INST_XVCPSGNDP | VSX_XX3((t), (a), (b)))) +#define PPC_RAW_VPERMXOR(vrt, vra, vrb, vrc) \ + ((PPC_INST_VPERMXOR | ___PPC_RT(vrt) | ___PPC_RA(vra) | ___PPC_RB(vrb) | (((vrc) & 0x1f) << 6))) +#define PPC_RAW_NAP (PPC_INST_NAP) +#define PPC_RAW_SLEEP (PPC_INST_SLEEP) +#define PPC_RAW_WINKLE (PPC_INST_WINKLE) +#define PPC_RAW_STOP (PPC_INST_STOP) +#define PPC_RAW_CLRBHRB (PPC_INST_CLRBHRB) +#define PPC_RAW_MFBHRBE(r, n) (PPC_INST_BHRBE | __PPC_RT(r) | (((n) & 0x3ff) << 11)) +#define PPC_RAW_TRECHKPT (PPC_INST_TRECHKPT) +#define PPC_RAW_TRECLAIM(r) (PPC_INST_TRECLAIM | __PPC_RA(r)) +#define PPC_RAW_TABORT(r) (PPC_INST_TABORT | __PPC_RA(r)) +#define TMRN(x) ((((x) & 0x1f) << 16) | (((x) & 0x3e0) << 6)) +#define PPC_RAW_MTTMR(tmr, r) (PPC_INST_MTTMR | TMRN(tmr) | ___PPC_RS(r)) +#define PPC_RAW_MFTMR(tmr, r) (PPC_INST_MFTMR | TMRN(tmr) | ___PPC_RT(r)) +#define PPC_RAW_ICSWX(s, a, b) (PPC_INST_ICSWX | ___PPC_RS(s) | ___PPC_RA(a) | ___PPC_RB(b)) +#define PPC_RAW_ICSWEPX(s, a, b) (PPC_INST_ICSWEPX | ___PPC_RS(s) | ___PPC_RA(a) | ___PPC_RB(b)) +#define PPC_RAW_SLBIA(IH) (PPC_INST_SLBIA | (((IH) & 0x7) << 21)) +#define PPC_RAW_VCMPEQUD_RC(vrt, vra, vrb) \ + (PPC_INST_VCMPEQUD | ___PPC_RT(vrt) | ___PPC_RA(vra) | ___PPC_RB(vrb) | __PPC_RC21) +#define PPC_RAW_VCMPEQUB_RC(vrt, vra, vrb) \ + (PPC_INST_VCMPEQUB | ___PPC_RT(vrt) | ___PPC_RA(vra) | ___PPC_RB(vrb) | __PPC_RC21) + /* Deal with instructions that older assemblers aren't aware of */ #define PPC_BCCTR_FLUSH stringify_in_c(.long PPC_INST_BCCTR_FLUSH) #define PPC_CP_ABORT stringify_in_c(.long PPC_INST_CP_ABORT) @@ -547,12 +633,6 @@ #define PPC_PHWSYNC stringify_in_c(.long PPC_INST_PHWSYNC) #define PPC_PLWSYNC stringify_in_c(.long PPC_INST_PLWSYNC) -/* - * Define what the VSX XX1 form instructions will look like, then add - * the 128 bit load store instructions based on that. - */ -#define VSX_XX1(s, a, b) (__PPC_XS(s) | __PPC_RA(a) | __PPC_RB(b)) -#define VSX_XX3(t, a, b) (__PPC_XT(t) | __PPC_XA(a) | __PPC_XB(b)) #define STXVD2X(s, a, b) stringify_in_c(.long PPC_INST_STXVD2X | \ VSX_XX1((s), a, b)) #define LXVD2X(s, a, b) stringify_in_c(.long PPC_INST_LXVD2X | \ @@ -597,7 +677,6 @@ | __PPC_RA(r)) /* book3e thread control instructions */ -#define TMRN(x) ((((x) & 0x1f) << 16) | (((x) & 0x3e0) << 6)) #define MTTMR(tmr, r) stringify_in_c(.long PPC_INST_MTTMR | \ TMRN(tmr) | ___PPC_RS(r)) #define MFTMR(tmr, r) stringify_in_c(.long PPC_INST_MFTMR | \ -- GitLab From 1d33dd84080f4a430bde2fc363d9b70f0a010c19 Mon Sep 17 00:00:00 2001 From: Balamuruhan S Date: Wed, 24 Jun 2020 17:00:34 +0530 Subject: [PATCH 0483/1476] powerpc/ppc-opcode: Move ppc instruction encoding from test_emulate_step Few ppc instructions are encoded in test_emulate_step.c, consolidate them and use it from ppc-opcode.h Signed-off-by: Balamuruhan S Tested-by: Naveen N. Rao Acked-by: Naveen N. Rao Acked-by: Sandipan Das Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20200624113038.908074-3-bala24@linux.ibm.com --- arch/powerpc/include/asm/ppc-opcode.h | 18 +++ arch/powerpc/lib/test_emulate_step.c | 155 ++++++++++---------------- 2 files changed, 74 insertions(+), 99 deletions(-) diff --git a/arch/powerpc/include/asm/ppc-opcode.h b/arch/powerpc/include/asm/ppc-opcode.h index 4aac2165da213..49701d4ea0e1f 100644 --- a/arch/powerpc/include/asm/ppc-opcode.h +++ b/arch/powerpc/include/asm/ppc-opcode.h @@ -76,6 +76,9 @@ #define __REGA0_R30 30 #define __REGA0_R31 31 +#define IMM_L(i) ((uintptr_t)(i) & 0xffff) +#define IMM_DS(i) ((uintptr_t)(i) & 0xfffc) + /* opcode and xopcode for instructions */ #define OP_TRAP 3 #define OP_TRAP_64 2 @@ -522,6 +525,21 @@ (PPC_INST_VCMPEQUD | ___PPC_RT(vrt) | ___PPC_RA(vra) | ___PPC_RB(vrb) | __PPC_RC21) #define PPC_RAW_VCMPEQUB_RC(vrt, vra, vrb) \ (PPC_INST_VCMPEQUB | ___PPC_RT(vrt) | ___PPC_RA(vra) | ___PPC_RB(vrb) | __PPC_RC21) +#define PPC_RAW_LD(r, base, i) (PPC_INST_LD | ___PPC_RT(r) | ___PPC_RA(base) | IMM_DS(i)) +#define PPC_RAW_LWZ(r, base, i) (PPC_INST_LWZ | ___PPC_RT(r) | ___PPC_RA(base) | IMM_L(i)) +#define PPC_RAW_LWZX(t, a, b) (PPC_INST_LWZX | ___PPC_RT(t) | ___PPC_RA(a) | ___PPC_RB(b)) +#define PPC_RAW_STD(r, base, i) (PPC_INST_STD | ___PPC_RS(r) | ___PPC_RA(base) | IMM_DS(i)) +#define PPC_RAW_STDCX(s, a, b) (PPC_INST_STDCX | ___PPC_RS(s) | ___PPC_RA(a) | ___PPC_RB(b)) +#define PPC_RAW_LFSX(t, a, b) (PPC_INST_LFSX | ___PPC_RT(t) | ___PPC_RA(a) | ___PPC_RB(b)) +#define PPC_RAW_STFSX(s, a, b) (PPC_INST_STFSX | ___PPC_RS(s) | ___PPC_RA(a) | ___PPC_RB(b)) +#define PPC_RAW_LFDX(t, a, b) (PPC_INST_LFDX | ___PPC_RT(t) | ___PPC_RA(a) | ___PPC_RB(b)) +#define PPC_RAW_STFDX(s, a, b) (PPC_INST_STFDX | ___PPC_RS(s) | ___PPC_RA(a) | ___PPC_RB(b)) +#define PPC_RAW_LVX(t, a, b) (PPC_INST_LVX | ___PPC_RT(t) | ___PPC_RA(a) | ___PPC_RB(b)) +#define PPC_RAW_STVX(s, a, b) (PPC_INST_STVX | ___PPC_RS(s) | ___PPC_RA(a) | ___PPC_RB(b)) +#define PPC_RAW_ADD(t, a, b) (PPC_INST_ADD | ___PPC_RT(t) | ___PPC_RA(a) | ___PPC_RB(b)) +#define PPC_RAW_ADD_DOT(t, a, b) (PPC_INST_ADD | ___PPC_RT(t) | ___PPC_RA(a) | ___PPC_RB(b) | 0x1) +#define PPC_RAW_ADDC(t, a, b) (PPC_INST_ADDC | ___PPC_RT(t) | ___PPC_RA(a) | ___PPC_RB(b)) +#define PPC_RAW_ADDC_DOT(t, a, b) (PPC_INST_ADDC | ___PPC_RT(t) | ___PPC_RA(a) | ___PPC_RB(b) | 0x1) /* Deal with instructions that older assemblers aren't aware of */ #define PPC_BCCTR_FLUSH stringify_in_c(.long PPC_INST_BCCTR_FLUSH) diff --git a/arch/powerpc/lib/test_emulate_step.c b/arch/powerpc/lib/test_emulate_step.c index 46af80279ebcc..988c734e7370a 100644 --- a/arch/powerpc/lib/test_emulate_step.c +++ b/arch/powerpc/lib/test_emulate_step.c @@ -13,49 +13,6 @@ #include #include -#define IMM_L(i) ((uintptr_t)(i) & 0xffff) -#define IMM_DS(i) ((uintptr_t)(i) & 0xfffc) - -/* - * Defined with TEST_ prefix so it does not conflict with other - * definitions. - */ -#define TEST_LD(r, base, i) ppc_inst(PPC_INST_LD | ___PPC_RT(r) | \ - ___PPC_RA(base) | IMM_DS(i)) -#define TEST_LWZ(r, base, i) ppc_inst(PPC_INST_LWZ | ___PPC_RT(r) | \ - ___PPC_RA(base) | IMM_L(i)) -#define TEST_LWZX(t, a, b) ppc_inst(PPC_INST_LWZX | ___PPC_RT(t) | \ - ___PPC_RA(a) | ___PPC_RB(b)) -#define TEST_STD(r, base, i) ppc_inst(PPC_INST_STD | ___PPC_RS(r) | \ - ___PPC_RA(base) | IMM_DS(i)) -#define TEST_LDARX(t, a, b, eh) ppc_inst(PPC_INST_LDARX | ___PPC_RT(t) | \ - ___PPC_RA(a) | ___PPC_RB(b) | \ - __PPC_EH(eh)) -#define TEST_STDCX(s, a, b) ppc_inst(PPC_INST_STDCX | ___PPC_RS(s) | \ - ___PPC_RA(a) | ___PPC_RB(b)) -#define TEST_LFSX(t, a, b) ppc_inst(PPC_INST_LFSX | ___PPC_RT(t) | \ - ___PPC_RA(a) | ___PPC_RB(b)) -#define TEST_STFSX(s, a, b) ppc_inst(PPC_INST_STFSX | ___PPC_RS(s) | \ - ___PPC_RA(a) | ___PPC_RB(b)) -#define TEST_LFDX(t, a, b) ppc_inst(PPC_INST_LFDX | ___PPC_RT(t) | \ - ___PPC_RA(a) | ___PPC_RB(b)) -#define TEST_STFDX(s, a, b) ppc_inst(PPC_INST_STFDX | ___PPC_RS(s) | \ - ___PPC_RA(a) | ___PPC_RB(b)) -#define TEST_LVX(t, a, b) ppc_inst(PPC_INST_LVX | ___PPC_RT(t) | \ - ___PPC_RA(a) | ___PPC_RB(b)) -#define TEST_STVX(s, a, b) ppc_inst(PPC_INST_STVX | ___PPC_RS(s) | \ - ___PPC_RA(a) | ___PPC_RB(b)) -#define TEST_LXVD2X(s, a, b) ppc_inst(PPC_INST_LXVD2X | VSX_XX1((s), R##a, R##b)) -#define TEST_STXVD2X(s, a, b) ppc_inst(PPC_INST_STXVD2X | VSX_XX1((s), R##a, R##b)) -#define TEST_ADD(t, a, b) ppc_inst(PPC_INST_ADD | ___PPC_RT(t) | \ - ___PPC_RA(a) | ___PPC_RB(b)) -#define TEST_ADD_DOT(t, a, b) ppc_inst(PPC_INST_ADD | ___PPC_RT(t) | \ - ___PPC_RA(a) | ___PPC_RB(b) | 0x1) -#define TEST_ADDC(t, a, b) ppc_inst(PPC_INST_ADDC | ___PPC_RT(t) | \ - ___PPC_RA(a) | ___PPC_RB(b)) -#define TEST_ADDC_DOT(t, a, b) ppc_inst(PPC_INST_ADDC | ___PPC_RT(t) | \ - ___PPC_RA(a) | ___PPC_RB(b) | 0x1) - #define MAX_SUBTESTS 16 #define IGNORE_GPR(n) (0x1UL << (n)) @@ -105,7 +62,7 @@ static void __init test_ld(void) regs.gpr[3] = (unsigned long) &a; /* ld r5, 0(r3) */ - stepped = emulate_step(®s, TEST_LD(5, 3, 0)); + stepped = emulate_step(®s, ppc_inst(PPC_RAW_LD(5, 3, 0))); if (stepped == 1 && regs.gpr[5] == a) show_result("ld", "PASS"); @@ -123,7 +80,7 @@ static void __init test_lwz(void) regs.gpr[3] = (unsigned long) &a; /* lwz r5, 0(r3) */ - stepped = emulate_step(®s, TEST_LWZ(5, 3, 0)); + stepped = emulate_step(®s, ppc_inst(PPC_RAW_LWZ(5, 3, 0))); if (stepped == 1 && regs.gpr[5] == a) show_result("lwz", "PASS"); @@ -143,7 +100,7 @@ static void __init test_lwzx(void) regs.gpr[5] = 0x8765; /* lwzx r5, r3, r4 */ - stepped = emulate_step(®s, TEST_LWZX(5, 3, 4)); + stepped = emulate_step(®s, ppc_inst(PPC_RAW_LWZX(5, 3, 4))); if (stepped == 1 && regs.gpr[5] == a[2]) show_result("lwzx", "PASS"); else @@ -161,7 +118,7 @@ static void __init test_std(void) regs.gpr[5] = 0x5678; /* std r5, 0(r3) */ - stepped = emulate_step(®s, TEST_STD(5, 3, 0)); + stepped = emulate_step(®s, ppc_inst(PPC_RAW_STD(5, 3, 0))); if (stepped == 1 && regs.gpr[5] == a) show_result("std", "PASS"); else @@ -186,7 +143,7 @@ static void __init test_ldarx_stdcx(void) regs.gpr[5] = 0x5678; /* ldarx r5, r3, r4, 0 */ - stepped = emulate_step(®s, TEST_LDARX(5, 3, 4, 0)); + stepped = emulate_step(®s, ppc_inst(PPC_RAW_LDARX(5, 3, 4, 0))); /* * Don't touch 'a' here. Touching 'a' can do Load/store @@ -204,7 +161,7 @@ static void __init test_ldarx_stdcx(void) regs.gpr[5] = 0x9ABC; /* stdcx. r5, r3, r4 */ - stepped = emulate_step(®s, TEST_STDCX(5, 3, 4)); + stepped = emulate_step(®s, ppc_inst(PPC_RAW_STDCX(5, 3, 4))); /* * Two possible scenarios that indicates successful emulation @@ -244,7 +201,7 @@ static void __init test_lfsx_stfsx(void) regs.gpr[4] = 0; /* lfsx frt10, r3, r4 */ - stepped = emulate_step(®s, TEST_LFSX(10, 3, 4)); + stepped = emulate_step(®s, ppc_inst(PPC_RAW_LFSX(10, 3, 4))); if (stepped == 1) show_result("lfsx", "PASS"); @@ -257,7 +214,7 @@ static void __init test_lfsx_stfsx(void) c.a = 678.91; /* stfsx frs10, r3, r4 */ - stepped = emulate_step(®s, TEST_STFSX(10, 3, 4)); + stepped = emulate_step(®s, ppc_inst(PPC_RAW_STFSX(10, 3, 4))); if (stepped == 1 && c.b == cached_b) show_result("stfsx", "PASS"); @@ -287,7 +244,7 @@ static void __init test_lfdx_stfdx(void) regs.gpr[4] = 0; /* lfdx frt10, r3, r4 */ - stepped = emulate_step(®s, TEST_LFDX(10, 3, 4)); + stepped = emulate_step(®s, ppc_inst(PPC_RAW_LFDX(10, 3, 4))); if (stepped == 1) show_result("lfdx", "PASS"); @@ -300,7 +257,7 @@ static void __init test_lfdx_stfdx(void) c.a = 987654.32; /* stfdx frs10, r3, r4 */ - stepped = emulate_step(®s, TEST_STFDX(10, 3, 4)); + stepped = emulate_step(®s, ppc_inst(PPC_RAW_STFDX(10, 3, 4))); if (stepped == 1 && c.b == cached_b) show_result("stfdx", "PASS"); @@ -346,7 +303,7 @@ static void __init test_lvx_stvx(void) regs.gpr[4] = 0; /* lvx vrt10, r3, r4 */ - stepped = emulate_step(®s, TEST_LVX(10, 3, 4)); + stepped = emulate_step(®s, ppc_inst(PPC_RAW_LVX(10, 3, 4))); if (stepped == 1) show_result("lvx", "PASS"); @@ -362,7 +319,7 @@ static void __init test_lvx_stvx(void) c.b[3] = 498532; /* stvx vrs10, r3, r4 */ - stepped = emulate_step(®s, TEST_STVX(10, 3, 4)); + stepped = emulate_step(®s, ppc_inst(PPC_RAW_STVX(10, 3, 4))); if (stepped == 1 && cached_b[0] == c.b[0] && cached_b[1] == c.b[1] && cached_b[2] == c.b[2] && cached_b[3] == c.b[3]) @@ -403,7 +360,7 @@ static void __init test_lxvd2x_stxvd2x(void) regs.gpr[4] = 0; /* lxvd2x vsr39, r3, r4 */ - stepped = emulate_step(®s, TEST_LXVD2X(39, 3, 4)); + stepped = emulate_step(®s, ppc_inst(PPC_RAW_LXVD2X(39, R3, R4))); if (stepped == 1 && cpu_has_feature(CPU_FTR_VSX)) { show_result("lxvd2x", "PASS"); @@ -423,7 +380,7 @@ static void __init test_lxvd2x_stxvd2x(void) c.b[3] = 4; /* stxvd2x vsr39, r3, r4 */ - stepped = emulate_step(®s, TEST_STXVD2X(39, 3, 4)); + stepped = emulate_step(®s, ppc_inst(PPC_RAW_STXVD2X(39, R3, R4))); if (stepped == 1 && cached_b[0] == c.b[0] && cached_b[1] == c.b[1] && cached_b[2] == c.b[2] && cached_b[3] == c.b[3] && @@ -485,7 +442,7 @@ static struct compute_test compute_tests[] = { .subtests = { { .descr = "RA = LONG_MIN, RB = LONG_MIN", - .instr = TEST_ADD(20, 21, 22), + .instr = ppc_inst(PPC_RAW_ADD(20, 21, 22)), .regs = { .gpr[21] = LONG_MIN, .gpr[22] = LONG_MIN, @@ -493,7 +450,7 @@ static struct compute_test compute_tests[] = { }, { .descr = "RA = LONG_MIN, RB = LONG_MAX", - .instr = TEST_ADD(20, 21, 22), + .instr = ppc_inst(PPC_RAW_ADD(20, 21, 22)), .regs = { .gpr[21] = LONG_MIN, .gpr[22] = LONG_MAX, @@ -501,7 +458,7 @@ static struct compute_test compute_tests[] = { }, { .descr = "RA = LONG_MAX, RB = LONG_MAX", - .instr = TEST_ADD(20, 21, 22), + .instr = ppc_inst(PPC_RAW_ADD(20, 21, 22)), .regs = { .gpr[21] = LONG_MAX, .gpr[22] = LONG_MAX, @@ -509,7 +466,7 @@ static struct compute_test compute_tests[] = { }, { .descr = "RA = ULONG_MAX, RB = ULONG_MAX", - .instr = TEST_ADD(20, 21, 22), + .instr = ppc_inst(PPC_RAW_ADD(20, 21, 22)), .regs = { .gpr[21] = ULONG_MAX, .gpr[22] = ULONG_MAX, @@ -517,7 +474,7 @@ static struct compute_test compute_tests[] = { }, { .descr = "RA = ULONG_MAX, RB = 0x1", - .instr = TEST_ADD(20, 21, 22), + .instr = ppc_inst(PPC_RAW_ADD(20, 21, 22)), .regs = { .gpr[21] = ULONG_MAX, .gpr[22] = 0x1, @@ -525,7 +482,7 @@ static struct compute_test compute_tests[] = { }, { .descr = "RA = INT_MIN, RB = INT_MIN", - .instr = TEST_ADD(20, 21, 22), + .instr = ppc_inst(PPC_RAW_ADD(20, 21, 22)), .regs = { .gpr[21] = INT_MIN, .gpr[22] = INT_MIN, @@ -533,7 +490,7 @@ static struct compute_test compute_tests[] = { }, { .descr = "RA = INT_MIN, RB = INT_MAX", - .instr = TEST_ADD(20, 21, 22), + .instr = ppc_inst(PPC_RAW_ADD(20, 21, 22)), .regs = { .gpr[21] = INT_MIN, .gpr[22] = INT_MAX, @@ -541,7 +498,7 @@ static struct compute_test compute_tests[] = { }, { .descr = "RA = INT_MAX, RB = INT_MAX", - .instr = TEST_ADD(20, 21, 22), + .instr = ppc_inst(PPC_RAW_ADD(20, 21, 22)), .regs = { .gpr[21] = INT_MAX, .gpr[22] = INT_MAX, @@ -549,7 +506,7 @@ static struct compute_test compute_tests[] = { }, { .descr = "RA = UINT_MAX, RB = UINT_MAX", - .instr = TEST_ADD(20, 21, 22), + .instr = ppc_inst(PPC_RAW_ADD(20, 21, 22)), .regs = { .gpr[21] = UINT_MAX, .gpr[22] = UINT_MAX, @@ -557,7 +514,7 @@ static struct compute_test compute_tests[] = { }, { .descr = "RA = UINT_MAX, RB = 0x1", - .instr = TEST_ADD(20, 21, 22), + .instr = ppc_inst(PPC_RAW_ADD(20, 21, 22)), .regs = { .gpr[21] = UINT_MAX, .gpr[22] = 0x1, @@ -571,7 +528,7 @@ static struct compute_test compute_tests[] = { { .descr = "RA = LONG_MIN, RB = LONG_MIN", .flags = IGNORE_CCR, - .instr = TEST_ADD_DOT(20, 21, 22), + .instr = ppc_inst(PPC_RAW_ADD_DOT(20, 21, 22)), .regs = { .gpr[21] = LONG_MIN, .gpr[22] = LONG_MIN, @@ -579,7 +536,7 @@ static struct compute_test compute_tests[] = { }, { .descr = "RA = LONG_MIN, RB = LONG_MAX", - .instr = TEST_ADD_DOT(20, 21, 22), + .instr = ppc_inst(PPC_RAW_ADD_DOT(20, 21, 22)), .regs = { .gpr[21] = LONG_MIN, .gpr[22] = LONG_MAX, @@ -588,7 +545,7 @@ static struct compute_test compute_tests[] = { { .descr = "RA = LONG_MAX, RB = LONG_MAX", .flags = IGNORE_CCR, - .instr = TEST_ADD_DOT(20, 21, 22), + .instr = ppc_inst(PPC_RAW_ADD_DOT(20, 21, 22)), .regs = { .gpr[21] = LONG_MAX, .gpr[22] = LONG_MAX, @@ -596,7 +553,7 @@ static struct compute_test compute_tests[] = { }, { .descr = "RA = ULONG_MAX, RB = ULONG_MAX", - .instr = TEST_ADD_DOT(20, 21, 22), + .instr = ppc_inst(PPC_RAW_ADD_DOT(20, 21, 22)), .regs = { .gpr[21] = ULONG_MAX, .gpr[22] = ULONG_MAX, @@ -604,7 +561,7 @@ static struct compute_test compute_tests[] = { }, { .descr = "RA = ULONG_MAX, RB = 0x1", - .instr = TEST_ADD_DOT(20, 21, 22), + .instr = ppc_inst(PPC_RAW_ADD_DOT(20, 21, 22)), .regs = { .gpr[21] = ULONG_MAX, .gpr[22] = 0x1, @@ -612,7 +569,7 @@ static struct compute_test compute_tests[] = { }, { .descr = "RA = INT_MIN, RB = INT_MIN", - .instr = TEST_ADD_DOT(20, 21, 22), + .instr = ppc_inst(PPC_RAW_ADD_DOT(20, 21, 22)), .regs = { .gpr[21] = INT_MIN, .gpr[22] = INT_MIN, @@ -620,7 +577,7 @@ static struct compute_test compute_tests[] = { }, { .descr = "RA = INT_MIN, RB = INT_MAX", - .instr = TEST_ADD_DOT(20, 21, 22), + .instr = ppc_inst(PPC_RAW_ADD_DOT(20, 21, 22)), .regs = { .gpr[21] = INT_MIN, .gpr[22] = INT_MAX, @@ -628,7 +585,7 @@ static struct compute_test compute_tests[] = { }, { .descr = "RA = INT_MAX, RB = INT_MAX", - .instr = TEST_ADD_DOT(20, 21, 22), + .instr = ppc_inst(PPC_RAW_ADD_DOT(20, 21, 22)), .regs = { .gpr[21] = INT_MAX, .gpr[22] = INT_MAX, @@ -636,7 +593,7 @@ static struct compute_test compute_tests[] = { }, { .descr = "RA = UINT_MAX, RB = UINT_MAX", - .instr = TEST_ADD_DOT(20, 21, 22), + .instr = ppc_inst(PPC_RAW_ADD_DOT(20, 21, 22)), .regs = { .gpr[21] = UINT_MAX, .gpr[22] = UINT_MAX, @@ -644,7 +601,7 @@ static struct compute_test compute_tests[] = { }, { .descr = "RA = UINT_MAX, RB = 0x1", - .instr = TEST_ADD_DOT(20, 21, 22), + .instr = ppc_inst(PPC_RAW_ADD_DOT(20, 21, 22)), .regs = { .gpr[21] = UINT_MAX, .gpr[22] = 0x1, @@ -657,7 +614,7 @@ static struct compute_test compute_tests[] = { .subtests = { { .descr = "RA = LONG_MIN, RB = LONG_MIN", - .instr = TEST_ADDC(20, 21, 22), + .instr = ppc_inst(PPC_RAW_ADDC(20, 21, 22)), .regs = { .gpr[21] = LONG_MIN, .gpr[22] = LONG_MIN, @@ -665,7 +622,7 @@ static struct compute_test compute_tests[] = { }, { .descr = "RA = LONG_MIN, RB = LONG_MAX", - .instr = TEST_ADDC(20, 21, 22), + .instr = ppc_inst(PPC_RAW_ADDC(20, 21, 22)), .regs = { .gpr[21] = LONG_MIN, .gpr[22] = LONG_MAX, @@ -673,7 +630,7 @@ static struct compute_test compute_tests[] = { }, { .descr = "RA = LONG_MAX, RB = LONG_MAX", - .instr = TEST_ADDC(20, 21, 22), + .instr = ppc_inst(PPC_RAW_ADDC(20, 21, 22)), .regs = { .gpr[21] = LONG_MAX, .gpr[22] = LONG_MAX, @@ -681,7 +638,7 @@ static struct compute_test compute_tests[] = { }, { .descr = "RA = ULONG_MAX, RB = ULONG_MAX", - .instr = TEST_ADDC(20, 21, 22), + .instr = ppc_inst(PPC_RAW_ADDC(20, 21, 22)), .regs = { .gpr[21] = ULONG_MAX, .gpr[22] = ULONG_MAX, @@ -689,7 +646,7 @@ static struct compute_test compute_tests[] = { }, { .descr = "RA = ULONG_MAX, RB = 0x1", - .instr = TEST_ADDC(20, 21, 22), + .instr = ppc_inst(PPC_RAW_ADDC(20, 21, 22)), .regs = { .gpr[21] = ULONG_MAX, .gpr[22] = 0x1, @@ -697,7 +654,7 @@ static struct compute_test compute_tests[] = { }, { .descr = "RA = INT_MIN, RB = INT_MIN", - .instr = TEST_ADDC(20, 21, 22), + .instr = ppc_inst(PPC_RAW_ADDC(20, 21, 22)), .regs = { .gpr[21] = INT_MIN, .gpr[22] = INT_MIN, @@ -705,7 +662,7 @@ static struct compute_test compute_tests[] = { }, { .descr = "RA = INT_MIN, RB = INT_MAX", - .instr = TEST_ADDC(20, 21, 22), + .instr = ppc_inst(PPC_RAW_ADDC(20, 21, 22)), .regs = { .gpr[21] = INT_MIN, .gpr[22] = INT_MAX, @@ -713,7 +670,7 @@ static struct compute_test compute_tests[] = { }, { .descr = "RA = INT_MAX, RB = INT_MAX", - .instr = TEST_ADDC(20, 21, 22), + .instr = ppc_inst(PPC_RAW_ADDC(20, 21, 22)), .regs = { .gpr[21] = INT_MAX, .gpr[22] = INT_MAX, @@ -721,7 +678,7 @@ static struct compute_test compute_tests[] = { }, { .descr = "RA = UINT_MAX, RB = UINT_MAX", - .instr = TEST_ADDC(20, 21, 22), + .instr = ppc_inst(PPC_RAW_ADDC(20, 21, 22)), .regs = { .gpr[21] = UINT_MAX, .gpr[22] = UINT_MAX, @@ -729,7 +686,7 @@ static struct compute_test compute_tests[] = { }, { .descr = "RA = UINT_MAX, RB = 0x1", - .instr = TEST_ADDC(20, 21, 22), + .instr = ppc_inst(PPC_RAW_ADDC(20, 21, 22)), .regs = { .gpr[21] = UINT_MAX, .gpr[22] = 0x1, @@ -737,7 +694,7 @@ static struct compute_test compute_tests[] = { }, { .descr = "RA = LONG_MIN | INT_MIN, RB = LONG_MIN | INT_MIN", - .instr = TEST_ADDC(20, 21, 22), + .instr = ppc_inst(PPC_RAW_ADDC(20, 21, 22)), .regs = { .gpr[21] = LONG_MIN | (uint)INT_MIN, .gpr[22] = LONG_MIN | (uint)INT_MIN, @@ -751,7 +708,7 @@ static struct compute_test compute_tests[] = { { .descr = "RA = LONG_MIN, RB = LONG_MIN", .flags = IGNORE_CCR, - .instr = TEST_ADDC_DOT(20, 21, 22), + .instr = ppc_inst(PPC_RAW_ADDC_DOT(20, 21, 22)), .regs = { .gpr[21] = LONG_MIN, .gpr[22] = LONG_MIN, @@ -759,7 +716,7 @@ static struct compute_test compute_tests[] = { }, { .descr = "RA = LONG_MIN, RB = LONG_MAX", - .instr = TEST_ADDC_DOT(20, 21, 22), + .instr = ppc_inst(PPC_RAW_ADDC_DOT(20, 21, 22)), .regs = { .gpr[21] = LONG_MIN, .gpr[22] = LONG_MAX, @@ -768,7 +725,7 @@ static struct compute_test compute_tests[] = { { .descr = "RA = LONG_MAX, RB = LONG_MAX", .flags = IGNORE_CCR, - .instr = TEST_ADDC_DOT(20, 21, 22), + .instr = ppc_inst(PPC_RAW_ADDC_DOT(20, 21, 22)), .regs = { .gpr[21] = LONG_MAX, .gpr[22] = LONG_MAX, @@ -776,7 +733,7 @@ static struct compute_test compute_tests[] = { }, { .descr = "RA = ULONG_MAX, RB = ULONG_MAX", - .instr = TEST_ADDC_DOT(20, 21, 22), + .instr = ppc_inst(PPC_RAW_ADDC_DOT(20, 21, 22)), .regs = { .gpr[21] = ULONG_MAX, .gpr[22] = ULONG_MAX, @@ -784,7 +741,7 @@ static struct compute_test compute_tests[] = { }, { .descr = "RA = ULONG_MAX, RB = 0x1", - .instr = TEST_ADDC_DOT(20, 21, 22), + .instr = ppc_inst(PPC_RAW_ADDC_DOT(20, 21, 22)), .regs = { .gpr[21] = ULONG_MAX, .gpr[22] = 0x1, @@ -792,7 +749,7 @@ static struct compute_test compute_tests[] = { }, { .descr = "RA = INT_MIN, RB = INT_MIN", - .instr = TEST_ADDC_DOT(20, 21, 22), + .instr = ppc_inst(PPC_RAW_ADDC_DOT(20, 21, 22)), .regs = { .gpr[21] = INT_MIN, .gpr[22] = INT_MIN, @@ -800,7 +757,7 @@ static struct compute_test compute_tests[] = { }, { .descr = "RA = INT_MIN, RB = INT_MAX", - .instr = TEST_ADDC_DOT(20, 21, 22), + .instr = ppc_inst(PPC_RAW_ADDC_DOT(20, 21, 22)), .regs = { .gpr[21] = INT_MIN, .gpr[22] = INT_MAX, @@ -808,7 +765,7 @@ static struct compute_test compute_tests[] = { }, { .descr = "RA = INT_MAX, RB = INT_MAX", - .instr = TEST_ADDC_DOT(20, 21, 22), + .instr = ppc_inst(PPC_RAW_ADDC_DOT(20, 21, 22)), .regs = { .gpr[21] = INT_MAX, .gpr[22] = INT_MAX, @@ -816,7 +773,7 @@ static struct compute_test compute_tests[] = { }, { .descr = "RA = UINT_MAX, RB = UINT_MAX", - .instr = TEST_ADDC_DOT(20, 21, 22), + .instr = ppc_inst(PPC_RAW_ADDC_DOT(20, 21, 22)), .regs = { .gpr[21] = UINT_MAX, .gpr[22] = UINT_MAX, @@ -824,7 +781,7 @@ static struct compute_test compute_tests[] = { }, { .descr = "RA = UINT_MAX, RB = 0x1", - .instr = TEST_ADDC_DOT(20, 21, 22), + .instr = ppc_inst(PPC_RAW_ADDC_DOT(20, 21, 22)), .regs = { .gpr[21] = UINT_MAX, .gpr[22] = 0x1, @@ -832,7 +789,7 @@ static struct compute_test compute_tests[] = { }, { .descr = "RA = LONG_MIN | INT_MIN, RB = LONG_MIN | INT_MIN", - .instr = TEST_ADDC_DOT(20, 21, 22), + .instr = ppc_inst(PPC_RAW_ADDC_DOT(20, 21, 22)), .regs = { .gpr[21] = LONG_MIN | (uint)INT_MIN, .gpr[22] = LONG_MIN | (uint)INT_MIN, -- GitLab From 0654186510a40e7e1fa788cb941d1a156ba2dcb2 Mon Sep 17 00:00:00 2001 From: Balamuruhan S Date: Wed, 24 Jun 2020 17:00:35 +0530 Subject: [PATCH 0484/1476] powerpc/bpf_jit: Reuse instruction macros from ppc-opcode.h Remove duplicate macro definitions from bpf_jit.h and reuse the macros from ppc-opcode.h Signed-off-by: Balamuruhan S Tested-by: Naveen N. Rao Acked-by: Naveen N. Rao Acked-by: Sandipan Das Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20200624113038.908074-4-bala24@linux.ibm.com --- arch/powerpc/net/bpf_jit.h | 18 +----------------- arch/powerpc/net/bpf_jit32.h | 10 +++++----- arch/powerpc/net/bpf_jit64.h | 4 ++-- arch/powerpc/net/bpf_jit_comp.c | 2 +- arch/powerpc/net/bpf_jit_comp64.c | 20 ++++++++++---------- 5 files changed, 19 insertions(+), 35 deletions(-) diff --git a/arch/powerpc/net/bpf_jit.h b/arch/powerpc/net/bpf_jit.h index 55d4377ccfae3..535d1de4dfeee 100644 --- a/arch/powerpc/net/bpf_jit.h +++ b/arch/powerpc/net/bpf_jit.h @@ -11,6 +11,7 @@ #ifndef __ASSEMBLY__ #include +#include #ifdef PPC64_ELF_ABI_v1 #define FUNCTION_DESCR_SIZE 24 @@ -26,7 +27,6 @@ #define IMM_H(i) ((uintptr_t)(i)>>16) #define IMM_HA(i) (((uintptr_t)(i)>>16) + \ (((uintptr_t)(i) & 0x8000) >> 15)) -#define IMM_L(i) ((uintptr_t)(i) & 0xffff) #define PLANT_INSTR(d, idx, instr) \ do { if (d) { (d)[idx] = instr; } idx++; } while (0) @@ -45,8 +45,6 @@ #define PPC_ADDIS(d, a, i) EMIT(PPC_INST_ADDIS | \ ___PPC_RT(d) | ___PPC_RA(a) | IMM_L(i)) #define PPC_LIS(r, i) PPC_ADDIS(r, 0, i) -#define PPC_STD(r, base, i) EMIT(PPC_INST_STD | ___PPC_RS(r) | \ - ___PPC_RA(base) | ((i) & 0xfffc)) #define PPC_STDX(r, base, b) EMIT(PPC_INST_STDX | ___PPC_RS(r) | \ ___PPC_RA(base) | ___PPC_RB(b)) #define PPC_STDU(r, base, i) EMIT(PPC_INST_STDU | ___PPC_RS(r) | \ @@ -62,12 +60,8 @@ #define PPC_LBZ(r, base, i) EMIT(PPC_INST_LBZ | ___PPC_RT(r) | \ ___PPC_RA(base) | IMM_L(i)) -#define PPC_LD(r, base, i) EMIT(PPC_INST_LD | ___PPC_RT(r) | \ - ___PPC_RA(base) | ((i) & 0xfffc)) #define PPC_LDX(r, base, b) EMIT(PPC_INST_LDX | ___PPC_RT(r) | \ ___PPC_RA(base) | ___PPC_RB(b)) -#define PPC_LWZ(r, base, i) EMIT(PPC_INST_LWZ | ___PPC_RT(r) | \ - ___PPC_RA(base) | IMM_L(i)) #define PPC_LHZ(r, base, i) EMIT(PPC_INST_LHZ | ___PPC_RT(r) | \ ___PPC_RA(base) | IMM_L(i)) #define PPC_LHBRX(r, base, b) EMIT(PPC_INST_LHBRX | ___PPC_RT(r) | \ @@ -75,16 +69,8 @@ #define PPC_LDBRX(r, base, b) EMIT(PPC_INST_LDBRX | ___PPC_RT(r) | \ ___PPC_RA(base) | ___PPC_RB(b)) -#define PPC_BPF_LDARX(t, a, b, eh) EMIT(PPC_INST_LDARX | ___PPC_RT(t) | \ - ___PPC_RA(a) | ___PPC_RB(b) | \ - __PPC_EH(eh)) -#define PPC_BPF_LWARX(t, a, b, eh) EMIT(PPC_INST_LWARX | ___PPC_RT(t) | \ - ___PPC_RA(a) | ___PPC_RB(b) | \ - __PPC_EH(eh)) #define PPC_BPF_STWCX(s, a, b) EMIT(PPC_INST_STWCX | ___PPC_RS(s) | \ ___PPC_RA(a) | ___PPC_RB(b)) -#define PPC_BPF_STDCX(s, a, b) EMIT(PPC_INST_STDCX | ___PPC_RS(s) | \ - ___PPC_RA(a) | ___PPC_RB(b)) #define PPC_CMPWI(a, i) EMIT(PPC_INST_CMPWI | ___PPC_RA(a) | IMM_L(i)) #define PPC_CMPDI(a, i) EMIT(PPC_INST_CMPDI | ___PPC_RA(a) | IMM_L(i)) #define PPC_CMPW(a, b) EMIT(PPC_INST_CMPW | ___PPC_RA(a) | \ @@ -100,8 +86,6 @@ #define PPC_SUB(d, a, b) EMIT(PPC_INST_SUB | ___PPC_RT(d) | \ ___PPC_RB(a) | ___PPC_RA(b)) -#define PPC_ADD(d, a, b) EMIT(PPC_INST_ADD | ___PPC_RT(d) | \ - ___PPC_RA(a) | ___PPC_RB(b)) #define PPC_MULD(d, a, b) EMIT(PPC_INST_MULLD | ___PPC_RT(d) | \ ___PPC_RA(a) | ___PPC_RB(b)) #define PPC_MULW(d, a, b) EMIT(PPC_INST_MULLW | ___PPC_RT(d) | \ diff --git a/arch/powerpc/net/bpf_jit32.h b/arch/powerpc/net/bpf_jit32.h index 4ec2a9f14f845..753c244a7cf91 100644 --- a/arch/powerpc/net/bpf_jit32.h +++ b/arch/powerpc/net/bpf_jit32.h @@ -76,13 +76,13 @@ DECLARE_LOAD_FUNC(sk_load_byte_msh); else { PPC_ADDIS(r, base, IMM_HA(i)); \ PPC_LBZ(r, r, IMM_L(i)); } } while(0) -#define PPC_LD_OFFS(r, base, i) do { if ((i) < 32768) PPC_LD(r, base, i); \ +#define PPC_LD_OFFS(r, base, i) do { if ((i) < 32768) EMIT(PPC_RAW_LD(r, base, i)); \ else { PPC_ADDIS(r, base, IMM_HA(i)); \ - PPC_LD(r, r, IMM_L(i)); } } while(0) + EMIT(PPC_RAW_LD(r, r, IMM_L(i))); } } while(0) -#define PPC_LWZ_OFFS(r, base, i) do { if ((i) < 32768) PPC_LWZ(r, base, i); \ +#define PPC_LWZ_OFFS(r, base, i) do { if ((i) < 32768) EMIT(PPC_RAW_LWZ(r, base, i)); \ else { PPC_ADDIS(r, base, IMM_HA(i)); \ - PPC_LWZ(r, r, IMM_L(i)); } } while(0) + EMIT(PPC_RAW_LWZ(r, r, IMM_L(i))); } } while(0) #define PPC_LHZ_OFFS(r, base, i) do { if ((i) < 32768) PPC_LHZ(r, base, i); \ else { PPC_ADDIS(r, base, IMM_HA(i)); \ @@ -118,7 +118,7 @@ DECLARE_LOAD_FUNC(sk_load_byte_msh); #define PPC_NTOHS_OFFS(r, base, i) PPC_LHZ_OFFS(r, base, i) #endif -#define PPC_BPF_LL(r, base, i) do { PPC_LWZ(r, base, i); } while(0) +#define PPC_BPF_LL(r, base, i) do { EMIT(PPC_RAW_LWZ(r, base, i)); } while(0) #define PPC_BPF_STL(r, base, i) do { PPC_STW(r, base, i); } while(0) #define PPC_BPF_STLU(r, base, i) do { PPC_STWU(r, base, i); } while(0) diff --git a/arch/powerpc/net/bpf_jit64.h b/arch/powerpc/net/bpf_jit64.h index cf3a7e337f025..c144a37771586 100644 --- a/arch/powerpc/net/bpf_jit64.h +++ b/arch/powerpc/net/bpf_jit64.h @@ -73,14 +73,14 @@ static const int b2p[] = { PPC_LI(b2p[TMP_REG_2], (i)); \ PPC_LDX(r, base, b2p[TMP_REG_2]); \ } else \ - PPC_LD(r, base, i); \ + EMIT(PPC_RAW_LD(r, base, i)); \ } while(0) #define PPC_BPF_STL(r, base, i) do { \ if ((i) % 4) { \ PPC_LI(b2p[TMP_REG_2], (i)); \ PPC_STDX(r, base, b2p[TMP_REG_2]); \ } else \ - PPC_STD(r, base, i); \ + EMIT(PPC_RAW_STD(r, base, i)); \ } while(0) #define PPC_BPF_STLU(r, base, i) do { PPC_STDU(r, base, i); } while(0) diff --git a/arch/powerpc/net/bpf_jit_comp.c b/arch/powerpc/net/bpf_jit_comp.c index 0acc9d5fb19e9..abcf56c00be50 100644 --- a/arch/powerpc/net/bpf_jit_comp.c +++ b/arch/powerpc/net/bpf_jit_comp.c @@ -134,7 +134,7 @@ static int bpf_jit_build_body(struct bpf_prog *fp, u32 *image, /*** ALU ops ***/ case BPF_ALU | BPF_ADD | BPF_X: /* A += X; */ ctx->seen |= SEEN_XREG; - PPC_ADD(r_A, r_A, r_X); + EMIT(PPC_RAW_ADD(r_A, r_A, r_X)); break; case BPF_ALU | BPF_ADD | BPF_K: /* A += K; */ if (!K) diff --git a/arch/powerpc/net/bpf_jit_comp64.c b/arch/powerpc/net/bpf_jit_comp64.c index be3517ef0574d..f721fbe6ca4dd 100644 --- a/arch/powerpc/net/bpf_jit_comp64.c +++ b/arch/powerpc/net/bpf_jit_comp64.c @@ -239,7 +239,7 @@ static void bpf_jit_emit_tail_call(u32 *image, struct codegen_context *ctx, u32 * if (index >= array->map.max_entries) * goto out; */ - PPC_LWZ(b2p[TMP_REG_1], b2p_bpf_array, offsetof(struct bpf_array, map.max_entries)); + EMIT(PPC_RAW_LWZ(b2p[TMP_REG_1], b2p_bpf_array, offsetof(struct bpf_array, map.max_entries))); PPC_RLWINM(b2p_index, b2p_index, 0, 0, 31); PPC_CMPLW(b2p_index, b2p[TMP_REG_1]); PPC_BCC(COND_GE, out); @@ -260,7 +260,7 @@ static void bpf_jit_emit_tail_call(u32 *image, struct codegen_context *ctx, u32 /* prog = array->ptrs[index]; */ PPC_MULI(b2p[TMP_REG_1], b2p_index, 8); - PPC_ADD(b2p[TMP_REG_1], b2p[TMP_REG_1], b2p_bpf_array); + EMIT(PPC_RAW_ADD(b2p[TMP_REG_1], b2p[TMP_REG_1], b2p_bpf_array)); PPC_BPF_LL(b2p[TMP_REG_1], b2p[TMP_REG_1], offsetof(struct bpf_array, ptrs)); /* @@ -340,7 +340,7 @@ static int bpf_jit_build_body(struct bpf_prog *fp, u32 *image, */ case BPF_ALU | BPF_ADD | BPF_X: /* (u32) dst += (u32) src */ case BPF_ALU64 | BPF_ADD | BPF_X: /* dst += src */ - PPC_ADD(dst_reg, dst_reg, src_reg); + EMIT(PPC_RAW_ADD(dst_reg, dst_reg, src_reg)); goto bpf_alu32_trunc; case BPF_ALU | BPF_SUB | BPF_X: /* (u32) dst -= (u32) src */ case BPF_ALU64 | BPF_SUB | BPF_X: /* dst -= src */ @@ -357,7 +357,7 @@ static int bpf_jit_build_body(struct bpf_prog *fp, u32 *image, PPC_ADDI(dst_reg, dst_reg, IMM_L(imm)); else { PPC_LI32(b2p[TMP_REG_1], imm); - PPC_ADD(dst_reg, dst_reg, b2p[TMP_REG_1]); + EMIT(PPC_RAW_ADD(dst_reg, dst_reg, b2p[TMP_REG_1])); } } goto bpf_alu32_trunc; @@ -689,9 +689,9 @@ emit_clear: PPC_ADDI(b2p[TMP_REG_1], dst_reg, off); tmp_idx = ctx->idx * 4; /* load value from memory into TMP_REG_2 */ - PPC_BPF_LWARX(b2p[TMP_REG_2], 0, b2p[TMP_REG_1], 0); + EMIT(PPC_RAW_LWARX(b2p[TMP_REG_2], 0, b2p[TMP_REG_1], 0)); /* add value from src_reg into this */ - PPC_ADD(b2p[TMP_REG_2], b2p[TMP_REG_2], src_reg); + EMIT(PPC_RAW_ADD(b2p[TMP_REG_2], b2p[TMP_REG_2], src_reg)); /* store result back */ PPC_BPF_STWCX(b2p[TMP_REG_2], 0, b2p[TMP_REG_1]); /* we're done if this succeeded */ @@ -701,9 +701,9 @@ emit_clear: case BPF_STX | BPF_XADD | BPF_DW: PPC_ADDI(b2p[TMP_REG_1], dst_reg, off); tmp_idx = ctx->idx * 4; - PPC_BPF_LDARX(b2p[TMP_REG_2], 0, b2p[TMP_REG_1], 0); - PPC_ADD(b2p[TMP_REG_2], b2p[TMP_REG_2], src_reg); - PPC_BPF_STDCX(b2p[TMP_REG_2], 0, b2p[TMP_REG_1]); + EMIT(PPC_RAW_LDARX(b2p[TMP_REG_2], 0, b2p[TMP_REG_1], 0)); + EMIT(PPC_RAW_ADD(b2p[TMP_REG_2], b2p[TMP_REG_2], src_reg)); + EMIT(PPC_RAW_STDCX(b2p[TMP_REG_2], 0, b2p[TMP_REG_1])); PPC_BCC_SHORT(COND_NE, tmp_idx); break; @@ -724,7 +724,7 @@ emit_clear: break; /* dst = *(u32 *)(ul) (src + off) */ case BPF_LDX | BPF_MEM | BPF_W: - PPC_LWZ(dst_reg, src_reg, off); + EMIT(PPC_RAW_LWZ(dst_reg, src_reg, off)); if (insn_is_zext(&insn[i + 1])) addrs[++i] = ctx->idx * 4; break; -- GitLab From 3a181237916310b2bbbad158d97933bb2b4e7552 Mon Sep 17 00:00:00 2001 From: Balamuruhan S Date: Wed, 24 Jun 2020 17:00:36 +0530 Subject: [PATCH 0485/1476] powerpc/ppc-opcode: Consolidate powerpc instructions from bpf_jit.h Move macro definitions of powerpc instructions from bpf_jit.h to ppc-opcode.h and adopt the users of the macros accordingly. `PPC_MR()` is defined twice in bpf_jit.h, remove the duplicate one. Signed-off-by: Balamuruhan S Tested-by: Naveen N. Rao Acked-by: Naveen N. Rao Acked-by: Sandipan Das Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20200624113038.908074-5-bala24@linux.ibm.com --- arch/powerpc/include/asm/ppc-opcode.h | 85 ++++++++ arch/powerpc/net/bpf_jit.h | 166 ++------------- arch/powerpc/net/bpf_jit32.h | 24 +-- arch/powerpc/net/bpf_jit64.h | 12 +- arch/powerpc/net/bpf_jit_comp.c | 132 ++++++------ arch/powerpc/net/bpf_jit_comp64.c | 278 +++++++++++++------------- 6 files changed, 324 insertions(+), 373 deletions(-) diff --git a/arch/powerpc/include/asm/ppc-opcode.h b/arch/powerpc/include/asm/ppc-opcode.h index 49701d4ea0e1f..cb24b758c7849 100644 --- a/arch/powerpc/include/asm/ppc-opcode.h +++ b/arch/powerpc/include/asm/ppc-opcode.h @@ -79,6 +79,16 @@ #define IMM_L(i) ((uintptr_t)(i) & 0xffff) #define IMM_DS(i) ((uintptr_t)(i) & 0xfffc) +/* + * 16-bit immediate helper macros: HA() is for use with sign-extending instrs + * (e.g. LD, ADDI). If the bottom 16 bits is "-ve", add another bit into the + * top half to negate the effect (i.e. 0xffff + 1 = 0x(1)0000). + */ +#define IMM_H(i) ((uintptr_t)(i)>>16) +#define IMM_HA(i) (((uintptr_t)(i)>>16) + \ + (((uintptr_t)(i) & 0x8000) >> 15)) + + /* opcode and xopcode for instructions */ #define OP_TRAP 3 #define OP_TRAP_64 2 @@ -540,6 +550,81 @@ #define PPC_RAW_ADD_DOT(t, a, b) (PPC_INST_ADD | ___PPC_RT(t) | ___PPC_RA(a) | ___PPC_RB(b) | 0x1) #define PPC_RAW_ADDC(t, a, b) (PPC_INST_ADDC | ___PPC_RT(t) | ___PPC_RA(a) | ___PPC_RB(b)) #define PPC_RAW_ADDC_DOT(t, a, b) (PPC_INST_ADDC | ___PPC_RT(t) | ___PPC_RA(a) | ___PPC_RB(b) | 0x1) +#define PPC_RAW_NOP() (PPC_INST_NOP) +#define PPC_RAW_BLR() (PPC_INST_BLR) +#define PPC_RAW_BLRL() (PPC_INST_BLRL) +#define PPC_RAW_MTLR(r) (PPC_INST_MTLR | ___PPC_RT(r)) +#define PPC_RAW_BCTR() (PPC_INST_BCTR) +#define PPC_RAW_MTCTR(r) (PPC_INST_MTCTR | ___PPC_RT(r)) +#define PPC_RAW_ADDI(d, a, i) (PPC_INST_ADDI | ___PPC_RT(d) | ___PPC_RA(a) | IMM_L(i)) +#define PPC_RAW_LI(r, i) PPC_RAW_ADDI(r, 0, i) +#define PPC_RAW_ADDIS(d, a, i) (PPC_INST_ADDIS | ___PPC_RT(d) | ___PPC_RA(a) | IMM_L(i)) +#define PPC_RAW_LIS(r, i) PPC_RAW_ADDIS(r, 0, i) +#define PPC_RAW_STDX(r, base, b) (PPC_INST_STDX | ___PPC_RS(r) | ___PPC_RA(base) | ___PPC_RB(b)) +#define PPC_RAW_STDU(r, base, i) (PPC_INST_STDU | ___PPC_RS(r) | ___PPC_RA(base) | ((i) & 0xfffc)) +#define PPC_RAW_STW(r, base, i) (PPC_INST_STW | ___PPC_RS(r) | ___PPC_RA(base) | IMM_L(i)) +#define PPC_RAW_STWU(r, base, i) (PPC_INST_STWU | ___PPC_RS(r) | ___PPC_RA(base) | IMM_L(i)) +#define PPC_RAW_STH(r, base, i) (PPC_INST_STH | ___PPC_RS(r) | ___PPC_RA(base) | IMM_L(i)) +#define PPC_RAW_STB(r, base, i) (PPC_INST_STB | ___PPC_RS(r) | ___PPC_RA(base) | IMM_L(i)) +#define PPC_RAW_LBZ(r, base, i) (PPC_INST_LBZ | ___PPC_RT(r) | ___PPC_RA(base) | IMM_L(i)) +#define PPC_RAW_LDX(r, base, b) (PPC_INST_LDX | ___PPC_RT(r) | ___PPC_RA(base) | ___PPC_RB(b)) +#define PPC_RAW_LHZ(r, base, i) (PPC_INST_LHZ | ___PPC_RT(r) | ___PPC_RA(base) | IMM_L(i)) +#define PPC_RAW_LHBRX(r, base, b) (PPC_INST_LHBRX | ___PPC_RT(r) | ___PPC_RA(base) | ___PPC_RB(b)) +#define PPC_RAW_LDBRX(r, base, b) (PPC_INST_LDBRX | ___PPC_RT(r) | ___PPC_RA(base) | ___PPC_RB(b)) +#define PPC_RAW_STWCX(s, a, b) (PPC_INST_STWCX | ___PPC_RS(s) | ___PPC_RA(a) | ___PPC_RB(b)) +#define PPC_RAW_CMPWI(a, i) (PPC_INST_CMPWI | ___PPC_RA(a) | IMM_L(i)) +#define PPC_RAW_CMPDI(a, i) (PPC_INST_CMPDI | ___PPC_RA(a) | IMM_L(i)) +#define PPC_RAW_CMPW(a, b) (PPC_INST_CMPW | ___PPC_RA(a) | ___PPC_RB(b)) +#define PPC_RAW_CMPD(a, b) (PPC_INST_CMPD | ___PPC_RA(a) | ___PPC_RB(b)) +#define PPC_RAW_CMPLWI(a, i) (PPC_INST_CMPLWI | ___PPC_RA(a) | IMM_L(i)) +#define PPC_RAW_CMPLDI(a, i) (PPC_INST_CMPLDI | ___PPC_RA(a) | IMM_L(i)) +#define PPC_RAW_CMPLW(a, b) (PPC_INST_CMPLW | ___PPC_RA(a) | ___PPC_RB(b)) +#define PPC_RAW_CMPLD(a, b) (PPC_INST_CMPLD | ___PPC_RA(a) | ___PPC_RB(b)) +#define PPC_RAW_SUB(d, a, b) (PPC_INST_SUB | ___PPC_RT(d) | ___PPC_RB(a) | ___PPC_RA(b)) +#define PPC_RAW_MULD(d, a, b) (PPC_INST_MULLD | ___PPC_RT(d) | ___PPC_RA(a) | ___PPC_RB(b)) +#define PPC_RAW_MULW(d, a, b) (PPC_INST_MULLW | ___PPC_RT(d) | ___PPC_RA(a) | ___PPC_RB(b)) +#define PPC_RAW_MULHWU(d, a, b) (PPC_INST_MULHWU | ___PPC_RT(d) | ___PPC_RA(a) | ___PPC_RB(b)) +#define PPC_RAW_MULI(d, a, i) (PPC_INST_MULLI | ___PPC_RT(d) | ___PPC_RA(a) | IMM_L(i)) +#define PPC_RAW_DIVWU(d, a, b) (PPC_INST_DIVWU | ___PPC_RT(d) | ___PPC_RA(a) | ___PPC_RB(b)) +#define PPC_RAW_DIVDU(d, a, b) (PPC_INST_DIVDU | ___PPC_RT(d) | ___PPC_RA(a) | ___PPC_RB(b)) +#define PPC_RAW_AND(d, a, b) (PPC_INST_AND | ___PPC_RA(d) | ___PPC_RS(a) | ___PPC_RB(b)) +#define PPC_RAW_ANDI(d, a, i) (PPC_INST_ANDI | ___PPC_RA(d) | ___PPC_RS(a) | IMM_L(i)) +#define PPC_RAW_AND_DOT(d, a, b) (PPC_INST_ANDDOT | ___PPC_RA(d) | ___PPC_RS(a) | ___PPC_RB(b)) +#define PPC_RAW_OR(d, a, b) (PPC_INST_OR | ___PPC_RA(d) | ___PPC_RS(a) | ___PPC_RB(b)) +#define PPC_RAW_MR(d, a) PPC_RAW_OR(d, a, a) +#define PPC_RAW_ORI(d, a, i) (PPC_INST_ORI | ___PPC_RA(d) | ___PPC_RS(a) | IMM_L(i)) +#define PPC_RAW_ORIS(d, a, i) (PPC_INST_ORIS | ___PPC_RA(d) | ___PPC_RS(a) | IMM_L(i)) +#define PPC_RAW_XOR(d, a, b) (PPC_INST_XOR | ___PPC_RA(d) | ___PPC_RS(a) | ___PPC_RB(b)) +#define PPC_RAW_XORI(d, a, i) (PPC_INST_XORI | ___PPC_RA(d) | ___PPC_RS(a) | IMM_L(i)) +#define PPC_RAW_XORIS(d, a, i) (PPC_INST_XORIS | ___PPC_RA(d) | ___PPC_RS(a) | IMM_L(i)) +#define PPC_RAW_EXTSW(d, a) (PPC_INST_EXTSW | ___PPC_RA(d) | ___PPC_RS(a)) +#define PPC_RAW_SLW(d, a, s) (PPC_INST_SLW | ___PPC_RA(d) | ___PPC_RS(a) | ___PPC_RB(s)) +#define PPC_RAW_SLD(d, a, s) (PPC_INST_SLD | ___PPC_RA(d) | ___PPC_RS(a) | ___PPC_RB(s)) +#define PPC_RAW_SRW(d, a, s) (PPC_INST_SRW | ___PPC_RA(d) | ___PPC_RS(a) | ___PPC_RB(s)) +#define PPC_RAW_SRAW(d, a, s) (PPC_INST_SRAW | ___PPC_RA(d) | ___PPC_RS(a) | ___PPC_RB(s)) +#define PPC_RAW_SRAWI(d, a, i) (PPC_INST_SRAWI | ___PPC_RA(d) | ___PPC_RS(a) | __PPC_SH(i)) +#define PPC_RAW_SRD(d, a, s) (PPC_INST_SRD | ___PPC_RA(d) | ___PPC_RS(a) | ___PPC_RB(s)) +#define PPC_RAW_SRAD(d, a, s) (PPC_INST_SRAD | ___PPC_RA(d) | ___PPC_RS(a) | ___PPC_RB(s)) +#define PPC_RAW_SRADI(d, a, i) (PPC_INST_SRADI | ___PPC_RA(d) | ___PPC_RS(a) | __PPC_SH64(i)) +#define PPC_RAW_RLWINM(d, a, i, mb, me) \ + (PPC_INST_RLWINM | ___PPC_RA(d) | ___PPC_RS(a) | __PPC_SH(i) | __PPC_MB(mb) | __PPC_ME(me)) +#define PPC_RAW_RLWINM_DOT(d, a, i, mb, me) \ + (PPC_INST_RLWINM_DOT | ___PPC_RA(d) | ___PPC_RS(a) | __PPC_SH(i) | __PPC_MB(mb) | __PPC_ME(me)) +#define PPC_RAW_RLWIMI(d, a, i, mb, me) \ + (PPC_INST_RLWIMI | ___PPC_RA(d) | ___PPC_RS(a) | __PPC_SH(i) | __PPC_MB(mb) | __PPC_ME(me)) +#define PPC_RAW_RLDICL(d, a, i, mb) (PPC_INST_RLDICL | ___PPC_RA(d) | ___PPC_RS(a) | __PPC_SH64(i) | __PPC_MB64(mb)) +#define PPC_RAW_RLDICR(d, a, i, me) (PPC_INST_RLDICR | ___PPC_RA(d) | ___PPC_RS(a) | __PPC_SH64(i) | __PPC_ME64(me)) + +/* slwi = rlwinm Rx, Ry, n, 0, 31-n */ +#define PPC_RAW_SLWI(d, a, i) PPC_RAW_RLWINM(d, a, i, 0, 31-(i)) +/* srwi = rlwinm Rx, Ry, 32-n, n, 31 */ +#define PPC_RAW_SRWI(d, a, i) PPC_RAW_RLWINM(d, a, 32-(i), i, 31) +/* sldi = rldicr Rx, Ry, n, 63-n */ +#define PPC_RAW_SLDI(d, a, i) PPC_RAW_RLDICR(d, a, i, 63-(i)) +/* sldi = rldicl Rx, Ry, 64-n, n */ +#define PPC_RAW_SRDI(d, a, i) PPC_RAW_RLDICL(d, a, 64-(i), i) + +#define PPC_RAW_NEG(d, a) (PPC_INST_NEG | ___PPC_RT(d) | ___PPC_RA(a)) /* Deal with instructions that older assemblers aren't aware of */ #define PPC_BCCTR_FLUSH stringify_in_c(.long PPC_INST_BCCTR_FLUSH) diff --git a/arch/powerpc/net/bpf_jit.h b/arch/powerpc/net/bpf_jit.h index 535d1de4dfeee..d0a67a1bbaf18 100644 --- a/arch/powerpc/net/bpf_jit.h +++ b/arch/powerpc/net/bpf_jit.h @@ -19,150 +19,10 @@ #define FUNCTION_DESCR_SIZE 0 #endif -/* - * 16-bit immediate helper macros: HA() is for use with sign-extending instrs - * (e.g. LD, ADDI). If the bottom 16 bits is "-ve", add another bit into the - * top half to negate the effect (i.e. 0xffff + 1 = 0x(1)0000). - */ -#define IMM_H(i) ((uintptr_t)(i)>>16) -#define IMM_HA(i) (((uintptr_t)(i)>>16) + \ - (((uintptr_t)(i) & 0x8000) >> 15)) - #define PLANT_INSTR(d, idx, instr) \ do { if (d) { (d)[idx] = instr; } idx++; } while (0) #define EMIT(instr) PLANT_INSTR(image, ctx->idx, instr) -#define PPC_NOP() EMIT(PPC_INST_NOP) -#define PPC_BLR() EMIT(PPC_INST_BLR) -#define PPC_BLRL() EMIT(PPC_INST_BLRL) -#define PPC_MTLR(r) EMIT(PPC_INST_MTLR | ___PPC_RT(r)) -#define PPC_BCTR() EMIT(PPC_INST_BCTR) -#define PPC_MTCTR(r) EMIT(PPC_INST_MTCTR | ___PPC_RT(r)) -#define PPC_ADDI(d, a, i) EMIT(PPC_INST_ADDI | ___PPC_RT(d) | \ - ___PPC_RA(a) | IMM_L(i)) -#define PPC_MR(d, a) PPC_OR(d, a, a) -#define PPC_LI(r, i) PPC_ADDI(r, 0, i) -#define PPC_ADDIS(d, a, i) EMIT(PPC_INST_ADDIS | \ - ___PPC_RT(d) | ___PPC_RA(a) | IMM_L(i)) -#define PPC_LIS(r, i) PPC_ADDIS(r, 0, i) -#define PPC_STDX(r, base, b) EMIT(PPC_INST_STDX | ___PPC_RS(r) | \ - ___PPC_RA(base) | ___PPC_RB(b)) -#define PPC_STDU(r, base, i) EMIT(PPC_INST_STDU | ___PPC_RS(r) | \ - ___PPC_RA(base) | ((i) & 0xfffc)) -#define PPC_STW(r, base, i) EMIT(PPC_INST_STW | ___PPC_RS(r) | \ - ___PPC_RA(base) | IMM_L(i)) -#define PPC_STWU(r, base, i) EMIT(PPC_INST_STWU | ___PPC_RS(r) | \ - ___PPC_RA(base) | IMM_L(i)) -#define PPC_STH(r, base, i) EMIT(PPC_INST_STH | ___PPC_RS(r) | \ - ___PPC_RA(base) | IMM_L(i)) -#define PPC_STB(r, base, i) EMIT(PPC_INST_STB | ___PPC_RS(r) | \ - ___PPC_RA(base) | IMM_L(i)) - -#define PPC_LBZ(r, base, i) EMIT(PPC_INST_LBZ | ___PPC_RT(r) | \ - ___PPC_RA(base) | IMM_L(i)) -#define PPC_LDX(r, base, b) EMIT(PPC_INST_LDX | ___PPC_RT(r) | \ - ___PPC_RA(base) | ___PPC_RB(b)) -#define PPC_LHZ(r, base, i) EMIT(PPC_INST_LHZ | ___PPC_RT(r) | \ - ___PPC_RA(base) | IMM_L(i)) -#define PPC_LHBRX(r, base, b) EMIT(PPC_INST_LHBRX | ___PPC_RT(r) | \ - ___PPC_RA(base) | ___PPC_RB(b)) -#define PPC_LDBRX(r, base, b) EMIT(PPC_INST_LDBRX | ___PPC_RT(r) | \ - ___PPC_RA(base) | ___PPC_RB(b)) - -#define PPC_BPF_STWCX(s, a, b) EMIT(PPC_INST_STWCX | ___PPC_RS(s) | \ - ___PPC_RA(a) | ___PPC_RB(b)) -#define PPC_CMPWI(a, i) EMIT(PPC_INST_CMPWI | ___PPC_RA(a) | IMM_L(i)) -#define PPC_CMPDI(a, i) EMIT(PPC_INST_CMPDI | ___PPC_RA(a) | IMM_L(i)) -#define PPC_CMPW(a, b) EMIT(PPC_INST_CMPW | ___PPC_RA(a) | \ - ___PPC_RB(b)) -#define PPC_CMPD(a, b) EMIT(PPC_INST_CMPD | ___PPC_RA(a) | \ - ___PPC_RB(b)) -#define PPC_CMPLWI(a, i) EMIT(PPC_INST_CMPLWI | ___PPC_RA(a) | IMM_L(i)) -#define PPC_CMPLDI(a, i) EMIT(PPC_INST_CMPLDI | ___PPC_RA(a) | IMM_L(i)) -#define PPC_CMPLW(a, b) EMIT(PPC_INST_CMPLW | ___PPC_RA(a) | \ - ___PPC_RB(b)) -#define PPC_CMPLD(a, b) EMIT(PPC_INST_CMPLD | ___PPC_RA(a) | \ - ___PPC_RB(b)) - -#define PPC_SUB(d, a, b) EMIT(PPC_INST_SUB | ___PPC_RT(d) | \ - ___PPC_RB(a) | ___PPC_RA(b)) -#define PPC_MULD(d, a, b) EMIT(PPC_INST_MULLD | ___PPC_RT(d) | \ - ___PPC_RA(a) | ___PPC_RB(b)) -#define PPC_MULW(d, a, b) EMIT(PPC_INST_MULLW | ___PPC_RT(d) | \ - ___PPC_RA(a) | ___PPC_RB(b)) -#define PPC_MULHWU(d, a, b) EMIT(PPC_INST_MULHWU | ___PPC_RT(d) | \ - ___PPC_RA(a) | ___PPC_RB(b)) -#define PPC_MULI(d, a, i) EMIT(PPC_INST_MULLI | ___PPC_RT(d) | \ - ___PPC_RA(a) | IMM_L(i)) -#define PPC_DIVWU(d, a, b) EMIT(PPC_INST_DIVWU | ___PPC_RT(d) | \ - ___PPC_RA(a) | ___PPC_RB(b)) -#define PPC_DIVDU(d, a, b) EMIT(PPC_INST_DIVDU | ___PPC_RT(d) | \ - ___PPC_RA(a) | ___PPC_RB(b)) -#define PPC_AND(d, a, b) EMIT(PPC_INST_AND | ___PPC_RA(d) | \ - ___PPC_RS(a) | ___PPC_RB(b)) -#define PPC_ANDI(d, a, i) EMIT(PPC_INST_ANDI | ___PPC_RA(d) | \ - ___PPC_RS(a) | IMM_L(i)) -#define PPC_AND_DOT(d, a, b) EMIT(PPC_INST_ANDDOT | ___PPC_RA(d) | \ - ___PPC_RS(a) | ___PPC_RB(b)) -#define PPC_OR(d, a, b) EMIT(PPC_INST_OR | ___PPC_RA(d) | \ - ___PPC_RS(a) | ___PPC_RB(b)) -#define PPC_MR(d, a) PPC_OR(d, a, a) -#define PPC_ORI(d, a, i) EMIT(PPC_INST_ORI | ___PPC_RA(d) | \ - ___PPC_RS(a) | IMM_L(i)) -#define PPC_ORIS(d, a, i) EMIT(PPC_INST_ORIS | ___PPC_RA(d) | \ - ___PPC_RS(a) | IMM_L(i)) -#define PPC_XOR(d, a, b) EMIT(PPC_INST_XOR | ___PPC_RA(d) | \ - ___PPC_RS(a) | ___PPC_RB(b)) -#define PPC_XORI(d, a, i) EMIT(PPC_INST_XORI | ___PPC_RA(d) | \ - ___PPC_RS(a) | IMM_L(i)) -#define PPC_XORIS(d, a, i) EMIT(PPC_INST_XORIS | ___PPC_RA(d) | \ - ___PPC_RS(a) | IMM_L(i)) -#define PPC_EXTSW(d, a) EMIT(PPC_INST_EXTSW | ___PPC_RA(d) | \ - ___PPC_RS(a)) -#define PPC_SLW(d, a, s) EMIT(PPC_INST_SLW | ___PPC_RA(d) | \ - ___PPC_RS(a) | ___PPC_RB(s)) -#define PPC_SLD(d, a, s) EMIT(PPC_INST_SLD | ___PPC_RA(d) | \ - ___PPC_RS(a) | ___PPC_RB(s)) -#define PPC_SRW(d, a, s) EMIT(PPC_INST_SRW | ___PPC_RA(d) | \ - ___PPC_RS(a) | ___PPC_RB(s)) -#define PPC_SRAW(d, a, s) EMIT(PPC_INST_SRAW | ___PPC_RA(d) | \ - ___PPC_RS(a) | ___PPC_RB(s)) -#define PPC_SRAWI(d, a, i) EMIT(PPC_INST_SRAWI | ___PPC_RA(d) | \ - ___PPC_RS(a) | __PPC_SH(i)) -#define PPC_SRD(d, a, s) EMIT(PPC_INST_SRD | ___PPC_RA(d) | \ - ___PPC_RS(a) | ___PPC_RB(s)) -#define PPC_SRAD(d, a, s) EMIT(PPC_INST_SRAD | ___PPC_RA(d) | \ - ___PPC_RS(a) | ___PPC_RB(s)) -#define PPC_SRADI(d, a, i) EMIT(PPC_INST_SRADI | ___PPC_RA(d) | \ - ___PPC_RS(a) | __PPC_SH64(i)) -#define PPC_RLWINM(d, a, i, mb, me) EMIT(PPC_INST_RLWINM | ___PPC_RA(d) | \ - ___PPC_RS(a) | __PPC_SH(i) | \ - __PPC_MB(mb) | __PPC_ME(me)) -#define PPC_RLWINM_DOT(d, a, i, mb, me) EMIT(PPC_INST_RLWINM_DOT | \ - ___PPC_RA(d) | ___PPC_RS(a) | \ - __PPC_SH(i) | __PPC_MB(mb) | \ - __PPC_ME(me)) -#define PPC_RLWIMI(d, a, i, mb, me) EMIT(PPC_INST_RLWIMI | ___PPC_RA(d) | \ - ___PPC_RS(a) | __PPC_SH(i) | \ - __PPC_MB(mb) | __PPC_ME(me)) -#define PPC_RLDICL(d, a, i, mb) EMIT(PPC_INST_RLDICL | ___PPC_RA(d) | \ - ___PPC_RS(a) | __PPC_SH64(i) | \ - __PPC_MB64(mb)) -#define PPC_RLDICR(d, a, i, me) EMIT(PPC_INST_RLDICR | ___PPC_RA(d) | \ - ___PPC_RS(a) | __PPC_SH64(i) | \ - __PPC_ME64(me)) - -/* slwi = rlwinm Rx, Ry, n, 0, 31-n */ -#define PPC_SLWI(d, a, i) PPC_RLWINM(d, a, i, 0, 31-(i)) -/* srwi = rlwinm Rx, Ry, 32-n, n, 31 */ -#define PPC_SRWI(d, a, i) PPC_RLWINM(d, a, 32-(i), i, 31) -/* sldi = rldicr Rx, Ry, n, 63-n */ -#define PPC_SLDI(d, a, i) PPC_RLDICR(d, a, i, 63-(i)) -/* sldi = rldicl Rx, Ry, 64-n, n */ -#define PPC_SRDI(d, a, i) PPC_RLDICL(d, a, 64-(i), i) - -#define PPC_NEG(d, a) EMIT(PPC_INST_NEG | ___PPC_RT(d) | ___PPC_RA(a)) - /* Long jump; (unconditional 'branch') */ #define PPC_JMP(dest) EMIT(PPC_INST_BRANCH | \ (((dest) - (ctx->idx * 4)) & 0x03fffffc)) @@ -175,11 +35,11 @@ #define PPC_LI32(d, i) do { \ if ((int)(uintptr_t)(i) >= -32768 && \ (int)(uintptr_t)(i) < 32768) \ - PPC_LI(d, i); \ + EMIT(PPC_RAW_LI(d, i)); \ else { \ - PPC_LIS(d, IMM_H(i)); \ + EMIT(PPC_RAW_LIS(d, IMM_H(i))); \ if (IMM_L(i)) \ - PPC_ORI(d, d, IMM_L(i)); \ + EMIT(PPC_RAW_ORI(d, d, IMM_L(i))); \ } } while(0) #define PPC_LI64(d, i) do { \ @@ -188,19 +48,21 @@ PPC_LI32(d, i); \ else { \ if (!((uintptr_t)(i) & 0xffff800000000000ULL)) \ - PPC_LI(d, ((uintptr_t)(i) >> 32) & 0xffff); \ + EMIT(PPC_RAW_LI(d, ((uintptr_t)(i) >> 32) & \ + 0xffff)); \ else { \ - PPC_LIS(d, ((uintptr_t)(i) >> 48)); \ + EMIT(PPC_RAW_LIS(d, ((uintptr_t)(i) >> 48))); \ if ((uintptr_t)(i) & 0x0000ffff00000000ULL) \ - PPC_ORI(d, d, \ - ((uintptr_t)(i) >> 32) & 0xffff); \ + EMIT(PPC_RAW_ORI(d, d, \ + ((uintptr_t)(i) >> 32) & 0xffff)); \ } \ - PPC_SLDI(d, d, 32); \ + EMIT(PPC_RAW_SLDI(d, d, 32)); \ if ((uintptr_t)(i) & 0x00000000ffff0000ULL) \ - PPC_ORIS(d, d, \ - ((uintptr_t)(i) >> 16) & 0xffff); \ + EMIT(PPC_RAW_ORIS(d, d, \ + ((uintptr_t)(i) >> 16) & 0xffff)); \ if ((uintptr_t)(i) & 0x000000000000ffffULL) \ - PPC_ORI(d, d, (uintptr_t)(i) & 0xffff); \ + EMIT(PPC_RAW_ORI(d, d, (uintptr_t)(i) & \ + 0xffff)); \ } } while (0) #ifdef CONFIG_PPC64 @@ -224,7 +86,7 @@ static inline bool is_nearbranch(int offset) #define PPC_BCC(cond, dest) do { \ if (is_nearbranch((dest) - (ctx->idx * 4))) { \ PPC_BCC_SHORT(cond, dest); \ - PPC_NOP(); \ + EMIT(PPC_RAW_NOP()); \ } else { \ /* Flip the 'T or F' bit to invert comparison */ \ PPC_BCC_SHORT(cond ^ COND_CMP_TRUE, (ctx->idx+2)*4); \ diff --git a/arch/powerpc/net/bpf_jit32.h b/arch/powerpc/net/bpf_jit32.h index 753c244a7cf91..448dfd4d98e18 100644 --- a/arch/powerpc/net/bpf_jit32.h +++ b/arch/powerpc/net/bpf_jit32.h @@ -72,21 +72,21 @@ DECLARE_LOAD_FUNC(sk_load_half); DECLARE_LOAD_FUNC(sk_load_byte); DECLARE_LOAD_FUNC(sk_load_byte_msh); -#define PPC_LBZ_OFFS(r, base, i) do { if ((i) < 32768) PPC_LBZ(r, base, i); \ - else { PPC_ADDIS(r, base, IMM_HA(i)); \ - PPC_LBZ(r, r, IMM_L(i)); } } while(0) +#define PPC_LBZ_OFFS(r, base, i) do { if ((i) < 32768) EMIT(PPC_RAW_LBZ(r, base, i)); \ + else { EMIT(PPC_RAW_ADDIS(r, base, IMM_HA(i))); \ + EMIT(PPC_RAW_LBZ(r, r, IMM_L(i))); } } while(0) #define PPC_LD_OFFS(r, base, i) do { if ((i) < 32768) EMIT(PPC_RAW_LD(r, base, i)); \ - else { PPC_ADDIS(r, base, IMM_HA(i)); \ + else { EMIT(PPC_RAW_ADDIS(r, base, IMM_HA(i))); \ EMIT(PPC_RAW_LD(r, r, IMM_L(i))); } } while(0) #define PPC_LWZ_OFFS(r, base, i) do { if ((i) < 32768) EMIT(PPC_RAW_LWZ(r, base, i)); \ - else { PPC_ADDIS(r, base, IMM_HA(i)); \ + else { EMIT(PPC_RAW_ADDIS(r, base, IMM_HA(i))); \ EMIT(PPC_RAW_LWZ(r, r, IMM_L(i))); } } while(0) -#define PPC_LHZ_OFFS(r, base, i) do { if ((i) < 32768) PPC_LHZ(r, base, i); \ - else { PPC_ADDIS(r, base, IMM_HA(i)); \ - PPC_LHZ(r, r, IMM_L(i)); } } while(0) +#define PPC_LHZ_OFFS(r, base, i) do { if ((i) < 32768) EMIT(PPC_RAW_LHZ(r, base, i)); \ + else { EMIT(PPC_RAW_ADDIS(r, base, IMM_HA(i))); \ + EMIT(PPC_RAW_LHZ(r, r, IMM_L(i))); } } while(0) #ifdef CONFIG_PPC64 #define PPC_LL_OFFS(r, base, i) do { PPC_LD_OFFS(r, base, i); } while(0) @@ -107,11 +107,11 @@ DECLARE_LOAD_FUNC(sk_load_byte_msh); } while(0) #endif #else -#define PPC_BPF_LOAD_CPU(r) do { PPC_LI(r, 0); } while(0) +#define PPC_BPF_LOAD_CPU(r) do { EMIT(PPC_RAW_LI(r, 0)); } while(0) #endif #define PPC_LHBRX_OFFS(r, base, i) \ - do { PPC_LI32(r, i); PPC_LHBRX(r, r, base); } while(0) + do { PPC_LI32(r, i); EMIT(PPC_RAW_LHBRX(r, r, base)); } while(0) #ifdef __LITTLE_ENDIAN__ #define PPC_NTOHS_OFFS(r, base, i) PPC_LHBRX_OFFS(r, base, i) #else @@ -119,8 +119,8 @@ DECLARE_LOAD_FUNC(sk_load_byte_msh); #endif #define PPC_BPF_LL(r, base, i) do { EMIT(PPC_RAW_LWZ(r, base, i)); } while(0) -#define PPC_BPF_STL(r, base, i) do { PPC_STW(r, base, i); } while(0) -#define PPC_BPF_STLU(r, base, i) do { PPC_STWU(r, base, i); } while(0) +#define PPC_BPF_STL(r, base, i) do { EMIT(PPC_RAW_STW(r, base, i)); } while(0) +#define PPC_BPF_STLU(r, base, i) do { EMIT(PPC_RAW_STWU(r, base, i)); } while(0) #define SEEN_DATAREF 0x10000 /* might call external helpers */ #define SEEN_XREG 0x20000 /* X reg is used */ diff --git a/arch/powerpc/net/bpf_jit64.h b/arch/powerpc/net/bpf_jit64.h index c144a37771586..2e33c6673ff95 100644 --- a/arch/powerpc/net/bpf_jit64.h +++ b/arch/powerpc/net/bpf_jit64.h @@ -70,19 +70,21 @@ static const int b2p[] = { */ #define PPC_BPF_LL(r, base, i) do { \ if ((i) % 4) { \ - PPC_LI(b2p[TMP_REG_2], (i)); \ - PPC_LDX(r, base, b2p[TMP_REG_2]); \ + EMIT(PPC_RAW_LI(b2p[TMP_REG_2], (i)));\ + EMIT(PPC_RAW_LDX(r, base, \ + b2p[TMP_REG_2])); \ } else \ EMIT(PPC_RAW_LD(r, base, i)); \ } while(0) #define PPC_BPF_STL(r, base, i) do { \ if ((i) % 4) { \ - PPC_LI(b2p[TMP_REG_2], (i)); \ - PPC_STDX(r, base, b2p[TMP_REG_2]); \ + EMIT(PPC_RAW_LI(b2p[TMP_REG_2], (i)));\ + EMIT(PPC_RAW_STDX(r, base, \ + b2p[TMP_REG_2])); \ } else \ EMIT(PPC_RAW_STD(r, base, i)); \ } while(0) -#define PPC_BPF_STLU(r, base, i) do { PPC_STDU(r, base, i); } while(0) +#define PPC_BPF_STLU(r, base, i) do { EMIT(PPC_RAW_STDU(r, base, i)); } while(0) #define SEEN_FUNC 0x1000 /* might call external helpers */ #define SEEN_STACK 0x2000 /* uses BPF stack */ diff --git a/arch/powerpc/net/bpf_jit_comp.c b/arch/powerpc/net/bpf_jit_comp.c index abcf56c00be50..16d09b36fe063 100644 --- a/arch/powerpc/net/bpf_jit_comp.c +++ b/arch/powerpc/net/bpf_jit_comp.c @@ -61,7 +61,7 @@ static void bpf_jit_build_prologue(struct bpf_prog *fp, u32 *image, PPC_LWZ_OFFS(r_scratch1, r_skb, offsetof(struct sk_buff, data_len)); PPC_LWZ_OFFS(r_HL, r_skb, offsetof(struct sk_buff, len)); - PPC_SUB(r_HL, r_HL, r_scratch1); + EMIT(PPC_RAW_SUB(r_HL, r_HL, r_scratch1)); PPC_LL_OFFS(r_D, r_skb, offsetof(struct sk_buff, data)); } @@ -70,12 +70,12 @@ static void bpf_jit_build_prologue(struct bpf_prog *fp, u32 *image, * TODO: Could also detect whether first instr. sets X and * avoid this (as below, with A). */ - PPC_LI(r_X, 0); + EMIT(PPC_RAW_LI(r_X, 0)); } /* make sure we dont leak kernel information to user */ if (bpf_needs_clear_a(&filter[0])) - PPC_LI(r_A, 0); + EMIT(PPC_RAW_LI(r_A, 0)); } static void bpf_jit_build_epilogue(u32 *image, struct codegen_context *ctx) @@ -83,10 +83,10 @@ static void bpf_jit_build_epilogue(u32 *image, struct codegen_context *ctx) int i; if (ctx->seen & (SEEN_MEM | SEEN_DATAREF)) { - PPC_ADDI(1, 1, BPF_PPC_STACKFRAME); + EMIT(PPC_RAW_ADDI(1, 1, BPF_PPC_STACKFRAME)); if (ctx->seen & SEEN_DATAREF) { PPC_BPF_LL(0, 1, PPC_LR_STKOFF); - PPC_MTLR(0); + EMIT(PPC_RAW_MTLR(0)); PPC_BPF_LL(r_D, 1, -(REG_SZ*(32-r_D))); PPC_BPF_LL(r_HL, 1, -(REG_SZ*(32-r_HL))); } @@ -100,7 +100,7 @@ static void bpf_jit_build_epilogue(u32 *image, struct codegen_context *ctx) } /* The RETs have left a return value in R3. */ - PPC_BLR(); + EMIT(PPC_RAW_BLR()); } #define CHOOSE_LOAD_FUNC(K, func) \ @@ -139,119 +139,119 @@ static int bpf_jit_build_body(struct bpf_prog *fp, u32 *image, case BPF_ALU | BPF_ADD | BPF_K: /* A += K; */ if (!K) break; - PPC_ADDI(r_A, r_A, IMM_L(K)); + EMIT(PPC_RAW_ADDI(r_A, r_A, IMM_L(K))); if (K >= 32768) - PPC_ADDIS(r_A, r_A, IMM_HA(K)); + EMIT(PPC_RAW_ADDIS(r_A, r_A, IMM_HA(K))); break; case BPF_ALU | BPF_SUB | BPF_X: /* A -= X; */ ctx->seen |= SEEN_XREG; - PPC_SUB(r_A, r_A, r_X); + EMIT(PPC_RAW_SUB(r_A, r_A, r_X)); break; case BPF_ALU | BPF_SUB | BPF_K: /* A -= K */ if (!K) break; - PPC_ADDI(r_A, r_A, IMM_L(-K)); + EMIT(PPC_RAW_ADDI(r_A, r_A, IMM_L(-K))); if (K >= 32768) - PPC_ADDIS(r_A, r_A, IMM_HA(-K)); + EMIT(PPC_RAW_ADDIS(r_A, r_A, IMM_HA(-K))); break; case BPF_ALU | BPF_MUL | BPF_X: /* A *= X; */ ctx->seen |= SEEN_XREG; - PPC_MULW(r_A, r_A, r_X); + EMIT(PPC_RAW_MULW(r_A, r_A, r_X)); break; case BPF_ALU | BPF_MUL | BPF_K: /* A *= K */ if (K < 32768) - PPC_MULI(r_A, r_A, K); + EMIT(PPC_RAW_MULI(r_A, r_A, K)); else { PPC_LI32(r_scratch1, K); - PPC_MULW(r_A, r_A, r_scratch1); + EMIT(PPC_RAW_MULW(r_A, r_A, r_scratch1)); } break; case BPF_ALU | BPF_MOD | BPF_X: /* A %= X; */ case BPF_ALU | BPF_DIV | BPF_X: /* A /= X; */ ctx->seen |= SEEN_XREG; - PPC_CMPWI(r_X, 0); + EMIT(PPC_RAW_CMPWI(r_X, 0)); if (ctx->pc_ret0 != -1) { PPC_BCC(COND_EQ, addrs[ctx->pc_ret0]); } else { PPC_BCC_SHORT(COND_NE, (ctx->idx*4)+12); - PPC_LI(r_ret, 0); + EMIT(PPC_RAW_LI(r_ret, 0)); PPC_JMP(exit_addr); } if (code == (BPF_ALU | BPF_MOD | BPF_X)) { - PPC_DIVWU(r_scratch1, r_A, r_X); - PPC_MULW(r_scratch1, r_X, r_scratch1); - PPC_SUB(r_A, r_A, r_scratch1); + EMIT(PPC_RAW_DIVWU(r_scratch1, r_A, r_X)); + EMIT(PPC_RAW_MULW(r_scratch1, r_X, r_scratch1)); + EMIT(PPC_RAW_SUB(r_A, r_A, r_scratch1)); } else { - PPC_DIVWU(r_A, r_A, r_X); + EMIT(PPC_RAW_DIVWU(r_A, r_A, r_X)); } break; case BPF_ALU | BPF_MOD | BPF_K: /* A %= K; */ PPC_LI32(r_scratch2, K); - PPC_DIVWU(r_scratch1, r_A, r_scratch2); - PPC_MULW(r_scratch1, r_scratch2, r_scratch1); - PPC_SUB(r_A, r_A, r_scratch1); + EMIT(PPC_RAW_DIVWU(r_scratch1, r_A, r_scratch2)); + EMIT(PPC_RAW_MULW(r_scratch1, r_scratch2, r_scratch1)); + EMIT(PPC_RAW_SUB(r_A, r_A, r_scratch1)); break; case BPF_ALU | BPF_DIV | BPF_K: /* A /= K */ if (K == 1) break; PPC_LI32(r_scratch1, K); - PPC_DIVWU(r_A, r_A, r_scratch1); + EMIT(PPC_RAW_DIVWU(r_A, r_A, r_scratch1)); break; case BPF_ALU | BPF_AND | BPF_X: ctx->seen |= SEEN_XREG; - PPC_AND(r_A, r_A, r_X); + EMIT(PPC_RAW_AND(r_A, r_A, r_X)); break; case BPF_ALU | BPF_AND | BPF_K: if (!IMM_H(K)) - PPC_ANDI(r_A, r_A, K); + EMIT(PPC_RAW_ANDI(r_A, r_A, K)); else { PPC_LI32(r_scratch1, K); - PPC_AND(r_A, r_A, r_scratch1); + EMIT(PPC_RAW_AND(r_A, r_A, r_scratch1)); } break; case BPF_ALU | BPF_OR | BPF_X: ctx->seen |= SEEN_XREG; - PPC_OR(r_A, r_A, r_X); + EMIT(PPC_RAW_OR(r_A, r_A, r_X)); break; case BPF_ALU | BPF_OR | BPF_K: if (IMM_L(K)) - PPC_ORI(r_A, r_A, IMM_L(K)); + EMIT(PPC_RAW_ORI(r_A, r_A, IMM_L(K))); if (K >= 65536) - PPC_ORIS(r_A, r_A, IMM_H(K)); + EMIT(PPC_RAW_ORIS(r_A, r_A, IMM_H(K))); break; case BPF_ANC | SKF_AD_ALU_XOR_X: case BPF_ALU | BPF_XOR | BPF_X: /* A ^= X */ ctx->seen |= SEEN_XREG; - PPC_XOR(r_A, r_A, r_X); + EMIT(PPC_RAW_XOR(r_A, r_A, r_X)); break; case BPF_ALU | BPF_XOR | BPF_K: /* A ^= K */ if (IMM_L(K)) - PPC_XORI(r_A, r_A, IMM_L(K)); + EMIT(PPC_RAW_XORI(r_A, r_A, IMM_L(K))); if (K >= 65536) - PPC_XORIS(r_A, r_A, IMM_H(K)); + EMIT(PPC_RAW_XORIS(r_A, r_A, IMM_H(K))); break; case BPF_ALU | BPF_LSH | BPF_X: /* A <<= X; */ ctx->seen |= SEEN_XREG; - PPC_SLW(r_A, r_A, r_X); + EMIT(PPC_RAW_SLW(r_A, r_A, r_X)); break; case BPF_ALU | BPF_LSH | BPF_K: if (K == 0) break; else - PPC_SLWI(r_A, r_A, K); + EMIT(PPC_RAW_SLWI(r_A, r_A, K)); break; case BPF_ALU | BPF_RSH | BPF_X: /* A >>= X; */ ctx->seen |= SEEN_XREG; - PPC_SRW(r_A, r_A, r_X); + EMIT(PPC_RAW_SRW(r_A, r_A, r_X)); break; case BPF_ALU | BPF_RSH | BPF_K: /* A >>= K; */ if (K == 0) break; else - PPC_SRWI(r_A, r_A, K); + EMIT(PPC_RAW_SRWI(r_A, r_A, K)); break; case BPF_ALU | BPF_NEG: - PPC_NEG(r_A, r_A); + EMIT(PPC_RAW_NEG(r_A, r_A)); break; case BPF_RET | BPF_K: PPC_LI32(r_ret, K); @@ -277,24 +277,24 @@ static int bpf_jit_build_body(struct bpf_prog *fp, u32 *image, if (ctx->seen) PPC_JMP(exit_addr); else - PPC_BLR(); + EMIT(PPC_RAW_BLR()); } break; case BPF_RET | BPF_A: - PPC_MR(r_ret, r_A); + EMIT(PPC_RAW_MR(r_ret, r_A)); if (i != flen - 1) { if (ctx->seen) PPC_JMP(exit_addr); else - PPC_BLR(); + EMIT(PPC_RAW_BLR()); } break; case BPF_MISC | BPF_TAX: /* X = A */ - PPC_MR(r_X, r_A); + EMIT(PPC_RAW_MR(r_X, r_A)); break; case BPF_MISC | BPF_TXA: /* A = X */ ctx->seen |= SEEN_XREG; - PPC_MR(r_A, r_X); + EMIT(PPC_RAW_MR(r_A, r_X)); break; /*** Constant loads/M[] access ***/ @@ -305,19 +305,19 @@ static int bpf_jit_build_body(struct bpf_prog *fp, u32 *image, PPC_LI32(r_X, K); break; case BPF_LD | BPF_MEM: /* A = mem[K] */ - PPC_MR(r_A, r_M + (K & 0xf)); + EMIT(PPC_RAW_MR(r_A, r_M + (K & 0xf))); ctx->seen |= SEEN_MEM | (1<<(K & 0xf)); break; case BPF_LDX | BPF_MEM: /* X = mem[K] */ - PPC_MR(r_X, r_M + (K & 0xf)); + EMIT(PPC_RAW_MR(r_X, r_M + (K & 0xf))); ctx->seen |= SEEN_MEM | (1<<(K & 0xf)); break; case BPF_ST: /* mem[K] = A */ - PPC_MR(r_M + (K & 0xf), r_A); + EMIT(PPC_RAW_MR(r_M + (K & 0xf), r_A)); ctx->seen |= SEEN_MEM | (1<<(K & 0xf)); break; case BPF_STX: /* mem[K] = X */ - PPC_MR(r_M + (K & 0xf), r_X); + EMIT(PPC_RAW_MR(r_M + (K & 0xf), r_X)); ctx->seen |= SEEN_XREG | SEEN_MEM | (1<<(K & 0xf)); break; case BPF_LD | BPF_W | BPF_LEN: /* A = skb->len; */ @@ -346,13 +346,13 @@ static int bpf_jit_build_body(struct bpf_prog *fp, u32 *image, type) != 2); PPC_LL_OFFS(r_scratch1, r_skb, offsetof(struct sk_buff, dev)); - PPC_CMPDI(r_scratch1, 0); + EMIT(PPC_RAW_CMPDI(r_scratch1, 0)); if (ctx->pc_ret0 != -1) { PPC_BCC(COND_EQ, addrs[ctx->pc_ret0]); } else { /* Exit, returning 0; first pass hits here. */ PPC_BCC_SHORT(COND_NE, ctx->idx * 4 + 12); - PPC_LI(r_ret, 0); + EMIT(PPC_RAW_LI(r_ret, 0)); PPC_JMP(exit_addr); } if (code == (BPF_ANC | SKF_AD_IFINDEX)) { @@ -383,9 +383,9 @@ static int bpf_jit_build_body(struct bpf_prog *fp, u32 *image, case BPF_ANC | SKF_AD_VLAN_TAG_PRESENT: PPC_LBZ_OFFS(r_A, r_skb, PKT_VLAN_PRESENT_OFFSET()); if (PKT_VLAN_PRESENT_BIT) - PPC_SRWI(r_A, r_A, PKT_VLAN_PRESENT_BIT); + EMIT(PPC_RAW_SRWI(r_A, r_A, PKT_VLAN_PRESENT_BIT)); if (PKT_VLAN_PRESENT_BIT < 7) - PPC_ANDI(r_A, r_A, 1); + EMIT(PPC_RAW_ANDI(r_A, r_A, 1)); break; case BPF_ANC | SKF_AD_QUEUE: BUILD_BUG_ON(sizeof_field(struct sk_buff, @@ -395,8 +395,8 @@ static int bpf_jit_build_body(struct bpf_prog *fp, u32 *image, break; case BPF_ANC | SKF_AD_PKTTYPE: PPC_LBZ_OFFS(r_A, r_skb, PKT_TYPE_OFFSET()); - PPC_ANDI(r_A, r_A, PKT_TYPE_MAX); - PPC_SRWI(r_A, r_A, 5); + EMIT(PPC_RAW_ANDI(r_A, r_A, PKT_TYPE_MAX)); + EMIT(PPC_RAW_SRWI(r_A, r_A, 5)); break; case BPF_ANC | SKF_AD_CPU: PPC_BPF_LOAD_CPU(r_A); @@ -414,9 +414,9 @@ static int bpf_jit_build_body(struct bpf_prog *fp, u32 *image, /* Load from [K]. */ ctx->seen |= SEEN_DATAREF; PPC_FUNC_ADDR(r_scratch1, func); - PPC_MTLR(r_scratch1); + EMIT(PPC_RAW_MTLR(r_scratch1)); PPC_LI32(r_addr, K); - PPC_BLRL(); + EMIT(PPC_RAW_BLRL()); /* * Helper returns 'lt' condition on error, and an * appropriate return value in r3 @@ -440,11 +440,11 @@ static int bpf_jit_build_body(struct bpf_prog *fp, u32 *image, */ ctx->seen |= SEEN_DATAREF | SEEN_XREG; PPC_FUNC_ADDR(r_scratch1, func); - PPC_MTLR(r_scratch1); - PPC_ADDI(r_addr, r_X, IMM_L(K)); + EMIT(PPC_RAW_MTLR(r_scratch1)); + EMIT(PPC_RAW_ADDI(r_addr, r_X, IMM_L(K))); if (K >= 32768) - PPC_ADDIS(r_addr, r_addr, IMM_HA(K)); - PPC_BLRL(); + EMIT(PPC_RAW_ADDIS(r_addr, r_addr, IMM_HA(K))); + EMIT(PPC_RAW_BLRL()); /* If error, cr0.LT set */ PPC_BCC(COND_LT, exit_addr); break; @@ -489,30 +489,30 @@ static int bpf_jit_build_body(struct bpf_prog *fp, u32 *image, case BPF_JMP | BPF_JGE | BPF_X: case BPF_JMP | BPF_JEQ | BPF_X: ctx->seen |= SEEN_XREG; - PPC_CMPLW(r_A, r_X); + EMIT(PPC_RAW_CMPLW(r_A, r_X)); break; case BPF_JMP | BPF_JSET | BPF_X: ctx->seen |= SEEN_XREG; - PPC_AND_DOT(r_scratch1, r_A, r_X); + EMIT(PPC_RAW_AND_DOT(r_scratch1, r_A, r_X)); break; case BPF_JMP | BPF_JEQ | BPF_K: case BPF_JMP | BPF_JGT | BPF_K: case BPF_JMP | BPF_JGE | BPF_K: if (K < 32768) - PPC_CMPLWI(r_A, K); + EMIT(PPC_RAW_CMPLWI(r_A, K)); else { PPC_LI32(r_scratch1, K); - PPC_CMPLW(r_A, r_scratch1); + EMIT(PPC_RAW_CMPLW(r_A, r_scratch1)); } break; case BPF_JMP | BPF_JSET | BPF_K: if (K < 32768) /* PPC_ANDI is /only/ dot-form */ - PPC_ANDI(r_scratch1, r_A, K); + EMIT(PPC_RAW_ANDI(r_scratch1, r_A, K)); else { PPC_LI32(r_scratch1, K); - PPC_AND_DOT(r_scratch1, r_A, - r_scratch1); + EMIT(PPC_RAW_AND_DOT(r_scratch1, r_A, + r_scratch1)); } break; } diff --git a/arch/powerpc/net/bpf_jit_comp64.c b/arch/powerpc/net/bpf_jit_comp64.c index f721fbe6ca4dd..022103c6a201a 100644 --- a/arch/powerpc/net/bpf_jit_comp64.c +++ b/arch/powerpc/net/bpf_jit_comp64.c @@ -95,12 +95,12 @@ static void bpf_jit_build_prologue(u32 *image, struct codegen_context *ctx) * invoked through a tail call. */ if (ctx->seen & SEEN_TAILCALL) { - PPC_LI(b2p[TMP_REG_1], 0); + EMIT(PPC_RAW_LI(b2p[TMP_REG_1], 0)); /* this goes in the redzone */ PPC_BPF_STL(b2p[TMP_REG_1], 1, -(BPF_PPC_STACK_SAVE + 8)); } else { - PPC_NOP(); - PPC_NOP(); + EMIT(PPC_RAW_NOP()); + EMIT(PPC_RAW_NOP()); } #define BPF_TAILCALL_PROLOGUE_SIZE 8 @@ -129,8 +129,8 @@ static void bpf_jit_build_prologue(u32 *image, struct codegen_context *ctx) /* Setup frame pointer to point to the bpf stack area */ if (bpf_is_seen_register(ctx, BPF_REG_FP)) - PPC_ADDI(b2p[BPF_REG_FP], 1, - STACK_FRAME_MIN_SIZE + ctx->stack_size); + EMIT(PPC_RAW_ADDI(b2p[BPF_REG_FP], 1, + STACK_FRAME_MIN_SIZE + ctx->stack_size)); } static void bpf_jit_emit_common_epilogue(u32 *image, struct codegen_context *ctx) @@ -144,10 +144,10 @@ static void bpf_jit_emit_common_epilogue(u32 *image, struct codegen_context *ctx /* Tear down our stack frame */ if (bpf_has_stack_frame(ctx)) { - PPC_ADDI(1, 1, BPF_PPC_STACKFRAME + ctx->stack_size); + EMIT(PPC_RAW_ADDI(1, 1, BPF_PPC_STACKFRAME + ctx->stack_size)); if (ctx->seen & SEEN_FUNC) { PPC_BPF_LL(0, 1, PPC_LR_STKOFF); - PPC_MTLR(0); + EMIT(PPC_RAW_MTLR(0)); } } } @@ -157,9 +157,9 @@ static void bpf_jit_build_epilogue(u32 *image, struct codegen_context *ctx) bpf_jit_emit_common_epilogue(image, ctx); /* Move result to r3 */ - PPC_MR(3, b2p[BPF_REG_0]); + EMIT(PPC_RAW_MR(3, b2p[BPF_REG_0])); - PPC_BLR(); + EMIT(PPC_RAW_BLR()); } static void bpf_jit_emit_func_call_hlp(u32 *image, struct codegen_context *ctx, @@ -171,7 +171,7 @@ static void bpf_jit_emit_func_call_hlp(u32 *image, struct codegen_context *ctx, /* Load actual entry point from function descriptor */ PPC_BPF_LL(b2p[TMP_REG_1], b2p[TMP_REG_2], 0); /* ... and move it to LR */ - PPC_MTLR(b2p[TMP_REG_1]); + EMIT(PPC_RAW_MTLR(b2p[TMP_REG_1])); /* * Load TOC from function descriptor at offset 8. * We can clobber r2 since we get called through a @@ -182,9 +182,9 @@ static void bpf_jit_emit_func_call_hlp(u32 *image, struct codegen_context *ctx, #else /* We can clobber r12 */ PPC_FUNC_ADDR(12, func); - PPC_MTLR(12); + EMIT(PPC_RAW_MTLR(12)); #endif - PPC_BLRL(); + EMIT(PPC_RAW_BLRL()); } static void bpf_jit_emit_func_call_rel(u32 *image, struct codegen_context *ctx, @@ -206,7 +206,7 @@ static void bpf_jit_emit_func_call_rel(u32 *image, struct codegen_context *ctx, * that PPC_LI64() can emit. */ for (i = ctx->idx - ctx_idx; i < 5; i++) - PPC_NOP(); + EMIT(PPC_RAW_NOP()); #ifdef PPC64_ELF_ABI_v1 /* @@ -220,8 +220,8 @@ static void bpf_jit_emit_func_call_rel(u32 *image, struct codegen_context *ctx, PPC_BPF_LL(12, 12, 0); #endif - PPC_MTLR(12); - PPC_BLRL(); + EMIT(PPC_RAW_MTLR(12)); + EMIT(PPC_RAW_BLRL()); } static void bpf_jit_emit_tail_call(u32 *image, struct codegen_context *ctx, u32 out) @@ -240,8 +240,8 @@ static void bpf_jit_emit_tail_call(u32 *image, struct codegen_context *ctx, u32 * goto out; */ EMIT(PPC_RAW_LWZ(b2p[TMP_REG_1], b2p_bpf_array, offsetof(struct bpf_array, map.max_entries))); - PPC_RLWINM(b2p_index, b2p_index, 0, 0, 31); - PPC_CMPLW(b2p_index, b2p[TMP_REG_1]); + EMIT(PPC_RAW_RLWINM(b2p_index, b2p_index, 0, 0, 31)); + EMIT(PPC_RAW_CMPLW(b2p_index, b2p[TMP_REG_1])); PPC_BCC(COND_GE, out); /* @@ -249,17 +249,17 @@ static void bpf_jit_emit_tail_call(u32 *image, struct codegen_context *ctx, u32 * goto out; */ PPC_BPF_LL(b2p[TMP_REG_1], 1, bpf_jit_stack_tailcallcnt(ctx)); - PPC_CMPLWI(b2p[TMP_REG_1], MAX_TAIL_CALL_CNT); + EMIT(PPC_RAW_CMPLWI(b2p[TMP_REG_1], MAX_TAIL_CALL_CNT)); PPC_BCC(COND_GT, out); /* * tail_call_cnt++; */ - PPC_ADDI(b2p[TMP_REG_1], b2p[TMP_REG_1], 1); + EMIT(PPC_RAW_ADDI(b2p[TMP_REG_1], b2p[TMP_REG_1], 1)); PPC_BPF_STL(b2p[TMP_REG_1], 1, bpf_jit_stack_tailcallcnt(ctx)); /* prog = array->ptrs[index]; */ - PPC_MULI(b2p[TMP_REG_1], b2p_index, 8); + EMIT(PPC_RAW_MULI(b2p[TMP_REG_1], b2p_index, 8)); EMIT(PPC_RAW_ADD(b2p[TMP_REG_1], b2p[TMP_REG_1], b2p_bpf_array)); PPC_BPF_LL(b2p[TMP_REG_1], b2p[TMP_REG_1], offsetof(struct bpf_array, ptrs)); @@ -267,24 +267,24 @@ static void bpf_jit_emit_tail_call(u32 *image, struct codegen_context *ctx, u32 * if (prog == NULL) * goto out; */ - PPC_CMPLDI(b2p[TMP_REG_1], 0); + EMIT(PPC_RAW_CMPLDI(b2p[TMP_REG_1], 0)); PPC_BCC(COND_EQ, out); /* goto *(prog->bpf_func + prologue_size); */ PPC_BPF_LL(b2p[TMP_REG_1], b2p[TMP_REG_1], offsetof(struct bpf_prog, bpf_func)); #ifdef PPC64_ELF_ABI_v1 /* skip past the function descriptor */ - PPC_ADDI(b2p[TMP_REG_1], b2p[TMP_REG_1], - FUNCTION_DESCR_SIZE + BPF_TAILCALL_PROLOGUE_SIZE); + EMIT(PPC_RAW_ADDI(b2p[TMP_REG_1], b2p[TMP_REG_1], + FUNCTION_DESCR_SIZE + BPF_TAILCALL_PROLOGUE_SIZE)); #else - PPC_ADDI(b2p[TMP_REG_1], b2p[TMP_REG_1], BPF_TAILCALL_PROLOGUE_SIZE); + EMIT(PPC_RAW_ADDI(b2p[TMP_REG_1], b2p[TMP_REG_1], BPF_TAILCALL_PROLOGUE_SIZE)); #endif - PPC_MTCTR(b2p[TMP_REG_1]); + EMIT(PPC_RAW_MTCTR(b2p[TMP_REG_1])); /* tear down stack, restore NVRs, ... */ bpf_jit_emit_common_epilogue(image, ctx); - PPC_BCTR(); + EMIT(PPC_RAW_BCTR()); /* out: */ } @@ -344,7 +344,7 @@ static int bpf_jit_build_body(struct bpf_prog *fp, u32 *image, goto bpf_alu32_trunc; case BPF_ALU | BPF_SUB | BPF_X: /* (u32) dst -= (u32) src */ case BPF_ALU64 | BPF_SUB | BPF_X: /* dst -= src */ - PPC_SUB(dst_reg, dst_reg, src_reg); + EMIT(PPC_RAW_SUB(dst_reg, dst_reg, src_reg)); goto bpf_alu32_trunc; case BPF_ALU | BPF_ADD | BPF_K: /* (u32) dst += (u32) imm */ case BPF_ALU | BPF_SUB | BPF_K: /* (u32) dst -= (u32) imm */ @@ -354,7 +354,7 @@ static int bpf_jit_build_body(struct bpf_prog *fp, u32 *image, imm = -imm; if (imm) { if (imm >= -32768 && imm < 32768) - PPC_ADDI(dst_reg, dst_reg, IMM_L(imm)); + EMIT(PPC_RAW_ADDI(dst_reg, dst_reg, IMM_L(imm))); else { PPC_LI32(b2p[TMP_REG_1], imm); EMIT(PPC_RAW_ADD(dst_reg, dst_reg, b2p[TMP_REG_1])); @@ -364,43 +364,43 @@ static int bpf_jit_build_body(struct bpf_prog *fp, u32 *image, case BPF_ALU | BPF_MUL | BPF_X: /* (u32) dst *= (u32) src */ case BPF_ALU64 | BPF_MUL | BPF_X: /* dst *= src */ if (BPF_CLASS(code) == BPF_ALU) - PPC_MULW(dst_reg, dst_reg, src_reg); + EMIT(PPC_RAW_MULW(dst_reg, dst_reg, src_reg)); else - PPC_MULD(dst_reg, dst_reg, src_reg); + EMIT(PPC_RAW_MULD(dst_reg, dst_reg, src_reg)); goto bpf_alu32_trunc; case BPF_ALU | BPF_MUL | BPF_K: /* (u32) dst *= (u32) imm */ case BPF_ALU64 | BPF_MUL | BPF_K: /* dst *= imm */ if (imm >= -32768 && imm < 32768) - PPC_MULI(dst_reg, dst_reg, IMM_L(imm)); + EMIT(PPC_RAW_MULI(dst_reg, dst_reg, IMM_L(imm))); else { PPC_LI32(b2p[TMP_REG_1], imm); if (BPF_CLASS(code) == BPF_ALU) - PPC_MULW(dst_reg, dst_reg, - b2p[TMP_REG_1]); + EMIT(PPC_RAW_MULW(dst_reg, dst_reg, + b2p[TMP_REG_1])); else - PPC_MULD(dst_reg, dst_reg, - b2p[TMP_REG_1]); + EMIT(PPC_RAW_MULD(dst_reg, dst_reg, + b2p[TMP_REG_1])); } goto bpf_alu32_trunc; case BPF_ALU | BPF_DIV | BPF_X: /* (u32) dst /= (u32) src */ case BPF_ALU | BPF_MOD | BPF_X: /* (u32) dst %= (u32) src */ if (BPF_OP(code) == BPF_MOD) { - PPC_DIVWU(b2p[TMP_REG_1], dst_reg, src_reg); - PPC_MULW(b2p[TMP_REG_1], src_reg, - b2p[TMP_REG_1]); - PPC_SUB(dst_reg, dst_reg, b2p[TMP_REG_1]); + EMIT(PPC_RAW_DIVWU(b2p[TMP_REG_1], dst_reg, src_reg)); + EMIT(PPC_RAW_MULW(b2p[TMP_REG_1], src_reg, + b2p[TMP_REG_1])); + EMIT(PPC_RAW_SUB(dst_reg, dst_reg, b2p[TMP_REG_1])); } else - PPC_DIVWU(dst_reg, dst_reg, src_reg); + EMIT(PPC_RAW_DIVWU(dst_reg, dst_reg, src_reg)); goto bpf_alu32_trunc; case BPF_ALU64 | BPF_DIV | BPF_X: /* dst /= src */ case BPF_ALU64 | BPF_MOD | BPF_X: /* dst %= src */ if (BPF_OP(code) == BPF_MOD) { - PPC_DIVDU(b2p[TMP_REG_1], dst_reg, src_reg); - PPC_MULD(b2p[TMP_REG_1], src_reg, - b2p[TMP_REG_1]); - PPC_SUB(dst_reg, dst_reg, b2p[TMP_REG_1]); + EMIT(PPC_RAW_DIVDU(b2p[TMP_REG_1], dst_reg, src_reg)); + EMIT(PPC_RAW_MULD(b2p[TMP_REG_1], src_reg, + b2p[TMP_REG_1])); + EMIT(PPC_RAW_SUB(dst_reg, dst_reg, b2p[TMP_REG_1])); } else - PPC_DIVDU(dst_reg, dst_reg, src_reg); + EMIT(PPC_RAW_DIVDU(dst_reg, dst_reg, src_reg)); break; case BPF_ALU | BPF_MOD | BPF_K: /* (u32) dst %= (u32) imm */ case BPF_ALU | BPF_DIV | BPF_K: /* (u32) dst /= (u32) imm */ @@ -415,35 +415,37 @@ static int bpf_jit_build_body(struct bpf_prog *fp, u32 *image, switch (BPF_CLASS(code)) { case BPF_ALU: if (BPF_OP(code) == BPF_MOD) { - PPC_DIVWU(b2p[TMP_REG_2], dst_reg, - b2p[TMP_REG_1]); - PPC_MULW(b2p[TMP_REG_1], + EMIT(PPC_RAW_DIVWU(b2p[TMP_REG_2], + dst_reg, + b2p[TMP_REG_1])); + EMIT(PPC_RAW_MULW(b2p[TMP_REG_1], b2p[TMP_REG_1], - b2p[TMP_REG_2]); - PPC_SUB(dst_reg, dst_reg, - b2p[TMP_REG_1]); + b2p[TMP_REG_2])); + EMIT(PPC_RAW_SUB(dst_reg, dst_reg, + b2p[TMP_REG_1])); } else - PPC_DIVWU(dst_reg, dst_reg, - b2p[TMP_REG_1]); + EMIT(PPC_RAW_DIVWU(dst_reg, dst_reg, + b2p[TMP_REG_1])); break; case BPF_ALU64: if (BPF_OP(code) == BPF_MOD) { - PPC_DIVDU(b2p[TMP_REG_2], dst_reg, - b2p[TMP_REG_1]); - PPC_MULD(b2p[TMP_REG_1], + EMIT(PPC_RAW_DIVDU(b2p[TMP_REG_2], + dst_reg, + b2p[TMP_REG_1])); + EMIT(PPC_RAW_MULD(b2p[TMP_REG_1], b2p[TMP_REG_1], - b2p[TMP_REG_2]); - PPC_SUB(dst_reg, dst_reg, - b2p[TMP_REG_1]); + b2p[TMP_REG_2])); + EMIT(PPC_RAW_SUB(dst_reg, dst_reg, + b2p[TMP_REG_1])); } else - PPC_DIVDU(dst_reg, dst_reg, - b2p[TMP_REG_1]); + EMIT(PPC_RAW_DIVDU(dst_reg, dst_reg, + b2p[TMP_REG_1])); break; } goto bpf_alu32_trunc; case BPF_ALU | BPF_NEG: /* (u32) dst = -dst */ case BPF_ALU64 | BPF_NEG: /* dst = -dst */ - PPC_NEG(dst_reg, dst_reg); + EMIT(PPC_RAW_NEG(dst_reg, dst_reg)); goto bpf_alu32_trunc; /* @@ -451,101 +453,101 @@ static int bpf_jit_build_body(struct bpf_prog *fp, u32 *image, */ case BPF_ALU | BPF_AND | BPF_X: /* (u32) dst = dst & src */ case BPF_ALU64 | BPF_AND | BPF_X: /* dst = dst & src */ - PPC_AND(dst_reg, dst_reg, src_reg); + EMIT(PPC_RAW_AND(dst_reg, dst_reg, src_reg)); goto bpf_alu32_trunc; case BPF_ALU | BPF_AND | BPF_K: /* (u32) dst = dst & imm */ case BPF_ALU64 | BPF_AND | BPF_K: /* dst = dst & imm */ if (!IMM_H(imm)) - PPC_ANDI(dst_reg, dst_reg, IMM_L(imm)); + EMIT(PPC_RAW_ANDI(dst_reg, dst_reg, IMM_L(imm))); else { /* Sign-extended */ PPC_LI32(b2p[TMP_REG_1], imm); - PPC_AND(dst_reg, dst_reg, b2p[TMP_REG_1]); + EMIT(PPC_RAW_AND(dst_reg, dst_reg, b2p[TMP_REG_1])); } goto bpf_alu32_trunc; case BPF_ALU | BPF_OR | BPF_X: /* dst = (u32) dst | (u32) src */ case BPF_ALU64 | BPF_OR | BPF_X: /* dst = dst | src */ - PPC_OR(dst_reg, dst_reg, src_reg); + EMIT(PPC_RAW_OR(dst_reg, dst_reg, src_reg)); goto bpf_alu32_trunc; case BPF_ALU | BPF_OR | BPF_K:/* dst = (u32) dst | (u32) imm */ case BPF_ALU64 | BPF_OR | BPF_K:/* dst = dst | imm */ if (imm < 0 && BPF_CLASS(code) == BPF_ALU64) { /* Sign-extended */ PPC_LI32(b2p[TMP_REG_1], imm); - PPC_OR(dst_reg, dst_reg, b2p[TMP_REG_1]); + EMIT(PPC_RAW_OR(dst_reg, dst_reg, b2p[TMP_REG_1])); } else { if (IMM_L(imm)) - PPC_ORI(dst_reg, dst_reg, IMM_L(imm)); + EMIT(PPC_RAW_ORI(dst_reg, dst_reg, IMM_L(imm))); if (IMM_H(imm)) - PPC_ORIS(dst_reg, dst_reg, IMM_H(imm)); + EMIT(PPC_RAW_ORIS(dst_reg, dst_reg, IMM_H(imm))); } goto bpf_alu32_trunc; case BPF_ALU | BPF_XOR | BPF_X: /* (u32) dst ^= src */ case BPF_ALU64 | BPF_XOR | BPF_X: /* dst ^= src */ - PPC_XOR(dst_reg, dst_reg, src_reg); + EMIT(PPC_RAW_XOR(dst_reg, dst_reg, src_reg)); goto bpf_alu32_trunc; case BPF_ALU | BPF_XOR | BPF_K: /* (u32) dst ^= (u32) imm */ case BPF_ALU64 | BPF_XOR | BPF_K: /* dst ^= imm */ if (imm < 0 && BPF_CLASS(code) == BPF_ALU64) { /* Sign-extended */ PPC_LI32(b2p[TMP_REG_1], imm); - PPC_XOR(dst_reg, dst_reg, b2p[TMP_REG_1]); + EMIT(PPC_RAW_XOR(dst_reg, dst_reg, b2p[TMP_REG_1])); } else { if (IMM_L(imm)) - PPC_XORI(dst_reg, dst_reg, IMM_L(imm)); + EMIT(PPC_RAW_XORI(dst_reg, dst_reg, IMM_L(imm))); if (IMM_H(imm)) - PPC_XORIS(dst_reg, dst_reg, IMM_H(imm)); + EMIT(PPC_RAW_XORIS(dst_reg, dst_reg, IMM_H(imm))); } goto bpf_alu32_trunc; case BPF_ALU | BPF_LSH | BPF_X: /* (u32) dst <<= (u32) src */ /* slw clears top 32 bits */ - PPC_SLW(dst_reg, dst_reg, src_reg); + EMIT(PPC_RAW_SLW(dst_reg, dst_reg, src_reg)); /* skip zero extension move, but set address map. */ if (insn_is_zext(&insn[i + 1])) addrs[++i] = ctx->idx * 4; break; case BPF_ALU64 | BPF_LSH | BPF_X: /* dst <<= src; */ - PPC_SLD(dst_reg, dst_reg, src_reg); + EMIT(PPC_RAW_SLD(dst_reg, dst_reg, src_reg)); break; case BPF_ALU | BPF_LSH | BPF_K: /* (u32) dst <<== (u32) imm */ /* with imm 0, we still need to clear top 32 bits */ - PPC_SLWI(dst_reg, dst_reg, imm); + EMIT(PPC_RAW_SLWI(dst_reg, dst_reg, imm)); if (insn_is_zext(&insn[i + 1])) addrs[++i] = ctx->idx * 4; break; case BPF_ALU64 | BPF_LSH | BPF_K: /* dst <<== imm */ if (imm != 0) - PPC_SLDI(dst_reg, dst_reg, imm); + EMIT(PPC_RAW_SLDI(dst_reg, dst_reg, imm)); break; case BPF_ALU | BPF_RSH | BPF_X: /* (u32) dst >>= (u32) src */ - PPC_SRW(dst_reg, dst_reg, src_reg); + EMIT(PPC_RAW_SRW(dst_reg, dst_reg, src_reg)); if (insn_is_zext(&insn[i + 1])) addrs[++i] = ctx->idx * 4; break; case BPF_ALU64 | BPF_RSH | BPF_X: /* dst >>= src */ - PPC_SRD(dst_reg, dst_reg, src_reg); + EMIT(PPC_RAW_SRD(dst_reg, dst_reg, src_reg)); break; case BPF_ALU | BPF_RSH | BPF_K: /* (u32) dst >>= (u32) imm */ - PPC_SRWI(dst_reg, dst_reg, imm); + EMIT(PPC_RAW_SRWI(dst_reg, dst_reg, imm)); if (insn_is_zext(&insn[i + 1])) addrs[++i] = ctx->idx * 4; break; case BPF_ALU64 | BPF_RSH | BPF_K: /* dst >>= imm */ if (imm != 0) - PPC_SRDI(dst_reg, dst_reg, imm); + EMIT(PPC_RAW_SRDI(dst_reg, dst_reg, imm)); break; case BPF_ALU | BPF_ARSH | BPF_X: /* (s32) dst >>= src */ - PPC_SRAW(dst_reg, dst_reg, src_reg); + EMIT(PPC_RAW_SRAW(dst_reg, dst_reg, src_reg)); goto bpf_alu32_trunc; case BPF_ALU64 | BPF_ARSH | BPF_X: /* (s64) dst >>= src */ - PPC_SRAD(dst_reg, dst_reg, src_reg); + EMIT(PPC_RAW_SRAD(dst_reg, dst_reg, src_reg)); break; case BPF_ALU | BPF_ARSH | BPF_K: /* (s32) dst >>= imm */ - PPC_SRAWI(dst_reg, dst_reg, imm); + EMIT(PPC_RAW_SRAWI(dst_reg, dst_reg, imm)); goto bpf_alu32_trunc; case BPF_ALU64 | BPF_ARSH | BPF_K: /* (s64) dst >>= imm */ if (imm != 0) - PPC_SRADI(dst_reg, dst_reg, imm); + EMIT(PPC_RAW_SRADI(dst_reg, dst_reg, imm)); break; /* @@ -555,10 +557,10 @@ static int bpf_jit_build_body(struct bpf_prog *fp, u32 *image, case BPF_ALU64 | BPF_MOV | BPF_X: /* dst = src */ if (imm == 1) { /* special mov32 for zext */ - PPC_RLWINM(dst_reg, dst_reg, 0, 0, 31); + EMIT(PPC_RAW_RLWINM(dst_reg, dst_reg, 0, 0, 31)); break; } - PPC_MR(dst_reg, src_reg); + EMIT(PPC_RAW_MR(dst_reg, src_reg)); goto bpf_alu32_trunc; case BPF_ALU | BPF_MOV | BPF_K: /* (u32) dst = imm */ case BPF_ALU64 | BPF_MOV | BPF_K: /* dst = (s64) imm */ @@ -572,7 +574,7 @@ static int bpf_jit_build_body(struct bpf_prog *fp, u32 *image, bpf_alu32_trunc: /* Truncate to 32-bits */ if (BPF_CLASS(code) == BPF_ALU && !fp->aux->verifier_zext) - PPC_RLWINM(dst_reg, dst_reg, 0, 0, 31); + EMIT(PPC_RAW_RLWINM(dst_reg, dst_reg, 0, 0, 31)); break; /* @@ -590,11 +592,11 @@ bpf_alu32_trunc: switch (imm) { case 16: /* Rotate 8 bits left & mask with 0x0000ff00 */ - PPC_RLWINM(b2p[TMP_REG_1], dst_reg, 8, 16, 23); + EMIT(PPC_RAW_RLWINM(b2p[TMP_REG_1], dst_reg, 8, 16, 23)); /* Rotate 8 bits right & insert LSB to reg */ - PPC_RLWIMI(b2p[TMP_REG_1], dst_reg, 24, 24, 31); + EMIT(PPC_RAW_RLWIMI(b2p[TMP_REG_1], dst_reg, 24, 24, 31)); /* Move result back to dst_reg */ - PPC_MR(dst_reg, b2p[TMP_REG_1]); + EMIT(PPC_RAW_MR(dst_reg, b2p[TMP_REG_1])); break; case 32: /* @@ -602,12 +604,12 @@ bpf_alu32_trunc: * 2 bytes are already in their final position * -- byte 2 and 4 (of bytes 1, 2, 3 and 4) */ - PPC_RLWINM(b2p[TMP_REG_1], dst_reg, 8, 0, 31); + EMIT(PPC_RAW_RLWINM(b2p[TMP_REG_1], dst_reg, 8, 0, 31)); /* Rotate 24 bits and insert byte 1 */ - PPC_RLWIMI(b2p[TMP_REG_1], dst_reg, 24, 0, 7); + EMIT(PPC_RAW_RLWIMI(b2p[TMP_REG_1], dst_reg, 24, 0, 7)); /* Rotate 24 bits and insert byte 3 */ - PPC_RLWIMI(b2p[TMP_REG_1], dst_reg, 24, 16, 23); - PPC_MR(dst_reg, b2p[TMP_REG_1]); + EMIT(PPC_RAW_RLWIMI(b2p[TMP_REG_1], dst_reg, 24, 16, 23)); + EMIT(PPC_RAW_MR(dst_reg, b2p[TMP_REG_1])); break; case 64: /* @@ -619,8 +621,8 @@ bpf_alu32_trunc: * same across all passes */ PPC_BPF_STL(dst_reg, 1, bpf_jit_stack_local(ctx)); - PPC_ADDI(b2p[TMP_REG_1], 1, bpf_jit_stack_local(ctx)); - PPC_LDBRX(dst_reg, 0, b2p[TMP_REG_1]); + EMIT(PPC_RAW_ADDI(b2p[TMP_REG_1], 1, bpf_jit_stack_local(ctx))); + EMIT(PPC_RAW_LDBRX(dst_reg, 0, b2p[TMP_REG_1])); break; } break; @@ -629,14 +631,14 @@ emit_clear: switch (imm) { case 16: /* zero-extend 16 bits into 64 bits */ - PPC_RLDICL(dst_reg, dst_reg, 0, 48); + EMIT(PPC_RAW_RLDICL(dst_reg, dst_reg, 0, 48)); if (insn_is_zext(&insn[i + 1])) addrs[++i] = ctx->idx * 4; break; case 32: if (!fp->aux->verifier_zext) /* zero-extend 32 bits into 64 bits */ - PPC_RLDICL(dst_reg, dst_reg, 0, 32); + EMIT(PPC_RAW_RLDICL(dst_reg, dst_reg, 0, 32)); break; case 64: /* nop */ @@ -650,18 +652,18 @@ emit_clear: case BPF_STX | BPF_MEM | BPF_B: /* *(u8 *)(dst + off) = src */ case BPF_ST | BPF_MEM | BPF_B: /* *(u8 *)(dst + off) = imm */ if (BPF_CLASS(code) == BPF_ST) { - PPC_LI(b2p[TMP_REG_1], imm); + EMIT(PPC_RAW_LI(b2p[TMP_REG_1], imm)); src_reg = b2p[TMP_REG_1]; } - PPC_STB(src_reg, dst_reg, off); + EMIT(PPC_RAW_STB(src_reg, dst_reg, off)); break; case BPF_STX | BPF_MEM | BPF_H: /* (u16 *)(dst + off) = src */ case BPF_ST | BPF_MEM | BPF_H: /* (u16 *)(dst + off) = imm */ if (BPF_CLASS(code) == BPF_ST) { - PPC_LI(b2p[TMP_REG_1], imm); + EMIT(PPC_RAW_LI(b2p[TMP_REG_1], imm)); src_reg = b2p[TMP_REG_1]; } - PPC_STH(src_reg, dst_reg, off); + EMIT(PPC_RAW_STH(src_reg, dst_reg, off)); break; case BPF_STX | BPF_MEM | BPF_W: /* *(u32 *)(dst + off) = src */ case BPF_ST | BPF_MEM | BPF_W: /* *(u32 *)(dst + off) = imm */ @@ -669,7 +671,7 @@ emit_clear: PPC_LI32(b2p[TMP_REG_1], imm); src_reg = b2p[TMP_REG_1]; } - PPC_STW(src_reg, dst_reg, off); + EMIT(PPC_RAW_STW(src_reg, dst_reg, off)); break; case BPF_STX | BPF_MEM | BPF_DW: /* (u64 *)(dst + off) = src */ case BPF_ST | BPF_MEM | BPF_DW: /* *(u64 *)(dst + off) = imm */ @@ -686,20 +688,20 @@ emit_clear: /* *(u32 *)(dst + off) += src */ case BPF_STX | BPF_XADD | BPF_W: /* Get EA into TMP_REG_1 */ - PPC_ADDI(b2p[TMP_REG_1], dst_reg, off); + EMIT(PPC_RAW_ADDI(b2p[TMP_REG_1], dst_reg, off)); tmp_idx = ctx->idx * 4; /* load value from memory into TMP_REG_2 */ EMIT(PPC_RAW_LWARX(b2p[TMP_REG_2], 0, b2p[TMP_REG_1], 0)); /* add value from src_reg into this */ EMIT(PPC_RAW_ADD(b2p[TMP_REG_2], b2p[TMP_REG_2], src_reg)); /* store result back */ - PPC_BPF_STWCX(b2p[TMP_REG_2], 0, b2p[TMP_REG_1]); + EMIT(PPC_RAW_STWCX(b2p[TMP_REG_2], 0, b2p[TMP_REG_1])); /* we're done if this succeeded */ PPC_BCC_SHORT(COND_NE, tmp_idx); break; /* *(u64 *)(dst + off) += src */ case BPF_STX | BPF_XADD | BPF_DW: - PPC_ADDI(b2p[TMP_REG_1], dst_reg, off); + EMIT(PPC_RAW_ADDI(b2p[TMP_REG_1], dst_reg, off)); tmp_idx = ctx->idx * 4; EMIT(PPC_RAW_LDARX(b2p[TMP_REG_2], 0, b2p[TMP_REG_1], 0)); EMIT(PPC_RAW_ADD(b2p[TMP_REG_2], b2p[TMP_REG_2], src_reg)); @@ -712,13 +714,13 @@ emit_clear: */ /* dst = *(u8 *)(ul) (src + off) */ case BPF_LDX | BPF_MEM | BPF_B: - PPC_LBZ(dst_reg, src_reg, off); + EMIT(PPC_RAW_LBZ(dst_reg, src_reg, off)); if (insn_is_zext(&insn[i + 1])) addrs[++i] = ctx->idx * 4; break; /* dst = *(u16 *)(ul) (src + off) */ case BPF_LDX | BPF_MEM | BPF_H: - PPC_LHZ(dst_reg, src_reg, off); + EMIT(PPC_RAW_LHZ(dst_reg, src_reg, off)); if (insn_is_zext(&insn[i + 1])) addrs[++i] = ctx->idx * 4; break; @@ -775,7 +777,7 @@ emit_clear: else bpf_jit_emit_func_call_rel(image, ctx, func_addr); /* move return value from r3 to BPF_REG_0 */ - PPC_MR(b2p[BPF_REG_0], 3); + EMIT(PPC_RAW_MR(b2p[BPF_REG_0], 3)); break; /* @@ -860,9 +862,9 @@ cond_branch: case BPF_JMP32 | BPF_JNE | BPF_X: /* unsigned comparison */ if (BPF_CLASS(code) == BPF_JMP32) - PPC_CMPLW(dst_reg, src_reg); + EMIT(PPC_RAW_CMPLW(dst_reg, src_reg)); else - PPC_CMPLD(dst_reg, src_reg); + EMIT(PPC_RAW_CMPLD(dst_reg, src_reg)); break; case BPF_JMP | BPF_JSGT | BPF_X: case BPF_JMP | BPF_JSLT | BPF_X: @@ -874,21 +876,21 @@ cond_branch: case BPF_JMP32 | BPF_JSLE | BPF_X: /* signed comparison */ if (BPF_CLASS(code) == BPF_JMP32) - PPC_CMPW(dst_reg, src_reg); + EMIT(PPC_RAW_CMPW(dst_reg, src_reg)); else - PPC_CMPD(dst_reg, src_reg); + EMIT(PPC_RAW_CMPD(dst_reg, src_reg)); break; case BPF_JMP | BPF_JSET | BPF_X: case BPF_JMP32 | BPF_JSET | BPF_X: if (BPF_CLASS(code) == BPF_JMP) { - PPC_AND_DOT(b2p[TMP_REG_1], dst_reg, - src_reg); + EMIT(PPC_RAW_AND_DOT(b2p[TMP_REG_1], dst_reg, + src_reg)); } else { int tmp_reg = b2p[TMP_REG_1]; - PPC_AND(tmp_reg, dst_reg, src_reg); - PPC_RLWINM_DOT(tmp_reg, tmp_reg, 0, 0, - 31); + EMIT(PPC_RAW_AND(tmp_reg, dst_reg, src_reg)); + EMIT(PPC_RAW_RLWINM_DOT(tmp_reg, tmp_reg, 0, 0, + 31)); } break; case BPF_JMP | BPF_JNE | BPF_K: @@ -912,19 +914,19 @@ cond_branch: */ if (imm >= 0 && imm < 32768) { if (is_jmp32) - PPC_CMPLWI(dst_reg, imm); + EMIT(PPC_RAW_CMPLWI(dst_reg, imm)); else - PPC_CMPLDI(dst_reg, imm); + EMIT(PPC_RAW_CMPLDI(dst_reg, imm)); } else { /* sign-extending load */ PPC_LI32(b2p[TMP_REG_1], imm); /* ... but unsigned comparison */ if (is_jmp32) - PPC_CMPLW(dst_reg, - b2p[TMP_REG_1]); + EMIT(PPC_RAW_CMPLW(dst_reg, + b2p[TMP_REG_1])); else - PPC_CMPLD(dst_reg, - b2p[TMP_REG_1]); + EMIT(PPC_RAW_CMPLD(dst_reg, + b2p[TMP_REG_1])); } break; } @@ -945,17 +947,17 @@ cond_branch: */ if (imm >= -32768 && imm < 32768) { if (is_jmp32) - PPC_CMPWI(dst_reg, imm); + EMIT(PPC_RAW_CMPWI(dst_reg, imm)); else - PPC_CMPDI(dst_reg, imm); + EMIT(PPC_RAW_CMPDI(dst_reg, imm)); } else { PPC_LI32(b2p[TMP_REG_1], imm); if (is_jmp32) - PPC_CMPW(dst_reg, - b2p[TMP_REG_1]); + EMIT(PPC_RAW_CMPW(dst_reg, + b2p[TMP_REG_1])); else - PPC_CMPD(dst_reg, - b2p[TMP_REG_1]); + EMIT(PPC_RAW_CMPD(dst_reg, + b2p[TMP_REG_1])); } break; } @@ -964,19 +966,19 @@ cond_branch: /* andi does not sign-extend the immediate */ if (imm >= 0 && imm < 32768) /* PPC_ANDI is _only/always_ dot-form */ - PPC_ANDI(b2p[TMP_REG_1], dst_reg, imm); + EMIT(PPC_RAW_ANDI(b2p[TMP_REG_1], dst_reg, imm)); else { int tmp_reg = b2p[TMP_REG_1]; PPC_LI32(tmp_reg, imm); if (BPF_CLASS(code) == BPF_JMP) { - PPC_AND_DOT(tmp_reg, dst_reg, - tmp_reg); + EMIT(PPC_RAW_AND_DOT(tmp_reg, dst_reg, + tmp_reg)); } else { - PPC_AND(tmp_reg, dst_reg, - tmp_reg); - PPC_RLWINM_DOT(tmp_reg, tmp_reg, - 0, 0, 31); + EMIT(PPC_RAW_AND(tmp_reg, dst_reg, + tmp_reg)); + EMIT(PPC_RAW_RLWINM_DOT(tmp_reg, tmp_reg, + 0, 0, 31)); } } break; -- GitLab From 357c572948310c88868cee00e64ca3f7fc933a74 Mon Sep 17 00:00:00 2001 From: Balamuruhan S Date: Wed, 24 Jun 2020 17:00:37 +0530 Subject: [PATCH 0486/1476] powerpc/ppc-opcode: Reuse raw instruction macros to stringify Wrap existing stringify macros to reuse raw instruction encoding macros that are newly added. Signed-off-by: Balamuruhan S Tested-by: Naveen N. Rao Acked-by: Naveen N. Rao [mpe: Add DCBFPS, DCBSTPS, PHWSYNC, PLWSYNC] Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20200624113038.908074-6-bala24@linux.ibm.com --- arch/powerpc/include/asm/ppc-opcode.h | 233 +++++++++----------------- 1 file changed, 75 insertions(+), 158 deletions(-) diff --git a/arch/powerpc/include/asm/ppc-opcode.h b/arch/powerpc/include/asm/ppc-opcode.h index cb24b758c7849..88cf292b3324f 100644 --- a/arch/powerpc/include/asm/ppc-opcode.h +++ b/arch/powerpc/include/asm/ppc-opcode.h @@ -628,175 +628,96 @@ /* Deal with instructions that older assemblers aren't aware of */ #define PPC_BCCTR_FLUSH stringify_in_c(.long PPC_INST_BCCTR_FLUSH) -#define PPC_CP_ABORT stringify_in_c(.long PPC_INST_CP_ABORT) -#define PPC_COPY(a, b) stringify_in_c(.long PPC_INST_COPY | \ - ___PPC_RA(a) | ___PPC_RB(b)) -#define PPC_DARN(t, l) stringify_in_c(.long PPC_INST_DARN | \ - ___PPC_RT(t) | \ - (((l) & 0x3) << 16)) -#define PPC_DCBAL(a, b) stringify_in_c(.long PPC_INST_DCBAL | \ - __PPC_RA(a) | __PPC_RB(b)) -#define PPC_DCBZL(a, b) stringify_in_c(.long PPC_INST_DCBZL | \ - __PPC_RA(a) | __PPC_RB(b)) -#define PPC_LQARX(t, a, b, eh) stringify_in_c(.long PPC_INST_LQARX | \ - ___PPC_RT(t) | ___PPC_RA(a) | \ - ___PPC_RB(b) | __PPC_EH(eh)) -#define PPC_LDARX(t, a, b, eh) stringify_in_c(.long PPC_INST_LDARX | \ - ___PPC_RT(t) | ___PPC_RA(a) | \ - ___PPC_RB(b) | __PPC_EH(eh)) -#define PPC_LWARX(t, a, b, eh) stringify_in_c(.long PPC_INST_LWARX | \ - ___PPC_RT(t) | ___PPC_RA(a) | \ - ___PPC_RB(b) | __PPC_EH(eh)) -#define PPC_STQCX(t, a, b) stringify_in_c(.long PPC_INST_STQCX | \ - ___PPC_RT(t) | ___PPC_RA(a) | \ - ___PPC_RB(b)) -#define PPC_MADDHD(t, a, b, c) stringify_in_c(.long PPC_INST_MADDHD | \ - ___PPC_RT(t) | ___PPC_RA(a) | \ - ___PPC_RB(b) | ___PPC_RC(c)) -#define PPC_MADDHDU(t, a, b, c) stringify_in_c(.long PPC_INST_MADDHDU | \ - ___PPC_RT(t) | ___PPC_RA(a) | \ - ___PPC_RB(b) | ___PPC_RC(c)) -#define PPC_MADDLD(t, a, b, c) stringify_in_c(.long PPC_INST_MADDLD | \ - ___PPC_RT(t) | ___PPC_RA(a) | \ - ___PPC_RB(b) | ___PPC_RC(c)) -#define PPC_MSGSND(b) stringify_in_c(.long PPC_INST_MSGSND | \ - ___PPC_RB(b)) -#define PPC_MSGSYNC stringify_in_c(.long PPC_INST_MSGSYNC) -#define PPC_MSGCLR(b) stringify_in_c(.long PPC_INST_MSGCLR | \ - ___PPC_RB(b)) -#define PPC_MSGSNDP(b) stringify_in_c(.long PPC_INST_MSGSNDP | \ - ___PPC_RB(b)) -#define PPC_MSGCLRP(b) stringify_in_c(.long PPC_INST_MSGCLRP | \ - ___PPC_RB(b)) -#define PPC_PASTE(a, b) stringify_in_c(.long PPC_INST_PASTE | \ - ___PPC_RA(a) | ___PPC_RB(b)) -#define PPC_POPCNTB(a, s) stringify_in_c(.long PPC_INST_POPCNTB | \ - __PPC_RA(a) | __PPC_RS(s)) -#define PPC_POPCNTD(a, s) stringify_in_c(.long PPC_INST_POPCNTD | \ - __PPC_RA(a) | __PPC_RS(s)) -#define PPC_POPCNTW(a, s) stringify_in_c(.long PPC_INST_POPCNTW | \ - __PPC_RA(a) | __PPC_RS(s)) -#define PPC_RFCI stringify_in_c(.long PPC_INST_RFCI) -#define PPC_RFDI stringify_in_c(.long PPC_INST_RFDI) -#define PPC_RFMCI stringify_in_c(.long PPC_INST_RFMCI) -#define PPC_TLBILX(t, a, b) stringify_in_c(.long PPC_INST_TLBILX | \ - __PPC_T_TLB(t) | __PPC_RA0(a) | __PPC_RB(b)) +#define PPC_CP_ABORT stringify_in_c(.long PPC_RAW_CP_ABORT) +#define PPC_COPY(a, b) stringify_in_c(.long PPC_RAW_COPY(a, b)) +#define PPC_DARN(t, l) stringify_in_c(.long PPC_RAW_DARN(t, l)) +#define PPC_DCBAL(a, b) stringify_in_c(.long PPC_RAW_DCBAL(a, b)) +#define PPC_DCBZL(a, b) stringify_in_c(.long PPC_RAW_DCBZL(a, b)) +#define PPC_LQARX(t, a, b, eh) stringify_in_c(.long PPC_RAW_LQARX(t, a, b, eh)) +#define PPC_LDARX(t, a, b, eh) stringify_in_c(.long PPC_RAW_LDARX(t, a, b, eh)) +#define PPC_LWARX(t, a, b, eh) stringify_in_c(.long PPC_RAW_LWARX(t, a, b, eh)) +#define PPC_STQCX(t, a, b) stringify_in_c(.long PPC_RAW_STQCX(t, a, b)) +#define PPC_MADDHD(t, a, b, c) stringify_in_c(.long PPC_RAW_MADDHD(t, a, b, c)) +#define PPC_MADDHDU(t, a, b, c) stringify_in_c(.long PPC_RAW_MADDHDU(t, a, b, c)) +#define PPC_MADDLD(t, a, b, c) stringify_in_c(.long PPC_RAW_MADDLD(t, a, b, c)) +#define PPC_MSGSND(b) stringify_in_c(.long PPC_RAW_MSGSND(b)) +#define PPC_MSGSYNC stringify_in_c(.long PPC_RAW_MSGSYNC) +#define PPC_MSGCLR(b) stringify_in_c(.long PPC_RAW_MSGCLR(b)) +#define PPC_MSGSNDP(b) stringify_in_c(.long PPC_RAW_MSGSNDP(b)) +#define PPC_MSGCLRP(b) stringify_in_c(.long PPC_RAW_MSGCLRP(b)) +#define PPC_PASTE(a, b) stringify_in_c(.long PPC_RAW_PASTE(a, b)) +#define PPC_POPCNTB(a, s) stringify_in_c(.long PPC_RAW_POPCNTB(a, s)) +#define PPC_POPCNTD(a, s) stringify_in_c(.long PPC_RAW_POPCNTD(a, s)) +#define PPC_POPCNTW(a, s) stringify_in_c(.long PPC_RAW_POPCNTW(a, s)) +#define PPC_RFCI stringify_in_c(.long PPC_RAW_RFCI) +#define PPC_RFDI stringify_in_c(.long PPC_RAW_RFDI) +#define PPC_RFMCI stringify_in_c(.long PPC_RAW_RFMCI) +#define PPC_TLBILX(t, a, b) stringify_in_c(.long PPC_RAW_TLBILX(t, a, b)) #define PPC_TLBILX_ALL(a, b) PPC_TLBILX(0, a, b) #define PPC_TLBILX_PID(a, b) PPC_TLBILX(1, a, b) #define PPC_TLBILX_VA(a, b) PPC_TLBILX(3, a, b) -#define PPC_WAIT(w) stringify_in_c(.long PPC_INST_WAIT | \ - __PPC_WC(w)) -#define PPC_TLBIE(lp,a) stringify_in_c(.long PPC_INST_TLBIE | \ - ___PPC_RB(a) | ___PPC_RS(lp)) -#define PPC_TLBIE_5(rb,rs,ric,prs,r) \ - stringify_in_c(.long PPC_INST_TLBIE | \ - ___PPC_RB(rb) | ___PPC_RS(rs) | \ - ___PPC_RIC(ric) | ___PPC_PRS(prs) | \ - ___PPC_R(r)) +#define PPC_WAIT(w) stringify_in_c(.long PPC_RAW_WAIT(w)) +#define PPC_TLBIE(lp, a) stringify_in_c(.long PPC_RAW_TLBIE(lp, a)) +#define PPC_TLBIE_5(rb, rs, ric, prs, r) \ + stringify_in_c(.long PPC_RAW_TLBIE_5(rb, rs, ric, prs, r)) #define PPC_TLBIEL(rb,rs,ric,prs,r) \ - stringify_in_c(.long PPC_INST_TLBIEL | \ - ___PPC_RB(rb) | ___PPC_RS(rs) | \ - ___PPC_RIC(ric) | ___PPC_PRS(prs) | \ - ___PPC_R(r)) -#define PPC_TLBSRX_DOT(a,b) stringify_in_c(.long PPC_INST_TLBSRX_DOT | \ - __PPC_RA0(a) | __PPC_RB(b)) -#define PPC_TLBIVAX(a,b) stringify_in_c(.long PPC_INST_TLBIVAX | \ - __PPC_RA0(a) | __PPC_RB(b)) - -#define PPC_ERATWE(s, a, w) stringify_in_c(.long PPC_INST_ERATWE | \ - __PPC_RS(s) | __PPC_RA(a) | __PPC_WS(w)) -#define PPC_ERATRE(s, a, w) stringify_in_c(.long PPC_INST_ERATRE | \ - __PPC_RS(s) | __PPC_RA(a) | __PPC_WS(w)) -#define PPC_ERATILX(t, a, b) stringify_in_c(.long PPC_INST_ERATILX | \ - __PPC_T_TLB(t) | __PPC_RA0(a) | \ - __PPC_RB(b)) -#define PPC_ERATIVAX(s, a, b) stringify_in_c(.long PPC_INST_ERATIVAX | \ - __PPC_RS(s) | __PPC_RA0(a) | __PPC_RB(b)) -#define PPC_ERATSX(t, a, w) stringify_in_c(.long PPC_INST_ERATSX | \ - __PPC_RS(t) | __PPC_RA0(a) | __PPC_RB(b)) -#define PPC_ERATSX_DOT(t, a, w) stringify_in_c(.long PPC_INST_ERATSX_DOT | \ - __PPC_RS(t) | __PPC_RA0(a) | __PPC_RB(b)) -#define PPC_SLBFEE_DOT(t, b) stringify_in_c(.long PPC_INST_SLBFEE | \ - __PPC_RT(t) | __PPC_RB(b)) -#define __PPC_SLBFEE_DOT(t, b) stringify_in_c(.long PPC_INST_SLBFEE | \ - ___PPC_RT(t) | ___PPC_RB(b)) -#define PPC_ICBT(c,a,b) stringify_in_c(.long PPC_INST_ICBT | \ - __PPC_CT(c) | __PPC_RA0(a) | __PPC_RB(b)) + stringify_in_c(.long PPC_RAW_TLBIEL(rb, rs, ric, prs, r)) +#define PPC_TLBSRX_DOT(a, b) stringify_in_c(.long PPC_RAW_TLBSRX_DOT(a, b)) +#define PPC_TLBIVAX(a, b) stringify_in_c(.long PPC_RAW_TLBIVAX(a, b)) + +#define PPC_ERATWE(s, a, w) stringify_in_c(.long PPC_RAW_ERATWE(s, a, w)) +#define PPC_ERATRE(s, a, w) stringify_in_c(.long PPC_RAW_ERATRE(a, a, w)) +#define PPC_ERATILX(t, a, b) stringify_in_c(.long PPC_RAW_ERATILX(t, a, b)) +#define PPC_ERATIVAX(s, a, b) stringify_in_c(.long PPC_RAW_ERATIVAX(s, a, b)) +#define PPC_ERATSX(t, a, w) stringify_in_c(.long PPC_RAW_ERATSX(t, a, w)) +#define PPC_ERATSX_DOT(t, a, w) stringify_in_c(.long PPC_RAW_ERATSX_DOT(t, a, w)) +#define PPC_SLBFEE_DOT(t, b) stringify_in_c(.long PPC_RAW_SLBFEE_DOT(t, b)) +#define __PPC_SLBFEE_DOT(t, b) stringify_in_c(.long __PPC_RAW_SLBFEE_DOT(t, b)) +#define PPC_ICBT(c, a, b) stringify_in_c(.long PPC_RAW_ICBT(c, a, b)) /* PASemi instructions */ -#define LBZCIX(t,a,b) stringify_in_c(.long PPC_INST_LBZCIX | \ - __PPC_RT(t) | __PPC_RA(a) | __PPC_RB(b)) -#define STBCIX(s,a,b) stringify_in_c(.long PPC_INST_STBCIX | \ - __PPC_RS(s) | __PPC_RA(a) | __PPC_RB(b)) - -#define PPC_DCBFPS(a, b) stringify_in_c(.long PPC_INST_DCBF | \ - ___PPC_RA(a) | ___PPC_RB(b) | (4 << 21)) -#define PPC_DCBSTPS(a, b) stringify_in_c(.long PPC_INST_DCBF | \ - ___PPC_RA(a) | ___PPC_RB(b) | (6 << 21)) - -#define PPC_PHWSYNC stringify_in_c(.long PPC_INST_PHWSYNC) -#define PPC_PLWSYNC stringify_in_c(.long PPC_INST_PLWSYNC) - -#define STXVD2X(s, a, b) stringify_in_c(.long PPC_INST_STXVD2X | \ - VSX_XX1((s), a, b)) -#define LXVD2X(s, a, b) stringify_in_c(.long PPC_INST_LXVD2X | \ - VSX_XX1((s), a, b)) -#define MFVRD(a, t) stringify_in_c(.long PPC_INST_MFVSRD | \ - VSX_XX1((t)+32, a, R0)) -#define MTVRD(t, a) stringify_in_c(.long PPC_INST_MTVSRD | \ - VSX_XX1((t)+32, a, R0)) -#define VPMSUMW(t, a, b) stringify_in_c(.long PPC_INST_VPMSUMW | \ - VSX_XX3((t), a, b)) -#define VPMSUMD(t, a, b) stringify_in_c(.long PPC_INST_VPMSUMD | \ - VSX_XX3((t), a, b)) -#define XXLOR(t, a, b) stringify_in_c(.long PPC_INST_XXLOR | \ - VSX_XX3((t), a, b)) -#define XXSWAPD(t, a) stringify_in_c(.long PPC_INST_XXSWAPD | \ - VSX_XX3((t), a, a)) -#define XVCPSGNDP(t, a, b) stringify_in_c(.long (PPC_INST_XVCPSGNDP | \ - VSX_XX3((t), (a), (b)))) +#define LBZCIX(t, a, b) stringify_in_c(.long PPC_RAW_LBZCIX(t, a, b)) +#define STBCIX(s, a, b) stringify_in_c(.long PPC_RAW_STBCIX(s, a, b)) +#define PPC_DCBFPS(a, b) stringify_in_c(.long PPC_RAW_DCBFPS(a, b)) +#define PPC_DCBSTPS(a, b) stringify_in_c(.long PPC_RAW_DCBSTPS(a, b)) +#define PPC_PHWSYNC stringify_in_c(.long PPC_RAW_PHWSYNC) +#define PPC_PLWSYNC stringify_in_c(.long PPC_RAW_PLWSYNC) +#define STXVD2X(s, a, b) stringify_in_c(.long PPC_RAW_STXVD2X(s, a, b)) +#define LXVD2X(s, a, b) stringify_in_c(.long PPC_RAW_LXVD2X(s, a, b)) +#define MFVRD(a, t) stringify_in_c(.long PPC_RAW_MFVRD(a, t)) +#define MTVRD(t, a) stringify_in_c(.long PPC_RAW_MTVRD(t, a)) +#define VPMSUMW(t, a, b) stringify_in_c(.long PPC_RAW_VPMSUMW(t, a, b)) +#define VPMSUMD(t, a, b) stringify_in_c(.long PPC_RAW_VPMSUMD(t, a, b)) +#define XXLOR(t, a, b) stringify_in_c(.long PPC_RAW_XXLOR(t, a, b)) +#define XXSWAPD(t, a) stringify_in_c(.long PPC_RAW_XXSWAPD(t, a)) +#define XVCPSGNDP(t, a, b) stringify_in_c(.long (PPC_RAW_XVCPSGNDP(t, a, b))) #define VPERMXOR(vrt, vra, vrb, vrc) \ - stringify_in_c(.long (PPC_INST_VPERMXOR | \ - ___PPC_RT(vrt) | ___PPC_RA(vra) | \ - ___PPC_RB(vrb) | (((vrc) & 0x1f) << 6))) + stringify_in_c(.long (PPC_RAW_VPERMXOR(vrt, vra, vrb, vrc))) -#define PPC_NAP stringify_in_c(.long PPC_INST_NAP) -#define PPC_SLEEP stringify_in_c(.long PPC_INST_SLEEP) -#define PPC_WINKLE stringify_in_c(.long PPC_INST_WINKLE) +#define PPC_NAP stringify_in_c(.long PPC_RAW_NAP) +#define PPC_SLEEP stringify_in_c(.long PPC_RAW_SLEEP) +#define PPC_WINKLE stringify_in_c(.long PPC_RAW_WINKLE) -#define PPC_STOP stringify_in_c(.long PPC_INST_STOP) +#define PPC_STOP stringify_in_c(.long PPC_RAW_STOP) /* BHRB instructions */ -#define PPC_CLRBHRB stringify_in_c(.long PPC_INST_CLRBHRB) -#define PPC_MFBHRBE(r, n) stringify_in_c(.long PPC_INST_BHRBE | \ - __PPC_RT(r) | \ - (((n) & 0x3ff) << 11)) +#define PPC_CLRBHRB stringify_in_c(.long PPC_RAW_CLRBHRB) +#define PPC_MFBHRBE(r, n) stringify_in_c(.long PPC_RAW_MFBHRBE(r, n)) /* Transactional memory instructions */ -#define TRECHKPT stringify_in_c(.long PPC_INST_TRECHKPT) -#define TRECLAIM(r) stringify_in_c(.long PPC_INST_TRECLAIM \ - | __PPC_RA(r)) -#define TABORT(r) stringify_in_c(.long PPC_INST_TABORT \ - | __PPC_RA(r)) +#define TRECHKPT stringify_in_c(.long PPC_RAW_TRECHKPT) +#define TRECLAIM(r) stringify_in_c(.long PPC_RAW_TRECLAIM(r)) +#define TABORT(r) stringify_in_c(.long PPC_RAW_TABORT(r)) /* book3e thread control instructions */ -#define MTTMR(tmr, r) stringify_in_c(.long PPC_INST_MTTMR | \ - TMRN(tmr) | ___PPC_RS(r)) -#define MFTMR(tmr, r) stringify_in_c(.long PPC_INST_MFTMR | \ - TMRN(tmr) | ___PPC_RT(r)) +#define MTTMR(tmr, r) stringify_in_c(.long PPC_RAW_MTTMR(tmr, r)) +#define MFTMR(tmr, r) stringify_in_c(.long PPC_RAW_MFTMR(tmr, r)) /* Coprocessor instructions */ -#define PPC_ICSWX(s, a, b) stringify_in_c(.long PPC_INST_ICSWX | \ - ___PPC_RS(s) | \ - ___PPC_RA(a) | \ - ___PPC_RB(b)) -#define PPC_ICSWEPX(s, a, b) stringify_in_c(.long PPC_INST_ICSWEPX | \ - ___PPC_RS(s) | \ - ___PPC_RA(a) | \ - ___PPC_RB(b)) - -#define PPC_SLBIA(IH) stringify_in_c(.long PPC_INST_SLBIA | \ - ((IH & 0x7) << 21)) +#define PPC_ICSWX(s, a, b) stringify_in_c(.long PPC_RAW_ICSWX(s, a, b)) +#define PPC_ICSWEPX(s, a, b) stringify_in_c(.long PPC_RAW_ICSWEPX(s, a, b)) + +#define PPC_SLBIA(IH) stringify_in_c(.long PPC_RAW_SLBIA(IH)) /* * These may only be used on ISA v3.0 or later (aka. CPU_FTR_ARCH_300, radix @@ -808,12 +729,8 @@ #define PPC_RADIX_INVALIDATE_ERAT_USER PPC_SLBIA(3) #define PPC_RADIX_INVALIDATE_ERAT_GUEST PPC_SLBIA(6) -#define VCMPEQUD_RC(vrt, vra, vrb) stringify_in_c(.long PPC_INST_VCMPEQUD | \ - ___PPC_RT(vrt) | ___PPC_RA(vra) | \ - ___PPC_RB(vrb) | __PPC_RC21) +#define VCMPEQUD_RC(vrt, vra, vrb) stringify_in_c(.long PPC_RAW_VCMPEQUD_RC(vrt, vra, vrb)) -#define VCMPEQUB_RC(vrt, vra, vrb) stringify_in_c(.long PPC_INST_VCMPEQUB | \ - ___PPC_RT(vrt) | ___PPC_RA(vra) | \ - ___PPC_RB(vrb) | __PPC_RC21) +#define VCMPEQUB_RC(vrt, vra, vrb) stringify_in_c(.long PPC_RAW_VCMPEQUB_RC(vrt, vra, vrb)) #endif /* _ASM_POWERPC_PPC_OPCODE_H */ -- GitLab From e4208f1399b1bf7ed84ba359a6ba0979d1df4029 Mon Sep 17 00:00:00 2001 From: Balamuruhan S Date: Wed, 24 Jun 2020 17:00:38 +0530 Subject: [PATCH 0487/1476] powerpc/ppc-opcode: Fold PPC_INST_* macros into PPC_RAW_* macros Lots of PPC_INST_* macros are used only ever in PPC_* macros, fold those PPC_INST_* into PPC_RAW_* to avoid using PPC_INST_* accidentally. Signed-off-by: Balamuruhan S Tested-by: Naveen N. Rao Acked-by: Naveen N. Rao [mpe: Deal with PHWSYNC, PLWSYNC] Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20200624113038.908074-7-bala24@linux.ibm.com --- arch/powerpc/include/asm/ppc-opcode.h | 394 +++++++++----------------- 1 file changed, 129 insertions(+), 265 deletions(-) diff --git a/arch/powerpc/include/asm/ppc-opcode.h b/arch/powerpc/include/asm/ppc-opcode.h index 88cf292b3324f..6b7a3f0f2d62f 100644 --- a/arch/powerpc/include/asm/ppc-opcode.h +++ b/arch/powerpc/include/asm/ppc-opcode.h @@ -209,58 +209,27 @@ /* sorted alphabetically */ #define PPC_INST_BCCTR_FLUSH 0x4c400420 -#define PPC_INST_BHRBE 0x7c00025c -#define PPC_INST_CLRBHRB 0x7c00035c #define PPC_INST_COPY 0x7c20060c -#define PPC_INST_CP_ABORT 0x7c00068c -#define PPC_INST_DARN 0x7c0005e6 #define PPC_INST_DCBA 0x7c0005ec #define PPC_INST_DCBA_MASK 0xfc0007fe -#define PPC_INST_DCBAL 0x7c2005ec -#define PPC_INST_DCBZL 0x7c2007ec -#define PPC_INST_ICBT 0x7c00002c -#define PPC_INST_ICSWX 0x7c00032d -#define PPC_INST_ICSWEPX 0x7c00076d #define PPC_INST_ISEL 0x7c00001e #define PPC_INST_ISEL_MASK 0xfc00003e -#define PPC_INST_LDARX 0x7c0000a8 -#define PPC_INST_STDCX 0x7c0001ad -#define PPC_INST_LQARX 0x7c000228 -#define PPC_INST_STQCX 0x7c00016d #define PPC_INST_LSWI 0x7c0004aa #define PPC_INST_LSWX 0x7c00042a -#define PPC_INST_LWARX 0x7c000028 -#define PPC_INST_STWCX 0x7c00012d #define PPC_INST_LWSYNC 0x7c2004ac #define PPC_INST_SYNC 0x7c0004ac -#define PPC_INST_PHWSYNC 0x7c8004ac -#define PPC_INST_PLWSYNC 0x7ca004ac #define PPC_INST_SYNC_MASK 0xfc0007fe #define PPC_INST_ISYNC 0x4c00012c -#define PPC_INST_LXVD2X 0x7c000698 #define PPC_INST_MCRXR 0x7c000400 #define PPC_INST_MCRXR_MASK 0xfc0007fe #define PPC_INST_MFSPR_PVR 0x7c1f42a6 #define PPC_INST_MFSPR_PVR_MASK 0xfc1ffffe -#define PPC_INST_MFTMR 0x7c0002dc -#define PPC_INST_MSGSND 0x7c00019c -#define PPC_INST_MSGCLR 0x7c0001dc -#define PPC_INST_MSGSYNC 0x7c0006ec -#define PPC_INST_MSGSNDP 0x7c00011c -#define PPC_INST_MSGCLRP 0x7c00015c #define PPC_INST_MTMSRD 0x7c000164 -#define PPC_INST_MTTMR 0x7c0003dc #define PPC_INST_NOP 0x60000000 -#define PPC_INST_PASTE 0x7c20070d #define PPC_INST_POPCNTB 0x7c0000f4 #define PPC_INST_POPCNTB_MASK 0xfc0007fe -#define PPC_INST_POPCNTD 0x7c0003f4 -#define PPC_INST_POPCNTW 0x7c0002f4 #define PPC_INST_RFEBB 0x4c000124 -#define PPC_INST_RFCI 0x4c000066 -#define PPC_INST_RFDI 0x4c00004e #define PPC_INST_RFID 0x4c000024 -#define PPC_INST_RFMCI 0x4c00004c #define PPC_INST_MFSPR 0x7c0002a6 #define PPC_INST_MFSPR_DSCR 0x7c1102a6 #define PPC_INST_MFSPR_DSCR_MASK 0xfc1ffffe @@ -270,134 +239,31 @@ #define PPC_INST_MFSPR_DSCR_USER_MASK 0xfc1ffffe #define PPC_INST_MTSPR_DSCR_USER 0x7c0303a6 #define PPC_INST_MTSPR_DSCR_USER_MASK 0xfc1ffffe -#define PPC_INST_MFVSRD 0x7c000066 -#define PPC_INST_MTVSRD 0x7c000166 #define PPC_INST_SC 0x44000002 -#define PPC_INST_SLBFEE 0x7c0007a7 -#define PPC_INST_SLBIA 0x7c0003e4 - #define PPC_INST_STRING 0x7c00042a #define PPC_INST_STRING_MASK 0xfc0007fe #define PPC_INST_STRING_GEN_MASK 0xfc00067e - #define PPC_INST_STSWI 0x7c0005aa #define PPC_INST_STSWX 0x7c00052a -#define PPC_INST_STXVD2X 0x7c000798 -#define PPC_INST_TLBIE 0x7c000264 -#define PPC_INST_TLBIEL 0x7c000224 -#define PPC_INST_TLBILX 0x7c000024 -#define PPC_INST_WAIT 0x7c00007c -#define PPC_INST_TLBIVAX 0x7c000624 -#define PPC_INST_TLBSRX_DOT 0x7c0006a5 -#define PPC_INST_VPMSUMW 0x10000488 -#define PPC_INST_VPMSUMD 0x100004c8 -#define PPC_INST_VPERMXOR 0x1000002d -#define PPC_INST_XXLOR 0xf0000490 -#define PPC_INST_XXSWAPD 0xf0000250 -#define PPC_INST_XVCPSGNDP 0xf0000780 #define PPC_INST_TRECHKPT 0x7c0007dd #define PPC_INST_TRECLAIM 0x7c00075d -#define PPC_INST_TABORT 0x7c00071d #define PPC_INST_TSR 0x7c0005dd - -#define PPC_INST_DCBF 0x7c0000ac - -#define PPC_INST_NAP 0x4c000364 -#define PPC_INST_SLEEP 0x4c0003a4 -#define PPC_INST_WINKLE 0x4c0003e4 - -#define PPC_INST_STOP 0x4c0002e4 - -/* A2 specific instructions */ -#define PPC_INST_ERATWE 0x7c0001a6 -#define PPC_INST_ERATRE 0x7c000166 -#define PPC_INST_ERATILX 0x7c000066 -#define PPC_INST_ERATIVAX 0x7c000666 -#define PPC_INST_ERATSX 0x7c000126 -#define PPC_INST_ERATSX_DOT 0x7c000127 - -/* Misc instructions for BPF compiler */ -#define PPC_INST_LBZ 0x88000000 #define PPC_INST_LD 0xe8000000 -#define PPC_INST_LDX 0x7c00002a -#define PPC_INST_LHZ 0xa0000000 -#define PPC_INST_LWZ 0x80000000 -#define PPC_INST_LHBRX 0x7c00062c -#define PPC_INST_LDBRX 0x7c000428 -#define PPC_INST_STB 0x98000000 -#define PPC_INST_STH 0xb0000000 #define PPC_INST_STD 0xf8000000 -#define PPC_INST_STDX 0x7c00012a -#define PPC_INST_STDU 0xf8000001 -#define PPC_INST_STW 0x90000000 -#define PPC_INST_STWU 0x94000000 #define PPC_INST_MFLR 0x7c0802a6 -#define PPC_INST_MTLR 0x7c0803a6 #define PPC_INST_MTCTR 0x7c0903a6 -#define PPC_INST_CMPWI 0x2c000000 -#define PPC_INST_CMPDI 0x2c200000 -#define PPC_INST_CMPW 0x7c000000 -#define PPC_INST_CMPD 0x7c200000 -#define PPC_INST_CMPLW 0x7c000040 -#define PPC_INST_CMPLD 0x7c200040 -#define PPC_INST_CMPLWI 0x28000000 -#define PPC_INST_CMPLDI 0x28200000 #define PPC_INST_ADDI 0x38000000 #define PPC_INST_ADDIS 0x3c000000 #define PPC_INST_ADD 0x7c000214 -#define PPC_INST_ADDC 0x7c000014 -#define PPC_INST_SUB 0x7c000050 #define PPC_INST_BLR 0x4e800020 -#define PPC_INST_BLRL 0x4e800021 #define PPC_INST_BCTR 0x4e800420 #define PPC_INST_BCTRL 0x4e800421 -#define PPC_INST_MULLD 0x7c0001d2 -#define PPC_INST_MULLW 0x7c0001d6 -#define PPC_INST_MULHWU 0x7c000016 -#define PPC_INST_MULLI 0x1c000000 -#define PPC_INST_MADDHD 0x10000030 -#define PPC_INST_MADDHDU 0x10000031 -#define PPC_INST_MADDLD 0x10000033 -#define PPC_INST_DIVWU 0x7c000396 #define PPC_INST_DIVD 0x7c0003d2 -#define PPC_INST_DIVDU 0x7c000392 -#define PPC_INST_RLWINM 0x54000000 -#define PPC_INST_RLWINM_DOT 0x54000001 -#define PPC_INST_RLWIMI 0x50000000 -#define PPC_INST_RLDICL 0x78000000 #define PPC_INST_RLDICR 0x78000004 -#define PPC_INST_SLW 0x7c000030 -#define PPC_INST_SLD 0x7c000036 -#define PPC_INST_SRW 0x7c000430 -#define PPC_INST_SRAW 0x7c000630 -#define PPC_INST_SRAWI 0x7c000670 -#define PPC_INST_SRD 0x7c000436 -#define PPC_INST_SRAD 0x7c000634 -#define PPC_INST_SRADI 0x7c000674 -#define PPC_INST_AND 0x7c000038 -#define PPC_INST_ANDDOT 0x7c000039 -#define PPC_INST_OR 0x7c000378 -#define PPC_INST_XOR 0x7c000278 -#define PPC_INST_ANDI 0x70000000 #define PPC_INST_ORI 0x60000000 #define PPC_INST_ORIS 0x64000000 -#define PPC_INST_XORI 0x68000000 -#define PPC_INST_XORIS 0x6c000000 -#define PPC_INST_NEG 0x7c0000d0 -#define PPC_INST_EXTSW 0x7c0007b4 #define PPC_INST_BRANCH 0x48000000 #define PPC_INST_BRANCH_COND 0x40800000 -#define PPC_INST_LBZCIX 0x7c0006aa -#define PPC_INST_STBCIX 0x7c0007aa -#define PPC_INST_LWZX 0x7c00002e -#define PPC_INST_LFSX 0x7c00042e -#define PPC_INST_STFSX 0x7c00052e -#define PPC_INST_LFDX 0x7c0004ae -#define PPC_INST_STFDX 0x7c0005ae -#define PPC_INST_LVX 0x7c0000ce -#define PPC_INST_STVX 0x7c0001ce -#define PPC_INST_VCMPEQUD 0x100000c7 -#define PPC_INST_VCMPEQUB 0x10000006 /* macros to insert fields into opcodes */ #define ___PPC_RA(a) (((a) & 0x1f) << 16) @@ -451,168 +317,166 @@ #endif /* Base instruction encoding */ -#define PPC_RAW_CP_ABORT (PPC_INST_CP_ABORT) +#define PPC_RAW_CP_ABORT (0x7c00068c) #define PPC_RAW_COPY(a, b) (PPC_INST_COPY | ___PPC_RA(a) | ___PPC_RB(b)) -#define PPC_RAW_DARN(t, l) (PPC_INST_DARN | ___PPC_RT(t) | (((l) & 0x3) << 16)) -#define PPC_RAW_DCBAL(a, b) (PPC_INST_DCBAL | __PPC_RA(a) | __PPC_RB(b)) -#define PPC_RAW_DCBZL(a, b) (PPC_INST_DCBZL | __PPC_RA(a) | __PPC_RB(b)) -#define PPC_RAW_LQARX(t, a, b, eh) (PPC_INST_LQARX | ___PPC_RT(t) | ___PPC_RA(a) | ___PPC_RB(b) | __PPC_EH(eh)) -#define PPC_RAW_LDARX(t, a, b, eh) (PPC_INST_LDARX | ___PPC_RT(t) | ___PPC_RA(a) | ___PPC_RB(b) | __PPC_EH(eh)) -#define PPC_RAW_LWARX(t, a, b, eh) (PPC_INST_LWARX | ___PPC_RT(t) | ___PPC_RA(a) | ___PPC_RB(b) | __PPC_EH(eh)) -#define PPC_RAW_PHWSYNC (PPC_INST_PHWSYNC) -#define PPC_RAW_PLWSYNC (PPC_INST_PLWSYNC) -#define PPC_RAW_STQCX(t, a, b) (PPC_INST_STQCX | ___PPC_RT(t) | ___PPC_RA(a) | ___PPC_RB(b)) -#define PPC_RAW_MADDHD(t, a, b, c) (PPC_INST_MADDHD | ___PPC_RT(t) | ___PPC_RA(a) | ___PPC_RB(b) | ___PPC_RC(c)) -#define PPC_RAW_MADDHDU(t, a, b, c) (PPC_INST_MADDHDU | ___PPC_RT(t) | ___PPC_RA(a) | ___PPC_RB(b) | ___PPC_RC(c)) -#define PPC_RAW_MADDLD(t, a, b, c) (PPC_INST_MADDLD | ___PPC_RT(t) | ___PPC_RA(a) | ___PPC_RB(b) | ___PPC_RC(c)) -#define PPC_RAW_MSGSND(b) (PPC_INST_MSGSND | ___PPC_RB(b)) -#define PPC_RAW_MSGSYNC (PPC_INST_MSGSYNC) -#define PPC_RAW_MSGCLR(b) (PPC_INST_MSGCLR | ___PPC_RB(b)) -#define PPC_RAW_MSGSNDP(b) (PPC_INST_MSGSNDP | ___PPC_RB(b)) -#define PPC_RAW_MSGCLRP(b) (PPC_INST_MSGCLRP | ___PPC_RB(b)) -#define PPC_RAW_PASTE(a, b) (PPC_INST_PASTE | ___PPC_RA(a) | ___PPC_RB(b)) +#define PPC_RAW_DARN(t, l) (0x7c0005e6 | ___PPC_RT(t) | (((l) & 0x3) << 16)) +#define PPC_RAW_DCBAL(a, b) (0x7c2005ec | __PPC_RA(a) | __PPC_RB(b)) +#define PPC_RAW_DCBZL(a, b) (0x7c2007ec | __PPC_RA(a) | __PPC_RB(b)) +#define PPC_RAW_LQARX(t, a, b, eh) (0x7c000228 | ___PPC_RT(t) | ___PPC_RA(a) | ___PPC_RB(b) | __PPC_EH(eh)) +#define PPC_RAW_LDARX(t, a, b, eh) (0x7c0000a8 | ___PPC_RT(t) | ___PPC_RA(a) | ___PPC_RB(b) | __PPC_EH(eh)) +#define PPC_RAW_LWARX(t, a, b, eh) (0x7c000028 | ___PPC_RT(t) | ___PPC_RA(a) | ___PPC_RB(b) | __PPC_EH(eh)) +#define PPC_RAW_PHWSYNC (0x7c8004ac) +#define PPC_RAW_PLWSYNC (0x7ca004ac) +#define PPC_RAW_STQCX(t, a, b) (0x7c00016d | ___PPC_RT(t) | ___PPC_RA(a) | ___PPC_RB(b)) +#define PPC_RAW_MADDHD(t, a, b, c) (0x10000030 | ___PPC_RT(t) | ___PPC_RA(a) | ___PPC_RB(b) | ___PPC_RC(c)) +#define PPC_RAW_MADDHDU(t, a, b, c) (0x10000031 | ___PPC_RT(t) | ___PPC_RA(a) | ___PPC_RB(b) | ___PPC_RC(c)) +#define PPC_RAW_MADDLD(t, a, b, c) (0x10000033 | ___PPC_RT(t) | ___PPC_RA(a) | ___PPC_RB(b) | ___PPC_RC(c)) +#define PPC_RAW_MSGSND(b) (0x7c00019c | ___PPC_RB(b)) +#define PPC_RAW_MSGSYNC (0x7c0006ec) +#define PPC_RAW_MSGCLR(b) (0x7c0001dc | ___PPC_RB(b)) +#define PPC_RAW_MSGSNDP(b) (0x7c00011c | ___PPC_RB(b)) +#define PPC_RAW_MSGCLRP(b) (0x7c00015c | ___PPC_RB(b)) +#define PPC_RAW_PASTE(a, b) (0x7c20070d | ___PPC_RA(a) | ___PPC_RB(b)) #define PPC_RAW_POPCNTB(a, s) (PPC_INST_POPCNTB | __PPC_RA(a) | __PPC_RS(s)) -#define PPC_RAW_POPCNTD(a, s) (PPC_INST_POPCNTD | __PPC_RA(a) | __PPC_RS(s)) -#define PPC_RAW_POPCNTW(a, s) (PPC_INST_POPCNTW | __PPC_RA(a) | __PPC_RS(s)) -#define PPC_RAW_RFCI (PPC_INST_RFCI) -#define PPC_RAW_RFDI (PPC_INST_RFDI) -#define PPC_RAW_RFMCI (PPC_INST_RFMCI) -#define PPC_RAW_TLBILX(t, a, b) (PPC_INST_TLBILX | __PPC_T_TLB(t) | __PPC_RA0(a) | __PPC_RB(b)) -#define PPC_RAW_WAIT(w) (PPC_INST_WAIT | __PPC_WC(w)) -#define PPC_RAW_TLBIE(lp, a) (PPC_INST_TLBIE | ___PPC_RB(a) | ___PPC_RS(lp)) +#define PPC_RAW_POPCNTD(a, s) (0x7c0003f4 | __PPC_RA(a) | __PPC_RS(s)) +#define PPC_RAW_POPCNTW(a, s) (0x7c0002f4 | __PPC_RA(a) | __PPC_RS(s)) +#define PPC_RAW_RFCI (0x4c000066) +#define PPC_RAW_RFDI (0x4c00004e) +#define PPC_RAW_RFMCI (0x4c00004c) +#define PPC_RAW_TLBILX(t, a, b) (0x7c000024 | __PPC_T_TLB(t) | __PPC_RA0(a) | __PPC_RB(b)) +#define PPC_RAW_WAIT(w) (0x7c00007c | __PPC_WC(w)) +#define PPC_RAW_TLBIE(lp, a) (0x7c000264 | ___PPC_RB(a) | ___PPC_RS(lp)) #define PPC_RAW_TLBIE_5(rb, rs, ric, prs, r) \ - (PPC_INST_TLBIE | ___PPC_RB(rb) | ___PPC_RS(rs) | ___PPC_RIC(ric) | ___PPC_PRS(prs) | ___PPC_R(r)) + (0x7c000264 | ___PPC_RB(rb) | ___PPC_RS(rs) | ___PPC_RIC(ric) | ___PPC_PRS(prs) | ___PPC_R(r)) #define PPC_RAW_TLBIEL(rb, rs, ric, prs, r) \ - (PPC_INST_TLBIEL | ___PPC_RB(rb) | ___PPC_RS(rs) | ___PPC_RIC(ric) | ___PPC_PRS(prs) | ___PPC_R(r)) -#define PPC_RAW_TLBSRX_DOT(a, b) (PPC_INST_TLBSRX_DOT | __PPC_RA0(a) | __PPC_RB(b)) -#define PPC_RAW_TLBIVAX(a, b) (PPC_INST_TLBIVAX | __PPC_RA0(a) | __PPC_RB(b)) -#define PPC_RAW_ERATWE(s, a, w) (PPC_INST_ERATWE | __PPC_RS(s) | __PPC_RA(a) | __PPC_WS(w)) -#define PPC_RAW_ERATRE(s, a, w) (PPC_INST_ERATRE | __PPC_RS(s) | __PPC_RA(a) | __PPC_WS(w)) -#define PPC_RAW_ERATILX(t, a, b) (PPC_INST_ERATILX | __PPC_T_TLB(t) | __PPC_RA0(a) | __PPC_RB(b)) -#define PPC_RAW_ERATIVAX(s, a, b) (PPC_INST_ERATIVAX | __PPC_RS(s) | __PPC_RA0(a) | __PPC_RB(b)) -#define PPC_RAW_ERATSX(t, a, w) (PPC_INST_ERATSX | __PPC_RS(t) | __PPC_RA0(a) | __PPC_RB(b)) -#define PPC_RAW_ERATSX_DOT(t, a, w) (PPC_INST_ERATSX_DOT | __PPC_RS(t) | __PPC_RA0(a) | __PPC_RB(b)) -#define PPC_RAW_SLBFEE_DOT(t, b) (PPC_INST_SLBFEE | __PPC_RT(t) | __PPC_RB(b)) -#define __PPC_RAW_SLBFEE_DOT(t, b) (PPC_INST_SLBFEE | ___PPC_RT(t) | ___PPC_RB(b)) -#define PPC_RAW_ICBT(c, a, b) (PPC_INST_ICBT | __PPC_CT(c) | __PPC_RA0(a) | __PPC_RB(b)) -#define PPC_RAW_LBZCIX(t, a, b) (PPC_INST_LBZCIX | __PPC_RT(t) | __PPC_RA(a) | __PPC_RB(b)) -#define PPC_RAW_STBCIX(s, a, b) (PPC_INST_STBCIX | __PPC_RS(s) | __PPC_RA(a) | __PPC_RB(b)) -#define PPC_RAW_DCBFPS(a, b) (PPC_INST_DCBF | ___PPC_RA(a) | ___PPC_RB(b) | (4 << 21)) -#define PPC_RAW_DCBSTPS(a, b) (PPC_INST_DCBF | ___PPC_RA(a) | ___PPC_RB(b) | (6 << 21)) + (0x7c000224 | ___PPC_RB(rb) | ___PPC_RS(rs) | ___PPC_RIC(ric) | ___PPC_PRS(prs) | ___PPC_R(r)) +#define PPC_RAW_TLBSRX_DOT(a, b) (0x7c0006a5 | __PPC_RA0(a) | __PPC_RB(b)) +#define PPC_RAW_TLBIVAX(a, b) (0x7c000624 | __PPC_RA0(a) | __PPC_RB(b)) +#define PPC_RAW_ERATWE(s, a, w) (0x7c0001a6 | __PPC_RS(s) | __PPC_RA(a) | __PPC_WS(w)) +#define PPC_RAW_ERATRE(s, a, w) (0x7c000166 | __PPC_RS(s) | __PPC_RA(a) | __PPC_WS(w)) +#define PPC_RAW_ERATILX(t, a, b) (0x7c000066 | __PPC_T_TLB(t) | __PPC_RA0(a) | __PPC_RB(b)) +#define PPC_RAW_ERATIVAX(s, a, b) (0x7c000666 | __PPC_RS(s) | __PPC_RA0(a) | __PPC_RB(b)) +#define PPC_RAW_ERATSX(t, a, w) (0x7c000126 | __PPC_RS(t) | __PPC_RA0(a) | __PPC_RB(b)) +#define PPC_RAW_ERATSX_DOT(t, a, w) (0x7c000127 | __PPC_RS(t) | __PPC_RA0(a) | __PPC_RB(b)) +#define PPC_RAW_SLBFEE_DOT(t, b) (0x7c0007a7 | __PPC_RT(t) | __PPC_RB(b)) +#define __PPC_RAW_SLBFEE_DOT(t, b) (0x7c0007a7 | ___PPC_RT(t) | ___PPC_RB(b)) +#define PPC_RAW_ICBT(c, a, b) (0x7c00002c | __PPC_CT(c) | __PPC_RA0(a) | __PPC_RB(b)) +#define PPC_RAW_LBZCIX(t, a, b) (0x7c0006aa | __PPC_RT(t) | __PPC_RA(a) | __PPC_RB(b)) +#define PPC_RAW_STBCIX(s, a, b) (0x7c0007aa | __PPC_RS(s) | __PPC_RA(a) | __PPC_RB(b)) +#define PPC_RAW_DCBFPS(a, b) (0x7c0000ac | ___PPC_RA(a) | ___PPC_RB(b) | (4 << 21)) +#define PPC_RAW_DCBSTPS(a, b) (0x7c0000ac | ___PPC_RA(a) | ___PPC_RB(b) | (6 << 21)) /* * Define what the VSX XX1 form instructions will look like, then add * the 128 bit load store instructions based on that. */ #define VSX_XX1(s, a, b) (__PPC_XS(s) | __PPC_RA(a) | __PPC_RB(b)) #define VSX_XX3(t, a, b) (__PPC_XT(t) | __PPC_XA(a) | __PPC_XB(b)) -#define PPC_RAW_STXVD2X(s, a, b) (PPC_INST_STXVD2X | VSX_XX1((s), a, b)) -#define PPC_RAW_LXVD2X(s, a, b) (PPC_INST_LXVD2X | VSX_XX1((s), a, b)) -#define PPC_RAW_MFVRD(a, t) (PPC_INST_MFVSRD | VSX_XX1((t) + 32, a, R0)) -#define PPC_RAW_MTVRD(t, a) (PPC_INST_MTVSRD | VSX_XX1((t) + 32, a, R0)) -#define PPC_RAW_VPMSUMW(t, a, b) (PPC_INST_VPMSUMW | VSX_XX3((t), a, b)) -#define PPC_RAW_VPMSUMD(t, a, b) (PPC_INST_VPMSUMD | VSX_XX3((t), a, b)) -#define PPC_RAW_XXLOR(t, a, b) (PPC_INST_XXLOR | VSX_XX3((t), a, b)) -#define PPC_RAW_XXSWAPD(t, a) (PPC_INST_XXSWAPD | VSX_XX3((t), a, a)) -#define PPC_RAW_XVCPSGNDP(t, a, b) ((PPC_INST_XVCPSGNDP | VSX_XX3((t), (a), (b)))) +#define PPC_RAW_STXVD2X(s, a, b) (0x7c000798 | VSX_XX1((s), a, b)) +#define PPC_RAW_LXVD2X(s, a, b) (0x7c000698 | VSX_XX1((s), a, b)) +#define PPC_RAW_MFVRD(a, t) (0x7c000066 | VSX_XX1((t) + 32, a, R0)) +#define PPC_RAW_MTVRD(t, a) (0x7c000166 | VSX_XX1((t) + 32, a, R0)) +#define PPC_RAW_VPMSUMW(t, a, b) (0x10000488 | VSX_XX3((t), a, b)) +#define PPC_RAW_VPMSUMD(t, a, b) (0x100004c8 | VSX_XX3((t), a, b)) +#define PPC_RAW_XXLOR(t, a, b) (0xf0000490 | VSX_XX3((t), a, b)) +#define PPC_RAW_XXSWAPD(t, a) (0xf0000250 | VSX_XX3((t), a, a)) +#define PPC_RAW_XVCPSGNDP(t, a, b) ((0xf0000780 | VSX_XX3((t), (a), (b)))) #define PPC_RAW_VPERMXOR(vrt, vra, vrb, vrc) \ - ((PPC_INST_VPERMXOR | ___PPC_RT(vrt) | ___PPC_RA(vra) | ___PPC_RB(vrb) | (((vrc) & 0x1f) << 6))) -#define PPC_RAW_NAP (PPC_INST_NAP) -#define PPC_RAW_SLEEP (PPC_INST_SLEEP) -#define PPC_RAW_WINKLE (PPC_INST_WINKLE) -#define PPC_RAW_STOP (PPC_INST_STOP) -#define PPC_RAW_CLRBHRB (PPC_INST_CLRBHRB) -#define PPC_RAW_MFBHRBE(r, n) (PPC_INST_BHRBE | __PPC_RT(r) | (((n) & 0x3ff) << 11)) + ((0x1000002d | ___PPC_RT(vrt) | ___PPC_RA(vra) | ___PPC_RB(vrb) | (((vrc) & 0x1f) << 6))) +#define PPC_RAW_NAP (0x4c000364) +#define PPC_RAW_SLEEP (0x4c0003a4) +#define PPC_RAW_WINKLE (0x4c0003e4) +#define PPC_RAW_STOP (0x4c0002e4) +#define PPC_RAW_CLRBHRB (0x7c00035c) +#define PPC_RAW_MFBHRBE(r, n) (0x7c00025c | __PPC_RT(r) | (((n) & 0x3ff) << 11)) #define PPC_RAW_TRECHKPT (PPC_INST_TRECHKPT) #define PPC_RAW_TRECLAIM(r) (PPC_INST_TRECLAIM | __PPC_RA(r)) -#define PPC_RAW_TABORT(r) (PPC_INST_TABORT | __PPC_RA(r)) +#define PPC_RAW_TABORT(r) (0x7c00071d | __PPC_RA(r)) #define TMRN(x) ((((x) & 0x1f) << 16) | (((x) & 0x3e0) << 6)) -#define PPC_RAW_MTTMR(tmr, r) (PPC_INST_MTTMR | TMRN(tmr) | ___PPC_RS(r)) -#define PPC_RAW_MFTMR(tmr, r) (PPC_INST_MFTMR | TMRN(tmr) | ___PPC_RT(r)) -#define PPC_RAW_ICSWX(s, a, b) (PPC_INST_ICSWX | ___PPC_RS(s) | ___PPC_RA(a) | ___PPC_RB(b)) -#define PPC_RAW_ICSWEPX(s, a, b) (PPC_INST_ICSWEPX | ___PPC_RS(s) | ___PPC_RA(a) | ___PPC_RB(b)) -#define PPC_RAW_SLBIA(IH) (PPC_INST_SLBIA | (((IH) & 0x7) << 21)) +#define PPC_RAW_MTTMR(tmr, r) (0x7c0003dc | TMRN(tmr) | ___PPC_RS(r)) +#define PPC_RAW_MFTMR(tmr, r) (0x7c0002dc | TMRN(tmr) | ___PPC_RT(r)) +#define PPC_RAW_ICSWX(s, a, b) (0x7c00032d | ___PPC_RS(s) | ___PPC_RA(a) | ___PPC_RB(b)) +#define PPC_RAW_ICSWEPX(s, a, b) (0x7c00076d | ___PPC_RS(s) | ___PPC_RA(a) | ___PPC_RB(b)) +#define PPC_RAW_SLBIA(IH) (0x7c0003e4 | (((IH) & 0x7) << 21)) #define PPC_RAW_VCMPEQUD_RC(vrt, vra, vrb) \ - (PPC_INST_VCMPEQUD | ___PPC_RT(vrt) | ___PPC_RA(vra) | ___PPC_RB(vrb) | __PPC_RC21) + (0x100000c7 | ___PPC_RT(vrt) | ___PPC_RA(vra) | ___PPC_RB(vrb) | __PPC_RC21) #define PPC_RAW_VCMPEQUB_RC(vrt, vra, vrb) \ - (PPC_INST_VCMPEQUB | ___PPC_RT(vrt) | ___PPC_RA(vra) | ___PPC_RB(vrb) | __PPC_RC21) + (0x10000006 | ___PPC_RT(vrt) | ___PPC_RA(vra) | ___PPC_RB(vrb) | __PPC_RC21) #define PPC_RAW_LD(r, base, i) (PPC_INST_LD | ___PPC_RT(r) | ___PPC_RA(base) | IMM_DS(i)) -#define PPC_RAW_LWZ(r, base, i) (PPC_INST_LWZ | ___PPC_RT(r) | ___PPC_RA(base) | IMM_L(i)) -#define PPC_RAW_LWZX(t, a, b) (PPC_INST_LWZX | ___PPC_RT(t) | ___PPC_RA(a) | ___PPC_RB(b)) +#define PPC_RAW_LWZ(r, base, i) (0x80000000 | ___PPC_RT(r) | ___PPC_RA(base) | IMM_L(i)) +#define PPC_RAW_LWZX(t, a, b) (0x7c00002e | ___PPC_RT(t) | ___PPC_RA(a) | ___PPC_RB(b)) #define PPC_RAW_STD(r, base, i) (PPC_INST_STD | ___PPC_RS(r) | ___PPC_RA(base) | IMM_DS(i)) -#define PPC_RAW_STDCX(s, a, b) (PPC_INST_STDCX | ___PPC_RS(s) | ___PPC_RA(a) | ___PPC_RB(b)) -#define PPC_RAW_LFSX(t, a, b) (PPC_INST_LFSX | ___PPC_RT(t) | ___PPC_RA(a) | ___PPC_RB(b)) -#define PPC_RAW_STFSX(s, a, b) (PPC_INST_STFSX | ___PPC_RS(s) | ___PPC_RA(a) | ___PPC_RB(b)) -#define PPC_RAW_LFDX(t, a, b) (PPC_INST_LFDX | ___PPC_RT(t) | ___PPC_RA(a) | ___PPC_RB(b)) -#define PPC_RAW_STFDX(s, a, b) (PPC_INST_STFDX | ___PPC_RS(s) | ___PPC_RA(a) | ___PPC_RB(b)) -#define PPC_RAW_LVX(t, a, b) (PPC_INST_LVX | ___PPC_RT(t) | ___PPC_RA(a) | ___PPC_RB(b)) -#define PPC_RAW_STVX(s, a, b) (PPC_INST_STVX | ___PPC_RS(s) | ___PPC_RA(a) | ___PPC_RB(b)) +#define PPC_RAW_STDCX(s, a, b) (0x7c0001ad | ___PPC_RS(s) | ___PPC_RA(a) | ___PPC_RB(b)) +#define PPC_RAW_LFSX(t, a, b) (0x7c00042e | ___PPC_RT(t) | ___PPC_RA(a) | ___PPC_RB(b)) +#define PPC_RAW_STFSX(s, a, b) (0x7c00052e | ___PPC_RS(s) | ___PPC_RA(a) | ___PPC_RB(b)) +#define PPC_RAW_LFDX(t, a, b) (0x7c0004ae | ___PPC_RT(t) | ___PPC_RA(a) | ___PPC_RB(b)) +#define PPC_RAW_STFDX(s, a, b) (0x7c0005ae | ___PPC_RS(s) | ___PPC_RA(a) | ___PPC_RB(b)) +#define PPC_RAW_LVX(t, a, b) (0x7c0000ce | ___PPC_RT(t) | ___PPC_RA(a) | ___PPC_RB(b)) +#define PPC_RAW_STVX(s, a, b) (0x7c0001ce | ___PPC_RS(s) | ___PPC_RA(a) | ___PPC_RB(b)) #define PPC_RAW_ADD(t, a, b) (PPC_INST_ADD | ___PPC_RT(t) | ___PPC_RA(a) | ___PPC_RB(b)) #define PPC_RAW_ADD_DOT(t, a, b) (PPC_INST_ADD | ___PPC_RT(t) | ___PPC_RA(a) | ___PPC_RB(b) | 0x1) -#define PPC_RAW_ADDC(t, a, b) (PPC_INST_ADDC | ___PPC_RT(t) | ___PPC_RA(a) | ___PPC_RB(b)) -#define PPC_RAW_ADDC_DOT(t, a, b) (PPC_INST_ADDC | ___PPC_RT(t) | ___PPC_RA(a) | ___PPC_RB(b) | 0x1) +#define PPC_RAW_ADDC(t, a, b) (0x7c000014 | ___PPC_RT(t) | ___PPC_RA(a) | ___PPC_RB(b)) +#define PPC_RAW_ADDC_DOT(t, a, b) (0x7c000014 | ___PPC_RT(t) | ___PPC_RA(a) | ___PPC_RB(b) | 0x1) #define PPC_RAW_NOP() (PPC_INST_NOP) #define PPC_RAW_BLR() (PPC_INST_BLR) -#define PPC_RAW_BLRL() (PPC_INST_BLRL) -#define PPC_RAW_MTLR(r) (PPC_INST_MTLR | ___PPC_RT(r)) +#define PPC_RAW_BLRL() (0x4e800021) +#define PPC_RAW_MTLR(r) (0x7c0803a6 | ___PPC_RT(r)) #define PPC_RAW_BCTR() (PPC_INST_BCTR) #define PPC_RAW_MTCTR(r) (PPC_INST_MTCTR | ___PPC_RT(r)) #define PPC_RAW_ADDI(d, a, i) (PPC_INST_ADDI | ___PPC_RT(d) | ___PPC_RA(a) | IMM_L(i)) #define PPC_RAW_LI(r, i) PPC_RAW_ADDI(r, 0, i) #define PPC_RAW_ADDIS(d, a, i) (PPC_INST_ADDIS | ___PPC_RT(d) | ___PPC_RA(a) | IMM_L(i)) #define PPC_RAW_LIS(r, i) PPC_RAW_ADDIS(r, 0, i) -#define PPC_RAW_STDX(r, base, b) (PPC_INST_STDX | ___PPC_RS(r) | ___PPC_RA(base) | ___PPC_RB(b)) -#define PPC_RAW_STDU(r, base, i) (PPC_INST_STDU | ___PPC_RS(r) | ___PPC_RA(base) | ((i) & 0xfffc)) -#define PPC_RAW_STW(r, base, i) (PPC_INST_STW | ___PPC_RS(r) | ___PPC_RA(base) | IMM_L(i)) -#define PPC_RAW_STWU(r, base, i) (PPC_INST_STWU | ___PPC_RS(r) | ___PPC_RA(base) | IMM_L(i)) -#define PPC_RAW_STH(r, base, i) (PPC_INST_STH | ___PPC_RS(r) | ___PPC_RA(base) | IMM_L(i)) -#define PPC_RAW_STB(r, base, i) (PPC_INST_STB | ___PPC_RS(r) | ___PPC_RA(base) | IMM_L(i)) -#define PPC_RAW_LBZ(r, base, i) (PPC_INST_LBZ | ___PPC_RT(r) | ___PPC_RA(base) | IMM_L(i)) -#define PPC_RAW_LDX(r, base, b) (PPC_INST_LDX | ___PPC_RT(r) | ___PPC_RA(base) | ___PPC_RB(b)) -#define PPC_RAW_LHZ(r, base, i) (PPC_INST_LHZ | ___PPC_RT(r) | ___PPC_RA(base) | IMM_L(i)) -#define PPC_RAW_LHBRX(r, base, b) (PPC_INST_LHBRX | ___PPC_RT(r) | ___PPC_RA(base) | ___PPC_RB(b)) -#define PPC_RAW_LDBRX(r, base, b) (PPC_INST_LDBRX | ___PPC_RT(r) | ___PPC_RA(base) | ___PPC_RB(b)) -#define PPC_RAW_STWCX(s, a, b) (PPC_INST_STWCX | ___PPC_RS(s) | ___PPC_RA(a) | ___PPC_RB(b)) -#define PPC_RAW_CMPWI(a, i) (PPC_INST_CMPWI | ___PPC_RA(a) | IMM_L(i)) -#define PPC_RAW_CMPDI(a, i) (PPC_INST_CMPDI | ___PPC_RA(a) | IMM_L(i)) -#define PPC_RAW_CMPW(a, b) (PPC_INST_CMPW | ___PPC_RA(a) | ___PPC_RB(b)) -#define PPC_RAW_CMPD(a, b) (PPC_INST_CMPD | ___PPC_RA(a) | ___PPC_RB(b)) -#define PPC_RAW_CMPLWI(a, i) (PPC_INST_CMPLWI | ___PPC_RA(a) | IMM_L(i)) -#define PPC_RAW_CMPLDI(a, i) (PPC_INST_CMPLDI | ___PPC_RA(a) | IMM_L(i)) -#define PPC_RAW_CMPLW(a, b) (PPC_INST_CMPLW | ___PPC_RA(a) | ___PPC_RB(b)) -#define PPC_RAW_CMPLD(a, b) (PPC_INST_CMPLD | ___PPC_RA(a) | ___PPC_RB(b)) -#define PPC_RAW_SUB(d, a, b) (PPC_INST_SUB | ___PPC_RT(d) | ___PPC_RB(a) | ___PPC_RA(b)) -#define PPC_RAW_MULD(d, a, b) (PPC_INST_MULLD | ___PPC_RT(d) | ___PPC_RA(a) | ___PPC_RB(b)) -#define PPC_RAW_MULW(d, a, b) (PPC_INST_MULLW | ___PPC_RT(d) | ___PPC_RA(a) | ___PPC_RB(b)) -#define PPC_RAW_MULHWU(d, a, b) (PPC_INST_MULHWU | ___PPC_RT(d) | ___PPC_RA(a) | ___PPC_RB(b)) -#define PPC_RAW_MULI(d, a, i) (PPC_INST_MULLI | ___PPC_RT(d) | ___PPC_RA(a) | IMM_L(i)) -#define PPC_RAW_DIVWU(d, a, b) (PPC_INST_DIVWU | ___PPC_RT(d) | ___PPC_RA(a) | ___PPC_RB(b)) -#define PPC_RAW_DIVDU(d, a, b) (PPC_INST_DIVDU | ___PPC_RT(d) | ___PPC_RA(a) | ___PPC_RB(b)) -#define PPC_RAW_AND(d, a, b) (PPC_INST_AND | ___PPC_RA(d) | ___PPC_RS(a) | ___PPC_RB(b)) -#define PPC_RAW_ANDI(d, a, i) (PPC_INST_ANDI | ___PPC_RA(d) | ___PPC_RS(a) | IMM_L(i)) -#define PPC_RAW_AND_DOT(d, a, b) (PPC_INST_ANDDOT | ___PPC_RA(d) | ___PPC_RS(a) | ___PPC_RB(b)) -#define PPC_RAW_OR(d, a, b) (PPC_INST_OR | ___PPC_RA(d) | ___PPC_RS(a) | ___PPC_RB(b)) +#define PPC_RAW_STDX(r, base, b) (0x7c00012a | ___PPC_RS(r) | ___PPC_RA(base) | ___PPC_RB(b)) +#define PPC_RAW_STDU(r, base, i) (0xf8000001 | ___PPC_RS(r) | ___PPC_RA(base) | ((i) & 0xfffc)) +#define PPC_RAW_STW(r, base, i) (0x90000000 | ___PPC_RS(r) | ___PPC_RA(base) | IMM_L(i)) +#define PPC_RAW_STWU(r, base, i) (0x94000000 | ___PPC_RS(r) | ___PPC_RA(base) | IMM_L(i)) +#define PPC_RAW_STH(r, base, i) (0xb0000000 | ___PPC_RS(r) | ___PPC_RA(base) | IMM_L(i)) +#define PPC_RAW_STB(r, base, i) (0x98000000 | ___PPC_RS(r) | ___PPC_RA(base) | IMM_L(i)) +#define PPC_RAW_LBZ(r, base, i) (0x88000000 | ___PPC_RT(r) | ___PPC_RA(base) | IMM_L(i)) +#define PPC_RAW_LDX(r, base, b) (0x7c00002a | ___PPC_RT(r) | ___PPC_RA(base) | ___PPC_RB(b)) +#define PPC_RAW_LHZ(r, base, i) (0xa0000000 | ___PPC_RT(r) | ___PPC_RA(base) | IMM_L(i)) +#define PPC_RAW_LHBRX(r, base, b) (0x7c00062c | ___PPC_RT(r) | ___PPC_RA(base) | ___PPC_RB(b)) +#define PPC_RAW_LDBRX(r, base, b) (0x7c000428 | ___PPC_RT(r) | ___PPC_RA(base) | ___PPC_RB(b)) +#define PPC_RAW_STWCX(s, a, b) (0x7c00012d | ___PPC_RS(s) | ___PPC_RA(a) | ___PPC_RB(b)) +#define PPC_RAW_CMPWI(a, i) (0x2c000000 | ___PPC_RA(a) | IMM_L(i)) +#define PPC_RAW_CMPDI(a, i) (0x2c200000 | ___PPC_RA(a) | IMM_L(i)) +#define PPC_RAW_CMPW(a, b) (0x7c000000 | ___PPC_RA(a) | ___PPC_RB(b)) +#define PPC_RAW_CMPD(a, b) (0x7c200000 | ___PPC_RA(a) | ___PPC_RB(b)) +#define PPC_RAW_CMPLWI(a, i) (0x28000000 | ___PPC_RA(a) | IMM_L(i)) +#define PPC_RAW_CMPLDI(a, i) (0x28200000 | ___PPC_RA(a) | IMM_L(i)) +#define PPC_RAW_CMPLW(a, b) (0x7c000040 | ___PPC_RA(a) | ___PPC_RB(b)) +#define PPC_RAW_CMPLD(a, b) (0x7c200040 | ___PPC_RA(a) | ___PPC_RB(b)) +#define PPC_RAW_SUB(d, a, b) (0x7c000050 | ___PPC_RT(d) | ___PPC_RB(a) | ___PPC_RA(b)) +#define PPC_RAW_MULD(d, a, b) (0x7c0001d2 | ___PPC_RT(d) | ___PPC_RA(a) | ___PPC_RB(b)) +#define PPC_RAW_MULW(d, a, b) (0x7c0001d6 | ___PPC_RT(d) | ___PPC_RA(a) | ___PPC_RB(b)) +#define PPC_RAW_MULHWU(d, a, b) (0x7c000016 | ___PPC_RT(d) | ___PPC_RA(a) | ___PPC_RB(b)) +#define PPC_RAW_MULI(d, a, i) (0x1c000000 | ___PPC_RT(d) | ___PPC_RA(a) | IMM_L(i)) +#define PPC_RAW_DIVWU(d, a, b) (0x7c000396 | ___PPC_RT(d) | ___PPC_RA(a) | ___PPC_RB(b)) +#define PPC_RAW_DIVDU(d, a, b) (0x7c000392 | ___PPC_RT(d) | ___PPC_RA(a) | ___PPC_RB(b)) +#define PPC_RAW_AND(d, a, b) (0x7c000038 | ___PPC_RA(d) | ___PPC_RS(a) | ___PPC_RB(b)) +#define PPC_RAW_ANDI(d, a, i) (0x70000000 | ___PPC_RA(d) | ___PPC_RS(a) | IMM_L(i)) +#define PPC_RAW_AND_DOT(d, a, b) (0x7c000039 | ___PPC_RA(d) | ___PPC_RS(a) | ___PPC_RB(b)) +#define PPC_RAW_OR(d, a, b) (0x7c000378 | ___PPC_RA(d) | ___PPC_RS(a) | ___PPC_RB(b)) #define PPC_RAW_MR(d, a) PPC_RAW_OR(d, a, a) #define PPC_RAW_ORI(d, a, i) (PPC_INST_ORI | ___PPC_RA(d) | ___PPC_RS(a) | IMM_L(i)) #define PPC_RAW_ORIS(d, a, i) (PPC_INST_ORIS | ___PPC_RA(d) | ___PPC_RS(a) | IMM_L(i)) -#define PPC_RAW_XOR(d, a, b) (PPC_INST_XOR | ___PPC_RA(d) | ___PPC_RS(a) | ___PPC_RB(b)) -#define PPC_RAW_XORI(d, a, i) (PPC_INST_XORI | ___PPC_RA(d) | ___PPC_RS(a) | IMM_L(i)) -#define PPC_RAW_XORIS(d, a, i) (PPC_INST_XORIS | ___PPC_RA(d) | ___PPC_RS(a) | IMM_L(i)) -#define PPC_RAW_EXTSW(d, a) (PPC_INST_EXTSW | ___PPC_RA(d) | ___PPC_RS(a)) -#define PPC_RAW_SLW(d, a, s) (PPC_INST_SLW | ___PPC_RA(d) | ___PPC_RS(a) | ___PPC_RB(s)) -#define PPC_RAW_SLD(d, a, s) (PPC_INST_SLD | ___PPC_RA(d) | ___PPC_RS(a) | ___PPC_RB(s)) -#define PPC_RAW_SRW(d, a, s) (PPC_INST_SRW | ___PPC_RA(d) | ___PPC_RS(a) | ___PPC_RB(s)) -#define PPC_RAW_SRAW(d, a, s) (PPC_INST_SRAW | ___PPC_RA(d) | ___PPC_RS(a) | ___PPC_RB(s)) -#define PPC_RAW_SRAWI(d, a, i) (PPC_INST_SRAWI | ___PPC_RA(d) | ___PPC_RS(a) | __PPC_SH(i)) -#define PPC_RAW_SRD(d, a, s) (PPC_INST_SRD | ___PPC_RA(d) | ___PPC_RS(a) | ___PPC_RB(s)) -#define PPC_RAW_SRAD(d, a, s) (PPC_INST_SRAD | ___PPC_RA(d) | ___PPC_RS(a) | ___PPC_RB(s)) -#define PPC_RAW_SRADI(d, a, i) (PPC_INST_SRADI | ___PPC_RA(d) | ___PPC_RS(a) | __PPC_SH64(i)) -#define PPC_RAW_RLWINM(d, a, i, mb, me) \ - (PPC_INST_RLWINM | ___PPC_RA(d) | ___PPC_RS(a) | __PPC_SH(i) | __PPC_MB(mb) | __PPC_ME(me)) +#define PPC_RAW_XOR(d, a, b) (0x7c000278 | ___PPC_RA(d) | ___PPC_RS(a) | ___PPC_RB(b)) +#define PPC_RAW_XORI(d, a, i) (0x68000000 | ___PPC_RA(d) | ___PPC_RS(a) | IMM_L(i)) +#define PPC_RAW_XORIS(d, a, i) (0x6c000000 | ___PPC_RA(d) | ___PPC_RS(a) | IMM_L(i)) +#define PPC_RAW_EXTSW(d, a) (0x7c0007b4 | ___PPC_RA(d) | ___PPC_RS(a)) +#define PPC_RAW_SLW(d, a, s) (0x7c000030 | ___PPC_RA(d) | ___PPC_RS(a) | ___PPC_RB(s)) +#define PPC_RAW_SLD(d, a, s) (0x7c000036 | ___PPC_RA(d) | ___PPC_RS(a) | ___PPC_RB(s)) +#define PPC_RAW_SRW(d, a, s) (0x7c000430 | ___PPC_RA(d) | ___PPC_RS(a) | ___PPC_RB(s)) +#define PPC_RAW_SRAW(d, a, s) (0x7c000630 | ___PPC_RA(d) | ___PPC_RS(a) | ___PPC_RB(s)) +#define PPC_RAW_SRAWI(d, a, i) (0x7c000670 | ___PPC_RA(d) | ___PPC_RS(a) | __PPC_SH(i)) +#define PPC_RAW_SRD(d, a, s) (0x7c000436 | ___PPC_RA(d) | ___PPC_RS(a) | ___PPC_RB(s)) +#define PPC_RAW_SRAD(d, a, s) (0x7c000634 | ___PPC_RA(d) | ___PPC_RS(a) | ___PPC_RB(s)) +#define PPC_RAW_SRADI(d, a, i) (0x7c000674 | ___PPC_RA(d) | ___PPC_RS(a) | __PPC_SH64(i)) +#define PPC_RAW_RLWINM(d, a, i, mb, me) (0x54000000 | ___PPC_RA(d) | ___PPC_RS(a) | __PPC_SH(i) | __PPC_MB(mb) | __PPC_ME(me)) #define PPC_RAW_RLWINM_DOT(d, a, i, mb, me) \ - (PPC_INST_RLWINM_DOT | ___PPC_RA(d) | ___PPC_RS(a) | __PPC_SH(i) | __PPC_MB(mb) | __PPC_ME(me)) -#define PPC_RAW_RLWIMI(d, a, i, mb, me) \ - (PPC_INST_RLWIMI | ___PPC_RA(d) | ___PPC_RS(a) | __PPC_SH(i) | __PPC_MB(mb) | __PPC_ME(me)) -#define PPC_RAW_RLDICL(d, a, i, mb) (PPC_INST_RLDICL | ___PPC_RA(d) | ___PPC_RS(a) | __PPC_SH64(i) | __PPC_MB64(mb)) + (0x54000001 | ___PPC_RA(d) | ___PPC_RS(a) | __PPC_SH(i) | __PPC_MB(mb) | __PPC_ME(me)) +#define PPC_RAW_RLWIMI(d, a, i, mb, me) (0x50000000 | ___PPC_RA(d) | ___PPC_RS(a) | __PPC_SH(i) | __PPC_MB(mb) | __PPC_ME(me)) +#define PPC_RAW_RLDICL(d, a, i, mb) (0x78000000 | ___PPC_RA(d) | ___PPC_RS(a) | __PPC_SH64(i) | __PPC_MB64(mb)) #define PPC_RAW_RLDICR(d, a, i, me) (PPC_INST_RLDICR | ___PPC_RA(d) | ___PPC_RS(a) | __PPC_SH64(i) | __PPC_ME64(me)) /* slwi = rlwinm Rx, Ry, n, 0, 31-n */ @@ -624,7 +488,7 @@ /* sldi = rldicl Rx, Ry, 64-n, n */ #define PPC_RAW_SRDI(d, a, i) PPC_RAW_RLDICL(d, a, 64-(i), i) -#define PPC_RAW_NEG(d, a) (PPC_INST_NEG | ___PPC_RT(d) | ___PPC_RA(a)) +#define PPC_RAW_NEG(d, a) (0x7c0000d0 | ___PPC_RT(d) | ___PPC_RA(a)) /* Deal with instructions that older assemblers aren't aware of */ #define PPC_BCCTR_FLUSH stringify_in_c(.long PPC_INST_BCCTR_FLUSH) -- GitLab From ade7667a981be49af9310f7c682c226283ec833d Mon Sep 17 00:00:00 2001 From: Anton Blanchard Date: Mon, 13 Jul 2020 18:36:01 +1000 Subject: [PATCH 0488/1476] powerpc: Add cputime_to_nsecs() Generic code has a wrapper to implement cputime_to_nsecs() on top of cputime_to_usecs() but we can easily return the full nanosecond resolution directly. Signed-off-by: Anton Blanchard Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20200713083601.1103978-1-anton@ozlabs.org --- arch/powerpc/include/asm/cputime.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/arch/powerpc/include/asm/cputime.h b/arch/powerpc/include/asm/cputime.h index 0fccd5ea1e9a2..9335b93924b43 100644 --- a/arch/powerpc/include/asm/cputime.h +++ b/arch/powerpc/include/asm/cputime.h @@ -36,6 +36,8 @@ static inline unsigned long cputime_to_usecs(const cputime_t ct) return mulhdu((__force u64) ct, __cputime_usec_factor); } +#define cputime_to_nsecs(cputime) tb_to_ns((__force u64)cputime) + /* * PPC64 uses PACA which is task independent for storing accounting data while * PPC32 uses struct thread_info, therefore at task switch the accounting data -- GitLab From ba608c4fa12cfd0cab0e153249c29441f4dd3312 Mon Sep 17 00:00:00 2001 From: Sourabh Jain Date: Mon, 13 Jul 2020 10:54:35 +0530 Subject: [PATCH 0489/1476] powerpc/fadump: fix race between pstore write and fadump crash trigger When we enter into fadump crash path via system reset we fail to update the pstore. On the system reset path we first update the pstore then we go for fadump crash. But the problem here is when all the CPUs try to get the pstore lock to initiate the pstore write, only one CPUs will acquire the lock and proceed with the pstore write. Since it in NMI context CPUs that fail to get lock do not wait for their turn to write to the pstore and simply proceed with the next operation which is fadump crash. One of the CPU who proceeded with fadump crash path triggers the crash and does not wait for the CPU who gets the pstore lock to complete the pstore update. Timeline diagram to depicts the sequence of events that leads to an unsuccessful pstore update when we hit fadump crash path via system reset. 1 2 3 ... n CPU Threads | | | | | | | | Reached to -->|--->|---->| ----------->| system reset | | | | path | | | | | | | | Try to -->|--->|---->|------------>| acquire the | | | | pstore lock | | | | | | | | | | | | Got the -->| +->| | |<-+ pstore lock | | | | | |--> Didn't get the | --------------------------+ lock and moving | | | | ahead on fadump | | | | crash path | | | | Begins the -->| | | | process to | | | |<-- Got the chance to update the | | | | trigger the crash pstore | -> | | ... <- | | | | | | | | | | | | |<-- Triggers the | | | | | | crash | | | | | | ^ | | | | | | | Writing to -->| | | | | | | pstore | | | | | | | | | | ^ |__________________| | | CPU Relax | | | +-----------------------------------------+ | v Race: crash triggered before pstore update completes To avoid this race condition a barrier is added on crash_fadump path, it prevents the CPU to trigger the crash until all the online CPUs completes their task. A barrier is added to make sure all the secondary CPUs hit the crash_fadump function before we initiates the crash. A timeout is kept to ensure the primary CPU (one who initiates the crash) do not wait for secondary CPUs indefinitely. Signed-off-by: Sourabh Jain Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20200713052435.183750-1-sourabhjain@linux.ibm.com --- arch/powerpc/kernel/fadump.c | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/arch/powerpc/kernel/fadump.c b/arch/powerpc/kernel/fadump.c index 78ab9a6ee6acc..1858896d68099 100644 --- a/arch/powerpc/kernel/fadump.c +++ b/arch/powerpc/kernel/fadump.c @@ -32,11 +32,20 @@ #include #include +/* + * The CPU who acquired the lock to trigger the fadump crash should + * wait for other CPUs to enter. + * + * The timeout is in milliseconds. + */ +#define CRASH_TIMEOUT 500 + static struct fw_dump fw_dump; static void __init fadump_reserve_crash_area(u64 base); struct kobject *fadump_kobj; +static atomic_t cpus_in_fadump; #ifndef CONFIG_PRESERVE_FA_DUMP static DEFINE_MUTEX(fadump_mutex); @@ -668,8 +677,11 @@ early_param("fadump_reserve_mem", early_fadump_reserve_mem); void crash_fadump(struct pt_regs *regs, const char *str) { + unsigned int msecs; struct fadump_crash_info_header *fdh = NULL; int old_cpu, this_cpu; + /* Do not include first CPU */ + unsigned int ncpus = num_online_cpus() - 1; if (!should_fadump_crash()) return; @@ -685,6 +697,8 @@ void crash_fadump(struct pt_regs *regs, const char *str) old_cpu = cmpxchg(&crashing_cpu, -1, this_cpu); if (old_cpu != -1) { + atomic_inc(&cpus_in_fadump); + /* * We can't loop here indefinitely. Wait as long as fadump * is in force. If we race with fadump un-registration this @@ -708,6 +722,16 @@ void crash_fadump(struct pt_regs *regs, const char *str) fdh->online_mask = *cpu_online_mask; + /* + * If we came in via system reset, wait a while for the secondary + * CPUs to enter. + */ + if (TRAP(&(fdh->regs)) == 0x100) { + msecs = CRASH_TIMEOUT; + while ((atomic_read(&cpus_in_fadump) < ncpus) && (--msecs > 0)) + mdelay(1); + } + fw_dump.ops->fadump_trigger(fdh, str); } -- GitLab From 29d9407e1037868b59d12948d42ad3ef58fc3a5a Mon Sep 17 00:00:00 2001 From: YueHaibing Date: Wed, 15 Jul 2020 10:50:40 +0800 Subject: [PATCH 0490/1476] powerpc/xive: Remove unused inline function xive_kexec_teardown_cpu() commit e27e0a94651e ("powerpc/xive: Remove xive_kexec_teardown_cpu()") left behind this, remove it. Signed-off-by: YueHaibing Reviewed-by: Greg Kurz Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20200715025040.33952-1-yuehaibing@huawei.com --- arch/powerpc/include/asm/xive.h | 1 - 1 file changed, 1 deletion(-) diff --git a/arch/powerpc/include/asm/xive.h b/arch/powerpc/include/asm/xive.h index d08ea11b271cb..309b4d65b74fa 100644 --- a/arch/powerpc/include/asm/xive.h +++ b/arch/powerpc/include/asm/xive.h @@ -155,7 +155,6 @@ static inline void xive_smp_probe(void) { } static inline int xive_smp_prepare_cpu(unsigned int cpu) { return -EINVAL; } static inline void xive_smp_setup_cpu(void) { } static inline void xive_smp_disable_cpu(void) { } -static inline void xive_kexec_teardown_cpu(int secondary) { } static inline void xive_shutdown(void) { } static inline void xive_flush_interrupt(void) { } -- GitLab From 89c140bbaeee7a55ed0360a88f294ead2b95201b Mon Sep 17 00:00:00 2001 From: Anton Blanchard Date: Wed, 15 Jul 2020 10:08:20 +1000 Subject: [PATCH 0491/1476] pseries: Fix 64 bit logical memory block panic Booting with a 4GB LMB size causes us to panic: qemu-system-ppc64: OS terminated: OS panic: Memory block size not suitable: 0x0 Fix pseries_memory_block_size() to handle 64 bit LMBs. Cc: stable@vger.kernel.org Signed-off-by: Anton Blanchard Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20200715000820.1255764-1-anton@ozlabs.org --- arch/powerpc/platforms/pseries/hotplug-memory.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/powerpc/platforms/pseries/hotplug-memory.c b/arch/powerpc/platforms/pseries/hotplug-memory.c index 73a5dcd977e11..5d545b78111f9 100644 --- a/arch/powerpc/platforms/pseries/hotplug-memory.c +++ b/arch/powerpc/platforms/pseries/hotplug-memory.c @@ -25,7 +25,7 @@ unsigned long pseries_memory_block_size(void) { struct device_node *np; - unsigned int memblock_size = MIN_MEMORY_BLOCK_SIZE; + u64 memblock_size = MIN_MEMORY_BLOCK_SIZE; struct resource r; np = of_find_node_by_path("/ibm,dynamic-reconfiguration-memory"); -- GitLab From 92fe8483b1660feaa602d8be6ca7efe95ae4789b Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Tue, 14 Jul 2020 22:24:24 +0800 Subject: [PATCH 0492/1476] cpuidle/pseries: Make symbol 'pseries_idle_driver' static The sparse tool complains as follows: drivers/cpuidle/cpuidle-pseries.c:25:23: warning: symbol 'pseries_idle_driver' was not declared. Should it be static? 'pseries_idle_driver' is not used outside of this file, so marks it static. Reported-by: Hulk Robot Signed-off-by: Wei Yongjun Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20200714142424.66648-1-weiyongjun1@huawei.com --- drivers/cpuidle/cpuidle-pseries.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/cpuidle/cpuidle-pseries.c b/drivers/cpuidle/cpuidle-pseries.c index 6513ef2af66a8..3e058ad2bb514 100644 --- a/drivers/cpuidle/cpuidle-pseries.c +++ b/drivers/cpuidle/cpuidle-pseries.c @@ -22,7 +22,7 @@ #include #include -struct cpuidle_driver pseries_idle_driver = { +static struct cpuidle_driver pseries_idle_driver = { .name = "pseries_idle", .owner = THIS_MODULE, }; -- GitLab From 07497137a5efa9b2628c18083e8b07b33160153d Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Mon, 13 Jul 2020 19:55:06 +0200 Subject: [PATCH 0493/1476] ocxl: Replace HTTP links with HTTPS ones Rationale: Reduces attack surface on kernel devs opening the links for MITM as HTTPS traffic is much harder to manipulate. Deterministic algorithm: For each file: If not .svg: For each line: If doesn't contain `\bxmlns\b`: For each link, `\bhttp://[^# \t\r\n]*(?:\w|/)`: If neither `\bgnu\.org/license`, nor `\bmozilla\.org/MPL\b`: If both the HTTP and HTTPS versions return 200 OK and serve the same content: Replace HTTP with HTTPS. Signed-off-by: Alexander A. Klimov Acked-by: Andrew Donnellan Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20200713175506.36676-1-grandmaster@al2klimov.de --- drivers/misc/ocxl/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/misc/ocxl/Kconfig b/drivers/misc/ocxl/Kconfig index 2d2266c1439ef..6551007a066ce 100644 --- a/drivers/misc/ocxl/Kconfig +++ b/drivers/misc/ocxl/Kconfig @@ -23,7 +23,7 @@ config OCXL The ocxl driver enables userspace programs to access these accelerators through devices in /dev/ocxl/. - For more information, see http://opencapi.org. + For more information, see https://opencapi.org. This is not to be confused with the support for IBM CAPI accelerators (CONFIG_CXL), which are PCI-based instead of a -- GitLab From 9a3e3dccbf4317d02d28f8f99a5d1ccce42f9922 Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Mon, 13 Jul 2020 21:26:56 +0200 Subject: [PATCH 0494/1476] powerpc/Kconfig: Replace HTTP links with HTTPS ones Rationale: Reduces attack surface on kernel devs opening the links for MITM as HTTPS traffic is much harder to manipulate. Deterministic algorithm: For each file: If not .svg: For each line: If doesn't contain `\bxmlns\b`: For each link, `\bhttp://[^# \t\r\n]*(?:\w|/)`: If neither `\bgnu\.org/license`, nor `\bmozilla\.org/MPL\b`: If both the HTTP and HTTPS versions return 200 OK and serve the same content: Replace HTTP with HTTPS. Signed-off-by: Alexander A. Klimov Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20200713192656.37443-1-grandmaster@al2klimov.de --- arch/powerpc/platforms/85xx/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/powerpc/platforms/85xx/Kconfig b/arch/powerpc/platforms/85xx/Kconfig index fa3d29dcb57e7..b77cbb0a50e1f 100644 --- a/arch/powerpc/platforms/85xx/Kconfig +++ b/arch/powerpc/platforms/85xx/Kconfig @@ -160,7 +160,7 @@ config XES_MPC85xx computers from Extreme Engineering Solutions (X-ES) based on Freescale MPC85xx processors. Manufacturer: Extreme Engineering Solutions, Inc. - URL: + URL: config STX_GP3 bool "Silicon Turnkey Express GP3" -- GitLab From 77ca3951cc37727ae8361d583a30da7a1b84e427 Mon Sep 17 00:00:00 2001 From: Anju T Sudhakar Date: Mon, 13 Jul 2020 20:16:23 +0530 Subject: [PATCH 0495/1476] powerpc/perf: Add kernel support for new MSR[HV PR] bits in trace-imc IMC trace-mode record has MSR[HV PR] bits added in the third DW. These bits can be used to set the cpumode for the instruction pointer captured in each sample. Add support in kernel to use these bits to set the cpumode for each sample. Signed-off-by: Anju T Sudhakar Signed-off-by: Madhavan Srinivasan Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20200713144623.508695-1-maddy@linux.ibm.com --- arch/powerpc/include/asm/imc-pmu.h | 5 +++++ arch/powerpc/perf/imc-pmu.c | 29 ++++++++++++++++++++++++----- 2 files changed, 29 insertions(+), 5 deletions(-) diff --git a/arch/powerpc/include/asm/imc-pmu.h b/arch/powerpc/include/asm/imc-pmu.h index 4da4fcba0684b..4f897993b7107 100644 --- a/arch/powerpc/include/asm/imc-pmu.h +++ b/arch/powerpc/include/asm/imc-pmu.h @@ -99,6 +99,11 @@ struct trace_imc_data { */ #define IMC_TRACE_RECORD_TB1_MASK 0x3ffffffffffULL +/* + * Bit 0:1 in third DW of IMC trace record + * specifies the MSR[HV PR] values. + */ +#define IMC_TRACE_RECORD_VAL_HVPR(x) ((x) >> 62) /* * Device tree parser code detects IMC pmu support and diff --git a/arch/powerpc/perf/imc-pmu.c b/arch/powerpc/perf/imc-pmu.c index 0edcfd0b491d9..a45d694a5d5d8 100644 --- a/arch/powerpc/perf/imc-pmu.c +++ b/arch/powerpc/perf/imc-pmu.c @@ -1288,11 +1288,30 @@ static int trace_imc_prepare_sample(struct trace_imc_data *mem, header->size = sizeof(*header) + event->header_size; header->misc = 0; - if (is_kernel_addr(data->ip)) - header->misc |= PERF_RECORD_MISC_KERNEL; - else - header->misc |= PERF_RECORD_MISC_USER; - + if (cpu_has_feature(CPU_FTR_ARCH_31)) { + switch (IMC_TRACE_RECORD_VAL_HVPR(mem->val)) { + case 0:/* when MSR HV and PR not set in the trace-record */ + header->misc |= PERF_RECORD_MISC_GUEST_KERNEL; + break; + case 1: /* MSR HV is 0 and PR is 1 */ + header->misc |= PERF_RECORD_MISC_GUEST_USER; + break; + case 2: /* MSR HV is 1 and PR is 0 */ + header->misc |= PERF_RECORD_MISC_HYPERVISOR; + break; + case 3: /* MSR HV is 1 and PR is 1 */ + header->misc |= PERF_RECORD_MISC_USER; + break; + default: + pr_info("IMC: Unable to set the flag based on MSR bits\n"); + break; + } + } else { + if (is_kernel_addr(data->ip)) + header->misc |= PERF_RECORD_MISC_KERNEL; + else + header->misc |= PERF_RECORD_MISC_USER; + } perf_event_header__init_id(header, data, event); return 0; -- GitLab From a9f675f950a07d5c1dbcbb97aabac56f5ed085e3 Mon Sep 17 00:00:00 2001 From: Milton Miller Date: Thu, 16 Jul 2020 09:37:04 +1000 Subject: [PATCH 0496/1476] powerpc/vdso: Fix vdso cpu truncation The code in vdso_cpu_init that exposes the cpu and numa node to userspace via SPRG_VDSO incorrctly masks the cpu to 12 bits. This means that any kernel running on a box with more than 4096 threads (NR_CPUS advertises a limit of of 8192 cpus) would expose userspace to two cpu contexts running at the same time with the same cpu number. Note: I'm not aware of any distro shipping a kernel with support for more than 4096 threads today, nor of any system image that currently exceeds 4096 threads. Found via code browsing. Fixes: 18ad51dd342a7eb09dbcd059d0b451b616d4dafc ("powerpc: Add VDSO version of getcpu") Signed-off-by: Milton Miller Signed-off-by: Anton Blanchard Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20200715233704.1352257-1-anton@ozlabs.org --- arch/powerpc/kernel/vdso.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/powerpc/kernel/vdso.c b/arch/powerpc/kernel/vdso.c index e0f4ba45b6cc7..8dad44262e751 100644 --- a/arch/powerpc/kernel/vdso.c +++ b/arch/powerpc/kernel/vdso.c @@ -677,7 +677,7 @@ int vdso_getcpu_init(void) node = cpu_to_node(cpu); WARN_ON_ONCE(node > 0xffff); - val = (cpu & 0xfff) | ((node & 0xffff) << 16); + val = (cpu & 0xffff) | ((node & 0xffff) << 16); mtspr(SPRN_SPRG_VDSO_WRITE, val); get_paca()->sprg_vdso = val; -- GitLab From 61f879d97ce4510dd29d676a20d67692e3b34806 Mon Sep 17 00:00:00 2001 From: Nayna Jain Date: Wed, 15 Jul 2020 07:52:01 -0400 Subject: [PATCH 0497/1476] powerpc/pseries: Detect secure and trusted boot state of the system. The device-tree properties to check secure and trusted boot state are different for guests (pseries) compared to baremetal (powernv). This patch updates the existing is_ppc_secureboot_enabled() and is_ppc_trustedboot_enabled() functions to add support for pseries. For pseries the secureboot and trustedboot state are exposed via device-tree properties /ibm,secure-boot and /ibm,trusted-boot. The values of ibm,secure-boot under pseries are interpreted as: 0 - Disabled 1 - Enabled in Log-only mode. This patch interprets this value as disabled, since audit mode is currently not supported for Linux. 2 - Enabled and enforced. 3-9 - Enabled and enforcing; requirements are at the discretion of the operating system. The values of ibm,trusted-boot under pseries are interpreted as: 0 - Disabled 1 - Enabled Signed-off-by: Nayna Jain Reviewed-by: Daniel Axtens Reviewed-by: Mimi Zohar [mpe: Drop machdep.h inclusion, tweak change log slightly] Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/1594813921-12425-1-git-send-email-nayna@linux.ibm.com --- arch/powerpc/kernel/secure_boot.c | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/arch/powerpc/kernel/secure_boot.c b/arch/powerpc/kernel/secure_boot.c index 4b982324d3681..f9af305d9579d 100644 --- a/arch/powerpc/kernel/secure_boot.c +++ b/arch/powerpc/kernel/secure_boot.c @@ -23,12 +23,19 @@ bool is_ppc_secureboot_enabled(void) { struct device_node *node; bool enabled = false; + u32 secureboot; node = get_ppc_fw_sb_node(); enabled = of_property_read_bool(node, "os-secureboot-enforcing"); - of_node_put(node); + if (enabled) + goto out; + + if (!of_property_read_u32(of_root, "ibm,secure-boot", &secureboot)) + enabled = (secureboot > 1); + +out: pr_info("Secure boot mode %s\n", enabled ? "enabled" : "disabled"); return enabled; @@ -38,12 +45,19 @@ bool is_ppc_trustedboot_enabled(void) { struct device_node *node; bool enabled = false; + u32 trustedboot; node = get_ppc_fw_sb_node(); enabled = of_property_read_bool(node, "trusted-enabled"); - of_node_put(node); + if (enabled) + goto out; + + if (!of_property_read_u32(of_root, "ibm,trusted-boot", &trustedboot)) + enabled = (trustedboot > 0); + +out: pr_info("Trusted boot mode %s\n", enabled ? "enabled" : "disabled"); return enabled; -- GitLab From c04011fe8cbd80af1be6e12b53193bf3846750d7 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Tue, 14 Jul 2020 08:47:43 +0200 Subject: [PATCH 0498/1476] fs: add a vfs_fchown helper Add a helper for struct file based chown operations. To be used by the initramfs code soon. Signed-off-by: Christoph Hellwig Acked-by: Linus Torvalds --- fs/open.c | 29 +++++++++++++++++------------ include/linux/fs.h | 2 ++ 2 files changed, 19 insertions(+), 12 deletions(-) diff --git a/fs/open.c b/fs/open.c index 6cd48a61cda3b..103c66309bee6 100644 --- a/fs/open.c +++ b/fs/open.c @@ -740,23 +740,28 @@ SYSCALL_DEFINE3(lchown, const char __user *, filename, uid_t, user, gid_t, group AT_SYMLINK_NOFOLLOW); } +int vfs_fchown(struct file *file, uid_t user, gid_t group) +{ + int error; + + error = mnt_want_write_file(file); + if (error) + return error; + audit_file(file); + error = chown_common(&file->f_path, user, group); + mnt_drop_write_file(file); + return error; +} + int ksys_fchown(unsigned int fd, uid_t user, gid_t group) { struct fd f = fdget(fd); int error = -EBADF; - if (!f.file) - goto out; - - error = mnt_want_write_file(f.file); - if (error) - goto out_fput; - audit_file(f.file); - error = chown_common(&f.file->f_path, user, group); - mnt_drop_write_file(f.file); -out_fput: - fdput(f); -out: + if (f.file) { + error = vfs_fchown(f.file, user, group); + fdput(f); + } return error; } diff --git a/include/linux/fs.h b/include/linux/fs.h index f5abba86107d8..0ddd64ca0b45c 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -1744,6 +1744,8 @@ int vfs_mkobj(struct dentry *, umode_t, int (*f)(struct dentry *, umode_t, void *), void *); +int vfs_fchown(struct file *file, uid_t user, gid_t group); + extern long vfs_ioctl(struct file *file, unsigned int cmd, unsigned long arg); #ifdef CONFIG_COMPAT -- GitLab From 9e96c8c0e94eea2f69a9705f5d0f51928ea26c17 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Tue, 14 Jul 2020 08:55:05 +0200 Subject: [PATCH 0499/1476] fs: add a vfs_fchmod helper Add a helper for struct file based chmode operations. To be used by the initramfs code soon. Signed-off-by: Christoph Hellwig Acked-by: Linus Torvalds --- fs/open.c | 9 +++++++-- include/linux/fs.h | 1 + 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/fs/open.c b/fs/open.c index 103c66309bee6..75166f071d280 100644 --- a/fs/open.c +++ b/fs/open.c @@ -602,14 +602,19 @@ out_unlock: return error; } +int vfs_fchmod(struct file *file, umode_t mode) +{ + audit_file(file); + return chmod_common(&file->f_path, mode); +} + int ksys_fchmod(unsigned int fd, umode_t mode) { struct fd f = fdget(fd); int err = -EBADF; if (f.file) { - audit_file(f.file); - err = chmod_common(&f.file->f_path, mode); + err = vfs_fchmod(f.file, mode); fdput(f); } return err; diff --git a/include/linux/fs.h b/include/linux/fs.h index 0ddd64ca0b45c..635086726f205 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -1745,6 +1745,7 @@ int vfs_mkobj(struct dentry *, umode_t, void *); int vfs_fchown(struct file *file, uid_t user, gid_t group); +int vfs_fchmod(struct file *file, umode_t mode); extern long vfs_ioctl(struct file *file, unsigned int cmd, unsigned long arg); -- GitLab From 881627f353754dd05a8f3e1c1941f0dbbf47a170 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Sat, 6 Jun 2020 10:49:35 +0200 Subject: [PATCH 0500/1476] init: remove the bstat helper The only caller of the bstat function becomes cleaner and simpler when open coding the function. Signed-off-by: Christoph Hellwig Reviewed-by: NeilBrown Acked-by: Song Liu Acked-by: Linus Torvalds --- init/do_mounts.h | 10 ---------- init/do_mounts_md.c | 8 ++++---- 2 files changed, 4 insertions(+), 14 deletions(-) diff --git a/init/do_mounts.h b/init/do_mounts.h index 0bb0806de4ce2..7513d1c14d13f 100644 --- a/init/do_mounts.h +++ b/init/do_mounts.h @@ -20,16 +20,6 @@ static inline int create_dev(char *name, dev_t dev) return ksys_mknod(name, S_IFBLK|0600, new_encode_dev(dev)); } -static inline u32 bstat(char *name) -{ - struct kstat stat; - if (vfs_stat(name, &stat) != 0) - return 0; - if (!S_ISBLK(stat.mode)) - return 0; - return stat.rdev; -} - #ifdef CONFIG_BLK_DEV_RAM int __init rd_load_disk(int n); diff --git a/init/do_mounts_md.c b/init/do_mounts_md.c index b84031528dd44..359363e85ccd0 100644 --- a/init/do_mounts_md.c +++ b/init/do_mounts_md.c @@ -138,9 +138,9 @@ static void __init md_setup_drive(void) dev = MKDEV(MD_MAJOR, minor); create_dev(name, dev); for (i = 0; i < MD_SB_DISKS && devname != NULL; i++) { + struct kstat stat; char *p; char comp_name[64]; - u32 rdev; p = strchr(devname, ','); if (p) @@ -150,9 +150,9 @@ static void __init md_setup_drive(void) if (strncmp(devname, "/dev/", 5) == 0) devname += 5; snprintf(comp_name, 63, "/dev/%s", devname); - rdev = bstat(comp_name); - if (rdev) - dev = new_decode_dev(rdev); + if (vfs_stat(comp_name, &stat) == 0 && + S_ISBLK(stat.mode)) + dev = new_decode_dev(stat.rdev); if (!dev) { printk(KERN_WARNING "md: Unknown device name: %s\n", devname); break; -- GitLab From 4f5b246b37e024955c0fcca0c7f5952089052d1d Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Sun, 7 Jun 2020 16:18:59 +0200 Subject: [PATCH 0501/1476] md: move the early init autodetect code to drivers/md/ Just like the NFS and CIFS root code this better lives with the driver it is tightly integrated with. Signed-off-by: Christoph Hellwig Acked-by: Song Liu Acked-by: Linus Torvalds --- drivers/md/Makefile | 3 +++ init/do_mounts_md.c => drivers/md/md-autodetect.c | 15 +++++++++++++-- include/linux/raid/detect.h | 8 ++++++++ init/Makefile | 1 - init/do_mounts.c | 1 + init/do_mounts.h | 10 ---------- 6 files changed, 25 insertions(+), 13 deletions(-) rename init/do_mounts_md.c => drivers/md/md-autodetect.c (96%) diff --git a/drivers/md/Makefile b/drivers/md/Makefile index 31840f95cd408..6d3e234dc46a5 100644 --- a/drivers/md/Makefile +++ b/drivers/md/Makefile @@ -43,6 +43,9 @@ obj-$(CONFIG_MD_FAULTY) += faulty.o obj-$(CONFIG_MD_CLUSTER) += md-cluster.o obj-$(CONFIG_BCACHE) += bcache/ obj-$(CONFIG_BLK_DEV_MD) += md-mod.o +ifeq ($(CONFIG_BLK_DEV_MD),y) +obj-y += md-autodetect.o +endif obj-$(CONFIG_BLK_DEV_DM) += dm-mod.o obj-$(CONFIG_BLK_DEV_DM_BUILTIN) += dm-builtin.o obj-$(CONFIG_DM_UNSTRIPED) += dm-unstripe.o diff --git a/init/do_mounts_md.c b/drivers/md/md-autodetect.c similarity index 96% rename from init/do_mounts_md.c rename to drivers/md/md-autodetect.c index 359363e85ccd0..fe806f7b9759a 100644 --- a/init/do_mounts_md.c +++ b/drivers/md/md-autodetect.c @@ -1,10 +1,15 @@ // SPDX-License-Identifier: GPL-2.0 +#include +#include +#include +#include +#include +#include #include +#include #include #include -#include "do_mounts.h" - /* * When md (and any require personalities) are compiled into the kernel * (not a module), arrays can be assembles are boot time using with AUTODETECT @@ -114,6 +119,12 @@ static int __init md_setup(char *str) return 1; } +static inline int create_dev(char *name, dev_t dev) +{ + ksys_unlink(name); + return ksys_mknod(name, S_IFBLK|0600, new_encode_dev(dev)); +} + static void __init md_setup_drive(void) { int minor, i, ent, partitioned; diff --git a/include/linux/raid/detect.h b/include/linux/raid/detect.h index 37dd3f40cd316..1f029a71c3ef0 100644 --- a/include/linux/raid/detect.h +++ b/include/linux/raid/detect.h @@ -1,3 +1,11 @@ /* SPDX-License-Identifier: GPL-2.0 */ void md_autodetect_dev(dev_t dev); + +#ifdef CONFIG_BLK_DEV_MD +void md_run_setup(void); +#else +static inline void md_run_setup(void) +{ +} +#endif diff --git a/init/Makefile b/init/Makefile index 57499b1ff4714..6bc37f64b3617 100644 --- a/init/Makefile +++ b/init/Makefile @@ -18,7 +18,6 @@ obj-y += init_task.o mounts-y := do_mounts.o mounts-$(CONFIG_BLK_DEV_RAM) += do_mounts_rd.o mounts-$(CONFIG_BLK_DEV_INITRD) += do_mounts_initrd.o -mounts-$(CONFIG_BLK_DEV_MD) += do_mounts_md.o # dependencies on generated files need to be listed explicitly $(obj)/version.o: include/generated/compile.h diff --git a/init/do_mounts.c b/init/do_mounts.c index 29d326b6c29d2..1a4dfa17fb289 100644 --- a/init/do_mounts.c +++ b/init/do_mounts.c @@ -23,6 +23,7 @@ #include #include #include +#include #include #include "do_mounts.h" diff --git a/init/do_mounts.h b/init/do_mounts.h index 7513d1c14d13f..50d6c8941e15a 100644 --- a/init/do_mounts.h +++ b/init/do_mounts.h @@ -41,13 +41,3 @@ bool __init initrd_load(void); static inline bool initrd_load(void) { return false; } #endif - -#ifdef CONFIG_BLK_DEV_MD - -void md_run_setup(void); - -#else - -static inline void md_run_setup(void) {} - -#endif -- GitLab From d82fa81c2364a464ae585eed523496aa00909c17 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Sat, 6 Jun 2020 15:00:24 +0200 Subject: [PATCH 0502/1476] md: replace the RAID_AUTORUN ioctl with a direct function call Instead of using a spcial RAID_AUTORUN ioctl that only exists for non-modular builds and is only called from the early init code, just call the actual function directly. Signed-off-by: Christoph Hellwig Reviewed-by: NeilBrown Acked-by: Song Liu Acked-by: Linus Torvalds --- drivers/md/md-autodetect.c | 10 ++-------- drivers/md/md.c | 14 +------------- drivers/md/md.h | 3 +++ 3 files changed, 6 insertions(+), 21 deletions(-) diff --git a/drivers/md/md-autodetect.c b/drivers/md/md-autodetect.c index fe806f7b9759a..0eb746211ed53 100644 --- a/drivers/md/md-autodetect.c +++ b/drivers/md/md-autodetect.c @@ -9,6 +9,7 @@ #include #include #include +#include "md.h" /* * When md (and any require personalities) are compiled into the kernel @@ -285,8 +286,6 @@ __setup("md=", md_setup); static void __init autodetect_raid(void) { - int fd; - /* * Since we don't want to detect and use half a raid array, we need to * wait for the known devices to complete their probing @@ -295,12 +294,7 @@ static void __init autodetect_raid(void) printk(KERN_INFO "md: If you don't use raid, use raid=noautodetect\n"); wait_for_device_probe(); - - fd = ksys_open("/dev/md0", 0, 0); - if (fd >= 0) { - ksys_ioctl(fd, RAID_AUTORUN, raid_autopart); - ksys_close(fd); - } + md_autostart_arrays(raid_autopart); } void __init md_run_setup(void) diff --git a/drivers/md/md.c b/drivers/md/md.c index f567f536b529b..6e9a48da47484 100644 --- a/drivers/md/md.c +++ b/drivers/md/md.c @@ -68,10 +68,6 @@ #include "md-bitmap.h" #include "md-cluster.h" -#ifndef MODULE -static void autostart_arrays(int part); -#endif - /* pers_list is a list of registered personalities protected * by pers_lock. * pers_lock does extra service to protect accesses to @@ -7421,7 +7417,6 @@ static inline bool md_ioctl_valid(unsigned int cmd) case GET_DISK_INFO: case HOT_ADD_DISK: case HOT_REMOVE_DISK: - case RAID_AUTORUN: case RAID_VERSION: case RESTART_ARRAY_RW: case RUN_ARRAY: @@ -7467,13 +7462,6 @@ static int md_ioctl(struct block_device *bdev, fmode_t mode, case RAID_VERSION: err = get_version(argp); goto out; - -#ifndef MODULE - case RAID_AUTORUN: - err = 0; - autostart_arrays(arg); - goto out; -#endif default:; } @@ -9721,7 +9709,7 @@ void md_autodetect_dev(dev_t dev) } } -static void autostart_arrays(int part) +void md_autostart_arrays(int part) { struct md_rdev *rdev; struct detected_devices_node *node_detected_dev; diff --git a/drivers/md/md.h b/drivers/md/md.h index 612814d07d35a..37315a3f28e97 100644 --- a/drivers/md/md.h +++ b/drivers/md/md.h @@ -800,4 +800,7 @@ static inline void mddev_check_write_zeroes(struct mddev *mddev, struct bio *bio !bio->bi_disk->queue->limits.max_write_zeroes_sectors) mddev->queue->limits.max_write_zeroes_sectors = 0; } + +void md_autostart_arrays(int part); + #endif /* _MD_MD_H */ -- GitLab From a1d6bc018911390274e3904bdd28240cd96ddc54 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Sat, 6 Jun 2020 14:56:41 +0200 Subject: [PATCH 0503/1476] md: remove the autoscan partition re-read devfs is long gone, and autoscan works just fine without this these days. Signed-off-by: Christoph Hellwig Reviewed-by: NeilBrown Acked-by: Song Liu Acked-by: Linus Torvalds --- drivers/md/md-autodetect.c | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/drivers/md/md-autodetect.c b/drivers/md/md-autodetect.c index 0eb746211ed53..6bc9b734eee6f 100644 --- a/drivers/md/md-autodetect.c +++ b/drivers/md/md-autodetect.c @@ -240,16 +240,6 @@ static void __init md_setup_drive(void) err = ksys_ioctl(fd, RUN_ARRAY, 0); if (err) printk(KERN_WARNING "md: starting md%d failed\n", minor); - else { - /* reread the partition table. - * I (neilb) and not sure why this is needed, but I cannot - * boot a kernel with devfs compiled in from partitioned md - * array without it - */ - ksys_close(fd); - fd = ksys_open(name, 0, 0); - ksys_ioctl(fd, BLKRRPART, 0); - } ksys_close(fd); } } -- GitLab From 1a6a050620e496abf42749a2e1d3882645cc053f Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Sun, 7 Jun 2020 16:33:01 +0200 Subject: [PATCH 0504/1476] md: remove the kernel version of md_u.h mdp_major can just move to drivers/md/md.h. Signed-off-by: Christoph Hellwig Acked-by: Song Liu Acked-by: Linus Torvalds --- drivers/md/md.h | 1 + include/linux/raid/md_u.h | 13 ------------- 2 files changed, 1 insertion(+), 13 deletions(-) delete mode 100644 include/linux/raid/md_u.h diff --git a/drivers/md/md.h b/drivers/md/md.h index 37315a3f28e97..6f8fff77ce10a 100644 --- a/drivers/md/md.h +++ b/drivers/md/md.h @@ -801,6 +801,7 @@ static inline void mddev_check_write_zeroes(struct mddev *mddev, struct bio *bio mddev->queue->limits.max_write_zeroes_sectors = 0; } +extern int mdp_major; void md_autostart_arrays(int part); #endif /* _MD_MD_H */ diff --git a/include/linux/raid/md_u.h b/include/linux/raid/md_u.h deleted file mode 100644 index 8dfec085a20ee..0000000000000 --- a/include/linux/raid/md_u.h +++ /dev/null @@ -1,13 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - md_u.h : user <=> kernel API between Linux raidtools and RAID drivers - Copyright (C) 1998 Ingo Molnar - -*/ -#ifndef _MD_U_H -#define _MD_U_H - -#include - -extern int mdp_major; -#endif -- GitLab From d1100488c3ce11b853785cf15ee4c19bfab4c842 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Sun, 7 Jun 2020 16:56:05 +0200 Subject: [PATCH 0505/1476] md: simplify md_setup_drive Move the loop over the possible arrays into the caller to remove a level of indentation for the whole function. Signed-off-by: Christoph Hellwig Reviewed-by: NeilBrown Acked-by: Song Liu Acked-by: Linus Torvalds --- drivers/md/md-autodetect.c | 203 ++++++++++++++++++------------------- 1 file changed, 101 insertions(+), 102 deletions(-) diff --git a/drivers/md/md-autodetect.c b/drivers/md/md-autodetect.c index 6bc9b734eee6f..a43a8f1580584 100644 --- a/drivers/md/md-autodetect.c +++ b/drivers/md/md-autodetect.c @@ -27,7 +27,7 @@ static int __initdata raid_noautodetect=1; #endif static int __initdata raid_autopart; -static struct { +static struct md_setup_args { int minor; int partitioned; int level; @@ -126,122 +126,117 @@ static inline int create_dev(char *name, dev_t dev) return ksys_mknod(name, S_IFBLK|0600, new_encode_dev(dev)); } -static void __init md_setup_drive(void) +static void __init md_setup_drive(struct md_setup_args *args) { - int minor, i, ent, partitioned; + int minor, i, partitioned; dev_t dev; dev_t devices[MD_SB_DISKS+1]; + int fd; + int err = 0; + char *devname; + mdu_disk_info_t dinfo; + char name[16]; - for (ent = 0; ent < md_setup_ents ; ent++) { - int fd; - int err = 0; - char *devname; - mdu_disk_info_t dinfo; - char name[16]; + minor = args->minor; + partitioned = args->partitioned; + devname = args->device_names; - minor = md_setup_args[ent].minor; - partitioned = md_setup_args[ent].partitioned; - devname = md_setup_args[ent].device_names; + sprintf(name, "/dev/md%s%d", partitioned?"_d":"", minor); + if (partitioned) + dev = MKDEV(mdp_major, minor << MdpMinorShift); + else + dev = MKDEV(MD_MAJOR, minor); + create_dev(name, dev); + for (i = 0; i < MD_SB_DISKS && devname != NULL; i++) { + struct kstat stat; + char *p; + char comp_name[64]; - sprintf(name, "/dev/md%s%d", partitioned?"_d":"", minor); - if (partitioned) - dev = MKDEV(mdp_major, minor << MdpMinorShift); - else - dev = MKDEV(MD_MAJOR, minor); - create_dev(name, dev); - for (i = 0; i < MD_SB_DISKS && devname != NULL; i++) { - struct kstat stat; - char *p; - char comp_name[64]; + p = strchr(devname, ','); + if (p) + *p++ = 0; - p = strchr(devname, ','); - if (p) - *p++ = 0; + dev = name_to_dev_t(devname); + if (strncmp(devname, "/dev/", 5) == 0) + devname += 5; + snprintf(comp_name, 63, "/dev/%s", devname); + if (vfs_stat(comp_name, &stat) == 0 && S_ISBLK(stat.mode)) + dev = new_decode_dev(stat.rdev); + if (!dev) { + printk(KERN_WARNING "md: Unknown device name: %s\n", devname); + break; + } - dev = name_to_dev_t(devname); - if (strncmp(devname, "/dev/", 5) == 0) - devname += 5; - snprintf(comp_name, 63, "/dev/%s", devname); - if (vfs_stat(comp_name, &stat) == 0 && - S_ISBLK(stat.mode)) - dev = new_decode_dev(stat.rdev); - if (!dev) { - printk(KERN_WARNING "md: Unknown device name: %s\n", devname); - break; - } + devices[i] = dev; + devname = p; + } + devices[i] = 0; - devices[i] = dev; + if (!i) + return; - devname = p; - } - devices[i] = 0; + printk(KERN_INFO "md: Loading md%s%d: %s\n", + partitioned ? "_d" : "", minor, + args->device_names); - if (!i) - continue; + fd = ksys_open(name, 0, 0); + if (fd < 0) { + printk(KERN_ERR "md: open failed - cannot start " + "array %s\n", name); + return; + } + if (ksys_ioctl(fd, SET_ARRAY_INFO, 0) == -EBUSY) { + printk(KERN_WARNING + "md: Ignoring md=%d, already autodetected. (Use raid=noautodetect)\n", + minor); + ksys_close(fd); + return; + } - printk(KERN_INFO "md: Loading md%s%d: %s\n", - partitioned ? "_d" : "", minor, - md_setup_args[ent].device_names); + if (args->level != LEVEL_NONE) { + /* non-persistent */ + mdu_array_info_t ainfo; + ainfo.level = args->level; + ainfo.size = 0; + ainfo.nr_disks =0; + ainfo.raid_disks =0; + while (devices[ainfo.raid_disks]) + ainfo.raid_disks++; + ainfo.md_minor =minor; + ainfo.not_persistent = 1; - fd = ksys_open(name, 0, 0); - if (fd < 0) { - printk(KERN_ERR "md: open failed - cannot start " - "array %s\n", name); - continue; - } - if (ksys_ioctl(fd, SET_ARRAY_INFO, 0) == -EBUSY) { - printk(KERN_WARNING - "md: Ignoring md=%d, already autodetected. (Use raid=noautodetect)\n", - minor); - ksys_close(fd); - continue; + ainfo.state = (1 << MD_SB_CLEAN); + ainfo.layout = 0; + ainfo.chunk_size = args->chunk; + err = ksys_ioctl(fd, SET_ARRAY_INFO, (long)&ainfo); + for (i = 0; !err && i <= MD_SB_DISKS; i++) { + dev = devices[i]; + if (!dev) + break; + dinfo.number = i; + dinfo.raid_disk = i; + dinfo.state = (1< Date: Sun, 7 Jun 2020 17:31:19 +0200 Subject: [PATCH 0506/1476] md: rewrite md_setup_drive to avoid ioctls md_setup_drive knows it works with md devices, so it is rather pointless to open a file descriptor and issue ioctls. Just call directly into the relevant low-level md routines after getting a handle to the device using blkdev_get_by_dev instead. Signed-off-by: Christoph Hellwig Reviewed-by: NeilBrown Acked-by: Song Liu Acked-by: Linus Torvalds --- drivers/md/md-autodetect.c | 134 +++++++++++++++++-------------------- drivers/md/md.c | 24 +++---- drivers/md/md.h | 8 +++ 3 files changed, 81 insertions(+), 85 deletions(-) diff --git a/drivers/md/md-autodetect.c b/drivers/md/md-autodetect.c index a43a8f1580584..14b6e86814c06 100644 --- a/drivers/md/md-autodetect.c +++ b/drivers/md/md-autodetect.c @@ -2,7 +2,6 @@ #include #include #include -#include #include #include #include @@ -120,37 +119,29 @@ static int __init md_setup(char *str) return 1; } -static inline int create_dev(char *name, dev_t dev) -{ - ksys_unlink(name); - return ksys_mknod(name, S_IFBLK|0600, new_encode_dev(dev)); -} - static void __init md_setup_drive(struct md_setup_args *args) { - int minor, i, partitioned; - dev_t dev; - dev_t devices[MD_SB_DISKS+1]; - int fd; - int err = 0; - char *devname; - mdu_disk_info_t dinfo; + char *devname = args->device_names; + dev_t devices[MD_SB_DISKS + 1], mdev; + struct mdu_array_info_s ainfo = { }; + struct block_device *bdev; + struct mddev *mddev; + int err = 0, i; char name[16]; - minor = args->minor; - partitioned = args->partitioned; - devname = args->device_names; + if (args->partitioned) { + mdev = MKDEV(mdp_major, args->minor << MdpMinorShift); + sprintf(name, "md_d%d", args->minor); + } else { + mdev = MKDEV(MD_MAJOR, args->minor); + sprintf(name, "md%d", args->minor); + } - sprintf(name, "/dev/md%s%d", partitioned?"_d":"", minor); - if (partitioned) - dev = MKDEV(mdp_major, minor << MdpMinorShift); - else - dev = MKDEV(MD_MAJOR, minor); - create_dev(name, dev); for (i = 0; i < MD_SB_DISKS && devname != NULL; i++) { struct kstat stat; char *p; char comp_name[64]; + dev_t dev; p = strchr(devname, ','); if (p) @@ -163,7 +154,7 @@ static void __init md_setup_drive(struct md_setup_args *args) if (vfs_stat(comp_name, &stat) == 0 && S_ISBLK(stat.mode)) dev = new_decode_dev(stat.rdev); if (!dev) { - printk(KERN_WARNING "md: Unknown device name: %s\n", devname); + pr_warn("md: Unknown device name: %s\n", devname); break; } @@ -175,68 +166,71 @@ static void __init md_setup_drive(struct md_setup_args *args) if (!i) return; - printk(KERN_INFO "md: Loading md%s%d: %s\n", - partitioned ? "_d" : "", minor, - args->device_names); + pr_info("md: Loading %s: %s\n", name, args->device_names); - fd = ksys_open(name, 0, 0); - if (fd < 0) { - printk(KERN_ERR "md: open failed - cannot start " - "array %s\n", name); + bdev = blkdev_get_by_dev(mdev, FMODE_READ, NULL); + if (IS_ERR(bdev)) { + pr_err("md: open failed - cannot start array %s\n", name); return; } - if (ksys_ioctl(fd, SET_ARRAY_INFO, 0) == -EBUSY) { - printk(KERN_WARNING - "md: Ignoring md=%d, already autodetected. (Use raid=noautodetect)\n", - minor); - ksys_close(fd); - return; + + err = -EIO; + if (WARN(bdev->bd_disk->fops != &md_fops, + "Opening block device %x resulted in non-md device\n", + mdev)) + goto out_blkdev_put; + + mddev = bdev->bd_disk->private_data; + + err = mddev_lock(mddev); + if (err) { + pr_err("md: failed to lock array %s\n", name); + goto out_blkdev_put; + } + + if (!list_empty(&mddev->disks) || mddev->raid_disks) { + pr_warn("md: Ignoring %s, already autodetected. (Use raid=noautodetect)\n", + name); + goto out_unlock; } if (args->level != LEVEL_NONE) { /* non-persistent */ - mdu_array_info_t ainfo; ainfo.level = args->level; - ainfo.size = 0; - ainfo.nr_disks =0; - ainfo.raid_disks =0; - while (devices[ainfo.raid_disks]) - ainfo.raid_disks++; - ainfo.md_minor =minor; + ainfo.md_minor = args->minor; ainfo.not_persistent = 1; - ainfo.state = (1 << MD_SB_CLEAN); - ainfo.layout = 0; ainfo.chunk_size = args->chunk; - err = ksys_ioctl(fd, SET_ARRAY_INFO, (long)&ainfo); - for (i = 0; !err && i <= MD_SB_DISKS; i++) { - dev = devices[i]; - if (!dev) - break; + while (devices[ainfo.raid_disks]) + ainfo.raid_disks++; + } + + err = md_set_array_info(mddev, &ainfo); + + for (i = 0; i <= MD_SB_DISKS && devices[i]; i++) { + struct mdu_disk_info_s dinfo = { + .major = MAJOR(devices[i]), + .minor = MINOR(devices[i]), + }; + + if (args->level != LEVEL_NONE) { dinfo.number = i; dinfo.raid_disk = i; - dinfo.state = (1<major_version != 0) { @@ -7055,7 +7052,7 @@ static int set_bitmap_file(struct mddev *mddev, int fd) } /* - * set_array_info is used two different ways + * md_set_array_info is used two different ways * The original usage is when creating a new array. * In this usage, raid_disks is > 0 and it together with * level, size, not_persistent,layout,chunksize determine the @@ -7067,9 +7064,8 @@ static int set_bitmap_file(struct mddev *mddev, int fd) * The minor and patch _version numbers are also kept incase the * super_block handler wishes to interpret them. */ -static int set_array_info(struct mddev *mddev, mdu_array_info_t *info) +int md_set_array_info(struct mddev *mddev, struct mdu_array_info_s *info) { - if (info->raid_disks == 0) { /* just setting version number for superblock loading */ if (info->major_version < 0 || @@ -7560,7 +7556,7 @@ static int md_ioctl(struct block_device *bdev, fmode_t mode, err = -EBUSY; goto unlock; } - err = set_array_info(mddev, &info); + err = md_set_array_info(mddev, &info); if (err) { pr_warn("md: couldn't set array info. %d\n", err); goto unlock; @@ -7614,7 +7610,7 @@ static int md_ioctl(struct block_device *bdev, fmode_t mode, /* Need to clear read-only for this */ break; else - err = add_new_disk(mddev, &info); + err = md_add_new_disk(mddev, &info); goto unlock; } break; @@ -7682,7 +7678,7 @@ static int md_ioctl(struct block_device *bdev, fmode_t mode, if (copy_from_user(&info, argp, sizeof(info))) err = -EFAULT; else - err = add_new_disk(mddev, &info); + err = md_add_new_disk(mddev, &info); goto unlock; } @@ -7808,7 +7804,7 @@ static int md_revalidate(struct gendisk *disk) mddev->changed = 0; return 0; } -static const struct block_device_operations md_fops = +const struct block_device_operations md_fops = { .owner = THIS_MODULE, .open = md_open, diff --git a/drivers/md/md.h b/drivers/md/md.h index 6f8fff77ce10a..ada3106cb1ddf 100644 --- a/drivers/md/md.h +++ b/drivers/md/md.h @@ -801,7 +801,15 @@ static inline void mddev_check_write_zeroes(struct mddev *mddev, struct bio *bio mddev->queue->limits.max_write_zeroes_sectors = 0; } +struct mdu_array_info_s; +struct mdu_disk_info_s; + extern int mdp_major; void md_autostart_arrays(int part); +int md_set_array_info(struct mddev *mddev, struct mdu_array_info_s *info); +int md_add_new_disk(struct mddev *mddev, struct mdu_disk_info_s *info); +int do_md_run(struct mddev *mddev); + +extern const struct block_device_operations md_fops; #endif /* _MD_MD_H */ -- GitLab From c3846c4cce15bf7d1f2ad75294445317eb4b26f1 Mon Sep 17 00:00:00 2001 From: Ludovic Desroches Date: Thu, 16 Jul 2020 09:15:24 +0200 Subject: [PATCH 0507/1476] MAINTAINERS: dmaengine: Microchip: add Tudor Ambarus as co-maintainer Add Tudor Ambarus as co-maintainer for both Microchip DMA drivers and take the opportunity to merge both entries. Signed-off-by: Ludovic Desroches Link: https://lore.kernel.org/r/20200716071524.25642-1-ludovic.desroches@microchip.com Signed-off-by: Vinod Koul --- MAINTAINERS | 31 +++++++++++++------------------ 1 file changed, 13 insertions(+), 18 deletions(-) diff --git a/MAINTAINERS b/MAINTAINERS index 49d096742d5dc..9af8f81f726d1 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -11194,6 +11194,19 @@ W: http://www.monstr.eu/fdt/ T: git git://git.monstr.eu/linux-2.6-microblaze.git F: arch/microblaze/ +MICROCHIP AT91 DMA DRIVERS +M: Ludovic Desroches +M: Tudor Ambarus +L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) +L: dmaengine@vger.kernel.org +S: Supported +F: Documentation/devicetree/bindings/dma/atmel-dma.txt +F: drivers/dma/at_hdmac.c +F: drivers/dma/at_hdmac_regs.h +F: drivers/dma/at_xdmac.c +F: include/dt-bindings/dma/at91.h +F: include/linux/platform_data/dma-atmel.h + MICROCHIP AT91 SERIAL DRIVER M: Richard Genoud S: Maintained @@ -11222,17 +11235,6 @@ L: alsa-devel@alsa-project.org (moderated for non-subscribers) S: Supported F: sound/soc/atmel -MICROCHIP DMA DRIVER -M: Ludovic Desroches -L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) -L: dmaengine@vger.kernel.org -S: Supported -F: Documentation/devicetree/bindings/dma/atmel-dma.txt -F: drivers/dma/at_hdmac.c -F: drivers/dma/at_hdmac_regs.h -F: include/dt-bindings/dma/at91.h -F: include/linux/platform_data/dma-atmel.h - MICROCHIP ECC DRIVER M: Tudor Ambarus L: linux-crypto@vger.kernel.org @@ -11362,13 +11364,6 @@ L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) S: Supported F: drivers/usb/gadget/udc/atmel_usba_udc.* -MICROCHIP XDMA DRIVER -M: Ludovic Desroches -L: linux-arm-kernel@lists.infradead.org -L: dmaengine@vger.kernel.org -S: Supported -F: drivers/dma/at_xdmac.c - MICROSEMI ETHERNET SWITCH DRIVER M: Alexandre Belloni M: Microchip Linux Driver Support -- GitLab From ab6041e409001dbbbe1167532aef0ec8b15e4393 Mon Sep 17 00:00:00 2001 From: "Koehrer Mathias (ETAS/EES-SL)" Date: Wed, 15 Jul 2020 08:41:19 +0000 Subject: [PATCH 0508/1476] dmaengine: Extend NXP QDMA driver to check transmission errors Extend NXP QDMA driver to check transmission errors The NXP QDMA driver (fsl-qdma.c) does not check the status bits that indicate if a DMA transfer has been completed successfully. This patch extends the driver to do exactly this. Signed-off-by: Mathias Koehrer Link: https://lore.kernel.org/r/744443c0462aac2df4754f99500a911527c0b235.camel@bosch.com Signed-off-by: Vinod Koul --- drivers/dma/fsl-qdma.c | 63 ++++++++++++++++++++++++++++++++++++++---- 1 file changed, 57 insertions(+), 6 deletions(-) diff --git a/drivers/dma/fsl-qdma.c b/drivers/dma/fsl-qdma.c index c33f491fae3e5..ed2ab46b15e7f 100644 --- a/drivers/dma/fsl-qdma.c +++ b/drivers/dma/fsl-qdma.c @@ -56,7 +56,7 @@ /* Registers for bit and genmask */ #define FSL_QDMA_CQIDR_SQT BIT(15) -#define QDMA_CCDF_FOTMAT BIT(29) +#define QDMA_CCDF_FORMAT BIT(29) #define QDMA_CCDF_SER BIT(30) #define QDMA_SG_FIN BIT(30) #define QDMA_SG_LEN_MASK GENMASK(29, 0) @@ -110,8 +110,19 @@ #define FSL_QDMA_CMD_DSEN_OFFSET 19 #define FSL_QDMA_CMD_LWC_OFFSET 16 +/* Field definition for Descriptor status */ +#define QDMA_CCDF_STATUS_RTE BIT(5) +#define QDMA_CCDF_STATUS_WTE BIT(4) +#define QDMA_CCDF_STATUS_CDE BIT(2) +#define QDMA_CCDF_STATUS_SDE BIT(1) +#define QDMA_CCDF_STATUS_DDE BIT(0) +#define QDMA_CCDF_STATUS_MASK (QDMA_CCDF_STATUS_RTE | \ + QDMA_CCDF_STATUS_WTE | \ + QDMA_CCDF_STATUS_CDE | \ + QDMA_CCDF_STATUS_SDE | \ + QDMA_CCDF_STATUS_DDE) + /* Field definition for Descriptor offset */ -#define QDMA_CCDF_STATUS 20 #define QDMA_CCDF_OFFSET 20 #define QDMA_SDDF_CMD(x) (((u64)(x)) << 32) @@ -243,13 +254,14 @@ qdma_ccdf_get_offset(const struct fsl_qdma_format *ccdf) static inline void qdma_ccdf_set_format(struct fsl_qdma_format *ccdf, int offset) { - ccdf->cfg = cpu_to_le32(QDMA_CCDF_FOTMAT | offset); + ccdf->cfg = cpu_to_le32(QDMA_CCDF_FORMAT | + (offset << QDMA_CCDF_OFFSET)); } static inline int qdma_ccdf_get_status(const struct fsl_qdma_format *ccdf) { - return (le32_to_cpu(ccdf->status) & QDMA_CCDF_MASK) >> QDMA_CCDF_STATUS; + return (le32_to_cpu(ccdf->status) & QDMA_CCDF_STATUS_MASK); } static inline void @@ -618,6 +630,7 @@ fsl_qdma_queue_transfer_complete(struct fsl_qdma_engine *fsl_qdma, { bool duplicate; u32 reg, i, count; + u8 completion_status; struct fsl_qdma_queue *temp_queue; struct fsl_qdma_format *status_addr; struct fsl_qdma_comp *fsl_comp = NULL; @@ -677,6 +690,8 @@ fsl_qdma_queue_transfer_complete(struct fsl_qdma_engine *fsl_qdma, } list_del(&fsl_comp->list); + completion_status = qdma_ccdf_get_status(status_addr); + reg = qdma_readl(fsl_qdma, block + FSL_QDMA_BSQMR); reg |= FSL_QDMA_BSQMR_DI; qdma_desc_addr_set64(status_addr, 0x0); @@ -686,6 +701,31 @@ fsl_qdma_queue_transfer_complete(struct fsl_qdma_engine *fsl_qdma, qdma_writel(fsl_qdma, reg, block + FSL_QDMA_BSQMR); spin_unlock(&temp_queue->queue_lock); + /* The completion_status is evaluated here + * (outside of spin lock) + */ + if (completion_status) { + /* A completion error occurred! */ + if (completion_status & QDMA_CCDF_STATUS_WTE) { + /* Write transaction error */ + fsl_comp->vdesc.tx_result.result = + DMA_TRANS_WRITE_FAILED; + } else if (completion_status & QDMA_CCDF_STATUS_RTE) { + /* Read transaction error */ + fsl_comp->vdesc.tx_result.result = + DMA_TRANS_READ_FAILED; + } else { + /* Command/source/destination + * description error + */ + fsl_comp->vdesc.tx_result.result = + DMA_TRANS_ABORTED; + dev_err(fsl_qdma->dma_dev.dev, + "DMA status descriptor error %x\n", + completion_status); + } + } + spin_lock(&fsl_comp->qchan->vchan.lock); vchan_cookie_complete(&fsl_comp->vdesc); fsl_comp->qchan->status = DMA_COMPLETE; @@ -700,11 +740,22 @@ static irqreturn_t fsl_qdma_error_handler(int irq, void *dev_id) unsigned int intr; struct fsl_qdma_engine *fsl_qdma = dev_id; void __iomem *status = fsl_qdma->status_base; + unsigned int decfdw0r; + unsigned int decfdw1r; + unsigned int decfdw2r; + unsigned int decfdw3r; intr = qdma_readl(fsl_qdma, status + FSL_QDMA_DEDR); - if (intr) - dev_err(fsl_qdma->dma_dev.dev, "DMA transaction error!\n"); + if (intr) { + decfdw0r = qdma_readl(fsl_qdma, status + FSL_QDMA_DECFDW0R); + decfdw1r = qdma_readl(fsl_qdma, status + FSL_QDMA_DECFDW1R); + decfdw2r = qdma_readl(fsl_qdma, status + FSL_QDMA_DECFDW2R); + decfdw3r = qdma_readl(fsl_qdma, status + FSL_QDMA_DECFDW3R); + dev_err(fsl_qdma->dma_dev.dev, + "DMA transaction error! (%x: %x-%x-%x-%x)\n", + intr, decfdw0r, decfdw1r, decfdw2r, decfdw3r); + } qdma_writel(fsl_qdma, FSL_QDMA_DEDR_CLEAR, status + FSL_QDMA_DEDR); return IRQ_HANDLED; -- GitLab From 936fa5cd7b8e3e2a21b434d0f3ac29784ac69791 Mon Sep 17 00:00:00 2001 From: Dejin Zheng Date: Thu, 9 Jul 2020 00:40:13 +0800 Subject: [PATCH 0509/1476] PCI: dwc: Convert to devm_platform_ioremap_resource_byname() Use devm_platform_ioremap_resource_byname() to simplify the code since it contains platform_get_resource_byname() and devm_ioremap_resource() respectively. Link: https://lore.kernel.org/r/20200708164013.5076-1-zhengdejin5@gmail.com Signed-off-by: Dejin Zheng Signed-off-by: Lorenzo Pieralisi Reviewed-by: Gustavo Pimentel Reviewed-by: Rob Herring --- drivers/pci/controller/dwc/pci-dra7xx.c | 11 ++++------- drivers/pci/controller/dwc/pci-keystone.c | 3 +-- drivers/pci/controller/dwc/pcie-artpec6.c | 12 ++++-------- .../pci/controller/dwc/pcie-designware-plat.c | 3 +-- drivers/pci/controller/dwc/pcie-histb.c | 7 ++----- drivers/pci/controller/dwc/pcie-intel-gw.c | 7 ++----- drivers/pci/controller/dwc/pcie-kirin.c | 17 ++++++----------- drivers/pci/controller/dwc/pcie-qcom.c | 6 ++---- drivers/pci/controller/dwc/pcie-uniphier.c | 3 +-- 9 files changed, 23 insertions(+), 46 deletions(-) diff --git a/drivers/pci/controller/dwc/pci-dra7xx.c b/drivers/pci/controller/dwc/pci-dra7xx.c index 6184ebc9392db..e5d0c7ac09b95 100644 --- a/drivers/pci/controller/dwc/pci-dra7xx.c +++ b/drivers/pci/controller/dwc/pci-dra7xx.c @@ -593,13 +593,12 @@ static int __init dra7xx_add_pcie_ep(struct dra7xx_pcie *dra7xx, ep = &pci->ep; ep->ops = &pcie_ep_ops; - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "ep_dbics"); - pci->dbi_base = devm_ioremap_resource(dev, res); + pci->dbi_base = devm_platform_ioremap_resource_byname(pdev, "ep_dbics"); if (IS_ERR(pci->dbi_base)) return PTR_ERR(pci->dbi_base); - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "ep_dbics2"); - pci->dbi_base2 = devm_ioremap_resource(dev, res); + pci->dbi_base2 = + devm_platform_ioremap_resource_byname(pdev, "ep_dbics2"); if (IS_ERR(pci->dbi_base2)) return PTR_ERR(pci->dbi_base2); @@ -626,7 +625,6 @@ static int __init dra7xx_add_pcie_port(struct dra7xx_pcie *dra7xx, struct dw_pcie *pci = dra7xx->pci; struct pcie_port *pp = &pci->pp; struct device *dev = pci->dev; - struct resource *res; pp->irq = platform_get_irq(pdev, 1); if (pp->irq < 0) { @@ -638,8 +636,7 @@ static int __init dra7xx_add_pcie_port(struct dra7xx_pcie *dra7xx, if (ret < 0) return ret; - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "rc_dbics"); - pci->dbi_base = devm_ioremap_resource(dev, res); + pci->dbi_base = devm_platform_ioremap_resource_byname(pdev, "rc_dbics"); if (IS_ERR(pci->dbi_base)) return PTR_ERR(pci->dbi_base); diff --git a/drivers/pci/controller/dwc/pci-keystone.c b/drivers/pci/controller/dwc/pci-keystone.c index 790679fdfa488..00279002102e3 100644 --- a/drivers/pci/controller/dwc/pci-keystone.c +++ b/drivers/pci/controller/dwc/pci-keystone.c @@ -1323,8 +1323,7 @@ static int __init ks_pcie_probe(struct platform_device *pdev) } if (pci->version >= 0x480A) { - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "atu"); - atu_base = devm_ioremap_resource(dev, res); + atu_base = devm_platform_ioremap_resource_byname(pdev, "atu"); if (IS_ERR(atu_base)) { ret = PTR_ERR(atu_base); goto err_get_sync; diff --git a/drivers/pci/controller/dwc/pcie-artpec6.c b/drivers/pci/controller/dwc/pcie-artpec6.c index 28d5a10952000..7d2cfa288b015 100644 --- a/drivers/pci/controller/dwc/pcie-artpec6.c +++ b/drivers/pci/controller/dwc/pcie-artpec6.c @@ -455,8 +455,7 @@ static int artpec6_add_pcie_ep(struct artpec6_pcie *artpec6_pcie, ep = &pci->ep; ep->ops = &pcie_ep_ops; - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dbi2"); - pci->dbi_base2 = devm_ioremap_resource(dev, res); + pci->dbi_base2 = devm_platform_ioremap_resource_byname(pdev, "dbi2"); if (IS_ERR(pci->dbi_base2)) return PTR_ERR(pci->dbi_base2); @@ -481,8 +480,6 @@ static int artpec6_pcie_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; struct dw_pcie *pci; struct artpec6_pcie *artpec6_pcie; - struct resource *dbi_base; - struct resource *phy_base; int ret; const struct of_device_id *match; const struct artpec_pcie_of_data *data; @@ -512,13 +509,12 @@ static int artpec6_pcie_probe(struct platform_device *pdev) artpec6_pcie->variant = variant; artpec6_pcie->mode = mode; - dbi_base = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dbi"); - pci->dbi_base = devm_ioremap_resource(dev, dbi_base); + pci->dbi_base = devm_platform_ioremap_resource_byname(pdev, "dbi"); if (IS_ERR(pci->dbi_base)) return PTR_ERR(pci->dbi_base); - phy_base = platform_get_resource_byname(pdev, IORESOURCE_MEM, "phy"); - artpec6_pcie->phy_base = devm_ioremap_resource(dev, phy_base); + artpec6_pcie->phy_base = + devm_platform_ioremap_resource_byname(pdev, "phy"); if (IS_ERR(artpec6_pcie->phy_base)) return PTR_ERR(artpec6_pcie->phy_base); diff --git a/drivers/pci/controller/dwc/pcie-designware-plat.c b/drivers/pci/controller/dwc/pcie-designware-plat.c index 73646b677affa..712456f6ce36c 100644 --- a/drivers/pci/controller/dwc/pcie-designware-plat.c +++ b/drivers/pci/controller/dwc/pcie-designware-plat.c @@ -153,8 +153,7 @@ static int dw_plat_add_pcie_ep(struct dw_plat_pcie *dw_plat_pcie, ep = &pci->ep; ep->ops = &pcie_ep_ops; - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dbi2"); - pci->dbi_base2 = devm_ioremap_resource(dev, res); + pci->dbi_base2 = devm_platform_ioremap_resource_byname(pdev, "dbi2"); if (IS_ERR(pci->dbi_base2)) return PTR_ERR(pci->dbi_base2); diff --git a/drivers/pci/controller/dwc/pcie-histb.c b/drivers/pci/controller/dwc/pcie-histb.c index 811b5c6d62eae..6d3524c39a9bb 100644 --- a/drivers/pci/controller/dwc/pcie-histb.c +++ b/drivers/pci/controller/dwc/pcie-histb.c @@ -304,7 +304,6 @@ static int histb_pcie_probe(struct platform_device *pdev) struct histb_pcie *hipcie; struct dw_pcie *pci; struct pcie_port *pp; - struct resource *res; struct device_node *np = pdev->dev.of_node; struct device *dev = &pdev->dev; enum of_gpio_flags of_flags; @@ -324,15 +323,13 @@ static int histb_pcie_probe(struct platform_device *pdev) pci->dev = dev; pci->ops = &dw_pcie_ops; - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "control"); - hipcie->ctrl = devm_ioremap_resource(dev, res); + hipcie->ctrl = devm_platform_ioremap_resource_byname(pdev, "control"); if (IS_ERR(hipcie->ctrl)) { dev_err(dev, "cannot get control reg base\n"); return PTR_ERR(hipcie->ctrl); } - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "rc-dbi"); - pci->dbi_base = devm_ioremap_resource(dev, res); + pci->dbi_base = devm_platform_ioremap_resource_byname(pdev, "rc-dbi"); if (IS_ERR(pci->dbi_base)) { dev_err(dev, "cannot get rc-dbi base\n"); return PTR_ERR(pci->dbi_base); diff --git a/drivers/pci/controller/dwc/pcie-intel-gw.c b/drivers/pci/controller/dwc/pcie-intel-gw.c index 2d8dbb318087a..c3b3a1d162b53 100644 --- a/drivers/pci/controller/dwc/pcie-intel-gw.c +++ b/drivers/pci/controller/dwc/pcie-intel-gw.c @@ -253,11 +253,9 @@ static int intel_pcie_get_resources(struct platform_device *pdev) struct intel_pcie_port *lpp = platform_get_drvdata(pdev); struct dw_pcie *pci = &lpp->pci; struct device *dev = pci->dev; - struct resource *res; int ret; - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dbi"); - pci->dbi_base = devm_ioremap_resource(dev, res); + pci->dbi_base = devm_platform_ioremap_resource_byname(pdev, "dbi"); if (IS_ERR(pci->dbi_base)) return PTR_ERR(pci->dbi_base); @@ -291,8 +289,7 @@ static int intel_pcie_get_resources(struct platform_device *pdev) ret = of_pci_get_max_link_speed(dev->of_node); lpp->link_gen = ret < 0 ? 0 : ret; - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "app"); - lpp->app_base = devm_ioremap_resource(dev, res); + lpp->app_base = devm_platform_ioremap_resource_byname(pdev, "app"); if (IS_ERR(lpp->app_base)) return PTR_ERR(lpp->app_base); diff --git a/drivers/pci/controller/dwc/pcie-kirin.c b/drivers/pci/controller/dwc/pcie-kirin.c index c19617a912bd5..e5e765038686b 100644 --- a/drivers/pci/controller/dwc/pcie-kirin.c +++ b/drivers/pci/controller/dwc/pcie-kirin.c @@ -147,23 +147,18 @@ static long kirin_pcie_get_clk(struct kirin_pcie *kirin_pcie, static long kirin_pcie_get_resource(struct kirin_pcie *kirin_pcie, struct platform_device *pdev) { - struct device *dev = &pdev->dev; - struct resource *apb; - struct resource *phy; - struct resource *dbi; - - apb = platform_get_resource_byname(pdev, IORESOURCE_MEM, "apb"); - kirin_pcie->apb_base = devm_ioremap_resource(dev, apb); + kirin_pcie->apb_base = + devm_platform_ioremap_resource_byname(pdev, "apb"); if (IS_ERR(kirin_pcie->apb_base)) return PTR_ERR(kirin_pcie->apb_base); - phy = platform_get_resource_byname(pdev, IORESOURCE_MEM, "phy"); - kirin_pcie->phy_base = devm_ioremap_resource(dev, phy); + kirin_pcie->phy_base = + devm_platform_ioremap_resource_byname(pdev, "phy"); if (IS_ERR(kirin_pcie->phy_base)) return PTR_ERR(kirin_pcie->phy_base); - dbi = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dbi"); - kirin_pcie->pci->dbi_base = devm_ioremap_resource(dev, dbi); + kirin_pcie->pci->dbi_base = + devm_platform_ioremap_resource_byname(pdev, "dbi"); if (IS_ERR(kirin_pcie->pci->dbi_base)) return PTR_ERR(kirin_pcie->pci->dbi_base); diff --git a/drivers/pci/controller/dwc/pcie-qcom.c b/drivers/pci/controller/dwc/pcie-qcom.c index 73d4eed26f655..b79f7878c9937 100644 --- a/drivers/pci/controller/dwc/pcie-qcom.c +++ b/drivers/pci/controller/dwc/pcie-qcom.c @@ -1405,8 +1405,7 @@ static int qcom_pcie_probe(struct platform_device *pdev) if (pcie->gen < 0) pcie->gen = 2; - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "parf"); - pcie->parf = devm_ioremap_resource(dev, res); + pcie->parf = devm_platform_ioremap_resource_byname(pdev, "parf"); if (IS_ERR(pcie->parf)) { ret = PTR_ERR(pcie->parf); goto err_pm_runtime_put; @@ -1419,8 +1418,7 @@ static int qcom_pcie_probe(struct platform_device *pdev) goto err_pm_runtime_put; } - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "elbi"); - pcie->elbi = devm_ioremap_resource(dev, res); + pcie->elbi = devm_platform_ioremap_resource_byname(pdev, "elbi"); if (IS_ERR(pcie->elbi)) { ret = PTR_ERR(pcie->elbi); goto err_pm_runtime_put; diff --git a/drivers/pci/controller/dwc/pcie-uniphier.c b/drivers/pci/controller/dwc/pcie-uniphier.c index a5401a0b1e587..3a7f403b57b8d 100644 --- a/drivers/pci/controller/dwc/pcie-uniphier.c +++ b/drivers/pci/controller/dwc/pcie-uniphier.c @@ -416,8 +416,7 @@ static int uniphier_pcie_probe(struct platform_device *pdev) if (IS_ERR(priv->pci.dbi_base)) return PTR_ERR(priv->pci.dbi_base); - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "link"); - priv->base = devm_ioremap_resource(dev, res); + priv->base = devm_platform_ioremap_resource_byname(pdev, "link"); if (IS_ERR(priv->base)) return PTR_ERR(priv->base); -- GitLab From 4750a171c3290f9bbebca16c6372db723a4cfa3b Mon Sep 17 00:00:00 2001 From: Eric Sandeen Date: Wed, 15 Jul 2020 08:30:37 -0700 Subject: [PATCH 0510/1476] xfs: preserve inode versioning across remounts The MS_I_VERSION mount flag is exposed via the VFS, as documented in the mount manpages etc; see the iversion and noiversion mount options in mount(8). As a result, mount -o remount looks for this option in /proc/mounts and will only send the I_VERSION flag back in during remount it it is present. Since it's not there, a remount will /remove/ the I_VERSION flag at the vfs level, and iversion functionality is lost. xfs v5 superblocks intend to always have i_version enabled; it is set as a default at mount time, but is lost during remount for the reasons above. The generic fix would be to expose this documented option in /proc/mounts, but since that was rejected, fix it up again in the xfs remount path instead, so that at least xfs won't suffer from this misbehavior. Signed-off-by: Eric Sandeen Reviewed-by: Dave Chinner Reviewed-by: Christoph Hellwig Signed-off-by: Darrick J. Wong Reviewed-by: Darrick J. Wong --- fs/xfs/xfs_super.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/fs/xfs/xfs_super.c b/fs/xfs/xfs_super.c index 5ef5d8416f706..71ac6c1cdc362 100644 --- a/fs/xfs/xfs_super.c +++ b/fs/xfs/xfs_super.c @@ -1721,6 +1721,10 @@ xfs_fc_reconfigure( int flags = fc->sb_flags; int error; + /* version 5 superblocks always support version counters. */ + if (XFS_SB_VERSION_NUM(&mp->m_sb) == XFS_SB_VERSION_5) + fc->sb_flags |= SB_I_VERSION; + error = xfs_fc_validate_params(new_mp); if (error) return error; -- GitLab From 3167e3d340c092fd47924bc4d23117a3074ef9a9 Mon Sep 17 00:00:00 2001 From: Xiongfeng Wang Date: Fri, 17 Jul 2020 15:59:25 +0800 Subject: [PATCH 0511/1476] PCI/ASPM: Add missing newline in sysfs 'policy' When I cat ASPM parameter 'policy' by sysfs, it displays as follows. Add a newline for easy reading. Other sysfs attributes already include a newline. [root@localhost ~]# cat /sys/module/pcie_aspm/parameters/policy [default] performance powersave powersupersave [root@localhost ~]# Fixes: 7d715a6c1ae5 ("PCI: add PCI Express ASPM support") Link: https://lore.kernel.org/r/1594972765-10404-1-git-send-email-wangxiongfeng2@huawei.com Signed-off-by: Xiongfeng Wang Signed-off-by: Bjorn Helgaas --- drivers/pci/pcie/aspm.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/pci/pcie/aspm.c b/drivers/pci/pcie/aspm.c index b17e5ffd31b14..253c30cc19678 100644 --- a/drivers/pci/pcie/aspm.c +++ b/drivers/pci/pcie/aspm.c @@ -1182,6 +1182,7 @@ static int pcie_aspm_get_policy(char *buffer, const struct kernel_param *kp) cnt += sprintf(buffer + cnt, "[%s] ", policy_str[i]); else cnt += sprintf(buffer + cnt, "%s ", policy_str[i]); + cnt += sprintf(buffer + cnt, "\n"); return cnt; } -- GitLab From 2be90e914c12bdea70b0bd297d5715ee66eb46d0 Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Fri, 17 Jul 2020 19:51:18 -0700 Subject: [PATCH 0512/1476] dmaengine: linux/dmaengine.h: drop duplicated word in a comment Drop the doubled word "has" in a comment. Signed-off-by: Randy Dunlap Cc: Vinod Koul Cc: dmaengine@vger.kernel.org Link: https://lore.kernel.org/r/06e64046-ebf1-15db-dbaf-73698de3b493@infradead.org Signed-off-by: Vinod Koul --- include/linux/dmaengine.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/linux/dmaengine.h b/include/linux/dmaengine.h index 9f9a13a2c01f8..883e1e087de5d 100644 --- a/include/linux/dmaengine.h +++ b/include/linux/dmaengine.h @@ -164,7 +164,7 @@ struct dma_interleaved_template { * @DMA_PREP_INTERRUPT - trigger an interrupt (callback) upon completion of * this transaction * @DMA_CTRL_ACK - if clear, the descriptor cannot be reused until the client - * acknowledges receipt, i.e. has has a chance to establish any dependency + * acknowledges receipt, i.e. has a chance to establish any dependency * chains * @DMA_PREP_PQ_DISABLE_P - prevent generation of P while generating Q * @DMA_PREP_PQ_DISABLE_Q - prevent generation of Q while generating P -- GitLab From c19c7a13ee9d18bc6a43b417b8daa2edb825d601 Mon Sep 17 00:00:00 2001 From: Dmitry Osipenko Date: Wed, 24 Jun 2020 17:08:45 +0200 Subject: [PATCH 0513/1476] media: staging: tegra-vde: Runtime PM is always available on Tegra Runtime PM is always available on Tegra nowadays since commit 40b2bb1b132a ("ARM: tegra: enforce PM requirement"), hence the case of unavailable RPM doesn't need to be handled. Signed-off-by: Dmitry Osipenko Acked-by: Thierry Reding Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/tegra-vde/vde.c | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/drivers/staging/media/tegra-vde/vde.c b/drivers/staging/media/tegra-vde/vde.c index 3fdf2cd0b99e4..9dd9f0c4cae39 100644 --- a/drivers/staging/media/tegra-vde/vde.c +++ b/drivers/staging/media/tegra-vde/vde.c @@ -1064,17 +1064,8 @@ static int tegra_vde_probe(struct platform_device *pdev) pm_runtime_use_autosuspend(dev); pm_runtime_set_autosuspend_delay(dev, 300); - if (!pm_runtime_enabled(dev)) { - err = tegra_vde_runtime_resume(dev); - if (err) - goto err_misc_unreg; - } - return 0; -err_misc_unreg: - misc_deregister(&vde->miscdev); - err_deinit_iommu: tegra_vde_iommu_deinit(vde); @@ -1089,13 +1080,6 @@ static int tegra_vde_remove(struct platform_device *pdev) { struct tegra_vde *vde = platform_get_drvdata(pdev); struct device *dev = &pdev->dev; - int err; - - if (!pm_runtime_enabled(dev)) { - err = tegra_vde_runtime_suspend(dev); - if (err) - return err; - } pm_runtime_dont_use_autosuspend(dev); pm_runtime_disable(dev); -- GitLab From 8f0c714ad9be1ef774c98e8819a7a571451cb019 Mon Sep 17 00:00:00 2001 From: Dmitry Osipenko Date: Wed, 24 Jun 2020 17:08:46 +0200 Subject: [PATCH 0514/1476] media: staging: tegra-vde: Turn ON power domain on shutdown On some devices bootloader isn't ready to a clamped VDE power, and thus, machine hangs on a warm reboot (CPU reset). The VDE power partition is turned ON by default on a cold boot, hence VDE driver should keep power partition enabled on system's reboot too. This fixes hang on a warm reboot on a Tegra20 Acer A500 device, which is handy if Embedded Controller driver is unavailable, i.e. cold reboot can't be performed. Signed-off-by: Dmitry Osipenko Acked-by: Thierry Reding Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/tegra-vde/vde.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/drivers/staging/media/tegra-vde/vde.c b/drivers/staging/media/tegra-vde/vde.c index 9dd9f0c4cae39..3dcf5eab6b182 100644 --- a/drivers/staging/media/tegra-vde/vde.c +++ b/drivers/staging/media/tegra-vde/vde.c @@ -1081,9 +1081,17 @@ static int tegra_vde_remove(struct platform_device *pdev) struct tegra_vde *vde = platform_get_drvdata(pdev); struct device *dev = &pdev->dev; + pm_runtime_get_sync(dev); pm_runtime_dont_use_autosuspend(dev); pm_runtime_disable(dev); + /* + * Balance RPM state, the VDE power domain is left ON and hardware + * is clock-gated. It's safe to reboot machine now. + */ + pm_runtime_put_noidle(dev); + clk_disable_unprepare(vde->clk); + misc_deregister(&vde->miscdev); tegra_vde_dmabuf_cache_unmap_all(vde); @@ -1095,6 +1103,16 @@ static int tegra_vde_remove(struct platform_device *pdev) return 0; } +static void tegra_vde_shutdown(struct platform_device *pdev) +{ + /* + * On some devices bootloader isn't ready to a power-gated VDE on + * a warm-reboot, machine will hang in that case. + */ + if (pm_runtime_status_suspended(&pdev->dev)) + tegra_vde_runtime_resume(&pdev->dev); +} + static __maybe_unused int tegra_vde_pm_suspend(struct device *dev) { struct tegra_vde *vde = dev_get_drvdata(dev); @@ -1140,6 +1158,7 @@ MODULE_DEVICE_TABLE(of, tegra_vde_of_match); static struct platform_driver tegra_vde_driver = { .probe = tegra_vde_probe, .remove = tegra_vde_remove, + .shutdown = tegra_vde_shutdown, .driver = { .name = "tegra-vde", .of_match_table = tegra_vde_of_match, -- GitLab From ebeacb1f612db22020561211c21b4d966cf7ca24 Mon Sep 17 00:00:00 2001 From: Dmitry Osipenko Date: Wed, 24 Jun 2020 17:08:47 +0200 Subject: [PATCH 0515/1476] media: staging: tegra-vde: Power-cycle hardware on probe VDE partition is left turned ON after bootloader on most devices, hence let's ensure that it's turned OFF in order to lower power leakage while hardware is idling by turning it ON and OFF during of the driver's probe. Signed-off-by: Dmitry Osipenko Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/tegra-vde/vde.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/staging/media/tegra-vde/vde.c b/drivers/staging/media/tegra-vde/vde.c index 3dcf5eab6b182..a3c24d96d5b9a 100644 --- a/drivers/staging/media/tegra-vde/vde.c +++ b/drivers/staging/media/tegra-vde/vde.c @@ -1064,6 +1064,14 @@ static int tegra_vde_probe(struct platform_device *pdev) pm_runtime_use_autosuspend(dev); pm_runtime_set_autosuspend_delay(dev, 300); + /* + * VDE partition may be left ON after bootloader, hence let's + * power-cycle it in order to put hardware into a predictable lower + * power state. + */ + pm_runtime_get_sync(dev); + pm_runtime_put(dev); + return 0; err_deinit_iommu: -- GitLab From 137641287eb40260783a4413847a0aef06023a6c Mon Sep 17 00:00:00 2001 From: Oliver Neukum Date: Tue, 5 May 2020 12:50:33 +0200 Subject: [PATCH 0516/1476] go7007: add sanity checking for endpoints A malicious USB device may lack endpoints the driver assumes to exist Accessing them leads to NULL pointer accesses. This patch introduces sanity checking. Reported-and-tested-by: syzbot+cabfa4b5b05ff6be4ef0@syzkaller.appspotmail.com Signed-off-by: Oliver Neukum Fixes: 866b8695d67e8 ("Staging: add the go7007 video driver") Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/go7007/go7007-usb.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/drivers/media/usb/go7007/go7007-usb.c b/drivers/media/usb/go7007/go7007-usb.c index f889c9d740cd1..dbf0455d5d50d 100644 --- a/drivers/media/usb/go7007/go7007-usb.c +++ b/drivers/media/usb/go7007/go7007-usb.c @@ -1132,6 +1132,10 @@ static int go7007_usb_probe(struct usb_interface *intf, go->hpi_ops = &go7007_usb_onboard_hpi_ops; go->hpi_context = usb; + ep = usb->usbdev->ep_in[4]; + if (!ep) + return -ENODEV; + /* Allocate the URB and buffer for receiving incoming interrupts */ usb->intr_urb = usb_alloc_urb(0, GFP_KERNEL); if (usb->intr_urb == NULL) @@ -1141,7 +1145,6 @@ static int go7007_usb_probe(struct usb_interface *intf, if (usb->intr_urb->transfer_buffer == NULL) goto allocfail; - ep = usb->usbdev->ep_in[4]; if (usb_endpoint_type(&ep->desc) == USB_ENDPOINT_XFER_BULK) usb_fill_bulk_urb(usb->intr_urb, usb->usbdev, usb_rcvbulkpipe(usb->usbdev, 4), @@ -1263,9 +1266,13 @@ static int go7007_usb_probe(struct usb_interface *intf, /* Allocate the URBs and buffers for receiving the video stream */ if (board->flags & GO7007_USB_EZUSB) { + if (!usb->usbdev->ep_in[6]) + goto allocfail; v_urb_len = 1024; video_pipe = usb_rcvbulkpipe(usb->usbdev, 6); } else { + if (!usb->usbdev->ep_in[1]) + goto allocfail; v_urb_len = 512; video_pipe = usb_rcvbulkpipe(usb->usbdev, 1); } @@ -1285,6 +1292,8 @@ static int go7007_usb_probe(struct usb_interface *intf, /* Allocate the URBs and buffers for receiving the audio stream */ if ((board->flags & GO7007_USB_EZUSB) && (board->main_info.flags & GO7007_BOARD_HAS_AUDIO)) { + if (!usb->usbdev->ep_in[8]) + goto allocfail; for (i = 0; i < 8; ++i) { usb->audio_urbs[i] = usb_alloc_urb(0, GFP_KERNEL); if (usb->audio_urbs[i] == NULL) -- GitLab From 6c42227c3467549ddc65efe99c869021d2f4a570 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 26 Jun 2020 12:44:26 +0200 Subject: [PATCH 0517/1476] cec-api: prevent leaking memory through hole in structure Fix this smatch warning: drivers/media/cec/core/cec-api.c:156 cec_adap_g_log_addrs() warn: check that 'log_addrs' doesn't leak information (struct has a hole after 'features') Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/cec/core/cec-api.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/drivers/media/cec/core/cec-api.c b/drivers/media/cec/core/cec-api.c index 17d1cb2e5f976..f922a2196b2b7 100644 --- a/drivers/media/cec/core/cec-api.c +++ b/drivers/media/cec/core/cec-api.c @@ -147,7 +147,13 @@ static long cec_adap_g_log_addrs(struct cec_adapter *adap, struct cec_log_addrs log_addrs; mutex_lock(&adap->lock); - log_addrs = adap->log_addrs; + /* + * We use memcpy here instead of assignment since there is a + * hole at the end of struct cec_log_addrs that an assignment + * might ignore. So when we do copy_to_user() we could leak + * one byte of memory. + */ + memcpy(&log_addrs, &adap->log_addrs, sizeof(log_addrs)); if (!adap->is_configured) memset(log_addrs.log_addr, CEC_LOG_ADDR_INVALID, sizeof(log_addrs.log_addr)); -- GitLab From e8bf419e0ab2d7401dccf737cd944191164a6028 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Tue, 30 Jun 2020 12:28:28 +0200 Subject: [PATCH 0518/1476] include/media/v4l2-subdev.h: fix typo Typo in comment block: v4l2_subdev_get_try_crop -> v4l2_subdev_get_try_compose Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- include/media/v4l2-subdev.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/media/v4l2-subdev.h b/include/media/v4l2-subdev.h index f7fe78a6f65a9..2607ea85096ae 100644 --- a/include/media/v4l2-subdev.h +++ b/include/media/v4l2-subdev.h @@ -959,7 +959,7 @@ static inline struct v4l2_rect } /** - * v4l2_subdev_get_try_crop - ancillary routine to call + * v4l2_subdev_get_try_compose - ancillary routine to call * &struct v4l2_subdev_pad_config->try_compose * * @sd: pointer to &struct v4l2_subdev -- GitLab From a69a7a33268308ddcc9abf0f7d7cd61ec4300cbe Mon Sep 17 00:00:00 2001 From: Ezequiel Garcia Date: Wed, 1 Jul 2020 15:17:30 +0200 Subject: [PATCH 0519/1476] v4l2-core: Print control name in VIDIOC_S/G_(EXT)_CTRL(S) While debugging, it's currently really hard to identify controls by their ID. Print the control name making the print more helpful. With this change, the print changes from: video1: VIDIOC_S_EXT_CTRLS: which=0xf010000, count=5, error_idx=4, request_fd=45, id/size=0x990ce8/1048, id/size=0x990ce9/12, id/size=0x990cea/480, id/size=0x990ceb/896, id/size=0x990cec/400 video1: VIDIOC_S_EXT_CTRLS: which=0xf010000, count=5, error_idx=4, request_fd=42, name=H264 Sequence Parameter Set, id/size=0x990ce8/1048, name=H264 Picture Parameter Set, id/size=0x990ce9/12, name=H264 Scaling Matrix, id/size=0x990cea/480, name=H264 Slice Parameters, id/size=0x990ceb/896, name=H264 Decode Parameters, id/size=0x990cec/400 For instance, this is specially helpful when the ioctl fails. Consider the following example: v4l2-ctrls: prepare_ext_ctrls: video1: pointer control id 0x990cec size too small, 400 bytes but 784 bytes needed v4l2-ctrls: try_set_ext_ctrls: video1: video1: try_set_ext_ctrls_common failed (-14) video1: VIDIOC_S_EXT_CTRLS: error -14: which=0xf010000, count=5, error_idx=5, request_fd=39, name=H264 Sequence Parameter Set, id/size=0x990ce8/1048, name=H264 Picture Parameter Set, id/size=0x990ce9/12, name=H264 Scaling Matrix, id/size=0x990cea/480, name=H264 Slice Parameters, id/size=0x990ceb/896, name=H264 Decode Parameters, id/size=0x990cec/400 Signed-off-by: Ezequiel Garcia Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-ioctl.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/drivers/media/v4l2-core/v4l2-ioctl.c b/drivers/media/v4l2-core/v4l2-ioctl.c index 02bfef0da76da..a556880f225a5 100644 --- a/drivers/media/v4l2-core/v4l2-ioctl.c +++ b/drivers/media/v4l2-core/v4l2-ioctl.c @@ -582,7 +582,10 @@ static void v4l_print_querymenu(const void *arg, bool write_only) static void v4l_print_control(const void *arg, bool write_only) { const struct v4l2_control *p = arg; + const char *name = v4l2_ctrl_get_name(p->id); + if (name) + pr_cont("name=%s, ", name); pr_cont("id=0x%x, value=%d\n", p->id, p->value); } @@ -594,12 +597,15 @@ static void v4l_print_ext_controls(const void *arg, bool write_only) pr_cont("which=0x%x, count=%d, error_idx=%d, request_fd=%d", p->which, p->count, p->error_idx, p->request_fd); for (i = 0; i < p->count; i++) { + unsigned int id = p->controls[i].id; + const char *name = v4l2_ctrl_get_name(id); + + if (name) + pr_cont(", name=%s", name); if (!p->controls[i].size) - pr_cont(", id/val=0x%x/0x%x", - p->controls[i].id, p->controls[i].value); + pr_cont(", id/val=0x%x/0x%x", id, p->controls[i].value); else - pr_cont(", id/size=0x%x/%u", - p->controls[i].id, p->controls[i].size); + pr_cont(", id/size=0x%x/%u", id, p->controls[i].size); } pr_cont("\n"); } -- GitLab From 77d7ceb1a14616af8877c77674c8a17ed45fce85 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Wed, 1 Jul 2020 15:56:52 +0200 Subject: [PATCH 0520/1476] media: gspca: remove redundant initialization of variable status The variable status is being initialized with a value that is never read and it is being updated later with a new value. The initialization is redundant and can be removed. Addresses-Coverity: ("Unused value") Signed-off-by: Colin Ian King Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/gspca/sn9c2028.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/usb/gspca/sn9c2028.c b/drivers/media/usb/gspca/sn9c2028.c index aff01b7538531..dbd1d6da37f1a 100644 --- a/drivers/media/usb/gspca/sn9c2028.c +++ b/drivers/media/usb/gspca/sn9c2028.c @@ -215,7 +215,7 @@ static int sd_config(struct gspca_dev *gspca_dev, /* this function is called at probe and resume time */ static int sd_init(struct gspca_dev *gspca_dev) { - int status = -1; + int status; sn9c2028_read1(gspca_dev); sn9c2028_read1(gspca_dev); -- GitLab From de26c90fc40d7d32a6f3f71babad2547ab3b7abb Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Tue, 7 Jul 2020 15:10:15 +0200 Subject: [PATCH 0521/1476] sunxi-csi: fill in bus_info of media device This fixes a v4l2-compliance warning: Required ioctls: warn: v4l2-test-media.cpp(52): empty bus_info test MEDIA_IOC_DEVICE_INFO: OK Signed-off-by: Hans Verkuil Acked-by: Maxime Ripard Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/sunxi/sun4i-csi/sun4i_csi.c | 2 ++ drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.c | 2 ++ 2 files changed, 4 insertions(+) diff --git a/drivers/media/platform/sunxi/sun4i-csi/sun4i_csi.c b/drivers/media/platform/sunxi/sun4i-csi/sun4i_csi.c index eff34ded63055..5319eb1ab3092 100644 --- a/drivers/media/platform/sunxi/sun4i-csi/sun4i_csi.c +++ b/drivers/media/platform/sunxi/sun4i-csi/sun4i_csi.c @@ -191,6 +191,8 @@ static int sun4i_csi_probe(struct platform_device *pdev) strscpy(csi->mdev.model, "Allwinner Video Capture Device", sizeof(csi->mdev.model)); csi->mdev.hw_revision = 0; + snprintf(csi->mdev.bus_info, sizeof(csi->mdev.bus_info), "platform:%s", + dev_name(csi->dev)); media_device_init(&csi->mdev); csi->v4l.mdev = &csi->mdev; diff --git a/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.c b/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.c index 055eb0b8e3969..28e89340fed98 100644 --- a/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.c +++ b/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.c @@ -733,6 +733,8 @@ static int sun6i_csi_v4l2_init(struct sun6i_csi *csi) strscpy(csi->media_dev.model, "Allwinner Video Capture Device", sizeof(csi->media_dev.model)); csi->media_dev.hw_revision = 0; + snprintf(csi->media_dev.bus_info, sizeof(csi->media_dev.bus_info), + "platform:%s", dev_name(csi->dev)); media_device_init(&csi->media_dev); v4l2_async_notifier_init(&csi->notifier); -- GitLab From 71ecc5356d227072c7c31e44f6774d461fd4865f Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Tue, 7 Jul 2020 15:32:59 +0200 Subject: [PATCH 0522/1476] sun4i-csi: drop read/write, enable VB2_DMABUF For no obvious reason VB2_DMABUF was not enabled. Add this. Also both read and write file ops were set, but this was not enabled in the vb2 io_modes or in the device_caps capabilities. Drop this. Signed-off-by: Hans Verkuil Acked-by: Maxime Ripard Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/sunxi/sun4i-csi/sun4i_dma.c | 2 +- drivers/media/platform/sunxi/sun4i-csi/sun4i_v4l2.c | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/media/platform/sunxi/sun4i-csi/sun4i_dma.c b/drivers/media/platform/sunxi/sun4i-csi/sun4i_dma.c index 78fa1c535ac64..3278746246aa4 100644 --- a/drivers/media/platform/sunxi/sun4i-csi/sun4i_dma.c +++ b/drivers/media/platform/sunxi/sun4i-csi/sun4i_dma.c @@ -413,7 +413,7 @@ int sun4i_csi_dma_register(struct sun4i_csi *csi, int irq) q->min_buffers_needed = 3; q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; - q->io_modes = VB2_MMAP; + q->io_modes = VB2_MMAP | VB2_DMABUF; q->lock = &csi->lock; q->drv_priv = csi; q->buf_struct_size = sizeof(struct sun4i_csi_buffer); diff --git a/drivers/media/platform/sunxi/sun4i-csi/sun4i_v4l2.c b/drivers/media/platform/sunxi/sun4i-csi/sun4i_v4l2.c index 1721e5aee9c68..06bf0d1709049 100644 --- a/drivers/media/platform/sunxi/sun4i-csi/sun4i_v4l2.c +++ b/drivers/media/platform/sunxi/sun4i-csi/sun4i_v4l2.c @@ -256,8 +256,6 @@ static const struct v4l2_file_operations sun4i_csi_fops = { .open = sun4i_csi_open, .release = sun4i_csi_release, .unlocked_ioctl = video_ioctl2, - .read = vb2_fop_read, - .write = vb2_fop_write, .poll = vb2_fop_poll, .mmap = vb2_fop_mmap, }; -- GitLab From 503ebad4b5d7883ab8476d95f0e2aa77bc56f91e Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Tue, 7 Jul 2020 15:36:47 +0200 Subject: [PATCH 0523/1476] sun4i-csi: call _vb2_fop_release instead of v4l2_fh_release This driver didn't call _vb2_fop_release in the sun4i_csi_release function. Without that call the vb2 queue is not properly canceled and buffers may not be freed. Signed-off-by: Hans Verkuil Acked-by: Maxime Ripard Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/sunxi/sun4i-csi/sun4i_v4l2.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/media/platform/sunxi/sun4i-csi/sun4i_v4l2.c b/drivers/media/platform/sunxi/sun4i-csi/sun4i_v4l2.c index 06bf0d1709049..8f4e254b6a41f 100644 --- a/drivers/media/platform/sunxi/sun4i-csi/sun4i_v4l2.c +++ b/drivers/media/platform/sunxi/sun4i-csi/sun4i_v4l2.c @@ -242,7 +242,8 @@ static int sun4i_csi_release(struct file *file) mutex_lock(&csi->lock); - v4l2_fh_release(file); + _vb2_fop_release(file, NULL); + v4l2_pipeline_pm_put(&csi->vdev.entity); pm_runtime_put(csi->dev); -- GitLab From b4cc96f9803a953d2688a77f5f3035a6693b575e Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Tue, 7 Jul 2020 21:17:44 +0200 Subject: [PATCH 0524/1476] Documentation: userspace-api/media: drop doubled words Drop duplicated words in Documentation/userspace-api/media/. This addresses the words "struct" and "value". Signed-off-by: Randy Dunlap Cc: Jonathan Corbet Signed-off-by: Hans Verkuil [hverkuil-cisco@xs4all.nl: dev-sdr.rst: there is two -> there are two] Signed-off-by: Mauro Carvalho Chehab --- Documentation/userspace-api/media/dvb/fe-get-info.rst | 3 +-- Documentation/userspace-api/media/v4l/buffer.rst | 4 ++-- Documentation/userspace-api/media/v4l/dev-osd.rst | 2 +- Documentation/userspace-api/media/v4l/dev-sdr.rst | 2 +- Documentation/userspace-api/media/v4l/hist-v4l2.rst | 4 ++-- Documentation/userspace-api/media/v4l/vidioc-dqevent.rst | 2 +- 6 files changed, 8 insertions(+), 9 deletions(-) diff --git a/Documentation/userspace-api/media/dvb/fe-get-info.rst b/Documentation/userspace-api/media/dvb/fe-get-info.rst index 80d9f8195ac45..6b3ffd301142e 100644 --- a/Documentation/userspace-api/media/dvb/fe-get-info.rst +++ b/Documentation/userspace-api/media/dvb/fe-get-info.rst @@ -34,8 +34,7 @@ Arguments File descriptor returned by :ref:`open() `. ``argp`` - pointer to struct struct - :c:type:`dvb_frontend_info` + pointer to struct :c:type:`dvb_frontend_info` Description diff --git a/Documentation/userspace-api/media/v4l/buffer.rst b/Documentation/userspace-api/media/v4l/buffer.rst index 5088393b5a5cf..57e752aaf414a 100644 --- a/Documentation/userspace-api/media/v4l/buffer.rst +++ b/Documentation/userspace-api/media/v4l/buffer.rst @@ -23,8 +23,8 @@ argument to the :ref:`VIDIOC_QUERYBUF`, :ref:`VIDIOC_QBUF ` and :ref:`VIDIOC_DQBUF ` ioctl. In the multi-planar API, some plane-specific members of struct :c:type:`v4l2_buffer`, -such as pointers and sizes for each plane, are stored in struct -struct :c:type:`v4l2_plane` instead. In that case, struct +such as pointers and sizes for each plane, are stored in +struct :c:type:`v4l2_plane` instead. In that case, struct :c:type:`v4l2_buffer` contains an array of plane structures. Dequeued video buffers come with timestamps. The driver decides at which diff --git a/Documentation/userspace-api/media/v4l/dev-osd.rst b/Documentation/userspace-api/media/v4l/dev-osd.rst index 67dc46373a91e..ad0c156c78981 100644 --- a/Documentation/userspace-api/media/v4l/dev-osd.rst +++ b/Documentation/userspace-api/media/v4l/dev-osd.rst @@ -51,7 +51,7 @@ other information, the physical address of the framebuffer in the ``base`` field of struct :c:type:`v4l2_framebuffer`. The framebuffer device ioctl ``FBIOGET_FSCREENINFO`` returns the same address in the ``smem_start`` field of struct -struct :c:type:`fb_fix_screeninfo`. The ``FBIOGET_FSCREENINFO`` +:c:type:`fb_fix_screeninfo`. The ``FBIOGET_FSCREENINFO`` ioctl and struct :c:type:`fb_fix_screeninfo` are defined in the ``linux/fb.h`` header file. diff --git a/Documentation/userspace-api/media/v4l/dev-sdr.rst b/Documentation/userspace-api/media/v4l/dev-sdr.rst index c9563bca444e0..4a80319a53c65 100644 --- a/Documentation/userspace-api/media/v4l/dev-sdr.rst +++ b/Documentation/userspace-api/media/v4l/dev-sdr.rst @@ -78,7 +78,7 @@ field of a struct :c:type:`v4l2_format` to ``V4L2_BUF_TYPE_SDR_CAPTURE`` or ``V4L2_BUF_TYPE_SDR_OUTPUT`` and use the struct :c:type:`v4l2_sdr_format` ``sdr`` member of the ``fmt`` union as needed per the desired operation. Currently -there is two fields, ``pixelformat`` and ``buffersize``, of struct +there are two fields, ``pixelformat`` and ``buffersize``, of struct :c:type:`v4l2_sdr_format` which are used. Content of the ``pixelformat`` is V4L2 FourCC code of the data format. The ``buffersize`` field is maximum buffer size in bytes required for diff --git a/Documentation/userspace-api/media/v4l/hist-v4l2.rst b/Documentation/userspace-api/media/v4l/hist-v4l2.rst index 7913d017cd337..6dcfe6046e336 100644 --- a/Documentation/userspace-api/media/v4l/hist-v4l2.rst +++ b/Documentation/userspace-api/media/v4l/hist-v4l2.rst @@ -43,7 +43,7 @@ transmission arguments. 1998-09-28: Revamped video standard. Made video controls individually enumerable. -1998-10-02: The ``id`` field was removed from struct +1998-10-02: The ``id`` field was removed from struct ``video_standard`` and the color subcarrier fields were renamed. The :ref:`VIDIOC_QUERYSTD` ioctl was renamed to :ref:`VIDIOC_ENUMSTD`, @@ -260,7 +260,7 @@ multiple tuners into account.) 2000-09-18: ``V4L2_BUF_TYPE_VBI`` was added. This may *break compatibility* as the :ref:`VIDIOC_G_FMT ` and -:ref:`VIDIOC_S_FMT ` ioctls may fail now if the struct +:ref:`VIDIOC_S_FMT ` ioctls may fail now if the struct ``v4l2_fmt`` ``type`` field does not contain ``V4L2_BUF_TYPE_VBI``. In the documentation of the struct :c:type:`v4l2_vbi_format` ``offset`` field the diff --git a/Documentation/userspace-api/media/v4l/vidioc-dqevent.rst b/Documentation/userspace-api/media/v4l/vidioc-dqevent.rst index a9a176d5256db..9412be0c37478 100644 --- a/Documentation/userspace-api/media/v4l/vidioc-dqevent.rst +++ b/Documentation/userspace-api/media/v4l/vidioc-dqevent.rst @@ -260,7 +260,7 @@ call. :ref:`v4l2_queryctrl `. * - __s32 - ``default_value`` - - The default value value of the control. See struct + - The default value of the control. See struct :ref:`v4l2_queryctrl `. -- GitLab From cf293a4f0e1a94c9a902d54879b189d459113c63 Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Wed, 8 Jul 2020 08:24:20 +0200 Subject: [PATCH 0525/1476] media: solo6x10: replace http references with https Replace HTTP links with HTTPS ones: SOFTLOGIC 6x10 MPEG CODEC Rationale: Reduces attack surface on kernel devs opening the links for MITM as HTTPS traffic is much harder to manipulate. Deterministic algorithm: For each file: If not .svg: For each line: If doesn't contain `\bxmlns\b`: If neither `\bgnu\.org/license`, nor `\bmozilla\.org/MPL\b`: If both the HTTP and HTTPS versions return 200 OK and serve the same content: Replace HTTP with HTTPS. Signed-off-by: Alexander A. Klimov Signed-off-by: Ismael Luceno Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/pci/solo6x10/solo6x10-core.c | 2 +- drivers/media/pci/solo6x10/solo6x10-disp.c | 2 +- drivers/media/pci/solo6x10/solo6x10-eeprom.c | 2 +- drivers/media/pci/solo6x10/solo6x10-enc.c | 2 +- drivers/media/pci/solo6x10/solo6x10-g723.c | 2 +- drivers/media/pci/solo6x10/solo6x10-gpio.c | 2 +- drivers/media/pci/solo6x10/solo6x10-i2c.c | 2 +- drivers/media/pci/solo6x10/solo6x10-jpeg.h | 2 +- drivers/media/pci/solo6x10/solo6x10-offsets.h | 2 +- drivers/media/pci/solo6x10/solo6x10-p2m.c | 2 +- drivers/media/pci/solo6x10/solo6x10-regs.h | 2 +- drivers/media/pci/solo6x10/solo6x10-tw28.c | 2 +- drivers/media/pci/solo6x10/solo6x10-tw28.h | 2 +- drivers/media/pci/solo6x10/solo6x10-v4l2-enc.c | 2 +- drivers/media/pci/solo6x10/solo6x10-v4l2.c | 2 +- drivers/media/pci/solo6x10/solo6x10.h | 2 +- 16 files changed, 16 insertions(+), 16 deletions(-) diff --git a/drivers/media/pci/solo6x10/solo6x10-core.c b/drivers/media/pci/solo6x10/solo6x10-core.c index 6e1ba4846ea47..c6e0090f27e81 100644 --- a/drivers/media/pci/solo6x10/solo6x10-core.c +++ b/drivers/media/pci/solo6x10/solo6x10-core.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* - * Copyright (C) 2010-2013 Bluecherry, LLC + * Copyright (C) 2010-2013 Bluecherry, LLC * * Original author: * Ben Collins diff --git a/drivers/media/pci/solo6x10/solo6x10-disp.c b/drivers/media/pci/solo6x10/solo6x10-disp.c index 4e28bf927de53..ad98ca7fb98b5 100644 --- a/drivers/media/pci/solo6x10/solo6x10-disp.c +++ b/drivers/media/pci/solo6x10/solo6x10-disp.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* - * Copyright (C) 2010-2013 Bluecherry, LLC + * Copyright (C) 2010-2013 Bluecherry, LLC * * Original author: * Ben Collins diff --git a/drivers/media/pci/solo6x10/solo6x10-eeprom.c b/drivers/media/pci/solo6x10/solo6x10-eeprom.c index 9aba64395a6bb..0d864b8ca0ab0 100644 --- a/drivers/media/pci/solo6x10/solo6x10-eeprom.c +++ b/drivers/media/pci/solo6x10/solo6x10-eeprom.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* - * Copyright (C) 2010-2013 Bluecherry, LLC + * Copyright (C) 2010-2013 Bluecherry, LLC * * Original author: * Ben Collins diff --git a/drivers/media/pci/solo6x10/solo6x10-enc.c b/drivers/media/pci/solo6x10/solo6x10-enc.c index a9c56897f7bc9..14a1d51cfad46 100644 --- a/drivers/media/pci/solo6x10/solo6x10-enc.c +++ b/drivers/media/pci/solo6x10/solo6x10-enc.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* - * Copyright (C) 2010-2013 Bluecherry, LLC + * Copyright (C) 2010-2013 Bluecherry, LLC * * Original author: * Ben Collins diff --git a/drivers/media/pci/solo6x10/solo6x10-g723.c b/drivers/media/pci/solo6x10/solo6x10-g723.c index d6d16e8fd997e..906ce86437ae3 100644 --- a/drivers/media/pci/solo6x10/solo6x10-g723.c +++ b/drivers/media/pci/solo6x10/solo6x10-g723.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* - * Copyright (C) 2010-2013 Bluecherry, LLC + * Copyright (C) 2010-2013 Bluecherry, LLC * * Original author: * Ben Collins diff --git a/drivers/media/pci/solo6x10/solo6x10-gpio.c b/drivers/media/pci/solo6x10/solo6x10-gpio.c index 526d67cf9942c..084c30760e459 100644 --- a/drivers/media/pci/solo6x10/solo6x10-gpio.c +++ b/drivers/media/pci/solo6x10/solo6x10-gpio.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* - * Copyright (C) 2010-2013 Bluecherry, LLC + * Copyright (C) 2010-2013 Bluecherry, LLC * * Original author: * Ben Collins diff --git a/drivers/media/pci/solo6x10/solo6x10-i2c.c b/drivers/media/pci/solo6x10/solo6x10-i2c.c index df1e3f2e9c05e..f86f12fa63501 100644 --- a/drivers/media/pci/solo6x10/solo6x10-i2c.c +++ b/drivers/media/pci/solo6x10/solo6x10-i2c.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* - * Copyright (C) 2010-2013 Bluecherry, LLC + * Copyright (C) 2010-2013 Bluecherry, LLC * * Original author: * Ben Collins diff --git a/drivers/media/pci/solo6x10/solo6x10-jpeg.h b/drivers/media/pci/solo6x10/solo6x10-jpeg.h index e35aad16ad335..e212f4828c094 100644 --- a/drivers/media/pci/solo6x10/solo6x10-jpeg.h +++ b/drivers/media/pci/solo6x10/solo6x10-jpeg.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ /* - * Copyright (C) 2010-2013 Bluecherry, LLC + * Copyright (C) 2010-2013 Bluecherry, LLC * * Original author: * Ben Collins diff --git a/drivers/media/pci/solo6x10/solo6x10-offsets.h b/drivers/media/pci/solo6x10/solo6x10-offsets.h index e3ae6a02dbb97..f414ee1316f29 100644 --- a/drivers/media/pci/solo6x10/solo6x10-offsets.h +++ b/drivers/media/pci/solo6x10/solo6x10-offsets.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ /* - * Copyright (C) 2010-2013 Bluecherry, LLC + * Copyright (C) 2010-2013 Bluecherry, LLC * * Original author: * Ben Collins diff --git a/drivers/media/pci/solo6x10/solo6x10-p2m.c b/drivers/media/pci/solo6x10/solo6x10-p2m.c index e2816e2629f8b..db2afc6a5fcb7 100644 --- a/drivers/media/pci/solo6x10/solo6x10-p2m.c +++ b/drivers/media/pci/solo6x10/solo6x10-p2m.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* - * Copyright (C) 2010-2013 Bluecherry, LLC + * Copyright (C) 2010-2013 Bluecherry, LLC * * Original author: * Ben Collins diff --git a/drivers/media/pci/solo6x10/solo6x10-regs.h b/drivers/media/pci/solo6x10/solo6x10-regs.h index 804505d01b252..12e0ac1904168 100644 --- a/drivers/media/pci/solo6x10/solo6x10-regs.h +++ b/drivers/media/pci/solo6x10/solo6x10-regs.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ /* - * Copyright (C) 2010-2013 Bluecherry, LLC + * Copyright (C) 2010-2013 Bluecherry, LLC * * Original author: * Ben Collins diff --git a/drivers/media/pci/solo6x10/solo6x10-tw28.c b/drivers/media/pci/solo6x10/solo6x10-tw28.c index 126cd1b01266b..1b7c22a9bc94f 100644 --- a/drivers/media/pci/solo6x10/solo6x10-tw28.c +++ b/drivers/media/pci/solo6x10/solo6x10-tw28.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* - * Copyright (C) 2010-2013 Bluecherry, LLC + * Copyright (C) 2010-2013 Bluecherry, LLC * * Original author: * Ben Collins diff --git a/drivers/media/pci/solo6x10/solo6x10-tw28.h b/drivers/media/pci/solo6x10/solo6x10-tw28.h index edbad194d31e6..4a8ede3139a85 100644 --- a/drivers/media/pci/solo6x10/solo6x10-tw28.h +++ b/drivers/media/pci/solo6x10/solo6x10-tw28.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ /* - * Copyright (C) 2010-2013 Bluecherry, LLC + * Copyright (C) 2010-2013 Bluecherry, LLC * * Original author: * Ben Collins diff --git a/drivers/media/pci/solo6x10/solo6x10-v4l2-enc.c b/drivers/media/pci/solo6x10/solo6x10-v4l2-enc.c index cbf85231b708f..3cf7bd6186aa8 100644 --- a/drivers/media/pci/solo6x10/solo6x10-v4l2-enc.c +++ b/drivers/media/pci/solo6x10/solo6x10-v4l2-enc.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* - * Copyright (C) 2010-2013 Bluecherry, LLC + * Copyright (C) 2010-2013 Bluecherry, LLC * * Original author: * Ben Collins diff --git a/drivers/media/pci/solo6x10/solo6x10-v4l2.c b/drivers/media/pci/solo6x10/solo6x10-v4l2.c index 54434f3c428d0..24ef0c446bef1 100644 --- a/drivers/media/pci/solo6x10/solo6x10-v4l2.c +++ b/drivers/media/pci/solo6x10/solo6x10-v4l2.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* - * Copyright (C) 2010-2013 Bluecherry, LLC + * Copyright (C) 2010-2013 Bluecherry, LLC * * Original author: * Ben Collins diff --git a/drivers/media/pci/solo6x10/solo6x10.h b/drivers/media/pci/solo6x10/solo6x10.h index 9f2314688cecd..126f6fb7b755d 100644 --- a/drivers/media/pci/solo6x10/solo6x10.h +++ b/drivers/media/pci/solo6x10/solo6x10.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ /* - * Copyright (C) 2010-2013 Bluecherry, LLC + * Copyright (C) 2010-2013 Bluecherry, LLC * * Original author: * Ben Collins -- GitLab From bb79974cdf953dc49ef653c59fda4daf1acc32b9 Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Thu, 9 Jul 2020 09:28:14 +0200 Subject: [PATCH 0526/1476] media: davinci: replace http references with https TI DAVINCI SERIES MEDIA DRIVER: Replace HTTP links with HTTPS ones Rationale: Reduces attack surface on kernel devs opening the links for MITM as HTTPS traffic is much harder to manipulate. Deterministic algorithm: For each file: If not .svg: For each line: If doesn't contain `\bxmlns\b`: If neither `\bgnu\.org/license`, nor `\bmozilla\.org/MPL\b`: If both the HTTP and HTTPS versions return 200 OK and serve the same content: Replace HTTP with HTTPS. Signed-off-by: Alexander A. Klimov Reviewed-by: Kieran Bingham Reviewed-by: Lad Prabhakar Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/davinci/vpbe_display.c | 2 +- drivers/media/platform/davinci/vpif.c | 2 +- drivers/media/platform/davinci/vpif.h | 2 +- drivers/media/platform/davinci/vpif_display.c | 2 +- drivers/media/platform/davinci/vpif_display.h | 2 +- include/media/davinci/vpbe_display.h | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/media/platform/davinci/vpbe_display.c b/drivers/media/platform/davinci/vpbe_display.c index 7ab13eb7527d1..d19bad997f30c 100644 --- a/drivers/media/platform/davinci/vpbe_display.c +++ b/drivers/media/platform/davinci/vpbe_display.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only /* - * Copyright (C) 2010 Texas Instruments Incorporated - http://www.ti.com/ + * Copyright (C) 2010 Texas Instruments Incorporated - https://www.ti.com/ */ #include #include diff --git a/drivers/media/platform/davinci/vpif.c b/drivers/media/platform/davinci/vpif.c index f28c1b27eac69..5e67994e62cca 100644 --- a/drivers/media/platform/davinci/vpif.c +++ b/drivers/media/platform/davinci/vpif.c @@ -5,7 +5,7 @@ * The hardware supports SDTV, HDTV formats, raw data capture. * Currently, the driver supports NTSC and PAL standards. * - * Copyright (C) 2009 Texas Instruments Incorporated - http://www.ti.com/ + * Copyright (C) 2009 Texas Instruments Incorporated - https://www.ti.com/ * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as diff --git a/drivers/media/platform/davinci/vpif.h b/drivers/media/platform/davinci/vpif.h index 2466c7c77deb4..c6d1d890478a7 100644 --- a/drivers/media/platform/davinci/vpif.h +++ b/drivers/media/platform/davinci/vpif.h @@ -1,7 +1,7 @@ /* * VPIF header file * - * Copyright (C) 2009 Texas Instruments Incorporated - http://www.ti.com/ + * Copyright (C) 2009 Texas Instruments Incorporated - https://www.ti.com/ * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as diff --git a/drivers/media/platform/davinci/vpif_display.c b/drivers/media/platform/davinci/vpif_display.c index 7d55fd45240e7..46afc029138fc 100644 --- a/drivers/media/platform/davinci/vpif_display.c +++ b/drivers/media/platform/davinci/vpif_display.c @@ -2,7 +2,7 @@ * vpif-display - VPIF display driver * Display driver for TI DaVinci VPIF * - * Copyright (C) 2009 Texas Instruments Incorporated - http://www.ti.com/ + * Copyright (C) 2009 Texas Instruments Incorporated - https://www.ti.com/ * Copyright (C) 2014 Lad, Prabhakar * * This program is free software; you can redistribute it and/or diff --git a/drivers/media/platform/davinci/vpif_display.h b/drivers/media/platform/davinci/vpif_display.h index af2765fdcea8b..f731a65eefd64 100644 --- a/drivers/media/platform/davinci/vpif_display.h +++ b/drivers/media/platform/davinci/vpif_display.h @@ -1,7 +1,7 @@ /* * VPIF display header file * - * Copyright (C) 2009 Texas Instruments Incorporated - http://www.ti.com/ + * Copyright (C) 2009 Texas Instruments Incorporated - https://www.ti.com/ * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as diff --git a/include/media/davinci/vpbe_display.h b/include/media/davinci/vpbe_display.h index 56d05a855140e..6d2a937401306 100644 --- a/include/media/davinci/vpbe_display.h +++ b/include/media/davinci/vpbe_display.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0-only */ /* - * Copyright (C) 2010 Texas Instruments Incorporated - http://www.ti.com/ + * Copyright (C) 2010 Texas Instruments Incorporated - https://www.ti.com/ */ #ifndef VPBE_DISPLAY_H #define VPBE_DISPLAY_H -- GitLab From 2e7ee8bb24a72b924ef78908737d81f81cf93053 Mon Sep 17 00:00:00 2001 From: Eugen Hristev Date: Thu, 9 Jul 2020 14:06:45 +0200 Subject: [PATCH 0527/1476] media: atmel: atmel-isc: rework component offsets The component offsets were computed in a negative way: they were subtracted from the actual color component value. So, a higher offset was reducing the component value. This is not really desirable, as the offset is a 2's complements number with 1 bit for sign and 12 value bits, so we would like to be able to also add to the component, not only subtract. The reported number in v4l2 is fine, a range from -4095 to +4095. However when configuring a negative value for the offset, this would in fact not function, because with the old code, the number was subtracted from the max value. By setting something negative, it was overflowing in fact. Reworked the component offsets by placing the real value as the v4l2 ctrls. Now, the values are the real number that is added or subtracted from the component. The negative values received from v4l2 are already in 2's complements, so there is no need for conversion. This actually simplifies a lot the computation procedure, eliminating the need for the macros that convert from v4l2 values to ISC values and viceversa. Also the ZERO_VAL is eliminated, as 0 is now 0, as it's supposed to be. Example after this change: # v4l2-ctl --set-ctrl=red_component_offset=-150 -L User Controls brightness 0x00980900 (int) : min=-1024 max=1023 step=1 default=0 value=0 flags=slider contrast 0x00980901 (int) : min=-2048 max=2047 step=1 default=16 value=20 flags=slider white_balance_automatic 0x0098090c (bool) : default=1 value=0 flags=update do_white_balance 0x0098090d (button) : flags=inactive, write-only, execute-on-write gamma 0x00980910 (int) : min=0 max=3 step=1 default=3 value=3 flags=slider red_component_gain 0x009819c0 (int) : min=0 max=8191 step=1 default=512 value=512 flags=slider blue_component_gain 0x009819c1 (int) : min=0 max=8191 step=1 default=512 value=512 flags=slider green_red_component_gain 0x009819c2 (int) : min=0 max=8191 step=1 default=512 value=512 flags=slider green_blue_component_gain 0x009819c3 (int) : min=0 max=8191 step=1 default=512 value=512 flags=slider red_component_offset 0x009819c4 (int) : min=-4095 max=4095 step=1 default=0 value=-150 flags=slider blue_component_offset 0x009819c5 (int) : min=-4095 max=4095 step=1 default=0 value=0 flags=slider green_red_component_offset 0x009819c6 (int) : min=-4095 max=4095 step=1 default=0 value=0 flags=slider green_blue_component_offset 0x009819c7 (int) : min=-4095 max=4095 step=1 default=0 value=0 flags=slider The auto white balance algorithm is unchanged, but the obtained value to 'subtract' is now converted to negative and saved as a v4l2 control and displayed properly. Signed-off-by: Eugen Hristev Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/atmel/atmel-isc-base.c | 60 ++++++++----------- drivers/media/platform/atmel/atmel-isc-regs.h | 2 - drivers/media/platform/atmel/atmel-isc.h | 2 +- 3 files changed, 25 insertions(+), 39 deletions(-) diff --git a/drivers/media/platform/atmel/atmel-isc-base.c b/drivers/media/platform/atmel/atmel-isc-base.c index a6e9797a0ec99..fe3ec8d0eaee3 100644 --- a/drivers/media/platform/atmel/atmel-isc-base.c +++ b/drivers/media/platform/atmel/atmel-isc-base.c @@ -225,9 +225,6 @@ const u32 isc_gamma_table[GAMMA_MAX + 1][GAMMA_ENTRIES] = { (((mbus_code) == MEDIA_BUS_FMT_Y10_1X10) | \ (((mbus_code) == MEDIA_BUS_FMT_Y8_1X8))) -#define ISC_CTRL_ISC_TO_V4L2(x) ((x) == ISC_WB_O_ZERO_VAL ? 0 : (x)) -#define ISC_CTRL_V4L2_TO_ISC(x) ((x) ? (x) : ISC_WB_O_ZERO_VAL) - static inline void isc_update_v4l2_ctrls(struct isc_device *isc) { struct isc_ctrls *ctrls = &isc->ctrls; @@ -238,14 +235,10 @@ static inline void isc_update_v4l2_ctrls(struct isc_device *isc) v4l2_ctrl_s_ctrl(isc->gr_gain_ctrl, ctrls->gain[ISC_HIS_CFG_MODE_GR]); v4l2_ctrl_s_ctrl(isc->gb_gain_ctrl, ctrls->gain[ISC_HIS_CFG_MODE_GB]); - v4l2_ctrl_s_ctrl(isc->r_off_ctrl, - ISC_CTRL_ISC_TO_V4L2(ctrls->offset[ISC_HIS_CFG_MODE_R])); - v4l2_ctrl_s_ctrl(isc->b_off_ctrl, - ISC_CTRL_ISC_TO_V4L2(ctrls->offset[ISC_HIS_CFG_MODE_B])); - v4l2_ctrl_s_ctrl(isc->gr_off_ctrl, - ISC_CTRL_ISC_TO_V4L2(ctrls->offset[ISC_HIS_CFG_MODE_GR])); - v4l2_ctrl_s_ctrl(isc->gb_off_ctrl, - ISC_CTRL_ISC_TO_V4L2(ctrls->offset[ISC_HIS_CFG_MODE_GB])); + v4l2_ctrl_s_ctrl(isc->r_off_ctrl, ctrls->offset[ISC_HIS_CFG_MODE_R]); + v4l2_ctrl_s_ctrl(isc->b_off_ctrl, ctrls->offset[ISC_HIS_CFG_MODE_B]); + v4l2_ctrl_s_ctrl(isc->gr_off_ctrl, ctrls->offset[ISC_HIS_CFG_MODE_GR]); + v4l2_ctrl_s_ctrl(isc->gb_off_ctrl, ctrls->offset[ISC_HIS_CFG_MODE_GB]); } static inline void isc_update_awb_ctrls(struct isc_device *isc) @@ -255,11 +248,11 @@ static inline void isc_update_awb_ctrls(struct isc_device *isc) /* In here we set our actual hw pipeline config */ regmap_write(isc->regmap, ISC_WB_O_RGR, - (ISC_WB_O_ZERO_VAL - (ctrls->offset[ISC_HIS_CFG_MODE_R])) | - ((ISC_WB_O_ZERO_VAL - ctrls->offset[ISC_HIS_CFG_MODE_GR]) << 16)); + ((ctrls->offset[ISC_HIS_CFG_MODE_R])) | + ((ctrls->offset[ISC_HIS_CFG_MODE_GR]) << 16)); regmap_write(isc->regmap, ISC_WB_O_BGB, - (ISC_WB_O_ZERO_VAL - (ctrls->offset[ISC_HIS_CFG_MODE_B])) | - ((ISC_WB_O_ZERO_VAL - ctrls->offset[ISC_HIS_CFG_MODE_GB]) << 16)); + ((ctrls->offset[ISC_HIS_CFG_MODE_B])) | + ((ctrls->offset[ISC_HIS_CFG_MODE_GB]) << 16)); regmap_write(isc->regmap, ISC_WB_G_RGR, ctrls->gain[ISC_HIS_CFG_MODE_R] | (ctrls->gain[ISC_HIS_CFG_MODE_GR] << 16)); @@ -275,12 +268,8 @@ static inline void isc_reset_awb_ctrls(struct isc_device *isc) for (c = ISC_HIS_CFG_MODE_GR; c <= ISC_HIS_CFG_MODE_B; c++) { /* gains have a fixed point at 9 decimals */ isc->ctrls.gain[c] = 1 << 9; - /* offsets are in 2's complements, the value - * will be substracted from ISC_WB_O_ZERO_VAL to obtain - * 2's complement of a value between 0 and - * ISC_WB_O_ZERO_VAL >> 1 - */ - isc->ctrls.offset[c] = ISC_WB_O_ZERO_VAL; + /* offsets are in 2's complements */ + isc->ctrls.offset[c] = 0; } } @@ -1774,9 +1763,12 @@ static void isc_wb_update(struct isc_ctrls *ctrls) */ ctrls->offset[c] = (offset[c] - 1) << 3; - /* the offset is then taken and converted to 2's complements */ - if (!ctrls->offset[c]) - ctrls->offset[c] = ISC_WB_O_ZERO_VAL; + /* + * the offset is then taken and converted to 2's complements, + * and must be negative, as we subtract this value from the + * color components + */ + ctrls->offset[c] = -ctrls->offset[c]; /* * the stretch gain is the total number of histogram bins @@ -1938,17 +1930,13 @@ static int isc_s_awb_ctrl(struct v4l2_ctrl *ctrl) ctrls->gain[ISC_HIS_CFG_MODE_GB] = isc->gb_gain_ctrl->val; if (ctrl->cluster[ISC_CTRL_R_OFF]->is_new) - ctrls->offset[ISC_HIS_CFG_MODE_R] = - ISC_CTRL_V4L2_TO_ISC(isc->r_off_ctrl->val); + ctrls->offset[ISC_HIS_CFG_MODE_R] = isc->r_off_ctrl->val; if (ctrl->cluster[ISC_CTRL_B_OFF]->is_new) - ctrls->offset[ISC_HIS_CFG_MODE_B] = - ISC_CTRL_V4L2_TO_ISC(isc->b_off_ctrl->val); + ctrls->offset[ISC_HIS_CFG_MODE_B] = isc->b_off_ctrl->val; if (ctrl->cluster[ISC_CTRL_GR_OFF]->is_new) - ctrls->offset[ISC_HIS_CFG_MODE_GR] = - ISC_CTRL_V4L2_TO_ISC(isc->gr_off_ctrl->val); + ctrls->offset[ISC_HIS_CFG_MODE_GR] = isc->gr_off_ctrl->val; if (ctrl->cluster[ISC_CTRL_GB_OFF]->is_new) - ctrls->offset[ISC_HIS_CFG_MODE_GB] = - ISC_CTRL_V4L2_TO_ISC(isc->gb_off_ctrl->val); + ctrls->offset[ISC_HIS_CFG_MODE_GB] = isc->gb_off_ctrl->val; isc_update_awb_ctrls(isc); @@ -2010,13 +1998,13 @@ static int isc_g_volatile_awb_ctrl(struct v4l2_ctrl *ctrl) ctrls->gain[ISC_HIS_CFG_MODE_GB]; ctrl->cluster[ISC_CTRL_R_OFF]->val = - ISC_CTRL_ISC_TO_V4L2(ctrls->offset[ISC_HIS_CFG_MODE_R]); + ctrls->offset[ISC_HIS_CFG_MODE_R]; ctrl->cluster[ISC_CTRL_B_OFF]->val = - ISC_CTRL_ISC_TO_V4L2(ctrls->offset[ISC_HIS_CFG_MODE_B]); + ctrls->offset[ISC_HIS_CFG_MODE_B]; ctrl->cluster[ISC_CTRL_GR_OFF]->val = - ISC_CTRL_ISC_TO_V4L2(ctrls->offset[ISC_HIS_CFG_MODE_GR]); + ctrls->offset[ISC_HIS_CFG_MODE_GR]; ctrl->cluster[ISC_CTRL_GB_OFF]->val = - ISC_CTRL_ISC_TO_V4L2(ctrls->offset[ISC_HIS_CFG_MODE_GB]); + ctrls->offset[ISC_HIS_CFG_MODE_GB]; break; } return 0; diff --git a/drivers/media/platform/atmel/atmel-isc-regs.h b/drivers/media/platform/atmel/atmel-isc-regs.h index c1283fb21bf61..f1e160ed43512 100644 --- a/drivers/media/platform/atmel/atmel-isc-regs.h +++ b/drivers/media/platform/atmel/atmel-isc-regs.h @@ -108,8 +108,6 @@ /* ISC White Balance Gain for B, GB Register */ #define ISC_WB_G_BGB 0x0000006c -#define ISC_WB_O_ZERO_VAL (1 << 13) - /* ISC Color Filter Array Control Register */ #define ISC_CFA_CTRL 0x00000070 diff --git a/drivers/media/platform/atmel/atmel-isc.h b/drivers/media/platform/atmel/atmel-isc.h index fc56a745c7d11..24b784b893d62 100644 --- a/drivers/media/platform/atmel/atmel-isc.h +++ b/drivers/media/platform/atmel/atmel-isc.h @@ -133,7 +133,7 @@ struct isc_ctrls { /* one for each component : GR, R, GB, B */ u32 gain[HIST_BAYER]; - u32 offset[HIST_BAYER]; + s32 offset[HIST_BAYER]; u32 hist_entry[HIST_ENTRIES]; u32 hist_count[HIST_BAYER]; -- GitLab From 3bdf27a33cc4f4806ab6ced841aaffed6625544f Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Thu, 9 Jul 2020 22:29:17 +0200 Subject: [PATCH 0528/1476] media: gspca: replace http references with https ones GSPCA USB WEBCAM DRIVER: Replace HTTP links with HTTPS ones Rationale: Reduces attack surface on kernel devs opening the links for MITM as HTTPS traffic is much harder to manipulate. Deterministic algorithm: For each file: If not .svg: For each line: If doesn't contain `\bxmlns\b`: If neither `\bgnu\.org/license`, nor `\bmozilla\.org/MPL\b`: If both the HTTP and HTTPS versions return 200 OK and serve the same content: Replace HTTP with HTTPS. Signed-off-by: Alexander A. Klimov Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/gspca/ov534.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/usb/gspca/ov534.c b/drivers/media/usb/gspca/ov534.c index 185c1f10fb30b..9a11158f38daf 100644 --- a/drivers/media/usb/gspca/ov534.c +++ b/drivers/media/usb/gspca/ov534.c @@ -14,7 +14,7 @@ * PS3 Eye camera - brightness, contrast, awb, agc, aec controls * added by Max Thrun * PS3 Eye camera - FPS range extended by Joseph Howse - * http://nummist.com + * https://nummist.com */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt -- GitLab From 467bd18d7ae37dce49955554014429ca8c679d4f Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Thu, 9 Jul 2020 23:29:43 +0200 Subject: [PATCH 0529/1476] media: docs: pvrusb2: replace http references with https ones PVRUSB2 VIDEO4LINUX DRIVER: Replace HTTP links with HTTPS ones Rationale: Reduces attack surface on kernel devs opening the links for MITM as HTTPS traffic is much harder to manipulate. Deterministic algorithm: For each file: If not .svg: For each line: If doesn't contain `\bxmlns\b`: If neither `\bgnu\.org/license`, nor `\bmozilla\.org/MPL\b`: If both the HTTP and HTTPS versions return 200 OK and serve the same content: Replace HTTP with HTTPS. Signed-off-by: Alexander A. Klimov Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- Documentation/driver-api/media/drivers/pvrusb2.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/driver-api/media/drivers/pvrusb2.rst b/Documentation/driver-api/media/drivers/pvrusb2.rst index 83bfaa531ea87..cbd9359c247ae 100644 --- a/Documentation/driver-api/media/drivers/pvrusb2.rst +++ b/Documentation/driver-api/media/drivers/pvrusb2.rst @@ -20,7 +20,7 @@ last known snapshot and evolved the driver to the state it is in here. More information on this driver can be found at: -http://www.isely.net/pvrusb2.html +https://www.isely.net/pvrusb2.html This driver has a strong separation of layers. They are very -- GitLab From d6129d36b305e9943bc6af0e4f0f7f6a77ded9b0 Mon Sep 17 00:00:00 2001 From: Flavio Suligoi Date: Tue, 16 Jun 2020 13:52:55 +0200 Subject: [PATCH 0530/1476] media: pci: dt3155: fix spelling mistake Fix typo: "TRIGER" --> "TRIGGER" Signed-off-by: Flavio Suligoi Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/pci/dt3155/dt3155.c | 2 +- drivers/media/pci/dt3155/dt3155.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/media/pci/dt3155/dt3155.c b/drivers/media/pci/dt3155/dt3155.c index 82581aa5a2a39..ef8d5c9cfffe6 100644 --- a/drivers/media/pci/dt3155/dt3155.c +++ b/drivers/media/pci/dt3155/dt3155.c @@ -426,7 +426,7 @@ static int dt3155_init_board(struct dt3155_priv *pd) iowrite32(FIFO_EN | SRST, pd->regs + CSR1); iowrite32(0xEEEEEE01, pd->regs + EVEN_PIXEL_FMT); iowrite32(0xEEEEEE01, pd->regs + ODD_PIXEL_FMT); - iowrite32(0x00000020, pd->regs + FIFO_TRIGER); + iowrite32(0x00000020, pd->regs + FIFO_TRIGGER); iowrite32(0x00000103, pd->regs + XFER_MODE); iowrite32(0, pd->regs + RETRY_WAIT_CNT); iowrite32(0, pd->regs + INT_CSR); diff --git a/drivers/media/pci/dt3155/dt3155.h b/drivers/media/pci/dt3155/dt3155.h index 43d32b8d5feb7..c9ce79cb55668 100644 --- a/drivers/media/pci/dt3155/dt3155.h +++ b/drivers/media/pci/dt3155/dt3155.h @@ -31,7 +31,7 @@ #define ODD_DMA_STRIDE 0x24 #define EVEN_PIXEL_FMT 0x30 #define ODD_PIXEL_FMT 0x34 -#define FIFO_TRIGER 0x38 +#define FIFO_TRIGGER 0x38 #define XFER_MODE 0x3C #define CSR1 0x40 #define RETRY_WAIT_CNT 0x44 -- GitLab From b3ab1c6058fad8cd5726f24e9ed9053e43bb2af4 Mon Sep 17 00:00:00 2001 From: Ezequiel Garcia Date: Wed, 24 Jun 2020 21:28:00 +0200 Subject: [PATCH 0531/1476] media: Add V4L2_TYPE_IS_CAPTURE helper It's all too easy to get confused by the V4L2_TYPE_IS_OUTPUT macro, when it's used as !V4L2_TYPE_IS_OUTPUT. Reduce the risk of confusion with macro to explicitly check for the CAPTURE queue type case. This change does not affect functionality, and it's only intended to make the code more readable. Suggested-by: Nicolas Dufresne Signed-off-by: Ezequiel Garcia Signed-off-by: Hans Verkuil [hverkuil-cisco@xs4all.nl: checkpatch: align with parenthesis] Signed-off-by: Mauro Carvalho Chehab --- drivers/media/common/videobuf2/videobuf2-v4l2.c | 4 ++-- drivers/media/platform/exynos-gsc/gsc-core.c | 2 +- drivers/media/platform/exynos-gsc/gsc-m2m.c | 2 +- drivers/media/platform/mtk-jpeg/mtk_jpeg_core.c | 2 +- drivers/media/platform/mtk-mdp/mtk_mdp_m2m.c | 7 +++---- drivers/media/platform/rcar_jpu.c | 2 +- drivers/media/platform/sti/hva/hva-v4l2.c | 2 +- drivers/media/platform/ti-vpe/vpe.c | 2 +- drivers/media/test-drivers/vicodec/vicodec-core.c | 6 +++--- drivers/media/v4l2-core/v4l2-mem2mem.c | 6 +++--- drivers/staging/media/hantro/hantro_v4l2.c | 2 +- drivers/staging/media/rkvdec/rkvdec.c | 2 +- include/uapi/linux/videodev2.h | 2 ++ 13 files changed, 21 insertions(+), 20 deletions(-) diff --git a/drivers/media/common/videobuf2/videobuf2-v4l2.c b/drivers/media/common/videobuf2/videobuf2-v4l2.c index 57aa183bd1983..30caad27281e1 100644 --- a/drivers/media/common/videobuf2/videobuf2-v4l2.c +++ b/drivers/media/common/videobuf2/videobuf2-v4l2.c @@ -97,7 +97,7 @@ static int __verify_length(struct vb2_buffer *vb, const struct v4l2_buffer *b) unsigned int bytesused; unsigned int plane; - if (!V4L2_TYPE_IS_OUTPUT(b->type)) + if (V4L2_TYPE_IS_CAPTURE(b->type)) return 0; if (V4L2_TYPE_IS_MULTIPLANAR(b->type)) { @@ -311,7 +311,7 @@ static int vb2_fill_vb2_v4l2_buffer(struct vb2_buffer *vb, struct v4l2_buffer *b /* Zero flags that we handle */ vbuf->flags = b->flags & ~V4L2_BUFFER_MASK_FLAGS; - if (!vb->vb2_queue->copy_timestamp || !V4L2_TYPE_IS_OUTPUT(b->type)) { + if (!vb->vb2_queue->copy_timestamp || V4L2_TYPE_IS_CAPTURE(b->type)) { /* * Non-COPY timestamps and non-OUTPUT queues will get * their timestamp and timestamp source flags from the diff --git a/drivers/media/platform/exynos-gsc/gsc-core.c b/drivers/media/platform/exynos-gsc/gsc-core.c index f6650b45bc3d2..9f41c2e7097a6 100644 --- a/drivers/media/platform/exynos-gsc/gsc-core.c +++ b/drivers/media/platform/exynos-gsc/gsc-core.c @@ -577,7 +577,7 @@ int gsc_try_selection(struct gsc_ctx *ctx, struct v4l2_selection *s) v4l_bound_align_image(&tmp_w, min_w, max_w, mod_x, &tmp_h, min_h, max_h, mod_y, 0); - if (!V4L2_TYPE_IS_OUTPUT(s->type) && + if (V4L2_TYPE_IS_CAPTURE(s->type) && (ctx->gsc_ctrls.rotate->val == 90 || ctx->gsc_ctrls.rotate->val == 270)) gsc_check_crop_change(tmp_h, tmp_w, diff --git a/drivers/media/platform/exynos-gsc/gsc-m2m.c b/drivers/media/platform/exynos-gsc/gsc-m2m.c index e2c162635f727..27a3c92c73bce 100644 --- a/drivers/media/platform/exynos-gsc/gsc-m2m.c +++ b/drivers/media/platform/exynos-gsc/gsc-m2m.c @@ -255,7 +255,7 @@ static int gsc_m2m_buf_prepare(struct vb2_buffer *vb) if (IS_ERR(frame)) return PTR_ERR(frame); - if (!V4L2_TYPE_IS_OUTPUT(vb->vb2_queue->type)) { + if (V4L2_TYPE_IS_CAPTURE(vb->vb2_queue->type)) { for (i = 0; i < frame->fmt->num_planes; i++) vb2_set_plane_payload(vb, i, frame->payload[i]); } diff --git a/drivers/media/platform/mtk-jpeg/mtk_jpeg_core.c b/drivers/media/platform/mtk-jpeg/mtk_jpeg_core.c index f82a81a3bdee2..61fed1e35a005 100644 --- a/drivers/media/platform/mtk-jpeg/mtk_jpeg_core.c +++ b/drivers/media/platform/mtk-jpeg/mtk_jpeg_core.c @@ -731,7 +731,7 @@ static void mtk_jpeg_stop_streaming(struct vb2_queue *q) * subsampling. Update capture queue when the stream is off. */ if (ctx->state == MTK_JPEG_SOURCE_CHANGE && - !V4L2_TYPE_IS_OUTPUT(q->type)) { + V4L2_TYPE_IS_CAPTURE(q->type)) { struct mtk_jpeg_src_buf *src_buf; vb = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx); diff --git a/drivers/media/platform/mtk-mdp/mtk_mdp_m2m.c b/drivers/media/platform/mtk-mdp/mtk_mdp_m2m.c index bb9caaf513bcb..724c7333b6e5a 100644 --- a/drivers/media/platform/mtk-mdp/mtk_mdp_m2m.c +++ b/drivers/media/platform/mtk-mdp/mtk_mdp_m2m.c @@ -193,7 +193,7 @@ static const struct mtk_mdp_fmt *mtk_mdp_try_fmt_mplane(struct mtk_mdp_ctx *ctx, pix_mp->field = V4L2_FIELD_NONE; pix_mp->pixelformat = fmt->pixelformat; - if (!V4L2_TYPE_IS_OUTPUT(f->type)) { + if (V4L2_TYPE_IS_CAPTURE(f->type)) { pix_mp->colorspace = ctx->colorspace; pix_mp->xfer_func = ctx->xfer_func; pix_mp->ycbcr_enc = ctx->ycbcr_enc; @@ -327,9 +327,8 @@ static int mtk_mdp_try_crop(struct mtk_mdp_ctx *ctx, u32 type, mtk_mdp_bound_align_image(&new_w, min_w, max_w, align_w, &new_h, min_h, max_h, align_h); - if (!V4L2_TYPE_IS_OUTPUT(type) && - (ctx->ctrls.rotate->val == 90 || - ctx->ctrls.rotate->val == 270)) + if (V4L2_TYPE_IS_CAPTURE(type) && + (ctx->ctrls.rotate->val == 90 || ctx->ctrls.rotate->val == 270)) mtk_mdp_check_crop_change(new_h, new_w, &r->width, &r->height); else diff --git a/drivers/media/platform/rcar_jpu.c b/drivers/media/platform/rcar_jpu.c index 5250a14324e91..9b99ff3686980 100644 --- a/drivers/media/platform/rcar_jpu.c +++ b/drivers/media/platform/rcar_jpu.c @@ -1066,7 +1066,7 @@ static int jpu_buf_prepare(struct vb2_buffer *vb) } /* decoder capture queue */ - if (!ctx->encoder && !V4L2_TYPE_IS_OUTPUT(vb->vb2_queue->type)) + if (!ctx->encoder && V4L2_TYPE_IS_CAPTURE(vb->vb2_queue->type)) vb2_set_plane_payload(vb, i, size); } diff --git a/drivers/media/platform/sti/hva/hva-v4l2.c b/drivers/media/platform/sti/hva/hva-v4l2.c index 197b99d8fd9cf..bb34d6997d991 100644 --- a/drivers/media/platform/sti/hva/hva-v4l2.c +++ b/drivers/media/platform/sti/hva/hva-v4l2.c @@ -1087,7 +1087,7 @@ static void hva_stop_streaming(struct vb2_queue *vq) if ((V4L2_TYPE_IS_OUTPUT(vq->type) && vb2_is_streaming(&ctx->fh.m2m_ctx->cap_q_ctx.q)) || - (!V4L2_TYPE_IS_OUTPUT(vq->type) && + (V4L2_TYPE_IS_CAPTURE(vq->type) && vb2_is_streaming(&ctx->fh.m2m_ctx->out_q_ctx.q))) { dev_dbg(dev, "%s %s out=%d cap=%d\n", ctx->name, to_type_str(vq->type), diff --git a/drivers/media/platform/ti-vpe/vpe.c b/drivers/media/platform/ti-vpe/vpe.c index cff2fcd6d812a..346f8212791cf 100644 --- a/drivers/media/platform/ti-vpe/vpe.c +++ b/drivers/media/platform/ti-vpe/vpe.c @@ -1576,7 +1576,7 @@ static int vpe_g_fmt(struct file *file, void *priv, struct v4l2_format *f) *f = q_data->format; - if (!V4L2_TYPE_IS_OUTPUT(f->type)) { + if (V4L2_TYPE_IS_CAPTURE(f->type)) { struct vpe_q_data *s_q_data; struct v4l2_pix_format_mplane *spix; diff --git a/drivers/media/test-drivers/vicodec/vicodec-core.c b/drivers/media/test-drivers/vicodec/vicodec-core.c index e879290727ef4..8941d73f6611d 100644 --- a/drivers/media/test-drivers/vicodec/vicodec-core.c +++ b/drivers/media/test-drivers/vicodec/vicodec-core.c @@ -1442,7 +1442,7 @@ static void vicodec_buf_queue(struct vb2_buffer *vb) .u.src_change.changes = V4L2_EVENT_SRC_CH_RESOLUTION, }; - if (!V4L2_TYPE_IS_OUTPUT(vb->vb2_queue->type) && + if (V4L2_TYPE_IS_CAPTURE(vb->vb2_queue->type) && vb2_is_streaming(vb->vb2_queue) && v4l2_m2m_dst_buf_is_last(ctx->fh.m2m_ctx)) { unsigned int i; @@ -1479,7 +1479,7 @@ static void vicodec_buf_queue(struct vb2_buffer *vb) * in the compressed stream */ if (ctx->is_stateless || ctx->is_enc || - !V4L2_TYPE_IS_OUTPUT(vb->vb2_queue->type)) { + V4L2_TYPE_IS_CAPTURE(vb->vb2_queue->type)) { v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, vbuf); return; } @@ -1574,7 +1574,7 @@ static int vicodec_start_streaming(struct vb2_queue *q, state->gop_cnt = 0; if ((V4L2_TYPE_IS_OUTPUT(q->type) && !ctx->is_enc) || - (!V4L2_TYPE_IS_OUTPUT(q->type) && ctx->is_enc)) + (V4L2_TYPE_IS_CAPTURE(q->type) && ctx->is_enc)) return 0; if (info->id == V4L2_PIX_FMT_FWHT || diff --git a/drivers/media/v4l2-core/v4l2-mem2mem.c b/drivers/media/v4l2-core/v4l2-mem2mem.c index 62ac9424c92a7..95a8f2dc5341d 100644 --- a/drivers/media/v4l2-core/v4l2-mem2mem.c +++ b/drivers/media/v4l2-core/v4l2-mem2mem.c @@ -556,7 +556,7 @@ int v4l2_m2m_querybuf(struct file *file, struct v4l2_m2m_ctx *m2m_ctx, ret = vb2_querybuf(vq, buf); /* Adjust MMAP memory offsets for the CAPTURE queue */ - if (buf->memory == V4L2_MEMORY_MMAP && !V4L2_TYPE_IS_OUTPUT(vq->type)) { + if (buf->memory == V4L2_MEMORY_MMAP && V4L2_TYPE_IS_CAPTURE(vq->type)) { if (V4L2_TYPE_IS_MULTIPLANAR(vq->type)) { for (i = 0; i < buf->length; ++i) buf->m.planes[i].m.mem_offset @@ -712,7 +712,7 @@ int v4l2_m2m_qbuf(struct file *file, struct v4l2_m2m_ctx *m2m_ctx, int ret; vq = v4l2_m2m_get_vq(m2m_ctx, buf->type); - if (!V4L2_TYPE_IS_OUTPUT(vq->type) && + if (V4L2_TYPE_IS_CAPTURE(vq->type) && (buf->flags & V4L2_BUF_FLAG_REQUEST_FD)) { dprintk("%s: requests cannot be used with capture buffers\n", __func__); @@ -729,7 +729,7 @@ int v4l2_m2m_qbuf(struct file *file, struct v4l2_m2m_ctx *m2m_ctx, * buffer as DONE with LAST flag since it won't be queued on the * device. */ - if (!V4L2_TYPE_IS_OUTPUT(vq->type) && + if (V4L2_TYPE_IS_CAPTURE(vq->type) && vb2_is_streaming(vq) && !vb2_start_streaming_called(vq) && (v4l2_m2m_has_stopped(m2m_ctx) || v4l2_m2m_dst_buf_is_last(m2m_ctx))) v4l2_m2m_force_last_buf_done(m2m_ctx, vq); diff --git a/drivers/staging/media/hantro/hantro_v4l2.c b/drivers/staging/media/hantro/hantro_v4l2.c index f28a94e2fa934..63859e8a0923e 100644 --- a/drivers/staging/media/hantro/hantro_v4l2.c +++ b/drivers/staging/media/hantro/hantro_v4l2.c @@ -237,7 +237,7 @@ static int hantro_try_fmt(const struct hantro_ctx *ctx, enum v4l2_buf_type type) { const struct hantro_fmt *fmt, *vpu_fmt; - bool capture = !V4L2_TYPE_IS_OUTPUT(type); + bool capture = V4L2_TYPE_IS_CAPTURE(type); bool coded; coded = capture == hantro_is_encoder_ctx(ctx); diff --git a/drivers/staging/media/rkvdec/rkvdec.c b/drivers/staging/media/rkvdec/rkvdec.c index 225eeca73356e..fd68671f02861 100644 --- a/drivers/staging/media/rkvdec/rkvdec.c +++ b/drivers/staging/media/rkvdec/rkvdec.c @@ -489,7 +489,7 @@ static int rkvdec_start_streaming(struct vb2_queue *q, unsigned int count) const struct rkvdec_coded_fmt_desc *desc; int ret; - if (!V4L2_TYPE_IS_OUTPUT(q->type)) + if (V4L2_TYPE_IS_CAPTURE(q->type)) return 0; desc = ctx->coded_fmt_desc; diff --git a/include/uapi/linux/videodev2.h b/include/uapi/linux/videodev2.h index 303805438814f..c7b70ff53bc1d 100644 --- a/include/uapi/linux/videodev2.h +++ b/include/uapi/linux/videodev2.h @@ -171,6 +171,8 @@ enum v4l2_buf_type { || (type) == V4L2_BUF_TYPE_SDR_OUTPUT \ || (type) == V4L2_BUF_TYPE_META_OUTPUT) +#define V4L2_TYPE_IS_CAPTURE(type) (!V4L2_TYPE_IS_OUTPUT(type)) + enum v4l2_tuner_type { V4L2_TUNER_RADIO = 1, V4L2_TUNER_ANALOG_TV = 2, -- GitLab From 94b9ce6870f9c90ac92505482689818b254312f7 Mon Sep 17 00:00:00 2001 From: Christophe JAILLET Date: Thu, 25 Jun 2020 22:19:47 +0200 Subject: [PATCH 0532/1476] media: s5p-g2d: Fix a memory leak in an error handling path in 'g2d_probe()' Memory allocated with 'v4l2_m2m_init()' must be freed by a corresponding call to 'v4l2_m2m_release()' Also reorder the code at the end of the probe function so that 'video_register_device()' is called last. Update the error handling path accordingly. Fixes: 5ce60d790a24 ("[media] s5p-g2d: Add DT based discovery support") Fixes: 918847341af0 ("[media] v4l: add G2D driver for s5p device family") Signed-off-by: Christophe JAILLET Signed-off-by: Hans Verkuil [hverkuil-cisco@xs4all.nl: checkpatch: align with parenthesis] Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/s5p-g2d/g2d.c | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/drivers/media/platform/s5p-g2d/g2d.c b/drivers/media/platform/s5p-g2d/g2d.c index 6932fd47071b0..15bcb7f6e113c 100644 --- a/drivers/media/platform/s5p-g2d/g2d.c +++ b/drivers/media/platform/s5p-g2d/g2d.c @@ -695,21 +695,13 @@ static int g2d_probe(struct platform_device *pdev) vfd->lock = &dev->mutex; vfd->v4l2_dev = &dev->v4l2_dev; vfd->device_caps = V4L2_CAP_VIDEO_M2M | V4L2_CAP_STREAMING; - ret = video_register_device(vfd, VFL_TYPE_VIDEO, 0); - if (ret) { - v4l2_err(&dev->v4l2_dev, "Failed to register video device\n"); - goto rel_vdev; - } - video_set_drvdata(vfd, dev); - dev->vfd = vfd; - v4l2_info(&dev->v4l2_dev, "device registered as /dev/video%d\n", - vfd->num); + platform_set_drvdata(pdev, dev); dev->m2m_dev = v4l2_m2m_init(&g2d_m2m_ops); if (IS_ERR(dev->m2m_dev)) { v4l2_err(&dev->v4l2_dev, "Failed to init mem2mem device\n"); ret = PTR_ERR(dev->m2m_dev); - goto unreg_video_dev; + goto rel_vdev; } def_frame.stride = (def_frame.width * def_frame.fmt->depth) >> 3; @@ -717,14 +709,24 @@ static int g2d_probe(struct platform_device *pdev) of_id = of_match_node(exynos_g2d_match, pdev->dev.of_node); if (!of_id) { ret = -ENODEV; - goto unreg_video_dev; + goto free_m2m; } dev->variant = (struct g2d_variant *)of_id->data; + ret = video_register_device(vfd, VFL_TYPE_VIDEO, 0); + if (ret) { + v4l2_err(&dev->v4l2_dev, "Failed to register video device\n"); + goto free_m2m; + } + video_set_drvdata(vfd, dev); + dev->vfd = vfd; + v4l2_info(&dev->v4l2_dev, "device registered as /dev/video%d\n", + vfd->num); + return 0; -unreg_video_dev: - video_unregister_device(dev->vfd); +free_m2m: + v4l2_m2m_release(dev->m2m_dev); rel_vdev: video_device_release(vfd); unreg_v4l2_dev: -- GitLab From 44d42520df8a67f47c1e47d934d6bcd38d87daac Mon Sep 17 00:00:00 2001 From: Florin Tudorache Date: Sun, 28 Jun 2020 11:24:47 +0200 Subject: [PATCH 0533/1476] gspca: First camera mode is skipped If a device supports a resolution in 2 different formats, mode[0] is skipped on first iteration and a different format is selected. Example: 320x240/YUYV isn't reachable for OV534/ov772x Signed-off-by: Florin Tudorache Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/gspca/gspca.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/usb/gspca/gspca.c b/drivers/media/usb/gspca/gspca.c index 0566e00d6fea7..c295f642d352c 100644 --- a/drivers/media/usb/gspca/gspca.c +++ b/drivers/media/usb/gspca/gspca.c @@ -925,7 +925,7 @@ static int wxh_to_nearest_mode(struct gspca_dev *gspca_dev, { int i; - for (i = gspca_dev->cam.nmodes; --i > 0; ) { + for (i = gspca_dev->cam.nmodes; --i >= 0; ) { if (width >= gspca_dev->cam.cam_mode[i].width && height >= gspca_dev->cam.cam_mode[i].height && pixelformat == gspca_dev->cam.cam_mode[i].pixelformat) -- GitLab From 03395df3977eba11e2ee9893082aa63fb6bdfc68 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 6 Jul 2020 20:35:22 +0200 Subject: [PATCH 0534/1476] media: ti-vpe: cal: Sort headers alphabetically Sorting headers alphabetically helps locating duplicates. Signed-off-by: Laurent Pinchart Reviewed-by: Tomi Valkeinen Reviewed-by: Benoit Parrot Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/ti-vpe/cal.c | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/drivers/media/platform/ti-vpe/cal.c b/drivers/media/platform/ti-vpe/cal.c index 9b18db7af6c38..19425248bd636 100644 --- a/drivers/media/platform/ti-vpe/cal.c +++ b/drivers/media/platform/ti-vpe/cal.c @@ -7,30 +7,31 @@ */ #include +#include #include #include #include +#include #include +#include +#include #include -#include #include -#include -#include #include +#include #include -#include -#include -#include #include #include #include #include #include -#include #include +#include +#include #include #include + #include "cal_regs.h" #define CAL_MODULE_NAME "cal" -- GitLab From 789026265985f3c8d5d2081690cfa24637b1a333 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 6 Jul 2020 20:35:23 +0200 Subject: [PATCH 0535/1476] media: ti-vpe: cal: Avoid function forward declaration Move the cal_complete_ctx() function earlier in the file to avoid a foward declaration. There is no functional change. Signed-off-by: Laurent Pinchart Reviewed-by: Tomi Valkeinen Reviewed-by: Benoit Parrot Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/ti-vpe/cal.c | 111 ++++++++++++++-------------- 1 file changed, 55 insertions(+), 56 deletions(-) diff --git a/drivers/media/platform/ti-vpe/cal.c b/drivers/media/platform/ti-vpe/cal.c index 19425248bd636..88fed930ed500 100644 --- a/drivers/media/platform/ti-vpe/cal.c +++ b/drivers/media/platform/ti-vpe/cal.c @@ -1852,7 +1852,61 @@ static const struct video_device cal_videodev = { * Initialization and module stuff * ------------------------------------------------------------------ */ -static int cal_complete_ctx(struct cal_ctx *ctx); + +static int cal_complete_ctx(struct cal_ctx *ctx) +{ + struct video_device *vfd; + struct vb2_queue *q; + int ret; + + ctx->timeperframe = tpf_default; + ctx->external_rate = 192000000; + + /* initialize locks */ + spin_lock_init(&ctx->slock); + mutex_init(&ctx->mutex); + + /* initialize queue */ + q = &ctx->vb_vidq; + q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + q->io_modes = VB2_MMAP | VB2_DMABUF | VB2_READ; + q->drv_priv = ctx; + q->buf_struct_size = sizeof(struct cal_buffer); + q->ops = &cal_video_qops; + q->mem_ops = &vb2_dma_contig_memops; + q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; + q->lock = &ctx->mutex; + q->min_buffers_needed = 3; + q->dev = ctx->v4l2_dev.dev; + + ret = vb2_queue_init(q); + if (ret) + return ret; + + /* init video dma queues */ + INIT_LIST_HEAD(&ctx->vidq.active); + + vfd = &ctx->vdev; + *vfd = cal_videodev; + vfd->v4l2_dev = &ctx->v4l2_dev; + vfd->queue = q; + + /* + * Provide a mutex to v4l2 core. It will be used to protect + * all fops and v4l2 ioctls. + */ + vfd->lock = &ctx->mutex; + video_set_drvdata(vfd, ctx); + + ret = video_register_device(vfd, VFL_TYPE_VIDEO, video_nr); + if (ret < 0) + return ret; + + v4l2_info(&ctx->v4l2_dev, "V4L2 device registered as %s\n", + video_device_node_name(vfd)); + + return 0; +} static int cal_async_bound(struct v4l2_async_notifier *notifier, struct v4l2_subdev *subdev, @@ -1948,61 +2002,6 @@ static const struct v4l2_async_notifier_operations cal_async_ops = { .complete = cal_async_complete, }; -static int cal_complete_ctx(struct cal_ctx *ctx) -{ - struct video_device *vfd; - struct vb2_queue *q; - int ret; - - ctx->timeperframe = tpf_default; - ctx->external_rate = 192000000; - - /* initialize locks */ - spin_lock_init(&ctx->slock); - mutex_init(&ctx->mutex); - - /* initialize queue */ - q = &ctx->vb_vidq; - q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - q->io_modes = VB2_MMAP | VB2_DMABUF | VB2_READ; - q->drv_priv = ctx; - q->buf_struct_size = sizeof(struct cal_buffer); - q->ops = &cal_video_qops; - q->mem_ops = &vb2_dma_contig_memops; - q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; - q->lock = &ctx->mutex; - q->min_buffers_needed = 3; - q->dev = ctx->v4l2_dev.dev; - - ret = vb2_queue_init(q); - if (ret) - return ret; - - /* init video dma queues */ - INIT_LIST_HEAD(&ctx->vidq.active); - - vfd = &ctx->vdev; - *vfd = cal_videodev; - vfd->v4l2_dev = &ctx->v4l2_dev; - vfd->queue = q; - - /* - * Provide a mutex to v4l2 core. It will be used to protect - * all fops and v4l2 ioctls. - */ - vfd->lock = &ctx->mutex; - video_set_drvdata(vfd, ctx); - - ret = video_register_device(vfd, VFL_TYPE_VIDEO, video_nr); - if (ret < 0) - return ret; - - v4l2_info(&ctx->v4l2_dev, "V4L2 device registered as %s\n", - video_device_node_name(vfd)); - - return 0; -} - static struct device_node * of_get_next_port(const struct device_node *parent, struct device_node *prev) -- GitLab From cff3784dfa6cca01c3001e8eca60ae0098de4c54 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 6 Jul 2020 20:35:24 +0200 Subject: [PATCH 0536/1476] media: ti-vpe: cal: Decouple CSI2 port and CPORT The CPORT mentioned in the CAL documentation has nothing to do with the CSI-2 ports. It's a remnant of CSI-3 support (or an attempt to achieve it), and now only serves as an arbitraty tag value used to associate data samples with contexts. Add a cport field to the cal_ctx structure and use it instead of the CSI-2 port number where a CPORT is required by the hardware. The value of the cport field is currently the same as the csi2_port field, so there's no functional change, but this will help reworking CPORT and CSI-2 port handling separately. Signed-off-by: Laurent Pinchart Reviewed-by: Tomi Valkeinen Reviewed-by: Benoit Parrot Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/ti-vpe/cal.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/drivers/media/platform/ti-vpe/cal.c b/drivers/media/platform/ti-vpe/cal.c index 88fed930ed500..201ef3585a48a 100644 --- a/drivers/media/platform/ti-vpe/cal.c +++ b/drivers/media/platform/ti-vpe/cal.c @@ -409,6 +409,7 @@ struct cal_ctx { struct vb2_queue vb_vidq; unsigned int seq_count; unsigned int csi2_port; + unsigned int cport; unsigned int virtual_channel; /* Pointer pointing to current v4l2_buffer */ @@ -971,7 +972,7 @@ static void csi2_ctx_config(struct cal_ctx *ctx) u32 val; val = reg_read(ctx->dev, CAL_CSI2_CTX0(ctx->csi2_port)); - set_field(&val, ctx->csi2_port, CAL_CSI2_CTX_CPORT_MASK); + set_field(&val, ctx->cport, CAL_CSI2_CTX_CPORT_MASK); /* * DT type: MIPI CSI-2 Specs * 0x1: All - DT filter is disabled @@ -1036,7 +1037,7 @@ static void pix_proc_config(struct cal_ctx *ctx) set_field(&val, CAL_PIX_PROC_DPCMD_BYPASS, CAL_PIX_PROC_DPCMD_MASK); set_field(&val, CAL_PIX_PROC_DPCME_BYPASS, CAL_PIX_PROC_DPCME_MASK); set_field(&val, pack, CAL_PIX_PROC_PACK_MASK); - set_field(&val, ctx->csi2_port, CAL_PIX_PROC_CPORT_MASK); + set_field(&val, ctx->cport, CAL_PIX_PROC_CPORT_MASK); set_field(&val, 1, CAL_PIX_PROC_EN_MASK); reg_write(ctx->dev, CAL_PIX_PROC(ctx->csi2_port), val); ctx_dbg(3, ctx, "CAL_PIX_PROC(%d) = 0x%08x\n", ctx->csi2_port, @@ -1049,7 +1050,7 @@ static void cal_wr_dma_config(struct cal_ctx *ctx, u32 val; val = reg_read(ctx->dev, CAL_WR_DMA_CTRL(ctx->csi2_port)); - set_field(&val, ctx->csi2_port, CAL_WR_DMA_CTRL_CPORT_MASK); + set_field(&val, ctx->cport, CAL_WR_DMA_CTRL_CPORT_MASK); set_field(&val, height, CAL_WR_DMA_CTRL_YSIZE_MASK); set_field(&val, CAL_WR_DMA_CTRL_DTAG_PIX_DAT, CAL_WR_DMA_CTRL_DTAG_MASK); @@ -2221,6 +2222,7 @@ static struct cal_ctx *cal_create_instance(struct cal_dev *dev, int inst) /* Store the instance id */ ctx->csi2_port = inst + 1; + ctx->cport = inst + 1; ret = of_cal_create_instance(ctx, inst); if (ret) { -- GitLab From 6442241630d1273033ae7f6665c4caec19671633 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 6 Jul 2020 20:35:25 +0200 Subject: [PATCH 0537/1476] media: ti-vpe: cal: Index CSI-2 port starting at 0 The datasheet numbers the CSI-2 PHYs (external to the CAL) starting from 1, but the PPI inputs to the CAL starting from 0. Update the numbering scheme for csi2_port to match the datasheet, which also simplifies the driver by removing the need to constantly subtract 1 from the value. Signed-off-by: Laurent Pinchart Reviewed-by: Tomi Valkeinen Reviewed-by: Benoit Parrot Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/ti-vpe/cal.c | 30 +++++------ drivers/media/platform/ti-vpe/cal_regs.h | 64 ++++++++++++------------ 2 files changed, 47 insertions(+), 47 deletions(-) diff --git a/drivers/media/platform/ti-vpe/cal.c b/drivers/media/platform/ti-vpe/cal.c index 201ef3585a48a..799c25f5aafd3 100644 --- a/drivers/media/platform/ti-vpe/cal.c +++ b/drivers/media/platform/ti-vpe/cal.c @@ -472,7 +472,7 @@ static inline void set_field(u32 *valp, u32 field, u32 mask) static u32 cal_data_get_phy_max_lanes(struct cal_ctx *ctx) { struct cal_dev *dev = ctx->dev; - u32 phy_id = ctx->csi2_port - 1; + u32 phy_id = ctx->csi2_port; return dev->data->csi2_phy_core[phy_id].num_lanes; } @@ -557,7 +557,7 @@ static struct regmap *cal_get_camerarx_regmap(struct cal_dev *dev) static void camerarx_phy_enable(struct cal_ctx *ctx) { struct cal_csi2_phy *phy; - u32 phy_id = ctx->csi2_port - 1; + u32 phy_id = ctx->csi2_port; u32 max_lanes; phy = &ctx->dev->data->csi2_phy_core[phy_id]; @@ -574,7 +574,7 @@ static void camerarx_phy_enable(struct cal_ctx *ctx) static void camerarx_phy_disable(struct cal_ctx *ctx) { struct cal_csi2_phy *phy; - u32 phy_id = ctx->csi2_port - 1; + u32 phy_id = ctx->csi2_port; phy = &ctx->dev->data->csi2_phy_core[phy_id]; regmap_field_write(phy->fields[F_CTRLCLKEN], 0); @@ -712,7 +712,7 @@ static void enable_irqs(struct cal_ctx *ctx) set_field(&val, 1, CAL_HL_IRQ_MASK(ctx->csi2_port)); reg_write(ctx->dev, CAL_HL_IRQENABLE_SET(3), val); /* Todo: Add VC_IRQ and CSI2_COMPLEXIO_IRQ handling */ - reg_write(ctx->dev, CAL_CSI2_VC_IRQENABLE(1), 0xFF000000); + reg_write(ctx->dev, CAL_CSI2_VC_IRQENABLE(0), 0xFF000000); } static void disable_irqs(struct cal_ctx *ctx) @@ -734,7 +734,7 @@ static void disable_irqs(struct cal_ctx *ctx) set_field(&val, 1, CAL_HL_IRQ_MASK(ctx->csi2_port)); reg_write(ctx->dev, CAL_HL_IRQENABLE_CLR(3), val); /* Todo: Add VC_IRQ and CSI2_COMPLEXIO_IRQ handling */ - reg_write(ctx->dev, CAL_CSI2_VC_IRQENABLE(1), 0); + reg_write(ctx->dev, CAL_CSI2_VC_IRQENABLE(0), 0); } static void csi2_cio_power(struct cal_ctx *ctx, bool enable) @@ -894,7 +894,7 @@ static void csi2_wait_for_phy(struct cal_ctx *ctx) csi2_wait_stop_state(ctx); ctx_dbg(1, ctx, "CSI2_%d_REG1 = 0x%08x (Bit(31,28) should be set!)\n", - (ctx->csi2_port - 1), reg_read(ctx->cc, CAL_CSI2_PHY_REG1)); + ctx->csi2_port, reg_read(ctx->cc, CAL_CSI2_PHY_REG1)); } static void csi2_phy_deinit(struct cal_ctx *ctx) @@ -1139,7 +1139,7 @@ static void csi2_phy_config(struct cal_ctx *ctx) set_field(®0, ths_term, CAL_CSI2_PHY_REG0_THS_TERM_MASK); set_field(®0, ths_settle, CAL_CSI2_PHY_REG0_THS_SETTLE_MASK); - ctx_dbg(1, ctx, "CSI2_%d_REG0 = 0x%08x\n", (ctx->csi2_port - 1), reg0); + ctx_dbg(1, ctx, "CSI2_%d_REG0 = 0x%08x\n", ctx->csi2_port, reg0); reg_write(ctx->cc, CAL_CSI2_PHY_REG0, reg0); reg1 = reg_read(ctx->cc, CAL_CSI2_PHY_REG1); @@ -1148,7 +1148,7 @@ static void csi2_phy_config(struct cal_ctx *ctx) set_field(®1, TCLK_MISS, CAL_CSI2_PHY_REG1_CTRLCLK_DIV_FACTOR_MASK); set_field(®1, TCLK_SETTLE, CAL_CSI2_PHY_REG1_TCLK_SETTLE_MASK); - ctx_dbg(1, ctx, "CSI2_%d_REG1 = 0x%08x\n", (ctx->csi2_port - 1), reg1); + ctx_dbg(1, ctx, "CSI2_%d_REG1 = 0x%08x\n", ctx->csi2_port, reg1); reg_write(ctx->cc, CAL_CSI2_PHY_REG1, reg1); } @@ -1217,7 +1217,7 @@ static irqreturn_t cal_irq(int irq_cal, void *data) if (irqst1 & CAL_HL_IRQ_OCPO_ERR_MASK) dev_err_ratelimited(&dev->pdev->dev, "OCPO ERROR\n"); - for (i = 1; i <= 2; ++i) { + for (i = 0; i < 2; ++i) { if (irqst1 & CAL_HL_IRQ_CIO_MASK(i)) { u32 cio_stat = reg_read(dev, CAL_CSI2_COMPLEXIO_IRQSTATUS(i)); @@ -1239,9 +1239,9 @@ static irqreturn_t cal_irq(int irq_cal, void *data) /* Clear Interrupt status */ reg_write(dev, CAL_HL_IRQSTATUS(2), irqst2); - for (i = 1; i <= 2; ++i) { + for (i = 0; i < 2; ++i) { if (isportirqset(irqst2, i)) { - ctx = dev->ctx[i - 1]; + ctx = dev->ctx[i]; spin_lock(&ctx->slock); ctx->dma_act = false; @@ -1262,9 +1262,9 @@ static irqreturn_t cal_irq(int irq_cal, void *data) /* Clear Interrupt status */ reg_write(dev, CAL_HL_IRQSTATUS(3), irqst3); - for (i = 1; i <= 2; ++i) { + for (i = 0; i < 2; ++i) { if (isportirqset(irqst3, i)) { - ctx = dev->ctx[i - 1]; + ctx = dev->ctx[i]; dma_q = &ctx->vidq; spin_lock(&ctx->slock); @@ -2221,8 +2221,8 @@ static struct cal_ctx *cal_create_instance(struct cal_dev *dev, int inst) ctx->cc = dev->cc[inst]; /* Store the instance id */ - ctx->csi2_port = inst + 1; - ctx->cport = inst + 1; + ctx->csi2_port = inst; + ctx->cport = inst; ret = of_cal_create_instance(ctx, inst); if (ret) { diff --git a/drivers/media/platform/ti-vpe/cal_regs.h b/drivers/media/platform/ti-vpe/cal_regs.h index ac54a2fe7bb62..56c8ccd0e75f9 100644 --- a/drivers/media/platform/ti-vpe/cal_regs.h +++ b/drivers/media/platform/ti-vpe/cal_regs.h @@ -46,7 +46,7 @@ #define CAL_HL_IRQSTATUS(m) (0x24U + ((m-1) * 0x10U)) #define CAL_HL_IRQENABLE_SET(m) (0x28U + ((m-1) * 0x10U)) #define CAL_HL_IRQENABLE_CLR(m) (0x2cU + ((m-1) * 0x10U)) -#define CAL_PIX_PROC(m) (0xc0U + ((m-1) * 0x4U)) +#define CAL_PIX_PROC(m) (0xc0U + (m) * 0x4U) #define CAL_CTRL 0x100 #define CAL_CTRL1 0x104 #define CAL_LINE_NUMBER_EVT 0x108 @@ -62,34 +62,34 @@ #define CAL_RD_DMA_INIT_ADDR 0x154 #define CAL_RD_DMA_INIT_OFST 0x168 #define CAL_RD_DMA_CTRL2 0x16c -#define CAL_WR_DMA_CTRL(m) (0x200U + ((m-1) * 0x10U)) -#define CAL_WR_DMA_ADDR(m) (0x204U + ((m-1) * 0x10U)) -#define CAL_WR_DMA_OFST(m) (0x208U + ((m-1) * 0x10U)) -#define CAL_WR_DMA_XSIZE(m) (0x20cU + ((m-1) * 0x10U)) -#define CAL_CSI2_PPI_CTRL(m) (0x300U + ((m-1) * 0x80U)) -#define CAL_CSI2_COMPLEXIO_CFG(m) (0x304U + ((m-1) * 0x80U)) -#define CAL_CSI2_COMPLEXIO_IRQSTATUS(m) (0x308U + ((m-1) * 0x80U)) -#define CAL_CSI2_SHORT_PACKET(m) (0x30cU + ((m-1) * 0x80U)) -#define CAL_CSI2_COMPLEXIO_IRQENABLE(m) (0x310U + ((m-1) * 0x80U)) -#define CAL_CSI2_TIMING(m) (0x314U + ((m-1) * 0x80U)) -#define CAL_CSI2_VC_IRQENABLE(m) (0x318U + ((m-1) * 0x80U)) -#define CAL_CSI2_VC_IRQSTATUS(m) (0x328U + ((m-1) * 0x80U)) -#define CAL_CSI2_CTX0(m) (0x330U + ((m-1) * 0x80U)) -#define CAL_CSI2_CTX1(m) (0x334U + ((m-1) * 0x80U)) -#define CAL_CSI2_CTX2(m) (0x338U + ((m-1) * 0x80U)) -#define CAL_CSI2_CTX3(m) (0x33cU + ((m-1) * 0x80U)) -#define CAL_CSI2_CTX4(m) (0x340U + ((m-1) * 0x80U)) -#define CAL_CSI2_CTX5(m) (0x344U + ((m-1) * 0x80U)) -#define CAL_CSI2_CTX6(m) (0x348U + ((m-1) * 0x80U)) -#define CAL_CSI2_CTX7(m) (0x34cU + ((m-1) * 0x80U)) -#define CAL_CSI2_STATUS0(m) (0x350U + ((m-1) * 0x80U)) -#define CAL_CSI2_STATUS1(m) (0x354U + ((m-1) * 0x80U)) -#define CAL_CSI2_STATUS2(m) (0x358U + ((m-1) * 0x80U)) -#define CAL_CSI2_STATUS3(m) (0x35cU + ((m-1) * 0x80U)) -#define CAL_CSI2_STATUS4(m) (0x360U + ((m-1) * 0x80U)) -#define CAL_CSI2_STATUS5(m) (0x364U + ((m-1) * 0x80U)) -#define CAL_CSI2_STATUS6(m) (0x368U + ((m-1) * 0x80U)) -#define CAL_CSI2_STATUS7(m) (0x36cU + ((m-1) * 0x80U)) +#define CAL_WR_DMA_CTRL(m) (0x200U + (m) * 0x10U) +#define CAL_WR_DMA_ADDR(m) (0x204U + (m) * 0x10U) +#define CAL_WR_DMA_OFST(m) (0x208U + (m) * 0x10U) +#define CAL_WR_DMA_XSIZE(m) (0x20cU + (m) * 0x10U) +#define CAL_CSI2_PPI_CTRL(m) (0x300U + (m) * 0x80U) +#define CAL_CSI2_COMPLEXIO_CFG(m) (0x304U + (m) * 0x80U) +#define CAL_CSI2_COMPLEXIO_IRQSTATUS(m) (0x308U + (m) * 0x80U) +#define CAL_CSI2_SHORT_PACKET(m) (0x30cU + (m) * 0x80U) +#define CAL_CSI2_COMPLEXIO_IRQENABLE(m) (0x310U + (m) * 0x80U) +#define CAL_CSI2_TIMING(m) (0x314U + (m) * 0x80U) +#define CAL_CSI2_VC_IRQENABLE(m) (0x318U + (m) * 0x80U) +#define CAL_CSI2_VC_IRQSTATUS(m) (0x328U + (m) * 0x80U) +#define CAL_CSI2_CTX0(m) (0x330U + (m) * 0x80U) +#define CAL_CSI2_CTX1(m) (0x334U + (m) * 0x80U) +#define CAL_CSI2_CTX2(m) (0x338U + (m) * 0x80U) +#define CAL_CSI2_CTX3(m) (0x33cU + (m) * 0x80U) +#define CAL_CSI2_CTX4(m) (0x340U + (m) * 0x80U) +#define CAL_CSI2_CTX5(m) (0x344U + (m) * 0x80U) +#define CAL_CSI2_CTX6(m) (0x348U + (m) * 0x80U) +#define CAL_CSI2_CTX7(m) (0x34cU + (m) * 0x80U) +#define CAL_CSI2_STATUS0(m) (0x350U + (m) * 0x80U) +#define CAL_CSI2_STATUS1(m) (0x354U + (m) * 0x80U) +#define CAL_CSI2_STATUS2(m) (0x358U + (m) * 0x80U) +#define CAL_CSI2_STATUS3(m) (0x35cU + (m) * 0x80U) +#define CAL_CSI2_STATUS4(m) (0x360U + (m) * 0x80U) +#define CAL_CSI2_STATUS5(m) (0x364U + (m) * 0x80U) +#define CAL_CSI2_STATUS6(m) (0x368U + (m) * 0x80U) +#define CAL_CSI2_STATUS7(m) (0x36cU + (m) * 0x80U) /* CAL CSI2 PHY register offsets */ #define CAL_CSI2_PHY_REG0 0x000 @@ -141,12 +141,12 @@ #define CAL_HL_IRQ_EOI_LINE_NUMBER_READ0 0 #define CAL_HL_IRQ_EOI_LINE_NUMBER_EOI0 0 -#define CAL_HL_IRQ_MASK(m) BIT((m) - 1) +#define CAL_HL_IRQ_MASK(m) BIT(m) #define CAL_HL_IRQ_OCPO_ERR_MASK BIT(6) -#define CAL_HL_IRQ_CIO_MASK(i) BIT(16 + ((i) - 1) * 8) -#define CAL_HL_IRQ_VC_MASK(i) BIT(17 + ((i) - 1) * 8) +#define CAL_HL_IRQ_CIO_MASK(i) BIT(16 + (i) * 8) +#define CAL_HL_IRQ_VC_MASK(i) BIT(17 + (i) * 8) #define CAL_PIX_PROC_EN_MASK BIT(0) #define CAL_PIX_PROC_EXTRACT_MASK GENMASK(4, 1) -- GitLab From 255502a723b5fecf01e7f02d19ba49d507c673d5 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 6 Jul 2020 20:35:26 +0200 Subject: [PATCH 0538/1476] media: ti-vpe: cal: Index IRQ registersstarting at 0 The datasheet numbers the IRQ registers starting from 0. Update the numbering scheme to match the datasheet. Signed-off-by: Laurent Pinchart Reviewed-by: Tomi Valkeinen Reviewed-by: Benoit Parrot Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/ti-vpe/cal.c | 38 ++++++++++++------------ drivers/media/platform/ti-vpe/cal_regs.h | 8 ++--- 2 files changed, 23 insertions(+), 23 deletions(-) diff --git a/drivers/media/platform/ti-vpe/cal.c b/drivers/media/platform/ti-vpe/cal.c index 799c25f5aafd3..b04d8cb869771 100644 --- a/drivers/media/platform/ti-vpe/cal.c +++ b/drivers/media/platform/ti-vpe/cal.c @@ -695,22 +695,22 @@ static void enable_irqs(struct cal_ctx *ctx) CAL_CSI2_COMPLEXIO_IRQ_ECC_NO_CORRECTION_MASK; /* Enable CIO error irqs */ - reg_write(ctx->dev, CAL_HL_IRQENABLE_SET(1), + reg_write(ctx->dev, CAL_HL_IRQENABLE_SET(0), CAL_HL_IRQ_CIO_MASK(ctx->csi2_port)); reg_write(ctx->dev, CAL_CSI2_COMPLEXIO_IRQENABLE(ctx->csi2_port), cio_err_mask); /* Always enable OCPO error */ - reg_write(ctx->dev, CAL_HL_IRQENABLE_SET(1), CAL_HL_IRQ_OCPO_ERR_MASK); + reg_write(ctx->dev, CAL_HL_IRQENABLE_SET(0), CAL_HL_IRQ_OCPO_ERR_MASK); /* Enable IRQ_WDMA_END 0/1 */ val = 0; set_field(&val, 1, CAL_HL_IRQ_MASK(ctx->csi2_port)); - reg_write(ctx->dev, CAL_HL_IRQENABLE_SET(2), val); + reg_write(ctx->dev, CAL_HL_IRQENABLE_SET(1), val); /* Enable IRQ_WDMA_START 0/1 */ val = 0; set_field(&val, 1, CAL_HL_IRQ_MASK(ctx->csi2_port)); - reg_write(ctx->dev, CAL_HL_IRQENABLE_SET(3), val); + reg_write(ctx->dev, CAL_HL_IRQENABLE_SET(2), val); /* Todo: Add VC_IRQ and CSI2_COMPLEXIO_IRQ handling */ reg_write(ctx->dev, CAL_CSI2_VC_IRQENABLE(0), 0xFF000000); } @@ -720,7 +720,7 @@ static void disable_irqs(struct cal_ctx *ctx) u32 val; /* Disable CIO error irqs */ - reg_write(ctx->dev, CAL_HL_IRQENABLE_CLR(1), + reg_write(ctx->dev, CAL_HL_IRQENABLE_CLR(0), CAL_HL_IRQ_CIO_MASK(ctx->csi2_port)); reg_write(ctx->dev, CAL_CSI2_COMPLEXIO_IRQENABLE(ctx->csi2_port), 0); @@ -728,11 +728,11 @@ static void disable_irqs(struct cal_ctx *ctx) /* Disable IRQ_WDMA_END 0/1 */ val = 0; set_field(&val, 1, CAL_HL_IRQ_MASK(ctx->csi2_port)); - reg_write(ctx->dev, CAL_HL_IRQENABLE_CLR(2), val); + reg_write(ctx->dev, CAL_HL_IRQENABLE_CLR(1), val); /* Disable IRQ_WDMA_START 0/1 */ val = 0; set_field(&val, 1, CAL_HL_IRQ_MASK(ctx->csi2_port)); - reg_write(ctx->dev, CAL_HL_IRQENABLE_CLR(3), val); + reg_write(ctx->dev, CAL_HL_IRQENABLE_CLR(2), val); /* Todo: Add VC_IRQ and CSI2_COMPLEXIO_IRQ handling */ reg_write(ctx->dev, CAL_CSI2_VC_IRQENABLE(0), 0); } @@ -1206,13 +1206,13 @@ static irqreturn_t cal_irq(int irq_cal, void *data) struct cal_dev *dev = (struct cal_dev *)data; struct cal_ctx *ctx; struct cal_dmaqueue *dma_q; - u32 irqst1, irqst2, irqst3; + u32 irqst0, irqst1, irqst2; - irqst1 = reg_read(dev, CAL_HL_IRQSTATUS(1)); - if (irqst1) { + irqst0 = reg_read(dev, CAL_HL_IRQSTATUS(0)); + if (irqst0) { int i; - reg_write(dev, CAL_HL_IRQSTATUS(1), irqst1); + reg_write(dev, CAL_HL_IRQSTATUS(0), irqst0); if (irqst1 & CAL_HL_IRQ_OCPO_ERR_MASK) dev_err_ratelimited(&dev->pdev->dev, "OCPO ERROR\n"); @@ -1232,15 +1232,15 @@ static irqreturn_t cal_irq(int irq_cal, void *data) } /* Check which DMA just finished */ - irqst2 = reg_read(dev, CAL_HL_IRQSTATUS(2)); - if (irqst2) { + irqst1 = reg_read(dev, CAL_HL_IRQSTATUS(1)); + if (irqst1) { int i; /* Clear Interrupt status */ - reg_write(dev, CAL_HL_IRQSTATUS(2), irqst2); + reg_write(dev, CAL_HL_IRQSTATUS(1), irqst1); for (i = 0; i < 2; ++i) { - if (isportirqset(irqst2, i)) { + if (isportirqset(irqst1, i)) { ctx = dev->ctx[i]; spin_lock(&ctx->slock); @@ -1255,15 +1255,15 @@ static irqreturn_t cal_irq(int irq_cal, void *data) } /* Check which DMA just started */ - irqst3 = reg_read(dev, CAL_HL_IRQSTATUS(3)); - if (irqst3) { + irqst2 = reg_read(dev, CAL_HL_IRQSTATUS(2)); + if (irqst2) { int i; /* Clear Interrupt status */ - reg_write(dev, CAL_HL_IRQSTATUS(3), irqst3); + reg_write(dev, CAL_HL_IRQSTATUS(2), irqst2); for (i = 0; i < 2; ++i) { - if (isportirqset(irqst3, i)) { + if (isportirqset(irqst2, i)) { ctx = dev->ctx[i]; dma_q = &ctx->vidq; diff --git a/drivers/media/platform/ti-vpe/cal_regs.h b/drivers/media/platform/ti-vpe/cal_regs.h index 56c8ccd0e75f9..b551e67d5d419 100644 --- a/drivers/media/platform/ti-vpe/cal_regs.h +++ b/drivers/media/platform/ti-vpe/cal_regs.h @@ -42,10 +42,10 @@ #define CAL_HL_HWINFO 0x0004 #define CAL_HL_SYSCONFIG 0x0010 #define CAL_HL_IRQ_EOI 0x001c -#define CAL_HL_IRQSTATUS_RAW(m) (0x20U + ((m-1) * 0x10U)) -#define CAL_HL_IRQSTATUS(m) (0x24U + ((m-1) * 0x10U)) -#define CAL_HL_IRQENABLE_SET(m) (0x28U + ((m-1) * 0x10U)) -#define CAL_HL_IRQENABLE_CLR(m) (0x2cU + ((m-1) * 0x10U)) +#define CAL_HL_IRQSTATUS_RAW(m) (0x20U + (m) * 0x10U) +#define CAL_HL_IRQSTATUS(m) (0x24U + (m) * 0x10U) +#define CAL_HL_IRQENABLE_SET(m) (0x28U + (m) * 0x10U) +#define CAL_HL_IRQENABLE_CLR(m) (0x2cU + (m) * 0x10U) #define CAL_PIX_PROC(m) (0xc0U + (m) * 0x4U) #define CAL_CTRL 0x100 #define CAL_CTRL1 0x104 -- GitLab From 47bed3fbe8a0ced333fda22e4a0a29342782c793 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 6 Jul 2020 20:35:27 +0200 Subject: [PATCH 0539/1476] media: ti-vpe: cal: Merge all status variables in IRQ handler The cal_irq() function reads three IRQ status registers and stores their values in three different variables. As each value is processed right after reading the corresponding register, a single variable is enough. Signed-off-by: Laurent Pinchart Reviewed-by: Tomi Valkeinen Reviewed-by: Benoit Parrot Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/ti-vpe/cal.c | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/drivers/media/platform/ti-vpe/cal.c b/drivers/media/platform/ti-vpe/cal.c index b04d8cb869771..2aa28af7cad33 100644 --- a/drivers/media/platform/ti-vpe/cal.c +++ b/drivers/media/platform/ti-vpe/cal.c @@ -1206,19 +1206,19 @@ static irqreturn_t cal_irq(int irq_cal, void *data) struct cal_dev *dev = (struct cal_dev *)data; struct cal_ctx *ctx; struct cal_dmaqueue *dma_q; - u32 irqst0, irqst1, irqst2; + u32 status; - irqst0 = reg_read(dev, CAL_HL_IRQSTATUS(0)); - if (irqst0) { + status = reg_read(dev, CAL_HL_IRQSTATUS(0)); + if (status) { int i; - reg_write(dev, CAL_HL_IRQSTATUS(0), irqst0); + reg_write(dev, CAL_HL_IRQSTATUS(0), status); - if (irqst1 & CAL_HL_IRQ_OCPO_ERR_MASK) + if (status & CAL_HL_IRQ_OCPO_ERR_MASK) dev_err_ratelimited(&dev->pdev->dev, "OCPO ERROR\n"); for (i = 0; i < 2; ++i) { - if (irqst1 & CAL_HL_IRQ_CIO_MASK(i)) { + if (status & CAL_HL_IRQ_CIO_MASK(i)) { u32 cio_stat = reg_read(dev, CAL_CSI2_COMPLEXIO_IRQSTATUS(i)); @@ -1232,15 +1232,15 @@ static irqreturn_t cal_irq(int irq_cal, void *data) } /* Check which DMA just finished */ - irqst1 = reg_read(dev, CAL_HL_IRQSTATUS(1)); - if (irqst1) { + status = reg_read(dev, CAL_HL_IRQSTATUS(1)); + if (status) { int i; /* Clear Interrupt status */ - reg_write(dev, CAL_HL_IRQSTATUS(1), irqst1); + reg_write(dev, CAL_HL_IRQSTATUS(1), status); for (i = 0; i < 2; ++i) { - if (isportirqset(irqst1, i)) { + if (isportirqset(status, i)) { ctx = dev->ctx[i]; spin_lock(&ctx->slock); @@ -1255,15 +1255,15 @@ static irqreturn_t cal_irq(int irq_cal, void *data) } /* Check which DMA just started */ - irqst2 = reg_read(dev, CAL_HL_IRQSTATUS(2)); - if (irqst2) { + status = reg_read(dev, CAL_HL_IRQSTATUS(2)); + if (status) { int i; /* Clear Interrupt status */ - reg_write(dev, CAL_HL_IRQSTATUS(2), irqst2); + reg_write(dev, CAL_HL_IRQSTATUS(2), status); for (i = 0; i < 2; ++i) { - if (isportirqset(irqst2, i)) { + if (isportirqset(status, i)) { ctx = dev->ctx[i]; dma_q = &ctx->vidq; -- GitLab From d2b084eb1eecc130bc4c1e7defd45467f5d2077a Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 6 Jul 2020 20:35:28 +0200 Subject: [PATCH 0540/1476] media: ti-vpe: cal: Inline CAL_VERSION macro in its only user The CAL_VERSION macro is used once only, in MODULE_VERSION(). This doesn't improve readability, inline it. Signed-off-by: Laurent Pinchart Reviewed-by: Tomi Valkeinen Reviewed-by: Benoit Parrot Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/ti-vpe/cal.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/media/platform/ti-vpe/cal.c b/drivers/media/platform/ti-vpe/cal.c index 2aa28af7cad33..9b5ad48538c15 100644 --- a/drivers/media/platform/ti-vpe/cal.c +++ b/drivers/media/platform/ti-vpe/cal.c @@ -39,12 +39,10 @@ #define MAX_WIDTH_BYTES (8192 * 8) #define MAX_HEIGHT_LINES 16383 -#define CAL_VERSION "0.1.0" - MODULE_DESCRIPTION("TI CAL driver"); MODULE_AUTHOR("Benoit Parrot, "); MODULE_LICENSE("GPL v2"); -MODULE_VERSION(CAL_VERSION); +MODULE_VERSION("0.1.0"); static unsigned video_nr = -1; module_param(video_nr, uint, 0644); -- GitLab From 50797fb30b95b04332b9f3a35c9f7ec2a09692a1 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 6 Jul 2020 20:35:29 +0200 Subject: [PATCH 0541/1476] media: ti-vpe: cal: Turn reg_(read|write)_field() into inline functions Turn the reg_(read|write)_field() macros into inline functions for additional type safety. Use the FIELD_GET() and FIELD_PREP() macros internally instead of reinventing the wheel. Signed-off-by: Laurent Pinchart Reviewed-by: Tomi Valkeinen Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/ti-vpe/cal.c | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/drivers/media/platform/ti-vpe/cal.c b/drivers/media/platform/ti-vpe/cal.c index 9b5ad48538c15..278477cf0ae92 100644 --- a/drivers/media/platform/ti-vpe/cal.c +++ b/drivers/media/platform/ti-vpe/cal.c @@ -6,6 +6,7 @@ * Benoit Parrot, */ +#include #include #include #include @@ -76,13 +77,6 @@ static const struct v4l2_fract #define reg_read(dev, offset) ioread32(dev->base + offset) #define reg_write(dev, offset, val) iowrite32(val, dev->base + offset) -#define reg_read_field(dev, offset, mask) get_field(reg_read(dev, offset), \ - mask) -#define reg_write_field(dev, offset, field, mask) { \ - u32 val = reg_read(dev, offset); \ - set_field(&val, field, mask); \ - reg_write(dev, offset, val); } - /* ------------------------------------------------------------------ * Basic structures * ------------------------------------------------------------------ @@ -418,6 +412,21 @@ struct cal_ctx { bool dma_act; }; +static inline u32 reg_read_field(struct cal_dev *dev, u32 offset, u32 mask) +{ + return FIELD_GET(mask, reg_read(dev, offset)); +} + +static inline void reg_write_field(struct cal_dev *dev, u32 offset, u32 value, + u32 mask) +{ + u32 val = reg_read(dev, offset); + + val &= ~mask; + val |= FIELD_PREP(mask, value); + reg_write(dev, offset, val); +} + static const struct cal_fmt *find_format_by_pix(struct cal_ctx *ctx, u32 pixelformat) { @@ -453,11 +462,6 @@ static inline struct cal_ctx *notifier_to_ctx(struct v4l2_async_notifier *n) return container_of(n, struct cal_ctx, notifier); } -static inline int get_field(u32 value, u32 mask) -{ - return (value & mask) >> __ffs(mask); -} - static inline void set_field(u32 *valp, u32 field, u32 mask) { u32 val = *valp; -- GitLab From 37166b2ff964714efabefe4e6809d4af2d10a3e9 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 6 Jul 2020 20:35:30 +0200 Subject: [PATCH 0542/1476] media: ti-vpe: cal: Make cal_formats array const The static cal_formats array contains const data, make it const. This requires turning a few cal_fmt pointers into const pointers. Signed-off-by: Laurent Pinchart Reviewed-by: Tomi Valkeinen Reviewed-by: Benoit Parrot Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/ti-vpe/cal.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/drivers/media/platform/ti-vpe/cal.c b/drivers/media/platform/ti-vpe/cal.c index 278477cf0ae92..3668c4d445fe0 100644 --- a/drivers/media/platform/ti-vpe/cal.c +++ b/drivers/media/platform/ti-vpe/cal.c @@ -89,7 +89,7 @@ struct cal_fmt { u8 bpp; }; -static struct cal_fmt cal_formats[] = { +static const struct cal_fmt cal_formats[] = { { .fourcc = V4L2_PIX_FMT_YUYV, .code = MEDIA_BUS_FMT_YUYV8_2X8, @@ -392,7 +392,7 @@ struct cal_ctx { struct v4l2_mbus_framefmt m_fmt; /* Current subdev enumerated format */ - struct cal_fmt *active_fmt[ARRAY_SIZE(cal_formats)]; + const struct cal_fmt *active_fmt[ARRAY_SIZE(cal_formats)]; int num_active_fmt; struct v4l2_fract timeperframe; @@ -1932,7 +1932,6 @@ static int cal_async_bound(struct v4l2_async_notifier *notifier, /* Enumerate sub device formats and enable all matching local formats */ ctx->num_active_fmt = 0; for (j = 0, i = 0; ret != -EINVAL; ++j) { - struct cal_fmt *fmt; memset(&mbus_code, 0, sizeof(mbus_code)); mbus_code.index = j; @@ -1947,7 +1946,7 @@ static int cal_async_bound(struct v4l2_async_notifier *notifier, subdev->name, mbus_code.code, j); for (k = 0; k < ARRAY_SIZE(cal_formats); k++) { - fmt = &cal_formats[k]; + const struct cal_fmt *fmt = &cal_formats[k]; if (mbus_code.code == fmt->code) { ctx->active_fmt[i] = fmt; -- GitLab From 6ff1d7260693cad1e06c24f99dc2f4d0cebb2589 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 6 Jul 2020 20:35:31 +0200 Subject: [PATCH 0543/1476] media: ti-vpe: cal: Remove needless variable initialization A few local variables are needlessly initialized. Fix them. Signed-off-by: Laurent Pinchart Reviewed-by: Tomi Valkeinen Reviewed-by: Benoit Parrot Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/ti-vpe/cal.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/drivers/media/platform/ti-vpe/cal.c b/drivers/media/platform/ti-vpe/cal.c index 3668c4d445fe0..d0390b23426d5 100644 --- a/drivers/media/platform/ti-vpe/cal.c +++ b/drivers/media/platform/ti-vpe/cal.c @@ -616,8 +616,8 @@ static struct cc_data *cc_create(struct cal_dev *dev, unsigned int core) */ static void cal_get_hwinfo(struct cal_dev *dev) { - u32 revision = 0; - u32 hwinfo = 0; + u32 revision; + u32 hwinfo; revision = reg_read(dev, CAL_HL_REVISION); cal_dbg(3, dev, "CAL_HL_REVISION = 0x%08x (expecting 0x40000200)\n", @@ -1302,7 +1302,7 @@ static int cal_enum_fmt_vid_cap(struct file *file, void *priv, struct v4l2_fmtdesc *f) { struct cal_ctx *ctx = video_drvdata(file); - const struct cal_fmt *fmt = NULL; + const struct cal_fmt *fmt; if (f->index >= ctx->num_active_fmt) return -EINVAL; @@ -1655,7 +1655,7 @@ static void cal_buffer_queue(struct vb2_buffer *vb) struct cal_buffer *buf = container_of(vb, struct cal_buffer, vb.vb2_buf); struct cal_dmaqueue *vidq = &ctx->vidq; - unsigned long flags = 0; + unsigned long flags; /* recheck locking */ spin_lock_irqsave(&ctx->slock, flags); @@ -1668,7 +1668,7 @@ static int cal_start_streaming(struct vb2_queue *vq, unsigned int count) struct cal_ctx *ctx = vb2_get_drv_priv(vq); struct cal_dmaqueue *dma_q = &ctx->vidq; struct cal_buffer *buf, *tmp; - unsigned long addr = 0; + unsigned long addr; unsigned long flags; int ret; @@ -2008,7 +2008,7 @@ static struct device_node * of_get_next_port(const struct device_node *parent, struct device_node *prev) { - struct device_node *port = NULL; + struct device_node *port; if (!parent) return NULL; @@ -2052,7 +2052,7 @@ static struct device_node * of_get_next_endpoint(const struct device_node *parent, struct device_node *prev) { - struct device_node *ep = NULL; + struct device_node *ep; if (!parent) return NULL; @@ -2266,8 +2266,8 @@ static int cal_probe(struct platform_device *pdev) struct cal_dev *dev; struct cal_ctx *ctx; struct device_node *parent = pdev->dev.of_node; - struct regmap *syscon_camerrx = NULL; - u32 syscon_camerrx_offset = 0; + struct regmap *syscon_camerrx; + u32 syscon_camerrx_offset; int ret; int irq; int i; -- GitLab From d64463598f50b882a92e8b06ce4c8c338f818a95 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 6 Jul 2020 20:35:32 +0200 Subject: [PATCH 0544/1476] media: ti-vpe: cal: Remove needless casts Casting from a void pointer to a struct pointer doesn't require a cast. Remove them. Signed-off-by: Laurent Pinchart Reviewed-by: Tomi Valkeinen Reviewed-by: Benoit Parrot Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/ti-vpe/cal.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/media/platform/ti-vpe/cal.c b/drivers/media/platform/ti-vpe/cal.c index d0390b23426d5..ee6f700ad1904 100644 --- a/drivers/media/platform/ti-vpe/cal.c +++ b/drivers/media/platform/ti-vpe/cal.c @@ -1205,7 +1205,7 @@ static inline void cal_process_buffer_complete(struct cal_ctx *ctx) static irqreturn_t cal_irq(int irq_cal, void *data) { - struct cal_dev *dev = (struct cal_dev *)data; + struct cal_dev *dev = data; struct cal_ctx *ctx; struct cal_dmaqueue *dma_q; u32 status; @@ -2404,8 +2404,7 @@ runtime_disable: static int cal_remove(struct platform_device *pdev) { - struct cal_dev *dev = - (struct cal_dev *)platform_get_drvdata(pdev); + struct cal_dev *dev = platform_get_drvdata(pdev); struct cal_ctx *ctx; int i; -- GitLab From fcc6d6f71d1f2363c557d8638174b14239c58021 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 6 Jul 2020 20:35:33 +0200 Subject: [PATCH 0545/1476] media: ti-vpe: cal: Turn boolean variable into bool The found_port variable contains a boolean value, make it a bool. Signed-off-by: Laurent Pinchart Reviewed-by: Tomi Valkeinen Reviewed-by: Benoit Parrot Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/ti-vpe/cal.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/media/platform/ti-vpe/cal.c b/drivers/media/platform/ti-vpe/cal.c index ee6f700ad1904..edb1034f3b04e 100644 --- a/drivers/media/platform/ti-vpe/cal.c +++ b/drivers/media/platform/ti-vpe/cal.c @@ -2074,7 +2074,8 @@ static int of_cal_create_instance(struct cal_ctx *ctx, int inst) struct v4l2_fwnode_endpoint *endpoint; struct v4l2_async_subdev *asd; u32 regval = 0; - int ret, index, found_port = 0, lane; + int ret, index, lane; + bool found_port = false; parent = pdev->dev.of_node; @@ -2099,7 +2100,7 @@ static int of_cal_create_instance(struct cal_ctx *ctx, int inst) ctx_dbg(3, ctx, "port:%d inst:%d :%d\n", index, inst, regval); if ((regval == inst) && (index == inst)) { - found_port = 1; + found_port = true; break; } } -- GitLab From 6341aa2ee4dd955bd6cd11ddebd8cb5ee6e99a44 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 6 Jul 2020 20:35:34 +0200 Subject: [PATCH 0546/1476] media: ti-vpe: cal: Make loop indices unsigned where applicable Many loop indices only take positive values. Make them unsigned. Signed-off-by: Laurent Pinchart Reviewed-by: Tomi Valkeinen Reviewed-by: Benoit Parrot Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/ti-vpe/cal.c | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/drivers/media/platform/ti-vpe/cal.c b/drivers/media/platform/ti-vpe/cal.c index edb1034f3b04e..579f823451f4f 100644 --- a/drivers/media/platform/ti-vpe/cal.c +++ b/drivers/media/platform/ti-vpe/cal.c @@ -488,7 +488,7 @@ static int cal_camerarx_regmap_init(struct cal_dev *dev) { struct reg_field *field; struct cal_csi2_phy *phy; - int i, j; + unsigned int i, j; if (!dev->data) return -EINVAL; @@ -901,7 +901,7 @@ static void csi2_wait_for_phy(struct cal_ctx *ctx) static void csi2_phy_deinit(struct cal_ctx *ctx) { - int i; + unsigned int i; csi2_cio_power(ctx, false); @@ -1212,7 +1212,7 @@ static irqreturn_t cal_irq(int irq_cal, void *data) status = reg_read(dev, CAL_HL_IRQSTATUS(0)); if (status) { - int i; + unsigned int i; reg_write(dev, CAL_HL_IRQSTATUS(0), status); @@ -1225,7 +1225,7 @@ static irqreturn_t cal_irq(int irq_cal, void *data) CAL_CSI2_COMPLEXIO_IRQSTATUS(i)); dev_err_ratelimited(&dev->pdev->dev, - "CIO%d error: %#08x\n", i, cio_stat); + "CIO%u error: %#08x\n", i, cio_stat); reg_write(dev, CAL_CSI2_COMPLEXIO_IRQSTATUS(i), cio_stat); @@ -1236,7 +1236,7 @@ static irqreturn_t cal_irq(int irq_cal, void *data) /* Check which DMA just finished */ status = reg_read(dev, CAL_HL_IRQSTATUS(1)); if (status) { - int i; + unsigned int i; /* Clear Interrupt status */ reg_write(dev, CAL_HL_IRQSTATUS(1), status); @@ -1259,7 +1259,7 @@ static irqreturn_t cal_irq(int irq_cal, void *data) /* Check which DMA just started */ status = reg_read(dev, CAL_HL_IRQSTATUS(2)); if (status) { - int i; + unsigned int i; /* Clear Interrupt status */ reg_write(dev, CAL_HL_IRQSTATUS(2), status); @@ -1917,8 +1917,8 @@ static int cal_async_bound(struct v4l2_async_notifier *notifier, { struct cal_ctx *ctx = notifier_to_ctx(notifier); struct v4l2_subdev_mbus_code_enum mbus_code; + unsigned int i, j, k; int ret = 0; - int i, j, k; if (ctx->sensor) { ctx_info(ctx, "Rejecting subdev %s (Already set!!)", @@ -1942,7 +1942,7 @@ static int cal_async_bound(struct v4l2_async_notifier *notifier, continue; ctx_dbg(2, ctx, - "subdev %s: code: %04x idx: %d\n", + "subdev %s: code: %04x idx: %u\n", subdev->name, mbus_code.code, j); for (k = 0; k < ARRAY_SIZE(cal_formats); k++) { @@ -1951,7 +1951,7 @@ static int cal_async_bound(struct v4l2_async_notifier *notifier, if (mbus_code.code == fmt->code) { ctx->active_fmt[i] = fmt; ctx_dbg(2, ctx, - "matched fourcc: %s: code: %04x idx: %d\n", + "matched fourcc: %s: code: %04x idx: %u\n", fourcc_to_str(fmt->fourcc), fmt->code, i); ctx->num_active_fmt = ++i; @@ -2269,9 +2269,9 @@ static int cal_probe(struct platform_device *pdev) struct device_node *parent = pdev->dev.of_node; struct regmap *syscon_camerrx; u32 syscon_camerrx_offset; + unsigned int i; int ret; int irq; - int i; dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL); if (!dev) @@ -2407,7 +2407,7 @@ static int cal_remove(struct platform_device *pdev) { struct cal_dev *dev = platform_get_drvdata(pdev); struct cal_ctx *ctx; - int i; + unsigned int i; cal_dbg(1, dev, "Removing %s\n", CAL_MODULE_NAME); -- GitLab From 5b4426e33b85432e7cae803e622c2d383e313f66 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 6 Jul 2020 20:35:35 +0200 Subject: [PATCH 0547/1476] media: ti-vpe: cal: Embed base_fields array in struct cal_csi2_phy The base_fields field of cal_csi2_phy points to a fixed-size array of struct reg_field. This level of indirection isn't needed, embed the array directly in struct cal_csi2_phy. Signed-off-by: Laurent Pinchart Reviewed-by: Tomi Valkeinen Reviewed-by: Benoit Parrot Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/ti-vpe/cal.c | 70 +++++++++++++---------------- 1 file changed, 30 insertions(+), 40 deletions(-) diff --git a/drivers/media/platform/ti-vpe/cal.c b/drivers/media/platform/ti-vpe/cal.c index 579f823451f4f..1f15481ba5a26 100644 --- a/drivers/media/platform/ti-vpe/cal.c +++ b/drivers/media/platform/ti-vpe/cal.c @@ -234,7 +234,7 @@ enum cal_camerarx_field { struct cal_csi2_phy { struct regmap_field *fields[F_MAX_FIELDS]; - struct reg_field *base_fields; + struct reg_field base_fields[F_MAX_FIELDS]; const int num_lanes; }; @@ -245,27 +245,23 @@ struct cal_data { const unsigned int flags; }; -static struct reg_field dra72x_ctrl_core_csi0_reg_fields[F_MAX_FIELDS] = { - [F_CTRLCLKEN] = REG_FIELD(0, 10, 10), - [F_CAMMODE] = REG_FIELD(0, 11, 12), - [F_LANEENABLE] = REG_FIELD(0, 13, 16), - [F_CSI_MODE] = REG_FIELD(0, 17, 17), -}; - -static struct reg_field dra72x_ctrl_core_csi1_reg_fields[F_MAX_FIELDS] = { - [F_CTRLCLKEN] = REG_FIELD(0, 0, 0), - [F_CAMMODE] = REG_FIELD(0, 1, 2), - [F_LANEENABLE] = REG_FIELD(0, 3, 4), - [F_CSI_MODE] = REG_FIELD(0, 5, 5), -}; - static struct cal_csi2_phy dra72x_cal_csi_phy[] = { { - .base_fields = dra72x_ctrl_core_csi0_reg_fields, + .base_fields = { + [F_CTRLCLKEN] = REG_FIELD(0, 10, 10), + [F_CAMMODE] = REG_FIELD(0, 11, 12), + [F_LANEENABLE] = REG_FIELD(0, 13, 16), + [F_CSI_MODE] = REG_FIELD(0, 17, 17), + }, .num_lanes = 4, }, { - .base_fields = dra72x_ctrl_core_csi1_reg_fields, + .base_fields = { + [F_CTRLCLKEN] = REG_FIELD(0, 0, 0), + [F_CAMMODE] = REG_FIELD(0, 1, 2), + [F_LANEENABLE] = REG_FIELD(0, 3, 4), + [F_CSI_MODE] = REG_FIELD(0, 5, 5), + }, .num_lanes = 2, }, }; @@ -281,27 +277,23 @@ static const struct cal_data dra72x_es1_cal_data = { .flags = DRA72_CAL_PRE_ES2_LDO_DISABLE, }; -static struct reg_field dra76x_ctrl_core_csi0_reg_fields[F_MAX_FIELDS] = { - [F_CTRLCLKEN] = REG_FIELD(0, 8, 8), - [F_CAMMODE] = REG_FIELD(0, 9, 10), - [F_CSI_MODE] = REG_FIELD(0, 11, 11), - [F_LANEENABLE] = REG_FIELD(0, 27, 31), -}; - -static struct reg_field dra76x_ctrl_core_csi1_reg_fields[F_MAX_FIELDS] = { - [F_CTRLCLKEN] = REG_FIELD(0, 0, 0), - [F_CAMMODE] = REG_FIELD(0, 1, 2), - [F_CSI_MODE] = REG_FIELD(0, 3, 3), - [F_LANEENABLE] = REG_FIELD(0, 24, 26), -}; - static struct cal_csi2_phy dra76x_cal_csi_phy[] = { { - .base_fields = dra76x_ctrl_core_csi0_reg_fields, + .base_fields = { + [F_CTRLCLKEN] = REG_FIELD(0, 8, 8), + [F_CAMMODE] = REG_FIELD(0, 9, 10), + [F_CSI_MODE] = REG_FIELD(0, 11, 11), + [F_LANEENABLE] = REG_FIELD(0, 27, 31), + }, .num_lanes = 5, }, { - .base_fields = dra76x_ctrl_core_csi1_reg_fields, + .base_fields = { + [F_CTRLCLKEN] = REG_FIELD(0, 0, 0), + [F_CAMMODE] = REG_FIELD(0, 1, 2), + [F_CSI_MODE] = REG_FIELD(0, 3, 3), + [F_LANEENABLE] = REG_FIELD(0, 24, 26), + }, .num_lanes = 3, }, }; @@ -311,15 +303,13 @@ static const struct cal_data dra76x_cal_data = { .num_csi2_phy = ARRAY_SIZE(dra76x_cal_csi_phy), }; -static struct reg_field am654_ctrl_core_csi0_reg_fields[F_MAX_FIELDS] = { - [F_CTRLCLKEN] = REG_FIELD(0, 15, 15), - [F_CAMMODE] = REG_FIELD(0, 24, 25), - [F_LANEENABLE] = REG_FIELD(0, 0, 4), -}; - static struct cal_csi2_phy am654_cal_csi_phy[] = { { - .base_fields = am654_ctrl_core_csi0_reg_fields, + .base_fields = { + [F_CTRLCLKEN] = REG_FIELD(0, 15, 15), + [F_CAMMODE] = REG_FIELD(0, 24, 25), + [F_LANEENABLE] = REG_FIELD(0, 0, 4), + }, .num_lanes = 5, }, }; -- GitLab From 649e9535f15772be3b4cfbd621a9327db7b0289a Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 6 Jul 2020 20:35:36 +0200 Subject: [PATCH 0548/1476] media: ti-vpe: cal: Don't modify cal_csi2_phy base_fields In preparation to constify cal_csi2_phy, avoid modifying the base_fields array at runtime. As the array now only needs to stored two values instead of a full struct reg_field instance, save memory by using a custom structure for the base_fields elements. Signed-off-by: Laurent Pinchart Reviewed-by: Tomi Valkeinen Reviewed-by: Benoit Parrot Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/ti-vpe/cal.c | 54 ++++++++++++++++------------- 1 file changed, 30 insertions(+), 24 deletions(-) diff --git a/drivers/media/platform/ti-vpe/cal.c b/drivers/media/platform/ti-vpe/cal.c index 1f15481ba5a26..ff2afcfdff018 100644 --- a/drivers/media/platform/ti-vpe/cal.c +++ b/drivers/media/platform/ti-vpe/cal.c @@ -234,7 +234,10 @@ enum cal_camerarx_field { struct cal_csi2_phy { struct regmap_field *fields[F_MAX_FIELDS]; - struct reg_field base_fields[F_MAX_FIELDS]; + struct { + unsigned int lsb; + unsigned int msb; + } base_fields[F_MAX_FIELDS]; const int num_lanes; }; @@ -248,19 +251,19 @@ struct cal_data { static struct cal_csi2_phy dra72x_cal_csi_phy[] = { { .base_fields = { - [F_CTRLCLKEN] = REG_FIELD(0, 10, 10), - [F_CAMMODE] = REG_FIELD(0, 11, 12), - [F_LANEENABLE] = REG_FIELD(0, 13, 16), - [F_CSI_MODE] = REG_FIELD(0, 17, 17), + [F_CTRLCLKEN] = { 10, 10 }, + [F_CAMMODE] = { 11, 12 }, + [F_LANEENABLE] = { 13, 16 }, + [F_CSI_MODE] = { 17, 17 }, }, .num_lanes = 4, }, { .base_fields = { - [F_CTRLCLKEN] = REG_FIELD(0, 0, 0), - [F_CAMMODE] = REG_FIELD(0, 1, 2), - [F_LANEENABLE] = REG_FIELD(0, 3, 4), - [F_CSI_MODE] = REG_FIELD(0, 5, 5), + [F_CTRLCLKEN] = { 0, 0 }, + [F_CAMMODE] = { 1, 2 }, + [F_LANEENABLE] = { 3, 4 }, + [F_CSI_MODE] = { 5, 5 }, }, .num_lanes = 2, }, @@ -280,19 +283,19 @@ static const struct cal_data dra72x_es1_cal_data = { static struct cal_csi2_phy dra76x_cal_csi_phy[] = { { .base_fields = { - [F_CTRLCLKEN] = REG_FIELD(0, 8, 8), - [F_CAMMODE] = REG_FIELD(0, 9, 10), - [F_CSI_MODE] = REG_FIELD(0, 11, 11), - [F_LANEENABLE] = REG_FIELD(0, 27, 31), + [F_CTRLCLKEN] = { 8, 8 }, + [F_CAMMODE] = { 9, 10 }, + [F_CSI_MODE] = { 11, 11 }, + [F_LANEENABLE] = { 27, 31 }, }, .num_lanes = 5, }, { .base_fields = { - [F_CTRLCLKEN] = REG_FIELD(0, 0, 0), - [F_CAMMODE] = REG_FIELD(0, 1, 2), - [F_CSI_MODE] = REG_FIELD(0, 3, 3), - [F_LANEENABLE] = REG_FIELD(0, 24, 26), + [F_CTRLCLKEN] = { 0, 0 }, + [F_CAMMODE] = { 1, 2 }, + [F_CSI_MODE] = { 3, 3 }, + [F_LANEENABLE] = { 24, 26 }, }, .num_lanes = 3, }, @@ -306,9 +309,9 @@ static const struct cal_data dra76x_cal_data = { static struct cal_csi2_phy am654_cal_csi_phy[] = { { .base_fields = { - [F_CTRLCLKEN] = REG_FIELD(0, 15, 15), - [F_CAMMODE] = REG_FIELD(0, 24, 25), - [F_LANEENABLE] = REG_FIELD(0, 0, 4), + [F_CTRLCLKEN] = { 15, 15 }, + [F_CAMMODE] = { 24, 25 }, + [F_LANEENABLE] = { 0, 4 }, }, .num_lanes = 5, }, @@ -476,7 +479,6 @@ static u32 cal_data_get_num_csi2_phy(struct cal_dev *dev) static int cal_camerarx_regmap_init(struct cal_dev *dev) { - struct reg_field *field; struct cal_csi2_phy *phy; unsigned int i, j; @@ -486,16 +488,20 @@ static int cal_camerarx_regmap_init(struct cal_dev *dev) for (i = 0; i < cal_data_get_num_csi2_phy(dev); i++) { phy = &dev->data->csi2_phy_core[i]; for (j = 0; j < F_MAX_FIELDS; j++) { - field = &phy->base_fields[j]; + struct reg_field field = { + .reg = dev->syscon_camerrx_offset, + .lsb = phy->base_fields[j].lsb, + .msb = phy->base_fields[j].msb, + }; + /* * Here we update the reg offset with the * value found in DT */ - field->reg = dev->syscon_camerrx_offset; phy->fields[j] = devm_regmap_field_alloc(&dev->pdev->dev, dev->syscon_camerrx, - *field); + field); if (IS_ERR(phy->fields[j])) { cal_err(dev, "Unable to allocate regmap fields\n"); return PTR_ERR(phy->fields[j]); -- GitLab From 5e19ffe77c096eab64f8a7893d58d80e725844c1 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 6 Jul 2020 20:35:37 +0200 Subject: [PATCH 0549/1476] media: ti-vpe: cal: Store PHY regmap fields in struct cc_data The PHY regmap fields are created dynamically at runtime. They don't belong to the platform data that should be constant. Move them to the cc_data structure. Signed-off-by: Laurent Pinchart Reviewed-by: Tomi Valkeinen Reviewed-by: Benoit Parrot Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/ti-vpe/cal.c | 92 +++++++++++++++-------------- 1 file changed, 47 insertions(+), 45 deletions(-) diff --git a/drivers/media/platform/ti-vpe/cal.c b/drivers/media/platform/ti-vpe/cal.c index ff2afcfdff018..3df97708e0444 100644 --- a/drivers/media/platform/ti-vpe/cal.c +++ b/drivers/media/platform/ti-vpe/cal.c @@ -215,13 +215,6 @@ struct cal_dmaqueue { int ini_jiffies; }; -struct cc_data { - void __iomem *base; - struct resource *res; - - struct platform_device *pdev; -}; - /* CTRL_CORE_CAMERRX_CONTROL register field id */ enum cal_camerarx_field { F_CTRLCLKEN, @@ -232,8 +225,17 @@ enum cal_camerarx_field { F_MAX_FIELDS, }; +struct cc_data { + void __iomem *base; + struct resource *res; + struct platform_device *pdev; + + struct { + struct regmap_field *fields[F_MAX_FIELDS]; + } phy; +}; + struct cal_csi2_phy { - struct regmap_field *fields[F_MAX_FIELDS]; struct { unsigned int lsb; unsigned int msb; @@ -477,37 +479,37 @@ static u32 cal_data_get_num_csi2_phy(struct cal_dev *dev) return dev->data->num_csi2_phy; } -static int cal_camerarx_regmap_init(struct cal_dev *dev) +static int cal_camerarx_regmap_init(struct cal_dev *dev, struct cc_data *cc, + unsigned int idx) { - struct cal_csi2_phy *phy; - unsigned int i, j; + const struct cal_csi2_phy *phy; + unsigned int i; if (!dev->data) return -EINVAL; - for (i = 0; i < cal_data_get_num_csi2_phy(dev); i++) { - phy = &dev->data->csi2_phy_core[i]; - for (j = 0; j < F_MAX_FIELDS; j++) { - struct reg_field field = { - .reg = dev->syscon_camerrx_offset, - .lsb = phy->base_fields[j].lsb, - .msb = phy->base_fields[j].msb, - }; - - /* - * Here we update the reg offset with the - * value found in DT - */ - phy->fields[j] = - devm_regmap_field_alloc(&dev->pdev->dev, - dev->syscon_camerrx, - field); - if (IS_ERR(phy->fields[j])) { - cal_err(dev, "Unable to allocate regmap fields\n"); - return PTR_ERR(phy->fields[j]); - } + phy = &dev->data->csi2_phy_core[idx]; + + for (i = 0; i < F_MAX_FIELDS; i++) { + struct reg_field field = { + .reg = dev->syscon_camerrx_offset, + .lsb = phy->base_fields[i].lsb, + .msb = phy->base_fields[i].msb, + }; + + /* + * Here we update the reg offset with the + * value found in DT + */ + cc->phy.fields[i] = devm_regmap_field_alloc(&dev->pdev->dev, + dev->syscon_camerrx, + field); + if (IS_ERR(cc->phy.fields[i])) { + cal_err(dev, "Unable to allocate regmap fields\n"); + return PTR_ERR(cc->phy.fields[i]); } } + return 0; } @@ -554,28 +556,26 @@ static struct regmap *cal_get_camerarx_regmap(struct cal_dev *dev) */ static void camerarx_phy_enable(struct cal_ctx *ctx) { - struct cal_csi2_phy *phy; u32 phy_id = ctx->csi2_port; + struct cc_data *cc = ctx->dev->cc[phy_id]; u32 max_lanes; - phy = &ctx->dev->data->csi2_phy_core[phy_id]; - regmap_field_write(phy->fields[F_CAMMODE], 0); + regmap_field_write(cc->phy.fields[F_CAMMODE], 0); /* Always enable all lanes at the phy control level */ max_lanes = (1 << cal_data_get_phy_max_lanes(ctx)) - 1; - regmap_field_write(phy->fields[F_LANEENABLE], max_lanes); + regmap_field_write(cc->phy.fields[F_LANEENABLE], max_lanes); /* F_CSI_MODE is not present on every architecture */ - if (phy->fields[F_CSI_MODE]) - regmap_field_write(phy->fields[F_CSI_MODE], 1); - regmap_field_write(phy->fields[F_CTRLCLKEN], 1); + if (cc->phy.fields[F_CSI_MODE]) + regmap_field_write(cc->phy.fields[F_CSI_MODE], 1); + regmap_field_write(cc->phy.fields[F_CTRLCLKEN], 1); } static void camerarx_phy_disable(struct cal_ctx *ctx) { - struct cal_csi2_phy *phy; u32 phy_id = ctx->csi2_port; + struct cc_data *cc = ctx->dev->cc[phy_id]; - phy = &ctx->dev->data->csi2_phy_core[phy_id]; - regmap_field_write(phy->fields[F_CTRLCLKEN], 0); + regmap_field_write(cc->phy.fields[F_CTRLCLKEN], 0); } /* @@ -585,6 +585,7 @@ static struct cc_data *cc_create(struct cal_dev *dev, unsigned int core) { struct platform_device *pdev = dev->pdev; struct cc_data *cc; + int ret; cc = devm_kzalloc(&pdev->dev, sizeof(*cc), GFP_KERNEL); if (!cc) @@ -604,6 +605,10 @@ static struct cc_data *cc_create(struct cal_dev *dev, unsigned int core) cal_dbg(1, dev, "ioresource %s at %pa - %pa\n", cc->res->name, &cc->res->start, &cc->res->end); + ret = cal_camerarx_regmap_init(dev, cc, core); + if (ret) + return ERR_PTR(ret); + return cc; } @@ -2322,9 +2327,6 @@ static int cal_probe(struct platform_device *pdev) dev->syscon_camerrx = syscon_camerrx; dev->syscon_camerrx_offset = syscon_camerrx_offset; - ret = cal_camerarx_regmap_init(dev); - if (ret) - return ret; dev->res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "cal_top"); -- GitLab From be19b49e6ed6963ece660d33b4a16ea5b956faae Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 6 Jul 2020 20:35:38 +0200 Subject: [PATCH 0550/1476] media: ti-vpe: cal: Rename cal_csi2_phy base_fields to fields The cal_csi2_phy structure has lost its 'fields' field that used to clash with the 'base_fields' field. Rename base_fields to fields. Signed-off-by: Laurent Pinchart Reviewed-by: Tomi Valkeinen Reviewed-by: Benoit Parrot Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/ti-vpe/cal.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/drivers/media/platform/ti-vpe/cal.c b/drivers/media/platform/ti-vpe/cal.c index 3df97708e0444..72d5a6434d9e8 100644 --- a/drivers/media/platform/ti-vpe/cal.c +++ b/drivers/media/platform/ti-vpe/cal.c @@ -239,7 +239,7 @@ struct cal_csi2_phy { struct { unsigned int lsb; unsigned int msb; - } base_fields[F_MAX_FIELDS]; + } fields[F_MAX_FIELDS]; const int num_lanes; }; @@ -252,7 +252,7 @@ struct cal_data { static struct cal_csi2_phy dra72x_cal_csi_phy[] = { { - .base_fields = { + .fields = { [F_CTRLCLKEN] = { 10, 10 }, [F_CAMMODE] = { 11, 12 }, [F_LANEENABLE] = { 13, 16 }, @@ -261,7 +261,7 @@ static struct cal_csi2_phy dra72x_cal_csi_phy[] = { .num_lanes = 4, }, { - .base_fields = { + .fields = { [F_CTRLCLKEN] = { 0, 0 }, [F_CAMMODE] = { 1, 2 }, [F_LANEENABLE] = { 3, 4 }, @@ -284,7 +284,7 @@ static const struct cal_data dra72x_es1_cal_data = { static struct cal_csi2_phy dra76x_cal_csi_phy[] = { { - .base_fields = { + .fields = { [F_CTRLCLKEN] = { 8, 8 }, [F_CAMMODE] = { 9, 10 }, [F_CSI_MODE] = { 11, 11 }, @@ -293,7 +293,7 @@ static struct cal_csi2_phy dra76x_cal_csi_phy[] = { .num_lanes = 5, }, { - .base_fields = { + .fields = { [F_CTRLCLKEN] = { 0, 0 }, [F_CAMMODE] = { 1, 2 }, [F_CSI_MODE] = { 3, 3 }, @@ -310,7 +310,7 @@ static const struct cal_data dra76x_cal_data = { static struct cal_csi2_phy am654_cal_csi_phy[] = { { - .base_fields = { + .fields = { [F_CTRLCLKEN] = { 15, 15 }, [F_CAMMODE] = { 24, 25 }, [F_LANEENABLE] = { 0, 4 }, @@ -493,8 +493,8 @@ static int cal_camerarx_regmap_init(struct cal_dev *dev, struct cc_data *cc, for (i = 0; i < F_MAX_FIELDS; i++) { struct reg_field field = { .reg = dev->syscon_camerrx_offset, - .lsb = phy->base_fields[i].lsb, - .msb = phy->base_fields[i].msb, + .lsb = phy->fields[i].lsb, + .msb = phy->fields[i].msb, }; /* -- GitLab From 01bd7499393217ee409f095f361dddda370f94a3 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 6 Jul 2020 20:35:39 +0200 Subject: [PATCH 0551/1476] media: ti-vpe: cal: Make structure fields unsigned where applicable Several structure fields declared as int store positive values only. Make them unsigned. Signed-off-by: Laurent Pinchart Reviewed-by: Tomi Valkeinen Reviewed-by: Benoit Parrot Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/ti-vpe/cal.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/media/platform/ti-vpe/cal.c b/drivers/media/platform/ti-vpe/cal.c index 72d5a6434d9e8..e7e1ad4280744 100644 --- a/drivers/media/platform/ti-vpe/cal.c +++ b/drivers/media/platform/ti-vpe/cal.c @@ -240,11 +240,11 @@ struct cal_csi2_phy { unsigned int lsb; unsigned int msb; } fields[F_MAX_FIELDS]; - const int num_lanes; + const unsigned int num_lanes; }; struct cal_data { - const int num_csi2_phy; + const unsigned int num_csi2_phy; struct cal_csi2_phy *csi2_phy_core; const unsigned int flags; @@ -388,7 +388,7 @@ struct cal_ctx { /* Current subdev enumerated format */ const struct cal_fmt *active_fmt[ARRAY_SIZE(cal_formats)]; - int num_active_fmt; + unsigned int num_active_fmt; struct v4l2_fract timeperframe; unsigned int sequence; -- GitLab From 9704762aed23aa0eb9fc88454c7ce423471bcb3a Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 6 Jul 2020 20:35:40 +0200 Subject: [PATCH 0552/1476] media: ti-vpe: cal: Constify platform data Now that no platform data fields are modified at runtime, make the corresponding structures const. The const qualifier for integer fields is removed as it's not required anymore. Signed-off-by: Laurent Pinchart Reviewed-by: Tomi Valkeinen Reviewed-by: Benoit Parrot Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/ti-vpe/cal.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/drivers/media/platform/ti-vpe/cal.c b/drivers/media/platform/ti-vpe/cal.c index e7e1ad4280744..86e67b98dc632 100644 --- a/drivers/media/platform/ti-vpe/cal.c +++ b/drivers/media/platform/ti-vpe/cal.c @@ -240,17 +240,17 @@ struct cal_csi2_phy { unsigned int lsb; unsigned int msb; } fields[F_MAX_FIELDS]; - const unsigned int num_lanes; + unsigned int num_lanes; }; struct cal_data { - const unsigned int num_csi2_phy; - struct cal_csi2_phy *csi2_phy_core; + unsigned int num_csi2_phy; + const struct cal_csi2_phy *csi2_phy_core; - const unsigned int flags; + unsigned int flags; }; -static struct cal_csi2_phy dra72x_cal_csi_phy[] = { +static const struct cal_csi2_phy dra72x_cal_csi_phy[] = { { .fields = { [F_CTRLCLKEN] = { 10, 10 }, @@ -282,7 +282,7 @@ static const struct cal_data dra72x_es1_cal_data = { .flags = DRA72_CAL_PRE_ES2_LDO_DISABLE, }; -static struct cal_csi2_phy dra76x_cal_csi_phy[] = { +static const struct cal_csi2_phy dra76x_cal_csi_phy[] = { { .fields = { [F_CTRLCLKEN] = { 8, 8 }, @@ -308,7 +308,7 @@ static const struct cal_data dra76x_cal_data = { .num_csi2_phy = ARRAY_SIZE(dra76x_cal_csi_phy), }; -static struct cal_csi2_phy am654_cal_csi_phy[] = { +static const struct cal_csi2_phy am654_cal_csi_phy[] = { { .fields = { [F_CTRLCLKEN] = { 15, 15 }, -- GitLab From ed641dc98dd624dfa12824611e339c6499e5f9fe Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 6 Jul 2020 20:35:41 +0200 Subject: [PATCH 0553/1476] media: ti-vpe: cal: Remove static const cal_regmap_config template The global static const cal_regmap_config template is only used in a single location, to initialize 3 fields of a local variable. Two of those fields then get overwritten. Remove the template and set the last remaining field manually. Simplify the code by hardcoding the field values instead of calculating them for a variable that always has the same value. Signed-off-by: Laurent Pinchart Reviewed-by: Tomi Valkeinen Reviewed-by: Benoit Parrot Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/ti-vpe/cal.c | 19 ++++++------------- 1 file changed, 6 insertions(+), 13 deletions(-) diff --git a/drivers/media/platform/ti-vpe/cal.c b/drivers/media/platform/ti-vpe/cal.c index 86e67b98dc632..15f4cd205e10b 100644 --- a/drivers/media/platform/ti-vpe/cal.c +++ b/drivers/media/platform/ti-vpe/cal.c @@ -513,19 +513,12 @@ static int cal_camerarx_regmap_init(struct cal_dev *dev, struct cc_data *cc, return 0; } -static const struct regmap_config cal_regmap_config = { - .reg_bits = 32, - .val_bits = 32, - .reg_stride = 4, -}; - static struct regmap *cal_get_camerarx_regmap(struct cal_dev *dev) { struct platform_device *pdev = dev->pdev; + struct regmap_config config = { }; struct regmap *regmap; void __iomem *base; - u32 reg_io_width; - struct regmap_config r_config = cal_regmap_config; struct resource *res; res = platform_get_resource_byname(pdev, IORESOURCE_MEM, @@ -539,12 +532,12 @@ static struct regmap *cal_get_camerarx_regmap(struct cal_dev *dev) cal_dbg(1, dev, "ioresource %s at %pa - %pa\n", res->name, &res->start, &res->end); - reg_io_width = 4; - r_config.reg_stride = reg_io_width; - r_config.val_bits = reg_io_width * 8; - r_config.max_register = resource_size(res) - reg_io_width; + config.reg_bits = 32; + config.reg_stride = 4; + config.val_bits = 32; + config.max_register = resource_size(res) - 4; - regmap = regmap_init_mmio(NULL, base, &r_config); + regmap = regmap_init_mmio(NULL, base, &config); if (IS_ERR(regmap)) pr_err("regmap init failed\n"); -- GitLab From 6aa4459bdedc171c0d7d66a16f76131d333111f6 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 6 Jul 2020 20:35:42 +0200 Subject: [PATCH 0554/1476] media: ti-vpe: cal: Remove unused structure fields Remove structure fields that are never set, set and never read, or set to a fixed value. This allows removal of a global variable and a macro. Signed-off-by: Laurent Pinchart Reviewed-by: Tomi Valkeinen Reviewed-by: Benoit Parrot Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/ti-vpe/cal.c | 34 +++-------------------------- 1 file changed, 3 insertions(+), 31 deletions(-) diff --git a/drivers/media/platform/ti-vpe/cal.c b/drivers/media/platform/ti-vpe/cal.c index 15f4cd205e10b..89f6fd2e13006 100644 --- a/drivers/media/platform/ti-vpe/cal.c +++ b/drivers/media/platform/ti-vpe/cal.c @@ -53,10 +53,6 @@ static unsigned debug; module_param(debug, uint, 0644); MODULE_PARM_DESC(debug, "activates debug info"); -/* timeperframe: min/max and default */ -static const struct v4l2_fract - tpf_default = {.numerator = 1001, .denominator = 30000}; - #define cal_dbg(level, caldev, fmt, arg...) \ v4l2_dbg(level, debug, &caldev->v4l2_dev, fmt, ##arg) #define cal_info(caldev, fmt, arg...) \ @@ -71,7 +67,6 @@ static const struct v4l2_fract #define ctx_err(ctx, fmt, arg...) \ v4l2_err(&ctx->v4l2_dev, fmt, ##arg) -#define CAL_NUM_INPUT 1 #define CAL_NUM_CONTEXT 2 #define reg_read(dev, offset) ioread32(dev->base + offset) @@ -204,15 +199,10 @@ struct cal_buffer { /* common v4l buffer stuff -- must be first */ struct vb2_v4l2_buffer vb; struct list_head list; - const struct cal_fmt *fmt; }; struct cal_dmaqueue { struct list_head active; - - /* Counters to control fps rate */ - int frame; - int ini_jiffies; }; /* CTRL_CORE_CAMERRX_CONTROL register field id */ @@ -362,7 +352,6 @@ struct cal_ctx { struct v4l2_subdev *sensor; struct v4l2_fwnode_endpoint endpoint; - struct v4l2_fh fh; struct cal_dev *dev; struct cc_data *cc; @@ -371,14 +360,8 @@ struct cal_ctx { /* v4l2 buffers lock */ spinlock_t slock; - /* Several counters */ - unsigned long jiffies; - struct cal_dmaqueue vidq; - /* Input Number */ - int input; - /* video capture */ const struct cal_fmt *fmt; /* Used to store current pixel format */ @@ -390,11 +373,9 @@ struct cal_ctx { const struct cal_fmt *active_fmt[ARRAY_SIZE(cal_formats)]; unsigned int num_active_fmt; - struct v4l2_fract timeperframe; unsigned int sequence; unsigned int external_rate; struct vb2_queue vb_vidq; - unsigned int seq_count; unsigned int csi2_port; unsigned int cport; unsigned int virtual_channel; @@ -1538,7 +1519,7 @@ static int cal_enum_framesizes(struct file *file, void *fh, static int cal_enum_input(struct file *file, void *priv, struct v4l2_input *inp) { - if (inp->index >= CAL_NUM_INPUT) + if (inp->index > 0) return -EINVAL; inp->type = V4L2_INPUT_TYPE_CAMERA; @@ -1548,21 +1529,13 @@ static int cal_enum_input(struct file *file, void *priv, static int cal_g_input(struct file *file, void *priv, unsigned int *i) { - struct cal_ctx *ctx = video_drvdata(file); - - *i = ctx->input; + *i = 0; return 0; } static int cal_s_input(struct file *file, void *priv, unsigned int i) { - struct cal_ctx *ctx = video_drvdata(file); - - if (i >= CAL_NUM_INPUT) - return -EINVAL; - - ctx->input = i; - return 0; + return i > 0 ? -EINVAL : 0; } /* timeperframe is arbitrary and continuous */ @@ -1856,7 +1829,6 @@ static int cal_complete_ctx(struct cal_ctx *ctx) struct vb2_queue *q; int ret; - ctx->timeperframe = tpf_default; ctx->external_rate = 192000000; /* initialize locks */ -- GitLab From 0a19960a6c6cdeee9327c71d4f0d9dee199fb887 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 6 Jul 2020 20:35:43 +0200 Subject: [PATCH 0555/1476] media: ti-vpe: cal: Remove flags field from struct cal_dev The cal_dev flags field is a copy of data->flags. Use the latter and remove the former. Signed-off-by: Laurent Pinchart Reviewed-by: Tomi Valkeinen Reviewed-by: Benoit Parrot Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/ti-vpe/cal.c | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/drivers/media/platform/ti-vpe/cal.c b/drivers/media/platform/ti-vpe/cal.c index 89f6fd2e13006..d04caa4fa9cf0 100644 --- a/drivers/media/platform/ti-vpe/cal.c +++ b/drivers/media/platform/ti-vpe/cal.c @@ -326,9 +326,6 @@ struct cal_dev { struct platform_device *pdev; struct v4l2_device v4l2_dev; - /* Controller flags for special cases */ - unsigned int flags; - const struct cal_data *data; /* Control Module handle */ @@ -2249,8 +2246,6 @@ static int cal_probe(struct platform_device *pdev) return -ENODEV; } - dev->flags = dev->data->flags; - /* set pseudo v4l2 device name so we can use v4l2_printk */ strscpy(dev->v4l2_dev.name, CAL_MODULE_NAME, sizeof(dev->v4l2_dev.name)); @@ -2402,7 +2397,7 @@ static int cal_runtime_resume(struct device *dev) { struct cal_dev *caldev = dev_get_drvdata(dev); - if (caldev->flags & DRA72_CAL_PRE_ES2_LDO_DISABLE) { + if (caldev->data->flags & DRA72_CAL_PRE_ES2_LDO_DISABLE) { /* * Apply errata on both port everytime we (re-)enable * the clock -- GitLab From ca486468503652dbf55c4b1ffa9f3f3c4f3775bb Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 6 Jul 2020 20:35:44 +0200 Subject: [PATCH 0556/1476] media: ti-vpe: cal: Move function to avoid forward declaration Move the csi2_phy_config() function to avoid its forward declaration. No functional change is included. Signed-off-by: Laurent Pinchart Reviewed-by: Tomi Valkeinen Reviewed-by: Benoit Parrot Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/ti-vpe/cal.c | 98 ++++++++++++++--------------- 1 file changed, 48 insertions(+), 50 deletions(-) diff --git a/drivers/media/platform/ti-vpe/cal.c b/drivers/media/platform/ti-vpe/cal.c index d04caa4fa9cf0..5db8d928cf094 100644 --- a/drivers/media/platform/ti-vpe/cal.c +++ b/drivers/media/platform/ti-vpe/cal.c @@ -740,7 +740,54 @@ static void csi2_cio_power(struct cal_ctx *ctx, bool enable) enable ? "up" : "down"); } -static void csi2_phy_config(struct cal_ctx *ctx); +/* + * TCLK values are OK at their reset values + */ +#define TCLK_TERM 0 +#define TCLK_MISS 1 +#define TCLK_SETTLE 14 + +static void csi2_phy_config(struct cal_ctx *ctx) +{ + unsigned int reg0, reg1; + unsigned int ths_term, ths_settle; + unsigned int csi2_ddrclk_khz; + struct v4l2_fwnode_bus_mipi_csi2 *mipi_csi2 = + &ctx->endpoint.bus.mipi_csi2; + u32 num_lanes = mipi_csi2->num_data_lanes; + + /* DPHY timing configuration */ + /* CSI-2 is DDR and we only count used lanes. */ + csi2_ddrclk_khz = ctx->external_rate / 1000 + / (2 * num_lanes) * ctx->fmt->bpp; + ctx_dbg(1, ctx, "csi2_ddrclk_khz: %d\n", csi2_ddrclk_khz); + + /* THS_TERM: Programmed value = floor(20 ns/DDRClk period) */ + ths_term = 20 * csi2_ddrclk_khz / 1000000; + ctx_dbg(1, ctx, "ths_term: %d (0x%02x)\n", ths_term, ths_term); + + /* THS_SETTLE: Programmed value = floor(105 ns/DDRClk period) + 4 */ + ths_settle = (105 * csi2_ddrclk_khz / 1000000) + 4; + ctx_dbg(1, ctx, "ths_settle: %d (0x%02x)\n", ths_settle, ths_settle); + + reg0 = reg_read(ctx->cc, CAL_CSI2_PHY_REG0); + set_field(®0, CAL_CSI2_PHY_REG0_HSCLOCKCONFIG_DISABLE, + CAL_CSI2_PHY_REG0_HSCLOCKCONFIG_MASK); + set_field(®0, ths_term, CAL_CSI2_PHY_REG0_THS_TERM_MASK); + set_field(®0, ths_settle, CAL_CSI2_PHY_REG0_THS_SETTLE_MASK); + + ctx_dbg(1, ctx, "CSI2_%d_REG0 = 0x%08x\n", ctx->csi2_port, reg0); + reg_write(ctx->cc, CAL_CSI2_PHY_REG0, reg0); + + reg1 = reg_read(ctx->cc, CAL_CSI2_PHY_REG1); + set_field(®1, TCLK_TERM, CAL_CSI2_PHY_REG1_TCLK_TERM_MASK); + set_field(®1, 0xb8, CAL_CSI2_PHY_REG1_DPHY_HS_SYNC_PATTERN_MASK); + set_field(®1, TCLK_MISS, CAL_CSI2_PHY_REG1_CTRLCLK_DIV_FACTOR_MASK); + set_field(®1, TCLK_SETTLE, CAL_CSI2_PHY_REG1_TCLK_SETTLE_MASK); + + ctx_dbg(1, ctx, "CSI2_%d_REG1 = 0x%08x\n", ctx->csi2_port, reg1); + reg_write(ctx->cc, CAL_CSI2_PHY_REG1, reg1); +} static void csi2_phy_init(struct cal_ctx *ctx) { @@ -1077,55 +1124,6 @@ static void cal_wr_dma_addr(struct cal_ctx *ctx, unsigned int dmaaddr) reg_write(ctx->dev, CAL_WR_DMA_ADDR(ctx->csi2_port), dmaaddr); } -/* - * TCLK values are OK at their reset values - */ -#define TCLK_TERM 0 -#define TCLK_MISS 1 -#define TCLK_SETTLE 14 - -static void csi2_phy_config(struct cal_ctx *ctx) -{ - unsigned int reg0, reg1; - unsigned int ths_term, ths_settle; - unsigned int csi2_ddrclk_khz; - struct v4l2_fwnode_bus_mipi_csi2 *mipi_csi2 = - &ctx->endpoint.bus.mipi_csi2; - u32 num_lanes = mipi_csi2->num_data_lanes; - - /* DPHY timing configuration */ - /* CSI-2 is DDR and we only count used lanes. */ - csi2_ddrclk_khz = ctx->external_rate / 1000 - / (2 * num_lanes) * ctx->fmt->bpp; - ctx_dbg(1, ctx, "csi2_ddrclk_khz: %d\n", csi2_ddrclk_khz); - - /* THS_TERM: Programmed value = floor(20 ns/DDRClk period) */ - ths_term = 20 * csi2_ddrclk_khz / 1000000; - ctx_dbg(1, ctx, "ths_term: %d (0x%02x)\n", ths_term, ths_term); - - /* THS_SETTLE: Programmed value = floor(105 ns/DDRClk period) + 4 */ - ths_settle = (105 * csi2_ddrclk_khz / 1000000) + 4; - ctx_dbg(1, ctx, "ths_settle: %d (0x%02x)\n", ths_settle, ths_settle); - - reg0 = reg_read(ctx->cc, CAL_CSI2_PHY_REG0); - set_field(®0, CAL_CSI2_PHY_REG0_HSCLOCKCONFIG_DISABLE, - CAL_CSI2_PHY_REG0_HSCLOCKCONFIG_MASK); - set_field(®0, ths_term, CAL_CSI2_PHY_REG0_THS_TERM_MASK); - set_field(®0, ths_settle, CAL_CSI2_PHY_REG0_THS_SETTLE_MASK); - - ctx_dbg(1, ctx, "CSI2_%d_REG0 = 0x%08x\n", ctx->csi2_port, reg0); - reg_write(ctx->cc, CAL_CSI2_PHY_REG0, reg0); - - reg1 = reg_read(ctx->cc, CAL_CSI2_PHY_REG1); - set_field(®1, TCLK_TERM, CAL_CSI2_PHY_REG1_TCLK_TERM_MASK); - set_field(®1, 0xb8, CAL_CSI2_PHY_REG1_DPHY_HS_SYNC_PATTERN_MASK); - set_field(®1, TCLK_MISS, CAL_CSI2_PHY_REG1_CTRLCLK_DIV_FACTOR_MASK); - set_field(®1, TCLK_SETTLE, CAL_CSI2_PHY_REG1_TCLK_SETTLE_MASK); - - ctx_dbg(1, ctx, "CSI2_%d_REG1 = 0x%08x\n", ctx->csi2_port, reg1); - reg_write(ctx->cc, CAL_CSI2_PHY_REG1, reg1); -} - static int cal_get_external_info(struct cal_ctx *ctx) { struct v4l2_ctrl *ctrl; -- GitLab From a7f8ac542a070cace1d1cc7f3e02f7791851a39f Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 6 Jul 2020 20:35:45 +0200 Subject: [PATCH 0557/1476] media: ti-vpe: cal: Rename cc_data to cal_camerarx The various data structures that describe the components of the camera access layer (CAL) are named without much consistency. Start cleaning this up by renaming the structure that describes the CAMERARX block, cc_data, to cal_camerarx. Signed-off-by: Laurent Pinchart Reviewed-by: Tomi Valkeinen Reviewed-by: Benoit Parrot Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/ti-vpe/cal.c | 46 +++++++++++++++++------------ 1 file changed, 27 insertions(+), 19 deletions(-) diff --git a/drivers/media/platform/ti-vpe/cal.c b/drivers/media/platform/ti-vpe/cal.c index 5db8d928cf094..621cc3f267620 100644 --- a/drivers/media/platform/ti-vpe/cal.c +++ b/drivers/media/platform/ti-vpe/cal.c @@ -215,16 +215,6 @@ enum cal_camerarx_field { F_MAX_FIELDS, }; -struct cc_data { - void __iomem *base; - struct resource *res; - struct platform_device *pdev; - - struct { - struct regmap_field *fields[F_MAX_FIELDS]; - } phy; -}; - struct cal_csi2_phy { struct { unsigned int lsb; @@ -315,9 +305,26 @@ static const struct cal_data am654_cal_data = { }; /* - * there is one cal_dev structure in the driver, it is shared by - * all instances. + * The Camera Adaptation Layer (CAL) module is paired with one or more complex + * I/O PHYs (CAMERARX). It contains multiple instances of CSI-2, processing and + * DMA contexts. + * + * The cal_dev structure represents the whole subsystem, including the CAL and + * the CAMERARX instances. The cal_camerarx structure represents one CAMERARX + * instance. The cal_ctx structure represents the combination of one CSI-2 + * context, one processing context and one DMA context. */ + +struct cal_camerarx { + void __iomem *base; + struct resource *res; + struct platform_device *pdev; + + struct { + struct regmap_field *fields[F_MAX_FIELDS]; + } phy; +}; + struct cal_dev { struct clk *fclk; int irq; @@ -333,7 +340,7 @@ struct cal_dev { u32 syscon_camerrx_offset; /* Camera Core Module handle */ - struct cc_data *cc[CAL_NUM_CSI2_PORTS]; + struct cal_camerarx *cc[CAL_NUM_CSI2_PORTS]; struct cal_ctx *ctx[CAL_NUM_CONTEXT]; }; @@ -350,7 +357,7 @@ struct cal_ctx { struct v4l2_fwnode_endpoint endpoint; struct cal_dev *dev; - struct cc_data *cc; + struct cal_camerarx *cc; /* v4l2_ioctl mutex */ struct mutex mutex; @@ -457,7 +464,8 @@ static u32 cal_data_get_num_csi2_phy(struct cal_dev *dev) return dev->data->num_csi2_phy; } -static int cal_camerarx_regmap_init(struct cal_dev *dev, struct cc_data *cc, +static int cal_camerarx_regmap_init(struct cal_dev *dev, + struct cal_camerarx *cc, unsigned int idx) { const struct cal_csi2_phy *phy; @@ -528,7 +536,7 @@ static struct regmap *cal_get_camerarx_regmap(struct cal_dev *dev) static void camerarx_phy_enable(struct cal_ctx *ctx) { u32 phy_id = ctx->csi2_port; - struct cc_data *cc = ctx->dev->cc[phy_id]; + struct cal_camerarx *cc = ctx->dev->cc[phy_id]; u32 max_lanes; regmap_field_write(cc->phy.fields[F_CAMMODE], 0); @@ -544,7 +552,7 @@ static void camerarx_phy_enable(struct cal_ctx *ctx) static void camerarx_phy_disable(struct cal_ctx *ctx) { u32 phy_id = ctx->csi2_port; - struct cc_data *cc = ctx->dev->cc[phy_id]; + struct cal_camerarx *cc = ctx->dev->cc[phy_id]; regmap_field_write(cc->phy.fields[F_CTRLCLKEN], 0); } @@ -552,10 +560,10 @@ static void camerarx_phy_disable(struct cal_ctx *ctx) /* * Camera Instance access block */ -static struct cc_data *cc_create(struct cal_dev *dev, unsigned int core) +static struct cal_camerarx *cc_create(struct cal_dev *dev, unsigned int core) { struct platform_device *pdev = dev->pdev; - struct cc_data *cc; + struct cal_camerarx *cc; int ret; cc = devm_kzalloc(&pdev->dev, sizeof(*cc), GFP_KERNEL); -- GitLab From d1739eabdd9600569ff2da170b3baf343263f952 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 6 Jul 2020 20:35:46 +0200 Subject: [PATCH 0558/1476] media: ti-vpe: cal: Rename cal_csi2_phy to cal_camerarx_data The cal_csi2_phy contains platform data for the CAMERARX blocks. Rename it to cal_camerarx_data. Signed-off-by: Laurent Pinchart Reviewed-by: Tomi Valkeinen Reviewed-by: Benoit Parrot Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/ti-vpe/cal.c | 33 ++++++++++++++--------------- 1 file changed, 16 insertions(+), 17 deletions(-) diff --git a/drivers/media/platform/ti-vpe/cal.c b/drivers/media/platform/ti-vpe/cal.c index 621cc3f267620..faf18980656e2 100644 --- a/drivers/media/platform/ti-vpe/cal.c +++ b/drivers/media/platform/ti-vpe/cal.c @@ -215,7 +215,7 @@ enum cal_camerarx_field { F_MAX_FIELDS, }; -struct cal_csi2_phy { +struct cal_camerarx_data { struct { unsigned int lsb; unsigned int msb; @@ -224,13 +224,12 @@ struct cal_csi2_phy { }; struct cal_data { + const struct cal_camerarx_data *camerarx; unsigned int num_csi2_phy; - const struct cal_csi2_phy *csi2_phy_core; - unsigned int flags; }; -static const struct cal_csi2_phy dra72x_cal_csi_phy[] = { +static const struct cal_camerarx_data dra72x_cal_camerarx[] = { { .fields = { [F_CTRLCLKEN] = { 10, 10 }, @@ -252,17 +251,17 @@ static const struct cal_csi2_phy dra72x_cal_csi_phy[] = { }; static const struct cal_data dra72x_cal_data = { - .csi2_phy_core = dra72x_cal_csi_phy, - .num_csi2_phy = ARRAY_SIZE(dra72x_cal_csi_phy), + .camerarx = dra72x_cal_camerarx, + .num_csi2_phy = ARRAY_SIZE(dra72x_cal_camerarx), }; static const struct cal_data dra72x_es1_cal_data = { - .csi2_phy_core = dra72x_cal_csi_phy, - .num_csi2_phy = ARRAY_SIZE(dra72x_cal_csi_phy), + .camerarx = dra72x_cal_camerarx, + .num_csi2_phy = ARRAY_SIZE(dra72x_cal_camerarx), .flags = DRA72_CAL_PRE_ES2_LDO_DISABLE, }; -static const struct cal_csi2_phy dra76x_cal_csi_phy[] = { +static const struct cal_camerarx_data dra76x_cal_csi_phy[] = { { .fields = { [F_CTRLCLKEN] = { 8, 8 }, @@ -284,11 +283,11 @@ static const struct cal_csi2_phy dra76x_cal_csi_phy[] = { }; static const struct cal_data dra76x_cal_data = { - .csi2_phy_core = dra76x_cal_csi_phy, + .camerarx = dra76x_cal_csi_phy, .num_csi2_phy = ARRAY_SIZE(dra76x_cal_csi_phy), }; -static const struct cal_csi2_phy am654_cal_csi_phy[] = { +static const struct cal_camerarx_data am654_cal_csi_phy[] = { { .fields = { [F_CTRLCLKEN] = { 15, 15 }, @@ -300,7 +299,7 @@ static const struct cal_csi2_phy am654_cal_csi_phy[] = { }; static const struct cal_data am654_cal_data = { - .csi2_phy_core = am654_cal_csi_phy, + .camerarx = am654_cal_csi_phy, .num_csi2_phy = ARRAY_SIZE(am654_cal_csi_phy), }; @@ -456,7 +455,7 @@ static u32 cal_data_get_phy_max_lanes(struct cal_ctx *ctx) struct cal_dev *dev = ctx->dev; u32 phy_id = ctx->csi2_port; - return dev->data->csi2_phy_core[phy_id].num_lanes; + return dev->data->camerarx[phy_id].num_lanes; } static u32 cal_data_get_num_csi2_phy(struct cal_dev *dev) @@ -468,19 +467,19 @@ static int cal_camerarx_regmap_init(struct cal_dev *dev, struct cal_camerarx *cc, unsigned int idx) { - const struct cal_csi2_phy *phy; + const struct cal_camerarx_data *phy_data; unsigned int i; if (!dev->data) return -EINVAL; - phy = &dev->data->csi2_phy_core[idx]; + phy_data = &dev->data->camerarx[idx]; for (i = 0; i < F_MAX_FIELDS; i++) { struct reg_field field = { .reg = dev->syscon_camerrx_offset, - .lsb = phy->fields[i].lsb, - .msb = phy->fields[i].msb, + .lsb = phy_data->fields[i].lsb, + .msb = phy_data->fields[i].msb, }; /* -- GitLab From 97cf202807c9154e6a84be4b53ce8c4228de2246 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 6 Jul 2020 20:35:47 +0200 Subject: [PATCH 0559/1476] media: ti-vpe: cal: Name all cal_dev pointers consistently Name all variables pointing to a cal_dev instance 'cal', instead of a combination of 'caldev' and 'dev'. 'caldev' is only used in a few places and could have been renamed to 'dev', but 'dev' is confusing as it's also used for struct device instances. This generates lots of changes, but increases readability. Signed-off-by: Laurent Pinchart Reviewed-by: Tomi Valkeinen Reviewed-by: Benoit Parrot Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/ti-vpe/cal.c | 368 ++++++++++++++-------------- 1 file changed, 186 insertions(+), 182 deletions(-) diff --git a/drivers/media/platform/ti-vpe/cal.c b/drivers/media/platform/ti-vpe/cal.c index faf18980656e2..f0e06a4699fce 100644 --- a/drivers/media/platform/ti-vpe/cal.c +++ b/drivers/media/platform/ti-vpe/cal.c @@ -53,12 +53,12 @@ static unsigned debug; module_param(debug, uint, 0644); MODULE_PARM_DESC(debug, "activates debug info"); -#define cal_dbg(level, caldev, fmt, arg...) \ - v4l2_dbg(level, debug, &caldev->v4l2_dev, fmt, ##arg) -#define cal_info(caldev, fmt, arg...) \ - v4l2_info(&caldev->v4l2_dev, fmt, ##arg) -#define cal_err(caldev, fmt, arg...) \ - v4l2_err(&caldev->v4l2_dev, fmt, ##arg) +#define cal_dbg(level, cal, fmt, arg...) \ + v4l2_dbg(level, debug, &cal->v4l2_dev, fmt, ##arg) +#define cal_info(cal, fmt, arg...) \ + v4l2_info(&cal->v4l2_dev, fmt, ##arg) +#define cal_err(cal, fmt, arg...) \ + v4l2_err(&cal->v4l2_dev, fmt, ##arg) #define ctx_dbg(level, ctx, fmt, arg...) \ v4l2_dbg(level, debug, &ctx->v4l2_dev, fmt, ##arg) @@ -309,9 +309,14 @@ static const struct cal_data am654_cal_data = { * DMA contexts. * * The cal_dev structure represents the whole subsystem, including the CAL and - * the CAMERARX instances. The cal_camerarx structure represents one CAMERARX - * instance. The cal_ctx structure represents the combination of one CSI-2 - * context, one processing context and one DMA context. + * the CAMERARX instances. Instances of struct cal_dev are named cal through the + * driver. + * + * The cal_camerarx structure represents one CAMERARX instance. + * + * The cal_ctx structure represents the combination of one CSI-2 context, one + * processing context and one DMA context. Instance of struct cal_ctx are named + * ctx through the driver. */ struct cal_camerarx { @@ -355,7 +360,7 @@ struct cal_ctx { struct v4l2_subdev *sensor; struct v4l2_fwnode_endpoint endpoint; - struct cal_dev *dev; + struct cal_dev *cal; struct cal_camerarx *cc; /* v4l2_ioctl mutex */ @@ -391,19 +396,19 @@ struct cal_ctx { bool dma_act; }; -static inline u32 reg_read_field(struct cal_dev *dev, u32 offset, u32 mask) +static inline u32 reg_read_field(struct cal_dev *cal, u32 offset, u32 mask) { - return FIELD_GET(mask, reg_read(dev, offset)); + return FIELD_GET(mask, reg_read(cal, offset)); } -static inline void reg_write_field(struct cal_dev *dev, u32 offset, u32 value, +static inline void reg_write_field(struct cal_dev *cal, u32 offset, u32 value, u32 mask) { - u32 val = reg_read(dev, offset); + u32 val = reg_read(cal, offset); val &= ~mask; val |= FIELD_PREP(mask, value); - reg_write(dev, offset, val); + reg_write(cal, offset, val); } static const struct cal_fmt *find_format_by_pix(struct cal_ctx *ctx, @@ -452,32 +457,31 @@ static inline void set_field(u32 *valp, u32 field, u32 mask) static u32 cal_data_get_phy_max_lanes(struct cal_ctx *ctx) { - struct cal_dev *dev = ctx->dev; u32 phy_id = ctx->csi2_port; - return dev->data->camerarx[phy_id].num_lanes; + return ctx->cal->data->camerarx[phy_id].num_lanes; } -static u32 cal_data_get_num_csi2_phy(struct cal_dev *dev) +static u32 cal_data_get_num_csi2_phy(struct cal_dev *cal) { - return dev->data->num_csi2_phy; + return cal->data->num_csi2_phy; } -static int cal_camerarx_regmap_init(struct cal_dev *dev, +static int cal_camerarx_regmap_init(struct cal_dev *cal, struct cal_camerarx *cc, unsigned int idx) { const struct cal_camerarx_data *phy_data; unsigned int i; - if (!dev->data) + if (!cal->data) return -EINVAL; - phy_data = &dev->data->camerarx[idx]; + phy_data = &cal->data->camerarx[idx]; for (i = 0; i < F_MAX_FIELDS; i++) { struct reg_field field = { - .reg = dev->syscon_camerrx_offset, + .reg = cal->syscon_camerrx_offset, .lsb = phy_data->fields[i].lsb, .msb = phy_data->fields[i].msb, }; @@ -486,11 +490,11 @@ static int cal_camerarx_regmap_init(struct cal_dev *dev, * Here we update the reg offset with the * value found in DT */ - cc->phy.fields[i] = devm_regmap_field_alloc(&dev->pdev->dev, - dev->syscon_camerrx, + cc->phy.fields[i] = devm_regmap_field_alloc(&cal->pdev->dev, + cal->syscon_camerrx, field); if (IS_ERR(cc->phy.fields[i])) { - cal_err(dev, "Unable to allocate regmap fields\n"); + cal_err(cal, "Unable to allocate regmap fields\n"); return PTR_ERR(cc->phy.fields[i]); } } @@ -498,9 +502,9 @@ static int cal_camerarx_regmap_init(struct cal_dev *dev, return 0; } -static struct regmap *cal_get_camerarx_regmap(struct cal_dev *dev) +static struct regmap *cal_get_camerarx_regmap(struct cal_dev *cal) { - struct platform_device *pdev = dev->pdev; + struct platform_device *pdev = cal->pdev; struct regmap_config config = { }; struct regmap *regmap; void __iomem *base; @@ -510,11 +514,11 @@ static struct regmap *cal_get_camerarx_regmap(struct cal_dev *dev) "camerrx_control"); base = devm_ioremap_resource(&pdev->dev, res); if (IS_ERR(base)) { - cal_err(dev, "failed to ioremap\n"); + cal_err(cal, "failed to ioremap\n"); return ERR_CAST(base); } - cal_dbg(1, dev, "ioresource %s at %pa - %pa\n", + cal_dbg(1, cal, "ioresource %s at %pa - %pa\n", res->name, &res->start, &res->end); config.reg_bits = 32; @@ -535,7 +539,7 @@ static struct regmap *cal_get_camerarx_regmap(struct cal_dev *dev) static void camerarx_phy_enable(struct cal_ctx *ctx) { u32 phy_id = ctx->csi2_port; - struct cal_camerarx *cc = ctx->dev->cc[phy_id]; + struct cal_camerarx *cc = ctx->cal->cc[phy_id]; u32 max_lanes; regmap_field_write(cc->phy.fields[F_CAMMODE], 0); @@ -551,7 +555,7 @@ static void camerarx_phy_enable(struct cal_ctx *ctx) static void camerarx_phy_disable(struct cal_ctx *ctx) { u32 phy_id = ctx->csi2_port; - struct cal_camerarx *cc = ctx->dev->cc[phy_id]; + struct cal_camerarx *cc = ctx->cal->cc[phy_id]; regmap_field_write(cc->phy.fields[F_CTRLCLKEN], 0); } @@ -559,9 +563,9 @@ static void camerarx_phy_disable(struct cal_ctx *ctx) /* * Camera Instance access block */ -static struct cal_camerarx *cc_create(struct cal_dev *dev, unsigned int core) +static struct cal_camerarx *cc_create(struct cal_dev *cal, unsigned int core) { - struct platform_device *pdev = dev->pdev; + struct platform_device *pdev = cal->pdev; struct cal_camerarx *cc; int ret; @@ -576,14 +580,14 @@ static struct cal_camerarx *cc_create(struct cal_dev *dev, unsigned int core) "cal_rx_core1"); cc->base = devm_ioremap_resource(&pdev->dev, cc->res); if (IS_ERR(cc->base)) { - cal_err(dev, "failed to ioremap\n"); + cal_err(cal, "failed to ioremap\n"); return ERR_CAST(cc->base); } - cal_dbg(1, dev, "ioresource %s at %pa - %pa\n", + cal_dbg(1, cal, "ioresource %s at %pa - %pa\n", cc->res->name, &cc->res->start, &cc->res->end); - ret = cal_camerarx_regmap_init(dev, cc, core); + ret = cal_camerarx_regmap_init(cal, cc, core); if (ret) return ERR_PTR(ret); @@ -593,17 +597,17 @@ static struct cal_camerarx *cc_create(struct cal_dev *dev, unsigned int core) /* * Get Revision and HW info */ -static void cal_get_hwinfo(struct cal_dev *dev) +static void cal_get_hwinfo(struct cal_dev *cal) { u32 revision; u32 hwinfo; - revision = reg_read(dev, CAL_HL_REVISION); - cal_dbg(3, dev, "CAL_HL_REVISION = 0x%08x (expecting 0x40000200)\n", + revision = reg_read(cal, CAL_HL_REVISION); + cal_dbg(3, cal, "CAL_HL_REVISION = 0x%08x (expecting 0x40000200)\n", revision); - hwinfo = reg_read(dev, CAL_HL_HWINFO); - cal_dbg(3, dev, "CAL_HL_HWINFO = 0x%08x (expecting 0xA3C90469)\n", + hwinfo = reg_read(cal, CAL_HL_HWINFO); + cal_dbg(3, cal, "CAL_HL_HWINFO = 0x%08x (expecting 0xA3C90469)\n", hwinfo); } @@ -626,38 +630,38 @@ static void cal_get_hwinfo(struct cal_dev *dev) * Core 0: 0x4845 B828 = 0x0000 0040 * Core 1: 0x4845 B928 = 0x0000 0040 */ -static void i913_errata(struct cal_dev *dev, unsigned int port) +static void i913_errata(struct cal_dev *cal, unsigned int port) { - u32 reg10 = reg_read(dev->cc[port], CAL_CSI2_PHY_REG10); + u32 reg10 = reg_read(cal->cc[port], CAL_CSI2_PHY_REG10); set_field(®10, 1, CAL_CSI2_PHY_REG10_I933_LDO_DISABLE_MASK); - cal_dbg(1, dev, "CSI2_%d_REG10 = 0x%08x\n", port, reg10); - reg_write(dev->cc[port], CAL_CSI2_PHY_REG10, reg10); + cal_dbg(1, cal, "CSI2_%d_REG10 = 0x%08x\n", port, reg10); + reg_write(cal->cc[port], CAL_CSI2_PHY_REG10, reg10); } -static void cal_quickdump_regs(struct cal_dev *dev) +static void cal_quickdump_regs(struct cal_dev *cal) { - cal_info(dev, "CAL Registers @ 0x%pa:\n", &dev->res->start); + cal_info(cal, "CAL Registers @ 0x%pa:\n", &cal->res->start); print_hex_dump(KERN_INFO, "", DUMP_PREFIX_OFFSET, 16, 4, - (__force const void *)dev->base, - resource_size(dev->res), false); + (__force const void *)cal->base, + resource_size(cal->res), false); - if (dev->ctx[0]) { - cal_info(dev, "CSI2 Core 0 Registers @ %pa:\n", - &dev->ctx[0]->cc->res->start); + if (cal->ctx[0]) { + cal_info(cal, "CSI2 Core 0 Registers @ %pa:\n", + &cal->ctx[0]->cc->res->start); print_hex_dump(KERN_INFO, "", DUMP_PREFIX_OFFSET, 16, 4, - (__force const void *)dev->ctx[0]->cc->base, - resource_size(dev->ctx[0]->cc->res), + (__force const void *)cal->ctx[0]->cc->base, + resource_size(cal->ctx[0]->cc->res), false); } - if (dev->ctx[1]) { - cal_info(dev, "CSI2 Core 1 Registers @ %pa:\n", - &dev->ctx[1]->cc->res->start); + if (cal->ctx[1]) { + cal_info(cal, "CSI2 Core 1 Registers @ %pa:\n", + &cal->ctx[1]->cc->res->start); print_hex_dump(KERN_INFO, "", DUMP_PREFIX_OFFSET, 16, 4, - (__force const void *)dev->ctx[1]->cc->base, - resource_size(dev->ctx[1]->cc->res), + (__force const void *)cal->ctx[1]->cc->base, + resource_size(cal->ctx[1]->cc->res), false); } } @@ -676,24 +680,24 @@ static void enable_irqs(struct cal_ctx *ctx) CAL_CSI2_COMPLEXIO_IRQ_ECC_NO_CORRECTION_MASK; /* Enable CIO error irqs */ - reg_write(ctx->dev, CAL_HL_IRQENABLE_SET(0), + reg_write(ctx->cal, CAL_HL_IRQENABLE_SET(0), CAL_HL_IRQ_CIO_MASK(ctx->csi2_port)); - reg_write(ctx->dev, CAL_CSI2_COMPLEXIO_IRQENABLE(ctx->csi2_port), + reg_write(ctx->cal, CAL_CSI2_COMPLEXIO_IRQENABLE(ctx->csi2_port), cio_err_mask); /* Always enable OCPO error */ - reg_write(ctx->dev, CAL_HL_IRQENABLE_SET(0), CAL_HL_IRQ_OCPO_ERR_MASK); + reg_write(ctx->cal, CAL_HL_IRQENABLE_SET(0), CAL_HL_IRQ_OCPO_ERR_MASK); /* Enable IRQ_WDMA_END 0/1 */ val = 0; set_field(&val, 1, CAL_HL_IRQ_MASK(ctx->csi2_port)); - reg_write(ctx->dev, CAL_HL_IRQENABLE_SET(1), val); + reg_write(ctx->cal, CAL_HL_IRQENABLE_SET(1), val); /* Enable IRQ_WDMA_START 0/1 */ val = 0; set_field(&val, 1, CAL_HL_IRQ_MASK(ctx->csi2_port)); - reg_write(ctx->dev, CAL_HL_IRQENABLE_SET(2), val); + reg_write(ctx->cal, CAL_HL_IRQENABLE_SET(2), val); /* Todo: Add VC_IRQ and CSI2_COMPLEXIO_IRQ handling */ - reg_write(ctx->dev, CAL_CSI2_VC_IRQENABLE(0), 0xFF000000); + reg_write(ctx->cal, CAL_CSI2_VC_IRQENABLE(0), 0xFF000000); } static void disable_irqs(struct cal_ctx *ctx) @@ -701,21 +705,21 @@ static void disable_irqs(struct cal_ctx *ctx) u32 val; /* Disable CIO error irqs */ - reg_write(ctx->dev, CAL_HL_IRQENABLE_CLR(0), + reg_write(ctx->cal, CAL_HL_IRQENABLE_CLR(0), CAL_HL_IRQ_CIO_MASK(ctx->csi2_port)); - reg_write(ctx->dev, CAL_CSI2_COMPLEXIO_IRQENABLE(ctx->csi2_port), + reg_write(ctx->cal, CAL_CSI2_COMPLEXIO_IRQENABLE(ctx->csi2_port), 0); /* Disable IRQ_WDMA_END 0/1 */ val = 0; set_field(&val, 1, CAL_HL_IRQ_MASK(ctx->csi2_port)); - reg_write(ctx->dev, CAL_HL_IRQENABLE_CLR(1), val); + reg_write(ctx->cal, CAL_HL_IRQENABLE_CLR(1), val); /* Disable IRQ_WDMA_START 0/1 */ val = 0; set_field(&val, 1, CAL_HL_IRQ_MASK(ctx->csi2_port)); - reg_write(ctx->dev, CAL_HL_IRQENABLE_CLR(2), val); + reg_write(ctx->cal, CAL_HL_IRQENABLE_CLR(2), val); /* Todo: Add VC_IRQ and CSI2_COMPLEXIO_IRQ handling */ - reg_write(ctx->dev, CAL_CSI2_VC_IRQENABLE(0), 0); + reg_write(ctx->cal, CAL_CSI2_VC_IRQENABLE(0), 0); } static void csi2_cio_power(struct cal_ctx *ctx, bool enable) @@ -726,13 +730,13 @@ static void csi2_cio_power(struct cal_ctx *ctx, bool enable) target_state = enable ? CAL_CSI2_COMPLEXIO_CFG_PWR_CMD_STATE_ON : CAL_CSI2_COMPLEXIO_CFG_PWR_CMD_STATE_OFF; - reg_write_field(ctx->dev, CAL_CSI2_COMPLEXIO_CFG(ctx->csi2_port), + reg_write_field(ctx->cal, CAL_CSI2_COMPLEXIO_CFG(ctx->csi2_port), target_state, CAL_CSI2_COMPLEXIO_CFG_PWR_CMD_MASK); for (i = 0; i < 10; i++) { u32 current_state; - current_state = reg_read_field(ctx->dev, + current_state = reg_read_field(ctx->cal, CAL_CSI2_COMPLEXIO_CFG(ctx->csi2_port), CAL_CSI2_COMPLEXIO_CFG_PWR_STATUS_MASK); @@ -822,12 +826,12 @@ static void csi2_phy_init(struct cal_ctx *ctx) camerarx_phy_enable(ctx); /* 2. Reset complex IO - Do not wait for reset completion */ - reg_write_field(ctx->dev, CAL_CSI2_COMPLEXIO_CFG(ctx->csi2_port), + reg_write_field(ctx->cal, CAL_CSI2_COMPLEXIO_CFG(ctx->csi2_port), CAL_CSI2_COMPLEXIO_CFG_RESET_CTRL_OPERATIONAL, CAL_CSI2_COMPLEXIO_CFG_RESET_CTRL_MASK); ctx_dbg(3, ctx, "CAL_CSI2_COMPLEXIO_CFG(%d) = 0x%08x De-assert Complex IO Reset\n", ctx->csi2_port, - reg_read(ctx->dev, CAL_CSI2_COMPLEXIO_CFG(ctx->csi2_port))); + reg_read(ctx->cal, CAL_CSI2_COMPLEXIO_CFG(ctx->csi2_port))); /* Dummy read to allow SCP reset to complete */ reg_read(ctx->cc, CAL_CSI2_PHY_REG0); @@ -844,23 +848,23 @@ static void csi2_phy_init(struct cal_ctx *ctx) * Stop-state-timeout must be more than 100us as per CSI2 spec, so we * calculate a timeout that's 100us (rounding up). */ - sscounter = DIV_ROUND_UP(clk_get_rate(ctx->dev->fclk), 10000 * 16 * 4); + sscounter = DIV_ROUND_UP(clk_get_rate(ctx->cal->fclk), 10000 * 16 * 4); - val = reg_read(ctx->dev, CAL_CSI2_TIMING(ctx->csi2_port)); + val = reg_read(ctx->cal, CAL_CSI2_TIMING(ctx->csi2_port)); set_field(&val, 1, CAL_CSI2_TIMING_STOP_STATE_X16_IO1_MASK); set_field(&val, 1, CAL_CSI2_TIMING_STOP_STATE_X4_IO1_MASK); set_field(&val, sscounter, CAL_CSI2_TIMING_STOP_STATE_COUNTER_IO1_MASK); - reg_write(ctx->dev, CAL_CSI2_TIMING(ctx->csi2_port), val); + reg_write(ctx->cal, CAL_CSI2_TIMING(ctx->csi2_port), val); ctx_dbg(3, ctx, "CAL_CSI2_TIMING(%d) = 0x%08x Stop States\n", ctx->csi2_port, - reg_read(ctx->dev, CAL_CSI2_TIMING(ctx->csi2_port))); + reg_read(ctx->cal, CAL_CSI2_TIMING(ctx->csi2_port))); /* 4. Force FORCERXMODE */ - reg_write_field(ctx->dev, CAL_CSI2_TIMING(ctx->csi2_port), + reg_write_field(ctx->cal, CAL_CSI2_TIMING(ctx->csi2_port), 1, CAL_CSI2_TIMING_FORCE_RX_MODE_IO1_MASK); ctx_dbg(3, ctx, "CAL_CSI2_TIMING(%d) = 0x%08x Force RXMODE\n", ctx->csi2_port, - reg_read(ctx->dev, CAL_CSI2_TIMING(ctx->csi2_port))); + reg_read(ctx->cal, CAL_CSI2_TIMING(ctx->csi2_port))); /* E. Power up the PHY using the complex IO */ csi2_cio_power(ctx, true); @@ -872,7 +876,7 @@ static void csi2_wait_complexio_reset(struct cal_ctx *ctx) timeout = jiffies + msecs_to_jiffies(750); while (time_before(jiffies, timeout)) { - if (reg_read_field(ctx->dev, + if (reg_read_field(ctx->cal, CAL_CSI2_COMPLEXIO_CFG(ctx->csi2_port), CAL_CSI2_COMPLEXIO_CFG_RESET_DONE_MASK) == CAL_CSI2_COMPLEXIO_CFG_RESET_DONE_RESETCOMPLETED) @@ -880,7 +884,7 @@ static void csi2_wait_complexio_reset(struct cal_ctx *ctx) usleep_range(500, 5000); } - if (reg_read_field(ctx->dev, CAL_CSI2_COMPLEXIO_CFG(ctx->csi2_port), + if (reg_read_field(ctx->cal, CAL_CSI2_COMPLEXIO_CFG(ctx->csi2_port), CAL_CSI2_COMPLEXIO_CFG_RESET_DONE_MASK) != CAL_CSI2_COMPLEXIO_CFG_RESET_DONE_RESETCOMPLETED) ctx_err(ctx, "Timeout waiting for Complex IO reset done\n"); @@ -892,14 +896,14 @@ static void csi2_wait_stop_state(struct cal_ctx *ctx) timeout = jiffies + msecs_to_jiffies(750); while (time_before(jiffies, timeout)) { - if (reg_read_field(ctx->dev, + if (reg_read_field(ctx->cal, CAL_CSI2_TIMING(ctx->csi2_port), CAL_CSI2_TIMING_FORCE_RX_MODE_IO1_MASK) == 0) break; usleep_range(500, 5000); } - if (reg_read_field(ctx->dev, CAL_CSI2_TIMING(ctx->csi2_port), + if (reg_read_field(ctx->cal, CAL_CSI2_TIMING(ctx->csi2_port), CAL_CSI2_TIMING_FORCE_RX_MODE_IO1_MASK) != 0) ctx_err(ctx, "Timeout waiting for stop state\n"); } @@ -932,13 +936,13 @@ static void csi2_phy_deinit(struct cal_ctx *ctx) csi2_cio_power(ctx, false); /* Assert Comple IO Reset */ - reg_write_field(ctx->dev, CAL_CSI2_COMPLEXIO_CFG(ctx->csi2_port), + reg_write_field(ctx->cal, CAL_CSI2_COMPLEXIO_CFG(ctx->csi2_port), CAL_CSI2_COMPLEXIO_CFG_RESET_CTRL, CAL_CSI2_COMPLEXIO_CFG_RESET_CTRL_MASK); /* Wait for power down completion */ for (i = 0; i < 10; i++) { - if (reg_read_field(ctx->dev, + if (reg_read_field(ctx->cal, CAL_CSI2_COMPLEXIO_CFG(ctx->csi2_port), CAL_CSI2_COMPLEXIO_CFG_RESET_DONE_MASK) == CAL_CSI2_COMPLEXIO_CFG_RESET_DONE_RESETONGOING) @@ -947,7 +951,7 @@ static void csi2_phy_deinit(struct cal_ctx *ctx) } ctx_dbg(3, ctx, "CAL_CSI2_COMPLEXIO_CFG(%d) = 0x%08x Complex IO in Reset (%d) %s\n", ctx->csi2_port, - reg_read(ctx->dev, CAL_CSI2_COMPLEXIO_CFG(ctx->csi2_port)), i, + reg_read(ctx->cal, CAL_CSI2_COMPLEXIO_CFG(ctx->csi2_port)), i, (i >= 10) ? "(timeout)" : ""); /* Disable the phy */ @@ -956,7 +960,7 @@ static void csi2_phy_deinit(struct cal_ctx *ctx) static void csi2_lane_config(struct cal_ctx *ctx) { - u32 val = reg_read(ctx->dev, CAL_CSI2_COMPLEXIO_CFG(ctx->csi2_port)); + u32 val = reg_read(ctx->cal, CAL_CSI2_COMPLEXIO_CFG(ctx->csi2_port)); u32 lane_mask = CAL_CSI2_COMPLEXIO_CFG_CLOCK_POSITION_MASK; u32 polarity_mask = CAL_CSI2_COMPLEXIO_CFG_CLOCK_POL_MASK; struct v4l2_fwnode_bus_mipi_csi2 *mipi_csi2 = @@ -977,21 +981,21 @@ static void csi2_lane_config(struct cal_ctx *ctx) polarity_mask); } - reg_write(ctx->dev, CAL_CSI2_COMPLEXIO_CFG(ctx->csi2_port), val); + reg_write(ctx->cal, CAL_CSI2_COMPLEXIO_CFG(ctx->csi2_port), val); ctx_dbg(3, ctx, "CAL_CSI2_COMPLEXIO_CFG(%d) = 0x%08x\n", ctx->csi2_port, val); } static void csi2_ppi_enable(struct cal_ctx *ctx) { - reg_write(ctx->dev, CAL_CSI2_PPI_CTRL(ctx->csi2_port), BIT(3)); - reg_write_field(ctx->dev, CAL_CSI2_PPI_CTRL(ctx->csi2_port), + reg_write(ctx->cal, CAL_CSI2_PPI_CTRL(ctx->csi2_port), BIT(3)); + reg_write_field(ctx->cal, CAL_CSI2_PPI_CTRL(ctx->csi2_port), 1, CAL_CSI2_PPI_CTRL_IF_EN_MASK); } static void csi2_ppi_disable(struct cal_ctx *ctx) { - reg_write_field(ctx->dev, CAL_CSI2_PPI_CTRL(ctx->csi2_port), + reg_write_field(ctx->cal, CAL_CSI2_PPI_CTRL(ctx->csi2_port), 0, CAL_CSI2_PPI_CTRL_IF_EN_MASK); } @@ -999,7 +1003,7 @@ static void csi2_ctx_config(struct cal_ctx *ctx) { u32 val; - val = reg_read(ctx->dev, CAL_CSI2_CTX0(ctx->csi2_port)); + val = reg_read(ctx->cal, CAL_CSI2_CTX0(ctx->csi2_port)); set_field(&val, ctx->cport, CAL_CSI2_CTX_CPORT_MASK); /* * DT type: MIPI CSI-2 Specs @@ -1016,9 +1020,9 @@ static void csi2_ctx_config(struct cal_ctx *ctx) set_field(&val, CAL_CSI2_CTX_ATT_PIX, CAL_CSI2_CTX_ATT_MASK); set_field(&val, CAL_CSI2_CTX_PACK_MODE_LINE, CAL_CSI2_CTX_PACK_MODE_MASK); - reg_write(ctx->dev, CAL_CSI2_CTX0(ctx->csi2_port), val); + reg_write(ctx->cal, CAL_CSI2_CTX0(ctx->csi2_port), val); ctx_dbg(3, ctx, "CAL_CSI2_CTX0(%d) = 0x%08x\n", ctx->csi2_port, - reg_read(ctx->dev, CAL_CSI2_CTX0(ctx->csi2_port))); + reg_read(ctx->cal, CAL_CSI2_CTX0(ctx->csi2_port))); } static void pix_proc_config(struct cal_ctx *ctx) @@ -1052,7 +1056,7 @@ static void pix_proc_config(struct cal_ctx *ctx) * * Instead of failing here just use 8 bpp as a default. */ - dev_warn_once(&ctx->dev->pdev->dev, + dev_warn_once(&ctx->cal->pdev->dev, "%s:%d:%s: bpp:%d unsupported! Overwritten with 8.\n", __FILE__, __LINE__, __func__, ctx->fmt->bpp); extract = CAL_PIX_PROC_EXTRACT_B8; @@ -1060,16 +1064,16 @@ static void pix_proc_config(struct cal_ctx *ctx) break; } - val = reg_read(ctx->dev, CAL_PIX_PROC(ctx->csi2_port)); + val = reg_read(ctx->cal, CAL_PIX_PROC(ctx->csi2_port)); set_field(&val, extract, CAL_PIX_PROC_EXTRACT_MASK); set_field(&val, CAL_PIX_PROC_DPCMD_BYPASS, CAL_PIX_PROC_DPCMD_MASK); set_field(&val, CAL_PIX_PROC_DPCME_BYPASS, CAL_PIX_PROC_DPCME_MASK); set_field(&val, pack, CAL_PIX_PROC_PACK_MASK); set_field(&val, ctx->cport, CAL_PIX_PROC_CPORT_MASK); set_field(&val, 1, CAL_PIX_PROC_EN_MASK); - reg_write(ctx->dev, CAL_PIX_PROC(ctx->csi2_port), val); + reg_write(ctx->cal, CAL_PIX_PROC(ctx->csi2_port), val); ctx_dbg(3, ctx, "CAL_PIX_PROC(%d) = 0x%08x\n", ctx->csi2_port, - reg_read(ctx->dev, CAL_PIX_PROC(ctx->csi2_port))); + reg_read(ctx->cal, CAL_PIX_PROC(ctx->csi2_port))); } static void cal_wr_dma_config(struct cal_ctx *ctx, @@ -1077,7 +1081,7 @@ static void cal_wr_dma_config(struct cal_ctx *ctx, { u32 val; - val = reg_read(ctx->dev, CAL_WR_DMA_CTRL(ctx->csi2_port)); + val = reg_read(ctx->cal, CAL_WR_DMA_CTRL(ctx->csi2_port)); set_field(&val, ctx->cport, CAL_WR_DMA_CTRL_CPORT_MASK); set_field(&val, height, CAL_WR_DMA_CTRL_YSIZE_MASK); set_field(&val, CAL_WR_DMA_CTRL_DTAG_PIX_DAT, @@ -1087,22 +1091,22 @@ static void cal_wr_dma_config(struct cal_ctx *ctx, set_field(&val, CAL_WR_DMA_CTRL_PATTERN_LINEAR, CAL_WR_DMA_CTRL_PATTERN_MASK); set_field(&val, 1, CAL_WR_DMA_CTRL_STALL_RD_MASK); - reg_write(ctx->dev, CAL_WR_DMA_CTRL(ctx->csi2_port), val); + reg_write(ctx->cal, CAL_WR_DMA_CTRL(ctx->csi2_port), val); ctx_dbg(3, ctx, "CAL_WR_DMA_CTRL(%d) = 0x%08x\n", ctx->csi2_port, - reg_read(ctx->dev, CAL_WR_DMA_CTRL(ctx->csi2_port))); + reg_read(ctx->cal, CAL_WR_DMA_CTRL(ctx->csi2_port))); /* * width/16 not sure but giving it a whirl. * zero does not work right */ - reg_write_field(ctx->dev, + reg_write_field(ctx->cal, CAL_WR_DMA_OFST(ctx->csi2_port), (width / 16), CAL_WR_DMA_OFST_MASK); ctx_dbg(3, ctx, "CAL_WR_DMA_OFST(%d) = 0x%08x\n", ctx->csi2_port, - reg_read(ctx->dev, CAL_WR_DMA_OFST(ctx->csi2_port))); + reg_read(ctx->cal, CAL_WR_DMA_OFST(ctx->csi2_port))); - val = reg_read(ctx->dev, CAL_WR_DMA_XSIZE(ctx->csi2_port)); + val = reg_read(ctx->cal, CAL_WR_DMA_XSIZE(ctx->csi2_port)); /* 64 bit word means no skipping */ set_field(&val, 0, CAL_WR_DMA_XSIZE_XSKIP_MASK); /* @@ -1111,24 +1115,24 @@ static void cal_wr_dma_config(struct cal_ctx *ctx, * is detected automagically */ set_field(&val, (width / 8), CAL_WR_DMA_XSIZE_MASK); - reg_write(ctx->dev, CAL_WR_DMA_XSIZE(ctx->csi2_port), val); + reg_write(ctx->cal, CAL_WR_DMA_XSIZE(ctx->csi2_port), val); ctx_dbg(3, ctx, "CAL_WR_DMA_XSIZE(%d) = 0x%08x\n", ctx->csi2_port, - reg_read(ctx->dev, CAL_WR_DMA_XSIZE(ctx->csi2_port))); + reg_read(ctx->cal, CAL_WR_DMA_XSIZE(ctx->csi2_port))); - val = reg_read(ctx->dev, CAL_CTRL); + val = reg_read(ctx->cal, CAL_CTRL); set_field(&val, CAL_CTRL_BURSTSIZE_BURST128, CAL_CTRL_BURSTSIZE_MASK); set_field(&val, 0xF, CAL_CTRL_TAGCNT_MASK); set_field(&val, CAL_CTRL_POSTED_WRITES_NONPOSTED, CAL_CTRL_POSTED_WRITES_MASK); set_field(&val, 0xFF, CAL_CTRL_MFLAGL_MASK); set_field(&val, 0xFF, CAL_CTRL_MFLAGH_MASK); - reg_write(ctx->dev, CAL_CTRL, val); - ctx_dbg(3, ctx, "CAL_CTRL = 0x%08x\n", reg_read(ctx->dev, CAL_CTRL)); + reg_write(ctx->cal, CAL_CTRL, val); + ctx_dbg(3, ctx, "CAL_CTRL = 0x%08x\n", reg_read(ctx->cal, CAL_CTRL)); } static void cal_wr_dma_addr(struct cal_ctx *ctx, unsigned int dmaaddr) { - reg_write(ctx->dev, CAL_WR_DMA_ADDR(ctx->csi2_port), dmaaddr); + reg_write(ctx->cal, CAL_WR_DMA_ADDR(ctx->csi2_port), dmaaddr); } static int cal_get_external_info(struct cal_ctx *ctx) @@ -1182,45 +1186,45 @@ static inline void cal_process_buffer_complete(struct cal_ctx *ctx) static irqreturn_t cal_irq(int irq_cal, void *data) { - struct cal_dev *dev = data; + struct cal_dev *cal = data; struct cal_ctx *ctx; struct cal_dmaqueue *dma_q; u32 status; - status = reg_read(dev, CAL_HL_IRQSTATUS(0)); + status = reg_read(cal, CAL_HL_IRQSTATUS(0)); if (status) { unsigned int i; - reg_write(dev, CAL_HL_IRQSTATUS(0), status); + reg_write(cal, CAL_HL_IRQSTATUS(0), status); if (status & CAL_HL_IRQ_OCPO_ERR_MASK) - dev_err_ratelimited(&dev->pdev->dev, "OCPO ERROR\n"); + dev_err_ratelimited(&cal->pdev->dev, "OCPO ERROR\n"); for (i = 0; i < 2; ++i) { if (status & CAL_HL_IRQ_CIO_MASK(i)) { - u32 cio_stat = reg_read(dev, + u32 cio_stat = reg_read(cal, CAL_CSI2_COMPLEXIO_IRQSTATUS(i)); - dev_err_ratelimited(&dev->pdev->dev, + dev_err_ratelimited(&cal->pdev->dev, "CIO%u error: %#08x\n", i, cio_stat); - reg_write(dev, CAL_CSI2_COMPLEXIO_IRQSTATUS(i), + reg_write(cal, CAL_CSI2_COMPLEXIO_IRQSTATUS(i), cio_stat); } } } /* Check which DMA just finished */ - status = reg_read(dev, CAL_HL_IRQSTATUS(1)); + status = reg_read(cal, CAL_HL_IRQSTATUS(1)); if (status) { unsigned int i; /* Clear Interrupt status */ - reg_write(dev, CAL_HL_IRQSTATUS(1), status); + reg_write(cal, CAL_HL_IRQSTATUS(1), status); for (i = 0; i < 2; ++i) { if (isportirqset(status, i)) { - ctx = dev->ctx[i]; + ctx = cal->ctx[i]; spin_lock(&ctx->slock); ctx->dma_act = false; @@ -1234,16 +1238,16 @@ static irqreturn_t cal_irq(int irq_cal, void *data) } /* Check which DMA just started */ - status = reg_read(dev, CAL_HL_IRQSTATUS(2)); + status = reg_read(cal, CAL_HL_IRQSTATUS(2)); if (status) { unsigned int i; /* Clear Interrupt status */ - reg_write(dev, CAL_HL_IRQSTATUS(2), status); + reg_write(cal, CAL_HL_IRQSTATUS(2), status); for (i = 0; i < 2; ++i) { if (isportirqset(status, i)) { - ctx = dev->ctx[i]; + ctx = cal->ctx[i]; dma_q = &ctx->vidq; spin_lock(&ctx->slock); @@ -1667,7 +1671,7 @@ static int cal_start_streaming(struct vb2_queue *vq, unsigned int count) goto err; } - pm_runtime_get_sync(&ctx->dev->pdev->dev); + pm_runtime_get_sync(&ctx->cal->pdev->dev); csi2_ctx_config(ctx); pix_proc_config(ctx); @@ -1682,7 +1686,7 @@ static int cal_start_streaming(struct vb2_queue *vq, unsigned int count) if (ret) { v4l2_subdev_call(ctx->sensor, core, s_power, 0); ctx_err(ctx, "stream on failed in subdev\n"); - pm_runtime_put_sync(&ctx->dev->pdev->dev); + pm_runtime_put_sync(&ctx->cal->pdev->dev); goto err; } @@ -1691,7 +1695,7 @@ static int cal_start_streaming(struct vb2_queue *vq, unsigned int count) csi2_ppi_enable(ctx); if (debug >= 4) - cal_quickdump_regs(ctx->dev); + cal_quickdump_regs(ctx->cal); return 0; @@ -1762,7 +1766,7 @@ static void cal_stop_streaming(struct vb2_queue *vq) ctx->next_frm = NULL; spin_unlock_irqrestore(&ctx->slock, flags); - pm_runtime_put_sync(&ctx->dev->pdev->dev); + pm_runtime_put_sync(&ctx->cal->pdev->dev); } static const struct vb2_ops cal_video_qops = { @@ -2037,7 +2041,7 @@ of_get_next_endpoint(const struct device_node *parent, static int of_cal_create_instance(struct cal_ctx *ctx, int inst) { - struct platform_device *pdev = ctx->dev->pdev; + struct platform_device *pdev = ctx->cal->pdev; struct device_node *ep_node, *port, *sensor_node, *parent; struct v4l2_fwnode_endpoint *endpoint; struct v4l2_async_subdev *asd; @@ -2160,22 +2164,22 @@ cleanup_exit: return ret; } -static struct cal_ctx *cal_create_instance(struct cal_dev *dev, int inst) +static struct cal_ctx *cal_create_instance(struct cal_dev *cal, int inst) { struct cal_ctx *ctx; struct v4l2_ctrl_handler *hdl; int ret; - ctx = devm_kzalloc(&dev->pdev->dev, sizeof(*ctx), GFP_KERNEL); + ctx = devm_kzalloc(&cal->pdev->dev, sizeof(*ctx), GFP_KERNEL); if (!ctx) return NULL; /* save the cal_dev * for future ref */ - ctx->dev = dev; + ctx->cal = cal; snprintf(ctx->v4l2_dev.name, sizeof(ctx->v4l2_dev.name), "%s-%03d", CAL_MODULE_NAME, inst); - ret = v4l2_device_register(&dev->pdev->dev, &ctx->v4l2_dev); + ret = v4l2_device_register(&cal->pdev->dev, &ctx->v4l2_dev); if (ret) goto err_exit; @@ -2188,7 +2192,7 @@ static struct cal_ctx *cal_create_instance(struct cal_dev *dev, int inst) ctx->v4l2_dev.ctrl_handler = hdl; /* Make sure Camera Core H/W register area is available */ - ctx->cc = dev->cc[inst]; + ctx->cc = cal->cc[inst]; /* Store the instance id */ ctx->csi2_port = inst; @@ -2232,7 +2236,7 @@ MODULE_DEVICE_TABLE(of, cal_of_match); static int cal_probe(struct platform_device *pdev) { - struct cal_dev *dev; + struct cal_dev *cal; struct cal_ctx *ctx; struct device_node *parent = pdev->dev.of_node; struct regmap *syscon_camerrx; @@ -2241,27 +2245,27 @@ static int cal_probe(struct platform_device *pdev) int ret; int irq; - dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL); - if (!dev) + cal = devm_kzalloc(&pdev->dev, sizeof(*cal), GFP_KERNEL); + if (!cal) return -ENOMEM; - dev->data = of_device_get_match_data(&pdev->dev); - if (!dev->data) { + cal->data = of_device_get_match_data(&pdev->dev); + if (!cal->data) { dev_err(&pdev->dev, "Could not get feature data based on compatible version\n"); return -ENODEV; } /* set pseudo v4l2 device name so we can use v4l2_printk */ - strscpy(dev->v4l2_dev.name, CAL_MODULE_NAME, - sizeof(dev->v4l2_dev.name)); + strscpy(cal->v4l2_dev.name, CAL_MODULE_NAME, + sizeof(cal->v4l2_dev.name)); /* save pdev pointer */ - dev->pdev = pdev; + cal->pdev = pdev; - dev->fclk = devm_clk_get(&pdev->dev, "fck"); - if (IS_ERR(dev->fclk)) { + cal->fclk = devm_clk_get(&pdev->dev, "fck"); + if (IS_ERR(cal->fclk)) { dev_err(&pdev->dev, "cannot get CAL fclk\n"); - return PTR_ERR(dev->fclk); + return PTR_ERR(cal->fclk); } syscon_camerrx = syscon_regmap_lookup_by_phandle(parent, @@ -2279,7 +2283,7 @@ static int cal_probe(struct platform_device *pdev) * If syscon entry is not present then check if the * camerrx_control resource is present. */ - syscon_camerrx = cal_get_camerarx_regmap(dev); + syscon_camerrx = cal_get_camerarx_regmap(cal); if (IS_ERR(syscon_camerrx)) { dev_err(&pdev->dev, "failed to get camerrx_control regmap\n"); return PTR_ERR(syscon_camerrx); @@ -2290,47 +2294,47 @@ static int cal_probe(struct platform_device *pdev) syscon_camerrx_offset = 0; } - dev->syscon_camerrx = syscon_camerrx; - dev->syscon_camerrx_offset = syscon_camerrx_offset; + cal->syscon_camerrx = syscon_camerrx; + cal->syscon_camerrx_offset = syscon_camerrx_offset; - dev->res = platform_get_resource_byname(pdev, IORESOURCE_MEM, + cal->res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "cal_top"); - dev->base = devm_ioremap_resource(&pdev->dev, dev->res); - if (IS_ERR(dev->base)) - return PTR_ERR(dev->base); + cal->base = devm_ioremap_resource(&pdev->dev, cal->res); + if (IS_ERR(cal->base)) + return PTR_ERR(cal->base); - cal_dbg(1, dev, "ioresource %s at %pa - %pa\n", - dev->res->name, &dev->res->start, &dev->res->end); + cal_dbg(1, cal, "ioresource %s at %pa - %pa\n", + cal->res->name, &cal->res->start, &cal->res->end); irq = platform_get_irq(pdev, 0); - cal_dbg(1, dev, "got irq# %d\n", irq); + cal_dbg(1, cal, "got irq# %d\n", irq); ret = devm_request_irq(&pdev->dev, irq, cal_irq, 0, CAL_MODULE_NAME, - dev); + cal); if (ret) return ret; - platform_set_drvdata(pdev, dev); + platform_set_drvdata(pdev, cal); - dev->cc[0] = cc_create(dev, 0); - if (IS_ERR(dev->cc[0])) - return PTR_ERR(dev->cc[0]); + cal->cc[0] = cc_create(cal, 0); + if (IS_ERR(cal->cc[0])) + return PTR_ERR(cal->cc[0]); - if (cal_data_get_num_csi2_phy(dev) > 1) { - dev->cc[1] = cc_create(dev, 1); - if (IS_ERR(dev->cc[1])) - return PTR_ERR(dev->cc[1]); + if (cal_data_get_num_csi2_phy(cal) > 1) { + cal->cc[1] = cc_create(cal, 1); + if (IS_ERR(cal->cc[1])) + return PTR_ERR(cal->cc[1]); } else { - dev->cc[1] = NULL; + cal->cc[1] = NULL; } - dev->ctx[0] = NULL; - dev->ctx[1] = NULL; + cal->ctx[0] = NULL; + cal->ctx[1] = NULL; - dev->ctx[0] = cal_create_instance(dev, 0); - if (cal_data_get_num_csi2_phy(dev) > 1) - dev->ctx[1] = cal_create_instance(dev, 1); - if (!dev->ctx[0] && !dev->ctx[1]) { - cal_err(dev, "Neither port is configured, no point in staying up\n"); + cal->ctx[0] = cal_create_instance(cal, 0); + if (cal_data_get_num_csi2_phy(cal) > 1) + cal->ctx[1] = cal_create_instance(cal, 1); + if (!cal->ctx[0] && !cal->ctx[1]) { + cal_err(cal, "Neither port is configured, no point in staying up\n"); return -ENODEV; } @@ -2343,7 +2347,7 @@ static int cal_probe(struct platform_device *pdev) goto runtime_disable; /* Just check we can actually access the module */ - cal_get_hwinfo(dev); + cal_get_hwinfo(cal); pm_runtime_put_sync(&pdev->dev); @@ -2354,7 +2358,7 @@ runtime_disable: pm_runtime_disable(&pdev->dev); for (i = 0; i < CAL_NUM_CONTEXT; i++) { - ctx = dev->ctx[i]; + ctx = cal->ctx[i]; if (ctx) { v4l2_async_notifier_unregister(&ctx->notifier); v4l2_async_notifier_cleanup(&ctx->notifier); @@ -2368,16 +2372,16 @@ runtime_disable: static int cal_remove(struct platform_device *pdev) { - struct cal_dev *dev = platform_get_drvdata(pdev); + struct cal_dev *cal = platform_get_drvdata(pdev); struct cal_ctx *ctx; unsigned int i; - cal_dbg(1, dev, "Removing %s\n", CAL_MODULE_NAME); + cal_dbg(1, cal, "Removing %s\n", CAL_MODULE_NAME); pm_runtime_get_sync(&pdev->dev); for (i = 0; i < CAL_NUM_CONTEXT; i++) { - ctx = dev->ctx[i]; + ctx = cal->ctx[i]; if (ctx) { ctx_dbg(1, ctx, "unregistering %s\n", video_device_node_name(&ctx->vdev)); @@ -2400,15 +2404,15 @@ static int cal_remove(struct platform_device *pdev) static int cal_runtime_resume(struct device *dev) { - struct cal_dev *caldev = dev_get_drvdata(dev); + struct cal_dev *cal = dev_get_drvdata(dev); - if (caldev->data->flags & DRA72_CAL_PRE_ES2_LDO_DISABLE) { + if (cal->data->flags & DRA72_CAL_PRE_ES2_LDO_DISABLE) { /* * Apply errata on both port everytime we (re-)enable * the clock */ - i913_errata(caldev, 0); - i913_errata(caldev, 1); + i913_errata(cal, 0); + i913_errata(cal, 1); } return 0; -- GitLab From d8444ea02f3bf366de6eee1fa9359e1772ad24a4 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 6 Jul 2020 20:35:48 +0200 Subject: [PATCH 0560/1476] media: ti-vpe: cal: Name all cal_camerarx pointers consistently Name all variables that point to a cal_camerax instance 'phy' instead of 'cc'. The name 'cc' refers to Camera Core, but is not commonly used in the driver or in datasheets. Signed-off-by: Laurent Pinchart Reviewed-by: Tomi Valkeinen Reviewed-by: Benoit Parrot Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/ti-vpe/cal.c | 102 ++++++++++++++-------------- 1 file changed, 51 insertions(+), 51 deletions(-) diff --git a/drivers/media/platform/ti-vpe/cal.c b/drivers/media/platform/ti-vpe/cal.c index f0e06a4699fce..d376cdd32f720 100644 --- a/drivers/media/platform/ti-vpe/cal.c +++ b/drivers/media/platform/ti-vpe/cal.c @@ -312,7 +312,8 @@ static const struct cal_data am654_cal_data = { * the CAMERARX instances. Instances of struct cal_dev are named cal through the * driver. * - * The cal_camerarx structure represents one CAMERARX instance. + * The cal_camerarx structure represents one CAMERARX instance. Instances of + * cal_camerarx are named phy through the driver. * * The cal_ctx structure represents the combination of one CSI-2 context, one * processing context and one DMA context. Instance of struct cal_ctx are named @@ -344,7 +345,7 @@ struct cal_dev { u32 syscon_camerrx_offset; /* Camera Core Module handle */ - struct cal_camerarx *cc[CAL_NUM_CSI2_PORTS]; + struct cal_camerarx *phy[CAL_NUM_CSI2_PORTS]; struct cal_ctx *ctx[CAL_NUM_CONTEXT]; }; @@ -361,7 +362,7 @@ struct cal_ctx { struct v4l2_fwnode_endpoint endpoint; struct cal_dev *cal; - struct cal_camerarx *cc; + struct cal_camerarx *phy; /* v4l2_ioctl mutex */ struct mutex mutex; @@ -468,7 +469,7 @@ static u32 cal_data_get_num_csi2_phy(struct cal_dev *cal) } static int cal_camerarx_regmap_init(struct cal_dev *cal, - struct cal_camerarx *cc, + struct cal_camerarx *phy, unsigned int idx) { const struct cal_camerarx_data *phy_data; @@ -490,12 +491,12 @@ static int cal_camerarx_regmap_init(struct cal_dev *cal, * Here we update the reg offset with the * value found in DT */ - cc->phy.fields[i] = devm_regmap_field_alloc(&cal->pdev->dev, - cal->syscon_camerrx, - field); - if (IS_ERR(cc->phy.fields[i])) { + phy->phy.fields[i] = devm_regmap_field_alloc(&cal->pdev->dev, + cal->syscon_camerrx, + field); + if (IS_ERR(phy->phy.fields[i])) { cal_err(cal, "Unable to allocate regmap fields\n"); - return PTR_ERR(cc->phy.fields[i]); + return PTR_ERR(phy->phy.fields[i]); } } @@ -539,25 +540,25 @@ static struct regmap *cal_get_camerarx_regmap(struct cal_dev *cal) static void camerarx_phy_enable(struct cal_ctx *ctx) { u32 phy_id = ctx->csi2_port; - struct cal_camerarx *cc = ctx->cal->cc[phy_id]; + struct cal_camerarx *phy = ctx->cal->phy[phy_id]; u32 max_lanes; - regmap_field_write(cc->phy.fields[F_CAMMODE], 0); + regmap_field_write(phy->phy.fields[F_CAMMODE], 0); /* Always enable all lanes at the phy control level */ max_lanes = (1 << cal_data_get_phy_max_lanes(ctx)) - 1; - regmap_field_write(cc->phy.fields[F_LANEENABLE], max_lanes); + regmap_field_write(phy->phy.fields[F_LANEENABLE], max_lanes); /* F_CSI_MODE is not present on every architecture */ - if (cc->phy.fields[F_CSI_MODE]) - regmap_field_write(cc->phy.fields[F_CSI_MODE], 1); - regmap_field_write(cc->phy.fields[F_CTRLCLKEN], 1); + if (phy->phy.fields[F_CSI_MODE]) + regmap_field_write(phy->phy.fields[F_CSI_MODE], 1); + regmap_field_write(phy->phy.fields[F_CTRLCLKEN], 1); } static void camerarx_phy_disable(struct cal_ctx *ctx) { u32 phy_id = ctx->csi2_port; - struct cal_camerarx *cc = ctx->cal->cc[phy_id]; + struct cal_camerarx *phy = ctx->cal->phy[phy_id]; - regmap_field_write(cc->phy.fields[F_CTRLCLKEN], 0); + regmap_field_write(phy->phy.fields[F_CTRLCLKEN], 0); } /* @@ -566,32 +567,31 @@ static void camerarx_phy_disable(struct cal_ctx *ctx) static struct cal_camerarx *cc_create(struct cal_dev *cal, unsigned int core) { struct platform_device *pdev = cal->pdev; - struct cal_camerarx *cc; + struct cal_camerarx *phy; int ret; - cc = devm_kzalloc(&pdev->dev, sizeof(*cc), GFP_KERNEL); - if (!cc) + phy = devm_kzalloc(&pdev->dev, sizeof(*phy), GFP_KERNEL); + if (!phy) return ERR_PTR(-ENOMEM); - cc->res = platform_get_resource_byname(pdev, - IORESOURCE_MEM, - (core == 0) ? + phy->res = platform_get_resource_byname(pdev, IORESOURCE_MEM, + (core == 0) ? "cal_rx_core0" : "cal_rx_core1"); - cc->base = devm_ioremap_resource(&pdev->dev, cc->res); - if (IS_ERR(cc->base)) { + phy->base = devm_ioremap_resource(&pdev->dev, phy->res); + if (IS_ERR(phy->base)) { cal_err(cal, "failed to ioremap\n"); - return ERR_CAST(cc->base); + return ERR_CAST(phy->base); } cal_dbg(1, cal, "ioresource %s at %pa - %pa\n", - cc->res->name, &cc->res->start, &cc->res->end); + phy->res->name, &phy->res->start, &phy->res->end); - ret = cal_camerarx_regmap_init(cal, cc, core); + ret = cal_camerarx_regmap_init(cal, phy, core); if (ret) return ERR_PTR(ret); - return cc; + return phy; } /* @@ -632,12 +632,12 @@ static void cal_get_hwinfo(struct cal_dev *cal) */ static void i913_errata(struct cal_dev *cal, unsigned int port) { - u32 reg10 = reg_read(cal->cc[port], CAL_CSI2_PHY_REG10); + u32 reg10 = reg_read(cal->phy[port], CAL_CSI2_PHY_REG10); set_field(®10, 1, CAL_CSI2_PHY_REG10_I933_LDO_DISABLE_MASK); cal_dbg(1, cal, "CSI2_%d_REG10 = 0x%08x\n", port, reg10); - reg_write(cal->cc[port], CAL_CSI2_PHY_REG10, reg10); + reg_write(cal->phy[port], CAL_CSI2_PHY_REG10, reg10); } static void cal_quickdump_regs(struct cal_dev *cal) @@ -649,19 +649,19 @@ static void cal_quickdump_regs(struct cal_dev *cal) if (cal->ctx[0]) { cal_info(cal, "CSI2 Core 0 Registers @ %pa:\n", - &cal->ctx[0]->cc->res->start); + &cal->ctx[0]->phy->res->start); print_hex_dump(KERN_INFO, "", DUMP_PREFIX_OFFSET, 16, 4, - (__force const void *)cal->ctx[0]->cc->base, - resource_size(cal->ctx[0]->cc->res), + (__force const void *)cal->ctx[0]->phy->base, + resource_size(cal->ctx[0]->phy->res), false); } if (cal->ctx[1]) { cal_info(cal, "CSI2 Core 1 Registers @ %pa:\n", - &cal->ctx[1]->cc->res->start); + &cal->ctx[1]->phy->res->start); print_hex_dump(KERN_INFO, "", DUMP_PREFIX_OFFSET, 16, 4, - (__force const void *)cal->ctx[1]->cc->base, - resource_size(cal->ctx[1]->cc->res), + (__force const void *)cal->ctx[1]->phy->base, + resource_size(cal->ctx[1]->phy->res), false); } } @@ -781,23 +781,23 @@ static void csi2_phy_config(struct cal_ctx *ctx) ths_settle = (105 * csi2_ddrclk_khz / 1000000) + 4; ctx_dbg(1, ctx, "ths_settle: %d (0x%02x)\n", ths_settle, ths_settle); - reg0 = reg_read(ctx->cc, CAL_CSI2_PHY_REG0); + reg0 = reg_read(ctx->phy, CAL_CSI2_PHY_REG0); set_field(®0, CAL_CSI2_PHY_REG0_HSCLOCKCONFIG_DISABLE, CAL_CSI2_PHY_REG0_HSCLOCKCONFIG_MASK); set_field(®0, ths_term, CAL_CSI2_PHY_REG0_THS_TERM_MASK); set_field(®0, ths_settle, CAL_CSI2_PHY_REG0_THS_SETTLE_MASK); ctx_dbg(1, ctx, "CSI2_%d_REG0 = 0x%08x\n", ctx->csi2_port, reg0); - reg_write(ctx->cc, CAL_CSI2_PHY_REG0, reg0); + reg_write(ctx->phy, CAL_CSI2_PHY_REG0, reg0); - reg1 = reg_read(ctx->cc, CAL_CSI2_PHY_REG1); + reg1 = reg_read(ctx->phy, CAL_CSI2_PHY_REG1); set_field(®1, TCLK_TERM, CAL_CSI2_PHY_REG1_TCLK_TERM_MASK); set_field(®1, 0xb8, CAL_CSI2_PHY_REG1_DPHY_HS_SYNC_PATTERN_MASK); set_field(®1, TCLK_MISS, CAL_CSI2_PHY_REG1_CTRLCLK_DIV_FACTOR_MASK); set_field(®1, TCLK_SETTLE, CAL_CSI2_PHY_REG1_TCLK_SETTLE_MASK); ctx_dbg(1, ctx, "CSI2_%d_REG1 = 0x%08x\n", ctx->csi2_port, reg1); - reg_write(ctx->cc, CAL_CSI2_PHY_REG1, reg1); + reg_write(ctx->phy, CAL_CSI2_PHY_REG1, reg1); } static void csi2_phy_init(struct cal_ctx *ctx) @@ -834,7 +834,7 @@ static void csi2_phy_init(struct cal_ctx *ctx) reg_read(ctx->cal, CAL_CSI2_COMPLEXIO_CFG(ctx->csi2_port))); /* Dummy read to allow SCP reset to complete */ - reg_read(ctx->cc, CAL_CSI2_PHY_REG0); + reg_read(ctx->phy, CAL_CSI2_PHY_REG0); /* 3.A. Program Phy Timing Parameters */ csi2_phy_config(ctx); @@ -926,7 +926,7 @@ static void csi2_wait_for_phy(struct cal_ctx *ctx) csi2_wait_stop_state(ctx); ctx_dbg(1, ctx, "CSI2_%d_REG1 = 0x%08x (Bit(31,28) should be set!)\n", - ctx->csi2_port, reg_read(ctx->cc, CAL_CSI2_PHY_REG1)); + ctx->csi2_port, reg_read(ctx->phy, CAL_CSI2_PHY_REG1)); } static void csi2_phy_deinit(struct cal_ctx *ctx) @@ -2192,7 +2192,7 @@ static struct cal_ctx *cal_create_instance(struct cal_dev *cal, int inst) ctx->v4l2_dev.ctrl_handler = hdl; /* Make sure Camera Core H/W register area is available */ - ctx->cc = cal->cc[inst]; + ctx->phy = cal->phy[inst]; /* Store the instance id */ ctx->csi2_port = inst; @@ -2315,16 +2315,16 @@ static int cal_probe(struct platform_device *pdev) platform_set_drvdata(pdev, cal); - cal->cc[0] = cc_create(cal, 0); - if (IS_ERR(cal->cc[0])) - return PTR_ERR(cal->cc[0]); + cal->phy[0] = cc_create(cal, 0); + if (IS_ERR(cal->phy[0])) + return PTR_ERR(cal->phy[0]); if (cal_data_get_num_csi2_phy(cal) > 1) { - cal->cc[1] = cc_create(cal, 1); - if (IS_ERR(cal->cc[1])) - return PTR_ERR(cal->cc[1]); + cal->phy[1] = cc_create(cal, 1); + if (IS_ERR(cal->phy[1])) + return PTR_ERR(cal->phy[1]); } else { - cal->cc[1] = NULL; + cal->phy[1] = NULL; } cal->ctx[0] = NULL; -- GitLab From be28027075adb8a940f340a2fe4e5ce18b500ed4 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 6 Jul 2020 20:35:49 +0200 Subject: [PATCH 0561/1476] media: ti-vpe: cal: Remove internal phy structure from cal_camerarx The cal_camerarx structure describes the PHY, there's no need for an internal structure named phy. Removed that level of indirection. Signed-off-by: Laurent Pinchart Reviewed-by: Tomi Valkeinen Reviewed-by: Benoit Parrot Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/ti-vpe/cal.c | 27 ++++++++++++--------------- 1 file changed, 12 insertions(+), 15 deletions(-) diff --git a/drivers/media/platform/ti-vpe/cal.c b/drivers/media/platform/ti-vpe/cal.c index d376cdd32f720..471e2581b28b9 100644 --- a/drivers/media/platform/ti-vpe/cal.c +++ b/drivers/media/platform/ti-vpe/cal.c @@ -324,10 +324,7 @@ struct cal_camerarx { void __iomem *base; struct resource *res; struct platform_device *pdev; - - struct { - struct regmap_field *fields[F_MAX_FIELDS]; - } phy; + struct regmap_field *fields[F_MAX_FIELDS]; }; struct cal_dev { @@ -491,12 +488,12 @@ static int cal_camerarx_regmap_init(struct cal_dev *cal, * Here we update the reg offset with the * value found in DT */ - phy->phy.fields[i] = devm_regmap_field_alloc(&cal->pdev->dev, - cal->syscon_camerrx, - field); - if (IS_ERR(phy->phy.fields[i])) { + phy->fields[i] = devm_regmap_field_alloc(&cal->pdev->dev, + cal->syscon_camerrx, + field); + if (IS_ERR(phy->fields[i])) { cal_err(cal, "Unable to allocate regmap fields\n"); - return PTR_ERR(phy->phy.fields[i]); + return PTR_ERR(phy->fields[i]); } } @@ -543,14 +540,14 @@ static void camerarx_phy_enable(struct cal_ctx *ctx) struct cal_camerarx *phy = ctx->cal->phy[phy_id]; u32 max_lanes; - regmap_field_write(phy->phy.fields[F_CAMMODE], 0); + regmap_field_write(phy->fields[F_CAMMODE], 0); /* Always enable all lanes at the phy control level */ max_lanes = (1 << cal_data_get_phy_max_lanes(ctx)) - 1; - regmap_field_write(phy->phy.fields[F_LANEENABLE], max_lanes); + regmap_field_write(phy->fields[F_LANEENABLE], max_lanes); /* F_CSI_MODE is not present on every architecture */ - if (phy->phy.fields[F_CSI_MODE]) - regmap_field_write(phy->phy.fields[F_CSI_MODE], 1); - regmap_field_write(phy->phy.fields[F_CTRLCLKEN], 1); + if (phy->fields[F_CSI_MODE]) + regmap_field_write(phy->fields[F_CSI_MODE], 1); + regmap_field_write(phy->fields[F_CTRLCLKEN], 1); } static void camerarx_phy_disable(struct cal_ctx *ctx) @@ -558,7 +555,7 @@ static void camerarx_phy_disable(struct cal_ctx *ctx) u32 phy_id = ctx->csi2_port; struct cal_camerarx *phy = ctx->cal->phy[phy_id]; - regmap_field_write(phy->phy.fields[F_CTRLCLKEN], 0); + regmap_field_write(phy->fields[F_CTRLCLKEN], 0); } /* -- GitLab From d01a7fea9d7e48eefe1b85a8706fe4bed06589db Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 6 Jul 2020 20:35:50 +0200 Subject: [PATCH 0562/1476] media: ti-vpe: cal: Store instance ID and cal pointer in cal_camerarx Store a pointer to the cal_dev and the cal_camerarx instance number in the cal_camerarx structure. This prepares for passing a cal_camerarx pointer instead of a cal_ctx pointer to multiple functions that deal with the CAMERARX. Signed-off-by: Laurent Pinchart Reviewed-by: Tomi Valkeinen Reviewed-by: Benoit Parrot Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/ti-vpe/cal.c | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/drivers/media/platform/ti-vpe/cal.c b/drivers/media/platform/ti-vpe/cal.c index 471e2581b28b9..ff9bc3ae58ba6 100644 --- a/drivers/media/platform/ti-vpe/cal.c +++ b/drivers/media/platform/ti-vpe/cal.c @@ -325,6 +325,9 @@ struct cal_camerarx { struct resource *res; struct platform_device *pdev; struct regmap_field *fields[F_MAX_FIELDS]; + + struct cal_dev *cal; + unsigned int instance; }; struct cal_dev { @@ -466,8 +469,7 @@ static u32 cal_data_get_num_csi2_phy(struct cal_dev *cal) } static int cal_camerarx_regmap_init(struct cal_dev *cal, - struct cal_camerarx *phy, - unsigned int idx) + struct cal_camerarx *phy) { const struct cal_camerarx_data *phy_data; unsigned int i; @@ -475,7 +477,7 @@ static int cal_camerarx_regmap_init(struct cal_dev *cal, if (!cal->data) return -EINVAL; - phy_data = &cal->data->camerarx[idx]; + phy_data = &cal->data->camerarx[phy->instance]; for (i = 0; i < F_MAX_FIELDS; i++) { struct reg_field field = { @@ -561,7 +563,8 @@ static void camerarx_phy_disable(struct cal_ctx *ctx) /* * Camera Instance access block */ -static struct cal_camerarx *cc_create(struct cal_dev *cal, unsigned int core) +static struct cal_camerarx *cc_create(struct cal_dev *cal, + unsigned int instance) { struct platform_device *pdev = cal->pdev; struct cal_camerarx *phy; @@ -571,8 +574,11 @@ static struct cal_camerarx *cc_create(struct cal_dev *cal, unsigned int core) if (!phy) return ERR_PTR(-ENOMEM); + phy->cal = cal; + phy->instance = instance; + phy->res = platform_get_resource_byname(pdev, IORESOURCE_MEM, - (core == 0) ? + (instance == 0) ? "cal_rx_core0" : "cal_rx_core1"); phy->base = devm_ioremap_resource(&pdev->dev, phy->res); @@ -584,7 +590,7 @@ static struct cal_camerarx *cc_create(struct cal_dev *cal, unsigned int core) cal_dbg(1, cal, "ioresource %s at %pa - %pa\n", phy->res->name, &phy->res->start, &phy->res->end); - ret = cal_camerarx_regmap_init(cal, phy, core); + ret = cal_camerarx_regmap_init(cal, phy); if (ret) return ERR_PTR(ret); -- GitLab From 552b07b56eba3e7883a69aa0aad80f2c09d87cc1 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 6 Jul 2020 20:35:51 +0200 Subject: [PATCH 0563/1476] media: ti-vpe: cal: Use dev_* print macros Use the dev_* print macros instead of the v4l2_* print macros. This prepares for a common print infrastructure that will also support the cal_camerarx instances. Signed-off-by: Laurent Pinchart Reviewed-by: Tomi Valkeinen Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/ti-vpe/cal.c | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/drivers/media/platform/ti-vpe/cal.c b/drivers/media/platform/ti-vpe/cal.c index ff9bc3ae58ba6..4b718b4a79c96 100644 --- a/drivers/media/platform/ti-vpe/cal.c +++ b/drivers/media/platform/ti-vpe/cal.c @@ -53,19 +53,23 @@ static unsigned debug; module_param(debug, uint, 0644); MODULE_PARM_DESC(debug, "activates debug info"); -#define cal_dbg(level, cal, fmt, arg...) \ - v4l2_dbg(level, debug, &cal->v4l2_dev, fmt, ##arg) +#define cal_dbg(level, cal, fmt, arg...) \ + do { \ + if (debug >= (level)) \ + dev_printk(KERN_DEBUG, &(cal)->pdev->dev, \ + fmt, ##arg); \ + } while (0) #define cal_info(cal, fmt, arg...) \ - v4l2_info(&cal->v4l2_dev, fmt, ##arg) + dev_info(&(cal)->pdev->dev, fmt, ##arg) #define cal_err(cal, fmt, arg...) \ - v4l2_err(&cal->v4l2_dev, fmt, ##arg) + dev_err(&(cal)->pdev->dev, fmt, ##arg) #define ctx_dbg(level, ctx, fmt, arg...) \ - v4l2_dbg(level, debug, &ctx->v4l2_dev, fmt, ##arg) + cal_dbg(level, (ctx)->cal, "ctx%u: " fmt, (ctx)->csi2_port, ##arg) #define ctx_info(ctx, fmt, arg...) \ - v4l2_info(&ctx->v4l2_dev, fmt, ##arg) + cal_info((ctx)->cal, "ctx%u: " fmt, (ctx)->csi2_port, ##arg) #define ctx_err(ctx, fmt, arg...) \ - v4l2_err(&ctx->v4l2_dev, fmt, ##arg) + cal_err((ctx)->cal, "ctx%u: " fmt, (ctx)->csi2_port, ##arg) #define CAL_NUM_CONTEXT 2 -- GitLab From 6961f5d60108bb8f4a3154bb3780011a26323537 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 6 Jul 2020 20:35:52 +0200 Subject: [PATCH 0564/1476] media: ti-vpe: cal: Add print macros for the cal_camerarx instances Prepare for passing the cal_camerarx pointer instead of the cal_ctx pointer to CAMERARX-related functions by adding print macros for cal_camerarx. Signed-off-by: Laurent Pinchart Reviewed-by: Tomi Valkeinen Reviewed-by: Benoit Parrot Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/ti-vpe/cal.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/media/platform/ti-vpe/cal.c b/drivers/media/platform/ti-vpe/cal.c index 4b718b4a79c96..7ec2b6febf710 100644 --- a/drivers/media/platform/ti-vpe/cal.c +++ b/drivers/media/platform/ti-vpe/cal.c @@ -71,6 +71,13 @@ MODULE_PARM_DESC(debug, "activates debug info"); #define ctx_err(ctx, fmt, arg...) \ cal_err((ctx)->cal, "ctx%u: " fmt, (ctx)->csi2_port, ##arg) +#define phy_dbg(level, phy, fmt, arg...) \ + cal_dbg(level, (phy)->cal, "phy%u: " fmt, (phy)->instance, ##arg) +#define phy_info(phy, fmt, arg...) \ + cal_info((phy)->cal, "phy%u: " fmt, (phy)->instance, ##arg) +#define phy_err(phy, fmt, arg...) \ + cal_err((phy)->cal, "phy%u: " fmt, (phy)->instance, ##arg) + #define CAL_NUM_CONTEXT 2 #define reg_read(dev, offset) ioread32(dev->base + offset) -- GitLab From 559cd8b836a15f27b40e877e7158373ecb43fc76 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 6 Jul 2020 20:35:53 +0200 Subject: [PATCH 0565/1476] media: ti-vpe: cal: Store sensor-related data in cal_camerarx The sensor is connected to the CAMERARX. Store its pointer, as well as the sensor pixel rate and the endpoint, in the cal_camerarx. This prepares for decoupling the cal_camerarx and cal_ctx. Signed-off-by: Laurent Pinchart Reviewed-by: Tomi Valkeinen Reviewed-by: Benoit Parrot Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/ti-vpe/cal.c | 59 +++++++++++++++-------------- 1 file changed, 30 insertions(+), 29 deletions(-) diff --git a/drivers/media/platform/ti-vpe/cal.c b/drivers/media/platform/ti-vpe/cal.c index 7ec2b6febf710..eb5fd81eda379 100644 --- a/drivers/media/platform/ti-vpe/cal.c +++ b/drivers/media/platform/ti-vpe/cal.c @@ -339,6 +339,10 @@ struct cal_camerarx { struct cal_dev *cal; unsigned int instance; + + struct v4l2_fwnode_endpoint endpoint; + struct v4l2_subdev *sensor; + unsigned int external_rate; }; struct cal_dev { @@ -369,8 +373,6 @@ struct cal_ctx { struct v4l2_ctrl_handler ctrl_handler; struct video_device vdev; struct v4l2_async_notifier notifier; - struct v4l2_subdev *sensor; - struct v4l2_fwnode_endpoint endpoint; struct cal_dev *cal; struct cal_camerarx *phy; @@ -394,7 +396,6 @@ struct cal_ctx { unsigned int num_active_fmt; unsigned int sequence; - unsigned int external_rate; struct vb2_queue vb_vidq; unsigned int csi2_port; unsigned int cport; @@ -587,6 +588,7 @@ static struct cal_camerarx *cc_create(struct cal_dev *cal, phy->cal = cal; phy->instance = instance; + phy->external_rate = 192000000; phy->res = platform_get_resource_byname(pdev, IORESOURCE_MEM, (instance == 0) ? @@ -778,12 +780,12 @@ static void csi2_phy_config(struct cal_ctx *ctx) unsigned int ths_term, ths_settle; unsigned int csi2_ddrclk_khz; struct v4l2_fwnode_bus_mipi_csi2 *mipi_csi2 = - &ctx->endpoint.bus.mipi_csi2; + &ctx->phy->endpoint.bus.mipi_csi2; u32 num_lanes = mipi_csi2->num_data_lanes; /* DPHY timing configuration */ /* CSI-2 is DDR and we only count used lanes. */ - csi2_ddrclk_khz = ctx->external_rate / 1000 + csi2_ddrclk_khz = ctx->phy->external_rate / 1000 / (2 * num_lanes) * ctx->fmt->bpp; ctx_dbg(1, ctx, "csi2_ddrclk_khz: %d\n", csi2_ddrclk_khz); @@ -978,7 +980,7 @@ static void csi2_lane_config(struct cal_ctx *ctx) u32 lane_mask = CAL_CSI2_COMPLEXIO_CFG_CLOCK_POSITION_MASK; u32 polarity_mask = CAL_CSI2_COMPLEXIO_CFG_CLOCK_POL_MASK; struct v4l2_fwnode_bus_mipi_csi2 *mipi_csi2 = - &ctx->endpoint.bus.mipi_csi2; + &ctx->phy->endpoint.bus.mipi_csi2; int lane; set_field(&val, mipi_csi2->clock_lane + 1, lane_mask); @@ -1149,22 +1151,22 @@ static void cal_wr_dma_addr(struct cal_ctx *ctx, unsigned int dmaaddr) reg_write(ctx->cal, CAL_WR_DMA_ADDR(ctx->csi2_port), dmaaddr); } -static int cal_get_external_info(struct cal_ctx *ctx) +static int cal_get_external_info(struct cal_camerarx *phy) { struct v4l2_ctrl *ctrl; - if (!ctx->sensor) + if (!phy->sensor) return -ENODEV; - ctrl = v4l2_ctrl_find(ctx->sensor->ctrl_handler, V4L2_CID_PIXEL_RATE); + ctrl = v4l2_ctrl_find(phy->sensor->ctrl_handler, V4L2_CID_PIXEL_RATE); if (!ctrl) { - ctx_err(ctx, "no pixel rate control in subdev: %s\n", - ctx->sensor->name); + phy_err(phy, "no pixel rate control in subdev: %s\n", + phy->sensor->name); return -EPIPE; } - ctx->external_rate = v4l2_ctrl_g_ctrl_int64(ctrl); - ctx_dbg(3, ctx, "sensor Pixel Rate: %d\n", ctx->external_rate); + phy->external_rate = v4l2_ctrl_g_ctrl_int64(ctrl); + phy_dbg(3, phy, "sensor Pixel Rate: %u\n", phy->external_rate); return 0; } @@ -1319,7 +1321,7 @@ static int __subdev_get_format(struct cal_ctx *ctx, sd_fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE; sd_fmt.pad = 0; - ret = v4l2_subdev_call(ctx->sensor, pad, get_fmt, NULL, &sd_fmt); + ret = v4l2_subdev_call(ctx->phy->sensor, pad, get_fmt, NULL, &sd_fmt); if (ret) return ret; @@ -1342,7 +1344,7 @@ static int __subdev_set_format(struct cal_ctx *ctx, sd_fmt.pad = 0; *mbus_fmt = *fmt; - ret = v4l2_subdev_call(ctx->sensor, pad, set_fmt, NULL, &sd_fmt); + ret = v4l2_subdev_call(ctx->phy->sensor, pad, set_fmt, NULL, &sd_fmt); if (ret) return ret; @@ -1423,7 +1425,7 @@ static int cal_try_fmt_vid_cap(struct file *file, void *priv, fse.code = fmt->code; fse.which = V4L2_SUBDEV_FORMAT_ACTIVE; for (fse.index = 0; ; fse.index++) { - ret = v4l2_subdev_call(ctx->sensor, pad, enum_frame_size, + ret = v4l2_subdev_call(ctx->phy->sensor, pad, enum_frame_size, NULL, &fse); if (ret) break; @@ -1521,7 +1523,8 @@ static int cal_enum_framesizes(struct file *file, void *fh, fse.code = fmt->code; fse.which = V4L2_SUBDEV_FORMAT_ACTIVE; - ret = v4l2_subdev_call(ctx->sensor, pad, enum_frame_size, NULL, &fse); + ret = v4l2_subdev_call(ctx->phy->sensor, pad, enum_frame_size, NULL, + &fse); if (ret) return ret; @@ -1577,7 +1580,7 @@ static int cal_enum_frameintervals(struct file *file, void *priv, return -EINVAL; fie.code = fmt->code; - ret = v4l2_subdev_call(ctx->sensor, pad, enum_frame_interval, + ret = v4l2_subdev_call(ctx->phy->sensor, pad, enum_frame_interval, NULL, &fie); if (ret) return ret; @@ -1675,11 +1678,11 @@ static int cal_start_streaming(struct vb2_queue *vq, unsigned int count) addr = vb2_dma_contig_plane_dma_addr(&ctx->cur_frm->vb.vb2_buf, 0); ctx->sequence = 0; - ret = cal_get_external_info(ctx); + ret = cal_get_external_info(ctx->phy); if (ret < 0) goto err; - ret = v4l2_subdev_call(ctx->sensor, core, s_power, 1); + ret = v4l2_subdev_call(ctx->phy->sensor, core, s_power, 1); if (ret < 0 && ret != -ENOIOCTLCMD && ret != -ENODEV) { ctx_err(ctx, "power on failed in subdev\n"); goto err; @@ -1696,9 +1699,9 @@ static int cal_start_streaming(struct vb2_queue *vq, unsigned int count) enable_irqs(ctx); csi2_phy_init(ctx); - ret = v4l2_subdev_call(ctx->sensor, video, s_stream, 1); + ret = v4l2_subdev_call(ctx->phy->sensor, video, s_stream, 1); if (ret) { - v4l2_subdev_call(ctx->sensor, core, s_power, 0); + v4l2_subdev_call(ctx->phy->sensor, core, s_power, 0); ctx_err(ctx, "stream on failed in subdev\n"); pm_runtime_put_sync(&ctx->cal->pdev->dev); goto err; @@ -1755,10 +1758,10 @@ static void cal_stop_streaming(struct vb2_queue *vq) disable_irqs(ctx); csi2_phy_deinit(ctx); - if (v4l2_subdev_call(ctx->sensor, video, s_stream, 0)) + if (v4l2_subdev_call(ctx->phy->sensor, video, s_stream, 0)) ctx_err(ctx, "stream off failed in subdev\n"); - ret = v4l2_subdev_call(ctx->sensor, core, s_power, 0); + ret = v4l2_subdev_call(ctx->phy->sensor, core, s_power, 0); if (ret < 0 && ret != -ENOIOCTLCMD && ret != -ENODEV) ctx_err(ctx, "power off failed in subdev\n"); @@ -1849,8 +1852,6 @@ static int cal_complete_ctx(struct cal_ctx *ctx) struct vb2_queue *q; int ret; - ctx->external_rate = 192000000; - /* initialize locks */ spin_lock_init(&ctx->slock); mutex_init(&ctx->mutex); @@ -1906,13 +1907,13 @@ static int cal_async_bound(struct v4l2_async_notifier *notifier, unsigned int i, j, k; int ret = 0; - if (ctx->sensor) { + if (ctx->phy->sensor) { ctx_info(ctx, "Rejecting subdev %s (Already set!!)", subdev->name); return 0; } - ctx->sensor = subdev; + ctx->phy->sensor = subdev; ctx_dbg(1, ctx, "Using sensor %s for capture\n", subdev->name); /* Enumerate sub device formats and enable all matching local formats */ @@ -2065,7 +2066,7 @@ static int of_cal_create_instance(struct cal_ctx *ctx, int inst) parent = pdev->dev.of_node; - endpoint = &ctx->endpoint; + endpoint = &ctx->phy->endpoint; ep_node = NULL; port = NULL; -- GitLab From f7cd15eb0b852ccc82d6b03e9cde7df9967c5195 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 6 Jul 2020 20:35:54 +0200 Subject: [PATCH 0566/1476] media: ti-vpe: cal: Create consistent naming for CAMERARX functions Rename all functions related to CAMERARX with a cal_camerarx_ prefix, and pass them a cal_camerarx pointer. This performs most of the decoupling of the CAMERARX from the context. Signed-off-by: Laurent Pinchart Reviewed-by: Tomi Valkeinen Reviewed-by: Benoit Parrot Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/ti-vpe/cal.c | 243 ++++++++++++++-------------- 1 file changed, 119 insertions(+), 124 deletions(-) diff --git a/drivers/media/platform/ti-vpe/cal.c b/drivers/media/platform/ti-vpe/cal.c index eb5fd81eda379..bbf7acab17af2 100644 --- a/drivers/media/platform/ti-vpe/cal.c +++ b/drivers/media/platform/ti-vpe/cal.c @@ -468,11 +468,9 @@ static inline void set_field(u32 *valp, u32 field, u32 mask) *valp = val; } -static u32 cal_data_get_phy_max_lanes(struct cal_ctx *ctx) +static u32 cal_camerarx_max_lanes(struct cal_camerarx *phy) { - u32 phy_id = ctx->csi2_port; - - return ctx->cal->data->camerarx[phy_id].num_lanes; + return phy->cal->data->camerarx[phy->instance].num_lanes; } static u32 cal_data_get_num_csi2_phy(struct cal_dev *cal) @@ -548,15 +546,13 @@ static struct regmap *cal_get_camerarx_regmap(struct cal_dev *cal) /* * Control Module CAMERARX block access */ -static void camerarx_phy_enable(struct cal_ctx *ctx) +static void cal_camerarx_enable(struct cal_camerarx *phy) { - u32 phy_id = ctx->csi2_port; - struct cal_camerarx *phy = ctx->cal->phy[phy_id]; u32 max_lanes; regmap_field_write(phy->fields[F_CAMMODE], 0); /* Always enable all lanes at the phy control level */ - max_lanes = (1 << cal_data_get_phy_max_lanes(ctx)) - 1; + max_lanes = (1 << cal_camerarx_max_lanes(phy)) - 1; regmap_field_write(phy->fields[F_LANEENABLE], max_lanes); /* F_CSI_MODE is not present on every architecture */ if (phy->fields[F_CSI_MODE]) @@ -564,19 +560,16 @@ static void camerarx_phy_enable(struct cal_ctx *ctx) regmap_field_write(phy->fields[F_CTRLCLKEN], 1); } -static void camerarx_phy_disable(struct cal_ctx *ctx) +static void cal_camerarx_disable(struct cal_camerarx *phy) { - u32 phy_id = ctx->csi2_port; - struct cal_camerarx *phy = ctx->cal->phy[phy_id]; - regmap_field_write(phy->fields[F_CTRLCLKEN], 0); } /* * Camera Instance access block */ -static struct cal_camerarx *cc_create(struct cal_dev *cal, - unsigned int instance) +static struct cal_camerarx *cal_camerarx_create(struct cal_dev *cal, + unsigned int instance) { struct platform_device *pdev = cal->pdev; struct cal_camerarx *phy; @@ -646,14 +639,14 @@ static void cal_get_hwinfo(struct cal_dev *cal) * Core 0: 0x4845 B828 = 0x0000 0040 * Core 1: 0x4845 B928 = 0x0000 0040 */ -static void i913_errata(struct cal_dev *cal, unsigned int port) +static void cal_camerarx_i913_errata(struct cal_camerarx *phy) { - u32 reg10 = reg_read(cal->phy[port], CAL_CSI2_PHY_REG10); + u32 reg10 = reg_read(phy, CAL_CSI2_PHY_REG10); set_field(®10, 1, CAL_CSI2_PHY_REG10_I933_LDO_DISABLE_MASK); - cal_dbg(1, cal, "CSI2_%d_REG10 = 0x%08x\n", port, reg10); - reg_write(cal->phy[port], CAL_CSI2_PHY_REG10, reg10); + phy_dbg(1, phy, "CSI2_%d_REG10 = 0x%08x\n", phy->instance, reg10); + reg_write(phy, CAL_CSI2_PHY_REG10, reg10); } static void cal_quickdump_regs(struct cal_dev *cal) @@ -685,7 +678,7 @@ static void cal_quickdump_regs(struct cal_dev *cal) /* * Enable the expected IRQ sources */ -static void enable_irqs(struct cal_ctx *ctx) +static void cal_camerarx_enable_irqs(struct cal_camerarx *phy) { u32 val; @@ -696,49 +689,49 @@ static void enable_irqs(struct cal_ctx *ctx) CAL_CSI2_COMPLEXIO_IRQ_ECC_NO_CORRECTION_MASK; /* Enable CIO error irqs */ - reg_write(ctx->cal, CAL_HL_IRQENABLE_SET(0), - CAL_HL_IRQ_CIO_MASK(ctx->csi2_port)); - reg_write(ctx->cal, CAL_CSI2_COMPLEXIO_IRQENABLE(ctx->csi2_port), + reg_write(phy->cal, CAL_HL_IRQENABLE_SET(0), + CAL_HL_IRQ_CIO_MASK(phy->instance)); + reg_write(phy->cal, CAL_CSI2_COMPLEXIO_IRQENABLE(phy->instance), cio_err_mask); /* Always enable OCPO error */ - reg_write(ctx->cal, CAL_HL_IRQENABLE_SET(0), CAL_HL_IRQ_OCPO_ERR_MASK); + reg_write(phy->cal, CAL_HL_IRQENABLE_SET(0), CAL_HL_IRQ_OCPO_ERR_MASK); /* Enable IRQ_WDMA_END 0/1 */ val = 0; - set_field(&val, 1, CAL_HL_IRQ_MASK(ctx->csi2_port)); - reg_write(ctx->cal, CAL_HL_IRQENABLE_SET(1), val); + set_field(&val, 1, CAL_HL_IRQ_MASK(phy->instance)); + reg_write(phy->cal, CAL_HL_IRQENABLE_SET(1), val); /* Enable IRQ_WDMA_START 0/1 */ val = 0; - set_field(&val, 1, CAL_HL_IRQ_MASK(ctx->csi2_port)); - reg_write(ctx->cal, CAL_HL_IRQENABLE_SET(2), val); + set_field(&val, 1, CAL_HL_IRQ_MASK(phy->instance)); + reg_write(phy->cal, CAL_HL_IRQENABLE_SET(2), val); /* Todo: Add VC_IRQ and CSI2_COMPLEXIO_IRQ handling */ - reg_write(ctx->cal, CAL_CSI2_VC_IRQENABLE(0), 0xFF000000); + reg_write(phy->cal, CAL_CSI2_VC_IRQENABLE(0), 0xFF000000); } -static void disable_irqs(struct cal_ctx *ctx) +static void cal_camerarx_disable_irqs(struct cal_camerarx *phy) { u32 val; /* Disable CIO error irqs */ - reg_write(ctx->cal, CAL_HL_IRQENABLE_CLR(0), - CAL_HL_IRQ_CIO_MASK(ctx->csi2_port)); - reg_write(ctx->cal, CAL_CSI2_COMPLEXIO_IRQENABLE(ctx->csi2_port), + reg_write(phy->cal, CAL_HL_IRQENABLE_CLR(0), + CAL_HL_IRQ_CIO_MASK(phy->instance)); + reg_write(phy->cal, CAL_CSI2_COMPLEXIO_IRQENABLE(phy->instance), 0); /* Disable IRQ_WDMA_END 0/1 */ val = 0; - set_field(&val, 1, CAL_HL_IRQ_MASK(ctx->csi2_port)); - reg_write(ctx->cal, CAL_HL_IRQENABLE_CLR(1), val); + set_field(&val, 1, CAL_HL_IRQ_MASK(phy->instance)); + reg_write(phy->cal, CAL_HL_IRQENABLE_CLR(1), val); /* Disable IRQ_WDMA_START 0/1 */ val = 0; - set_field(&val, 1, CAL_HL_IRQ_MASK(ctx->csi2_port)); - reg_write(ctx->cal, CAL_HL_IRQENABLE_CLR(2), val); + set_field(&val, 1, CAL_HL_IRQ_MASK(phy->instance)); + reg_write(phy->cal, CAL_HL_IRQENABLE_CLR(2), val); /* Todo: Add VC_IRQ and CSI2_COMPLEXIO_IRQ handling */ - reg_write(ctx->cal, CAL_CSI2_VC_IRQENABLE(0), 0); + reg_write(phy->cal, CAL_CSI2_VC_IRQENABLE(0), 0); } -static void csi2_cio_power(struct cal_ctx *ctx, bool enable) +static void cal_camerarx_power(struct cal_camerarx *phy, bool enable) { u32 target_state; unsigned int i; @@ -746,14 +739,14 @@ static void csi2_cio_power(struct cal_ctx *ctx, bool enable) target_state = enable ? CAL_CSI2_COMPLEXIO_CFG_PWR_CMD_STATE_ON : CAL_CSI2_COMPLEXIO_CFG_PWR_CMD_STATE_OFF; - reg_write_field(ctx->cal, CAL_CSI2_COMPLEXIO_CFG(ctx->csi2_port), + reg_write_field(phy->cal, CAL_CSI2_COMPLEXIO_CFG(phy->instance), target_state, CAL_CSI2_COMPLEXIO_CFG_PWR_CMD_MASK); for (i = 0; i < 10; i++) { u32 current_state; - current_state = reg_read_field(ctx->cal, - CAL_CSI2_COMPLEXIO_CFG(ctx->csi2_port), + current_state = reg_read_field(phy->cal, + CAL_CSI2_COMPLEXIO_CFG(phy->instance), CAL_CSI2_COMPLEXIO_CFG_PWR_STATUS_MASK); if (current_state == target_state) @@ -763,7 +756,7 @@ static void csi2_cio_power(struct cal_ctx *ctx, bool enable) } if (i == 10) - ctx_err(ctx, "Failed to power %s complexio\n", + phy_err(phy, "Failed to power %s complexio\n", enable ? "up" : "down"); } @@ -774,49 +767,51 @@ static void csi2_cio_power(struct cal_ctx *ctx, bool enable) #define TCLK_MISS 1 #define TCLK_SETTLE 14 -static void csi2_phy_config(struct cal_ctx *ctx) +static void cal_camerarx_config(struct cal_camerarx *phy, + const struct cal_fmt *fmt) { unsigned int reg0, reg1; unsigned int ths_term, ths_settle; unsigned int csi2_ddrclk_khz; struct v4l2_fwnode_bus_mipi_csi2 *mipi_csi2 = - &ctx->phy->endpoint.bus.mipi_csi2; + &phy->endpoint.bus.mipi_csi2; u32 num_lanes = mipi_csi2->num_data_lanes; /* DPHY timing configuration */ /* CSI-2 is DDR and we only count used lanes. */ - csi2_ddrclk_khz = ctx->phy->external_rate / 1000 - / (2 * num_lanes) * ctx->fmt->bpp; - ctx_dbg(1, ctx, "csi2_ddrclk_khz: %d\n", csi2_ddrclk_khz); + csi2_ddrclk_khz = phy->external_rate / 1000 + / (2 * num_lanes) * fmt->bpp; + phy_dbg(1, phy, "csi2_ddrclk_khz: %d\n", csi2_ddrclk_khz); /* THS_TERM: Programmed value = floor(20 ns/DDRClk period) */ ths_term = 20 * csi2_ddrclk_khz / 1000000; - ctx_dbg(1, ctx, "ths_term: %d (0x%02x)\n", ths_term, ths_term); + phy_dbg(1, phy, "ths_term: %d (0x%02x)\n", ths_term, ths_term); /* THS_SETTLE: Programmed value = floor(105 ns/DDRClk period) + 4 */ ths_settle = (105 * csi2_ddrclk_khz / 1000000) + 4; - ctx_dbg(1, ctx, "ths_settle: %d (0x%02x)\n", ths_settle, ths_settle); + phy_dbg(1, phy, "ths_settle: %d (0x%02x)\n", ths_settle, ths_settle); - reg0 = reg_read(ctx->phy, CAL_CSI2_PHY_REG0); + reg0 = reg_read(phy, CAL_CSI2_PHY_REG0); set_field(®0, CAL_CSI2_PHY_REG0_HSCLOCKCONFIG_DISABLE, CAL_CSI2_PHY_REG0_HSCLOCKCONFIG_MASK); set_field(®0, ths_term, CAL_CSI2_PHY_REG0_THS_TERM_MASK); set_field(®0, ths_settle, CAL_CSI2_PHY_REG0_THS_SETTLE_MASK); - ctx_dbg(1, ctx, "CSI2_%d_REG0 = 0x%08x\n", ctx->csi2_port, reg0); - reg_write(ctx->phy, CAL_CSI2_PHY_REG0, reg0); + phy_dbg(1, phy, "CSI2_%d_REG0 = 0x%08x\n", phy->instance, reg0); + reg_write(phy, CAL_CSI2_PHY_REG0, reg0); - reg1 = reg_read(ctx->phy, CAL_CSI2_PHY_REG1); + reg1 = reg_read(phy, CAL_CSI2_PHY_REG1); set_field(®1, TCLK_TERM, CAL_CSI2_PHY_REG1_TCLK_TERM_MASK); set_field(®1, 0xb8, CAL_CSI2_PHY_REG1_DPHY_HS_SYNC_PATTERN_MASK); set_field(®1, TCLK_MISS, CAL_CSI2_PHY_REG1_CTRLCLK_DIV_FACTOR_MASK); set_field(®1, TCLK_SETTLE, CAL_CSI2_PHY_REG1_TCLK_SETTLE_MASK); - ctx_dbg(1, ctx, "CSI2_%d_REG1 = 0x%08x\n", ctx->csi2_port, reg1); - reg_write(ctx->phy, CAL_CSI2_PHY_REG1, reg1); + phy_dbg(1, phy, "CSI2_%d_REG1 = 0x%08x\n", phy->instance, reg1); + reg_write(phy, CAL_CSI2_PHY_REG1, reg1); } -static void csi2_phy_init(struct cal_ctx *ctx) +static void cal_camerarx_init(struct cal_camerarx *phy, + const struct cal_fmt *fmt) { u32 val; u32 sscounter; @@ -839,21 +834,21 @@ static void csi2_phy_init(struct cal_ctx *ctx) */ /* 1. Configure D-PHY mode and enable required lanes */ - camerarx_phy_enable(ctx); + cal_camerarx_enable(phy); /* 2. Reset complex IO - Do not wait for reset completion */ - reg_write_field(ctx->cal, CAL_CSI2_COMPLEXIO_CFG(ctx->csi2_port), + reg_write_field(phy->cal, CAL_CSI2_COMPLEXIO_CFG(phy->instance), CAL_CSI2_COMPLEXIO_CFG_RESET_CTRL_OPERATIONAL, CAL_CSI2_COMPLEXIO_CFG_RESET_CTRL_MASK); - ctx_dbg(3, ctx, "CAL_CSI2_COMPLEXIO_CFG(%d) = 0x%08x De-assert Complex IO Reset\n", - ctx->csi2_port, - reg_read(ctx->cal, CAL_CSI2_COMPLEXIO_CFG(ctx->csi2_port))); + phy_dbg(3, phy, "CAL_CSI2_COMPLEXIO_CFG(%d) = 0x%08x De-assert Complex IO Reset\n", + phy->instance, + reg_read(phy->cal, CAL_CSI2_COMPLEXIO_CFG(phy->instance))); /* Dummy read to allow SCP reset to complete */ - reg_read(ctx->phy, CAL_CSI2_PHY_REG0); + reg_read(phy, CAL_CSI2_PHY_REG0); /* 3.A. Program Phy Timing Parameters */ - csi2_phy_config(ctx); + cal_camerarx_config(phy, fmt); /* 3.B. Program Stop States */ /* @@ -864,67 +859,67 @@ static void csi2_phy_init(struct cal_ctx *ctx) * Stop-state-timeout must be more than 100us as per CSI2 spec, so we * calculate a timeout that's 100us (rounding up). */ - sscounter = DIV_ROUND_UP(clk_get_rate(ctx->cal->fclk), 10000 * 16 * 4); + sscounter = DIV_ROUND_UP(clk_get_rate(phy->cal->fclk), 10000 * 16 * 4); - val = reg_read(ctx->cal, CAL_CSI2_TIMING(ctx->csi2_port)); + val = reg_read(phy->cal, CAL_CSI2_TIMING(phy->instance)); set_field(&val, 1, CAL_CSI2_TIMING_STOP_STATE_X16_IO1_MASK); set_field(&val, 1, CAL_CSI2_TIMING_STOP_STATE_X4_IO1_MASK); set_field(&val, sscounter, CAL_CSI2_TIMING_STOP_STATE_COUNTER_IO1_MASK); - reg_write(ctx->cal, CAL_CSI2_TIMING(ctx->csi2_port), val); - ctx_dbg(3, ctx, "CAL_CSI2_TIMING(%d) = 0x%08x Stop States\n", - ctx->csi2_port, - reg_read(ctx->cal, CAL_CSI2_TIMING(ctx->csi2_port))); + reg_write(phy->cal, CAL_CSI2_TIMING(phy->instance), val); + phy_dbg(3, phy, "CAL_CSI2_TIMING(%d) = 0x%08x Stop States\n", + phy->instance, + reg_read(phy->cal, CAL_CSI2_TIMING(phy->instance))); /* 4. Force FORCERXMODE */ - reg_write_field(ctx->cal, CAL_CSI2_TIMING(ctx->csi2_port), + reg_write_field(phy->cal, CAL_CSI2_TIMING(phy->instance), 1, CAL_CSI2_TIMING_FORCE_RX_MODE_IO1_MASK); - ctx_dbg(3, ctx, "CAL_CSI2_TIMING(%d) = 0x%08x Force RXMODE\n", - ctx->csi2_port, - reg_read(ctx->cal, CAL_CSI2_TIMING(ctx->csi2_port))); + phy_dbg(3, phy, "CAL_CSI2_TIMING(%d) = 0x%08x Force RXMODE\n", + phy->instance, + reg_read(phy->cal, CAL_CSI2_TIMING(phy->instance))); /* E. Power up the PHY using the complex IO */ - csi2_cio_power(ctx, true); + cal_camerarx_power(phy, true); } -static void csi2_wait_complexio_reset(struct cal_ctx *ctx) +static void cal_camerarx_wait_reset(struct cal_camerarx *phy) { unsigned long timeout; timeout = jiffies + msecs_to_jiffies(750); while (time_before(jiffies, timeout)) { - if (reg_read_field(ctx->cal, - CAL_CSI2_COMPLEXIO_CFG(ctx->csi2_port), + if (reg_read_field(phy->cal, + CAL_CSI2_COMPLEXIO_CFG(phy->instance), CAL_CSI2_COMPLEXIO_CFG_RESET_DONE_MASK) == CAL_CSI2_COMPLEXIO_CFG_RESET_DONE_RESETCOMPLETED) break; usleep_range(500, 5000); } - if (reg_read_field(ctx->cal, CAL_CSI2_COMPLEXIO_CFG(ctx->csi2_port), + if (reg_read_field(phy->cal, CAL_CSI2_COMPLEXIO_CFG(phy->instance), CAL_CSI2_COMPLEXIO_CFG_RESET_DONE_MASK) != CAL_CSI2_COMPLEXIO_CFG_RESET_DONE_RESETCOMPLETED) - ctx_err(ctx, "Timeout waiting for Complex IO reset done\n"); + phy_err(phy, "Timeout waiting for Complex IO reset done\n"); } -static void csi2_wait_stop_state(struct cal_ctx *ctx) +static void cal_camerarx_wait_stop_state(struct cal_camerarx *phy) { unsigned long timeout; timeout = jiffies + msecs_to_jiffies(750); while (time_before(jiffies, timeout)) { - if (reg_read_field(ctx->cal, - CAL_CSI2_TIMING(ctx->csi2_port), + if (reg_read_field(phy->cal, + CAL_CSI2_TIMING(phy->instance), CAL_CSI2_TIMING_FORCE_RX_MODE_IO1_MASK) == 0) break; usleep_range(500, 5000); } - if (reg_read_field(ctx->cal, CAL_CSI2_TIMING(ctx->csi2_port), + if (reg_read_field(phy->cal, CAL_CSI2_TIMING(phy->instance), CAL_CSI2_TIMING_FORCE_RX_MODE_IO1_MASK) != 0) - ctx_err(ctx, "Timeout waiting for stop state\n"); + phy_err(phy, "Timeout waiting for stop state\n"); } -static void csi2_wait_for_phy(struct cal_ctx *ctx) +static void cal_camerarx_wait_ready(struct cal_camerarx *phy) { /* Steps * 2. Wait for completion of reset @@ -936,51 +931,51 @@ static void csi2_wait_for_phy(struct cal_ctx *ctx) */ /* 2. Wait for reset completion */ - csi2_wait_complexio_reset(ctx); + cal_camerarx_wait_reset(phy); /* 4. G. Wait for all enabled lane to reach stop state */ - csi2_wait_stop_state(ctx); + cal_camerarx_wait_stop_state(phy); - ctx_dbg(1, ctx, "CSI2_%d_REG1 = 0x%08x (Bit(31,28) should be set!)\n", - ctx->csi2_port, reg_read(ctx->phy, CAL_CSI2_PHY_REG1)); + phy_dbg(1, phy, "CSI2_%d_REG1 = 0x%08x (Bit(31,28) should be set!)\n", + phy->instance, reg_read(phy, CAL_CSI2_PHY_REG1)); } -static void csi2_phy_deinit(struct cal_ctx *ctx) +static void cal_camerarx_deinit(struct cal_camerarx *phy) { unsigned int i; - csi2_cio_power(ctx, false); + cal_camerarx_power(phy, false); /* Assert Comple IO Reset */ - reg_write_field(ctx->cal, CAL_CSI2_COMPLEXIO_CFG(ctx->csi2_port), + reg_write_field(phy->cal, CAL_CSI2_COMPLEXIO_CFG(phy->instance), CAL_CSI2_COMPLEXIO_CFG_RESET_CTRL, CAL_CSI2_COMPLEXIO_CFG_RESET_CTRL_MASK); /* Wait for power down completion */ for (i = 0; i < 10; i++) { - if (reg_read_field(ctx->cal, - CAL_CSI2_COMPLEXIO_CFG(ctx->csi2_port), + if (reg_read_field(phy->cal, + CAL_CSI2_COMPLEXIO_CFG(phy->instance), CAL_CSI2_COMPLEXIO_CFG_RESET_DONE_MASK) == CAL_CSI2_COMPLEXIO_CFG_RESET_DONE_RESETONGOING) break; usleep_range(1000, 1100); } - ctx_dbg(3, ctx, "CAL_CSI2_COMPLEXIO_CFG(%d) = 0x%08x Complex IO in Reset (%d) %s\n", - ctx->csi2_port, - reg_read(ctx->cal, CAL_CSI2_COMPLEXIO_CFG(ctx->csi2_port)), i, + phy_dbg(3, phy, "CAL_CSI2_COMPLEXIO_CFG(%d) = 0x%08x Complex IO in Reset (%d) %s\n", + phy->instance, + reg_read(phy->cal, CAL_CSI2_COMPLEXIO_CFG(phy->instance)), i, (i >= 10) ? "(timeout)" : ""); /* Disable the phy */ - camerarx_phy_disable(ctx); + cal_camerarx_disable(phy); } -static void csi2_lane_config(struct cal_ctx *ctx) +static void cal_camerarx_lane_config(struct cal_camerarx *phy) { - u32 val = reg_read(ctx->cal, CAL_CSI2_COMPLEXIO_CFG(ctx->csi2_port)); + u32 val = reg_read(phy->cal, CAL_CSI2_COMPLEXIO_CFG(phy->instance)); u32 lane_mask = CAL_CSI2_COMPLEXIO_CFG_CLOCK_POSITION_MASK; u32 polarity_mask = CAL_CSI2_COMPLEXIO_CFG_CLOCK_POL_MASK; struct v4l2_fwnode_bus_mipi_csi2 *mipi_csi2 = - &ctx->phy->endpoint.bus.mipi_csi2; + &phy->endpoint.bus.mipi_csi2; int lane; set_field(&val, mipi_csi2->clock_lane + 1, lane_mask); @@ -997,21 +992,21 @@ static void csi2_lane_config(struct cal_ctx *ctx) polarity_mask); } - reg_write(ctx->cal, CAL_CSI2_COMPLEXIO_CFG(ctx->csi2_port), val); - ctx_dbg(3, ctx, "CAL_CSI2_COMPLEXIO_CFG(%d) = 0x%08x\n", - ctx->csi2_port, val); + reg_write(phy->cal, CAL_CSI2_COMPLEXIO_CFG(phy->instance), val); + phy_dbg(3, phy, "CAL_CSI2_COMPLEXIO_CFG(%d) = 0x%08x\n", + phy->instance, val); } -static void csi2_ppi_enable(struct cal_ctx *ctx) +static void cal_camerarx_ppi_enable(struct cal_camerarx *phy) { - reg_write(ctx->cal, CAL_CSI2_PPI_CTRL(ctx->csi2_port), BIT(3)); - reg_write_field(ctx->cal, CAL_CSI2_PPI_CTRL(ctx->csi2_port), + reg_write(phy->cal, CAL_CSI2_PPI_CTRL(phy->instance), BIT(3)); + reg_write_field(phy->cal, CAL_CSI2_PPI_CTRL(phy->instance), 1, CAL_CSI2_PPI_CTRL_IF_EN_MASK); } -static void csi2_ppi_disable(struct cal_ctx *ctx) +static void cal_camerarx_ppi_disable(struct cal_camerarx *phy) { - reg_write_field(ctx->cal, CAL_CSI2_PPI_CTRL(ctx->csi2_port), + reg_write_field(phy->cal, CAL_CSI2_PPI_CTRL(phy->instance), 0, CAL_CSI2_PPI_CTRL_IF_EN_MASK); } @@ -1151,7 +1146,7 @@ static void cal_wr_dma_addr(struct cal_ctx *ctx, unsigned int dmaaddr) reg_write(ctx->cal, CAL_WR_DMA_ADDR(ctx->csi2_port), dmaaddr); } -static int cal_get_external_info(struct cal_camerarx *phy) +static int cal_camerarx_get_external_info(struct cal_camerarx *phy) { struct v4l2_ctrl *ctrl; @@ -1678,7 +1673,7 @@ static int cal_start_streaming(struct vb2_queue *vq, unsigned int count) addr = vb2_dma_contig_plane_dma_addr(&ctx->cur_frm->vb.vb2_buf, 0); ctx->sequence = 0; - ret = cal_get_external_info(ctx->phy); + ret = cal_camerarx_get_external_info(ctx->phy); if (ret < 0) goto err; @@ -1694,10 +1689,10 @@ static int cal_start_streaming(struct vb2_queue *vq, unsigned int count) pix_proc_config(ctx); cal_wr_dma_config(ctx, ctx->v_fmt.fmt.pix.bytesperline, ctx->v_fmt.fmt.pix.height); - csi2_lane_config(ctx); + cal_camerarx_lane_config(ctx->phy); - enable_irqs(ctx); - csi2_phy_init(ctx); + cal_camerarx_enable_irqs(ctx->phy); + cal_camerarx_init(ctx->phy, ctx->fmt); ret = v4l2_subdev_call(ctx->phy->sensor, video, s_stream, 1); if (ret) { @@ -1707,9 +1702,9 @@ static int cal_start_streaming(struct vb2_queue *vq, unsigned int count) goto err; } - csi2_wait_for_phy(ctx); + cal_camerarx_wait_ready(ctx->phy); cal_wr_dma_addr(ctx, addr); - csi2_ppi_enable(ctx); + cal_camerarx_ppi_enable(ctx->phy); if (debug >= 4) cal_quickdump_regs(ctx->cal); @@ -1739,7 +1734,7 @@ static void cal_stop_streaming(struct vb2_queue *vq) int ret; bool dma_act; - csi2_ppi_disable(ctx); + cal_camerarx_ppi_disable(ctx->phy); /* wait for stream and dma to finish */ dma_act = true; @@ -1755,8 +1750,8 @@ static void cal_stop_streaming(struct vb2_queue *vq) if (dma_act) ctx_err(ctx, "failed to disable dma cleanly\n"); - disable_irqs(ctx); - csi2_phy_deinit(ctx); + cal_camerarx_disable_irqs(ctx->phy); + cal_camerarx_deinit(ctx->phy); if (v4l2_subdev_call(ctx->phy->sensor, video, s_stream, 0)) ctx_err(ctx, "stream off failed in subdev\n"); @@ -2330,12 +2325,12 @@ static int cal_probe(struct platform_device *pdev) platform_set_drvdata(pdev, cal); - cal->phy[0] = cc_create(cal, 0); + cal->phy[0] = cal_camerarx_create(cal, 0); if (IS_ERR(cal->phy[0])) return PTR_ERR(cal->phy[0]); if (cal_data_get_num_csi2_phy(cal) > 1) { - cal->phy[1] = cc_create(cal, 1); + cal->phy[1] = cal_camerarx_create(cal, 1); if (IS_ERR(cal->phy[1])) return PTR_ERR(cal->phy[1]); } else { @@ -2400,7 +2395,7 @@ static int cal_remove(struct platform_device *pdev) if (ctx) { ctx_dbg(1, ctx, "unregistering %s\n", video_device_node_name(&ctx->vdev)); - camerarx_phy_disable(ctx); + cal_camerarx_disable(ctx->phy); v4l2_async_notifier_unregister(&ctx->notifier); v4l2_async_notifier_cleanup(&ctx->notifier); v4l2_ctrl_handler_free(&ctx->ctrl_handler); @@ -2426,8 +2421,8 @@ static int cal_runtime_resume(struct device *dev) * Apply errata on both port everytime we (re-)enable * the clock */ - i913_errata(cal, 0); - i913_errata(cal, 1); + cal_camerarx_i913_errata(cal->phy[0]); + cal_camerarx_i913_errata(cal->phy[1]); } return 0; -- GitLab From 825800dc2250ce278fafc24c0cfd4d76d1b51cc3 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 6 Jul 2020 20:35:55 +0200 Subject: [PATCH 0567/1476] media: ti-vpe: cal: Group CAMERARX-related functions together Group the CAMERARX functions together to make the overall driver structure easier to navigate. This only moves functions around, no functional change is included. Signed-off-by: Laurent Pinchart Reviewed-by: Tomi Valkeinen Reviewed-by: Benoit Parrot Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/ti-vpe/cal.c | 333 ++++++++++++++-------------- 1 file changed, 166 insertions(+), 167 deletions(-) diff --git a/drivers/media/platform/ti-vpe/cal.c b/drivers/media/platform/ti-vpe/cal.c index bbf7acab17af2..57d2feabc2e8d 100644 --- a/drivers/media/platform/ti-vpe/cal.c +++ b/drivers/media/platform/ti-vpe/cal.c @@ -468,9 +468,30 @@ static inline void set_field(u32 *valp, u32 field, u32 mask) *valp = val; } -static u32 cal_camerarx_max_lanes(struct cal_camerarx *phy) +static void cal_quickdump_regs(struct cal_dev *cal) { - return phy->cal->data->camerarx[phy->instance].num_lanes; + cal_info(cal, "CAL Registers @ 0x%pa:\n", &cal->res->start); + print_hex_dump(KERN_INFO, "", DUMP_PREFIX_OFFSET, 16, 4, + (__force const void *)cal->base, + resource_size(cal->res), false); + + if (cal->ctx[0]) { + cal_info(cal, "CSI2 Core 0 Registers @ %pa:\n", + &cal->ctx[0]->phy->res->start); + print_hex_dump(KERN_INFO, "", DUMP_PREFIX_OFFSET, 16, 4, + (__force const void *)cal->ctx[0]->phy->base, + resource_size(cal->ctx[0]->phy->res), + false); + } + + if (cal->ctx[1]) { + cal_info(cal, "CSI2 Core 1 Registers @ %pa:\n", + &cal->ctx[1]->phy->res->start); + print_hex_dump(KERN_INFO, "", DUMP_PREFIX_OFFSET, 16, 4, + (__force const void *)cal->ctx[1]->phy->base, + resource_size(cal->ctx[1]->phy->res), + false); + } } static u32 cal_data_get_num_csi2_phy(struct cal_dev *cal) @@ -478,74 +499,16 @@ static u32 cal_data_get_num_csi2_phy(struct cal_dev *cal) return cal->data->num_csi2_phy; } -static int cal_camerarx_regmap_init(struct cal_dev *cal, - struct cal_camerarx *phy) -{ - const struct cal_camerarx_data *phy_data; - unsigned int i; - - if (!cal->data) - return -EINVAL; - - phy_data = &cal->data->camerarx[phy->instance]; - - for (i = 0; i < F_MAX_FIELDS; i++) { - struct reg_field field = { - .reg = cal->syscon_camerrx_offset, - .lsb = phy_data->fields[i].lsb, - .msb = phy_data->fields[i].msb, - }; - - /* - * Here we update the reg offset with the - * value found in DT - */ - phy->fields[i] = devm_regmap_field_alloc(&cal->pdev->dev, - cal->syscon_camerrx, - field); - if (IS_ERR(phy->fields[i])) { - cal_err(cal, "Unable to allocate regmap fields\n"); - return PTR_ERR(phy->fields[i]); - } - } - - return 0; -} +/* ------------------------------------------------------------------ + * CAMERARX Management + * ------------------------------------------------------------------ + */ -static struct regmap *cal_get_camerarx_regmap(struct cal_dev *cal) +static u32 cal_camerarx_max_lanes(struct cal_camerarx *phy) { - struct platform_device *pdev = cal->pdev; - struct regmap_config config = { }; - struct regmap *regmap; - void __iomem *base; - struct resource *res; - - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, - "camerrx_control"); - base = devm_ioremap_resource(&pdev->dev, res); - if (IS_ERR(base)) { - cal_err(cal, "failed to ioremap\n"); - return ERR_CAST(base); - } - - cal_dbg(1, cal, "ioresource %s at %pa - %pa\n", - res->name, &res->start, &res->end); - - config.reg_bits = 32; - config.reg_stride = 4; - config.val_bits = 32; - config.max_register = resource_size(res) - 4; - - regmap = regmap_init_mmio(NULL, base, &config); - if (IS_ERR(regmap)) - pr_err("regmap init failed\n"); - - return regmap; + return phy->cal->data->camerarx[phy->instance].num_lanes; } -/* - * Control Module CAMERARX block access - */ static void cal_camerarx_enable(struct cal_camerarx *phy) { u32 max_lanes; @@ -565,61 +528,6 @@ static void cal_camerarx_disable(struct cal_camerarx *phy) regmap_field_write(phy->fields[F_CTRLCLKEN], 0); } -/* - * Camera Instance access block - */ -static struct cal_camerarx *cal_camerarx_create(struct cal_dev *cal, - unsigned int instance) -{ - struct platform_device *pdev = cal->pdev; - struct cal_camerarx *phy; - int ret; - - phy = devm_kzalloc(&pdev->dev, sizeof(*phy), GFP_KERNEL); - if (!phy) - return ERR_PTR(-ENOMEM); - - phy->cal = cal; - phy->instance = instance; - phy->external_rate = 192000000; - - phy->res = platform_get_resource_byname(pdev, IORESOURCE_MEM, - (instance == 0) ? - "cal_rx_core0" : - "cal_rx_core1"); - phy->base = devm_ioremap_resource(&pdev->dev, phy->res); - if (IS_ERR(phy->base)) { - cal_err(cal, "failed to ioremap\n"); - return ERR_CAST(phy->base); - } - - cal_dbg(1, cal, "ioresource %s at %pa - %pa\n", - phy->res->name, &phy->res->start, &phy->res->end); - - ret = cal_camerarx_regmap_init(cal, phy); - if (ret) - return ERR_PTR(ret); - - return phy; -} - -/* - * Get Revision and HW info - */ -static void cal_get_hwinfo(struct cal_dev *cal) -{ - u32 revision; - u32 hwinfo; - - revision = reg_read(cal, CAL_HL_REVISION); - cal_dbg(3, cal, "CAL_HL_REVISION = 0x%08x (expecting 0x40000200)\n", - revision); - - hwinfo = reg_read(cal, CAL_HL_HWINFO); - cal_dbg(3, cal, "CAL_HL_HWINFO = 0x%08x (expecting 0xA3C90469)\n", - hwinfo); -} - /* * Errata i913: CSI2 LDO Needs to be disabled when module is powered on * @@ -649,32 +557,6 @@ static void cal_camerarx_i913_errata(struct cal_camerarx *phy) reg_write(phy, CAL_CSI2_PHY_REG10, reg10); } -static void cal_quickdump_regs(struct cal_dev *cal) -{ - cal_info(cal, "CAL Registers @ 0x%pa:\n", &cal->res->start); - print_hex_dump(KERN_INFO, "", DUMP_PREFIX_OFFSET, 16, 4, - (__force const void *)cal->base, - resource_size(cal->res), false); - - if (cal->ctx[0]) { - cal_info(cal, "CSI2 Core 0 Registers @ %pa:\n", - &cal->ctx[0]->phy->res->start); - print_hex_dump(KERN_INFO, "", DUMP_PREFIX_OFFSET, 16, 4, - (__force const void *)cal->ctx[0]->phy->base, - resource_size(cal->ctx[0]->phy->res), - false); - } - - if (cal->ctx[1]) { - cal_info(cal, "CSI2 Core 1 Registers @ %pa:\n", - &cal->ctx[1]->phy->res->start); - print_hex_dump(KERN_INFO, "", DUMP_PREFIX_OFFSET, 16, 4, - (__force const void *)cal->ctx[1]->phy->base, - resource_size(cal->ctx[1]->phy->res), - false); - } -} - /* * Enable the expected IRQ sources */ @@ -1010,6 +892,126 @@ static void cal_camerarx_ppi_disable(struct cal_camerarx *phy) 0, CAL_CSI2_PPI_CTRL_IF_EN_MASK); } +static int cal_camerarx_get_external_info(struct cal_camerarx *phy) +{ + struct v4l2_ctrl *ctrl; + + if (!phy->sensor) + return -ENODEV; + + ctrl = v4l2_ctrl_find(phy->sensor->ctrl_handler, V4L2_CID_PIXEL_RATE); + if (!ctrl) { + phy_err(phy, "no pixel rate control in subdev: %s\n", + phy->sensor->name); + return -EPIPE; + } + + phy->external_rate = v4l2_ctrl_g_ctrl_int64(ctrl); + phy_dbg(3, phy, "sensor Pixel Rate: %u\n", phy->external_rate); + + return 0; +} + +static int cal_camerarx_regmap_init(struct cal_dev *cal, + struct cal_camerarx *phy) +{ + const struct cal_camerarx_data *phy_data; + unsigned int i; + + if (!cal->data) + return -EINVAL; + + phy_data = &cal->data->camerarx[phy->instance]; + + for (i = 0; i < F_MAX_FIELDS; i++) { + struct reg_field field = { + .reg = cal->syscon_camerrx_offset, + .lsb = phy_data->fields[i].lsb, + .msb = phy_data->fields[i].msb, + }; + + /* + * Here we update the reg offset with the + * value found in DT + */ + phy->fields[i] = devm_regmap_field_alloc(&cal->pdev->dev, + cal->syscon_camerrx, + field); + if (IS_ERR(phy->fields[i])) { + cal_err(cal, "Unable to allocate regmap fields\n"); + return PTR_ERR(phy->fields[i]); + } + } + + return 0; +} + +static struct cal_camerarx *cal_camerarx_create(struct cal_dev *cal, + unsigned int instance) +{ + struct platform_device *pdev = cal->pdev; + struct cal_camerarx *phy; + int ret; + + phy = devm_kzalloc(&pdev->dev, sizeof(*phy), GFP_KERNEL); + if (!phy) + return ERR_PTR(-ENOMEM); + + phy->cal = cal; + phy->instance = instance; + phy->external_rate = 192000000; + + phy->res = platform_get_resource_byname(pdev, IORESOURCE_MEM, + (instance == 0) ? + "cal_rx_core0" : + "cal_rx_core1"); + phy->base = devm_ioremap_resource(&pdev->dev, phy->res); + if (IS_ERR(phy->base)) { + cal_err(cal, "failed to ioremap\n"); + return ERR_CAST(phy->base); + } + + cal_dbg(1, cal, "ioresource %s at %pa - %pa\n", + phy->res->name, &phy->res->start, &phy->res->end); + + ret = cal_camerarx_regmap_init(cal, phy); + if (ret) + return ERR_PTR(ret); + + return phy; +} + +static struct regmap *cal_get_camerarx_regmap(struct cal_dev *cal) +{ + struct platform_device *pdev = cal->pdev; + struct regmap_config config = { }; + struct regmap *regmap; + void __iomem *base; + struct resource *res; + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, + "camerrx_control"); + base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(base)) { + cal_err(cal, "failed to ioremap\n"); + return ERR_CAST(base); + } + + cal_dbg(1, cal, "ioresource %s at %pa - %pa\n", + res->name, &res->start, &res->end); + + config.reg_bits = 32; + config.reg_stride = 4; + config.val_bits = 32; + config.max_register = resource_size(res) - 4; + + regmap = regmap_init_mmio(NULL, base, &config); + if (IS_ERR(regmap)) + pr_err("regmap init failed\n"); + + return regmap; +} + static void csi2_ctx_config(struct cal_ctx *ctx) { u32 val; @@ -1146,26 +1148,6 @@ static void cal_wr_dma_addr(struct cal_ctx *ctx, unsigned int dmaaddr) reg_write(ctx->cal, CAL_WR_DMA_ADDR(ctx->csi2_port), dmaaddr); } -static int cal_camerarx_get_external_info(struct cal_camerarx *phy) -{ - struct v4l2_ctrl *ctrl; - - if (!phy->sensor) - return -ENODEV; - - ctrl = v4l2_ctrl_find(phy->sensor->ctrl_handler, V4L2_CID_PIXEL_RATE); - if (!ctrl) { - phy_err(phy, "no pixel rate control in subdev: %s\n", - phy->sensor->name); - return -EPIPE; - } - - phy->external_rate = v4l2_ctrl_g_ctrl_int64(ctrl); - phy_dbg(3, phy, "sensor Pixel Rate: %u\n", phy->external_rate); - - return 0; -} - static inline void cal_schedule_next_buffer(struct cal_ctx *ctx) { struct cal_dmaqueue *dma_q = &ctx->vidq; @@ -2244,6 +2226,23 @@ static const struct of_device_id cal_of_match[] = { }; MODULE_DEVICE_TABLE(of, cal_of_match); +/* + * Get Revision and HW info + */ +static void cal_get_hwinfo(struct cal_dev *cal) +{ + u32 revision; + u32 hwinfo; + + revision = reg_read(cal, CAL_HL_REVISION); + cal_dbg(3, cal, "CAL_HL_REVISION = 0x%08x (expecting 0x40000200)\n", + revision); + + hwinfo = reg_read(cal, CAL_HL_HWINFO); + cal_dbg(3, cal, "CAL_HL_HWINFO = 0x%08x (expecting 0xA3C90469)\n", + hwinfo); +} + static int cal_probe(struct platform_device *pdev) { struct cal_dev *cal; -- GitLab From f3ca1f9db5b9f3c0d3b3bf890ae92791852aff37 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 6 Jul 2020 20:35:56 +0200 Subject: [PATCH 0568/1476] media: ti-vpe: cal: Inline cal_data_get_num_csi2_phy() in its caller The cal_data_get_num_csi2_phy() function simply returns cal->data->num_csi2_phy, and is not consistently used across the driver, as most accesses to cal->data->num_csi2_phy are open-coded. We could fix those open-coded accesses to use cal_data_get_num_csi2_phy(), but that wouldn't bring much in terms of readability, so inline cal_data_get_num_csi2_phy() in its only caller instead, and drop the function. Signed-off-by: Laurent Pinchart Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/ti-vpe/cal.c | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/drivers/media/platform/ti-vpe/cal.c b/drivers/media/platform/ti-vpe/cal.c index 57d2feabc2e8d..5b89d03d1f623 100644 --- a/drivers/media/platform/ti-vpe/cal.c +++ b/drivers/media/platform/ti-vpe/cal.c @@ -494,11 +494,6 @@ static void cal_quickdump_regs(struct cal_dev *cal) } } -static u32 cal_data_get_num_csi2_phy(struct cal_dev *cal) -{ - return cal->data->num_csi2_phy; -} - /* ------------------------------------------------------------------ * CAMERARX Management * ------------------------------------------------------------------ @@ -2328,7 +2323,7 @@ static int cal_probe(struct platform_device *pdev) if (IS_ERR(cal->phy[0])) return PTR_ERR(cal->phy[0]); - if (cal_data_get_num_csi2_phy(cal) > 1) { + if (cal->data->num_csi2_phy > 1) { cal->phy[1] = cal_camerarx_create(cal, 1); if (IS_ERR(cal->phy[1])) return PTR_ERR(cal->phy[1]); @@ -2340,7 +2335,7 @@ static int cal_probe(struct platform_device *pdev) cal->ctx[1] = NULL; cal->ctx[0] = cal_create_instance(cal, 0); - if (cal_data_get_num_csi2_phy(cal) > 1) + if (cal->data->num_csi2_phy > 1) cal->ctx[1] = cal_create_instance(cal, 1); if (!cal->ctx[0] && !cal->ctx[1]) { cal_err(cal, "Neither port is configured, no point in staying up\n"); -- GitLab From 9f2b94b1ca7d6431697ea8f190a6493cf221364b Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 6 Jul 2020 20:35:57 +0200 Subject: [PATCH 0569/1476] media: ti-vpe: cal: Create consistent naming for context functions Rename all functions related to contexts with a cal_ctx_ prefix to increase readability. Signed-off-by: Laurent Pinchart Reviewed-by: Tomi Valkeinen Reviewed-by: Benoit Parrot Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/ti-vpe/cal.c | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/drivers/media/platform/ti-vpe/cal.c b/drivers/media/platform/ti-vpe/cal.c index 5b89d03d1f623..7914a0f22f0ee 100644 --- a/drivers/media/platform/ti-vpe/cal.c +++ b/drivers/media/platform/ti-vpe/cal.c @@ -1007,7 +1007,12 @@ static struct regmap *cal_get_camerarx_regmap(struct cal_dev *cal) return regmap; } -static void csi2_ctx_config(struct cal_ctx *ctx) +/* ------------------------------------------------------------------ + * Context Management + * ------------------------------------------------------------------ + */ + +static void cal_ctx_csi2_config(struct cal_ctx *ctx) { u32 val; @@ -1033,7 +1038,7 @@ static void csi2_ctx_config(struct cal_ctx *ctx) reg_read(ctx->cal, CAL_CSI2_CTX0(ctx->csi2_port))); } -static void pix_proc_config(struct cal_ctx *ctx) +static void cal_ctx_pix_proc_config(struct cal_ctx *ctx) { u32 val, extract, pack; @@ -1084,8 +1089,8 @@ static void pix_proc_config(struct cal_ctx *ctx) reg_read(ctx->cal, CAL_PIX_PROC(ctx->csi2_port))); } -static void cal_wr_dma_config(struct cal_ctx *ctx, - unsigned int width, unsigned int height) +static void cal_ctx_wr_dma_config(struct cal_ctx *ctx, + unsigned int width, unsigned int height) { u32 val; @@ -1138,7 +1143,7 @@ static void cal_wr_dma_config(struct cal_ctx *ctx, ctx_dbg(3, ctx, "CAL_CTRL = 0x%08x\n", reg_read(ctx->cal, CAL_CTRL)); } -static void cal_wr_dma_addr(struct cal_ctx *ctx, unsigned int dmaaddr) +static void cal_ctx_wr_dma_addr(struct cal_ctx *ctx, unsigned int dmaaddr) { reg_write(ctx->cal, CAL_WR_DMA_ADDR(ctx->csi2_port), dmaaddr); } @@ -1154,7 +1159,7 @@ static inline void cal_schedule_next_buffer(struct cal_ctx *ctx) list_del(&buf->list); addr = vb2_dma_contig_plane_dma_addr(&buf->vb.vb2_buf, 0); - cal_wr_dma_addr(ctx, addr); + cal_ctx_wr_dma_addr(ctx, addr); } static inline void cal_process_buffer_complete(struct cal_ctx *ctx) @@ -1662,10 +1667,10 @@ static int cal_start_streaming(struct vb2_queue *vq, unsigned int count) pm_runtime_get_sync(&ctx->cal->pdev->dev); - csi2_ctx_config(ctx); - pix_proc_config(ctx); - cal_wr_dma_config(ctx, ctx->v_fmt.fmt.pix.bytesperline, - ctx->v_fmt.fmt.pix.height); + cal_ctx_csi2_config(ctx); + cal_ctx_pix_proc_config(ctx); + cal_ctx_wr_dma_config(ctx, ctx->v_fmt.fmt.pix.bytesperline, + ctx->v_fmt.fmt.pix.height); cal_camerarx_lane_config(ctx->phy); cal_camerarx_enable_irqs(ctx->phy); @@ -1680,7 +1685,7 @@ static int cal_start_streaming(struct vb2_queue *vq, unsigned int count) } cal_camerarx_wait_ready(ctx->phy); - cal_wr_dma_addr(ctx, addr); + cal_ctx_wr_dma_addr(ctx, addr); cal_camerarx_ppi_enable(ctx->phy); if (debug >= 4) -- GitLab From 6770318dc07287b3506753927ea1c9a99c20fe1e Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 6 Jul 2020 20:35:58 +0200 Subject: [PATCH 0570/1476] media: ti-vpe: cal: Reorganize remaining code in sections Increase readability by reorganizing the remaining code in sections. No functional change is included. Signed-off-by: Laurent Pinchart Reviewed-by: Kieran Bingham Reviewed-by: Tomi Valkeinen Reviewed-by: Benoit Parrot Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/ti-vpe/cal.c | 340 +++++++++++++++------------- 1 file changed, 182 insertions(+), 158 deletions(-) diff --git a/drivers/media/platform/ti-vpe/cal.c b/drivers/media/platform/ti-vpe/cal.c index 7914a0f22f0ee..d452c3e950f0e 100644 --- a/drivers/media/platform/ti-vpe/cal.c +++ b/drivers/media/platform/ti-vpe/cal.c @@ -37,9 +37,6 @@ #define CAL_MODULE_NAME "cal" -#define MAX_WIDTH_BYTES (8192 * 8) -#define MAX_HEIGHT_LINES 16383 - MODULE_DESCRIPTION("TI CAL driver"); MODULE_AUTHOR("Benoit Parrot, "); MODULE_LICENSE("GPL v2"); @@ -80,11 +77,11 @@ MODULE_PARM_DESC(debug, "activates debug info"); #define CAL_NUM_CONTEXT 2 -#define reg_read(dev, offset) ioread32(dev->base + offset) -#define reg_write(dev, offset, val) iowrite32(val, dev->base + offset) +#define MAX_WIDTH_BYTES (8192 * 8) +#define MAX_HEIGHT_LINES 16383 /* ------------------------------------------------------------------ - * Basic structures + * Format Handling * ------------------------------------------------------------------ */ @@ -205,6 +202,11 @@ static char *fourcc_to_str(u32 fmt) return code; } +/* ------------------------------------------------------------------ + * Driver Structures + * ------------------------------------------------------------------ + */ + /* buffer for one video frame */ struct cal_buffer { /* common v4l buffer stuff -- must be first */ @@ -240,80 +242,6 @@ struct cal_data { unsigned int flags; }; -static const struct cal_camerarx_data dra72x_cal_camerarx[] = { - { - .fields = { - [F_CTRLCLKEN] = { 10, 10 }, - [F_CAMMODE] = { 11, 12 }, - [F_LANEENABLE] = { 13, 16 }, - [F_CSI_MODE] = { 17, 17 }, - }, - .num_lanes = 4, - }, - { - .fields = { - [F_CTRLCLKEN] = { 0, 0 }, - [F_CAMMODE] = { 1, 2 }, - [F_LANEENABLE] = { 3, 4 }, - [F_CSI_MODE] = { 5, 5 }, - }, - .num_lanes = 2, - }, -}; - -static const struct cal_data dra72x_cal_data = { - .camerarx = dra72x_cal_camerarx, - .num_csi2_phy = ARRAY_SIZE(dra72x_cal_camerarx), -}; - -static const struct cal_data dra72x_es1_cal_data = { - .camerarx = dra72x_cal_camerarx, - .num_csi2_phy = ARRAY_SIZE(dra72x_cal_camerarx), - .flags = DRA72_CAL_PRE_ES2_LDO_DISABLE, -}; - -static const struct cal_camerarx_data dra76x_cal_csi_phy[] = { - { - .fields = { - [F_CTRLCLKEN] = { 8, 8 }, - [F_CAMMODE] = { 9, 10 }, - [F_CSI_MODE] = { 11, 11 }, - [F_LANEENABLE] = { 27, 31 }, - }, - .num_lanes = 5, - }, - { - .fields = { - [F_CTRLCLKEN] = { 0, 0 }, - [F_CAMMODE] = { 1, 2 }, - [F_CSI_MODE] = { 3, 3 }, - [F_LANEENABLE] = { 24, 26 }, - }, - .num_lanes = 3, - }, -}; - -static const struct cal_data dra76x_cal_data = { - .camerarx = dra76x_cal_csi_phy, - .num_csi2_phy = ARRAY_SIZE(dra76x_cal_csi_phy), -}; - -static const struct cal_camerarx_data am654_cal_csi_phy[] = { - { - .fields = { - [F_CTRLCLKEN] = { 15, 15 }, - [F_CAMMODE] = { 24, 25 }, - [F_LANEENABLE] = { 0, 4 }, - }, - .num_lanes = 5, - }, -}; - -static const struct cal_data am654_cal_data = { - .camerarx = am654_cal_csi_phy, - .num_csi2_phy = ARRAY_SIZE(am654_cal_csi_phy), -}; - /* * The Camera Adaptation Layer (CAL) module is paired with one or more complex * I/O PHYs (CAMERARX). It contains multiple instances of CSI-2, processing and @@ -409,54 +337,111 @@ struct cal_ctx { bool dma_act; }; -static inline u32 reg_read_field(struct cal_dev *cal, u32 offset, u32 mask) +static inline struct cal_ctx *notifier_to_ctx(struct v4l2_async_notifier *n) { - return FIELD_GET(mask, reg_read(cal, offset)); + return container_of(n, struct cal_ctx, notifier); } -static inline void reg_write_field(struct cal_dev *cal, u32 offset, u32 value, - u32 mask) -{ - u32 val = reg_read(cal, offset); +/* ------------------------------------------------------------------ + * Platform Data + * ------------------------------------------------------------------ + */ - val &= ~mask; - val |= FIELD_PREP(mask, value); - reg_write(cal, offset, val); -} +static const struct cal_camerarx_data dra72x_cal_camerarx[] = { + { + .fields = { + [F_CTRLCLKEN] = { 10, 10 }, + [F_CAMMODE] = { 11, 12 }, + [F_LANEENABLE] = { 13, 16 }, + [F_CSI_MODE] = { 17, 17 }, + }, + .num_lanes = 4, + }, + { + .fields = { + [F_CTRLCLKEN] = { 0, 0 }, + [F_CAMMODE] = { 1, 2 }, + [F_LANEENABLE] = { 3, 4 }, + [F_CSI_MODE] = { 5, 5 }, + }, + .num_lanes = 2, + }, +}; -static const struct cal_fmt *find_format_by_pix(struct cal_ctx *ctx, - u32 pixelformat) -{ - const struct cal_fmt *fmt; - unsigned int k; +static const struct cal_data dra72x_cal_data = { + .camerarx = dra72x_cal_camerarx, + .num_csi2_phy = ARRAY_SIZE(dra72x_cal_camerarx), +}; - for (k = 0; k < ctx->num_active_fmt; k++) { - fmt = ctx->active_fmt[k]; - if (fmt->fourcc == pixelformat) - return fmt; - } +static const struct cal_data dra72x_es1_cal_data = { + .camerarx = dra72x_cal_camerarx, + .num_csi2_phy = ARRAY_SIZE(dra72x_cal_camerarx), + .flags = DRA72_CAL_PRE_ES2_LDO_DISABLE, +}; - return NULL; -} +static const struct cal_camerarx_data dra76x_cal_csi_phy[] = { + { + .fields = { + [F_CTRLCLKEN] = { 8, 8 }, + [F_CAMMODE] = { 9, 10 }, + [F_CSI_MODE] = { 11, 11 }, + [F_LANEENABLE] = { 27, 31 }, + }, + .num_lanes = 5, + }, + { + .fields = { + [F_CTRLCLKEN] = { 0, 0 }, + [F_CAMMODE] = { 1, 2 }, + [F_CSI_MODE] = { 3, 3 }, + [F_LANEENABLE] = { 24, 26 }, + }, + .num_lanes = 3, + }, +}; -static const struct cal_fmt *find_format_by_code(struct cal_ctx *ctx, - u32 code) -{ - const struct cal_fmt *fmt; - unsigned int k; +static const struct cal_data dra76x_cal_data = { + .camerarx = dra76x_cal_csi_phy, + .num_csi2_phy = ARRAY_SIZE(dra76x_cal_csi_phy), +}; - for (k = 0; k < ctx->num_active_fmt; k++) { - fmt = ctx->active_fmt[k]; - if (fmt->code == code) - return fmt; - } +static const struct cal_camerarx_data am654_cal_csi_phy[] = { + { + .fields = { + [F_CTRLCLKEN] = { 15, 15 }, + [F_CAMMODE] = { 24, 25 }, + [F_LANEENABLE] = { 0, 4 }, + }, + .num_lanes = 5, + }, +}; - return NULL; +static const struct cal_data am654_cal_data = { + .camerarx = am654_cal_csi_phy, + .num_csi2_phy = ARRAY_SIZE(am654_cal_csi_phy), +}; + +/* ------------------------------------------------------------------ + * I/O Register Accessors + * ------------------------------------------------------------------ + */ + +#define reg_read(dev, offset) ioread32(dev->base + offset) +#define reg_write(dev, offset, val) iowrite32(val, dev->base + offset) + +static inline u32 reg_read_field(struct cal_dev *cal, u32 offset, u32 mask) +{ + return FIELD_GET(mask, reg_read(cal, offset)); } -static inline struct cal_ctx *notifier_to_ctx(struct v4l2_async_notifier *n) +static inline void reg_write_field(struct cal_dev *cal, u32 offset, u32 value, + u32 mask) { - return container_of(n, struct cal_ctx, notifier); + u32 val = reg_read(cal, offset); + + val &= ~mask; + val |= FIELD_PREP(mask, value); + reg_write(cal, offset, val); } static inline void set_field(u32 *valp, u32 field, u32 mask) @@ -1148,6 +1133,11 @@ static void cal_ctx_wr_dma_addr(struct cal_ctx *ctx, unsigned int dmaaddr) reg_write(ctx->cal, CAL_WR_DMA_ADDR(ctx->csi2_port), dmaaddr); } +/* ------------------------------------------------------------------ + * IRQ Handling + * ------------------------------------------------------------------ + */ + static inline void cal_schedule_next_buffer(struct cal_ctx *ctx) { struct cal_dmaqueue *dma_q = &ctx->vidq; @@ -1256,9 +1246,41 @@ static irqreturn_t cal_irq(int irq_cal, void *data) return IRQ_HANDLED; } -/* - * video ioctls +/* ------------------------------------------------------------------ + * V4L2 Video IOCTLs + * ------------------------------------------------------------------ */ + +static const struct cal_fmt *find_format_by_pix(struct cal_ctx *ctx, + u32 pixelformat) +{ + const struct cal_fmt *fmt; + unsigned int k; + + for (k = 0; k < ctx->num_active_fmt; k++) { + fmt = ctx->active_fmt[k]; + if (fmt->fourcc == pixelformat) + return fmt; + } + + return NULL; +} + +static const struct cal_fmt *find_format_by_code(struct cal_ctx *ctx, + u32 code) +{ + const struct cal_fmt *fmt; + unsigned int k; + + for (k = 0; k < ctx->num_active_fmt; k++) { + fmt = ctx->active_fmt[k]; + if (fmt->code == code) + return fmt; + } + + return NULL; +} + static int cal_querycap(struct file *file, void *priv, struct v4l2_capability *cap) { @@ -1567,9 +1589,46 @@ static int cal_enum_frameintervals(struct file *file, void *priv, return 0; } -/* - * Videobuf operations +static const struct v4l2_file_operations cal_fops = { + .owner = THIS_MODULE, + .open = v4l2_fh_open, + .release = vb2_fop_release, + .read = vb2_fop_read, + .poll = vb2_fop_poll, + .unlocked_ioctl = video_ioctl2, /* V4L2 ioctl handler */ + .mmap = vb2_fop_mmap, +}; + +static const struct v4l2_ioctl_ops cal_ioctl_ops = { + .vidioc_querycap = cal_querycap, + .vidioc_enum_fmt_vid_cap = cal_enum_fmt_vid_cap, + .vidioc_g_fmt_vid_cap = cal_g_fmt_vid_cap, + .vidioc_try_fmt_vid_cap = cal_try_fmt_vid_cap, + .vidioc_s_fmt_vid_cap = cal_s_fmt_vid_cap, + .vidioc_enum_framesizes = cal_enum_framesizes, + .vidioc_reqbufs = vb2_ioctl_reqbufs, + .vidioc_create_bufs = vb2_ioctl_create_bufs, + .vidioc_prepare_buf = vb2_ioctl_prepare_buf, + .vidioc_querybuf = vb2_ioctl_querybuf, + .vidioc_qbuf = vb2_ioctl_qbuf, + .vidioc_dqbuf = vb2_ioctl_dqbuf, + .vidioc_expbuf = vb2_ioctl_expbuf, + .vidioc_enum_input = cal_enum_input, + .vidioc_g_input = cal_g_input, + .vidioc_s_input = cal_s_input, + .vidioc_enum_frameintervals = cal_enum_frameintervals, + .vidioc_streamon = vb2_ioctl_streamon, + .vidioc_streamoff = vb2_ioctl_streamoff, + .vidioc_log_status = v4l2_ctrl_log_status, + .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, +}; + +/* ------------------------------------------------------------------ + * videobuf2 Operations + * ------------------------------------------------------------------ */ + static int cal_queue_setup(struct vb2_queue *vq, unsigned int *nbuffers, unsigned int *nplanes, unsigned int sizes[], struct device *alloc_devs[]) @@ -1773,40 +1832,10 @@ static const struct vb2_ops cal_video_qops = { .wait_finish = vb2_ops_wait_finish, }; -static const struct v4l2_file_operations cal_fops = { - .owner = THIS_MODULE, - .open = v4l2_fh_open, - .release = vb2_fop_release, - .read = vb2_fop_read, - .poll = vb2_fop_poll, - .unlocked_ioctl = video_ioctl2, /* V4L2 ioctl handler */ - .mmap = vb2_fop_mmap, -}; - -static const struct v4l2_ioctl_ops cal_ioctl_ops = { - .vidioc_querycap = cal_querycap, - .vidioc_enum_fmt_vid_cap = cal_enum_fmt_vid_cap, - .vidioc_g_fmt_vid_cap = cal_g_fmt_vid_cap, - .vidioc_try_fmt_vid_cap = cal_try_fmt_vid_cap, - .vidioc_s_fmt_vid_cap = cal_s_fmt_vid_cap, - .vidioc_enum_framesizes = cal_enum_framesizes, - .vidioc_reqbufs = vb2_ioctl_reqbufs, - .vidioc_create_bufs = vb2_ioctl_create_bufs, - .vidioc_prepare_buf = vb2_ioctl_prepare_buf, - .vidioc_querybuf = vb2_ioctl_querybuf, - .vidioc_qbuf = vb2_ioctl_qbuf, - .vidioc_dqbuf = vb2_ioctl_dqbuf, - .vidioc_expbuf = vb2_ioctl_expbuf, - .vidioc_enum_input = cal_enum_input, - .vidioc_g_input = cal_g_input, - .vidioc_s_input = cal_s_input, - .vidioc_enum_frameintervals = cal_enum_frameintervals, - .vidioc_streamon = vb2_ioctl_streamon, - .vidioc_streamoff = vb2_ioctl_streamoff, - .vidioc_log_status = v4l2_ctrl_log_status, - .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, - .vidioc_unsubscribe_event = v4l2_event_unsubscribe, -}; +/* ------------------------------------------------------------------ + * Initialization and module stuff + * ------------------------------------------------------------------ + */ static const struct video_device cal_videodev = { .name = CAL_MODULE_NAME, @@ -1818,11 +1847,6 @@ static const struct video_device cal_videodev = { V4L2_CAP_READWRITE, }; -/* ----------------------------------------------------------------- - * Initialization and module stuff - * ------------------------------------------------------------------ - */ - static int cal_complete_ctx(struct cal_ctx *ctx) { struct video_device *vfd; -- GitLab From 20d4eb10911acb5a7f65816393532f451f166c2b Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 6 Jul 2020 20:35:59 +0200 Subject: [PATCH 0571/1476] media: ti-vpe: cal: Rename cal_ctx.csi2_port to cal_ctx.index The csi2_port field of the cal_ctx structure holds the context index, and is independent from the CSI-2 port (even if it currently has the same numerical value). Rename it to index to avoid the ambiguity. Signed-off-by: Laurent Pinchart Reviewed-by: Tomi Valkeinen Reviewed-by: Benoit Parrot Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/ti-vpe/cal.c | 50 ++++++++++++++--------------- 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/drivers/media/platform/ti-vpe/cal.c b/drivers/media/platform/ti-vpe/cal.c index d452c3e950f0e..ac23c41230fb5 100644 --- a/drivers/media/platform/ti-vpe/cal.c +++ b/drivers/media/platform/ti-vpe/cal.c @@ -62,11 +62,11 @@ MODULE_PARM_DESC(debug, "activates debug info"); dev_err(&(cal)->pdev->dev, fmt, ##arg) #define ctx_dbg(level, ctx, fmt, arg...) \ - cal_dbg(level, (ctx)->cal, "ctx%u: " fmt, (ctx)->csi2_port, ##arg) + cal_dbg(level, (ctx)->cal, "ctx%u: " fmt, (ctx)->index, ##arg) #define ctx_info(ctx, fmt, arg...) \ - cal_info((ctx)->cal, "ctx%u: " fmt, (ctx)->csi2_port, ##arg) + cal_info((ctx)->cal, "ctx%u: " fmt, (ctx)->index, ##arg) #define ctx_err(ctx, fmt, arg...) \ - cal_err((ctx)->cal, "ctx%u: " fmt, (ctx)->csi2_port, ##arg) + cal_err((ctx)->cal, "ctx%u: " fmt, (ctx)->index, ##arg) #define phy_dbg(level, phy, fmt, arg...) \ cal_dbg(level, (phy)->cal, "phy%u: " fmt, (phy)->instance, ##arg) @@ -325,7 +325,7 @@ struct cal_ctx { unsigned int sequence; struct vb2_queue vb_vidq; - unsigned int csi2_port; + unsigned int index; unsigned int cport; unsigned int virtual_channel; @@ -1001,7 +1001,7 @@ static void cal_ctx_csi2_config(struct cal_ctx *ctx) { u32 val; - val = reg_read(ctx->cal, CAL_CSI2_CTX0(ctx->csi2_port)); + val = reg_read(ctx->cal, CAL_CSI2_CTX0(ctx->index)); set_field(&val, ctx->cport, CAL_CSI2_CTX_CPORT_MASK); /* * DT type: MIPI CSI-2 Specs @@ -1018,9 +1018,9 @@ static void cal_ctx_csi2_config(struct cal_ctx *ctx) set_field(&val, CAL_CSI2_CTX_ATT_PIX, CAL_CSI2_CTX_ATT_MASK); set_field(&val, CAL_CSI2_CTX_PACK_MODE_LINE, CAL_CSI2_CTX_PACK_MODE_MASK); - reg_write(ctx->cal, CAL_CSI2_CTX0(ctx->csi2_port), val); - ctx_dbg(3, ctx, "CAL_CSI2_CTX0(%d) = 0x%08x\n", ctx->csi2_port, - reg_read(ctx->cal, CAL_CSI2_CTX0(ctx->csi2_port))); + reg_write(ctx->cal, CAL_CSI2_CTX0(ctx->index), val); + ctx_dbg(3, ctx, "CAL_CSI2_CTX0(%d) = 0x%08x\n", ctx->index, + reg_read(ctx->cal, CAL_CSI2_CTX0(ctx->index))); } static void cal_ctx_pix_proc_config(struct cal_ctx *ctx) @@ -1062,16 +1062,16 @@ static void cal_ctx_pix_proc_config(struct cal_ctx *ctx) break; } - val = reg_read(ctx->cal, CAL_PIX_PROC(ctx->csi2_port)); + val = reg_read(ctx->cal, CAL_PIX_PROC(ctx->index)); set_field(&val, extract, CAL_PIX_PROC_EXTRACT_MASK); set_field(&val, CAL_PIX_PROC_DPCMD_BYPASS, CAL_PIX_PROC_DPCMD_MASK); set_field(&val, CAL_PIX_PROC_DPCME_BYPASS, CAL_PIX_PROC_DPCME_MASK); set_field(&val, pack, CAL_PIX_PROC_PACK_MASK); set_field(&val, ctx->cport, CAL_PIX_PROC_CPORT_MASK); set_field(&val, 1, CAL_PIX_PROC_EN_MASK); - reg_write(ctx->cal, CAL_PIX_PROC(ctx->csi2_port), val); - ctx_dbg(3, ctx, "CAL_PIX_PROC(%d) = 0x%08x\n", ctx->csi2_port, - reg_read(ctx->cal, CAL_PIX_PROC(ctx->csi2_port))); + reg_write(ctx->cal, CAL_PIX_PROC(ctx->index), val); + ctx_dbg(3, ctx, "CAL_PIX_PROC(%d) = 0x%08x\n", ctx->index, + reg_read(ctx->cal, CAL_PIX_PROC(ctx->index))); } static void cal_ctx_wr_dma_config(struct cal_ctx *ctx, @@ -1079,7 +1079,7 @@ static void cal_ctx_wr_dma_config(struct cal_ctx *ctx, { u32 val; - val = reg_read(ctx->cal, CAL_WR_DMA_CTRL(ctx->csi2_port)); + val = reg_read(ctx->cal, CAL_WR_DMA_CTRL(ctx->index)); set_field(&val, ctx->cport, CAL_WR_DMA_CTRL_CPORT_MASK); set_field(&val, height, CAL_WR_DMA_CTRL_YSIZE_MASK); set_field(&val, CAL_WR_DMA_CTRL_DTAG_PIX_DAT, @@ -1089,22 +1089,22 @@ static void cal_ctx_wr_dma_config(struct cal_ctx *ctx, set_field(&val, CAL_WR_DMA_CTRL_PATTERN_LINEAR, CAL_WR_DMA_CTRL_PATTERN_MASK); set_field(&val, 1, CAL_WR_DMA_CTRL_STALL_RD_MASK); - reg_write(ctx->cal, CAL_WR_DMA_CTRL(ctx->csi2_port), val); - ctx_dbg(3, ctx, "CAL_WR_DMA_CTRL(%d) = 0x%08x\n", ctx->csi2_port, - reg_read(ctx->cal, CAL_WR_DMA_CTRL(ctx->csi2_port))); + reg_write(ctx->cal, CAL_WR_DMA_CTRL(ctx->index), val); + ctx_dbg(3, ctx, "CAL_WR_DMA_CTRL(%d) = 0x%08x\n", ctx->index, + reg_read(ctx->cal, CAL_WR_DMA_CTRL(ctx->index))); /* * width/16 not sure but giving it a whirl. * zero does not work right */ reg_write_field(ctx->cal, - CAL_WR_DMA_OFST(ctx->csi2_port), + CAL_WR_DMA_OFST(ctx->index), (width / 16), CAL_WR_DMA_OFST_MASK); - ctx_dbg(3, ctx, "CAL_WR_DMA_OFST(%d) = 0x%08x\n", ctx->csi2_port, - reg_read(ctx->cal, CAL_WR_DMA_OFST(ctx->csi2_port))); + ctx_dbg(3, ctx, "CAL_WR_DMA_OFST(%d) = 0x%08x\n", ctx->index, + reg_read(ctx->cal, CAL_WR_DMA_OFST(ctx->index))); - val = reg_read(ctx->cal, CAL_WR_DMA_XSIZE(ctx->csi2_port)); + val = reg_read(ctx->cal, CAL_WR_DMA_XSIZE(ctx->index)); /* 64 bit word means no skipping */ set_field(&val, 0, CAL_WR_DMA_XSIZE_XSKIP_MASK); /* @@ -1113,9 +1113,9 @@ static void cal_ctx_wr_dma_config(struct cal_ctx *ctx, * is detected automagically */ set_field(&val, (width / 8), CAL_WR_DMA_XSIZE_MASK); - reg_write(ctx->cal, CAL_WR_DMA_XSIZE(ctx->csi2_port), val); - ctx_dbg(3, ctx, "CAL_WR_DMA_XSIZE(%d) = 0x%08x\n", ctx->csi2_port, - reg_read(ctx->cal, CAL_WR_DMA_XSIZE(ctx->csi2_port))); + reg_write(ctx->cal, CAL_WR_DMA_XSIZE(ctx->index), val); + ctx_dbg(3, ctx, "CAL_WR_DMA_XSIZE(%d) = 0x%08x\n", ctx->index, + reg_read(ctx->cal, CAL_WR_DMA_XSIZE(ctx->index))); val = reg_read(ctx->cal, CAL_CTRL); set_field(&val, CAL_CTRL_BURSTSIZE_BURST128, CAL_CTRL_BURSTSIZE_MASK); @@ -1130,7 +1130,7 @@ static void cal_ctx_wr_dma_config(struct cal_ctx *ctx, static void cal_ctx_wr_dma_addr(struct cal_ctx *ctx, unsigned int dmaaddr) { - reg_write(ctx->cal, CAL_WR_DMA_ADDR(ctx->csi2_port), dmaaddr); + reg_write(ctx->cal, CAL_WR_DMA_ADDR(ctx->index), dmaaddr); } /* ------------------------------------------------------------------ @@ -2211,7 +2211,7 @@ static struct cal_ctx *cal_create_instance(struct cal_dev *cal, int inst) ctx->phy = cal->phy[inst]; /* Store the instance id */ - ctx->csi2_port = inst; + ctx->index = inst; ctx->cport = inst; ret = of_cal_create_instance(ctx, inst); -- GitLab From 17513da19e0a170d0a63c6c80e5d88cbf3eb1788 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 6 Jul 2020 20:36:00 +0200 Subject: [PATCH 0572/1476] media: ti-vpe: cal: Use correct device name for bus_info The v4l2_capability bus_info field, filled by the VIDIOC_QUERYCAP ioctl handler, specifies the location of the device in the system. For platform devices, V4L2 specifies that the value must be "platform:" followed by the device name. Fix the cal_querycap() function to set the right value. Signed-off-by: Laurent Pinchart Reviewed-by: Tomi Valkeinen Reviewed-by: Benoit Parrot Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/ti-vpe/cal.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/platform/ti-vpe/cal.c b/drivers/media/platform/ti-vpe/cal.c index ac23c41230fb5..028479498c07f 100644 --- a/drivers/media/platform/ti-vpe/cal.c +++ b/drivers/media/platform/ti-vpe/cal.c @@ -1290,7 +1290,7 @@ static int cal_querycap(struct file *file, void *priv, strscpy(cap->card, CAL_MODULE_NAME, sizeof(cap->card)); snprintf(cap->bus_info, sizeof(cap->bus_info), - "platform:%s", ctx->v4l2_dev.name); + "platform:%s", dev_name(&ctx->cal->pdev->dev)); return 0; } -- GitLab From 281736ef5545bb07b3291d00f655d6f884df9f80 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 6 Jul 2020 20:36:01 +0200 Subject: [PATCH 0573/1476] media: ti-vpe: cal: Get struct device without going through v4l2_device Instead of retrieving the struct device for the platform device from the v4l2_device, get it from the platform device directly. This prepares for cleanups related to v4l2_device handling. Signed-off-by: Laurent Pinchart Reviewed-by: Tomi Valkeinen Reviewed-by: Benoit Parrot Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/ti-vpe/cal.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/platform/ti-vpe/cal.c b/drivers/media/platform/ti-vpe/cal.c index 028479498c07f..22679baec3649 100644 --- a/drivers/media/platform/ti-vpe/cal.c +++ b/drivers/media/platform/ti-vpe/cal.c @@ -1868,7 +1868,7 @@ static int cal_complete_ctx(struct cal_ctx *ctx) q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; q->lock = &ctx->mutex; q->min_buffers_needed = 3; - q->dev = ctx->v4l2_dev.dev; + q->dev = &ctx->cal->pdev->dev; ret = vb2_queue_init(q); if (ret) -- GitLab From ed595da7d70fbb12bb5bd30936b472164082c484 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 6 Jul 2020 20:36:02 +0200 Subject: [PATCH 0574/1476] media: ti-vpe: cal: Use ctx_info() instead of v4l2_info() Use the context-specific print macro to replace the last usage of the v4l2_* print macros. Signed-off-by: Laurent Pinchart Reviewed-by: Tomi Valkeinen Reviewed-by: Benoit Parrot Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/ti-vpe/cal.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/media/platform/ti-vpe/cal.c b/drivers/media/platform/ti-vpe/cal.c index 22679baec3649..b55c8fb369a06 100644 --- a/drivers/media/platform/ti-vpe/cal.c +++ b/drivers/media/platform/ti-vpe/cal.c @@ -1893,8 +1893,8 @@ static int cal_complete_ctx(struct cal_ctx *ctx) if (ret < 0) return ret; - v4l2_info(&ctx->v4l2_dev, "V4L2 device registered as %s\n", - video_device_node_name(vfd)); + ctx_info(ctx, "V4L2 device registered as %s\n", + video_device_node_name(vfd)); return 0; } -- GitLab From 2f63379b93fa389ed65432d456da6697f60f7338 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 6 Jul 2020 20:36:03 +0200 Subject: [PATCH 0575/1476] media: ti-vpe: cal: Use a loop to create CAMERARX and context instances Replace a manually unrolled loop with an explicit for loop to increase readability when creating the CAMERARX and context instances. The explicit NULL initialization of cal->phy[] and cal->ctx[] is removed, as the cal structure is zeroed when allocated. Signed-off-by: Laurent Pinchart Reviewed-by: Tomi Valkeinen Reviewed-by: Benoit Parrot Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/ti-vpe/cal.c | 21 ++++++--------------- 1 file changed, 6 insertions(+), 15 deletions(-) diff --git a/drivers/media/platform/ti-vpe/cal.c b/drivers/media/platform/ti-vpe/cal.c index b55c8fb369a06..f92d6e49571f1 100644 --- a/drivers/media/platform/ti-vpe/cal.c +++ b/drivers/media/platform/ti-vpe/cal.c @@ -2348,24 +2348,15 @@ static int cal_probe(struct platform_device *pdev) platform_set_drvdata(pdev, cal); - cal->phy[0] = cal_camerarx_create(cal, 0); - if (IS_ERR(cal->phy[0])) - return PTR_ERR(cal->phy[0]); - - if (cal->data->num_csi2_phy > 1) { - cal->phy[1] = cal_camerarx_create(cal, 1); - if (IS_ERR(cal->phy[1])) - return PTR_ERR(cal->phy[1]); - } else { - cal->phy[1] = NULL; + for (i = 0; i < cal->data->num_csi2_phy; ++i) { + cal->phy[i] = cal_camerarx_create(cal, i); + if (IS_ERR(cal->phy[i])) + return PTR_ERR(cal->phy[i]); } - cal->ctx[0] = NULL; - cal->ctx[1] = NULL; + for (i = 0; i < cal->data->num_csi2_phy; ++i) + cal->ctx[i] = cal_create_instance(cal, i); - cal->ctx[0] = cal_create_instance(cal, 0); - if (cal->data->num_csi2_phy > 1) - cal->ctx[1] = cal_create_instance(cal, 1); if (!cal->ctx[0] && !cal->ctx[1]) { cal_err(cal, "Neither port is configured, no point in staying up\n"); return -ENODEV; -- GitLab From 4607c2487bf21846b849878f904ff80755158aa3 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 6 Jul 2020 20:36:04 +0200 Subject: [PATCH 0576/1476] media: ti-vpe: cal: Drop struct cal_dev v4l2_dev field The v4l2_dev field of the cal_dev structure is initialized but never used. Drop it. Signed-off-by: Laurent Pinchart Reviewed-by: Tomi Valkeinen Reviewed-by: Benoit Parrot Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/ti-vpe/cal.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/drivers/media/platform/ti-vpe/cal.c b/drivers/media/platform/ti-vpe/cal.c index f92d6e49571f1..feb53b862306a 100644 --- a/drivers/media/platform/ti-vpe/cal.c +++ b/drivers/media/platform/ti-vpe/cal.c @@ -279,7 +279,6 @@ struct cal_dev { void __iomem *base; struct resource *res; struct platform_device *pdev; - struct v4l2_device v4l2_dev; const struct cal_data *data; @@ -2288,10 +2287,6 @@ static int cal_probe(struct platform_device *pdev) return -ENODEV; } - /* set pseudo v4l2 device name so we can use v4l2_printk */ - strscpy(cal->v4l2_dev.name, CAL_MODULE_NAME, - sizeof(cal->v4l2_dev.name)); - /* save pdev pointer */ cal->pdev = pdev; -- GitLab From 573e298cfe679490a43e6d439885b79f5bd63e23 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 6 Jul 2020 20:36:05 +0200 Subject: [PATCH 0577/1476] media: ti-vpe: cal: Split CAMERARX syscon regmap retrieval to a function Simplify the cal_probe() function by splitting the CAMERARX syscon regmap retrieval to a separate function. A few local variables are renamed in the process to shorten them (syscon_camerrx_*) or to make them more accurate (parent isn't the parent OF node but the CAL device's own OF node). Signed-off-by: Laurent Pinchart Reviewed-by: Tomi Valkeinen Reviewed-by: Benoit Parrot Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/ti-vpe/cal.c | 73 +++++++++++++++++------------ 1 file changed, 42 insertions(+), 31 deletions(-) diff --git a/drivers/media/platform/ti-vpe/cal.c b/drivers/media/platform/ti-vpe/cal.c index feb53b862306a..6607e359855d1 100644 --- a/drivers/media/platform/ti-vpe/cal.c +++ b/drivers/media/platform/ti-vpe/cal.c @@ -991,6 +991,45 @@ static struct regmap *cal_get_camerarx_regmap(struct cal_dev *cal) return regmap; } +static int cal_camerarx_init_regmap(struct cal_dev *cal) +{ + struct device_node *np = cal->pdev->dev.of_node; + struct regmap *syscon; + u32 syscon_offset; + int ret; + + syscon = syscon_regmap_lookup_by_phandle(np, "ti,camerrx-control"); + ret = of_property_read_u32_index(np, "ti,camerrx-control", 1, + &syscon_offset); + if (IS_ERR(syscon)) + ret = PTR_ERR(syscon); + if (ret) { + dev_warn(&cal->pdev->dev, + "failed to get ti,camerrx-control: %d\n", ret); + + /* + * Backward DTS compatibility. + * If syscon entry is not present then check if the + * camerrx_control resource is present. + */ + syscon = cal_get_camerarx_regmap(cal); + if (IS_ERR(syscon)) { + dev_err(&cal->pdev->dev, + "failed to get camerrx_control regmap\n"); + return PTR_ERR(syscon); + } + /* In this case the base already point to the direct + * CM register so no need for an offset + */ + syscon_offset = 0; + } + + cal->syscon_camerrx = syscon; + cal->syscon_camerrx_offset = syscon_offset; + + return 0; +} + /* ------------------------------------------------------------------ * Context Management * ------------------------------------------------------------------ @@ -2270,9 +2309,6 @@ static int cal_probe(struct platform_device *pdev) { struct cal_dev *cal; struct cal_ctx *ctx; - struct device_node *parent = pdev->dev.of_node; - struct regmap *syscon_camerrx; - u32 syscon_camerrx_offset; unsigned int i; int ret; int irq; @@ -2296,34 +2332,9 @@ static int cal_probe(struct platform_device *pdev) return PTR_ERR(cal->fclk); } - syscon_camerrx = syscon_regmap_lookup_by_phandle(parent, - "ti,camerrx-control"); - ret = of_property_read_u32_index(parent, "ti,camerrx-control", 1, - &syscon_camerrx_offset); - if (IS_ERR(syscon_camerrx)) - ret = PTR_ERR(syscon_camerrx); - if (ret) { - dev_warn(&pdev->dev, "failed to get ti,camerrx-control: %d\n", - ret); - - /* - * Backward DTS compatibility. - * If syscon entry is not present then check if the - * camerrx_control resource is present. - */ - syscon_camerrx = cal_get_camerarx_regmap(cal); - if (IS_ERR(syscon_camerrx)) { - dev_err(&pdev->dev, "failed to get camerrx_control regmap\n"); - return PTR_ERR(syscon_camerrx); - } - /* In this case the base already point to the direct - * CM register so no need for an offset - */ - syscon_camerrx_offset = 0; - } - - cal->syscon_camerrx = syscon_camerrx; - cal->syscon_camerrx_offset = syscon_camerrx_offset; + ret = cal_camerarx_init_regmap(cal); + if (ret < 0) + return ret; cal->res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "cal_top"); -- GitLab From 2cabb7ade7a0a1c8da62ba4ed18e71a7d54e7c62 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 6 Jul 2020 20:36:06 +0200 Subject: [PATCH 0578/1476] media: ti-vpe: cal: Use syscon_regmap_lookup_by_phandle_args() Use the syscon_regmap_lookup_by_phandle_args() function to replace manual lookup of the syscon regmap offset. This simplifies the cal_camerarx_init_regmap() implementation. Signed-off-by: Laurent Pinchart Reviewed-by: Tomi Valkeinen Reviewed-by: Benoit Parrot Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/ti-vpe/cal.c | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/drivers/media/platform/ti-vpe/cal.c b/drivers/media/platform/ti-vpe/cal.c index 6607e359855d1..cb567d3130351 100644 --- a/drivers/media/platform/ti-vpe/cal.c +++ b/drivers/media/platform/ti-vpe/cal.c @@ -995,17 +995,14 @@ static int cal_camerarx_init_regmap(struct cal_dev *cal) { struct device_node *np = cal->pdev->dev.of_node; struct regmap *syscon; - u32 syscon_offset; - int ret; + unsigned int offset; - syscon = syscon_regmap_lookup_by_phandle(np, "ti,camerrx-control"); - ret = of_property_read_u32_index(np, "ti,camerrx-control", 1, - &syscon_offset); - if (IS_ERR(syscon)) - ret = PTR_ERR(syscon); - if (ret) { + syscon = syscon_regmap_lookup_by_phandle_args(np, "ti,camerrx-control", + 1, &offset); + if (IS_ERR(syscon)) { dev_warn(&cal->pdev->dev, - "failed to get ti,camerrx-control: %d\n", ret); + "failed to get ti,camerrx-control: %ld\n", + PTR_ERR(syscon)); /* * Backward DTS compatibility. @@ -1021,11 +1018,11 @@ static int cal_camerarx_init_regmap(struct cal_dev *cal) /* In this case the base already point to the direct * CM register so no need for an offset */ - syscon_offset = 0; + offset = 0; } cal->syscon_camerrx = syscon; - cal->syscon_camerrx_offset = syscon_offset; + cal->syscon_camerrx_offset = offset; return 0; } -- GitLab From 6963e46ce61388c05ae26918ce3afc138ac5c9f8 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 6 Jul 2020 20:36:07 +0200 Subject: [PATCH 0579/1476] media: ti-vpe: cal: Inline cal_get_camerarx_regmap() in caller The cal_get_camerarx_regmap() function is called in a single place. Inline it in its caller, as it results in a clear code flow. Signed-off-by: Laurent Pinchart Reviewed-by: Tomi Valkeinen Reviewed-by: Benoit Parrot Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/ti-vpe/cal.c | 75 ++++++++++++----------------- 1 file changed, 32 insertions(+), 43 deletions(-) diff --git a/drivers/media/platform/ti-vpe/cal.c b/drivers/media/platform/ti-vpe/cal.c index cb567d3130351..aecded3cad161 100644 --- a/drivers/media/platform/ti-vpe/cal.c +++ b/drivers/media/platform/ti-vpe/cal.c @@ -960,20 +960,36 @@ static struct cal_camerarx *cal_camerarx_create(struct cal_dev *cal, return phy; } -static struct regmap *cal_get_camerarx_regmap(struct cal_dev *cal) +static int cal_camerarx_init_regmap(struct cal_dev *cal) { - struct platform_device *pdev = cal->pdev; + struct device_node *np = cal->pdev->dev.of_node; struct regmap_config config = { }; - struct regmap *regmap; - void __iomem *base; + struct regmap *syscon; struct resource *res; + unsigned int offset; + void __iomem *base; + + syscon = syscon_regmap_lookup_by_phandle_args(np, "ti,camerrx-control", + 1, &offset); + if (!IS_ERR(syscon)) { + cal->syscon_camerrx = syscon; + cal->syscon_camerrx_offset = offset; + return 0; + } - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, + dev_warn(&cal->pdev->dev, "failed to get ti,camerrx-control: %ld\n", + PTR_ERR(syscon)); + + /* + * Backward DTS compatibility. If syscon entry is not present then + * check if the camerrx_control resource is present. + */ + res = platform_get_resource_byname(cal->pdev, IORESOURCE_MEM, "camerrx_control"); - base = devm_ioremap_resource(&pdev->dev, res); + base = devm_ioremap_resource(&cal->pdev->dev, res); if (IS_ERR(base)) { - cal_err(cal, "failed to ioremap\n"); - return ERR_CAST(base); + cal_err(cal, "failed to ioremap camerrx_control\n"); + return PTR_ERR(base); } cal_dbg(1, cal, "ioresource %s at %pa - %pa\n", @@ -984,45 +1000,18 @@ static struct regmap *cal_get_camerarx_regmap(struct cal_dev *cal) config.val_bits = 32; config.max_register = resource_size(res) - 4; - regmap = regmap_init_mmio(NULL, base, &config); - if (IS_ERR(regmap)) - pr_err("regmap init failed\n"); - - return regmap; -} - -static int cal_camerarx_init_regmap(struct cal_dev *cal) -{ - struct device_node *np = cal->pdev->dev.of_node; - struct regmap *syscon; - unsigned int offset; - - syscon = syscon_regmap_lookup_by_phandle_args(np, "ti,camerrx-control", - 1, &offset); + syscon = regmap_init_mmio(NULL, base, &config); if (IS_ERR(syscon)) { - dev_warn(&cal->pdev->dev, - "failed to get ti,camerrx-control: %ld\n", - PTR_ERR(syscon)); - - /* - * Backward DTS compatibility. - * If syscon entry is not present then check if the - * camerrx_control resource is present. - */ - syscon = cal_get_camerarx_regmap(cal); - if (IS_ERR(syscon)) { - dev_err(&cal->pdev->dev, - "failed to get camerrx_control regmap\n"); - return PTR_ERR(syscon); - } - /* In this case the base already point to the direct - * CM register so no need for an offset - */ - offset = 0; + pr_err("regmap init failed\n"); + return PTR_ERR(syscon); } + /* + * In this case the base already point to the direct CM register so no + * need for an offset. + */ cal->syscon_camerrx = syscon; - cal->syscon_camerrx_offset = offset; + cal->syscon_camerrx_offset = 0; return 0; } -- GitLab From 214f995c20938fc19375c0806b48f0ea2dec17f3 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 6 Jul 2020 20:36:08 +0200 Subject: [PATCH 0580/1476] media: ti-vpe: cal: Add comments to cal_probe() to delimitate sections The cal_probe() function is a bit long, add comments to delimitate sections in order to improve readability. The platform_set_drvdata() call is moved to a more logical place as a result. Signed-off-by: Laurent Pinchart Reviewed-by: Tomi Valkeinen Reviewed-by: Benoit Parrot Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/ti-vpe/cal.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/drivers/media/platform/ti-vpe/cal.c b/drivers/media/platform/ti-vpe/cal.c index aecded3cad161..738fd7a463991 100644 --- a/drivers/media/platform/ti-vpe/cal.c +++ b/drivers/media/platform/ti-vpe/cal.c @@ -2309,9 +2309,10 @@ static int cal_probe(struct platform_device *pdev) return -ENODEV; } - /* save pdev pointer */ cal->pdev = pdev; + platform_set_drvdata(pdev, cal); + /* Acquire resources: clocks, CAMERARX regmap, I/O memory and IRQ. */ cal->fclk = devm_clk_get(&pdev->dev, "fck"); if (IS_ERR(cal->fclk)) { dev_err(&pdev->dev, "cannot get CAL fclk\n"); @@ -2338,14 +2339,14 @@ static int cal_probe(struct platform_device *pdev) if (ret) return ret; - platform_set_drvdata(pdev, cal); - + /* Create CAMERARX PHYs. */ for (i = 0; i < cal->data->num_csi2_phy; ++i) { cal->phy[i] = cal_camerarx_create(cal, i); if (IS_ERR(cal->phy[i])) return PTR_ERR(cal->phy[i]); } + /* Create contexts. */ for (i = 0; i < cal->data->num_csi2_phy; ++i) cal->ctx[i] = cal_create_instance(cal, i); @@ -2356,15 +2357,13 @@ static int cal_probe(struct platform_device *pdev) vb2_dma_contig_set_max_seg_size(&pdev->dev, DMA_BIT_MASK(32)); + /* Read the revision and hardware info to verify hardware access. */ pm_runtime_enable(&pdev->dev); - ret = pm_runtime_get_sync(&pdev->dev); if (ret) goto runtime_disable; - /* Just check we can actually access the module */ cal_get_hwinfo(cal); - pm_runtime_put_sync(&pdev->dev); return 0; -- GitLab From 2355593496c5556ef6cecaa87d7add702882c060 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 6 Jul 2020 20:36:09 +0200 Subject: [PATCH 0581/1476] media: ti-vpe: cal: Rename cal_create_instance() to cal_ctx_create() The cal_create_instance() function creates a CAL context. Rename it to cal_ctx_create() to make its purpose more explicit. Signed-off-by: Laurent Pinchart Reviewed-by: Tomi Valkeinen Reviewed-by: Benoit Parrot Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/ti-vpe/cal.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/media/platform/ti-vpe/cal.c b/drivers/media/platform/ti-vpe/cal.c index 738fd7a463991..4796e211e33d6 100644 --- a/drivers/media/platform/ti-vpe/cal.c +++ b/drivers/media/platform/ti-vpe/cal.c @@ -2204,7 +2204,7 @@ cleanup_exit: return ret; } -static struct cal_ctx *cal_create_instance(struct cal_dev *cal, int inst) +static struct cal_ctx *cal_ctx_create(struct cal_dev *cal, int inst) { struct cal_ctx *ctx; struct v4l2_ctrl_handler *hdl; @@ -2348,7 +2348,7 @@ static int cal_probe(struct platform_device *pdev) /* Create contexts. */ for (i = 0; i < cal->data->num_csi2_phy; ++i) - cal->ctx[i] = cal_create_instance(cal, i); + cal->ctx[i] = cal_ctx_create(cal, i); if (!cal->ctx[0] && !cal->ctx[1]) { cal_err(cal, "Neither port is configured, no point in staying up\n"); -- GitLab From 8af2741a173408ad5ca9fb468b8f8b60495f7b20 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 6 Jul 2020 20:36:10 +0200 Subject: [PATCH 0582/1476] media: ti-vpe: cal: Hardcode virtual channel to 0 Using the endpoint reg value to pass the CSI-2 source virtual channel ID in DT is a hack, isn't documented in the DT bindings, and isn't used in neither upstream DT nor TI official overlays. Hardcode the virtual channel to 0 to simplify reworking the code, proper virtual channel support will be implemented later. Signed-off-by: Laurent Pinchart Reviewed-by: Tomi Valkeinen Reviewed-by: Benoit Parrot Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/ti-vpe/cal.c | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/drivers/media/platform/ti-vpe/cal.c b/drivers/media/platform/ti-vpe/cal.c index 4796e211e33d6..ff0367dac76ae 100644 --- a/drivers/media/platform/ti-vpe/cal.c +++ b/drivers/media/platform/ti-vpe/cal.c @@ -326,7 +326,6 @@ struct cal_ctx { struct vb2_queue vb_vidq; unsigned int index; unsigned int cport; - unsigned int virtual_channel; /* Pointer pointing to current v4l2_buffer */ struct cal_buffer *cur_frm; @@ -1036,8 +1035,7 @@ static void cal_ctx_csi2_config(struct cal_ctx *ctx) * 0x1E: YUV422 2 pixels = 4 bytes */ set_field(&val, 0x1, CAL_CSI2_CTX_DT_MASK); - /* Virtual Channel from the CSI2 sensor usually 0! */ - set_field(&val, ctx->virtual_channel, CAL_CSI2_CTX_VC_MASK); + set_field(&val, 0, CAL_CSI2_CTX_VC_MASK); set_field(&val, ctx->v_fmt.fmt.pix.height, CAL_CSI2_CTX_LINES_MASK); set_field(&val, CAL_CSI2_CTX_ATT_PIX, CAL_CSI2_CTX_ATT_MASK); set_field(&val, CAL_CSI2_CTX_PACK_MODE_LINE, @@ -2146,11 +2144,7 @@ static int of_cal_create_instance(struct cal_ctx *ctx, int inst) goto cleanup_exit; } - /* Store Virtual Channel number */ - ctx->virtual_channel = endpoint->base.id; - ctx_dbg(3, ctx, "Port:%d v4l2-endpoint: CSI2\n", inst); - ctx_dbg(3, ctx, "Virtual Channel=%d\n", ctx->virtual_channel); ctx_dbg(3, ctx, "flags=0x%08x\n", endpoint->bus.mipi_csi2.flags); ctx_dbg(3, ctx, "clock_lane=%d\n", endpoint->bus.mipi_csi2.clock_lane); ctx_dbg(3, ctx, "num_data_lanes=%d\n", -- GitLab From cb1d4202a389511e6bc2589bd46bb6c339e4a9e8 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 6 Jul 2020 20:36:11 +0200 Subject: [PATCH 0583/1476] media: ti-vpe: cal: Use of_graph_get_endpoint_by_regs() to parse OF Replace the manual of_get_next_port() and of_get_next_endpoint() implementations and the corresponding logic in the caller with a call to of_graph_get_endpoint_by_regs(). This greatly simplifies OF parsing. Signed-off-by: Laurent Pinchart Reviewed-by: Tomi Valkeinen Reviewed-by: Benoit Parrot Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/ti-vpe/cal.c | 114 ++-------------------------- 1 file changed, 7 insertions(+), 107 deletions(-) diff --git a/drivers/media/platform/ti-vpe/cal.c b/drivers/media/platform/ti-vpe/cal.c index ff0367dac76ae..b406a56debacf 100644 --- a/drivers/media/platform/ti-vpe/cal.c +++ b/drivers/media/platform/ti-vpe/cal.c @@ -2014,125 +2014,26 @@ static const struct v4l2_async_notifier_operations cal_async_ops = { .complete = cal_async_complete, }; -static struct device_node * -of_get_next_port(const struct device_node *parent, - struct device_node *prev) -{ - struct device_node *port; - - if (!parent) - return NULL; - - if (!prev) { - struct device_node *ports; - /* - * It's the first call, we have to find a port subnode - * within this node or within an optional 'ports' node. - */ - ports = of_get_child_by_name(parent, "ports"); - if (ports) - parent = ports; - - port = of_get_child_by_name(parent, "port"); - - /* release the 'ports' node */ - of_node_put(ports); - } else { - struct device_node *ports; - - ports = of_get_parent(prev); - if (!ports) - return NULL; - - do { - port = of_get_next_child(ports, prev); - if (!port) { - of_node_put(ports); - return NULL; - } - prev = port; - } while (!of_node_name_eq(port, "port")); - of_node_put(ports); - } - - return port; -} - -static struct device_node * -of_get_next_endpoint(const struct device_node *parent, - struct device_node *prev) -{ - struct device_node *ep; - - if (!parent) - return NULL; - - do { - ep = of_get_next_child(parent, prev); - if (!ep) - return NULL; - prev = ep; - } while (!of_node_name_eq(ep, "endpoint")); - - return ep; -} - static int of_cal_create_instance(struct cal_ctx *ctx, int inst) { struct platform_device *pdev = ctx->cal->pdev; - struct device_node *ep_node, *port, *sensor_node, *parent; + struct device_node *ep_node, *sensor_node; struct v4l2_fwnode_endpoint *endpoint; struct v4l2_async_subdev *asd; - u32 regval = 0; - int ret, index, lane; - bool found_port = false; - - parent = pdev->dev.of_node; + int ret = -EINVAL, lane; endpoint = &ctx->phy->endpoint; - ep_node = NULL; - port = NULL; - sensor_node = NULL; - ret = -EINVAL; - - ctx_dbg(3, ctx, "Scanning Port node for csi2 port: %d\n", inst); - for (index = 0; index < CAL_NUM_CSI2_PORTS; index++) { - port = of_get_next_port(parent, port); - if (!port) { - ctx_dbg(1, ctx, "No port node found for csi2 port:%d\n", - index); - goto cleanup_exit; - } - - /* Match the slice number with */ - of_property_read_u32(port, "reg", ®val); - ctx_dbg(3, ctx, "port:%d inst:%d :%d\n", - index, inst, regval); - if ((regval == inst) && (index == inst)) { - found_port = true; - break; - } - } - - if (!found_port) { - ctx_dbg(1, ctx, "No port node matches csi2 port:%d\n", - inst); - goto cleanup_exit; - } - - ctx_dbg(3, ctx, "Scanning sub-device for csi2 port: %d\n", - inst); - - ep_node = of_get_next_endpoint(port, ep_node); + ctx_dbg(3, ctx, "Getting endpoint for port@%d\n", inst); + ep_node = of_graph_get_endpoint_by_regs(pdev->dev.of_node, inst, 0); if (!ep_node) { - ctx_dbg(3, ctx, "can't get next endpoint\n"); - goto cleanup_exit; + ctx_dbg(3, ctx, "Can't get endpoint\n"); + return -EINVAL; } sensor_node = of_graph_get_remote_port_parent(ep_node); if (!sensor_node) { - ctx_dbg(3, ctx, "can't get remote parent\n"); + ctx_dbg(3, ctx, "Can't get remote parent\n"); goto cleanup_exit; } @@ -2193,7 +2094,6 @@ static int of_cal_create_instance(struct cal_ctx *ctx, int inst) cleanup_exit: of_node_put(sensor_node); of_node_put(ep_node); - of_node_put(port); return ret; } -- GitLab From b1ca64f269300d8ef4e0912f787b8f3488a1e1d6 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 6 Jul 2020 20:36:12 +0200 Subject: [PATCH 0584/1476] media: ti-vpe: cal: Fix usage of v4l2_fwnode_endpoint_parse() Letting the v4l2_fwnode_endpoint_parse() function determine the bus type automatically is deprecated. Set the bus type to DPHY manually as the TI CAL only supports DPHY. Signed-off-by: Laurent Pinchart Reviewed-by: Tomi Valkeinen Reviewed-by: Benoit Parrot Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/ti-vpe/cal.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/drivers/media/platform/ti-vpe/cal.c b/drivers/media/platform/ti-vpe/cal.c index b406a56debacf..8de4d7b96a225 100644 --- a/drivers/media/platform/ti-vpe/cal.c +++ b/drivers/media/platform/ti-vpe/cal.c @@ -2037,11 +2037,10 @@ static int of_cal_create_instance(struct cal_ctx *ctx, int inst) goto cleanup_exit; } - v4l2_fwnode_endpoint_parse(of_fwnode_handle(ep_node), endpoint); - - if (endpoint->bus_type != V4L2_MBUS_CSI2_DPHY) { - ctx_err(ctx, "Port:%d sub-device %pOFn is not a CSI2 device\n", - inst, sensor_node); + endpoint->bus_type = V4L2_MBUS_CSI2_DPHY; + ret = v4l2_fwnode_endpoint_parse(of_fwnode_handle(ep_node), endpoint); + if (ret < 0) { + ctx_err(ctx, "Failed to parse endpoint\n"); goto cleanup_exit; } -- GitLab From 4b71bfbc9787e359f63ef0d876e173b1c474ffc7 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 6 Jul 2020 20:36:13 +0200 Subject: [PATCH 0585/1476] media: ti-vpe: cal: Decouple control handler from v4l2_device To prepare for decoupling the v4l2_device from the cal_ctx, don't set the control handler in the v4l2_device and expect the video node to use it automatically, but set the video node control handler directly. This requires adding the sensor subdev controls to the control handler manually, as that operation was performed on the v4l2_device by v4l2_device_register_subdev(). Signed-off-by: Laurent Pinchart Reviewed-by: Benoit Parrot Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/ti-vpe/cal.c | 34 ++++++++++++++++------------- 1 file changed, 19 insertions(+), 15 deletions(-) diff --git a/drivers/media/platform/ti-vpe/cal.c b/drivers/media/platform/ti-vpe/cal.c index 8de4d7b96a225..fe96edebd9c2d 100644 --- a/drivers/media/platform/ti-vpe/cal.c +++ b/drivers/media/platform/ti-vpe/cal.c @@ -1871,6 +1871,7 @@ static const struct video_device cal_videodev = { static int cal_complete_ctx(struct cal_ctx *ctx) { + struct v4l2_ctrl_handler *hdl = &ctx->ctrl_handler; struct video_device *vfd; struct vb2_queue *q; int ret; @@ -1904,6 +1905,17 @@ static int cal_complete_ctx(struct cal_ctx *ctx) vfd->v4l2_dev = &ctx->v4l2_dev; vfd->queue = q; + /* Initialize the control handler. */ + v4l2_ctrl_handler_init(hdl, 11); + v4l2_ctrl_add_handler(hdl, ctx->phy->sensor->ctrl_handler, NULL, true); + if (hdl->error) { + ctx_err(ctx, "Failed to init ctrl handler\n"); + ret = hdl->error; + goto error; + } + + vfd->ctrl_handler = hdl; + /* * Provide a mutex to v4l2 core. It will be used to protect * all fops and v4l2 ioctls. @@ -1913,12 +1925,16 @@ static int cal_complete_ctx(struct cal_ctx *ctx) ret = video_register_device(vfd, VFL_TYPE_VIDEO, video_nr); if (ret < 0) - return ret; + goto error; ctx_info(ctx, "V4L2 device registered as %s\n", video_device_node_name(vfd)); return 0; + +error: + v4l2_ctrl_handler_free(hdl); + return ret; } static int cal_async_bound(struct v4l2_async_notifier *notifier, @@ -2100,7 +2116,6 @@ cleanup_exit: static struct cal_ctx *cal_ctx_create(struct cal_dev *cal, int inst) { struct cal_ctx *ctx; - struct v4l2_ctrl_handler *hdl; int ret; ctx = devm_kzalloc(&cal->pdev->dev, sizeof(*ctx), GFP_KERNEL); @@ -2114,15 +2129,7 @@ static struct cal_ctx *cal_ctx_create(struct cal_dev *cal, int inst) "%s-%03d", CAL_MODULE_NAME, inst); ret = v4l2_device_register(&cal->pdev->dev, &ctx->v4l2_dev); if (ret) - goto err_exit; - - hdl = &ctx->ctrl_handler; - ret = v4l2_ctrl_handler_init(hdl, 11); - if (ret) { - ctx_err(ctx, "Failed to init ctrl handler\n"); - goto unreg_dev; - } - ctx->v4l2_dev.ctrl_handler = hdl; + return NULL; /* Make sure Camera Core H/W register area is available */ ctx->phy = cal->phy[inst]; @@ -2134,15 +2141,12 @@ static struct cal_ctx *cal_ctx_create(struct cal_dev *cal, int inst) ret = of_cal_create_instance(ctx, inst); if (ret) { ret = -EINVAL; - goto free_hdl; + goto unreg_dev; } return ctx; -free_hdl: - v4l2_ctrl_handler_free(hdl); unreg_dev: v4l2_device_unregister(&ctx->v4l2_dev); -err_exit: return NULL; } -- GitLab From b4b784953c49c01ef0a52b7e81a938bb4bbf53d5 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 6 Jul 2020 20:36:14 +0200 Subject: [PATCH 0586/1476] media: ti-vpe: cal: Move v4l2_device from cal_ctx to cal_dev The v4l2_device structure is meant to represent the whole device. In the CAL case, this corresponds to the CAL, the CAMERARX instances and the connected sensors. There should thus be a single v4l2_device instance. Replace the per-context instance with a global instance in the cal_dev structure. Don't set the v4l2_device name manually as v4l2_device_register() sets it to a value that is suitable for the driver. Signed-off-by: Laurent Pinchart Reviewed-by: Benoit Parrot Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/ti-vpe/cal.c | 44 ++++++++++++++--------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/drivers/media/platform/ti-vpe/cal.c b/drivers/media/platform/ti-vpe/cal.c index fe96edebd9c2d..e747d8eb96aae 100644 --- a/drivers/media/platform/ti-vpe/cal.c +++ b/drivers/media/platform/ti-vpe/cal.c @@ -290,13 +290,14 @@ struct cal_dev { struct cal_camerarx *phy[CAL_NUM_CSI2_PORTS]; struct cal_ctx *ctx[CAL_NUM_CONTEXT]; + + struct v4l2_device v4l2_dev; }; /* * There is one cal_ctx structure for each camera core context. */ struct cal_ctx { - struct v4l2_device v4l2_dev; struct v4l2_ctrl_handler ctrl_handler; struct video_device vdev; struct v4l2_async_notifier notifier; @@ -1902,7 +1903,7 @@ static int cal_complete_ctx(struct cal_ctx *ctx) vfd = &ctx->vdev; *vfd = cal_videodev; - vfd->v4l2_dev = &ctx->v4l2_dev; + vfd->v4l2_dev = &ctx->cal->v4l2_dev; vfd->queue = q; /* Initialize the control handler. */ @@ -2091,7 +2092,7 @@ static int of_cal_create_instance(struct cal_ctx *ctx, int inst) } ctx->notifier.ops = &cal_async_ops; - ret = v4l2_async_notifier_register(&ctx->v4l2_dev, + ret = v4l2_async_notifier_register(&ctx->cal->v4l2_dev, &ctx->notifier); if (ret) { ctx_err(ctx, "Error registering async notifier\n"); @@ -2125,12 +2126,6 @@ static struct cal_ctx *cal_ctx_create(struct cal_dev *cal, int inst) /* save the cal_dev * for future ref */ ctx->cal = cal; - snprintf(ctx->v4l2_dev.name, sizeof(ctx->v4l2_dev.name), - "%s-%03d", CAL_MODULE_NAME, inst); - ret = v4l2_device_register(&cal->pdev->dev, &ctx->v4l2_dev); - if (ret) - return NULL; - /* Make sure Camera Core H/W register area is available */ ctx->phy = cal->phy[inst]; @@ -2139,15 +2134,10 @@ static struct cal_ctx *cal_ctx_create(struct cal_dev *cal, int inst) ctx->cport = inst; ret = of_cal_create_instance(ctx, inst); - if (ret) { - ret = -EINVAL; - goto unreg_dev; - } - return ctx; + if (ret) + return NULL; -unreg_dev: - v4l2_device_unregister(&ctx->v4l2_dev); - return NULL; + return ctx; } static const struct of_device_id cal_of_match[] = { @@ -2243,13 +2233,21 @@ static int cal_probe(struct platform_device *pdev) return PTR_ERR(cal->phy[i]); } + /* Register the V4L2 device. */ + ret = v4l2_device_register(&pdev->dev, &cal->v4l2_dev); + if (ret) { + cal_err(cal, "Failed to register V4L2 device\n"); + return ret; + } + /* Create contexts. */ for (i = 0; i < cal->data->num_csi2_phy; ++i) cal->ctx[i] = cal_ctx_create(cal, i); if (!cal->ctx[0] && !cal->ctx[1]) { cal_err(cal, "Neither port is configured, no point in staying up\n"); - return -ENODEV; + ret = -ENODEV; + goto error_v4l2; } vb2_dma_contig_set_max_seg_size(&pdev->dev, DMA_BIT_MASK(32)); @@ -2258,14 +2256,14 @@ static int cal_probe(struct platform_device *pdev) pm_runtime_enable(&pdev->dev); ret = pm_runtime_get_sync(&pdev->dev); if (ret) - goto runtime_disable; + goto error_pm_runtime; cal_get_hwinfo(cal); pm_runtime_put_sync(&pdev->dev); return 0; -runtime_disable: +error_pm_runtime: vb2_dma_contig_clear_max_seg_size(&pdev->dev); pm_runtime_disable(&pdev->dev); @@ -2275,10 +2273,11 @@ runtime_disable: v4l2_async_notifier_unregister(&ctx->notifier); v4l2_async_notifier_cleanup(&ctx->notifier); v4l2_ctrl_handler_free(&ctx->ctrl_handler); - v4l2_device_unregister(&ctx->v4l2_dev); } } +error_v4l2: + v4l2_device_unregister(&cal->v4l2_dev); return ret; } @@ -2301,11 +2300,12 @@ static int cal_remove(struct platform_device *pdev) v4l2_async_notifier_unregister(&ctx->notifier); v4l2_async_notifier_cleanup(&ctx->notifier); v4l2_ctrl_handler_free(&ctx->ctrl_handler); - v4l2_device_unregister(&ctx->v4l2_dev); video_unregister_device(&ctx->vdev); } } + v4l2_device_unregister(&cal->v4l2_dev); + pm_runtime_put_sync(&pdev->dev); pm_runtime_disable(&pdev->dev); -- GitLab From d68a94e98a89230750b8dfb667c71c3b4e0a02ed Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 6 Jul 2020 20:36:15 +0200 Subject: [PATCH 0587/1476] media: ti-vpe: cal: Split video device initialization and registration There's no need to delay most of the video device initialization until the sensor subdevs are bound. Split the initialization and registration, and perform the initialization when creating the context. Signed-off-by: Laurent Pinchart Reviewed-by: Benoit Parrot Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/ti-vpe/cal.c | 70 +++++++++++++++++------------ 1 file changed, 42 insertions(+), 28 deletions(-) diff --git a/drivers/media/platform/ti-vpe/cal.c b/drivers/media/platform/ti-vpe/cal.c index e747d8eb96aae..447ba33c4337a 100644 --- a/drivers/media/platform/ti-vpe/cal.c +++ b/drivers/media/platform/ti-vpe/cal.c @@ -1856,7 +1856,7 @@ static const struct vb2_ops cal_video_qops = { }; /* ------------------------------------------------------------------ - * Initialization and module stuff + * V4L2 Initialization and Registration * ------------------------------------------------------------------ */ @@ -1870,11 +1870,36 @@ static const struct video_device cal_videodev = { V4L2_CAP_READWRITE, }; -static int cal_complete_ctx(struct cal_ctx *ctx) +static int cal_ctx_v4l2_register(struct cal_ctx *ctx) { struct v4l2_ctrl_handler *hdl = &ctx->ctrl_handler; - struct video_device *vfd; - struct vb2_queue *q; + struct video_device *vfd = &ctx->vdev; + int ret; + + ret = v4l2_ctrl_add_handler(hdl, ctx->phy->sensor->ctrl_handler, NULL, + true); + if (ret < 0) { + ctx_err(ctx, "Failed to add sensor ctrl handler\n"); + return ret; + } + + ret = video_register_device(vfd, VFL_TYPE_VIDEO, video_nr); + if (ret < 0) { + ctx_err(ctx, "Failed to register video device\n"); + return ret; + } + + ctx_info(ctx, "V4L2 device registered as %s\n", + video_device_node_name(vfd)); + + return 0; +} + +static int cal_ctx_v4l2_init(struct cal_ctx *ctx) +{ + struct v4l2_ctrl_handler *hdl = &ctx->ctrl_handler; + struct video_device *vfd = &ctx->vdev; + struct vb2_queue *q = &ctx->vb_vidq; int ret; /* initialize locks */ @@ -1882,7 +1907,6 @@ static int cal_complete_ctx(struct cal_ctx *ctx) mutex_init(&ctx->mutex); /* initialize queue */ - q = &ctx->vb_vidq; q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; q->io_modes = VB2_MMAP | VB2_DMABUF | VB2_READ; q->drv_priv = ctx; @@ -1901,18 +1925,15 @@ static int cal_complete_ctx(struct cal_ctx *ctx) /* init video dma queues */ INIT_LIST_HEAD(&ctx->vidq.active); - vfd = &ctx->vdev; *vfd = cal_videodev; vfd->v4l2_dev = &ctx->cal->v4l2_dev; vfd->queue = q; /* Initialize the control handler. */ - v4l2_ctrl_handler_init(hdl, 11); - v4l2_ctrl_add_handler(hdl, ctx->phy->sensor->ctrl_handler, NULL, true); - if (hdl->error) { + ret = v4l2_ctrl_handler_init(hdl, 11); + if (ret < 0) { ctx_err(ctx, "Failed to init ctrl handler\n"); - ret = hdl->error; - goto error; + return ret; } vfd->ctrl_handler = hdl; @@ -1924,20 +1945,14 @@ static int cal_complete_ctx(struct cal_ctx *ctx) vfd->lock = &ctx->mutex; video_set_drvdata(vfd, ctx); - ret = video_register_device(vfd, VFL_TYPE_VIDEO, video_nr); - if (ret < 0) - goto error; - - ctx_info(ctx, "V4L2 device registered as %s\n", - video_device_node_name(vfd)); - return 0; - -error: - v4l2_ctrl_handler_free(hdl); - return ret; } +/* ------------------------------------------------------------------ + * Initialization and module stuff + * ------------------------------------------------------------------ + */ + static int cal_async_bound(struct v4l2_async_notifier *notifier, struct v4l2_subdev *subdev, struct v4l2_async_subdev *asd) @@ -1992,7 +2007,7 @@ static int cal_async_bound(struct v4l2_async_notifier *notifier, return -EINVAL; } - cal_complete_ctx(ctx); + cal_ctx_v4l2_register(ctx); return 0; } @@ -2123,16 +2138,15 @@ static struct cal_ctx *cal_ctx_create(struct cal_dev *cal, int inst) if (!ctx) return NULL; - /* save the cal_dev * for future ref */ ctx->cal = cal; - - /* Make sure Camera Core H/W register area is available */ ctx->phy = cal->phy[inst]; - - /* Store the instance id */ ctx->index = inst; ctx->cport = inst; + ret = cal_ctx_v4l2_init(ctx); + if (ret) + return NULL; + ret = of_cal_create_instance(ctx, inst); if (ret) return NULL; -- GitLab From 19a2374068b557ed1a19c68ebaef5f1a161e2973 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 6 Jul 2020 20:36:16 +0200 Subject: [PATCH 0588/1476] media: ti-vpe: cal: Add context V4L2 cleanup and unregister functions Create cal_ctx_v4l2_unregister() and cal_ctx_v4l2_cleanup() functions to unregister and cleanup the V4L2-related objects from the context, and call them in cal_remove() and in the error path of cal_probe(). Signed-off-by: Laurent Pinchart Reviewed-by: Benoit Parrot Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/ti-vpe/cal.c | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/drivers/media/platform/ti-vpe/cal.c b/drivers/media/platform/ti-vpe/cal.c index 447ba33c4337a..5f218ac5fb60e 100644 --- a/drivers/media/platform/ti-vpe/cal.c +++ b/drivers/media/platform/ti-vpe/cal.c @@ -1895,6 +1895,11 @@ static int cal_ctx_v4l2_register(struct cal_ctx *ctx) return 0; } +static void cal_ctx_v4l2_unregister(struct cal_ctx *ctx) +{ + video_unregister_device(&ctx->vdev); +} + static int cal_ctx_v4l2_init(struct cal_ctx *ctx) { struct v4l2_ctrl_handler *hdl = &ctx->ctrl_handler; @@ -1948,6 +1953,11 @@ static int cal_ctx_v4l2_init(struct cal_ctx *ctx) return 0; } +static void cal_ctx_v4l2_cleanup(struct cal_ctx *ctx) +{ + v4l2_ctrl_handler_free(&ctx->ctrl_handler); +} + /* ------------------------------------------------------------------ * Initialization and module stuff * ------------------------------------------------------------------ @@ -2286,7 +2296,7 @@ error_pm_runtime: if (ctx) { v4l2_async_notifier_unregister(&ctx->notifier); v4l2_async_notifier_cleanup(&ctx->notifier); - v4l2_ctrl_handler_free(&ctx->ctrl_handler); + cal_ctx_v4l2_cleanup(ctx); } } @@ -2313,8 +2323,8 @@ static int cal_remove(struct platform_device *pdev) cal_camerarx_disable(ctx->phy); v4l2_async_notifier_unregister(&ctx->notifier); v4l2_async_notifier_cleanup(&ctx->notifier); - v4l2_ctrl_handler_free(&ctx->ctrl_handler); - video_unregister_device(&ctx->vdev); + cal_ctx_v4l2_cleanup(ctx); + cal_ctx_v4l2_unregister(ctx); } } -- GitLab From 6675e871ca4dab14e849298767d34c2722f3ee1e Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 6 Jul 2020 20:36:17 +0200 Subject: [PATCH 0589/1476] media: ti-vpe: cal: Unregister video device before cleanup In cal_remove(), unregister the video devices as the first operation, before cleaning up the V4L2 objects, to avoid use-after-free. This isn't a complete solution yet, as video nodes can be kept open across unregistration. Signed-off-by: Laurent Pinchart Reviewed-by: Benoit Parrot Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/ti-vpe/cal.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/platform/ti-vpe/cal.c b/drivers/media/platform/ti-vpe/cal.c index 5f218ac5fb60e..5580913a1356f 100644 --- a/drivers/media/platform/ti-vpe/cal.c +++ b/drivers/media/platform/ti-vpe/cal.c @@ -2320,11 +2320,11 @@ static int cal_remove(struct platform_device *pdev) if (ctx) { ctx_dbg(1, ctx, "unregistering %s\n", video_device_node_name(&ctx->vdev)); + cal_ctx_v4l2_unregister(ctx); cal_camerarx_disable(ctx->phy); v4l2_async_notifier_unregister(&ctx->notifier); v4l2_async_notifier_cleanup(&ctx->notifier); cal_ctx_v4l2_cleanup(ctx); - cal_ctx_v4l2_unregister(ctx); } } -- GitLab From d3665f3b7c08bc50f97b2f308f4b745f7e80b3e8 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 6 Jul 2020 20:36:18 +0200 Subject: [PATCH 0590/1476] media: ti-vpe: cal: Add cal_camerarx_destroy() to cleanup CAMERARX The cal_camerarx_create() function allocates resources with devm_*, and thus doesn't need any manual cleanup. Those won't hold true for long, as we will need to store resources that have no devm_* allocation variant in cal_camerarx. Furthermore, devm_kzalloc() is the wrong memory allocation API for structures that can be accessed from userspace, as device nodes can be kept open across device removal. Add a cal_camerarx_destroy() function to destroy a CAMERARX instance explicitly, and switch to kzalloc() for memory allocation. Signed-off-by: Laurent Pinchart Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/ti-vpe/cal.c | 36 ++++++++++++++++++++++++----- 1 file changed, 30 insertions(+), 6 deletions(-) diff --git a/drivers/media/platform/ti-vpe/cal.c b/drivers/media/platform/ti-vpe/cal.c index 5580913a1356f..492141f07d69d 100644 --- a/drivers/media/platform/ti-vpe/cal.c +++ b/drivers/media/platform/ti-vpe/cal.c @@ -932,7 +932,7 @@ static struct cal_camerarx *cal_camerarx_create(struct cal_dev *cal, struct cal_camerarx *phy; int ret; - phy = devm_kzalloc(&pdev->dev, sizeof(*phy), GFP_KERNEL); + phy = kzalloc(sizeof(*phy), GFP_KERNEL); if (!phy) return ERR_PTR(-ENOMEM); @@ -947,7 +947,8 @@ static struct cal_camerarx *cal_camerarx_create(struct cal_dev *cal, phy->base = devm_ioremap_resource(&pdev->dev, phy->res); if (IS_ERR(phy->base)) { cal_err(cal, "failed to ioremap\n"); - return ERR_CAST(phy->base); + ret = PTR_ERR(phy->base); + goto error; } cal_dbg(1, cal, "ioresource %s at %pa - %pa\n", @@ -955,9 +956,21 @@ static struct cal_camerarx *cal_camerarx_create(struct cal_dev *cal, ret = cal_camerarx_regmap_init(cal, phy); if (ret) - return ERR_PTR(ret); + goto error; return phy; + +error: + kfree(phy); + return ERR_PTR(ret); +} + +static void cal_camerarx_destroy(struct cal_camerarx *phy) +{ + if (!phy) + return; + + kfree(phy); } static int cal_camerarx_init_regmap(struct cal_dev *cal) @@ -2253,15 +2266,18 @@ static int cal_probe(struct platform_device *pdev) /* Create CAMERARX PHYs. */ for (i = 0; i < cal->data->num_csi2_phy; ++i) { cal->phy[i] = cal_camerarx_create(cal, i); - if (IS_ERR(cal->phy[i])) - return PTR_ERR(cal->phy[i]); + if (IS_ERR(cal->phy[i])) { + ret = PTR_ERR(cal->phy[i]); + cal->phy[i] = NULL; + goto error_camerarx; + } } /* Register the V4L2 device. */ ret = v4l2_device_register(&pdev->dev, &cal->v4l2_dev); if (ret) { cal_err(cal, "Failed to register V4L2 device\n"); - return ret; + goto error_camerarx; } /* Create contexts. */ @@ -2302,6 +2318,11 @@ error_pm_runtime: error_v4l2: v4l2_device_unregister(&cal->v4l2_dev); + +error_camerarx: + for (i = 0; i < ARRAY_SIZE(cal->phy); i++) + cal_camerarx_destroy(cal->phy[i]); + return ret; } @@ -2330,6 +2351,9 @@ static int cal_remove(struct platform_device *pdev) v4l2_device_unregister(&cal->v4l2_dev); + for (i = 0; i < ARRAY_SIZE(cal->phy); i++) + cal_camerarx_destroy(cal->phy[i]); + pm_runtime_put_sync(&pdev->dev); pm_runtime_disable(&pdev->dev); -- GitLab From f5ddf19eca33281d8aabb8d802bed5a5658bbcb6 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 6 Jul 2020 20:36:19 +0200 Subject: [PATCH 0591/1476] media: ti-vpe: cal: Move DT parsing to CAMERARX The DT port nodes corresponding to the CSI-2 inputs belong to the CAMERARX instances. Move parsing of the DT properties to a new cal_camerarx_parse_dt() function, called by cal_camerarx_create(). Signed-off-by: Laurent Pinchart Reviewed-by: Benoit Parrot Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/ti-vpe/cal.c | 185 +++++++++++++++++----------- 1 file changed, 111 insertions(+), 74 deletions(-) diff --git a/drivers/media/platform/ti-vpe/cal.c b/drivers/media/platform/ti-vpe/cal.c index 492141f07d69d..a2c245ad23be5 100644 --- a/drivers/media/platform/ti-vpe/cal.c +++ b/drivers/media/platform/ti-vpe/cal.c @@ -269,6 +269,7 @@ struct cal_camerarx { unsigned int instance; struct v4l2_fwnode_endpoint endpoint; + struct device_node *sensor_node; struct v4l2_subdev *sensor; unsigned int external_rate; }; @@ -925,6 +926,73 @@ static int cal_camerarx_regmap_init(struct cal_dev *cal, return 0; } +static int cal_camerarx_parse_dt(struct cal_camerarx *phy) +{ + struct v4l2_fwnode_endpoint *endpoint = &phy->endpoint; + struct platform_device *pdev = phy->cal->pdev; + struct device_node *ep_node; + char data_lanes[V4L2_FWNODE_CSI2_MAX_DATA_LANES * 2]; + unsigned int i; + int ret; + + /* + * Find the endpoint node for the port corresponding to the PHY + * instance, and parse its CSI-2-related properties. + */ + ep_node = of_graph_get_endpoint_by_regs(pdev->dev.of_node, + phy->instance, 0); + if (!ep_node) { + /* + * The endpoint is not mandatory, not all PHY instances need to + * be connected in DT. + */ + phy_dbg(3, phy, "Port has no endpoint\n"); + return 0; + } + + endpoint->bus_type = V4L2_MBUS_CSI2_DPHY; + ret = v4l2_fwnode_endpoint_parse(of_fwnode_handle(ep_node), endpoint); + if (ret < 0) { + phy_err(phy, "Failed to parse endpoint\n"); + goto done; + } + + for (i = 0; i < endpoint->bus.mipi_csi2.num_data_lanes; i++) { + unsigned int lane = endpoint->bus.mipi_csi2.data_lanes[i]; + + if (lane > 4) { + phy_err(phy, "Invalid position %u for data lane %u\n", + lane, i); + ret = -EINVAL; + goto done; + } + + data_lanes[i*2] = '0' + lane; + data_lanes[i*2+1] = ' '; + } + + data_lanes[i*2-1] = '\0'; + + phy_dbg(3, phy, + "CSI-2 bus: clock lane <%u>, data lanes <%s>, flags 0x%08x\n", + endpoint->bus.mipi_csi2.clock_lane, data_lanes, + endpoint->bus.mipi_csi2.flags); + + /* Retrieve the connected device and store it for later use. */ + phy->sensor_node = of_graph_get_remote_port_parent(ep_node); + if (!phy->sensor_node) { + phy_dbg(3, phy, "Can't get remote parent\n"); + ret = -EINVAL; + goto done; + } + + phy_dbg(1, phy, "Found connected device %pOFn\n", phy->sensor_node); + +done: + of_node_put(ep_node); + return ret; +} + static struct cal_camerarx *cal_camerarx_create(struct cal_dev *cal, unsigned int instance) { @@ -958,6 +1026,10 @@ static struct cal_camerarx *cal_camerarx_create(struct cal_dev *cal, if (ret) goto error; + ret = cal_camerarx_parse_dt(phy); + if (ret) + goto error; + return phy; error: @@ -970,6 +1042,7 @@ static void cal_camerarx_destroy(struct cal_camerarx *phy) if (!phy) return; + of_node_put(phy->sensor_node); kfree(phy); } @@ -1972,7 +2045,7 @@ static void cal_ctx_v4l2_cleanup(struct cal_ctx *ctx) } /* ------------------------------------------------------------------ - * Initialization and module stuff + * Asynchronous V4L2 subdev binding * ------------------------------------------------------------------ */ @@ -2071,87 +2144,37 @@ static const struct v4l2_async_notifier_operations cal_async_ops = { static int of_cal_create_instance(struct cal_ctx *ctx, int inst) { - struct platform_device *pdev = ctx->cal->pdev; - struct device_node *ep_node, *sensor_node; - struct v4l2_fwnode_endpoint *endpoint; struct v4l2_async_subdev *asd; - int ret = -EINVAL, lane; - - endpoint = &ctx->phy->endpoint; - - ctx_dbg(3, ctx, "Getting endpoint for port@%d\n", inst); - ep_node = of_graph_get_endpoint_by_regs(pdev->dev.of_node, inst, 0); - if (!ep_node) { - ctx_dbg(3, ctx, "Can't get endpoint\n"); - return -EINVAL; - } - - sensor_node = of_graph_get_remote_port_parent(ep_node); - if (!sensor_node) { - ctx_dbg(3, ctx, "Can't get remote parent\n"); - goto cleanup_exit; - } - - endpoint->bus_type = V4L2_MBUS_CSI2_DPHY; - ret = v4l2_fwnode_endpoint_parse(of_fwnode_handle(ep_node), endpoint); - if (ret < 0) { - ctx_err(ctx, "Failed to parse endpoint\n"); - goto cleanup_exit; - } - - ctx_dbg(3, ctx, "Port:%d v4l2-endpoint: CSI2\n", inst); - ctx_dbg(3, ctx, "flags=0x%08x\n", endpoint->bus.mipi_csi2.flags); - ctx_dbg(3, ctx, "clock_lane=%d\n", endpoint->bus.mipi_csi2.clock_lane); - ctx_dbg(3, ctx, "num_data_lanes=%d\n", - endpoint->bus.mipi_csi2.num_data_lanes); - ctx_dbg(3, ctx, "data_lanes= <\n"); - for (lane = 0; lane < endpoint->bus.mipi_csi2.num_data_lanes; lane++) - ctx_dbg(3, ctx, "\t%d\n", - endpoint->bus.mipi_csi2.data_lanes[lane]); - ctx_dbg(3, ctx, "\t>\n"); - - ctx_dbg(1, ctx, "Port: %d found sub-device %pOFn\n", - inst, sensor_node); + struct fwnode_handle *fwnode; + int ret; v4l2_async_notifier_init(&ctx->notifier); + ctx->notifier.ops = &cal_async_ops; - asd = kzalloc(sizeof(*asd), GFP_KERNEL); - if (!asd) - goto cleanup_exit; - - asd->match_type = V4L2_ASYNC_MATCH_FWNODE; - asd->match.fwnode = of_fwnode_handle(sensor_node); - - ret = v4l2_async_notifier_add_subdev(&ctx->notifier, asd); - if (ret) { - ctx_err(ctx, "Error adding asd\n"); - kfree(asd); - goto cleanup_exit; + fwnode = of_fwnode_handle(ctx->phy->sensor_node); + asd = v4l2_async_notifier_add_fwnode_subdev(&ctx->notifier, fwnode, + sizeof(*asd)); + if (IS_ERR(asd)) { + ctx_err(ctx, "Failed to add subdev to notifier\n"); + return PTR_ERR(asd); } - ctx->notifier.ops = &cal_async_ops; ret = v4l2_async_notifier_register(&ctx->cal->v4l2_dev, &ctx->notifier); if (ret) { ctx_err(ctx, "Error registering async notifier\n"); v4l2_async_notifier_cleanup(&ctx->notifier); - ret = -EINVAL; + return ret; } - /* - * On success we need to keep reference on sensor_node, or - * if notifier_cleanup was called above, sensor_node was - * already put. - */ - sensor_node = NULL; - -cleanup_exit: - of_node_put(sensor_node); - of_node_put(ep_node); - - return ret; + return 0; } +/* ------------------------------------------------------------------ + * Initialization and module stuff + * ------------------------------------------------------------------ + */ + static struct cal_ctx *cal_ctx_create(struct cal_dev *cal, int inst) { struct cal_ctx *ctx; @@ -2219,6 +2242,7 @@ static int cal_probe(struct platform_device *pdev) { struct cal_dev *cal; struct cal_ctx *ctx; + bool connected = false; unsigned int i; int ret; int irq; @@ -2271,6 +2295,15 @@ static int cal_probe(struct platform_device *pdev) cal->phy[i] = NULL; goto error_camerarx; } + + if (cal->phy[i]->sensor_node) + connected = true; + } + + if (!connected) { + cal_err(cal, "Neither port is configured, no point in staying up\n"); + ret = -ENODEV; + goto error_camerarx; } /* Register the V4L2 device. */ @@ -2281,13 +2314,16 @@ static int cal_probe(struct platform_device *pdev) } /* Create contexts. */ - for (i = 0; i < cal->data->num_csi2_phy; ++i) - cal->ctx[i] = cal_ctx_create(cal, i); + for (i = 0; i < cal->data->num_csi2_phy; ++i) { + if (!cal->phy[i]->sensor_node) + continue; - if (!cal->ctx[0] && !cal->ctx[1]) { - cal_err(cal, "Neither port is configured, no point in staying up\n"); - ret = -ENODEV; - goto error_v4l2; + cal->ctx[i] = cal_ctx_create(cal, i); + if (!cal->ctx[i]) { + cal_err(cal, "Failed to create context %u\n", i); + ret = -ENODEV; + goto error_context; + } } vb2_dma_contig_set_max_seg_size(&pdev->dev, DMA_BIT_MASK(32)); @@ -2307,6 +2343,8 @@ error_pm_runtime: vb2_dma_contig_clear_max_seg_size(&pdev->dev); pm_runtime_disable(&pdev->dev); + +error_context: for (i = 0; i < CAL_NUM_CONTEXT; i++) { ctx = cal->ctx[i]; if (ctx) { @@ -2316,7 +2354,6 @@ error_pm_runtime: } } -error_v4l2: v4l2_device_unregister(&cal->v4l2_dev); error_camerarx: -- GitLab From 4d04c23fadc67acc47f26f7143e15c81b0143f47 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 6 Jul 2020 20:36:20 +0200 Subject: [PATCH 0592/1476] media: ti-vpe: cal: Use ARRAY_SIZE to replace numerical value Use ARRAY_SIZE(cal->ctx) to iterate over the cal->ctx array instead of using the numerical value from the CAL_NUM_CONTEXT macro (or, worse, hardcoding the value 2). This will allow reworking contexts with less changes in the code. Signed-off-by: Laurent Pinchart Reviewed-by: Benoit Parrot Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/ti-vpe/cal.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/media/platform/ti-vpe/cal.c b/drivers/media/platform/ti-vpe/cal.c index a2c245ad23be5..e91788519bb40 100644 --- a/drivers/media/platform/ti-vpe/cal.c +++ b/drivers/media/platform/ti-vpe/cal.c @@ -1314,7 +1314,7 @@ static irqreturn_t cal_irq(int irq_cal, void *data) /* Clear Interrupt status */ reg_write(cal, CAL_HL_IRQSTATUS(1), status); - for (i = 0; i < 2; ++i) { + for (i = 0; i < ARRAY_SIZE(cal->ctx); ++i) { if (isportirqset(status, i)) { ctx = cal->ctx[i]; @@ -1337,7 +1337,7 @@ static irqreturn_t cal_irq(int irq_cal, void *data) /* Clear Interrupt status */ reg_write(cal, CAL_HL_IRQSTATUS(2), status); - for (i = 0; i < 2; ++i) { + for (i = 0; i < ARRAY_SIZE(cal->ctx); ++i) { if (isportirqset(status, i)) { ctx = cal->ctx[i]; dma_q = &ctx->vidq; @@ -2345,7 +2345,7 @@ error_pm_runtime: pm_runtime_disable(&pdev->dev); error_context: - for (i = 0; i < CAL_NUM_CONTEXT; i++) { + for (i = 0; i < ARRAY_SIZE(cal->ctx); i++) { ctx = cal->ctx[i]; if (ctx) { v4l2_async_notifier_unregister(&ctx->notifier); @@ -2373,7 +2373,7 @@ static int cal_remove(struct platform_device *pdev) pm_runtime_get_sync(&pdev->dev); - for (i = 0; i < CAL_NUM_CONTEXT; i++) { + for (i = 0; i < ARRAY_SIZE(cal->ctx); i++) { ctx = cal->ctx[i]; if (ctx) { ctx_dbg(1, ctx, "unregistering %s\n", -- GitLab From a439e57358ee1562257668d77b19f8f1a629a1fd Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 6 Jul 2020 20:36:21 +0200 Subject: [PATCH 0593/1476] media: ti-vpe: cal: Move all sensor-related init to .bound() notifier The initialization of the context based on the connected sensor is split between the async notifier .bound() and .complete() operations. Move it all to a separate function and call it from .bound() operation to prepare for the move of the notifiers from the contexts to the cal_dev. Only V4L2 registration is kept in the .complete() operation. Signed-off-by: Laurent Pinchart Reviewed-by: Benoit Parrot Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/ti-vpe/cal.c | 132 ++++++++++++++-------------- 1 file changed, 68 insertions(+), 64 deletions(-) diff --git a/drivers/media/platform/ti-vpe/cal.c b/drivers/media/platform/ti-vpe/cal.c index e91788519bb40..b5eb6a640d705 100644 --- a/drivers/media/platform/ti-vpe/cal.c +++ b/drivers/media/platform/ti-vpe/cal.c @@ -1986,6 +1986,72 @@ static void cal_ctx_v4l2_unregister(struct cal_ctx *ctx) video_unregister_device(&ctx->vdev); } +static int cal_ctx_v4l2_init_formats(struct cal_ctx *ctx) +{ + struct v4l2_subdev_mbus_code_enum mbus_code; + struct v4l2_mbus_framefmt mbus_fmt; + const struct cal_fmt *fmt; + unsigned int i, j, k; + int ret = 0; + + /* Enumerate sub device formats and enable all matching local formats */ + ctx->num_active_fmt = 0; + for (j = 0, i = 0; ret != -EINVAL; ++j) { + + memset(&mbus_code, 0, sizeof(mbus_code)); + mbus_code.index = j; + mbus_code.which = V4L2_SUBDEV_FORMAT_ACTIVE; + ret = v4l2_subdev_call(ctx->phy->sensor, pad, enum_mbus_code, + NULL, &mbus_code); + if (ret) + continue; + + ctx_dbg(2, ctx, + "subdev %s: code: %04x idx: %u\n", + ctx->phy->sensor->name, mbus_code.code, j); + + for (k = 0; k < ARRAY_SIZE(cal_formats); k++) { + const struct cal_fmt *fmt = &cal_formats[k]; + + if (mbus_code.code == fmt->code) { + ctx->active_fmt[i] = fmt; + ctx_dbg(2, ctx, + "matched fourcc: %s: code: %04x idx: %u\n", + fourcc_to_str(fmt->fourcc), + fmt->code, i); + ctx->num_active_fmt = ++i; + } + } + } + + if (i == 0) { + ctx_err(ctx, "No suitable format reported by subdev %s\n", + ctx->phy->sensor->name); + return -EINVAL; + } + + ret = __subdev_get_format(ctx, &mbus_fmt); + if (ret) + return ret; + + fmt = find_format_by_code(ctx, mbus_fmt.code); + if (!fmt) { + ctx_dbg(3, ctx, "mbus code format (0x%08x) not found.\n", + mbus_fmt.code); + return -EINVAL; + } + + /* Save current subdev format */ + v4l2_fill_pix_format(&ctx->v_fmt.fmt.pix, &mbus_fmt); + ctx->v_fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + ctx->v_fmt.fmt.pix.pixelformat = fmt->fourcc; + cal_calc_format_size(ctx, fmt, &ctx->v_fmt); + ctx->fmt = fmt; + ctx->m_fmt = mbus_fmt; + + return 0; +} + static int cal_ctx_v4l2_init(struct cal_ctx *ctx) { struct v4l2_ctrl_handler *hdl = &ctx->ctrl_handler; @@ -2054,9 +2120,6 @@ static int cal_async_bound(struct v4l2_async_notifier *notifier, struct v4l2_async_subdev *asd) { struct cal_ctx *ctx = notifier_to_ctx(notifier); - struct v4l2_subdev_mbus_code_enum mbus_code; - unsigned int i, j, k; - int ret = 0; if (ctx->phy->sensor) { ctx_info(ctx, "Rejecting subdev %s (Already set!!)", @@ -2067,73 +2130,14 @@ static int cal_async_bound(struct v4l2_async_notifier *notifier, ctx->phy->sensor = subdev; ctx_dbg(1, ctx, "Using sensor %s for capture\n", subdev->name); - /* Enumerate sub device formats and enable all matching local formats */ - ctx->num_active_fmt = 0; - for (j = 0, i = 0; ret != -EINVAL; ++j) { - - memset(&mbus_code, 0, sizeof(mbus_code)); - mbus_code.index = j; - mbus_code.which = V4L2_SUBDEV_FORMAT_ACTIVE; - ret = v4l2_subdev_call(subdev, pad, enum_mbus_code, - NULL, &mbus_code); - if (ret) - continue; - - ctx_dbg(2, ctx, - "subdev %s: code: %04x idx: %u\n", - subdev->name, mbus_code.code, j); - - for (k = 0; k < ARRAY_SIZE(cal_formats); k++) { - const struct cal_fmt *fmt = &cal_formats[k]; - - if (mbus_code.code == fmt->code) { - ctx->active_fmt[i] = fmt; - ctx_dbg(2, ctx, - "matched fourcc: %s: code: %04x idx: %u\n", - fourcc_to_str(fmt->fourcc), - fmt->code, i); - ctx->num_active_fmt = ++i; - } - } - } - - if (i == 0) { - ctx_err(ctx, "No suitable format reported by subdev %s\n", - subdev->name); - return -EINVAL; - } - - cal_ctx_v4l2_register(ctx); - - return 0; + return cal_ctx_v4l2_init_formats(ctx); } static int cal_async_complete(struct v4l2_async_notifier *notifier) { struct cal_ctx *ctx = notifier_to_ctx(notifier); - const struct cal_fmt *fmt; - struct v4l2_mbus_framefmt mbus_fmt; - int ret; - - ret = __subdev_get_format(ctx, &mbus_fmt); - if (ret) - return ret; - - fmt = find_format_by_code(ctx, mbus_fmt.code); - if (!fmt) { - ctx_dbg(3, ctx, "mbus code format (0x%08x) not found.\n", - mbus_fmt.code); - return -EINVAL; - } - - /* Save current subdev format */ - v4l2_fill_pix_format(&ctx->v_fmt.fmt.pix, &mbus_fmt); - ctx->v_fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - ctx->v_fmt.fmt.pix.pixelformat = fmt->fourcc; - cal_calc_format_size(ctx, fmt, &ctx->v_fmt); - ctx->fmt = fmt; - ctx->m_fmt = mbus_fmt; + cal_ctx_v4l2_register(ctx); return 0; } -- GitLab From 8fcb7576ad197b0d65fe68ef60b7e1097e18df0c Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 6 Jul 2020 20:36:22 +0200 Subject: [PATCH 0594/1476] media: ti-vpe: cal: Allow multiple contexts per subdev notifier The subdev notifier handling assumes a 1:1 mapping between CAL contexts and notifiers. To prepare for merging the multiple notifiers into a single one, retrieve the CAL context from the async subdev structure instead of from the notifier. Signed-off-by: Laurent Pinchart Reviewed-by: Benoit Parrot Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/ti-vpe/cal.c | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/drivers/media/platform/ti-vpe/cal.c b/drivers/media/platform/ti-vpe/cal.c index b5eb6a640d705..83e790941e22b 100644 --- a/drivers/media/platform/ti-vpe/cal.c +++ b/drivers/media/platform/ti-vpe/cal.c @@ -2115,11 +2115,22 @@ static void cal_ctx_v4l2_cleanup(struct cal_ctx *ctx) * ------------------------------------------------------------------ */ +struct cal_v4l2_async_subdev { + struct v4l2_async_subdev asd; + struct cal_ctx *ctx; +}; + +static inline struct cal_v4l2_async_subdev * +to_cal_asd(struct v4l2_async_subdev *asd) +{ + return container_of(asd, struct cal_v4l2_async_subdev, asd); +} + static int cal_async_bound(struct v4l2_async_notifier *notifier, struct v4l2_subdev *subdev, struct v4l2_async_subdev *asd) { - struct cal_ctx *ctx = notifier_to_ctx(notifier); + struct cal_ctx *ctx = to_cal_asd(asd)->ctx; if (ctx->phy->sensor) { ctx_info(ctx, "Rejecting subdev %s (Already set!!)", @@ -2148,6 +2159,7 @@ static const struct v4l2_async_notifier_operations cal_async_ops = { static int of_cal_create_instance(struct cal_ctx *ctx, int inst) { + struct cal_v4l2_async_subdev *casd; struct v4l2_async_subdev *asd; struct fwnode_handle *fwnode; int ret; @@ -2163,6 +2175,9 @@ static int of_cal_create_instance(struct cal_ctx *ctx, int inst) return PTR_ERR(asd); } + casd = to_cal_asd(asd); + casd->ctx = ctx; + ret = v4l2_async_notifier_register(&ctx->cal->v4l2_dev, &ctx->notifier); if (ret) { -- GitLab From a539a0536f6e22a26e3e85a4cfb87344f800016f Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 6 Jul 2020 20:36:23 +0200 Subject: [PATCH 0595/1476] media: ti-vpe: cal: Move async notifiers from contexts to cal_dev There's no need to create one async notifier per CAL context. Merge them all in a single notifier, stored in cal_dev. Signed-off-by: Laurent Pinchart Reviewed-by: Benoit Parrot Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/ti-vpe/cal.c | 117 +++++++++++++++++----------- 1 file changed, 71 insertions(+), 46 deletions(-) diff --git a/drivers/media/platform/ti-vpe/cal.c b/drivers/media/platform/ti-vpe/cal.c index 83e790941e22b..55528b4d749cf 100644 --- a/drivers/media/platform/ti-vpe/cal.c +++ b/drivers/media/platform/ti-vpe/cal.c @@ -293,6 +293,7 @@ struct cal_dev { struct cal_ctx *ctx[CAL_NUM_CONTEXT]; struct v4l2_device v4l2_dev; + struct v4l2_async_notifier notifier; }; /* @@ -301,7 +302,6 @@ struct cal_dev { struct cal_ctx { struct v4l2_ctrl_handler ctrl_handler; struct video_device vdev; - struct v4l2_async_notifier notifier; struct cal_dev *cal; struct cal_camerarx *phy; @@ -337,11 +337,6 @@ struct cal_ctx { bool dma_act; }; -static inline struct cal_ctx *notifier_to_ctx(struct v4l2_async_notifier *n) -{ - return container_of(n, struct cal_ctx, notifier); -} - /* ------------------------------------------------------------------ * Platform Data * ------------------------------------------------------------------ @@ -2126,9 +2121,9 @@ to_cal_asd(struct v4l2_async_subdev *asd) return container_of(asd, struct cal_v4l2_async_subdev, asd); } -static int cal_async_bound(struct v4l2_async_notifier *notifier, - struct v4l2_subdev *subdev, - struct v4l2_async_subdev *asd) +static int cal_async_notifier_bound(struct v4l2_async_notifier *notifier, + struct v4l2_subdev *subdev, + struct v4l2_async_subdev *asd) { struct cal_ctx *ctx = to_cal_asd(asd)->ctx; @@ -2141,52 +2136,84 @@ static int cal_async_bound(struct v4l2_async_notifier *notifier, ctx->phy->sensor = subdev; ctx_dbg(1, ctx, "Using sensor %s for capture\n", subdev->name); - return cal_ctx_v4l2_init_formats(ctx); + return 0; } -static int cal_async_complete(struct v4l2_async_notifier *notifier) +static int cal_async_notifier_complete(struct v4l2_async_notifier *notifier) { - struct cal_ctx *ctx = notifier_to_ctx(notifier); + struct cal_dev *cal = container_of(notifier, struct cal_dev, notifier); + unsigned int i; + int ret; + + for (i = 0; i < ARRAY_SIZE(cal->ctx); ++i) { + struct cal_ctx *ctx = cal->ctx[i]; + + if (!ctx) + continue; + + ret = cal_ctx_v4l2_init_formats(ctx); + if (ret) + continue; + + cal_ctx_v4l2_register(ctx); + } - cal_ctx_v4l2_register(ctx); return 0; } -static const struct v4l2_async_notifier_operations cal_async_ops = { - .bound = cal_async_bound, - .complete = cal_async_complete, +static const struct v4l2_async_notifier_operations cal_async_notifier_ops = { + .bound = cal_async_notifier_bound, + .complete = cal_async_notifier_complete, }; -static int of_cal_create_instance(struct cal_ctx *ctx, int inst) +static int cal_async_notifier_register(struct cal_dev *cal) { - struct cal_v4l2_async_subdev *casd; - struct v4l2_async_subdev *asd; - struct fwnode_handle *fwnode; + unsigned int i; int ret; - v4l2_async_notifier_init(&ctx->notifier); - ctx->notifier.ops = &cal_async_ops; + v4l2_async_notifier_init(&cal->notifier); + cal->notifier.ops = &cal_async_notifier_ops; - fwnode = of_fwnode_handle(ctx->phy->sensor_node); - asd = v4l2_async_notifier_add_fwnode_subdev(&ctx->notifier, fwnode, - sizeof(*asd)); - if (IS_ERR(asd)) { - ctx_err(ctx, "Failed to add subdev to notifier\n"); - return PTR_ERR(asd); - } + for (i = 0; i < ARRAY_SIZE(cal->ctx); ++i) { + struct cal_ctx *ctx = cal->ctx[i]; + struct cal_v4l2_async_subdev *casd; + struct v4l2_async_subdev *asd; + struct fwnode_handle *fwnode; + + if (!ctx) + continue; - casd = to_cal_asd(asd); - casd->ctx = ctx; + fwnode = of_fwnode_handle(ctx->phy->sensor_node); + asd = v4l2_async_notifier_add_fwnode_subdev(&cal->notifier, + fwnode, + sizeof(*asd)); + if (IS_ERR(asd)) { + ctx_err(ctx, "Failed to add subdev to notifier\n"); + ret = PTR_ERR(asd); + goto error; + } + + casd = to_cal_asd(asd); + casd->ctx = ctx; + } - ret = v4l2_async_notifier_register(&ctx->cal->v4l2_dev, - &ctx->notifier); + ret = v4l2_async_notifier_register(&cal->v4l2_dev, &cal->notifier); if (ret) { - ctx_err(ctx, "Error registering async notifier\n"); - v4l2_async_notifier_cleanup(&ctx->notifier); - return ret; + cal_err(cal, "Error registering async notifier\n"); + goto error; } return 0; + +error: + v4l2_async_notifier_cleanup(&cal->notifier); + return ret; +} + +static void cal_async_notifier_unregister(struct cal_dev *cal) +{ + v4l2_async_notifier_unregister(&cal->notifier); + v4l2_async_notifier_cleanup(&cal->notifier); } /* ------------------------------------------------------------------ @@ -2212,10 +2239,6 @@ static struct cal_ctx *cal_ctx_create(struct cal_dev *cal, int inst) if (ret) return NULL; - ret = of_cal_create_instance(ctx, inst); - if (ret) - return NULL; - return ctx; } @@ -2356,6 +2379,11 @@ static int cal_probe(struct platform_device *pdev) cal_get_hwinfo(cal); pm_runtime_put_sync(&pdev->dev); + /* Register the async notifier. */ + ret = cal_async_notifier_register(cal); + if (ret) + goto error_pm_runtime; + return 0; error_pm_runtime: @@ -2366,11 +2394,8 @@ error_pm_runtime: error_context: for (i = 0; i < ARRAY_SIZE(cal->ctx); i++) { ctx = cal->ctx[i]; - if (ctx) { - v4l2_async_notifier_unregister(&ctx->notifier); - v4l2_async_notifier_cleanup(&ctx->notifier); + if (ctx) cal_ctx_v4l2_cleanup(ctx); - } } v4l2_device_unregister(&cal->v4l2_dev); @@ -2392,6 +2417,8 @@ static int cal_remove(struct platform_device *pdev) pm_runtime_get_sync(&pdev->dev); + cal_async_notifier_unregister(cal); + for (i = 0; i < ARRAY_SIZE(cal->ctx); i++) { ctx = cal->ctx[i]; if (ctx) { @@ -2399,8 +2426,6 @@ static int cal_remove(struct platform_device *pdev) video_device_node_name(&ctx->vdev)); cal_ctx_v4l2_unregister(ctx); cal_camerarx_disable(ctx->phy); - v4l2_async_notifier_unregister(&ctx->notifier); - v4l2_async_notifier_cleanup(&ctx->notifier); cal_ctx_v4l2_cleanup(ctx); } } -- GitLab From 539e7c5d7c028180ea16fc3b816cd07e0a09155d Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 6 Jul 2020 20:36:24 +0200 Subject: [PATCH 0596/1476] media: ti-vpe: cal: Replace context with phy in async notifier entries The async notifier is meant to bind to subdevs connected to the CSI-2 ports. Those ports are modelled by the CAMERARX phy instances. To prepare for additional decoupling of contexts and phys, make the notifier operate on phys. We still initialize and register the context V4L2 support in the async notifier complete operation as that's our signal that the userspace API is ready to be exposed. Signed-off-by: Laurent Pinchart Reviewed-by: Benoit Parrot Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/ti-vpe/cal.c | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/drivers/media/platform/ti-vpe/cal.c b/drivers/media/platform/ti-vpe/cal.c index 55528b4d749cf..59fa1f8c15643 100644 --- a/drivers/media/platform/ti-vpe/cal.c +++ b/drivers/media/platform/ti-vpe/cal.c @@ -2112,7 +2112,7 @@ static void cal_ctx_v4l2_cleanup(struct cal_ctx *ctx) struct cal_v4l2_async_subdev { struct v4l2_async_subdev asd; - struct cal_ctx *ctx; + struct cal_camerarx *phy; }; static inline struct cal_v4l2_async_subdev * @@ -2125,16 +2125,16 @@ static int cal_async_notifier_bound(struct v4l2_async_notifier *notifier, struct v4l2_subdev *subdev, struct v4l2_async_subdev *asd) { - struct cal_ctx *ctx = to_cal_asd(asd)->ctx; + struct cal_camerarx *phy = to_cal_asd(asd)->phy; - if (ctx->phy->sensor) { - ctx_info(ctx, "Rejecting subdev %s (Already set!!)", + if (phy->sensor) { + phy_info(phy, "Rejecting subdev %s (Already set!!)", subdev->name); return 0; } - ctx->phy->sensor = subdev; - ctx_dbg(1, ctx, "Using sensor %s for capture\n", subdev->name); + phy->sensor = subdev; + phy_dbg(1, phy, "Using sensor %s for capture\n", subdev->name); return 0; } @@ -2174,27 +2174,27 @@ static int cal_async_notifier_register(struct cal_dev *cal) v4l2_async_notifier_init(&cal->notifier); cal->notifier.ops = &cal_async_notifier_ops; - for (i = 0; i < ARRAY_SIZE(cal->ctx); ++i) { - struct cal_ctx *ctx = cal->ctx[i]; + for (i = 0; i < ARRAY_SIZE(cal->phy); ++i) { + struct cal_camerarx *phy = cal->phy[i]; struct cal_v4l2_async_subdev *casd; struct v4l2_async_subdev *asd; struct fwnode_handle *fwnode; - if (!ctx) + if (!phy || !phy->sensor_node) continue; - fwnode = of_fwnode_handle(ctx->phy->sensor_node); + fwnode = of_fwnode_handle(phy->sensor_node); asd = v4l2_async_notifier_add_fwnode_subdev(&cal->notifier, fwnode, sizeof(*asd)); if (IS_ERR(asd)) { - ctx_err(ctx, "Failed to add subdev to notifier\n"); + phy_err(phy, "Failed to add subdev to notifier\n"); ret = PTR_ERR(asd); goto error; } casd = to_cal_asd(asd); - casd->ctx = ctx; + casd->phy = phy; } ret = v4l2_async_notifier_register(&cal->v4l2_dev, &cal->notifier); -- GitLab From 517cf52b7777894ce5c0e63f5b57f95b879b2bb5 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 6 Jul 2020 20:36:25 +0200 Subject: [PATCH 0597/1476] media: ti-vpe: cal: Operate on phy instances in cal_quickdump_regs() The cal_quickdump_regs() dumps registers for the two CAMERARX instances. Retrieve those instances from the cal_dev directly instead of going through the contexts, and simplify the code by using a loop. Signed-off-by: Laurent Pinchart Reviewed-by: Benoit Parrot Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/ti-vpe/cal.c | 24 +++++++++++------------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/drivers/media/platform/ti-vpe/cal.c b/drivers/media/platform/ti-vpe/cal.c index 59fa1f8c15643..03b4d5351bfde 100644 --- a/drivers/media/platform/ti-vpe/cal.c +++ b/drivers/media/platform/ti-vpe/cal.c @@ -450,26 +450,24 @@ static inline void set_field(u32 *valp, u32 field, u32 mask) static void cal_quickdump_regs(struct cal_dev *cal) { + unsigned int i; + cal_info(cal, "CAL Registers @ 0x%pa:\n", &cal->res->start); print_hex_dump(KERN_INFO, "", DUMP_PREFIX_OFFSET, 16, 4, (__force const void *)cal->base, resource_size(cal->res), false); - if (cal->ctx[0]) { - cal_info(cal, "CSI2 Core 0 Registers @ %pa:\n", - &cal->ctx[0]->phy->res->start); - print_hex_dump(KERN_INFO, "", DUMP_PREFIX_OFFSET, 16, 4, - (__force const void *)cal->ctx[0]->phy->base, - resource_size(cal->ctx[0]->phy->res), - false); - } + for (i = 0; i < ARRAY_SIZE(cal->phy); ++i) { + struct cal_camerarx *phy = cal->phy[i]; + + if (!phy) + continue; - if (cal->ctx[1]) { - cal_info(cal, "CSI2 Core 1 Registers @ %pa:\n", - &cal->ctx[1]->phy->res->start); + cal_info(cal, "CSI2 Core %u Registers @ %pa:\n", i, + &phy->res->start); print_hex_dump(KERN_INFO, "", DUMP_PREFIX_OFFSET, 16, 4, - (__force const void *)cal->ctx[1]->phy->base, - resource_size(cal->ctx[1]->phy->res), + (__force const void *)phy->base, + resource_size(phy->res), false); } } -- GitLab From 497b5e94f820b3900dae89d34cd29db5ea394ff3 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 6 Jul 2020 20:36:26 +0200 Subject: [PATCH 0598/1476] media: ti-vpe: cal: Decouple context and phy cleanup at remove time The driver happens the use the same number of CAMERARX and context, but coupling their cleanup at remove time is wrong. To prepare for the introduction of additional contexts, decouple the two. Signed-off-by: Laurent Pinchart Reviewed-by: Benoit Parrot Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/ti-vpe/cal.c | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/drivers/media/platform/ti-vpe/cal.c b/drivers/media/platform/ti-vpe/cal.c index 03b4d5351bfde..e4d8cadf7befd 100644 --- a/drivers/media/platform/ti-vpe/cal.c +++ b/drivers/media/platform/ti-vpe/cal.c @@ -1976,6 +1976,9 @@ static int cal_ctx_v4l2_register(struct cal_ctx *ctx) static void cal_ctx_v4l2_unregister(struct cal_ctx *ctx) { + ctx_dbg(1, ctx, "unregistering %s\n", + video_device_node_name(&ctx->vdev)); + video_unregister_device(&ctx->vdev); } @@ -2408,7 +2411,6 @@ error_camerarx: static int cal_remove(struct platform_device *pdev) { struct cal_dev *cal = platform_get_drvdata(pdev); - struct cal_ctx *ctx; unsigned int i; cal_dbg(1, cal, "Removing %s\n", CAL_MODULE_NAME); @@ -2418,14 +2420,18 @@ static int cal_remove(struct platform_device *pdev) cal_async_notifier_unregister(cal); for (i = 0; i < ARRAY_SIZE(cal->ctx); i++) { - ctx = cal->ctx[i]; - if (ctx) { - ctx_dbg(1, ctx, "unregistering %s\n", - video_device_node_name(&ctx->vdev)); - cal_ctx_v4l2_unregister(ctx); - cal_camerarx_disable(ctx->phy); - cal_ctx_v4l2_cleanup(ctx); - } + if (cal->ctx[i]) + cal_ctx_v4l2_unregister(cal->ctx[i]); + } + + for (i = 0; i < ARRAY_SIZE(cal->phy); i++) { + if (cal->phy[i]) + cal_camerarx_disable(cal->phy[i]); + } + + for (i = 0; i < ARRAY_SIZE(cal->ctx); i++) { + if (cal->ctx[i]) + cal_ctx_v4l2_cleanup(cal->ctx[i]); } v4l2_device_unregister(&cal->v4l2_dev); -- GitLab From 4c0ed7ba65d1c2e9476c083ba83c23fe68ef6ebc Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 6 Jul 2020 20:36:27 +0200 Subject: [PATCH 0599/1476] media: ti-vpe: cal: Move CAL_NUM_CSI2_PORTS from cal_regs.h to cal.c The CAL_NUM_CSI2_PORTS macro isn't a register definition. Move it to cal.c, and fix indentation of the other macros while at it. Signed-off-by: Laurent Pinchart Reviewed-by: Benoit Parrot Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/ti-vpe/cal.c | 7 ++++--- drivers/media/platform/ti-vpe/cal_regs.h | 2 -- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/drivers/media/platform/ti-vpe/cal.c b/drivers/media/platform/ti-vpe/cal.c index e4d8cadf7befd..67b87d33639dc 100644 --- a/drivers/media/platform/ti-vpe/cal.c +++ b/drivers/media/platform/ti-vpe/cal.c @@ -75,10 +75,11 @@ MODULE_PARM_DESC(debug, "activates debug info"); #define phy_err(phy, fmt, arg...) \ cal_err((phy)->cal, "phy%u: " fmt, (phy)->instance, ##arg) -#define CAL_NUM_CONTEXT 2 +#define CAL_NUM_CONTEXT 2 +#define CAL_NUM_CSI2_PORTS 2 -#define MAX_WIDTH_BYTES (8192 * 8) -#define MAX_HEIGHT_LINES 16383 +#define MAX_WIDTH_BYTES (8192 * 8) +#define MAX_HEIGHT_LINES 16383 /* ------------------------------------------------------------------ * Format Handling diff --git a/drivers/media/platform/ti-vpe/cal_regs.h b/drivers/media/platform/ti-vpe/cal_regs.h index b551e67d5d419..f752096dcf7f7 100644 --- a/drivers/media/platform/ti-vpe/cal_regs.h +++ b/drivers/media/platform/ti-vpe/cal_regs.h @@ -34,8 +34,6 @@ */ #define DRA72_CAL_PRE_ES2_LDO_DISABLE BIT(0) -#define CAL_NUM_CSI2_PORTS 2 - /* CAL register offsets */ #define CAL_HL_REVISION 0x0000 -- GitLab From 7e75b1502f65d70088f522cb4474502644b44428 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 6 Jul 2020 20:36:28 +0200 Subject: [PATCH 0600/1476] media: ti-vpe: cal: Remove isvcirqset() and isportirqset() macros The isvcirqset() isn't used. The isportirqset() doesn't increase readability. Inline the latter and simply drop the former. Signed-off-by: Laurent Pinchart Reviewed-by: Benoit Parrot Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/ti-vpe/cal.c | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/drivers/media/platform/ti-vpe/cal.c b/drivers/media/platform/ti-vpe/cal.c index 67b87d33639dc..e57ee60105555 100644 --- a/drivers/media/platform/ti-vpe/cal.c +++ b/drivers/media/platform/ti-vpe/cal.c @@ -1265,11 +1265,6 @@ static inline void cal_process_buffer_complete(struct cal_ctx *ctx) ctx->cur_frm = ctx->next_frm; } -#define isvcirqset(irq, vc, ff) (irq & \ - (CAL_CSI2_VC_IRQENABLE_ ##ff ##_IRQ_##vc ##_MASK)) - -#define isportirqset(irq, port) (irq & CAL_HL_IRQ_MASK(port)) - static irqreturn_t cal_irq(int irq_cal, void *data) { struct cal_dev *cal = data; @@ -1309,7 +1304,7 @@ static irqreturn_t cal_irq(int irq_cal, void *data) reg_write(cal, CAL_HL_IRQSTATUS(1), status); for (i = 0; i < ARRAY_SIZE(cal->ctx); ++i) { - if (isportirqset(status, i)) { + if (status & CAL_HL_IRQ_MASK(i)) { ctx = cal->ctx[i]; spin_lock(&ctx->slock); @@ -1332,7 +1327,7 @@ static irqreturn_t cal_irq(int irq_cal, void *data) reg_write(cal, CAL_HL_IRQSTATUS(2), status); for (i = 0; i < ARRAY_SIZE(cal->ctx); ++i) { - if (isportirqset(status, i)) { + if (status & CAL_HL_IRQ_MASK(i)) { ctx = cal->ctx[i]; dma_q = &ctx->vidq; -- GitLab From a0db060fb100c16b533595f468ed57ba4bb2d5a3 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 6 Jul 2020 20:36:29 +0200 Subject: [PATCH 0601/1476] media: ti-vpe: cal: Replace number of ports numerical value by macro Use the CAL_NUM_CSI2_PORTS macro instead of the hardcoded numerical value 2 to iterate over the CSI-2 ports. Signed-off-by: Laurent Pinchart Reviewed-by: Benoit Parrot Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/ti-vpe/cal.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/platform/ti-vpe/cal.c b/drivers/media/platform/ti-vpe/cal.c index e57ee60105555..617b171330715 100644 --- a/drivers/media/platform/ti-vpe/cal.c +++ b/drivers/media/platform/ti-vpe/cal.c @@ -1281,7 +1281,7 @@ static irqreturn_t cal_irq(int irq_cal, void *data) if (status & CAL_HL_IRQ_OCPO_ERR_MASK) dev_err_ratelimited(&cal->pdev->dev, "OCPO ERROR\n"); - for (i = 0; i < 2; ++i) { + for (i = 0; i < CAL_NUM_CSI2_PORTS; ++i) { if (status & CAL_HL_IRQ_CIO_MASK(i)) { u32 cio_stat = reg_read(cal, CAL_CSI2_COMPLEXIO_IRQSTATUS(i)); -- GitLab From 45dd15e6ce2241da7d3f08b86b680d8b7d3dcd67 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 6 Jul 2020 20:36:30 +0200 Subject: [PATCH 0602/1476] media: ti-vpe: cal: Split media initialization and cleanup to functions Create four functions to handle initialization, cleanup, registration and unregistration of the V4L2 (and soon media controller) objects: - init() is meant to be called early at probe time to initialize the objects, before they get used from within the kernel - cleanup() is the counterpart of init, and is meant to be called at the end of the remove sequence to free all objects - register() is meant to be called at the end of the probe sequence, to register the userspace-facing devices - unregister() is the counterpart of register, and is meant to be called at the beginning for the remove sequence, to disallow access from userspace Signed-off-by: Laurent Pinchart Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/ti-vpe/cal.c | 116 +++++++++++++++++++++------- 1 file changed, 90 insertions(+), 26 deletions(-) diff --git a/drivers/media/platform/ti-vpe/cal.c b/drivers/media/platform/ti-vpe/cal.c index 617b171330715..340cbf385d42c 100644 --- a/drivers/media/platform/ti-vpe/cal.c +++ b/drivers/media/platform/ti-vpe/cal.c @@ -2213,6 +2213,88 @@ static void cal_async_notifier_unregister(struct cal_dev *cal) v4l2_async_notifier_cleanup(&cal->notifier); } +/* ------------------------------------------------------------------ + * Media and V4L2 device handling + * ------------------------------------------------------------------ + */ + +/* + * Register user-facing devices. To be called at the end of the probe function + * when all resources are initialized and ready. + */ +static int cal_media_register(struct cal_dev *cal) +{ + int ret; + + /* + * Register the async notifier. This may trigger registration of the + * V4L2 video devices if all subdevs are ready. + */ + ret = cal_async_notifier_register(cal); + if (ret) + return ret; + + return 0; +} + +/* + * Unregister the user-facing devices, but don't free memory yet. To be called + * at the beginning of the remove function, to disallow access from userspace. + */ +static void cal_media_unregister(struct cal_dev *cal) +{ + unsigned int i; + + /* Unregister all the V4L2 video devices. */ + for (i = 0; i < ARRAY_SIZE(cal->ctx); i++) { + if (cal->ctx[i]) + cal_ctx_v4l2_unregister(cal->ctx[i]); + } + + cal_async_notifier_unregister(cal); +} + +/* + * Initialize the in-kernel objects. To be called at the beginning of the probe + * function, before the V4L2 device is used by the driver. + */ +static int cal_media_init(struct cal_dev *cal) +{ + int ret; + + /* + * Initialize the V4L2 device (despite the function name, this performs + * initialization, not registration). + */ + ret = v4l2_device_register(&cal->pdev->dev, &cal->v4l2_dev); + if (ret) { + cal_err(cal, "Failed to register V4L2 device\n"); + return ret; + } + + vb2_dma_contig_set_max_seg_size(&cal->pdev->dev, DMA_BIT_MASK(32)); + + return 0; +} + +/* + * Cleanup the in-kernel objects, freeing memory. To be called at the very end + * of the remove sequence, when nothing (including userspace) can access the + * objects anymore. + */ +static void cal_media_cleanup(struct cal_dev *cal) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(cal->ctx); i++) { + if (cal->ctx[i]) + cal_ctx_v4l2_cleanup(cal->ctx[i]); + } + + v4l2_device_unregister(&cal->v4l2_dev); + vb2_dma_contig_clear_max_seg_size(&cal->pdev->dev); +} + /* ------------------------------------------------------------------ * Initialization and module stuff * ------------------------------------------------------------------ @@ -2345,12 +2427,10 @@ static int cal_probe(struct platform_device *pdev) goto error_camerarx; } - /* Register the V4L2 device. */ - ret = v4l2_device_register(&pdev->dev, &cal->v4l2_dev); - if (ret) { - cal_err(cal, "Failed to register V4L2 device\n"); + /* Initialize the media device. */ + ret = cal_media_init(cal); + if (ret < 0) goto error_camerarx; - } /* Create contexts. */ for (i = 0; i < cal->data->num_csi2_phy; ++i) { @@ -2365,8 +2445,6 @@ static int cal_probe(struct platform_device *pdev) } } - vb2_dma_contig_set_max_seg_size(&pdev->dev, DMA_BIT_MASK(32)); - /* Read the revision and hardware info to verify hardware access. */ pm_runtime_enable(&pdev->dev); ret = pm_runtime_get_sync(&pdev->dev); @@ -2376,16 +2454,14 @@ static int cal_probe(struct platform_device *pdev) cal_get_hwinfo(cal); pm_runtime_put_sync(&pdev->dev); - /* Register the async notifier. */ - ret = cal_async_notifier_register(cal); + /* Register the media device. */ + ret = cal_media_register(cal); if (ret) goto error_pm_runtime; return 0; error_pm_runtime: - vb2_dma_contig_clear_max_seg_size(&pdev->dev); - pm_runtime_disable(&pdev->dev); error_context: @@ -2395,7 +2471,7 @@ error_context: cal_ctx_v4l2_cleanup(ctx); } - v4l2_device_unregister(&cal->v4l2_dev); + cal_media_cleanup(cal); error_camerarx: for (i = 0; i < ARRAY_SIZE(cal->phy); i++) @@ -2413,24 +2489,14 @@ static int cal_remove(struct platform_device *pdev) pm_runtime_get_sync(&pdev->dev); - cal_async_notifier_unregister(cal); - - for (i = 0; i < ARRAY_SIZE(cal->ctx); i++) { - if (cal->ctx[i]) - cal_ctx_v4l2_unregister(cal->ctx[i]); - } + cal_media_unregister(cal); for (i = 0; i < ARRAY_SIZE(cal->phy); i++) { if (cal->phy[i]) cal_camerarx_disable(cal->phy[i]); } - for (i = 0; i < ARRAY_SIZE(cal->ctx); i++) { - if (cal->ctx[i]) - cal_ctx_v4l2_cleanup(cal->ctx[i]); - } - - v4l2_device_unregister(&cal->v4l2_dev); + cal_media_cleanup(cal); for (i = 0; i < ARRAY_SIZE(cal->phy); i++) cal_camerarx_destroy(cal->phy[i]); @@ -2438,8 +2504,6 @@ static int cal_remove(struct platform_device *pdev) pm_runtime_put_sync(&pdev->dev); pm_runtime_disable(&pdev->dev); - vb2_dma_contig_clear_max_seg_size(&pdev->dev); - return 0; } -- GitLab From 05930cf1e5c5d7d526e492cfe0f0bb3116bc29de Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 6 Jul 2020 20:36:31 +0200 Subject: [PATCH 0603/1476] media: ti-vpe: cal: Read hardware revision earlier during probe Read the hardware revision and info right after allocating resources, as there's no need to delay doing so until all initialization is complete. Signed-off-by: Laurent Pinchart Reviewed-by: Benoit Parrot Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/ti-vpe/cal.c | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/drivers/media/platform/ti-vpe/cal.c b/drivers/media/platform/ti-vpe/cal.c index 340cbf385d42c..fca591a94aca9 100644 --- a/drivers/media/platform/ti-vpe/cal.c +++ b/drivers/media/platform/ti-vpe/cal.c @@ -2408,6 +2408,15 @@ static int cal_probe(struct platform_device *pdev) if (ret) return ret; + /* Read the revision and hardware info to verify hardware access. */ + pm_runtime_enable(&pdev->dev); + ret = pm_runtime_get_sync(&pdev->dev); + if (ret) + goto error_pm_runtime; + + cal_get_hwinfo(cal); + pm_runtime_put_sync(&pdev->dev); + /* Create CAMERARX PHYs. */ for (i = 0; i < cal->data->num_csi2_phy; ++i) { cal->phy[i] = cal_camerarx_create(cal, i); @@ -2445,25 +2454,13 @@ static int cal_probe(struct platform_device *pdev) } } - /* Read the revision and hardware info to verify hardware access. */ - pm_runtime_enable(&pdev->dev); - ret = pm_runtime_get_sync(&pdev->dev); - if (ret) - goto error_pm_runtime; - - cal_get_hwinfo(cal); - pm_runtime_put_sync(&pdev->dev); - /* Register the media device. */ ret = cal_media_register(cal); if (ret) - goto error_pm_runtime; + goto error_context; return 0; -error_pm_runtime: - pm_runtime_disable(&pdev->dev); - error_context: for (i = 0; i < ARRAY_SIZE(cal->ctx); i++) { ctx = cal->ctx[i]; @@ -2477,6 +2474,9 @@ error_camerarx: for (i = 0; i < ARRAY_SIZE(cal->phy); i++) cal_camerarx_destroy(cal->phy[i]); +error_pm_runtime: + pm_runtime_disable(&pdev->dev); + return ret; } -- GitLab From dfbb172e512eaba56039e0b4c485256295442147 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 6 Jul 2020 20:36:32 +0200 Subject: [PATCH 0604/1476] media: ti-vpe: cal: Print revision and hwinfo in a more readable format Print the hardware revision in the X.Y.R format, which is more readable that the 32-bit hex value. For the hardware info register, only print its value if it doesn't contain what we expect. Signed-off-by: Laurent Pinchart Reviewed-by: Benoit Parrot Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/ti-vpe/cal.c | 29 ++++++++++++++++++++++------- 1 file changed, 22 insertions(+), 7 deletions(-) diff --git a/drivers/media/platform/ti-vpe/cal.c b/drivers/media/platform/ti-vpe/cal.c index fca591a94aca9..ec52cb7f3039c 100644 --- a/drivers/media/platform/ti-vpe/cal.c +++ b/drivers/media/platform/ti-vpe/cal.c @@ -2342,21 +2342,36 @@ static const struct of_device_id cal_of_match[] = { }; MODULE_DEVICE_TABLE(of, cal_of_match); -/* - * Get Revision and HW info - */ +/* Get hardware revision and info. */ + +#define CAL_HL_HWINFO_VALUE 0xa3c90469 + static void cal_get_hwinfo(struct cal_dev *cal) { u32 revision; u32 hwinfo; revision = reg_read(cal, CAL_HL_REVISION); - cal_dbg(3, cal, "CAL_HL_REVISION = 0x%08x (expecting 0x40000200)\n", - revision); + switch (FIELD_GET(CAL_HL_REVISION_SCHEME_MASK, revision)) { + case CAL_HL_REVISION_SCHEME_H08: + cal_dbg(3, cal, "CAL HW revision %lu.%lu.%lu (0x%08x)\n", + FIELD_GET(CAL_HL_REVISION_MAJOR_MASK, revision), + FIELD_GET(CAL_HL_REVISION_MINOR_MASK, revision), + FIELD_GET(CAL_HL_REVISION_RTL_MASK, revision), + revision); + break; + + case CAL_HL_REVISION_SCHEME_LEGACY: + default: + cal_info(cal, "Unexpected CAL HW revision 0x%08x\n", + revision); + break; + } hwinfo = reg_read(cal, CAL_HL_HWINFO); - cal_dbg(3, cal, "CAL_HL_HWINFO = 0x%08x (expecting 0xA3C90469)\n", - hwinfo); + if (hwinfo != CAL_HL_HWINFO_VALUE) + cal_info(cal, "CAL_HL_HWINFO = 0x%08x, expected 0x%08x\n", + hwinfo, CAL_HL_HWINFO_VALUE); } static int cal_probe(struct platform_device *pdev) -- GitLab From 92790656cee2f0b02855452d73f85127ac68081c Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 6 Jul 2020 20:36:33 +0200 Subject: [PATCH 0605/1476] media: ti-vpe: cal: Store struct device in cal_dev The cal_dev structure stores the platform_device pointer, but most accesses to that field need the device pointer. Store the struct device pointer directly to simplify the code, and use to_platform_device() in the two locations that need the platform device. Signed-off-by: Laurent Pinchart Reviewed-by: Benoit Parrot Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/ti-vpe/cal.c | 55 ++++++++++++++--------------- 1 file changed, 27 insertions(+), 28 deletions(-) diff --git a/drivers/media/platform/ti-vpe/cal.c b/drivers/media/platform/ti-vpe/cal.c index ec52cb7f3039c..42b948e784d22 100644 --- a/drivers/media/platform/ti-vpe/cal.c +++ b/drivers/media/platform/ti-vpe/cal.c @@ -53,13 +53,12 @@ MODULE_PARM_DESC(debug, "activates debug info"); #define cal_dbg(level, cal, fmt, arg...) \ do { \ if (debug >= (level)) \ - dev_printk(KERN_DEBUG, &(cal)->pdev->dev, \ - fmt, ##arg); \ + dev_printk(KERN_DEBUG, (cal)->dev, fmt, ##arg); \ } while (0) #define cal_info(cal, fmt, arg...) \ - dev_info(&(cal)->pdev->dev, fmt, ##arg) + dev_info((cal)->dev, fmt, ##arg) #define cal_err(cal, fmt, arg...) \ - dev_err(&(cal)->pdev->dev, fmt, ##arg) + dev_err((cal)->dev, fmt, ##arg) #define ctx_dbg(level, ctx, fmt, arg...) \ cal_dbg(level, (ctx)->cal, "ctx%u: " fmt, (ctx)->index, ##arg) @@ -263,7 +262,7 @@ struct cal_data { struct cal_camerarx { void __iomem *base; struct resource *res; - struct platform_device *pdev; + struct device *dev; struct regmap_field *fields[F_MAX_FIELDS]; struct cal_dev *cal; @@ -280,7 +279,7 @@ struct cal_dev { int irq; void __iomem *base; struct resource *res; - struct platform_device *pdev; + struct device *dev; const struct cal_data *data; @@ -908,7 +907,7 @@ static int cal_camerarx_regmap_init(struct cal_dev *cal, * Here we update the reg offset with the * value found in DT */ - phy->fields[i] = devm_regmap_field_alloc(&cal->pdev->dev, + phy->fields[i] = devm_regmap_field_alloc(cal->dev, cal->syscon_camerrx, field); if (IS_ERR(phy->fields[i])) { @@ -923,7 +922,6 @@ static int cal_camerarx_regmap_init(struct cal_dev *cal, static int cal_camerarx_parse_dt(struct cal_camerarx *phy) { struct v4l2_fwnode_endpoint *endpoint = &phy->endpoint; - struct platform_device *pdev = phy->cal->pdev; struct device_node *ep_node; char data_lanes[V4L2_FWNODE_CSI2_MAX_DATA_LANES * 2]; unsigned int i; @@ -933,7 +931,7 @@ static int cal_camerarx_parse_dt(struct cal_camerarx *phy) * Find the endpoint node for the port corresponding to the PHY * instance, and parse its CSI-2-related properties. */ - ep_node = of_graph_get_endpoint_by_regs(pdev->dev.of_node, + ep_node = of_graph_get_endpoint_by_regs(phy->cal->dev->of_node, phy->instance, 0); if (!ep_node) { /* @@ -990,7 +988,7 @@ done: static struct cal_camerarx *cal_camerarx_create(struct cal_dev *cal, unsigned int instance) { - struct platform_device *pdev = cal->pdev; + struct platform_device *pdev = to_platform_device(cal->dev); struct cal_camerarx *phy; int ret; @@ -1006,7 +1004,7 @@ static struct cal_camerarx *cal_camerarx_create(struct cal_dev *cal, (instance == 0) ? "cal_rx_core0" : "cal_rx_core1"); - phy->base = devm_ioremap_resource(&pdev->dev, phy->res); + phy->base = devm_ioremap_resource(cal->dev, phy->res); if (IS_ERR(phy->base)) { cal_err(cal, "failed to ioremap\n"); ret = PTR_ERR(phy->base); @@ -1042,7 +1040,8 @@ static void cal_camerarx_destroy(struct cal_camerarx *phy) static int cal_camerarx_init_regmap(struct cal_dev *cal) { - struct device_node *np = cal->pdev->dev.of_node; + struct platform_device *pdev = to_platform_device(cal->dev); + struct device_node *np = cal->dev->of_node; struct regmap_config config = { }; struct regmap *syscon; struct resource *res; @@ -1057,16 +1056,16 @@ static int cal_camerarx_init_regmap(struct cal_dev *cal) return 0; } - dev_warn(&cal->pdev->dev, "failed to get ti,camerrx-control: %ld\n", + dev_warn(cal->dev, "failed to get ti,camerrx-control: %ld\n", PTR_ERR(syscon)); /* * Backward DTS compatibility. If syscon entry is not present then * check if the camerrx_control resource is present. */ - res = platform_get_resource_byname(cal->pdev, IORESOURCE_MEM, + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "camerrx_control"); - base = devm_ioremap_resource(&cal->pdev->dev, res); + base = devm_ioremap_resource(cal->dev, res); if (IS_ERR(base)) { cal_err(cal, "failed to ioremap camerrx_control\n"); return PTR_ERR(base); @@ -1157,7 +1156,7 @@ static void cal_ctx_pix_proc_config(struct cal_ctx *ctx) * * Instead of failing here just use 8 bpp as a default. */ - dev_warn_once(&ctx->cal->pdev->dev, + dev_warn_once(ctx->cal->dev, "%s:%d:%s: bpp:%d unsupported! Overwritten with 8.\n", __FILE__, __LINE__, __func__, ctx->fmt->bpp); extract = CAL_PIX_PROC_EXTRACT_B8; @@ -1279,14 +1278,14 @@ static irqreturn_t cal_irq(int irq_cal, void *data) reg_write(cal, CAL_HL_IRQSTATUS(0), status); if (status & CAL_HL_IRQ_OCPO_ERR_MASK) - dev_err_ratelimited(&cal->pdev->dev, "OCPO ERROR\n"); + dev_err_ratelimited(cal->dev, "OCPO ERROR\n"); for (i = 0; i < CAL_NUM_CSI2_PORTS; ++i) { if (status & CAL_HL_IRQ_CIO_MASK(i)) { u32 cio_stat = reg_read(cal, CAL_CSI2_COMPLEXIO_IRQSTATUS(i)); - dev_err_ratelimited(&cal->pdev->dev, + dev_err_ratelimited(cal->dev, "CIO%u error: %#08x\n", i, cio_stat); reg_write(cal, CAL_CSI2_COMPLEXIO_IRQSTATUS(i), @@ -1388,7 +1387,7 @@ static int cal_querycap(struct file *file, void *priv, strscpy(cap->card, CAL_MODULE_NAME, sizeof(cap->card)); snprintf(cap->bus_info, sizeof(cap->bus_info), - "platform:%s", dev_name(&ctx->cal->pdev->dev)); + "platform:%s", dev_name(ctx->cal->dev)); return 0; } @@ -1822,7 +1821,7 @@ static int cal_start_streaming(struct vb2_queue *vq, unsigned int count) goto err; } - pm_runtime_get_sync(&ctx->cal->pdev->dev); + pm_runtime_get_sync(ctx->cal->dev); cal_ctx_csi2_config(ctx); cal_ctx_pix_proc_config(ctx); @@ -1837,7 +1836,7 @@ static int cal_start_streaming(struct vb2_queue *vq, unsigned int count) if (ret) { v4l2_subdev_call(ctx->phy->sensor, core, s_power, 0); ctx_err(ctx, "stream on failed in subdev\n"); - pm_runtime_put_sync(&ctx->cal->pdev->dev); + pm_runtime_put_sync(ctx->cal->dev); goto err; } @@ -1917,7 +1916,7 @@ static void cal_stop_streaming(struct vb2_queue *vq) ctx->next_frm = NULL; spin_unlock_irqrestore(&ctx->slock, flags); - pm_runtime_put_sync(&ctx->cal->pdev->dev); + pm_runtime_put_sync(ctx->cal->dev); } static const struct vb2_ops cal_video_qops = { @@ -2065,7 +2064,7 @@ static int cal_ctx_v4l2_init(struct cal_ctx *ctx) q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; q->lock = &ctx->mutex; q->min_buffers_needed = 3; - q->dev = &ctx->cal->pdev->dev; + q->dev = ctx->cal->dev; ret = vb2_queue_init(q); if (ret) @@ -2266,13 +2265,13 @@ static int cal_media_init(struct cal_dev *cal) * Initialize the V4L2 device (despite the function name, this performs * initialization, not registration). */ - ret = v4l2_device_register(&cal->pdev->dev, &cal->v4l2_dev); + ret = v4l2_device_register(cal->dev, &cal->v4l2_dev); if (ret) { cal_err(cal, "Failed to register V4L2 device\n"); return ret; } - vb2_dma_contig_set_max_seg_size(&cal->pdev->dev, DMA_BIT_MASK(32)); + vb2_dma_contig_set_max_seg_size(cal->dev, DMA_BIT_MASK(32)); return 0; } @@ -2292,7 +2291,7 @@ static void cal_media_cleanup(struct cal_dev *cal) } v4l2_device_unregister(&cal->v4l2_dev); - vb2_dma_contig_clear_max_seg_size(&cal->pdev->dev); + vb2_dma_contig_clear_max_seg_size(cal->dev); } /* ------------------------------------------------------------------ @@ -2305,7 +2304,7 @@ static struct cal_ctx *cal_ctx_create(struct cal_dev *cal, int inst) struct cal_ctx *ctx; int ret; - ctx = devm_kzalloc(&cal->pdev->dev, sizeof(*ctx), GFP_KERNEL); + ctx = devm_kzalloc(cal->dev, sizeof(*ctx), GFP_KERNEL); if (!ctx) return NULL; @@ -2393,7 +2392,7 @@ static int cal_probe(struct platform_device *pdev) return -ENODEV; } - cal->pdev = pdev; + cal->dev = &pdev->dev; platform_set_drvdata(pdev, cal); /* Acquire resources: clocks, CAMERARX regmap, I/O memory and IRQ. */ -- GitLab From b908235114d122ad59c8287f2dad0a1bd45b4e3a Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 6 Jul 2020 20:36:34 +0200 Subject: [PATCH 0606/1476] media: ti-vpe: cal: Register a media device Enable the media controller API by registering a media device and initializing the media entities corresponding to the video devices. The context initialization is slightly refactored as a result. The media graph will be built in a subsequent change. Signed-off-by: Laurent Pinchart Reviewed-by: Benoit Parrot Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/ti-vpe/cal.c | 71 ++++++++++++++++++++--------- 1 file changed, 49 insertions(+), 22 deletions(-) diff --git a/drivers/media/platform/ti-vpe/cal.c b/drivers/media/platform/ti-vpe/cal.c index 42b948e784d22..236fe826b635c 100644 --- a/drivers/media/platform/ti-vpe/cal.c +++ b/drivers/media/platform/ti-vpe/cal.c @@ -22,6 +22,7 @@ #include #include +#include #include #include #include @@ -282,6 +283,7 @@ struct cal_dev { struct device *dev; const struct cal_data *data; + u32 revision; /* Control Module handle */ struct regmap *syscon_camerrx; @@ -292,6 +294,7 @@ struct cal_dev { struct cal_ctx *ctx[CAL_NUM_CONTEXT]; + struct media_device mdev; struct v4l2_device v4l2_dev; struct v4l2_async_notifier notifier; }; @@ -302,6 +305,7 @@ struct cal_dev { struct cal_ctx { struct v4l2_ctrl_handler ctrl_handler; struct video_device vdev; + struct media_pad pad; struct cal_dev *cal; struct cal_camerarx *phy; @@ -2050,11 +2054,11 @@ static int cal_ctx_v4l2_init(struct cal_ctx *ctx) struct vb2_queue *q = &ctx->vb_vidq; int ret; - /* initialize locks */ + INIT_LIST_HEAD(&ctx->vidq.active); spin_lock_init(&ctx->slock); mutex_init(&ctx->mutex); - /* initialize queue */ + /* Initialize the vb2 queue. */ q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; q->io_modes = VB2_MMAP | VB2_DMABUF | VB2_READ; q->drv_priv = ctx; @@ -2070,35 +2074,39 @@ static int cal_ctx_v4l2_init(struct cal_ctx *ctx) if (ret) return ret; - /* init video dma queues */ - INIT_LIST_HEAD(&ctx->vidq.active); - + /* Initialize the video device and media entity. */ *vfd = cal_videodev; vfd->v4l2_dev = &ctx->cal->v4l2_dev; vfd->queue = q; + snprintf(vfd->name, sizeof(vfd->name), "CAL output %u", ctx->index); + vfd->lock = &ctx->mutex; + video_set_drvdata(vfd, ctx); + + ctx->pad.flags = MEDIA_PAD_FL_SINK; + ret = media_entity_pads_init(&vfd->entity, 1, &ctx->pad); + if (ret < 0) + return ret; /* Initialize the control handler. */ ret = v4l2_ctrl_handler_init(hdl, 11); if (ret < 0) { ctx_err(ctx, "Failed to init ctrl handler\n"); - return ret; + goto error; } vfd->ctrl_handler = hdl; - /* - * Provide a mutex to v4l2 core. It will be used to protect - * all fops and v4l2 ioctls. - */ - vfd->lock = &ctx->mutex; - video_set_drvdata(vfd, ctx); - return 0; + +error: + media_entity_cleanup(&vfd->entity); + return ret; } static void cal_ctx_v4l2_cleanup(struct cal_ctx *ctx) { v4l2_ctrl_handler_free(&ctx->ctrl_handler); + media_entity_cleanup(&ctx->vdev.entity); } /* ------------------------------------------------------------------ @@ -2225,13 +2233,21 @@ static int cal_media_register(struct cal_dev *cal) { int ret; + ret = media_device_register(&cal->mdev); + if (ret) { + cal_err(cal, "Failed to register media device\n"); + return ret; + } + /* * Register the async notifier. This may trigger registration of the * V4L2 video devices if all subdevs are ready. */ ret = cal_async_notifier_register(cal); - if (ret) + if (ret) { + media_device_unregister(&cal->mdev); return ret; + } return 0; } @@ -2251,6 +2267,7 @@ static void cal_media_unregister(struct cal_dev *cal) } cal_async_notifier_unregister(cal); + media_device_unregister(&cal->mdev); } /* @@ -2259,12 +2276,21 @@ static void cal_media_unregister(struct cal_dev *cal) */ static int cal_media_init(struct cal_dev *cal) { + struct media_device *mdev = &cal->mdev; int ret; + mdev->dev = cal->dev; + mdev->hw_revision = cal->revision; + strscpy(mdev->model, "CAL", sizeof(mdev->model)); + snprintf(mdev->bus_info, sizeof(mdev->bus_info), "platform:%s", + dev_name(mdev->dev)); + media_device_init(mdev); + /* * Initialize the V4L2 device (despite the function name, this performs * initialization, not registration). */ + cal->v4l2_dev.mdev = mdev; ret = v4l2_device_register(cal->dev, &cal->v4l2_dev); if (ret) { cal_err(cal, "Failed to register V4L2 device\n"); @@ -2291,6 +2317,8 @@ static void cal_media_cleanup(struct cal_dev *cal) } v4l2_device_unregister(&cal->v4l2_dev); + media_device_cleanup(&cal->mdev); + vb2_dma_contig_clear_max_seg_size(cal->dev); } @@ -2347,23 +2375,22 @@ MODULE_DEVICE_TABLE(of, cal_of_match); static void cal_get_hwinfo(struct cal_dev *cal) { - u32 revision; u32 hwinfo; - revision = reg_read(cal, CAL_HL_REVISION); - switch (FIELD_GET(CAL_HL_REVISION_SCHEME_MASK, revision)) { + cal->revision = reg_read(cal, CAL_HL_REVISION); + switch (FIELD_GET(CAL_HL_REVISION_SCHEME_MASK, cal->revision)) { case CAL_HL_REVISION_SCHEME_H08: cal_dbg(3, cal, "CAL HW revision %lu.%lu.%lu (0x%08x)\n", - FIELD_GET(CAL_HL_REVISION_MAJOR_MASK, revision), - FIELD_GET(CAL_HL_REVISION_MINOR_MASK, revision), - FIELD_GET(CAL_HL_REVISION_RTL_MASK, revision), - revision); + FIELD_GET(CAL_HL_REVISION_MAJOR_MASK, cal->revision), + FIELD_GET(CAL_HL_REVISION_MINOR_MASK, cal->revision), + FIELD_GET(CAL_HL_REVISION_RTL_MASK, cal->revision), + cal->revision); break; case CAL_HL_REVISION_SCHEME_LEGACY: default: cal_info(cal, "Unexpected CAL HW revision 0x%08x\n", - revision); + cal->revision); break; } -- GitLab From 1b22b7a0ec9c505ac9b55adeb84a7c15cbd0bbb0 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 6 Jul 2020 20:36:35 +0200 Subject: [PATCH 0607/1476] media: ti-vpe: cal: Init formats in cal_ctx_v4l2_register() To prepare for splitting the V4L2 API support to a separate file, call cal_ctx_v4l2_init_formats() from cal_ctx_v4l2_register(). Signed-off-by: Laurent Pinchart Reviewed-by: Benoit Parrot Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/ti-vpe/cal.c | 83 ++++++++++++++--------------- 1 file changed, 39 insertions(+), 44 deletions(-) diff --git a/drivers/media/platform/ti-vpe/cal.c b/drivers/media/platform/ti-vpe/cal.c index 236fe826b635c..26747e6da3588 100644 --- a/drivers/media/platform/ti-vpe/cal.c +++ b/drivers/media/platform/ti-vpe/cal.c @@ -1948,39 +1948,6 @@ static const struct video_device cal_videodev = { V4L2_CAP_READWRITE, }; -static int cal_ctx_v4l2_register(struct cal_ctx *ctx) -{ - struct v4l2_ctrl_handler *hdl = &ctx->ctrl_handler; - struct video_device *vfd = &ctx->vdev; - int ret; - - ret = v4l2_ctrl_add_handler(hdl, ctx->phy->sensor->ctrl_handler, NULL, - true); - if (ret < 0) { - ctx_err(ctx, "Failed to add sensor ctrl handler\n"); - return ret; - } - - ret = video_register_device(vfd, VFL_TYPE_VIDEO, video_nr); - if (ret < 0) { - ctx_err(ctx, "Failed to register video device\n"); - return ret; - } - - ctx_info(ctx, "V4L2 device registered as %s\n", - video_device_node_name(vfd)); - - return 0; -} - -static void cal_ctx_v4l2_unregister(struct cal_ctx *ctx) -{ - ctx_dbg(1, ctx, "unregistering %s\n", - video_device_node_name(&ctx->vdev)); - - video_unregister_device(&ctx->vdev); -} - static int cal_ctx_v4l2_init_formats(struct cal_ctx *ctx) { struct v4l2_subdev_mbus_code_enum mbus_code; @@ -2047,6 +2014,43 @@ static int cal_ctx_v4l2_init_formats(struct cal_ctx *ctx) return 0; } +static int cal_ctx_v4l2_register(struct cal_ctx *ctx) +{ + struct v4l2_ctrl_handler *hdl = &ctx->ctrl_handler; + struct video_device *vfd = &ctx->vdev; + int ret; + + ret = cal_ctx_v4l2_init_formats(ctx); + if (ret) + return ret; + + ret = v4l2_ctrl_add_handler(hdl, ctx->phy->sensor->ctrl_handler, NULL, + true); + if (ret < 0) { + ctx_err(ctx, "Failed to add sensor ctrl handler\n"); + return ret; + } + + ret = video_register_device(vfd, VFL_TYPE_VIDEO, video_nr); + if (ret < 0) { + ctx_err(ctx, "Failed to register video device\n"); + return ret; + } + + ctx_info(ctx, "V4L2 device registered as %s\n", + video_device_node_name(vfd)); + + return 0; +} + +static void cal_ctx_v4l2_unregister(struct cal_ctx *ctx) +{ + ctx_dbg(1, ctx, "unregistering %s\n", + video_device_node_name(&ctx->vdev)); + + video_unregister_device(&ctx->vdev); +} + static int cal_ctx_v4l2_init(struct cal_ctx *ctx) { struct v4l2_ctrl_handler *hdl = &ctx->ctrl_handler; @@ -2147,19 +2151,10 @@ static int cal_async_notifier_complete(struct v4l2_async_notifier *notifier) { struct cal_dev *cal = container_of(notifier, struct cal_dev, notifier); unsigned int i; - int ret; for (i = 0; i < ARRAY_SIZE(cal->ctx); ++i) { - struct cal_ctx *ctx = cal->ctx[i]; - - if (!ctx) - continue; - - ret = cal_ctx_v4l2_init_formats(ctx); - if (ret) - continue; - - cal_ctx_v4l2_register(ctx); + if (cal->ctx[i]) + cal_ctx_v4l2_register(cal->ctx[i]); } return 0; -- GitLab From 0a8e64ed2100511454d652d03f5ec9098ded5c44 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 6 Jul 2020 20:36:36 +0200 Subject: [PATCH 0608/1476] media: ti-vpe: cal: Allocate cal_ctx active_fmt array dynamically To avoid making the cal_ctx structure layoug depend on the size of the cal_formats array, allocate the active_fmt array dynamically. This prepares for splitting the driver in multiple files. Signed-off-by: Laurent Pinchart Reviewed-by: Benoit Parrot Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/ti-vpe/cal.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/drivers/media/platform/ti-vpe/cal.c b/drivers/media/platform/ti-vpe/cal.c index 26747e6da3588..31878a32f6a15 100644 --- a/drivers/media/platform/ti-vpe/cal.c +++ b/drivers/media/platform/ti-vpe/cal.c @@ -325,7 +325,7 @@ struct cal_ctx { struct v4l2_mbus_framefmt m_fmt; /* Current subdev enumerated format */ - const struct cal_fmt *active_fmt[ARRAY_SIZE(cal_formats)]; + const struct cal_fmt **active_fmt; unsigned int num_active_fmt; unsigned int sequence; @@ -1957,7 +1957,13 @@ static int cal_ctx_v4l2_init_formats(struct cal_ctx *ctx) int ret = 0; /* Enumerate sub device formats and enable all matching local formats */ + ctx->active_fmt = devm_kcalloc(ctx->cal->dev, ARRAY_SIZE(cal_formats), + sizeof(*ctx->active_fmt), GFP_KERNEL); + if (!ctx->active_fmt) + return -ENOMEM; + ctx->num_active_fmt = 0; + for (j = 0, i = 0; ret != -EINVAL; ++j) { memset(&mbus_code, 0, sizeof(mbus_code)); -- GitLab From cd2144603ad3276cb0379f5f70d85cedcc68ecb4 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 6 Jul 2020 20:36:37 +0200 Subject: [PATCH 0609/1476] media: ti-vpe: cal: Inline cal_camerarx_max_lanes() in its only caller The cal_camerarx_max_lanes() function is a one-liner that has a single caller. It doesn't improve readability. Inline it in its caller. Signed-off-by: Laurent Pinchart Reviewed-by: Benoit Parrot Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/ti-vpe/cal.c | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/drivers/media/platform/ti-vpe/cal.c b/drivers/media/platform/ti-vpe/cal.c index 31878a32f6a15..4be1fa7e81972 100644 --- a/drivers/media/platform/ti-vpe/cal.c +++ b/drivers/media/platform/ti-vpe/cal.c @@ -481,19 +481,13 @@ static void cal_quickdump_regs(struct cal_dev *cal) * ------------------------------------------------------------------ */ -static u32 cal_camerarx_max_lanes(struct cal_camerarx *phy) -{ - return phy->cal->data->camerarx[phy->instance].num_lanes; -} - static void cal_camerarx_enable(struct cal_camerarx *phy) { - u32 max_lanes; + u32 num_lanes = phy->cal->data->camerarx[phy->instance].num_lanes; regmap_field_write(phy->fields[F_CAMMODE], 0); /* Always enable all lanes at the phy control level */ - max_lanes = (1 << cal_camerarx_max_lanes(phy)) - 1; - regmap_field_write(phy->fields[F_LANEENABLE], max_lanes); + regmap_field_write(phy->fields[F_LANEENABLE], (1 << num_lanes) - 1); /* F_CSI_MODE is not present on every architecture */ if (phy->fields[F_CSI_MODE]) regmap_field_write(phy->fields[F_CSI_MODE], 1); -- GitLab From e085ede10d690f25e21e47d248bc4ab2bfd5f7bf Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 6 Jul 2020 20:36:38 +0200 Subject: [PATCH 0610/1476] media: ti-vpe: cal: Reorder camerarx functions to prepare refactoring To prepare for the camerarx refactoring, reorder functions without any functional change to ease review of the refactoring itself. Signed-off-by: Laurent Pinchart Reviewed-by: Benoit Parrot Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/ti-vpe/cal.c | 428 ++++++++++++++-------------- 1 file changed, 214 insertions(+), 214 deletions(-) diff --git a/drivers/media/platform/ti-vpe/cal.c b/drivers/media/platform/ti-vpe/cal.c index 4be1fa7e81972..6b0ed2946eba8 100644 --- a/drivers/media/platform/ti-vpe/cal.c +++ b/drivers/media/platform/ti-vpe/cal.c @@ -481,6 +481,54 @@ static void cal_quickdump_regs(struct cal_dev *cal) * ------------------------------------------------------------------ */ +static int cal_camerarx_get_external_info(struct cal_camerarx *phy) +{ + struct v4l2_ctrl *ctrl; + + if (!phy->sensor) + return -ENODEV; + + ctrl = v4l2_ctrl_find(phy->sensor->ctrl_handler, V4L2_CID_PIXEL_RATE); + if (!ctrl) { + phy_err(phy, "no pixel rate control in subdev: %s\n", + phy->sensor->name); + return -EPIPE; + } + + phy->external_rate = v4l2_ctrl_g_ctrl_int64(ctrl); + phy_dbg(3, phy, "sensor Pixel Rate: %u\n", phy->external_rate); + + return 0; +} + +static void cal_camerarx_lane_config(struct cal_camerarx *phy) +{ + u32 val = reg_read(phy->cal, CAL_CSI2_COMPLEXIO_CFG(phy->instance)); + u32 lane_mask = CAL_CSI2_COMPLEXIO_CFG_CLOCK_POSITION_MASK; + u32 polarity_mask = CAL_CSI2_COMPLEXIO_CFG_CLOCK_POL_MASK; + struct v4l2_fwnode_bus_mipi_csi2 *mipi_csi2 = + &phy->endpoint.bus.mipi_csi2; + int lane; + + set_field(&val, mipi_csi2->clock_lane + 1, lane_mask); + set_field(&val, mipi_csi2->lane_polarities[0], polarity_mask); + for (lane = 0; lane < mipi_csi2->num_data_lanes; lane++) { + /* + * Every lane are one nibble apart starting with the + * clock followed by the data lanes so shift masks by 4. + */ + lane_mask <<= 4; + polarity_mask <<= 4; + set_field(&val, mipi_csi2->data_lanes[lane] + 1, lane_mask); + set_field(&val, mipi_csi2->lane_polarities[lane + 1], + polarity_mask); + } + + reg_write(phy->cal, CAL_CSI2_COMPLEXIO_CFG(phy->instance), val); + phy_dbg(3, phy, "CAL_CSI2_COMPLEXIO_CFG(%d) = 0x%08x\n", + phy->instance, val); +} + static void cal_camerarx_enable(struct cal_camerarx *phy) { u32 num_lanes = phy->cal->data->camerarx[phy->instance].num_lanes; @@ -500,88 +548,53 @@ static void cal_camerarx_disable(struct cal_camerarx *phy) } /* - * Errata i913: CSI2 LDO Needs to be disabled when module is powered on - * - * Enabling CSI2 LDO shorts it to core supply. It is crucial the 2 CSI2 - * LDOs on the device are disabled if CSI-2 module is powered on - * (0x4845 B304 | 0x4845 B384 [28:27] = 0x1) or in ULPS (0x4845 B304 - * | 0x4845 B384 [28:27] = 0x2) mode. Common concerns include: high - * current draw on the module supply in active mode. - * - * Errata does not apply when CSI-2 module is powered off - * (0x4845 B304 | 0x4845 B384 [28:27] = 0x0). - * - * SW Workaround: - * Set the following register bits to disable the LDO, - * which is essentially CSI2 REG10 bit 6: - * - * Core 0: 0x4845 B828 = 0x0000 0040 - * Core 1: 0x4845 B928 = 0x0000 0040 + * TCLK values are OK at their reset values */ -static void cal_camerarx_i913_errata(struct cal_camerarx *phy) -{ - u32 reg10 = reg_read(phy, CAL_CSI2_PHY_REG10); - - set_field(®10, 1, CAL_CSI2_PHY_REG10_I933_LDO_DISABLE_MASK); - - phy_dbg(1, phy, "CSI2_%d_REG10 = 0x%08x\n", phy->instance, reg10); - reg_write(phy, CAL_CSI2_PHY_REG10, reg10); -} +#define TCLK_TERM 0 +#define TCLK_MISS 1 +#define TCLK_SETTLE 14 -/* - * Enable the expected IRQ sources - */ -static void cal_camerarx_enable_irqs(struct cal_camerarx *phy) +static void cal_camerarx_config(struct cal_camerarx *phy, + const struct cal_fmt *fmt) { - u32 val; + unsigned int reg0, reg1; + unsigned int ths_term, ths_settle; + unsigned int csi2_ddrclk_khz; + struct v4l2_fwnode_bus_mipi_csi2 *mipi_csi2 = + &phy->endpoint.bus.mipi_csi2; + u32 num_lanes = mipi_csi2->num_data_lanes; - const u32 cio_err_mask = - CAL_CSI2_COMPLEXIO_IRQ_LANE_ERRORS_MASK | - CAL_CSI2_COMPLEXIO_IRQ_FIFO_OVR_MASK | - CAL_CSI2_COMPLEXIO_IRQ_SHORT_PACKET_MASK | - CAL_CSI2_COMPLEXIO_IRQ_ECC_NO_CORRECTION_MASK; + /* DPHY timing configuration */ + /* CSI-2 is DDR and we only count used lanes. */ + csi2_ddrclk_khz = phy->external_rate / 1000 + / (2 * num_lanes) * fmt->bpp; + phy_dbg(1, phy, "csi2_ddrclk_khz: %d\n", csi2_ddrclk_khz); - /* Enable CIO error irqs */ - reg_write(phy->cal, CAL_HL_IRQENABLE_SET(0), - CAL_HL_IRQ_CIO_MASK(phy->instance)); - reg_write(phy->cal, CAL_CSI2_COMPLEXIO_IRQENABLE(phy->instance), - cio_err_mask); + /* THS_TERM: Programmed value = floor(20 ns/DDRClk period) */ + ths_term = 20 * csi2_ddrclk_khz / 1000000; + phy_dbg(1, phy, "ths_term: %d (0x%02x)\n", ths_term, ths_term); - /* Always enable OCPO error */ - reg_write(phy->cal, CAL_HL_IRQENABLE_SET(0), CAL_HL_IRQ_OCPO_ERR_MASK); + /* THS_SETTLE: Programmed value = floor(105 ns/DDRClk period) + 4 */ + ths_settle = (105 * csi2_ddrclk_khz / 1000000) + 4; + phy_dbg(1, phy, "ths_settle: %d (0x%02x)\n", ths_settle, ths_settle); - /* Enable IRQ_WDMA_END 0/1 */ - val = 0; - set_field(&val, 1, CAL_HL_IRQ_MASK(phy->instance)); - reg_write(phy->cal, CAL_HL_IRQENABLE_SET(1), val); - /* Enable IRQ_WDMA_START 0/1 */ - val = 0; - set_field(&val, 1, CAL_HL_IRQ_MASK(phy->instance)); - reg_write(phy->cal, CAL_HL_IRQENABLE_SET(2), val); - /* Todo: Add VC_IRQ and CSI2_COMPLEXIO_IRQ handling */ - reg_write(phy->cal, CAL_CSI2_VC_IRQENABLE(0), 0xFF000000); -} + reg0 = reg_read(phy, CAL_CSI2_PHY_REG0); + set_field(®0, CAL_CSI2_PHY_REG0_HSCLOCKCONFIG_DISABLE, + CAL_CSI2_PHY_REG0_HSCLOCKCONFIG_MASK); + set_field(®0, ths_term, CAL_CSI2_PHY_REG0_THS_TERM_MASK); + set_field(®0, ths_settle, CAL_CSI2_PHY_REG0_THS_SETTLE_MASK); -static void cal_camerarx_disable_irqs(struct cal_camerarx *phy) -{ - u32 val; + phy_dbg(1, phy, "CSI2_%d_REG0 = 0x%08x\n", phy->instance, reg0); + reg_write(phy, CAL_CSI2_PHY_REG0, reg0); - /* Disable CIO error irqs */ - reg_write(phy->cal, CAL_HL_IRQENABLE_CLR(0), - CAL_HL_IRQ_CIO_MASK(phy->instance)); - reg_write(phy->cal, CAL_CSI2_COMPLEXIO_IRQENABLE(phy->instance), - 0); + reg1 = reg_read(phy, CAL_CSI2_PHY_REG1); + set_field(®1, TCLK_TERM, CAL_CSI2_PHY_REG1_TCLK_TERM_MASK); + set_field(®1, 0xb8, CAL_CSI2_PHY_REG1_DPHY_HS_SYNC_PATTERN_MASK); + set_field(®1, TCLK_MISS, CAL_CSI2_PHY_REG1_CTRLCLK_DIV_FACTOR_MASK); + set_field(®1, TCLK_SETTLE, CAL_CSI2_PHY_REG1_TCLK_SETTLE_MASK); - /* Disable IRQ_WDMA_END 0/1 */ - val = 0; - set_field(&val, 1, CAL_HL_IRQ_MASK(phy->instance)); - reg_write(phy->cal, CAL_HL_IRQENABLE_CLR(1), val); - /* Disable IRQ_WDMA_START 0/1 */ - val = 0; - set_field(&val, 1, CAL_HL_IRQ_MASK(phy->instance)); - reg_write(phy->cal, CAL_HL_IRQENABLE_CLR(2), val); - /* Todo: Add VC_IRQ and CSI2_COMPLEXIO_IRQ handling */ - reg_write(phy->cal, CAL_CSI2_VC_IRQENABLE(0), 0); + phy_dbg(1, phy, "CSI2_%d_REG1 = 0x%08x\n", phy->instance, reg1); + reg_write(phy, CAL_CSI2_PHY_REG1, reg1); } static void cal_camerarx_power(struct cal_camerarx *phy, bool enable) @@ -613,54 +626,63 @@ static void cal_camerarx_power(struct cal_camerarx *phy, bool enable) enable ? "up" : "down"); } -/* - * TCLK values are OK at their reset values - */ -#define TCLK_TERM 0 -#define TCLK_MISS 1 -#define TCLK_SETTLE 14 - -static void cal_camerarx_config(struct cal_camerarx *phy, - const struct cal_fmt *fmt) +static void cal_camerarx_wait_reset(struct cal_camerarx *phy) { - unsigned int reg0, reg1; - unsigned int ths_term, ths_settle; - unsigned int csi2_ddrclk_khz; - struct v4l2_fwnode_bus_mipi_csi2 *mipi_csi2 = - &phy->endpoint.bus.mipi_csi2; - u32 num_lanes = mipi_csi2->num_data_lanes; + unsigned long timeout; - /* DPHY timing configuration */ - /* CSI-2 is DDR and we only count used lanes. */ - csi2_ddrclk_khz = phy->external_rate / 1000 - / (2 * num_lanes) * fmt->bpp; - phy_dbg(1, phy, "csi2_ddrclk_khz: %d\n", csi2_ddrclk_khz); + timeout = jiffies + msecs_to_jiffies(750); + while (time_before(jiffies, timeout)) { + if (reg_read_field(phy->cal, + CAL_CSI2_COMPLEXIO_CFG(phy->instance), + CAL_CSI2_COMPLEXIO_CFG_RESET_DONE_MASK) == + CAL_CSI2_COMPLEXIO_CFG_RESET_DONE_RESETCOMPLETED) + break; + usleep_range(500, 5000); + } - /* THS_TERM: Programmed value = floor(20 ns/DDRClk period) */ - ths_term = 20 * csi2_ddrclk_khz / 1000000; - phy_dbg(1, phy, "ths_term: %d (0x%02x)\n", ths_term, ths_term); + if (reg_read_field(phy->cal, CAL_CSI2_COMPLEXIO_CFG(phy->instance), + CAL_CSI2_COMPLEXIO_CFG_RESET_DONE_MASK) != + CAL_CSI2_COMPLEXIO_CFG_RESET_DONE_RESETCOMPLETED) + phy_err(phy, "Timeout waiting for Complex IO reset done\n"); +} - /* THS_SETTLE: Programmed value = floor(105 ns/DDRClk period) + 4 */ - ths_settle = (105 * csi2_ddrclk_khz / 1000000) + 4; - phy_dbg(1, phy, "ths_settle: %d (0x%02x)\n", ths_settle, ths_settle); +static void cal_camerarx_wait_stop_state(struct cal_camerarx *phy) +{ + unsigned long timeout; - reg0 = reg_read(phy, CAL_CSI2_PHY_REG0); - set_field(®0, CAL_CSI2_PHY_REG0_HSCLOCKCONFIG_DISABLE, - CAL_CSI2_PHY_REG0_HSCLOCKCONFIG_MASK); - set_field(®0, ths_term, CAL_CSI2_PHY_REG0_THS_TERM_MASK); - set_field(®0, ths_settle, CAL_CSI2_PHY_REG0_THS_SETTLE_MASK); + timeout = jiffies + msecs_to_jiffies(750); + while (time_before(jiffies, timeout)) { + if (reg_read_field(phy->cal, + CAL_CSI2_TIMING(phy->instance), + CAL_CSI2_TIMING_FORCE_RX_MODE_IO1_MASK) == 0) + break; + usleep_range(500, 5000); + } - phy_dbg(1, phy, "CSI2_%d_REG0 = 0x%08x\n", phy->instance, reg0); - reg_write(phy, CAL_CSI2_PHY_REG0, reg0); + if (reg_read_field(phy->cal, CAL_CSI2_TIMING(phy->instance), + CAL_CSI2_TIMING_FORCE_RX_MODE_IO1_MASK) != 0) + phy_err(phy, "Timeout waiting for stop state\n"); +} - reg1 = reg_read(phy, CAL_CSI2_PHY_REG1); - set_field(®1, TCLK_TERM, CAL_CSI2_PHY_REG1_TCLK_TERM_MASK); - set_field(®1, 0xb8, CAL_CSI2_PHY_REG1_DPHY_HS_SYNC_PATTERN_MASK); - set_field(®1, TCLK_MISS, CAL_CSI2_PHY_REG1_CTRLCLK_DIV_FACTOR_MASK); - set_field(®1, TCLK_SETTLE, CAL_CSI2_PHY_REG1_TCLK_SETTLE_MASK); +static void cal_camerarx_wait_ready(struct cal_camerarx *phy) +{ + /* Steps + * 2. Wait for completion of reset + * Note if the external sensor is not sending byte clock, + * the reset will timeout + * 4.Force FORCERXMODE + * G. Wait for all enabled lane to reach stop state + * H. Disable pull down using pad control + */ - phy_dbg(1, phy, "CSI2_%d_REG1 = 0x%08x\n", phy->instance, reg1); - reg_write(phy, CAL_CSI2_PHY_REG1, reg1); + /* 2. Wait for reset completion */ + cal_camerarx_wait_reset(phy); + + /* 4. G. Wait for all enabled lane to reach stop state */ + cal_camerarx_wait_stop_state(phy); + + phy_dbg(1, phy, "CSI2_%d_REG1 = 0x%08x (Bit(31,28) should be set!)\n", + phy->instance, reg_read(phy, CAL_CSI2_PHY_REG1)); } static void cal_camerarx_init(struct cal_camerarx *phy, @@ -734,65 +756,6 @@ static void cal_camerarx_init(struct cal_camerarx *phy, cal_camerarx_power(phy, true); } -static void cal_camerarx_wait_reset(struct cal_camerarx *phy) -{ - unsigned long timeout; - - timeout = jiffies + msecs_to_jiffies(750); - while (time_before(jiffies, timeout)) { - if (reg_read_field(phy->cal, - CAL_CSI2_COMPLEXIO_CFG(phy->instance), - CAL_CSI2_COMPLEXIO_CFG_RESET_DONE_MASK) == - CAL_CSI2_COMPLEXIO_CFG_RESET_DONE_RESETCOMPLETED) - break; - usleep_range(500, 5000); - } - - if (reg_read_field(phy->cal, CAL_CSI2_COMPLEXIO_CFG(phy->instance), - CAL_CSI2_COMPLEXIO_CFG_RESET_DONE_MASK) != - CAL_CSI2_COMPLEXIO_CFG_RESET_DONE_RESETCOMPLETED) - phy_err(phy, "Timeout waiting for Complex IO reset done\n"); -} - -static void cal_camerarx_wait_stop_state(struct cal_camerarx *phy) -{ - unsigned long timeout; - - timeout = jiffies + msecs_to_jiffies(750); - while (time_before(jiffies, timeout)) { - if (reg_read_field(phy->cal, - CAL_CSI2_TIMING(phy->instance), - CAL_CSI2_TIMING_FORCE_RX_MODE_IO1_MASK) == 0) - break; - usleep_range(500, 5000); - } - - if (reg_read_field(phy->cal, CAL_CSI2_TIMING(phy->instance), - CAL_CSI2_TIMING_FORCE_RX_MODE_IO1_MASK) != 0) - phy_err(phy, "Timeout waiting for stop state\n"); -} - -static void cal_camerarx_wait_ready(struct cal_camerarx *phy) -{ - /* Steps - * 2. Wait for completion of reset - * Note if the external sensor is not sending byte clock, - * the reset will timeout - * 4.Force FORCERXMODE - * G. Wait for all enabled lane to reach stop state - * H. Disable pull down using pad control - */ - - /* 2. Wait for reset completion */ - cal_camerarx_wait_reset(phy); - - /* 4. G. Wait for all enabled lane to reach stop state */ - cal_camerarx_wait_stop_state(phy); - - phy_dbg(1, phy, "CSI2_%d_REG1 = 0x%08x (Bit(31,28) should be set!)\n", - phy->instance, reg_read(phy, CAL_CSI2_PHY_REG1)); -} - static void cal_camerarx_deinit(struct cal_camerarx *phy) { unsigned int i; @@ -822,32 +785,89 @@ static void cal_camerarx_deinit(struct cal_camerarx *phy) cal_camerarx_disable(phy); } -static void cal_camerarx_lane_config(struct cal_camerarx *phy) +/* + * Errata i913: CSI2 LDO Needs to be disabled when module is powered on + * + * Enabling CSI2 LDO shorts it to core supply. It is crucial the 2 CSI2 + * LDOs on the device are disabled if CSI-2 module is powered on + * (0x4845 B304 | 0x4845 B384 [28:27] = 0x1) or in ULPS (0x4845 B304 + * | 0x4845 B384 [28:27] = 0x2) mode. Common concerns include: high + * current draw on the module supply in active mode. + * + * Errata does not apply when CSI-2 module is powered off + * (0x4845 B304 | 0x4845 B384 [28:27] = 0x0). + * + * SW Workaround: + * Set the following register bits to disable the LDO, + * which is essentially CSI2 REG10 bit 6: + * + * Core 0: 0x4845 B828 = 0x0000 0040 + * Core 1: 0x4845 B928 = 0x0000 0040 + */ +static void cal_camerarx_i913_errata(struct cal_camerarx *phy) { - u32 val = reg_read(phy->cal, CAL_CSI2_COMPLEXIO_CFG(phy->instance)); - u32 lane_mask = CAL_CSI2_COMPLEXIO_CFG_CLOCK_POSITION_MASK; - u32 polarity_mask = CAL_CSI2_COMPLEXIO_CFG_CLOCK_POL_MASK; - struct v4l2_fwnode_bus_mipi_csi2 *mipi_csi2 = - &phy->endpoint.bus.mipi_csi2; - int lane; + u32 reg10 = reg_read(phy, CAL_CSI2_PHY_REG10); - set_field(&val, mipi_csi2->clock_lane + 1, lane_mask); - set_field(&val, mipi_csi2->lane_polarities[0], polarity_mask); - for (lane = 0; lane < mipi_csi2->num_data_lanes; lane++) { - /* - * Every lane are one nibble apart starting with the - * clock followed by the data lanes so shift masks by 4. - */ - lane_mask <<= 4; - polarity_mask <<= 4; - set_field(&val, mipi_csi2->data_lanes[lane] + 1, lane_mask); - set_field(&val, mipi_csi2->lane_polarities[lane + 1], - polarity_mask); - } + set_field(®10, 1, CAL_CSI2_PHY_REG10_I933_LDO_DISABLE_MASK); - reg_write(phy->cal, CAL_CSI2_COMPLEXIO_CFG(phy->instance), val); - phy_dbg(3, phy, "CAL_CSI2_COMPLEXIO_CFG(%d) = 0x%08x\n", - phy->instance, val); + phy_dbg(1, phy, "CSI2_%d_REG10 = 0x%08x\n", phy->instance, reg10); + reg_write(phy, CAL_CSI2_PHY_REG10, reg10); +} + +/* + * Enable the expected IRQ sources + */ +static void cal_camerarx_enable_irqs(struct cal_camerarx *phy) +{ + u32 val; + + const u32 cio_err_mask = + CAL_CSI2_COMPLEXIO_IRQ_LANE_ERRORS_MASK | + CAL_CSI2_COMPLEXIO_IRQ_FIFO_OVR_MASK | + CAL_CSI2_COMPLEXIO_IRQ_SHORT_PACKET_MASK | + CAL_CSI2_COMPLEXIO_IRQ_ECC_NO_CORRECTION_MASK; + + /* Enable CIO error irqs */ + reg_write(phy->cal, CAL_HL_IRQENABLE_SET(0), + CAL_HL_IRQ_CIO_MASK(phy->instance)); + reg_write(phy->cal, CAL_CSI2_COMPLEXIO_IRQENABLE(phy->instance), + cio_err_mask); + + /* Always enable OCPO error */ + reg_write(phy->cal, CAL_HL_IRQENABLE_SET(0), CAL_HL_IRQ_OCPO_ERR_MASK); + + /* Enable IRQ_WDMA_END 0/1 */ + val = 0; + set_field(&val, 1, CAL_HL_IRQ_MASK(phy->instance)); + reg_write(phy->cal, CAL_HL_IRQENABLE_SET(1), val); + /* Enable IRQ_WDMA_START 0/1 */ + val = 0; + set_field(&val, 1, CAL_HL_IRQ_MASK(phy->instance)); + reg_write(phy->cal, CAL_HL_IRQENABLE_SET(2), val); + /* Todo: Add VC_IRQ and CSI2_COMPLEXIO_IRQ handling */ + reg_write(phy->cal, CAL_CSI2_VC_IRQENABLE(0), 0xFF000000); +} + +static void cal_camerarx_disable_irqs(struct cal_camerarx *phy) +{ + u32 val; + + /* Disable CIO error irqs */ + reg_write(phy->cal, CAL_HL_IRQENABLE_CLR(0), + CAL_HL_IRQ_CIO_MASK(phy->instance)); + reg_write(phy->cal, CAL_CSI2_COMPLEXIO_IRQENABLE(phy->instance), + 0); + + /* Disable IRQ_WDMA_END 0/1 */ + val = 0; + set_field(&val, 1, CAL_HL_IRQ_MASK(phy->instance)); + reg_write(phy->cal, CAL_HL_IRQENABLE_CLR(1), val); + /* Disable IRQ_WDMA_START 0/1 */ + val = 0; + set_field(&val, 1, CAL_HL_IRQ_MASK(phy->instance)); + reg_write(phy->cal, CAL_HL_IRQENABLE_CLR(2), val); + /* Todo: Add VC_IRQ and CSI2_COMPLEXIO_IRQ handling */ + reg_write(phy->cal, CAL_CSI2_VC_IRQENABLE(0), 0); } static void cal_camerarx_ppi_enable(struct cal_camerarx *phy) @@ -863,26 +883,6 @@ static void cal_camerarx_ppi_disable(struct cal_camerarx *phy) 0, CAL_CSI2_PPI_CTRL_IF_EN_MASK); } -static int cal_camerarx_get_external_info(struct cal_camerarx *phy) -{ - struct v4l2_ctrl *ctrl; - - if (!phy->sensor) - return -ENODEV; - - ctrl = v4l2_ctrl_find(phy->sensor->ctrl_handler, V4L2_CID_PIXEL_RATE); - if (!ctrl) { - phy_err(phy, "no pixel rate control in subdev: %s\n", - phy->sensor->name); - return -EPIPE; - } - - phy->external_rate = v4l2_ctrl_g_ctrl_int64(ctrl); - phy_dbg(3, phy, "sensor Pixel Rate: %u\n", phy->external_rate); - - return 0; -} - static int cal_camerarx_regmap_init(struct cal_dev *cal, struct cal_camerarx *phy) { -- GitLab From 49b184a0dae93b7eaa3dbd09c0287db9a471c9da Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 6 Jul 2020 20:36:39 +0200 Subject: [PATCH 0611/1476] media: ti-vpe: cal: Refactor camerarx start and stop Refactor the camerarx start and stop procedure to group all the corresponding operations in two functions, cal_camerarx_start() and cal_camerarx_stop() instead of splitting them in multiple steps called from the vb2 stream start and stop functions. This reduces the coupling between the camerarx and the contexts, and better models the camerarx hardware. The cal_camerarx_start() is a reworked version of cal_camerarx_init() with the description of the start procedure updated to match the AM65x, DRA7[124567]x and DRA80xM TRMs. The cal_camerarx_wait_ready() function is inlined in cal_camerarx_start() to better describe the start procedure. No functional change is included in the camerarx start and stop procedures themselves, but the interleaving of the start steps with the context configuration has been changed in cal_start_streaming(). Signed-off-by: Laurent Pinchart Reviewed-by: Benoit Parrot Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/ti-vpe/cal.c | 170 +++++++++++++++------------- 1 file changed, 94 insertions(+), 76 deletions(-) diff --git a/drivers/media/platform/ti-vpe/cal.c b/drivers/media/platform/ti-vpe/cal.c index 6b0ed2946eba8..50fe6c19c4172 100644 --- a/drivers/media/platform/ti-vpe/cal.c +++ b/drivers/media/platform/ti-vpe/cal.c @@ -664,54 +664,50 @@ static void cal_camerarx_wait_stop_state(struct cal_camerarx *phy) phy_err(phy, "Timeout waiting for stop state\n"); } -static void cal_camerarx_wait_ready(struct cal_camerarx *phy) -{ - /* Steps - * 2. Wait for completion of reset - * Note if the external sensor is not sending byte clock, - * the reset will timeout - * 4.Force FORCERXMODE - * G. Wait for all enabled lane to reach stop state - * H. Disable pull down using pad control - */ - - /* 2. Wait for reset completion */ - cal_camerarx_wait_reset(phy); - - /* 4. G. Wait for all enabled lane to reach stop state */ - cal_camerarx_wait_stop_state(phy); - - phy_dbg(1, phy, "CSI2_%d_REG1 = 0x%08x (Bit(31,28) should be set!)\n", - phy->instance, reg_read(phy, CAL_CSI2_PHY_REG1)); -} - -static void cal_camerarx_init(struct cal_camerarx *phy, +static int cal_camerarx_start(struct cal_camerarx *phy, const struct cal_fmt *fmt) { - u32 val; u32 sscounter; + u32 val; + int ret; + + ret = cal_camerarx_get_external_info(phy); + if (ret < 0) + return ret; + + ret = v4l2_subdev_call(phy->sensor, core, s_power, 1); + if (ret < 0 && ret != -ENOIOCTLCMD && ret != -ENODEV) { + phy_err(phy, "power on failed in subdev\n"); + return ret; + } - /* Steps - * 1. Configure D-PHY mode and enable required lanes - * 2. Reset complex IO - Wait for completion of reset - * Note if the external sensor is not sending byte clock, - * the reset will timeout - * 3 Program Stop States - * A. Program THS_TERM, THS_SETTLE, etc... Timings parameters - * in terms of DDR clock periods - * B. Enable stop state transition timeouts - * 4.Force FORCERXMODE - * D. Enable pull down using pad control - * E. Power up PHY - * F. Wait for power up completion - * G. Wait for all enabled lane to reach stop state - * H. Disable pull down using pad control + /* + * CSI-2 PHY Link Initialization Sequence, according to the DRA74xP / + * DRA75xP / DRA76xP / DRA77xP TRM. The DRA71x / DRA72x and the AM65x / + * DRA80xM TRMs have a a slightly simplified sequence. */ - /* 1. Configure D-PHY mode and enable required lanes */ + /* + * 1. Configure all CSI-2 low level protocol registers to be ready to + * receive signals/data from the CSI-2 PHY. + * + * i.-v. Configure the lanes position and polarity. + */ + cal_camerarx_lane_config(phy); + + /* + * vi.-vii. Configure D-PHY mode, enable the required lanes and + * enable the CAMERARX clock. + */ cal_camerarx_enable(phy); - /* 2. Reset complex IO - Do not wait for reset completion */ + /* + * 2. CSI PHY and link initialization sequence. + * + * a. Deassert the CSI-2 PHY reset. Do not wait for reset completion + * at this point, as it requires the external sensor to send the + * CSI-2 HS clock. + */ reg_write_field(phy->cal, CAL_CSI2_COMPLEXIO_CFG(phy->instance), CAL_CSI2_COMPLEXIO_CFG_RESET_CTRL_OPERATIONAL, CAL_CSI2_COMPLEXIO_CFG_RESET_CTRL_MASK); @@ -719,19 +715,20 @@ static void cal_camerarx_init(struct cal_camerarx *phy, phy->instance, reg_read(phy->cal, CAL_CSI2_COMPLEXIO_CFG(phy->instance))); - /* Dummy read to allow SCP reset to complete */ + /* Dummy read to allow SCP reset to complete. */ reg_read(phy, CAL_CSI2_PHY_REG0); - /* 3.A. Program Phy Timing Parameters */ + /* Program the PHY timing parameters. */ cal_camerarx_config(phy, fmt); - /* 3.B. Program Stop States */ /* + * b. Assert the FORCERXMODE signal. + * * The stop-state-counter is based on fclk cycles, and we always use * the x16 and x4 settings, so stop-state-timeout = * fclk-cycle * 16 * 4 * counter. * - * Stop-state-timeout must be more than 100us as per CSI2 spec, so we + * Stop-state-timeout must be more than 100us as per CSI-2 spec, so we * calculate a timeout that's 100us (rounding up). */ sscounter = DIV_ROUND_UP(clk_get_rate(phy->cal->fclk), 10000 * 16 * 4); @@ -745,24 +742,63 @@ static void cal_camerarx_init(struct cal_camerarx *phy, phy->instance, reg_read(phy->cal, CAL_CSI2_TIMING(phy->instance))); - /* 4. Force FORCERXMODE */ + /* Assert the FORCERXMODE signal. */ reg_write_field(phy->cal, CAL_CSI2_TIMING(phy->instance), 1, CAL_CSI2_TIMING_FORCE_RX_MODE_IO1_MASK); phy_dbg(3, phy, "CAL_CSI2_TIMING(%d) = 0x%08x Force RXMODE\n", phy->instance, reg_read(phy->cal, CAL_CSI2_TIMING(phy->instance))); - /* E. Power up the PHY using the complex IO */ + /* + * c. Connect pull-down on CSI-2 PHY link (using pad control). + * + * This is not required on DRA71x, DRA72x, AM65x and DRA80xM. Not + * implemented. + */ + + /* + * d. Power up the CSI-2 PHY. + * e. Check whether the state status reaches the ON state. + */ cal_camerarx_power(phy, true); + + /* + * Start the sensor to enable the CSI-2 HS clock. We can now wait for + * CSI-2 PHY reset to complete. + */ + ret = v4l2_subdev_call(phy->sensor, video, s_stream, 1); + if (ret) { + v4l2_subdev_call(phy->sensor, core, s_power, 0); + phy_err(phy, "stream on failed in subdev\n"); + return ret; + } + + cal_camerarx_wait_reset(phy); + + /* f. Wait for STOPSTATE=1 for all enabled lane modules. */ + cal_camerarx_wait_stop_state(phy); + + phy_dbg(1, phy, "CSI2_%u_REG1 = 0x%08x (bits 31-28 should be set)\n", + phy->instance, reg_read(phy, CAL_CSI2_PHY_REG1)); + + /* + * g. Disable pull-down on CSI-2 PHY link (using pad control). + * + * This is not required on DRA71x, DRA72x, AM65x and DRA80xM. Not + * implemented. + */ + + return 0; } -static void cal_camerarx_deinit(struct cal_camerarx *phy) +static void cal_camerarx_stop(struct cal_camerarx *phy) { unsigned int i; + int ret; cal_camerarx_power(phy, false); - /* Assert Comple IO Reset */ + /* Assert Complex IO Reset */ reg_write_field(phy->cal, CAL_CSI2_COMPLEXIO_CFG(phy->instance), CAL_CSI2_COMPLEXIO_CFG_RESET_CTRL, CAL_CSI2_COMPLEXIO_CFG_RESET_CTRL_MASK); @@ -783,6 +819,13 @@ static void cal_camerarx_deinit(struct cal_camerarx *phy) /* Disable the phy */ cal_camerarx_disable(phy); + + if (v4l2_subdev_call(phy->sensor, video, s_stream, 0)) + phy_err(phy, "stream off failed in subdev\n"); + + ret = v4l2_subdev_call(phy->sensor, core, s_power, 0); + if (ret < 0 && ret != -ENOIOCTLCMD && ret != -ENODEV) + phy_err(phy, "power off failed in subdev\n"); } /* @@ -1809,36 +1852,19 @@ static int cal_start_streaming(struct vb2_queue *vq, unsigned int count) addr = vb2_dma_contig_plane_dma_addr(&ctx->cur_frm->vb.vb2_buf, 0); ctx->sequence = 0; - ret = cal_camerarx_get_external_info(ctx->phy); - if (ret < 0) - goto err; - - ret = v4l2_subdev_call(ctx->phy->sensor, core, s_power, 1); - if (ret < 0 && ret != -ENOIOCTLCMD && ret != -ENODEV) { - ctx_err(ctx, "power on failed in subdev\n"); - goto err; - } - pm_runtime_get_sync(ctx->cal->dev); cal_ctx_csi2_config(ctx); cal_ctx_pix_proc_config(ctx); cal_ctx_wr_dma_config(ctx, ctx->v_fmt.fmt.pix.bytesperline, ctx->v_fmt.fmt.pix.height); - cal_camerarx_lane_config(ctx->phy); cal_camerarx_enable_irqs(ctx->phy); - cal_camerarx_init(ctx->phy, ctx->fmt); - ret = v4l2_subdev_call(ctx->phy->sensor, video, s_stream, 1); - if (ret) { - v4l2_subdev_call(ctx->phy->sensor, core, s_power, 0); - ctx_err(ctx, "stream on failed in subdev\n"); - pm_runtime_put_sync(ctx->cal->dev); + ret = cal_camerarx_start(ctx->phy, ctx->fmt); + if (ret) goto err; - } - cal_camerarx_wait_ready(ctx->phy); cal_ctx_wr_dma_addr(ctx, addr); cal_camerarx_ppi_enable(ctx->phy); @@ -1867,7 +1893,6 @@ static void cal_stop_streaming(struct vb2_queue *vq) struct cal_buffer *buf, *tmp; unsigned long timeout; unsigned long flags; - int ret; bool dma_act; cal_camerarx_ppi_disable(ctx->phy); @@ -1887,14 +1912,7 @@ static void cal_stop_streaming(struct vb2_queue *vq) ctx_err(ctx, "failed to disable dma cleanly\n"); cal_camerarx_disable_irqs(ctx->phy); - cal_camerarx_deinit(ctx->phy); - - if (v4l2_subdev_call(ctx->phy->sensor, video, s_stream, 0)) - ctx_err(ctx, "stream off failed in subdev\n"); - - ret = v4l2_subdev_call(ctx->phy->sensor, core, s_power, 0); - if (ret < 0 && ret != -ENOIOCTLCMD && ret != -ENODEV) - ctx_err(ctx, "power off failed in subdev\n"); + cal_camerarx_stop(ctx->phy); /* Release all active buffers */ spin_lock_irqsave(&ctx->slock, flags); -- GitLab From 399b0a3d54b695def41a77a05ad8221c0e9ffc29 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 6 Jul 2020 20:36:40 +0200 Subject: [PATCH 0612/1476] media: ti-vpe: cal: Don't store external rate in cal_camerarx The external pixel rate is retrieved when starting the camerarx and only used then. There's no need to store it in the cal_camerarx structure, it can be returned by cal_camerarx_get_external_info() and explicitly passed to cal_camerarx_config(). While at it, rename cal_camerarx_get_external_info() to cal_camerarx_get_external_rate() to better reflect the function's purpose. Signed-off-by: Laurent Pinchart Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/ti-vpe/cal.c | 35 +++++++++++++++++------------ 1 file changed, 21 insertions(+), 14 deletions(-) diff --git a/drivers/media/platform/ti-vpe/cal.c b/drivers/media/platform/ti-vpe/cal.c index 50fe6c19c4172..8b1815bbf1a76 100644 --- a/drivers/media/platform/ti-vpe/cal.c +++ b/drivers/media/platform/ti-vpe/cal.c @@ -272,7 +272,6 @@ struct cal_camerarx { struct v4l2_fwnode_endpoint endpoint; struct device_node *sensor_node; struct v4l2_subdev *sensor; - unsigned int external_rate; }; struct cal_dev { @@ -481,9 +480,10 @@ static void cal_quickdump_regs(struct cal_dev *cal) * ------------------------------------------------------------------ */ -static int cal_camerarx_get_external_info(struct cal_camerarx *phy) +static s64 cal_camerarx_get_external_rate(struct cal_camerarx *phy) { struct v4l2_ctrl *ctrl; + s64 rate; if (!phy->sensor) return -ENODEV; @@ -495,10 +495,10 @@ static int cal_camerarx_get_external_info(struct cal_camerarx *phy) return -EPIPE; } - phy->external_rate = v4l2_ctrl_g_ctrl_int64(ctrl); - phy_dbg(3, phy, "sensor Pixel Rate: %u\n", phy->external_rate); + rate = v4l2_ctrl_g_ctrl_int64(ctrl); + phy_dbg(3, phy, "sensor Pixel Rate: %llu\n", rate); - return 0; + return rate; } static void cal_camerarx_lane_config(struct cal_camerarx *phy) @@ -554,7 +554,7 @@ static void cal_camerarx_disable(struct cal_camerarx *phy) #define TCLK_MISS 1 #define TCLK_SETTLE 14 -static void cal_camerarx_config(struct cal_camerarx *phy, +static void cal_camerarx_config(struct cal_camerarx *phy, s64 external_rate, const struct cal_fmt *fmt) { unsigned int reg0, reg1; @@ -565,9 +565,16 @@ static void cal_camerarx_config(struct cal_camerarx *phy, u32 num_lanes = mipi_csi2->num_data_lanes; /* DPHY timing configuration */ - /* CSI-2 is DDR and we only count used lanes. */ - csi2_ddrclk_khz = phy->external_rate / 1000 - / (2 * num_lanes) * fmt->bpp; + + /* + * CSI-2 is DDR and we only count used lanes. + * + * csi2_ddrclk_khz = external_rate / 1000 + * / (2 * num_lanes) * fmt->bpp; + */ + csi2_ddrclk_khz = div_s64(external_rate * fmt->bpp, + 2 * num_lanes * 1000); + phy_dbg(1, phy, "csi2_ddrclk_khz: %d\n", csi2_ddrclk_khz); /* THS_TERM: Programmed value = floor(20 ns/DDRClk period) */ @@ -667,13 +674,14 @@ static void cal_camerarx_wait_stop_state(struct cal_camerarx *phy) static int cal_camerarx_start(struct cal_camerarx *phy, const struct cal_fmt *fmt) { + s64 external_rate; u32 sscounter; u32 val; int ret; - ret = cal_camerarx_get_external_info(phy); - if (ret < 0) - return ret; + external_rate = cal_camerarx_get_external_rate(phy); + if (external_rate < 0) + return external_rate; ret = v4l2_subdev_call(phy->sensor, core, s_power, 1); if (ret < 0 && ret != -ENOIOCTLCMD && ret != -ENODEV) { @@ -719,7 +727,7 @@ static int cal_camerarx_start(struct cal_camerarx *phy, reg_read(phy, CAL_CSI2_PHY_REG0); /* Program the PHY timing parameters. */ - cal_camerarx_config(phy, fmt); + cal_camerarx_config(phy, external_rate, fmt); /* * b. Assert the FORCERXMODE signal. @@ -1039,7 +1047,6 @@ static struct cal_camerarx *cal_camerarx_create(struct cal_dev *cal, phy->cal = cal; phy->instance = instance; - phy->external_rate = 192000000; phy->res = platform_get_resource_byname(pdev, IORESOURCE_MEM, (instance == 0) ? -- GitLab From 56d1d67468ae195613c374e1e42fac85d22f24e5 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 6 Jul 2020 20:36:41 +0200 Subject: [PATCH 0613/1476] media: ti-vpe: cal: Remove unneeded phy->sensor NULL check The phy->sensor NULL check in cal_camerarx_get_external_rate() is not needed, as the V4L2 video devices are only registered when the sensor is bound. Remove it. Signed-off-by: Laurent Pinchart Reviewed-by: Benoit Parrot Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/ti-vpe/cal.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/media/platform/ti-vpe/cal.c b/drivers/media/platform/ti-vpe/cal.c index 8b1815bbf1a76..ade76739de47f 100644 --- a/drivers/media/platform/ti-vpe/cal.c +++ b/drivers/media/platform/ti-vpe/cal.c @@ -485,9 +485,6 @@ static s64 cal_camerarx_get_external_rate(struct cal_camerarx *phy) struct v4l2_ctrl *ctrl; s64 rate; - if (!phy->sensor) - return -ENODEV; - ctrl = v4l2_ctrl_find(phy->sensor->ctrl_handler, V4L2_CID_PIXEL_RATE); if (!ctrl) { phy_err(phy, "no pixel rate control in subdev: %s\n", -- GitLab From f4d9837d872523e8ef8bd1e0156eb90e2fed8dc3 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 6 Jul 2020 20:36:42 +0200 Subject: [PATCH 0614/1476] media: ti-vpe: cal: Use 'unsigned int' type instead of 'unsigned' Specifying 'int' explicitly is generally preferred in the kernel for unsigned int types. Fix the only wrong occurrence. Signed-off-by: Laurent Pinchart Reviewed-by: Benoit Parrot Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/ti-vpe/cal.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/platform/ti-vpe/cal.c b/drivers/media/platform/ti-vpe/cal.c index ade76739de47f..fdb40c85de9b8 100644 --- a/drivers/media/platform/ti-vpe/cal.c +++ b/drivers/media/platform/ti-vpe/cal.c @@ -1776,7 +1776,7 @@ static int cal_queue_setup(struct vb2_queue *vq, unsigned int sizes[], struct device *alloc_devs[]) { struct cal_ctx *ctx = vb2_get_drv_priv(vq); - unsigned size = ctx->v_fmt.fmt.pix.sizeimage; + unsigned int size = ctx->v_fmt.fmt.pix.sizeimage; if (vq->num_buffers + *nbuffers < 3) *nbuffers = 3 - vq->num_buffers; -- GitLab From 9d551891473e35eae4ea3b21cac30136f455d405 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 6 Jul 2020 20:36:43 +0200 Subject: [PATCH 0615/1476] media: ti-vpe: cal: Split video node handling to cal-video.c To prepare for implementation of media controller centric device configuration, move all the video node handling support to a separate file. Signed-off-by: Laurent Pinchart Reviewed-by: Benoit Parrot Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/ti-vpe/Makefile | 2 +- drivers/media/platform/ti-vpe/cal-video.c | 887 +++++++++++++++++ drivers/media/platform/ti-vpe/cal.c | 1085 +-------------------- drivers/media/platform/ti-vpe/cal.h | 226 +++++ 4 files changed, 1137 insertions(+), 1063 deletions(-) create mode 100644 drivers/media/platform/ti-vpe/cal-video.c create mode 100644 drivers/media/platform/ti-vpe/cal.h diff --git a/drivers/media/platform/ti-vpe/Makefile b/drivers/media/platform/ti-vpe/Makefile index 886ac5ec073f0..a7bf8f2a13862 100644 --- a/drivers/media/platform/ti-vpe/Makefile +++ b/drivers/media/platform/ti-vpe/Makefile @@ -13,4 +13,4 @@ ccflags-$(CONFIG_VIDEO_TI_VPE_DEBUG) += -DDEBUG obj-$(CONFIG_VIDEO_TI_CAL) += ti-cal.o -ti-cal-y := cal.o +ti-cal-y := cal.o cal-video.o diff --git a/drivers/media/platform/ti-vpe/cal-video.c b/drivers/media/platform/ti-vpe/cal-video.c new file mode 100644 index 0000000000000..82b32a2f9db5f --- /dev/null +++ b/drivers/media/platform/ti-vpe/cal-video.c @@ -0,0 +1,887 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * TI Camera Access Layer (CAL) - Video Device + * + * Copyright (c) 2015-2020 Texas Instruments Inc. + * + * Authors: + * Benoit Parrot + * Laurent Pinchart + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "cal.h" + +/* ------------------------------------------------------------------ + * Format Handling + * ------------------------------------------------------------------ + */ + +static const struct cal_fmt cal_formats[] = { + { + .fourcc = V4L2_PIX_FMT_YUYV, + .code = MEDIA_BUS_FMT_YUYV8_2X8, + .bpp = 16, + }, { + .fourcc = V4L2_PIX_FMT_UYVY, + .code = MEDIA_BUS_FMT_UYVY8_2X8, + .bpp = 16, + }, { + .fourcc = V4L2_PIX_FMT_YVYU, + .code = MEDIA_BUS_FMT_YVYU8_2X8, + .bpp = 16, + }, { + .fourcc = V4L2_PIX_FMT_VYUY, + .code = MEDIA_BUS_FMT_VYUY8_2X8, + .bpp = 16, + }, { + .fourcc = V4L2_PIX_FMT_RGB565, /* gggbbbbb rrrrrggg */ + .code = MEDIA_BUS_FMT_RGB565_2X8_LE, + .bpp = 16, + }, { + .fourcc = V4L2_PIX_FMT_RGB565X, /* rrrrrggg gggbbbbb */ + .code = MEDIA_BUS_FMT_RGB565_2X8_BE, + .bpp = 16, + }, { + .fourcc = V4L2_PIX_FMT_RGB555, /* gggbbbbb arrrrrgg */ + .code = MEDIA_BUS_FMT_RGB555_2X8_PADHI_LE, + .bpp = 16, + }, { + .fourcc = V4L2_PIX_FMT_RGB555X, /* arrrrrgg gggbbbbb */ + .code = MEDIA_BUS_FMT_RGB555_2X8_PADHI_BE, + .bpp = 16, + }, { + .fourcc = V4L2_PIX_FMT_RGB24, /* rgb */ + .code = MEDIA_BUS_FMT_RGB888_2X12_LE, + .bpp = 24, + }, { + .fourcc = V4L2_PIX_FMT_BGR24, /* bgr */ + .code = MEDIA_BUS_FMT_RGB888_2X12_BE, + .bpp = 24, + }, { + .fourcc = V4L2_PIX_FMT_RGB32, /* argb */ + .code = MEDIA_BUS_FMT_ARGB8888_1X32, + .bpp = 32, + }, { + .fourcc = V4L2_PIX_FMT_SBGGR8, + .code = MEDIA_BUS_FMT_SBGGR8_1X8, + .bpp = 8, + }, { + .fourcc = V4L2_PIX_FMT_SGBRG8, + .code = MEDIA_BUS_FMT_SGBRG8_1X8, + .bpp = 8, + }, { + .fourcc = V4L2_PIX_FMT_SGRBG8, + .code = MEDIA_BUS_FMT_SGRBG8_1X8, + .bpp = 8, + }, { + .fourcc = V4L2_PIX_FMT_SRGGB8, + .code = MEDIA_BUS_FMT_SRGGB8_1X8, + .bpp = 8, + }, { + .fourcc = V4L2_PIX_FMT_SBGGR10, + .code = MEDIA_BUS_FMT_SBGGR10_1X10, + .bpp = 10, + }, { + .fourcc = V4L2_PIX_FMT_SGBRG10, + .code = MEDIA_BUS_FMT_SGBRG10_1X10, + .bpp = 10, + }, { + .fourcc = V4L2_PIX_FMT_SGRBG10, + .code = MEDIA_BUS_FMT_SGRBG10_1X10, + .bpp = 10, + }, { + .fourcc = V4L2_PIX_FMT_SRGGB10, + .code = MEDIA_BUS_FMT_SRGGB10_1X10, + .bpp = 10, + }, { + .fourcc = V4L2_PIX_FMT_SBGGR12, + .code = MEDIA_BUS_FMT_SBGGR12_1X12, + .bpp = 12, + }, { + .fourcc = V4L2_PIX_FMT_SGBRG12, + .code = MEDIA_BUS_FMT_SGBRG12_1X12, + .bpp = 12, + }, { + .fourcc = V4L2_PIX_FMT_SGRBG12, + .code = MEDIA_BUS_FMT_SGRBG12_1X12, + .bpp = 12, + }, { + .fourcc = V4L2_PIX_FMT_SRGGB12, + .code = MEDIA_BUS_FMT_SRGGB12_1X12, + .bpp = 12, + }, +}; + +/* Print Four-character-code (FOURCC) */ +static char *fourcc_to_str(u32 fmt) +{ + static char code[5]; + + code[0] = (unsigned char)(fmt & 0xff); + code[1] = (unsigned char)((fmt >> 8) & 0xff); + code[2] = (unsigned char)((fmt >> 16) & 0xff); + code[3] = (unsigned char)((fmt >> 24) & 0xff); + code[4] = '\0'; + + return code; +} + +/* ------------------------------------------------------------------ + * V4L2 Video IOCTLs + * ------------------------------------------------------------------ + */ + +static const struct cal_fmt *find_format_by_pix(struct cal_ctx *ctx, + u32 pixelformat) +{ + const struct cal_fmt *fmt; + unsigned int k; + + for (k = 0; k < ctx->num_active_fmt; k++) { + fmt = ctx->active_fmt[k]; + if (fmt->fourcc == pixelformat) + return fmt; + } + + return NULL; +} + +static const struct cal_fmt *find_format_by_code(struct cal_ctx *ctx, + u32 code) +{ + const struct cal_fmt *fmt; + unsigned int k; + + for (k = 0; k < ctx->num_active_fmt; k++) { + fmt = ctx->active_fmt[k]; + if (fmt->code == code) + return fmt; + } + + return NULL; +} + +static int cal_querycap(struct file *file, void *priv, + struct v4l2_capability *cap) +{ + struct cal_ctx *ctx = video_drvdata(file); + + strscpy(cap->driver, CAL_MODULE_NAME, sizeof(cap->driver)); + strscpy(cap->card, CAL_MODULE_NAME, sizeof(cap->card)); + + snprintf(cap->bus_info, sizeof(cap->bus_info), + "platform:%s", dev_name(ctx->cal->dev)); + return 0; +} + +static int cal_enum_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_fmtdesc *f) +{ + struct cal_ctx *ctx = video_drvdata(file); + const struct cal_fmt *fmt; + + if (f->index >= ctx->num_active_fmt) + return -EINVAL; + + fmt = ctx->active_fmt[f->index]; + + f->pixelformat = fmt->fourcc; + f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + return 0; +} + +static int __subdev_get_format(struct cal_ctx *ctx, + struct v4l2_mbus_framefmt *fmt) +{ + struct v4l2_subdev_format sd_fmt; + struct v4l2_mbus_framefmt *mbus_fmt = &sd_fmt.format; + int ret; + + sd_fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE; + sd_fmt.pad = 0; + + ret = v4l2_subdev_call(ctx->phy->sensor, pad, get_fmt, NULL, &sd_fmt); + if (ret) + return ret; + + *fmt = *mbus_fmt; + + ctx_dbg(1, ctx, "%s %dx%d code:%04X\n", __func__, + fmt->width, fmt->height, fmt->code); + + return 0; +} + +static int __subdev_set_format(struct cal_ctx *ctx, + struct v4l2_mbus_framefmt *fmt) +{ + struct v4l2_subdev_format sd_fmt; + struct v4l2_mbus_framefmt *mbus_fmt = &sd_fmt.format; + int ret; + + sd_fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE; + sd_fmt.pad = 0; + *mbus_fmt = *fmt; + + ret = v4l2_subdev_call(ctx->phy->sensor, pad, set_fmt, NULL, &sd_fmt); + if (ret) + return ret; + + ctx_dbg(1, ctx, "%s %dx%d code:%04X\n", __func__, + fmt->width, fmt->height, fmt->code); + + return 0; +} + +static int cal_calc_format_size(struct cal_ctx *ctx, + const struct cal_fmt *fmt, + struct v4l2_format *f) +{ + u32 bpl, max_width; + + if (!fmt) { + ctx_dbg(3, ctx, "No cal_fmt provided!\n"); + return -EINVAL; + } + + /* + * Maximum width is bound by the DMA max width in bytes. + * We need to recalculate the actual maxi width depending on the + * number of bytes per pixels required. + */ + max_width = MAX_WIDTH_BYTES / (ALIGN(fmt->bpp, 8) >> 3); + v4l_bound_align_image(&f->fmt.pix.width, 48, max_width, 2, + &f->fmt.pix.height, 32, MAX_HEIGHT_LINES, 0, 0); + + bpl = (f->fmt.pix.width * ALIGN(fmt->bpp, 8)) >> 3; + f->fmt.pix.bytesperline = ALIGN(bpl, 16); + + f->fmt.pix.sizeimage = f->fmt.pix.height * + f->fmt.pix.bytesperline; + + ctx_dbg(3, ctx, "%s: fourcc: %s size: %dx%d bpl:%d img_size:%d\n", + __func__, fourcc_to_str(f->fmt.pix.pixelformat), + f->fmt.pix.width, f->fmt.pix.height, + f->fmt.pix.bytesperline, f->fmt.pix.sizeimage); + + return 0; +} + +static int cal_g_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct cal_ctx *ctx = video_drvdata(file); + + *f = ctx->v_fmt; + + return 0; +} + +static int cal_try_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct cal_ctx *ctx = video_drvdata(file); + const struct cal_fmt *fmt; + struct v4l2_subdev_frame_size_enum fse; + int ret, found; + + fmt = find_format_by_pix(ctx, f->fmt.pix.pixelformat); + if (!fmt) { + ctx_dbg(3, ctx, "Fourcc format (0x%08x) not found.\n", + f->fmt.pix.pixelformat); + + /* Just get the first one enumerated */ + fmt = ctx->active_fmt[0]; + f->fmt.pix.pixelformat = fmt->fourcc; + } + + f->fmt.pix.field = ctx->v_fmt.fmt.pix.field; + + /* check for/find a valid width/height */ + ret = 0; + found = false; + fse.pad = 0; + fse.code = fmt->code; + fse.which = V4L2_SUBDEV_FORMAT_ACTIVE; + for (fse.index = 0; ; fse.index++) { + ret = v4l2_subdev_call(ctx->phy->sensor, pad, enum_frame_size, + NULL, &fse); + if (ret) + break; + + if ((f->fmt.pix.width == fse.max_width) && + (f->fmt.pix.height == fse.max_height)) { + found = true; + break; + } else if ((f->fmt.pix.width >= fse.min_width) && + (f->fmt.pix.width <= fse.max_width) && + (f->fmt.pix.height >= fse.min_height) && + (f->fmt.pix.height <= fse.max_height)) { + found = true; + break; + } + } + + if (!found) { + /* use existing values as default */ + f->fmt.pix.width = ctx->v_fmt.fmt.pix.width; + f->fmt.pix.height = ctx->v_fmt.fmt.pix.height; + } + + /* + * Use current colorspace for now, it will get + * updated properly during s_fmt + */ + f->fmt.pix.colorspace = ctx->v_fmt.fmt.pix.colorspace; + return cal_calc_format_size(ctx, fmt, f); +} + +static int cal_s_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct cal_ctx *ctx = video_drvdata(file); + struct vb2_queue *q = &ctx->vb_vidq; + const struct cal_fmt *fmt; + struct v4l2_mbus_framefmt mbus_fmt; + int ret; + + if (vb2_is_busy(q)) { + ctx_dbg(3, ctx, "%s device busy\n", __func__); + return -EBUSY; + } + + ret = cal_try_fmt_vid_cap(file, priv, f); + if (ret < 0) + return ret; + + fmt = find_format_by_pix(ctx, f->fmt.pix.pixelformat); + + v4l2_fill_mbus_format(&mbus_fmt, &f->fmt.pix, fmt->code); + + ret = __subdev_set_format(ctx, &mbus_fmt); + if (ret) + return ret; + + /* Just double check nothing has gone wrong */ + if (mbus_fmt.code != fmt->code) { + ctx_dbg(3, ctx, + "%s subdev changed format on us, this should not happen\n", + __func__); + return -EINVAL; + } + + v4l2_fill_pix_format(&ctx->v_fmt.fmt.pix, &mbus_fmt); + ctx->v_fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + ctx->v_fmt.fmt.pix.pixelformat = fmt->fourcc; + cal_calc_format_size(ctx, fmt, &ctx->v_fmt); + ctx->fmt = fmt; + ctx->m_fmt = mbus_fmt; + *f = ctx->v_fmt; + + return 0; +} + +static int cal_enum_framesizes(struct file *file, void *fh, + struct v4l2_frmsizeenum *fsize) +{ + struct cal_ctx *ctx = video_drvdata(file); + const struct cal_fmt *fmt; + struct v4l2_subdev_frame_size_enum fse; + int ret; + + /* check for valid format */ + fmt = find_format_by_pix(ctx, fsize->pixel_format); + if (!fmt) { + ctx_dbg(3, ctx, "Invalid pixel code: %x\n", + fsize->pixel_format); + return -EINVAL; + } + + fse.index = fsize->index; + fse.pad = 0; + fse.code = fmt->code; + fse.which = V4L2_SUBDEV_FORMAT_ACTIVE; + + ret = v4l2_subdev_call(ctx->phy->sensor, pad, enum_frame_size, NULL, + &fse); + if (ret) + return ret; + + ctx_dbg(1, ctx, "%s: index: %d code: %x W:[%d,%d] H:[%d,%d]\n", + __func__, fse.index, fse.code, fse.min_width, fse.max_width, + fse.min_height, fse.max_height); + + fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE; + fsize->discrete.width = fse.max_width; + fsize->discrete.height = fse.max_height; + + return 0; +} + +static int cal_enum_input(struct file *file, void *priv, + struct v4l2_input *inp) +{ + if (inp->index > 0) + return -EINVAL; + + inp->type = V4L2_INPUT_TYPE_CAMERA; + sprintf(inp->name, "Camera %u", inp->index); + return 0; +} + +static int cal_g_input(struct file *file, void *priv, unsigned int *i) +{ + *i = 0; + return 0; +} + +static int cal_s_input(struct file *file, void *priv, unsigned int i) +{ + return i > 0 ? -EINVAL : 0; +} + +/* timeperframe is arbitrary and continuous */ +static int cal_enum_frameintervals(struct file *file, void *priv, + struct v4l2_frmivalenum *fival) +{ + struct cal_ctx *ctx = video_drvdata(file); + const struct cal_fmt *fmt; + struct v4l2_subdev_frame_interval_enum fie = { + .index = fival->index, + .width = fival->width, + .height = fival->height, + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + }; + int ret; + + fmt = find_format_by_pix(ctx, fival->pixel_format); + if (!fmt) + return -EINVAL; + + fie.code = fmt->code; + ret = v4l2_subdev_call(ctx->phy->sensor, pad, enum_frame_interval, + NULL, &fie); + if (ret) + return ret; + fival->type = V4L2_FRMIVAL_TYPE_DISCRETE; + fival->discrete = fie.interval; + + return 0; +} + +static const struct v4l2_file_operations cal_fops = { + .owner = THIS_MODULE, + .open = v4l2_fh_open, + .release = vb2_fop_release, + .read = vb2_fop_read, + .poll = vb2_fop_poll, + .unlocked_ioctl = video_ioctl2, /* V4L2 ioctl handler */ + .mmap = vb2_fop_mmap, +}; + +static const struct v4l2_ioctl_ops cal_ioctl_ops = { + .vidioc_querycap = cal_querycap, + .vidioc_enum_fmt_vid_cap = cal_enum_fmt_vid_cap, + .vidioc_g_fmt_vid_cap = cal_g_fmt_vid_cap, + .vidioc_try_fmt_vid_cap = cal_try_fmt_vid_cap, + .vidioc_s_fmt_vid_cap = cal_s_fmt_vid_cap, + .vidioc_enum_framesizes = cal_enum_framesizes, + .vidioc_reqbufs = vb2_ioctl_reqbufs, + .vidioc_create_bufs = vb2_ioctl_create_bufs, + .vidioc_prepare_buf = vb2_ioctl_prepare_buf, + .vidioc_querybuf = vb2_ioctl_querybuf, + .vidioc_qbuf = vb2_ioctl_qbuf, + .vidioc_dqbuf = vb2_ioctl_dqbuf, + .vidioc_expbuf = vb2_ioctl_expbuf, + .vidioc_enum_input = cal_enum_input, + .vidioc_g_input = cal_g_input, + .vidioc_s_input = cal_s_input, + .vidioc_enum_frameintervals = cal_enum_frameintervals, + .vidioc_streamon = vb2_ioctl_streamon, + .vidioc_streamoff = vb2_ioctl_streamoff, + .vidioc_log_status = v4l2_ctrl_log_status, + .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, +}; + +/* ------------------------------------------------------------------ + * videobuf2 Operations + * ------------------------------------------------------------------ + */ + +static int cal_queue_setup(struct vb2_queue *vq, + unsigned int *nbuffers, unsigned int *nplanes, + unsigned int sizes[], struct device *alloc_devs[]) +{ + struct cal_ctx *ctx = vb2_get_drv_priv(vq); + unsigned int size = ctx->v_fmt.fmt.pix.sizeimage; + + if (vq->num_buffers + *nbuffers < 3) + *nbuffers = 3 - vq->num_buffers; + + if (*nplanes) { + if (sizes[0] < size) + return -EINVAL; + size = sizes[0]; + } + + *nplanes = 1; + sizes[0] = size; + + ctx_dbg(3, ctx, "nbuffers=%d, size=%d\n", *nbuffers, sizes[0]); + + return 0; +} + +static int cal_buffer_prepare(struct vb2_buffer *vb) +{ + struct cal_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); + struct cal_buffer *buf = container_of(vb, struct cal_buffer, + vb.vb2_buf); + unsigned long size; + + if (WARN_ON(!ctx->fmt)) + return -EINVAL; + + size = ctx->v_fmt.fmt.pix.sizeimage; + if (vb2_plane_size(vb, 0) < size) { + ctx_err(ctx, + "data will not fit into plane (%lu < %lu)\n", + vb2_plane_size(vb, 0), size); + return -EINVAL; + } + + vb2_set_plane_payload(&buf->vb.vb2_buf, 0, size); + return 0; +} + +static void cal_buffer_queue(struct vb2_buffer *vb) +{ + struct cal_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); + struct cal_buffer *buf = container_of(vb, struct cal_buffer, + vb.vb2_buf); + struct cal_dmaqueue *vidq = &ctx->vidq; + unsigned long flags; + + /* recheck locking */ + spin_lock_irqsave(&ctx->slock, flags); + list_add_tail(&buf->list, &vidq->active); + spin_unlock_irqrestore(&ctx->slock, flags); +} + +static int cal_start_streaming(struct vb2_queue *vq, unsigned int count) +{ + struct cal_ctx *ctx = vb2_get_drv_priv(vq); + struct cal_dmaqueue *dma_q = &ctx->vidq; + struct cal_buffer *buf, *tmp; + unsigned long addr; + unsigned long flags; + int ret; + + spin_lock_irqsave(&ctx->slock, flags); + if (list_empty(&dma_q->active)) { + spin_unlock_irqrestore(&ctx->slock, flags); + ctx_dbg(3, ctx, "buffer queue is empty\n"); + return -EIO; + } + + buf = list_entry(dma_q->active.next, struct cal_buffer, list); + ctx->cur_frm = buf; + ctx->next_frm = buf; + list_del(&buf->list); + spin_unlock_irqrestore(&ctx->slock, flags); + + addr = vb2_dma_contig_plane_dma_addr(&ctx->cur_frm->vb.vb2_buf, 0); + ctx->sequence = 0; + + pm_runtime_get_sync(ctx->cal->dev); + + cal_ctx_csi2_config(ctx); + cal_ctx_pix_proc_config(ctx); + cal_ctx_wr_dma_config(ctx, ctx->v_fmt.fmt.pix.bytesperline, + ctx->v_fmt.fmt.pix.height); + + cal_camerarx_enable_irqs(ctx->phy); + + ret = cal_camerarx_start(ctx->phy, ctx->fmt); + if (ret) + goto err; + + cal_ctx_wr_dma_addr(ctx, addr); + cal_camerarx_ppi_enable(ctx->phy); + + if (cal_debug >= 4) + cal_quickdump_regs(ctx->cal); + + return 0; + +err: + spin_lock_irqsave(&ctx->slock, flags); + vb2_buffer_done(&ctx->cur_frm->vb.vb2_buf, VB2_BUF_STATE_QUEUED); + ctx->cur_frm = NULL; + ctx->next_frm = NULL; + list_for_each_entry_safe(buf, tmp, &dma_q->active, list) { + list_del(&buf->list); + vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_QUEUED); + } + spin_unlock_irqrestore(&ctx->slock, flags); + return ret; +} + +static void cal_stop_streaming(struct vb2_queue *vq) +{ + struct cal_ctx *ctx = vb2_get_drv_priv(vq); + struct cal_dmaqueue *dma_q = &ctx->vidq; + struct cal_buffer *buf, *tmp; + unsigned long timeout; + unsigned long flags; + bool dma_act; + + cal_camerarx_ppi_disable(ctx->phy); + + /* wait for stream and dma to finish */ + dma_act = true; + timeout = jiffies + msecs_to_jiffies(500); + while (dma_act && time_before(jiffies, timeout)) { + msleep(50); + + spin_lock_irqsave(&ctx->slock, flags); + dma_act = ctx->dma_act; + spin_unlock_irqrestore(&ctx->slock, flags); + } + + if (dma_act) + ctx_err(ctx, "failed to disable dma cleanly\n"); + + cal_camerarx_disable_irqs(ctx->phy); + cal_camerarx_stop(ctx->phy); + + /* Release all active buffers */ + spin_lock_irqsave(&ctx->slock, flags); + list_for_each_entry_safe(buf, tmp, &dma_q->active, list) { + list_del(&buf->list); + vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR); + } + + if (ctx->cur_frm == ctx->next_frm) { + vb2_buffer_done(&ctx->cur_frm->vb.vb2_buf, VB2_BUF_STATE_ERROR); + } else { + vb2_buffer_done(&ctx->cur_frm->vb.vb2_buf, VB2_BUF_STATE_ERROR); + vb2_buffer_done(&ctx->next_frm->vb.vb2_buf, + VB2_BUF_STATE_ERROR); + } + ctx->cur_frm = NULL; + ctx->next_frm = NULL; + spin_unlock_irqrestore(&ctx->slock, flags); + + pm_runtime_put_sync(ctx->cal->dev); +} + +static const struct vb2_ops cal_video_qops = { + .queue_setup = cal_queue_setup, + .buf_prepare = cal_buffer_prepare, + .buf_queue = cal_buffer_queue, + .start_streaming = cal_start_streaming, + .stop_streaming = cal_stop_streaming, + .wait_prepare = vb2_ops_wait_prepare, + .wait_finish = vb2_ops_wait_finish, +}; + +/* ------------------------------------------------------------------ + * V4L2 Initialization and Registration + * ------------------------------------------------------------------ + */ + +static const struct video_device cal_videodev = { + .name = CAL_MODULE_NAME, + .fops = &cal_fops, + .ioctl_ops = &cal_ioctl_ops, + .minor = -1, + .release = video_device_release_empty, + .device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING | + V4L2_CAP_READWRITE, +}; + +static int cal_ctx_v4l2_init_formats(struct cal_ctx *ctx) +{ + struct v4l2_subdev_mbus_code_enum mbus_code; + struct v4l2_mbus_framefmt mbus_fmt; + const struct cal_fmt *fmt; + unsigned int i, j, k; + int ret = 0; + + /* Enumerate sub device formats and enable all matching local formats */ + ctx->active_fmt = devm_kcalloc(ctx->cal->dev, ARRAY_SIZE(cal_formats), + sizeof(*ctx->active_fmt), GFP_KERNEL); + ctx->num_active_fmt = 0; + + for (j = 0, i = 0; ret != -EINVAL; ++j) { + + memset(&mbus_code, 0, sizeof(mbus_code)); + mbus_code.index = j; + mbus_code.which = V4L2_SUBDEV_FORMAT_ACTIVE; + ret = v4l2_subdev_call(ctx->phy->sensor, pad, enum_mbus_code, + NULL, &mbus_code); + if (ret) + continue; + + ctx_dbg(2, ctx, + "subdev %s: code: %04x idx: %u\n", + ctx->phy->sensor->name, mbus_code.code, j); + + for (k = 0; k < ARRAY_SIZE(cal_formats); k++) { + const struct cal_fmt *fmt = &cal_formats[k]; + + if (mbus_code.code == fmt->code) { + ctx->active_fmt[i] = fmt; + ctx_dbg(2, ctx, + "matched fourcc: %s: code: %04x idx: %u\n", + fourcc_to_str(fmt->fourcc), + fmt->code, i); + ctx->num_active_fmt = ++i; + } + } + } + + if (i == 0) { + ctx_err(ctx, "No suitable format reported by subdev %s\n", + ctx->phy->sensor->name); + return -EINVAL; + } + + ret = __subdev_get_format(ctx, &mbus_fmt); + if (ret) + return ret; + + fmt = find_format_by_code(ctx, mbus_fmt.code); + if (!fmt) { + ctx_dbg(3, ctx, "mbus code format (0x%08x) not found.\n", + mbus_fmt.code); + return -EINVAL; + } + + /* Save current subdev format */ + v4l2_fill_pix_format(&ctx->v_fmt.fmt.pix, &mbus_fmt); + ctx->v_fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + ctx->v_fmt.fmt.pix.pixelformat = fmt->fourcc; + cal_calc_format_size(ctx, fmt, &ctx->v_fmt); + ctx->fmt = fmt; + ctx->m_fmt = mbus_fmt; + + return 0; +} + +int cal_ctx_v4l2_register(struct cal_ctx *ctx) +{ + struct v4l2_ctrl_handler *hdl = &ctx->ctrl_handler; + struct video_device *vfd = &ctx->vdev; + int ret; + + ret = cal_ctx_v4l2_init_formats(ctx); + if (ret) + return ret; + + ret = v4l2_ctrl_add_handler(hdl, ctx->phy->sensor->ctrl_handler, NULL, + true); + if (ret < 0) { + ctx_err(ctx, "Failed to add sensor ctrl handler\n"); + return ret; + } + + ret = video_register_device(vfd, VFL_TYPE_VIDEO, cal_video_nr); + if (ret < 0) { + ctx_err(ctx, "Failed to register video device\n"); + return ret; + } + + ctx_info(ctx, "V4L2 device registered as %s\n", + video_device_node_name(vfd)); + + return 0; +} + +void cal_ctx_v4l2_unregister(struct cal_ctx *ctx) +{ + ctx_dbg(1, ctx, "unregistering %s\n", + video_device_node_name(&ctx->vdev)); + + video_unregister_device(&ctx->vdev); +} + +int cal_ctx_v4l2_init(struct cal_ctx *ctx) +{ + struct v4l2_ctrl_handler *hdl = &ctx->ctrl_handler; + struct video_device *vfd = &ctx->vdev; + struct vb2_queue *q = &ctx->vb_vidq; + int ret; + + INIT_LIST_HEAD(&ctx->vidq.active); + spin_lock_init(&ctx->slock); + mutex_init(&ctx->mutex); + + /* Initialize the vb2 queue. */ + q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + q->io_modes = VB2_MMAP | VB2_DMABUF | VB2_READ; + q->drv_priv = ctx; + q->buf_struct_size = sizeof(struct cal_buffer); + q->ops = &cal_video_qops; + q->mem_ops = &vb2_dma_contig_memops; + q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; + q->lock = &ctx->mutex; + q->min_buffers_needed = 3; + q->dev = ctx->cal->dev; + + ret = vb2_queue_init(q); + if (ret) + return ret; + + /* Initialize the video device and media entity. */ + *vfd = cal_videodev; + vfd->v4l2_dev = &ctx->cal->v4l2_dev; + vfd->queue = q; + snprintf(vfd->name, sizeof(vfd->name), "CAL output %u", ctx->index); + vfd->lock = &ctx->mutex; + video_set_drvdata(vfd, ctx); + + ctx->pad.flags = MEDIA_PAD_FL_SINK; + ret = media_entity_pads_init(&vfd->entity, 1, &ctx->pad); + if (ret < 0) + return ret; + + /* Initialize the control handler. */ + ret = v4l2_ctrl_handler_init(hdl, 11); + if (ret < 0) { + ctx_err(ctx, "Failed to init ctrl handler\n"); + goto error; + } + + vfd->ctrl_handler = hdl; + + return 0; + +error: + media_entity_cleanup(&vfd->entity); + return ret; +} + +void cal_ctx_v4l2_cleanup(struct cal_ctx *ctx) +{ + v4l2_ctrl_handler_free(&ctx->ctrl_handler); + media_entity_cleanup(&ctx->vdev.entity); +} diff --git a/drivers/media/platform/ti-vpe/cal.c b/drivers/media/platform/ti-vpe/cal.c index fdb40c85de9b8..e81c8507893b0 100644 --- a/drivers/media/platform/ti-vpe/cal.c +++ b/drivers/media/platform/ti-vpe/cal.c @@ -1,9 +1,12 @@ // SPDX-License-Identifier: GPL-2.0-only /* - * TI CAL camera interface driver + * TI Camera Access Layer (CAL) - Driver * - * Copyright (c) 2015 Texas Instruments Inc. - * Benoit Parrot, + * Copyright (c) 2015-2020 Texas Instruments Inc. + * + * Authors: + * Benoit Parrot + * Laurent Pinchart */ #include @@ -11,7 +14,6 @@ #include #include #include -#include #include #include #include @@ -27,319 +29,26 @@ #include #include #include -#include -#include #include -#include #include #include +#include "cal.h" #include "cal_regs.h" -#define CAL_MODULE_NAME "cal" - MODULE_DESCRIPTION("TI CAL driver"); MODULE_AUTHOR("Benoit Parrot, "); MODULE_LICENSE("GPL v2"); MODULE_VERSION("0.1.0"); -static unsigned video_nr = -1; -module_param(video_nr, uint, 0644); +int cal_video_nr = -1; +module_param_named(video_nr, cal_video_nr, uint, 0644); MODULE_PARM_DESC(video_nr, "videoX start number, -1 is autodetect"); -static unsigned debug; -module_param(debug, uint, 0644); +unsigned int cal_debug; +module_param_named(debug, cal_debug, uint, 0644); MODULE_PARM_DESC(debug, "activates debug info"); -#define cal_dbg(level, cal, fmt, arg...) \ - do { \ - if (debug >= (level)) \ - dev_printk(KERN_DEBUG, (cal)->dev, fmt, ##arg); \ - } while (0) -#define cal_info(cal, fmt, arg...) \ - dev_info((cal)->dev, fmt, ##arg) -#define cal_err(cal, fmt, arg...) \ - dev_err((cal)->dev, fmt, ##arg) - -#define ctx_dbg(level, ctx, fmt, arg...) \ - cal_dbg(level, (ctx)->cal, "ctx%u: " fmt, (ctx)->index, ##arg) -#define ctx_info(ctx, fmt, arg...) \ - cal_info((ctx)->cal, "ctx%u: " fmt, (ctx)->index, ##arg) -#define ctx_err(ctx, fmt, arg...) \ - cal_err((ctx)->cal, "ctx%u: " fmt, (ctx)->index, ##arg) - -#define phy_dbg(level, phy, fmt, arg...) \ - cal_dbg(level, (phy)->cal, "phy%u: " fmt, (phy)->instance, ##arg) -#define phy_info(phy, fmt, arg...) \ - cal_info((phy)->cal, "phy%u: " fmt, (phy)->instance, ##arg) -#define phy_err(phy, fmt, arg...) \ - cal_err((phy)->cal, "phy%u: " fmt, (phy)->instance, ##arg) - -#define CAL_NUM_CONTEXT 2 -#define CAL_NUM_CSI2_PORTS 2 - -#define MAX_WIDTH_BYTES (8192 * 8) -#define MAX_HEIGHT_LINES 16383 - -/* ------------------------------------------------------------------ - * Format Handling - * ------------------------------------------------------------------ - */ - -struct cal_fmt { - u32 fourcc; - u32 code; - /* Bits per pixel */ - u8 bpp; -}; - -static const struct cal_fmt cal_formats[] = { - { - .fourcc = V4L2_PIX_FMT_YUYV, - .code = MEDIA_BUS_FMT_YUYV8_2X8, - .bpp = 16, - }, { - .fourcc = V4L2_PIX_FMT_UYVY, - .code = MEDIA_BUS_FMT_UYVY8_2X8, - .bpp = 16, - }, { - .fourcc = V4L2_PIX_FMT_YVYU, - .code = MEDIA_BUS_FMT_YVYU8_2X8, - .bpp = 16, - }, { - .fourcc = V4L2_PIX_FMT_VYUY, - .code = MEDIA_BUS_FMT_VYUY8_2X8, - .bpp = 16, - }, { - .fourcc = V4L2_PIX_FMT_RGB565, /* gggbbbbb rrrrrggg */ - .code = MEDIA_BUS_FMT_RGB565_2X8_LE, - .bpp = 16, - }, { - .fourcc = V4L2_PIX_FMT_RGB565X, /* rrrrrggg gggbbbbb */ - .code = MEDIA_BUS_FMT_RGB565_2X8_BE, - .bpp = 16, - }, { - .fourcc = V4L2_PIX_FMT_RGB555, /* gggbbbbb arrrrrgg */ - .code = MEDIA_BUS_FMT_RGB555_2X8_PADHI_LE, - .bpp = 16, - }, { - .fourcc = V4L2_PIX_FMT_RGB555X, /* arrrrrgg gggbbbbb */ - .code = MEDIA_BUS_FMT_RGB555_2X8_PADHI_BE, - .bpp = 16, - }, { - .fourcc = V4L2_PIX_FMT_RGB24, /* rgb */ - .code = MEDIA_BUS_FMT_RGB888_2X12_LE, - .bpp = 24, - }, { - .fourcc = V4L2_PIX_FMT_BGR24, /* bgr */ - .code = MEDIA_BUS_FMT_RGB888_2X12_BE, - .bpp = 24, - }, { - .fourcc = V4L2_PIX_FMT_RGB32, /* argb */ - .code = MEDIA_BUS_FMT_ARGB8888_1X32, - .bpp = 32, - }, { - .fourcc = V4L2_PIX_FMT_SBGGR8, - .code = MEDIA_BUS_FMT_SBGGR8_1X8, - .bpp = 8, - }, { - .fourcc = V4L2_PIX_FMT_SGBRG8, - .code = MEDIA_BUS_FMT_SGBRG8_1X8, - .bpp = 8, - }, { - .fourcc = V4L2_PIX_FMT_SGRBG8, - .code = MEDIA_BUS_FMT_SGRBG8_1X8, - .bpp = 8, - }, { - .fourcc = V4L2_PIX_FMT_SRGGB8, - .code = MEDIA_BUS_FMT_SRGGB8_1X8, - .bpp = 8, - }, { - .fourcc = V4L2_PIX_FMT_SBGGR10, - .code = MEDIA_BUS_FMT_SBGGR10_1X10, - .bpp = 10, - }, { - .fourcc = V4L2_PIX_FMT_SGBRG10, - .code = MEDIA_BUS_FMT_SGBRG10_1X10, - .bpp = 10, - }, { - .fourcc = V4L2_PIX_FMT_SGRBG10, - .code = MEDIA_BUS_FMT_SGRBG10_1X10, - .bpp = 10, - }, { - .fourcc = V4L2_PIX_FMT_SRGGB10, - .code = MEDIA_BUS_FMT_SRGGB10_1X10, - .bpp = 10, - }, { - .fourcc = V4L2_PIX_FMT_SBGGR12, - .code = MEDIA_BUS_FMT_SBGGR12_1X12, - .bpp = 12, - }, { - .fourcc = V4L2_PIX_FMT_SGBRG12, - .code = MEDIA_BUS_FMT_SGBRG12_1X12, - .bpp = 12, - }, { - .fourcc = V4L2_PIX_FMT_SGRBG12, - .code = MEDIA_BUS_FMT_SGRBG12_1X12, - .bpp = 12, - }, { - .fourcc = V4L2_PIX_FMT_SRGGB12, - .code = MEDIA_BUS_FMT_SRGGB12_1X12, - .bpp = 12, - }, -}; - -/* Print Four-character-code (FOURCC) */ -static char *fourcc_to_str(u32 fmt) -{ - static char code[5]; - - code[0] = (unsigned char)(fmt & 0xff); - code[1] = (unsigned char)((fmt >> 8) & 0xff); - code[2] = (unsigned char)((fmt >> 16) & 0xff); - code[3] = (unsigned char)((fmt >> 24) & 0xff); - code[4] = '\0'; - - return code; -} - -/* ------------------------------------------------------------------ - * Driver Structures - * ------------------------------------------------------------------ - */ - -/* buffer for one video frame */ -struct cal_buffer { - /* common v4l buffer stuff -- must be first */ - struct vb2_v4l2_buffer vb; - struct list_head list; -}; - -struct cal_dmaqueue { - struct list_head active; -}; - -/* CTRL_CORE_CAMERRX_CONTROL register field id */ -enum cal_camerarx_field { - F_CTRLCLKEN, - F_CAMMODE, - F_LANEENABLE, - F_CSI_MODE, - - F_MAX_FIELDS, -}; - -struct cal_camerarx_data { - struct { - unsigned int lsb; - unsigned int msb; - } fields[F_MAX_FIELDS]; - unsigned int num_lanes; -}; - -struct cal_data { - const struct cal_camerarx_data *camerarx; - unsigned int num_csi2_phy; - unsigned int flags; -}; - -/* - * The Camera Adaptation Layer (CAL) module is paired with one or more complex - * I/O PHYs (CAMERARX). It contains multiple instances of CSI-2, processing and - * DMA contexts. - * - * The cal_dev structure represents the whole subsystem, including the CAL and - * the CAMERARX instances. Instances of struct cal_dev are named cal through the - * driver. - * - * The cal_camerarx structure represents one CAMERARX instance. Instances of - * cal_camerarx are named phy through the driver. - * - * The cal_ctx structure represents the combination of one CSI-2 context, one - * processing context and one DMA context. Instance of struct cal_ctx are named - * ctx through the driver. - */ - -struct cal_camerarx { - void __iomem *base; - struct resource *res; - struct device *dev; - struct regmap_field *fields[F_MAX_FIELDS]; - - struct cal_dev *cal; - unsigned int instance; - - struct v4l2_fwnode_endpoint endpoint; - struct device_node *sensor_node; - struct v4l2_subdev *sensor; -}; - -struct cal_dev { - struct clk *fclk; - int irq; - void __iomem *base; - struct resource *res; - struct device *dev; - - const struct cal_data *data; - u32 revision; - - /* Control Module handle */ - struct regmap *syscon_camerrx; - u32 syscon_camerrx_offset; - - /* Camera Core Module handle */ - struct cal_camerarx *phy[CAL_NUM_CSI2_PORTS]; - - struct cal_ctx *ctx[CAL_NUM_CONTEXT]; - - struct media_device mdev; - struct v4l2_device v4l2_dev; - struct v4l2_async_notifier notifier; -}; - -/* - * There is one cal_ctx structure for each camera core context. - */ -struct cal_ctx { - struct v4l2_ctrl_handler ctrl_handler; - struct video_device vdev; - struct media_pad pad; - - struct cal_dev *cal; - struct cal_camerarx *phy; - - /* v4l2_ioctl mutex */ - struct mutex mutex; - /* v4l2 buffers lock */ - spinlock_t slock; - - struct cal_dmaqueue vidq; - - /* video capture */ - const struct cal_fmt *fmt; - /* Used to store current pixel format */ - struct v4l2_format v_fmt; - /* Used to store current mbus frame format */ - struct v4l2_mbus_framefmt m_fmt; - - /* Current subdev enumerated format */ - const struct cal_fmt **active_fmt; - unsigned int num_active_fmt; - - unsigned int sequence; - struct vb2_queue vb_vidq; - unsigned int index; - unsigned int cport; - - /* Pointer pointing to current v4l2_buffer */ - struct cal_buffer *cur_frm; - /* Pointer pointing to next v4l2_buffer */ - struct cal_buffer *next_frm; - - bool dma_act; -}; - /* ------------------------------------------------------------------ * Platform Data * ------------------------------------------------------------------ @@ -451,7 +160,7 @@ static inline void set_field(u32 *valp, u32 field, u32 mask) *valp = val; } -static void cal_quickdump_regs(struct cal_dev *cal) +void cal_quickdump_regs(struct cal_dev *cal) { unsigned int i; @@ -668,8 +377,7 @@ static void cal_camerarx_wait_stop_state(struct cal_camerarx *phy) phy_err(phy, "Timeout waiting for stop state\n"); } -static int cal_camerarx_start(struct cal_camerarx *phy, - const struct cal_fmt *fmt) +int cal_camerarx_start(struct cal_camerarx *phy, const struct cal_fmt *fmt) { s64 external_rate; u32 sscounter; @@ -796,7 +504,7 @@ static int cal_camerarx_start(struct cal_camerarx *phy, return 0; } -static void cal_camerarx_stop(struct cal_camerarx *phy) +void cal_camerarx_stop(struct cal_camerarx *phy) { unsigned int i; int ret; @@ -865,7 +573,7 @@ static void cal_camerarx_i913_errata(struct cal_camerarx *phy) /* * Enable the expected IRQ sources */ -static void cal_camerarx_enable_irqs(struct cal_camerarx *phy) +void cal_camerarx_enable_irqs(struct cal_camerarx *phy) { u32 val; @@ -896,7 +604,7 @@ static void cal_camerarx_enable_irqs(struct cal_camerarx *phy) reg_write(phy->cal, CAL_CSI2_VC_IRQENABLE(0), 0xFF000000); } -static void cal_camerarx_disable_irqs(struct cal_camerarx *phy) +void cal_camerarx_disable_irqs(struct cal_camerarx *phy) { u32 val; @@ -918,14 +626,14 @@ static void cal_camerarx_disable_irqs(struct cal_camerarx *phy) reg_write(phy->cal, CAL_CSI2_VC_IRQENABLE(0), 0); } -static void cal_camerarx_ppi_enable(struct cal_camerarx *phy) +void cal_camerarx_ppi_enable(struct cal_camerarx *phy) { reg_write(phy->cal, CAL_CSI2_PPI_CTRL(phy->instance), BIT(3)); reg_write_field(phy->cal, CAL_CSI2_PPI_CTRL(phy->instance), 1, CAL_CSI2_PPI_CTRL_IF_EN_MASK); } -static void cal_camerarx_ppi_disable(struct cal_camerarx *phy) +void cal_camerarx_ppi_disable(struct cal_camerarx *phy) { reg_write_field(phy->cal, CAL_CSI2_PPI_CTRL(phy->instance), 0, CAL_CSI2_PPI_CTRL_IF_EN_MASK); @@ -1145,7 +853,7 @@ static int cal_camerarx_init_regmap(struct cal_dev *cal) * ------------------------------------------------------------------ */ -static void cal_ctx_csi2_config(struct cal_ctx *ctx) +void cal_ctx_csi2_config(struct cal_ctx *ctx) { u32 val; @@ -1170,7 +878,7 @@ static void cal_ctx_csi2_config(struct cal_ctx *ctx) reg_read(ctx->cal, CAL_CSI2_CTX0(ctx->index))); } -static void cal_ctx_pix_proc_config(struct cal_ctx *ctx) +void cal_ctx_pix_proc_config(struct cal_ctx *ctx) { u32 val, extract, pack; @@ -1221,8 +929,8 @@ static void cal_ctx_pix_proc_config(struct cal_ctx *ctx) reg_read(ctx->cal, CAL_PIX_PROC(ctx->index))); } -static void cal_ctx_wr_dma_config(struct cal_ctx *ctx, - unsigned int width, unsigned int height) +void cal_ctx_wr_dma_config(struct cal_ctx *ctx, unsigned int width, + unsigned int height) { u32 val; @@ -1275,7 +983,7 @@ static void cal_ctx_wr_dma_config(struct cal_ctx *ctx, ctx_dbg(3, ctx, "CAL_CTRL = 0x%08x\n", reg_read(ctx->cal, CAL_CTRL)); } -static void cal_ctx_wr_dma_addr(struct cal_ctx *ctx, unsigned int dmaaddr) +void cal_ctx_wr_dma_addr(struct cal_ctx *ctx, unsigned int dmaaddr) { reg_write(ctx->cal, CAL_WR_DMA_ADDR(ctx->index), dmaaddr); } @@ -1388,753 +1096,6 @@ static irqreturn_t cal_irq(int irq_cal, void *data) return IRQ_HANDLED; } -/* ------------------------------------------------------------------ - * V4L2 Video IOCTLs - * ------------------------------------------------------------------ - */ - -static const struct cal_fmt *find_format_by_pix(struct cal_ctx *ctx, - u32 pixelformat) -{ - const struct cal_fmt *fmt; - unsigned int k; - - for (k = 0; k < ctx->num_active_fmt; k++) { - fmt = ctx->active_fmt[k]; - if (fmt->fourcc == pixelformat) - return fmt; - } - - return NULL; -} - -static const struct cal_fmt *find_format_by_code(struct cal_ctx *ctx, - u32 code) -{ - const struct cal_fmt *fmt; - unsigned int k; - - for (k = 0; k < ctx->num_active_fmt; k++) { - fmt = ctx->active_fmt[k]; - if (fmt->code == code) - return fmt; - } - - return NULL; -} - -static int cal_querycap(struct file *file, void *priv, - struct v4l2_capability *cap) -{ - struct cal_ctx *ctx = video_drvdata(file); - - strscpy(cap->driver, CAL_MODULE_NAME, sizeof(cap->driver)); - strscpy(cap->card, CAL_MODULE_NAME, sizeof(cap->card)); - - snprintf(cap->bus_info, sizeof(cap->bus_info), - "platform:%s", dev_name(ctx->cal->dev)); - return 0; -} - -static int cal_enum_fmt_vid_cap(struct file *file, void *priv, - struct v4l2_fmtdesc *f) -{ - struct cal_ctx *ctx = video_drvdata(file); - const struct cal_fmt *fmt; - - if (f->index >= ctx->num_active_fmt) - return -EINVAL; - - fmt = ctx->active_fmt[f->index]; - - f->pixelformat = fmt->fourcc; - f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - return 0; -} - -static int __subdev_get_format(struct cal_ctx *ctx, - struct v4l2_mbus_framefmt *fmt) -{ - struct v4l2_subdev_format sd_fmt; - struct v4l2_mbus_framefmt *mbus_fmt = &sd_fmt.format; - int ret; - - sd_fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE; - sd_fmt.pad = 0; - - ret = v4l2_subdev_call(ctx->phy->sensor, pad, get_fmt, NULL, &sd_fmt); - if (ret) - return ret; - - *fmt = *mbus_fmt; - - ctx_dbg(1, ctx, "%s %dx%d code:%04X\n", __func__, - fmt->width, fmt->height, fmt->code); - - return 0; -} - -static int __subdev_set_format(struct cal_ctx *ctx, - struct v4l2_mbus_framefmt *fmt) -{ - struct v4l2_subdev_format sd_fmt; - struct v4l2_mbus_framefmt *mbus_fmt = &sd_fmt.format; - int ret; - - sd_fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE; - sd_fmt.pad = 0; - *mbus_fmt = *fmt; - - ret = v4l2_subdev_call(ctx->phy->sensor, pad, set_fmt, NULL, &sd_fmt); - if (ret) - return ret; - - ctx_dbg(1, ctx, "%s %dx%d code:%04X\n", __func__, - fmt->width, fmt->height, fmt->code); - - return 0; -} - -static int cal_calc_format_size(struct cal_ctx *ctx, - const struct cal_fmt *fmt, - struct v4l2_format *f) -{ - u32 bpl, max_width; - - if (!fmt) { - ctx_dbg(3, ctx, "No cal_fmt provided!\n"); - return -EINVAL; - } - - /* - * Maximum width is bound by the DMA max width in bytes. - * We need to recalculate the actual maxi width depending on the - * number of bytes per pixels required. - */ - max_width = MAX_WIDTH_BYTES / (ALIGN(fmt->bpp, 8) >> 3); - v4l_bound_align_image(&f->fmt.pix.width, 48, max_width, 2, - &f->fmt.pix.height, 32, MAX_HEIGHT_LINES, 0, 0); - - bpl = (f->fmt.pix.width * ALIGN(fmt->bpp, 8)) >> 3; - f->fmt.pix.bytesperline = ALIGN(bpl, 16); - - f->fmt.pix.sizeimage = f->fmt.pix.height * - f->fmt.pix.bytesperline; - - ctx_dbg(3, ctx, "%s: fourcc: %s size: %dx%d bpl:%d img_size:%d\n", - __func__, fourcc_to_str(f->fmt.pix.pixelformat), - f->fmt.pix.width, f->fmt.pix.height, - f->fmt.pix.bytesperline, f->fmt.pix.sizeimage); - - return 0; -} - -static int cal_g_fmt_vid_cap(struct file *file, void *priv, - struct v4l2_format *f) -{ - struct cal_ctx *ctx = video_drvdata(file); - - *f = ctx->v_fmt; - - return 0; -} - -static int cal_try_fmt_vid_cap(struct file *file, void *priv, - struct v4l2_format *f) -{ - struct cal_ctx *ctx = video_drvdata(file); - const struct cal_fmt *fmt; - struct v4l2_subdev_frame_size_enum fse; - int ret, found; - - fmt = find_format_by_pix(ctx, f->fmt.pix.pixelformat); - if (!fmt) { - ctx_dbg(3, ctx, "Fourcc format (0x%08x) not found.\n", - f->fmt.pix.pixelformat); - - /* Just get the first one enumerated */ - fmt = ctx->active_fmt[0]; - f->fmt.pix.pixelformat = fmt->fourcc; - } - - f->fmt.pix.field = ctx->v_fmt.fmt.pix.field; - - /* check for/find a valid width/height */ - ret = 0; - found = false; - fse.pad = 0; - fse.code = fmt->code; - fse.which = V4L2_SUBDEV_FORMAT_ACTIVE; - for (fse.index = 0; ; fse.index++) { - ret = v4l2_subdev_call(ctx->phy->sensor, pad, enum_frame_size, - NULL, &fse); - if (ret) - break; - - if ((f->fmt.pix.width == fse.max_width) && - (f->fmt.pix.height == fse.max_height)) { - found = true; - break; - } else if ((f->fmt.pix.width >= fse.min_width) && - (f->fmt.pix.width <= fse.max_width) && - (f->fmt.pix.height >= fse.min_height) && - (f->fmt.pix.height <= fse.max_height)) { - found = true; - break; - } - } - - if (!found) { - /* use existing values as default */ - f->fmt.pix.width = ctx->v_fmt.fmt.pix.width; - f->fmt.pix.height = ctx->v_fmt.fmt.pix.height; - } - - /* - * Use current colorspace for now, it will get - * updated properly during s_fmt - */ - f->fmt.pix.colorspace = ctx->v_fmt.fmt.pix.colorspace; - return cal_calc_format_size(ctx, fmt, f); -} - -static int cal_s_fmt_vid_cap(struct file *file, void *priv, - struct v4l2_format *f) -{ - struct cal_ctx *ctx = video_drvdata(file); - struct vb2_queue *q = &ctx->vb_vidq; - const struct cal_fmt *fmt; - struct v4l2_mbus_framefmt mbus_fmt; - int ret; - - if (vb2_is_busy(q)) { - ctx_dbg(3, ctx, "%s device busy\n", __func__); - return -EBUSY; - } - - ret = cal_try_fmt_vid_cap(file, priv, f); - if (ret < 0) - return ret; - - fmt = find_format_by_pix(ctx, f->fmt.pix.pixelformat); - - v4l2_fill_mbus_format(&mbus_fmt, &f->fmt.pix, fmt->code); - - ret = __subdev_set_format(ctx, &mbus_fmt); - if (ret) - return ret; - - /* Just double check nothing has gone wrong */ - if (mbus_fmt.code != fmt->code) { - ctx_dbg(3, ctx, - "%s subdev changed format on us, this should not happen\n", - __func__); - return -EINVAL; - } - - v4l2_fill_pix_format(&ctx->v_fmt.fmt.pix, &mbus_fmt); - ctx->v_fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - ctx->v_fmt.fmt.pix.pixelformat = fmt->fourcc; - cal_calc_format_size(ctx, fmt, &ctx->v_fmt); - ctx->fmt = fmt; - ctx->m_fmt = mbus_fmt; - *f = ctx->v_fmt; - - return 0; -} - -static int cal_enum_framesizes(struct file *file, void *fh, - struct v4l2_frmsizeenum *fsize) -{ - struct cal_ctx *ctx = video_drvdata(file); - const struct cal_fmt *fmt; - struct v4l2_subdev_frame_size_enum fse; - int ret; - - /* check for valid format */ - fmt = find_format_by_pix(ctx, fsize->pixel_format); - if (!fmt) { - ctx_dbg(3, ctx, "Invalid pixel code: %x\n", - fsize->pixel_format); - return -EINVAL; - } - - fse.index = fsize->index; - fse.pad = 0; - fse.code = fmt->code; - fse.which = V4L2_SUBDEV_FORMAT_ACTIVE; - - ret = v4l2_subdev_call(ctx->phy->sensor, pad, enum_frame_size, NULL, - &fse); - if (ret) - return ret; - - ctx_dbg(1, ctx, "%s: index: %d code: %x W:[%d,%d] H:[%d,%d]\n", - __func__, fse.index, fse.code, fse.min_width, fse.max_width, - fse.min_height, fse.max_height); - - fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE; - fsize->discrete.width = fse.max_width; - fsize->discrete.height = fse.max_height; - - return 0; -} - -static int cal_enum_input(struct file *file, void *priv, - struct v4l2_input *inp) -{ - if (inp->index > 0) - return -EINVAL; - - inp->type = V4L2_INPUT_TYPE_CAMERA; - sprintf(inp->name, "Camera %u", inp->index); - return 0; -} - -static int cal_g_input(struct file *file, void *priv, unsigned int *i) -{ - *i = 0; - return 0; -} - -static int cal_s_input(struct file *file, void *priv, unsigned int i) -{ - return i > 0 ? -EINVAL : 0; -} - -/* timeperframe is arbitrary and continuous */ -static int cal_enum_frameintervals(struct file *file, void *priv, - struct v4l2_frmivalenum *fival) -{ - struct cal_ctx *ctx = video_drvdata(file); - const struct cal_fmt *fmt; - struct v4l2_subdev_frame_interval_enum fie = { - .index = fival->index, - .width = fival->width, - .height = fival->height, - .which = V4L2_SUBDEV_FORMAT_ACTIVE, - }; - int ret; - - fmt = find_format_by_pix(ctx, fival->pixel_format); - if (!fmt) - return -EINVAL; - - fie.code = fmt->code; - ret = v4l2_subdev_call(ctx->phy->sensor, pad, enum_frame_interval, - NULL, &fie); - if (ret) - return ret; - fival->type = V4L2_FRMIVAL_TYPE_DISCRETE; - fival->discrete = fie.interval; - - return 0; -} - -static const struct v4l2_file_operations cal_fops = { - .owner = THIS_MODULE, - .open = v4l2_fh_open, - .release = vb2_fop_release, - .read = vb2_fop_read, - .poll = vb2_fop_poll, - .unlocked_ioctl = video_ioctl2, /* V4L2 ioctl handler */ - .mmap = vb2_fop_mmap, -}; - -static const struct v4l2_ioctl_ops cal_ioctl_ops = { - .vidioc_querycap = cal_querycap, - .vidioc_enum_fmt_vid_cap = cal_enum_fmt_vid_cap, - .vidioc_g_fmt_vid_cap = cal_g_fmt_vid_cap, - .vidioc_try_fmt_vid_cap = cal_try_fmt_vid_cap, - .vidioc_s_fmt_vid_cap = cal_s_fmt_vid_cap, - .vidioc_enum_framesizes = cal_enum_framesizes, - .vidioc_reqbufs = vb2_ioctl_reqbufs, - .vidioc_create_bufs = vb2_ioctl_create_bufs, - .vidioc_prepare_buf = vb2_ioctl_prepare_buf, - .vidioc_querybuf = vb2_ioctl_querybuf, - .vidioc_qbuf = vb2_ioctl_qbuf, - .vidioc_dqbuf = vb2_ioctl_dqbuf, - .vidioc_expbuf = vb2_ioctl_expbuf, - .vidioc_enum_input = cal_enum_input, - .vidioc_g_input = cal_g_input, - .vidioc_s_input = cal_s_input, - .vidioc_enum_frameintervals = cal_enum_frameintervals, - .vidioc_streamon = vb2_ioctl_streamon, - .vidioc_streamoff = vb2_ioctl_streamoff, - .vidioc_log_status = v4l2_ctrl_log_status, - .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, - .vidioc_unsubscribe_event = v4l2_event_unsubscribe, -}; - -/* ------------------------------------------------------------------ - * videobuf2 Operations - * ------------------------------------------------------------------ - */ - -static int cal_queue_setup(struct vb2_queue *vq, - unsigned int *nbuffers, unsigned int *nplanes, - unsigned int sizes[], struct device *alloc_devs[]) -{ - struct cal_ctx *ctx = vb2_get_drv_priv(vq); - unsigned int size = ctx->v_fmt.fmt.pix.sizeimage; - - if (vq->num_buffers + *nbuffers < 3) - *nbuffers = 3 - vq->num_buffers; - - if (*nplanes) { - if (sizes[0] < size) - return -EINVAL; - size = sizes[0]; - } - - *nplanes = 1; - sizes[0] = size; - - ctx_dbg(3, ctx, "nbuffers=%d, size=%d\n", *nbuffers, sizes[0]); - - return 0; -} - -static int cal_buffer_prepare(struct vb2_buffer *vb) -{ - struct cal_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); - struct cal_buffer *buf = container_of(vb, struct cal_buffer, - vb.vb2_buf); - unsigned long size; - - if (WARN_ON(!ctx->fmt)) - return -EINVAL; - - size = ctx->v_fmt.fmt.pix.sizeimage; - if (vb2_plane_size(vb, 0) < size) { - ctx_err(ctx, - "data will not fit into plane (%lu < %lu)\n", - vb2_plane_size(vb, 0), size); - return -EINVAL; - } - - vb2_set_plane_payload(&buf->vb.vb2_buf, 0, size); - return 0; -} - -static void cal_buffer_queue(struct vb2_buffer *vb) -{ - struct cal_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); - struct cal_buffer *buf = container_of(vb, struct cal_buffer, - vb.vb2_buf); - struct cal_dmaqueue *vidq = &ctx->vidq; - unsigned long flags; - - /* recheck locking */ - spin_lock_irqsave(&ctx->slock, flags); - list_add_tail(&buf->list, &vidq->active); - spin_unlock_irqrestore(&ctx->slock, flags); -} - -static int cal_start_streaming(struct vb2_queue *vq, unsigned int count) -{ - struct cal_ctx *ctx = vb2_get_drv_priv(vq); - struct cal_dmaqueue *dma_q = &ctx->vidq; - struct cal_buffer *buf, *tmp; - unsigned long addr; - unsigned long flags; - int ret; - - spin_lock_irqsave(&ctx->slock, flags); - if (list_empty(&dma_q->active)) { - spin_unlock_irqrestore(&ctx->slock, flags); - ctx_dbg(3, ctx, "buffer queue is empty\n"); - return -EIO; - } - - buf = list_entry(dma_q->active.next, struct cal_buffer, list); - ctx->cur_frm = buf; - ctx->next_frm = buf; - list_del(&buf->list); - spin_unlock_irqrestore(&ctx->slock, flags); - - addr = vb2_dma_contig_plane_dma_addr(&ctx->cur_frm->vb.vb2_buf, 0); - ctx->sequence = 0; - - pm_runtime_get_sync(ctx->cal->dev); - - cal_ctx_csi2_config(ctx); - cal_ctx_pix_proc_config(ctx); - cal_ctx_wr_dma_config(ctx, ctx->v_fmt.fmt.pix.bytesperline, - ctx->v_fmt.fmt.pix.height); - - cal_camerarx_enable_irqs(ctx->phy); - - ret = cal_camerarx_start(ctx->phy, ctx->fmt); - if (ret) - goto err; - - cal_ctx_wr_dma_addr(ctx, addr); - cal_camerarx_ppi_enable(ctx->phy); - - if (debug >= 4) - cal_quickdump_regs(ctx->cal); - - return 0; - -err: - spin_lock_irqsave(&ctx->slock, flags); - vb2_buffer_done(&ctx->cur_frm->vb.vb2_buf, VB2_BUF_STATE_QUEUED); - ctx->cur_frm = NULL; - ctx->next_frm = NULL; - list_for_each_entry_safe(buf, tmp, &dma_q->active, list) { - list_del(&buf->list); - vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_QUEUED); - } - spin_unlock_irqrestore(&ctx->slock, flags); - return ret; -} - -static void cal_stop_streaming(struct vb2_queue *vq) -{ - struct cal_ctx *ctx = vb2_get_drv_priv(vq); - struct cal_dmaqueue *dma_q = &ctx->vidq; - struct cal_buffer *buf, *tmp; - unsigned long timeout; - unsigned long flags; - bool dma_act; - - cal_camerarx_ppi_disable(ctx->phy); - - /* wait for stream and dma to finish */ - dma_act = true; - timeout = jiffies + msecs_to_jiffies(500); - while (dma_act && time_before(jiffies, timeout)) { - msleep(50); - - spin_lock_irqsave(&ctx->slock, flags); - dma_act = ctx->dma_act; - spin_unlock_irqrestore(&ctx->slock, flags); - } - - if (dma_act) - ctx_err(ctx, "failed to disable dma cleanly\n"); - - cal_camerarx_disable_irqs(ctx->phy); - cal_camerarx_stop(ctx->phy); - - /* Release all active buffers */ - spin_lock_irqsave(&ctx->slock, flags); - list_for_each_entry_safe(buf, tmp, &dma_q->active, list) { - list_del(&buf->list); - vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR); - } - - if (ctx->cur_frm == ctx->next_frm) { - vb2_buffer_done(&ctx->cur_frm->vb.vb2_buf, VB2_BUF_STATE_ERROR); - } else { - vb2_buffer_done(&ctx->cur_frm->vb.vb2_buf, VB2_BUF_STATE_ERROR); - vb2_buffer_done(&ctx->next_frm->vb.vb2_buf, - VB2_BUF_STATE_ERROR); - } - ctx->cur_frm = NULL; - ctx->next_frm = NULL; - spin_unlock_irqrestore(&ctx->slock, flags); - - pm_runtime_put_sync(ctx->cal->dev); -} - -static const struct vb2_ops cal_video_qops = { - .queue_setup = cal_queue_setup, - .buf_prepare = cal_buffer_prepare, - .buf_queue = cal_buffer_queue, - .start_streaming = cal_start_streaming, - .stop_streaming = cal_stop_streaming, - .wait_prepare = vb2_ops_wait_prepare, - .wait_finish = vb2_ops_wait_finish, -}; - -/* ------------------------------------------------------------------ - * V4L2 Initialization and Registration - * ------------------------------------------------------------------ - */ - -static const struct video_device cal_videodev = { - .name = CAL_MODULE_NAME, - .fops = &cal_fops, - .ioctl_ops = &cal_ioctl_ops, - .minor = -1, - .release = video_device_release_empty, - .device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING | - V4L2_CAP_READWRITE, -}; - -static int cal_ctx_v4l2_init_formats(struct cal_ctx *ctx) -{ - struct v4l2_subdev_mbus_code_enum mbus_code; - struct v4l2_mbus_framefmt mbus_fmt; - const struct cal_fmt *fmt; - unsigned int i, j, k; - int ret = 0; - - /* Enumerate sub device formats and enable all matching local formats */ - ctx->active_fmt = devm_kcalloc(ctx->cal->dev, ARRAY_SIZE(cal_formats), - sizeof(*ctx->active_fmt), GFP_KERNEL); - if (!ctx->active_fmt) - return -ENOMEM; - - ctx->num_active_fmt = 0; - - for (j = 0, i = 0; ret != -EINVAL; ++j) { - - memset(&mbus_code, 0, sizeof(mbus_code)); - mbus_code.index = j; - mbus_code.which = V4L2_SUBDEV_FORMAT_ACTIVE; - ret = v4l2_subdev_call(ctx->phy->sensor, pad, enum_mbus_code, - NULL, &mbus_code); - if (ret) - continue; - - ctx_dbg(2, ctx, - "subdev %s: code: %04x idx: %u\n", - ctx->phy->sensor->name, mbus_code.code, j); - - for (k = 0; k < ARRAY_SIZE(cal_formats); k++) { - const struct cal_fmt *fmt = &cal_formats[k]; - - if (mbus_code.code == fmt->code) { - ctx->active_fmt[i] = fmt; - ctx_dbg(2, ctx, - "matched fourcc: %s: code: %04x idx: %u\n", - fourcc_to_str(fmt->fourcc), - fmt->code, i); - ctx->num_active_fmt = ++i; - } - } - } - - if (i == 0) { - ctx_err(ctx, "No suitable format reported by subdev %s\n", - ctx->phy->sensor->name); - return -EINVAL; - } - - ret = __subdev_get_format(ctx, &mbus_fmt); - if (ret) - return ret; - - fmt = find_format_by_code(ctx, mbus_fmt.code); - if (!fmt) { - ctx_dbg(3, ctx, "mbus code format (0x%08x) not found.\n", - mbus_fmt.code); - return -EINVAL; - } - - /* Save current subdev format */ - v4l2_fill_pix_format(&ctx->v_fmt.fmt.pix, &mbus_fmt); - ctx->v_fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - ctx->v_fmt.fmt.pix.pixelformat = fmt->fourcc; - cal_calc_format_size(ctx, fmt, &ctx->v_fmt); - ctx->fmt = fmt; - ctx->m_fmt = mbus_fmt; - - return 0; -} - -static int cal_ctx_v4l2_register(struct cal_ctx *ctx) -{ - struct v4l2_ctrl_handler *hdl = &ctx->ctrl_handler; - struct video_device *vfd = &ctx->vdev; - int ret; - - ret = cal_ctx_v4l2_init_formats(ctx); - if (ret) - return ret; - - ret = v4l2_ctrl_add_handler(hdl, ctx->phy->sensor->ctrl_handler, NULL, - true); - if (ret < 0) { - ctx_err(ctx, "Failed to add sensor ctrl handler\n"); - return ret; - } - - ret = video_register_device(vfd, VFL_TYPE_VIDEO, video_nr); - if (ret < 0) { - ctx_err(ctx, "Failed to register video device\n"); - return ret; - } - - ctx_info(ctx, "V4L2 device registered as %s\n", - video_device_node_name(vfd)); - - return 0; -} - -static void cal_ctx_v4l2_unregister(struct cal_ctx *ctx) -{ - ctx_dbg(1, ctx, "unregistering %s\n", - video_device_node_name(&ctx->vdev)); - - video_unregister_device(&ctx->vdev); -} - -static int cal_ctx_v4l2_init(struct cal_ctx *ctx) -{ - struct v4l2_ctrl_handler *hdl = &ctx->ctrl_handler; - struct video_device *vfd = &ctx->vdev; - struct vb2_queue *q = &ctx->vb_vidq; - int ret; - - INIT_LIST_HEAD(&ctx->vidq.active); - spin_lock_init(&ctx->slock); - mutex_init(&ctx->mutex); - - /* Initialize the vb2 queue. */ - q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - q->io_modes = VB2_MMAP | VB2_DMABUF | VB2_READ; - q->drv_priv = ctx; - q->buf_struct_size = sizeof(struct cal_buffer); - q->ops = &cal_video_qops; - q->mem_ops = &vb2_dma_contig_memops; - q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; - q->lock = &ctx->mutex; - q->min_buffers_needed = 3; - q->dev = ctx->cal->dev; - - ret = vb2_queue_init(q); - if (ret) - return ret; - - /* Initialize the video device and media entity. */ - *vfd = cal_videodev; - vfd->v4l2_dev = &ctx->cal->v4l2_dev; - vfd->queue = q; - snprintf(vfd->name, sizeof(vfd->name), "CAL output %u", ctx->index); - vfd->lock = &ctx->mutex; - video_set_drvdata(vfd, ctx); - - ctx->pad.flags = MEDIA_PAD_FL_SINK; - ret = media_entity_pads_init(&vfd->entity, 1, &ctx->pad); - if (ret < 0) - return ret; - - /* Initialize the control handler. */ - ret = v4l2_ctrl_handler_init(hdl, 11); - if (ret < 0) { - ctx_err(ctx, "Failed to init ctrl handler\n"); - goto error; - } - - vfd->ctrl_handler = hdl; - - return 0; - -error: - media_entity_cleanup(&vfd->entity); - return ret; -} - -static void cal_ctx_v4l2_cleanup(struct cal_ctx *ctx) -{ - v4l2_ctrl_handler_free(&ctx->ctrl_handler); - media_entity_cleanup(&ctx->vdev.entity); -} - /* ------------------------------------------------------------------ * Asynchronous V4L2 subdev binding * ------------------------------------------------------------------ diff --git a/drivers/media/platform/ti-vpe/cal.h b/drivers/media/platform/ti-vpe/cal.h new file mode 100644 index 0000000000000..383fa9295048d --- /dev/null +++ b/drivers/media/platform/ti-vpe/cal.h @@ -0,0 +1,226 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * TI Camera Access Layer (CAL) + * + * Copyright (c) 2015-2020 Texas Instruments Inc. + * + * Authors: + * Benoit Parrot + * Laurent Pinchart + */ +#ifndef __TI_CAL_H__ +#define __TI_CAL_H__ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#define CAL_MODULE_NAME "cal" +#define CAL_NUM_CONTEXT 2 +#define CAL_NUM_CSI2_PORTS 2 + +#define MAX_WIDTH_BYTES (8192 * 8) +#define MAX_HEIGHT_LINES 16383 + +struct device; +struct device_node; +struct resource; +struct regmap; +struct regmap_fied; +struct v4l2_subdev; + +/* CTRL_CORE_CAMERRX_CONTROL register field id */ +enum cal_camerarx_field { + F_CTRLCLKEN, + F_CAMMODE, + F_LANEENABLE, + F_CSI_MODE, + F_MAX_FIELDS, +}; + +struct cal_fmt { + u32 fourcc; + u32 code; + /* Bits per pixel */ + u8 bpp; +}; + +/* buffer for one video frame */ +struct cal_buffer { + /* common v4l buffer stuff -- must be first */ + struct vb2_v4l2_buffer vb; + struct list_head list; +}; + +struct cal_dmaqueue { + struct list_head active; +}; + +struct cal_camerarx_data { + struct { + unsigned int lsb; + unsigned int msb; + } fields[F_MAX_FIELDS]; + unsigned int num_lanes; +}; + +struct cal_data { + const struct cal_camerarx_data *camerarx; + unsigned int num_csi2_phy; + unsigned int flags; +}; + +/* + * The Camera Adaptation Layer (CAL) module is paired with one or more complex + * I/O PHYs (CAMERARX). It contains multiple instances of CSI-2, processing and + * DMA contexts. + * + * The cal_dev structure represents the whole subsystem, including the CAL and + * the CAMERARX instances. Instances of struct cal_dev are named cal through the + * driver. + * + * The cal_camerarx structure represents one CAMERARX instance. Instances of + * cal_camerarx are named phy through the driver. + * + * The cal_ctx structure represents the combination of one CSI-2 context, one + * processing context and one DMA context. Instance of struct cal_ctx are named + * ctx through the driver. + */ + +struct cal_camerarx { + void __iomem *base; + struct resource *res; + struct device *dev; + struct regmap_field *fields[F_MAX_FIELDS]; + + struct cal_dev *cal; + unsigned int instance; + + struct v4l2_fwnode_endpoint endpoint; + struct device_node *sensor_node; + struct v4l2_subdev *sensor; +}; + +struct cal_dev { + struct clk *fclk; + int irq; + void __iomem *base; + struct resource *res; + struct device *dev; + + const struct cal_data *data; + u32 revision; + + /* Control Module handle */ + struct regmap *syscon_camerrx; + u32 syscon_camerrx_offset; + + /* Camera Core Module handle */ + struct cal_camerarx *phy[CAL_NUM_CSI2_PORTS]; + + struct cal_ctx *ctx[CAL_NUM_CONTEXT]; + + struct media_device mdev; + struct v4l2_device v4l2_dev; + struct v4l2_async_notifier notifier; +}; + +/* + * There is one cal_ctx structure for each camera core context. + */ +struct cal_ctx { + struct v4l2_ctrl_handler ctrl_handler; + struct video_device vdev; + struct media_pad pad; + + struct cal_dev *cal; + struct cal_camerarx *phy; + + /* v4l2_ioctl mutex */ + struct mutex mutex; + /* v4l2 buffers lock */ + spinlock_t slock; + + struct cal_dmaqueue vidq; + + /* video capture */ + const struct cal_fmt *fmt; + /* Used to store current pixel format */ + struct v4l2_format v_fmt; + /* Used to store current mbus frame format */ + struct v4l2_mbus_framefmt m_fmt; + + /* Current subdev enumerated format */ + const struct cal_fmt **active_fmt; + unsigned int num_active_fmt; + + unsigned int sequence; + struct vb2_queue vb_vidq; + unsigned int index; + unsigned int cport; + + /* Pointer pointing to current v4l2_buffer */ + struct cal_buffer *cur_frm; + /* Pointer pointing to next v4l2_buffer */ + struct cal_buffer *next_frm; + + bool dma_act; +}; + +extern unsigned int cal_debug; +extern int cal_video_nr; + +#define cal_dbg(level, cal, fmt, arg...) \ + do { \ + if (cal_debug >= (level)) \ + dev_printk(KERN_DEBUG, (cal)->dev, fmt, ##arg); \ + } while (0) +#define cal_info(cal, fmt, arg...) \ + dev_info((cal)->dev, fmt, ##arg) +#define cal_err(cal, fmt, arg...) \ + dev_err((cal)->dev, fmt, ##arg) + +#define ctx_dbg(level, ctx, fmt, arg...) \ + cal_dbg(level, (ctx)->cal, "ctx%u: " fmt, (ctx)->index, ##arg) +#define ctx_info(ctx, fmt, arg...) \ + cal_info((ctx)->cal, "ctx%u: " fmt, (ctx)->index, ##arg) +#define ctx_err(ctx, fmt, arg...) \ + cal_err((ctx)->cal, "ctx%u: " fmt, (ctx)->index, ##arg) + +#define phy_dbg(level, phy, fmt, arg...) \ + cal_dbg(level, (phy)->cal, "phy%u: " fmt, (phy)->instance, ##arg) +#define phy_info(phy, fmt, arg...) \ + cal_info((phy)->cal, "phy%u: " fmt, (phy)->instance, ##arg) +#define phy_err(phy, fmt, arg...) \ + cal_err((phy)->cal, "phy%u: " fmt, (phy)->instance, ##arg) + +void cal_quickdump_regs(struct cal_dev *cal); + +int cal_camerarx_start(struct cal_camerarx *phy, const struct cal_fmt *fmt); +void cal_camerarx_stop(struct cal_camerarx *phy); +void cal_camerarx_enable_irqs(struct cal_camerarx *phy); +void cal_camerarx_disable_irqs(struct cal_camerarx *phy); +void cal_camerarx_ppi_enable(struct cal_camerarx *phy); +void cal_camerarx_ppi_disable(struct cal_camerarx *phy); + +void cal_ctx_csi2_config(struct cal_ctx *ctx); +void cal_ctx_pix_proc_config(struct cal_ctx *ctx); +void cal_ctx_wr_dma_config(struct cal_ctx *ctx, unsigned int width, + unsigned int height); +void cal_ctx_wr_dma_addr(struct cal_ctx *ctx, unsigned int dmaaddr); + +int cal_ctx_v4l2_register(struct cal_ctx *ctx); +void cal_ctx_v4l2_unregister(struct cal_ctx *ctx); +int cal_ctx_v4l2_init(struct cal_ctx *ctx); +void cal_ctx_v4l2_cleanup(struct cal_ctx *ctx); + +#endif /* __TI_CAL_H__ */ -- GitLab From d373018f3abda9d2446a059133cd6425f56b3b51 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 6 Jul 2020 20:36:44 +0200 Subject: [PATCH 0616/1476] media: ti-vpe: cal: Move CAL I/O accessors to cal.h To prepare for the split of the camerarx code to a separate file, move the CAL I/O accessors to cal.h. This requires renaming the accessors with a cal_prefix, as the current names are too generic and prone to namespace clashes. The reg_read() and read_write() macros, that cover both CAL and CAMERARX register access, are split in two groups of inline functions, one for CAL access and one for CAMERARX access. Signed-off-by: Laurent Pinchart Reviewed-by: Benoit Parrot Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/ti-vpe/cal-video.c | 1 - drivers/media/platform/ti-vpe/cal.c | 282 ++++++++++------------ drivers/media/platform/ti-vpe/cal.h | 36 +++ 3 files changed, 169 insertions(+), 150 deletions(-) diff --git a/drivers/media/platform/ti-vpe/cal-video.c b/drivers/media/platform/ti-vpe/cal-video.c index 82b32a2f9db5f..df472a175e839 100644 --- a/drivers/media/platform/ti-vpe/cal-video.c +++ b/drivers/media/platform/ti-vpe/cal-video.c @@ -9,7 +9,6 @@ * Laurent Pinchart */ -#include #include #include #include diff --git a/drivers/media/platform/ti-vpe/cal.c b/drivers/media/platform/ti-vpe/cal.c index e81c8507893b0..c2252ed36d68c 100644 --- a/drivers/media/platform/ti-vpe/cal.c +++ b/drivers/media/platform/ti-vpe/cal.c @@ -9,11 +9,9 @@ * Laurent Pinchart */ -#include #include #include #include -#include #include #include #include @@ -133,33 +131,6 @@ static const struct cal_data am654_cal_data = { * ------------------------------------------------------------------ */ -#define reg_read(dev, offset) ioread32(dev->base + offset) -#define reg_write(dev, offset, val) iowrite32(val, dev->base + offset) - -static inline u32 reg_read_field(struct cal_dev *cal, u32 offset, u32 mask) -{ - return FIELD_GET(mask, reg_read(cal, offset)); -} - -static inline void reg_write_field(struct cal_dev *cal, u32 offset, u32 value, - u32 mask) -{ - u32 val = reg_read(cal, offset); - - val &= ~mask; - val |= FIELD_PREP(mask, value); - reg_write(cal, offset, val); -} - -static inline void set_field(u32 *valp, u32 field, u32 mask) -{ - u32 val = *valp; - - val &= ~mask; - val |= (field << __ffs(mask)) & mask; - *valp = val; -} - void cal_quickdump_regs(struct cal_dev *cal) { unsigned int i; @@ -189,6 +160,16 @@ void cal_quickdump_regs(struct cal_dev *cal) * ------------------------------------------------------------------ */ +static inline u32 camerarx_read(struct cal_camerarx *phy, u32 offset) +{ + return ioread32(phy->base + offset); +} + +static inline void camerarx_write(struct cal_camerarx *phy, u32 offset, u32 val) +{ + iowrite32(val, phy->base + offset); +} + static s64 cal_camerarx_get_external_rate(struct cal_camerarx *phy) { struct v4l2_ctrl *ctrl; @@ -209,15 +190,15 @@ static s64 cal_camerarx_get_external_rate(struct cal_camerarx *phy) static void cal_camerarx_lane_config(struct cal_camerarx *phy) { - u32 val = reg_read(phy->cal, CAL_CSI2_COMPLEXIO_CFG(phy->instance)); + u32 val = cal_read(phy->cal, CAL_CSI2_COMPLEXIO_CFG(phy->instance)); u32 lane_mask = CAL_CSI2_COMPLEXIO_CFG_CLOCK_POSITION_MASK; u32 polarity_mask = CAL_CSI2_COMPLEXIO_CFG_CLOCK_POL_MASK; struct v4l2_fwnode_bus_mipi_csi2 *mipi_csi2 = &phy->endpoint.bus.mipi_csi2; int lane; - set_field(&val, mipi_csi2->clock_lane + 1, lane_mask); - set_field(&val, mipi_csi2->lane_polarities[0], polarity_mask); + cal_set_field(&val, mipi_csi2->clock_lane + 1, lane_mask); + cal_set_field(&val, mipi_csi2->lane_polarities[0], polarity_mask); for (lane = 0; lane < mipi_csi2->num_data_lanes; lane++) { /* * Every lane are one nibble apart starting with the @@ -225,12 +206,12 @@ static void cal_camerarx_lane_config(struct cal_camerarx *phy) */ lane_mask <<= 4; polarity_mask <<= 4; - set_field(&val, mipi_csi2->data_lanes[lane] + 1, lane_mask); - set_field(&val, mipi_csi2->lane_polarities[lane + 1], - polarity_mask); + cal_set_field(&val, mipi_csi2->data_lanes[lane] + 1, lane_mask); + cal_set_field(&val, mipi_csi2->lane_polarities[lane + 1], + polarity_mask); } - reg_write(phy->cal, CAL_CSI2_COMPLEXIO_CFG(phy->instance), val); + cal_write(phy->cal, CAL_CSI2_COMPLEXIO_CFG(phy->instance), val); phy_dbg(3, phy, "CAL_CSI2_COMPLEXIO_CFG(%d) = 0x%08x\n", phy->instance, val); } @@ -291,23 +272,24 @@ static void cal_camerarx_config(struct cal_camerarx *phy, s64 external_rate, ths_settle = (105 * csi2_ddrclk_khz / 1000000) + 4; phy_dbg(1, phy, "ths_settle: %d (0x%02x)\n", ths_settle, ths_settle); - reg0 = reg_read(phy, CAL_CSI2_PHY_REG0); - set_field(®0, CAL_CSI2_PHY_REG0_HSCLOCKCONFIG_DISABLE, - CAL_CSI2_PHY_REG0_HSCLOCKCONFIG_MASK); - set_field(®0, ths_term, CAL_CSI2_PHY_REG0_THS_TERM_MASK); - set_field(®0, ths_settle, CAL_CSI2_PHY_REG0_THS_SETTLE_MASK); + reg0 = camerarx_read(phy, CAL_CSI2_PHY_REG0); + cal_set_field(®0, CAL_CSI2_PHY_REG0_HSCLOCKCONFIG_DISABLE, + CAL_CSI2_PHY_REG0_HSCLOCKCONFIG_MASK); + cal_set_field(®0, ths_term, CAL_CSI2_PHY_REG0_THS_TERM_MASK); + cal_set_field(®0, ths_settle, CAL_CSI2_PHY_REG0_THS_SETTLE_MASK); phy_dbg(1, phy, "CSI2_%d_REG0 = 0x%08x\n", phy->instance, reg0); - reg_write(phy, CAL_CSI2_PHY_REG0, reg0); + camerarx_write(phy, CAL_CSI2_PHY_REG0, reg0); - reg1 = reg_read(phy, CAL_CSI2_PHY_REG1); - set_field(®1, TCLK_TERM, CAL_CSI2_PHY_REG1_TCLK_TERM_MASK); - set_field(®1, 0xb8, CAL_CSI2_PHY_REG1_DPHY_HS_SYNC_PATTERN_MASK); - set_field(®1, TCLK_MISS, CAL_CSI2_PHY_REG1_CTRLCLK_DIV_FACTOR_MASK); - set_field(®1, TCLK_SETTLE, CAL_CSI2_PHY_REG1_TCLK_SETTLE_MASK); + reg1 = camerarx_read(phy, CAL_CSI2_PHY_REG1); + cal_set_field(®1, TCLK_TERM, CAL_CSI2_PHY_REG1_TCLK_TERM_MASK); + cal_set_field(®1, 0xb8, CAL_CSI2_PHY_REG1_DPHY_HS_SYNC_PATTERN_MASK); + cal_set_field(®1, TCLK_MISS, + CAL_CSI2_PHY_REG1_CTRLCLK_DIV_FACTOR_MASK); + cal_set_field(®1, TCLK_SETTLE, CAL_CSI2_PHY_REG1_TCLK_SETTLE_MASK); phy_dbg(1, phy, "CSI2_%d_REG1 = 0x%08x\n", phy->instance, reg1); - reg_write(phy, CAL_CSI2_PHY_REG1, reg1); + camerarx_write(phy, CAL_CSI2_PHY_REG1, reg1); } static void cal_camerarx_power(struct cal_camerarx *phy, bool enable) @@ -318,13 +300,13 @@ static void cal_camerarx_power(struct cal_camerarx *phy, bool enable) target_state = enable ? CAL_CSI2_COMPLEXIO_CFG_PWR_CMD_STATE_ON : CAL_CSI2_COMPLEXIO_CFG_PWR_CMD_STATE_OFF; - reg_write_field(phy->cal, CAL_CSI2_COMPLEXIO_CFG(phy->instance), + cal_write_field(phy->cal, CAL_CSI2_COMPLEXIO_CFG(phy->instance), target_state, CAL_CSI2_COMPLEXIO_CFG_PWR_CMD_MASK); for (i = 0; i < 10; i++) { u32 current_state; - current_state = reg_read_field(phy->cal, + current_state = cal_read_field(phy->cal, CAL_CSI2_COMPLEXIO_CFG(phy->instance), CAL_CSI2_COMPLEXIO_CFG_PWR_STATUS_MASK); @@ -345,7 +327,7 @@ static void cal_camerarx_wait_reset(struct cal_camerarx *phy) timeout = jiffies + msecs_to_jiffies(750); while (time_before(jiffies, timeout)) { - if (reg_read_field(phy->cal, + if (cal_read_field(phy->cal, CAL_CSI2_COMPLEXIO_CFG(phy->instance), CAL_CSI2_COMPLEXIO_CFG_RESET_DONE_MASK) == CAL_CSI2_COMPLEXIO_CFG_RESET_DONE_RESETCOMPLETED) @@ -353,7 +335,7 @@ static void cal_camerarx_wait_reset(struct cal_camerarx *phy) usleep_range(500, 5000); } - if (reg_read_field(phy->cal, CAL_CSI2_COMPLEXIO_CFG(phy->instance), + if (cal_read_field(phy->cal, CAL_CSI2_COMPLEXIO_CFG(phy->instance), CAL_CSI2_COMPLEXIO_CFG_RESET_DONE_MASK) != CAL_CSI2_COMPLEXIO_CFG_RESET_DONE_RESETCOMPLETED) phy_err(phy, "Timeout waiting for Complex IO reset done\n"); @@ -365,14 +347,14 @@ static void cal_camerarx_wait_stop_state(struct cal_camerarx *phy) timeout = jiffies + msecs_to_jiffies(750); while (time_before(jiffies, timeout)) { - if (reg_read_field(phy->cal, + if (cal_read_field(phy->cal, CAL_CSI2_TIMING(phy->instance), CAL_CSI2_TIMING_FORCE_RX_MODE_IO1_MASK) == 0) break; usleep_range(500, 5000); } - if (reg_read_field(phy->cal, CAL_CSI2_TIMING(phy->instance), + if (cal_read_field(phy->cal, CAL_CSI2_TIMING(phy->instance), CAL_CSI2_TIMING_FORCE_RX_MODE_IO1_MASK) != 0) phy_err(phy, "Timeout waiting for stop state\n"); } @@ -421,15 +403,15 @@ int cal_camerarx_start(struct cal_camerarx *phy, const struct cal_fmt *fmt) * at this point, as it requires the external sensor to send the * CSI-2 HS clock. */ - reg_write_field(phy->cal, CAL_CSI2_COMPLEXIO_CFG(phy->instance), + cal_write_field(phy->cal, CAL_CSI2_COMPLEXIO_CFG(phy->instance), CAL_CSI2_COMPLEXIO_CFG_RESET_CTRL_OPERATIONAL, CAL_CSI2_COMPLEXIO_CFG_RESET_CTRL_MASK); phy_dbg(3, phy, "CAL_CSI2_COMPLEXIO_CFG(%d) = 0x%08x De-assert Complex IO Reset\n", phy->instance, - reg_read(phy->cal, CAL_CSI2_COMPLEXIO_CFG(phy->instance))); + cal_read(phy->cal, CAL_CSI2_COMPLEXIO_CFG(phy->instance))); /* Dummy read to allow SCP reset to complete. */ - reg_read(phy, CAL_CSI2_PHY_REG0); + camerarx_read(phy, CAL_CSI2_PHY_REG0); /* Program the PHY timing parameters. */ cal_camerarx_config(phy, external_rate, fmt); @@ -446,21 +428,22 @@ int cal_camerarx_start(struct cal_camerarx *phy, const struct cal_fmt *fmt) */ sscounter = DIV_ROUND_UP(clk_get_rate(phy->cal->fclk), 10000 * 16 * 4); - val = reg_read(phy->cal, CAL_CSI2_TIMING(phy->instance)); - set_field(&val, 1, CAL_CSI2_TIMING_STOP_STATE_X16_IO1_MASK); - set_field(&val, 1, CAL_CSI2_TIMING_STOP_STATE_X4_IO1_MASK); - set_field(&val, sscounter, CAL_CSI2_TIMING_STOP_STATE_COUNTER_IO1_MASK); - reg_write(phy->cal, CAL_CSI2_TIMING(phy->instance), val); + val = cal_read(phy->cal, CAL_CSI2_TIMING(phy->instance)); + cal_set_field(&val, 1, CAL_CSI2_TIMING_STOP_STATE_X16_IO1_MASK); + cal_set_field(&val, 1, CAL_CSI2_TIMING_STOP_STATE_X4_IO1_MASK); + cal_set_field(&val, sscounter, + CAL_CSI2_TIMING_STOP_STATE_COUNTER_IO1_MASK); + cal_write(phy->cal, CAL_CSI2_TIMING(phy->instance), val); phy_dbg(3, phy, "CAL_CSI2_TIMING(%d) = 0x%08x Stop States\n", phy->instance, - reg_read(phy->cal, CAL_CSI2_TIMING(phy->instance))); + cal_read(phy->cal, CAL_CSI2_TIMING(phy->instance))); /* Assert the FORCERXMODE signal. */ - reg_write_field(phy->cal, CAL_CSI2_TIMING(phy->instance), + cal_write_field(phy->cal, CAL_CSI2_TIMING(phy->instance), 1, CAL_CSI2_TIMING_FORCE_RX_MODE_IO1_MASK); phy_dbg(3, phy, "CAL_CSI2_TIMING(%d) = 0x%08x Force RXMODE\n", phy->instance, - reg_read(phy->cal, CAL_CSI2_TIMING(phy->instance))); + cal_read(phy->cal, CAL_CSI2_TIMING(phy->instance))); /* * c. Connect pull-down on CSI-2 PHY link (using pad control). @@ -492,7 +475,7 @@ int cal_camerarx_start(struct cal_camerarx *phy, const struct cal_fmt *fmt) cal_camerarx_wait_stop_state(phy); phy_dbg(1, phy, "CSI2_%u_REG1 = 0x%08x (bits 31-28 should be set)\n", - phy->instance, reg_read(phy, CAL_CSI2_PHY_REG1)); + phy->instance, camerarx_read(phy, CAL_CSI2_PHY_REG1)); /* * g. Disable pull-down on CSI-2 PHY link (using pad control). @@ -512,13 +495,13 @@ void cal_camerarx_stop(struct cal_camerarx *phy) cal_camerarx_power(phy, false); /* Assert Complex IO Reset */ - reg_write_field(phy->cal, CAL_CSI2_COMPLEXIO_CFG(phy->instance), + cal_write_field(phy->cal, CAL_CSI2_COMPLEXIO_CFG(phy->instance), CAL_CSI2_COMPLEXIO_CFG_RESET_CTRL, CAL_CSI2_COMPLEXIO_CFG_RESET_CTRL_MASK); /* Wait for power down completion */ for (i = 0; i < 10; i++) { - if (reg_read_field(phy->cal, + if (cal_read_field(phy->cal, CAL_CSI2_COMPLEXIO_CFG(phy->instance), CAL_CSI2_COMPLEXIO_CFG_RESET_DONE_MASK) == CAL_CSI2_COMPLEXIO_CFG_RESET_DONE_RESETONGOING) @@ -527,7 +510,7 @@ void cal_camerarx_stop(struct cal_camerarx *phy) } phy_dbg(3, phy, "CAL_CSI2_COMPLEXIO_CFG(%d) = 0x%08x Complex IO in Reset (%d) %s\n", phy->instance, - reg_read(phy->cal, CAL_CSI2_COMPLEXIO_CFG(phy->instance)), i, + cal_read(phy->cal, CAL_CSI2_COMPLEXIO_CFG(phy->instance)), i, (i >= 10) ? "(timeout)" : ""); /* Disable the phy */ @@ -562,12 +545,12 @@ void cal_camerarx_stop(struct cal_camerarx *phy) */ static void cal_camerarx_i913_errata(struct cal_camerarx *phy) { - u32 reg10 = reg_read(phy, CAL_CSI2_PHY_REG10); + u32 reg10 = camerarx_read(phy, CAL_CSI2_PHY_REG10); - set_field(®10, 1, CAL_CSI2_PHY_REG10_I933_LDO_DISABLE_MASK); + cal_set_field(®10, 1, CAL_CSI2_PHY_REG10_I933_LDO_DISABLE_MASK); phy_dbg(1, phy, "CSI2_%d_REG10 = 0x%08x\n", phy->instance, reg10); - reg_write(phy, CAL_CSI2_PHY_REG10, reg10); + camerarx_write(phy, CAL_CSI2_PHY_REG10, reg10); } /* @@ -584,24 +567,24 @@ void cal_camerarx_enable_irqs(struct cal_camerarx *phy) CAL_CSI2_COMPLEXIO_IRQ_ECC_NO_CORRECTION_MASK; /* Enable CIO error irqs */ - reg_write(phy->cal, CAL_HL_IRQENABLE_SET(0), + cal_write(phy->cal, CAL_HL_IRQENABLE_SET(0), CAL_HL_IRQ_CIO_MASK(phy->instance)); - reg_write(phy->cal, CAL_CSI2_COMPLEXIO_IRQENABLE(phy->instance), + cal_write(phy->cal, CAL_CSI2_COMPLEXIO_IRQENABLE(phy->instance), cio_err_mask); /* Always enable OCPO error */ - reg_write(phy->cal, CAL_HL_IRQENABLE_SET(0), CAL_HL_IRQ_OCPO_ERR_MASK); + cal_write(phy->cal, CAL_HL_IRQENABLE_SET(0), CAL_HL_IRQ_OCPO_ERR_MASK); /* Enable IRQ_WDMA_END 0/1 */ val = 0; - set_field(&val, 1, CAL_HL_IRQ_MASK(phy->instance)); - reg_write(phy->cal, CAL_HL_IRQENABLE_SET(1), val); + cal_set_field(&val, 1, CAL_HL_IRQ_MASK(phy->instance)); + cal_write(phy->cal, CAL_HL_IRQENABLE_SET(1), val); /* Enable IRQ_WDMA_START 0/1 */ val = 0; - set_field(&val, 1, CAL_HL_IRQ_MASK(phy->instance)); - reg_write(phy->cal, CAL_HL_IRQENABLE_SET(2), val); + cal_set_field(&val, 1, CAL_HL_IRQ_MASK(phy->instance)); + cal_write(phy->cal, CAL_HL_IRQENABLE_SET(2), val); /* Todo: Add VC_IRQ and CSI2_COMPLEXIO_IRQ handling */ - reg_write(phy->cal, CAL_CSI2_VC_IRQENABLE(0), 0xFF000000); + cal_write(phy->cal, CAL_CSI2_VC_IRQENABLE(0), 0xFF000000); } void cal_camerarx_disable_irqs(struct cal_camerarx *phy) @@ -609,33 +592,33 @@ void cal_camerarx_disable_irqs(struct cal_camerarx *phy) u32 val; /* Disable CIO error irqs */ - reg_write(phy->cal, CAL_HL_IRQENABLE_CLR(0), + cal_write(phy->cal, CAL_HL_IRQENABLE_CLR(0), CAL_HL_IRQ_CIO_MASK(phy->instance)); - reg_write(phy->cal, CAL_CSI2_COMPLEXIO_IRQENABLE(phy->instance), + cal_write(phy->cal, CAL_CSI2_COMPLEXIO_IRQENABLE(phy->instance), 0); /* Disable IRQ_WDMA_END 0/1 */ val = 0; - set_field(&val, 1, CAL_HL_IRQ_MASK(phy->instance)); - reg_write(phy->cal, CAL_HL_IRQENABLE_CLR(1), val); + cal_set_field(&val, 1, CAL_HL_IRQ_MASK(phy->instance)); + cal_write(phy->cal, CAL_HL_IRQENABLE_CLR(1), val); /* Disable IRQ_WDMA_START 0/1 */ val = 0; - set_field(&val, 1, CAL_HL_IRQ_MASK(phy->instance)); - reg_write(phy->cal, CAL_HL_IRQENABLE_CLR(2), val); + cal_set_field(&val, 1, CAL_HL_IRQ_MASK(phy->instance)); + cal_write(phy->cal, CAL_HL_IRQENABLE_CLR(2), val); /* Todo: Add VC_IRQ and CSI2_COMPLEXIO_IRQ handling */ - reg_write(phy->cal, CAL_CSI2_VC_IRQENABLE(0), 0); + cal_write(phy->cal, CAL_CSI2_VC_IRQENABLE(0), 0); } void cal_camerarx_ppi_enable(struct cal_camerarx *phy) { - reg_write(phy->cal, CAL_CSI2_PPI_CTRL(phy->instance), BIT(3)); - reg_write_field(phy->cal, CAL_CSI2_PPI_CTRL(phy->instance), + cal_write(phy->cal, CAL_CSI2_PPI_CTRL(phy->instance), BIT(3)); + cal_write_field(phy->cal, CAL_CSI2_PPI_CTRL(phy->instance), 1, CAL_CSI2_PPI_CTRL_IF_EN_MASK); } void cal_camerarx_ppi_disable(struct cal_camerarx *phy) { - reg_write_field(phy->cal, CAL_CSI2_PPI_CTRL(phy->instance), + cal_write_field(phy->cal, CAL_CSI2_PPI_CTRL(phy->instance), 0, CAL_CSI2_PPI_CTRL_IF_EN_MASK); } @@ -857,8 +840,8 @@ void cal_ctx_csi2_config(struct cal_ctx *ctx) { u32 val; - val = reg_read(ctx->cal, CAL_CSI2_CTX0(ctx->index)); - set_field(&val, ctx->cport, CAL_CSI2_CTX_CPORT_MASK); + val = cal_read(ctx->cal, CAL_CSI2_CTX0(ctx->index)); + cal_set_field(&val, ctx->cport, CAL_CSI2_CTX_CPORT_MASK); /* * DT type: MIPI CSI-2 Specs * 0x1: All - DT filter is disabled @@ -867,15 +850,15 @@ void cal_ctx_csi2_config(struct cal_ctx *ctx) * 0x2A: RAW8 1 pixel = 1 byte * 0x1E: YUV422 2 pixels = 4 bytes */ - set_field(&val, 0x1, CAL_CSI2_CTX_DT_MASK); - set_field(&val, 0, CAL_CSI2_CTX_VC_MASK); - set_field(&val, ctx->v_fmt.fmt.pix.height, CAL_CSI2_CTX_LINES_MASK); - set_field(&val, CAL_CSI2_CTX_ATT_PIX, CAL_CSI2_CTX_ATT_MASK); - set_field(&val, CAL_CSI2_CTX_PACK_MODE_LINE, - CAL_CSI2_CTX_PACK_MODE_MASK); - reg_write(ctx->cal, CAL_CSI2_CTX0(ctx->index), val); + cal_set_field(&val, 0x1, CAL_CSI2_CTX_DT_MASK); + cal_set_field(&val, 0, CAL_CSI2_CTX_VC_MASK); + cal_set_field(&val, ctx->v_fmt.fmt.pix.height, CAL_CSI2_CTX_LINES_MASK); + cal_set_field(&val, CAL_CSI2_CTX_ATT_PIX, CAL_CSI2_CTX_ATT_MASK); + cal_set_field(&val, CAL_CSI2_CTX_PACK_MODE_LINE, + CAL_CSI2_CTX_PACK_MODE_MASK); + cal_write(ctx->cal, CAL_CSI2_CTX0(ctx->index), val); ctx_dbg(3, ctx, "CAL_CSI2_CTX0(%d) = 0x%08x\n", ctx->index, - reg_read(ctx->cal, CAL_CSI2_CTX0(ctx->index))); + cal_read(ctx->cal, CAL_CSI2_CTX0(ctx->index))); } void cal_ctx_pix_proc_config(struct cal_ctx *ctx) @@ -917,16 +900,16 @@ void cal_ctx_pix_proc_config(struct cal_ctx *ctx) break; } - val = reg_read(ctx->cal, CAL_PIX_PROC(ctx->index)); - set_field(&val, extract, CAL_PIX_PROC_EXTRACT_MASK); - set_field(&val, CAL_PIX_PROC_DPCMD_BYPASS, CAL_PIX_PROC_DPCMD_MASK); - set_field(&val, CAL_PIX_PROC_DPCME_BYPASS, CAL_PIX_PROC_DPCME_MASK); - set_field(&val, pack, CAL_PIX_PROC_PACK_MASK); - set_field(&val, ctx->cport, CAL_PIX_PROC_CPORT_MASK); - set_field(&val, 1, CAL_PIX_PROC_EN_MASK); - reg_write(ctx->cal, CAL_PIX_PROC(ctx->index), val); + val = cal_read(ctx->cal, CAL_PIX_PROC(ctx->index)); + cal_set_field(&val, extract, CAL_PIX_PROC_EXTRACT_MASK); + cal_set_field(&val, CAL_PIX_PROC_DPCMD_BYPASS, CAL_PIX_PROC_DPCMD_MASK); + cal_set_field(&val, CAL_PIX_PROC_DPCME_BYPASS, CAL_PIX_PROC_DPCME_MASK); + cal_set_field(&val, pack, CAL_PIX_PROC_PACK_MASK); + cal_set_field(&val, ctx->cport, CAL_PIX_PROC_CPORT_MASK); + cal_set_field(&val, 1, CAL_PIX_PROC_EN_MASK); + cal_write(ctx->cal, CAL_PIX_PROC(ctx->index), val); ctx_dbg(3, ctx, "CAL_PIX_PROC(%d) = 0x%08x\n", ctx->index, - reg_read(ctx->cal, CAL_PIX_PROC(ctx->index))); + cal_read(ctx->cal, CAL_PIX_PROC(ctx->index))); } void cal_ctx_wr_dma_config(struct cal_ctx *ctx, unsigned int width, @@ -934,58 +917,59 @@ void cal_ctx_wr_dma_config(struct cal_ctx *ctx, unsigned int width, { u32 val; - val = reg_read(ctx->cal, CAL_WR_DMA_CTRL(ctx->index)); - set_field(&val, ctx->cport, CAL_WR_DMA_CTRL_CPORT_MASK); - set_field(&val, height, CAL_WR_DMA_CTRL_YSIZE_MASK); - set_field(&val, CAL_WR_DMA_CTRL_DTAG_PIX_DAT, - CAL_WR_DMA_CTRL_DTAG_MASK); - set_field(&val, CAL_WR_DMA_CTRL_MODE_CONST, - CAL_WR_DMA_CTRL_MODE_MASK); - set_field(&val, CAL_WR_DMA_CTRL_PATTERN_LINEAR, - CAL_WR_DMA_CTRL_PATTERN_MASK); - set_field(&val, 1, CAL_WR_DMA_CTRL_STALL_RD_MASK); - reg_write(ctx->cal, CAL_WR_DMA_CTRL(ctx->index), val); + val = cal_read(ctx->cal, CAL_WR_DMA_CTRL(ctx->index)); + cal_set_field(&val, ctx->cport, CAL_WR_DMA_CTRL_CPORT_MASK); + cal_set_field(&val, height, CAL_WR_DMA_CTRL_YSIZE_MASK); + cal_set_field(&val, CAL_WR_DMA_CTRL_DTAG_PIX_DAT, + CAL_WR_DMA_CTRL_DTAG_MASK); + cal_set_field(&val, CAL_WR_DMA_CTRL_MODE_CONST, + CAL_WR_DMA_CTRL_MODE_MASK); + cal_set_field(&val, CAL_WR_DMA_CTRL_PATTERN_LINEAR, + CAL_WR_DMA_CTRL_PATTERN_MASK); + cal_set_field(&val, 1, CAL_WR_DMA_CTRL_STALL_RD_MASK); + cal_write(ctx->cal, CAL_WR_DMA_CTRL(ctx->index), val); ctx_dbg(3, ctx, "CAL_WR_DMA_CTRL(%d) = 0x%08x\n", ctx->index, - reg_read(ctx->cal, CAL_WR_DMA_CTRL(ctx->index))); + cal_read(ctx->cal, CAL_WR_DMA_CTRL(ctx->index))); /* * width/16 not sure but giving it a whirl. * zero does not work right */ - reg_write_field(ctx->cal, + cal_write_field(ctx->cal, CAL_WR_DMA_OFST(ctx->index), (width / 16), CAL_WR_DMA_OFST_MASK); ctx_dbg(3, ctx, "CAL_WR_DMA_OFST(%d) = 0x%08x\n", ctx->index, - reg_read(ctx->cal, CAL_WR_DMA_OFST(ctx->index))); + cal_read(ctx->cal, CAL_WR_DMA_OFST(ctx->index))); - val = reg_read(ctx->cal, CAL_WR_DMA_XSIZE(ctx->index)); + val = cal_read(ctx->cal, CAL_WR_DMA_XSIZE(ctx->index)); /* 64 bit word means no skipping */ - set_field(&val, 0, CAL_WR_DMA_XSIZE_XSKIP_MASK); + cal_set_field(&val, 0, CAL_WR_DMA_XSIZE_XSKIP_MASK); /* * (width*8)/64 this should be size of an entire line * in 64bit word but 0 means all data until the end * is detected automagically */ - set_field(&val, (width / 8), CAL_WR_DMA_XSIZE_MASK); - reg_write(ctx->cal, CAL_WR_DMA_XSIZE(ctx->index), val); + cal_set_field(&val, (width / 8), CAL_WR_DMA_XSIZE_MASK); + cal_write(ctx->cal, CAL_WR_DMA_XSIZE(ctx->index), val); ctx_dbg(3, ctx, "CAL_WR_DMA_XSIZE(%d) = 0x%08x\n", ctx->index, - reg_read(ctx->cal, CAL_WR_DMA_XSIZE(ctx->index))); - - val = reg_read(ctx->cal, CAL_CTRL); - set_field(&val, CAL_CTRL_BURSTSIZE_BURST128, CAL_CTRL_BURSTSIZE_MASK); - set_field(&val, 0xF, CAL_CTRL_TAGCNT_MASK); - set_field(&val, CAL_CTRL_POSTED_WRITES_NONPOSTED, - CAL_CTRL_POSTED_WRITES_MASK); - set_field(&val, 0xFF, CAL_CTRL_MFLAGL_MASK); - set_field(&val, 0xFF, CAL_CTRL_MFLAGH_MASK); - reg_write(ctx->cal, CAL_CTRL, val); - ctx_dbg(3, ctx, "CAL_CTRL = 0x%08x\n", reg_read(ctx->cal, CAL_CTRL)); + cal_read(ctx->cal, CAL_WR_DMA_XSIZE(ctx->index))); + + val = cal_read(ctx->cal, CAL_CTRL); + cal_set_field(&val, CAL_CTRL_BURSTSIZE_BURST128, + CAL_CTRL_BURSTSIZE_MASK); + cal_set_field(&val, 0xF, CAL_CTRL_TAGCNT_MASK); + cal_set_field(&val, CAL_CTRL_POSTED_WRITES_NONPOSTED, + CAL_CTRL_POSTED_WRITES_MASK); + cal_set_field(&val, 0xFF, CAL_CTRL_MFLAGL_MASK); + cal_set_field(&val, 0xFF, CAL_CTRL_MFLAGH_MASK); + cal_write(ctx->cal, CAL_CTRL, val); + ctx_dbg(3, ctx, "CAL_CTRL = 0x%08x\n", cal_read(ctx->cal, CAL_CTRL)); } void cal_ctx_wr_dma_addr(struct cal_ctx *ctx, unsigned int dmaaddr) { - reg_write(ctx->cal, CAL_WR_DMA_ADDR(ctx->index), dmaaddr); + cal_write(ctx->cal, CAL_WR_DMA_ADDR(ctx->index), dmaaddr); } /* ------------------------------------------------------------------ @@ -1024,36 +1008,36 @@ static irqreturn_t cal_irq(int irq_cal, void *data) struct cal_dmaqueue *dma_q; u32 status; - status = reg_read(cal, CAL_HL_IRQSTATUS(0)); + status = cal_read(cal, CAL_HL_IRQSTATUS(0)); if (status) { unsigned int i; - reg_write(cal, CAL_HL_IRQSTATUS(0), status); + cal_write(cal, CAL_HL_IRQSTATUS(0), status); if (status & CAL_HL_IRQ_OCPO_ERR_MASK) dev_err_ratelimited(cal->dev, "OCPO ERROR\n"); for (i = 0; i < CAL_NUM_CSI2_PORTS; ++i) { if (status & CAL_HL_IRQ_CIO_MASK(i)) { - u32 cio_stat = reg_read(cal, + u32 cio_stat = cal_read(cal, CAL_CSI2_COMPLEXIO_IRQSTATUS(i)); dev_err_ratelimited(cal->dev, "CIO%u error: %#08x\n", i, cio_stat); - reg_write(cal, CAL_CSI2_COMPLEXIO_IRQSTATUS(i), + cal_write(cal, CAL_CSI2_COMPLEXIO_IRQSTATUS(i), cio_stat); } } } /* Check which DMA just finished */ - status = reg_read(cal, CAL_HL_IRQSTATUS(1)); + status = cal_read(cal, CAL_HL_IRQSTATUS(1)); if (status) { unsigned int i; /* Clear Interrupt status */ - reg_write(cal, CAL_HL_IRQSTATUS(1), status); + cal_write(cal, CAL_HL_IRQSTATUS(1), status); for (i = 0; i < ARRAY_SIZE(cal->ctx); ++i) { if (status & CAL_HL_IRQ_MASK(i)) { @@ -1071,12 +1055,12 @@ static irqreturn_t cal_irq(int irq_cal, void *data) } /* Check which DMA just started */ - status = reg_read(cal, CAL_HL_IRQSTATUS(2)); + status = cal_read(cal, CAL_HL_IRQSTATUS(2)); if (status) { unsigned int i; /* Clear Interrupt status */ - reg_write(cal, CAL_HL_IRQSTATUS(2), status); + cal_write(cal, CAL_HL_IRQSTATUS(2), status); for (i = 0; i < ARRAY_SIZE(cal->ctx); ++i) { if (status & CAL_HL_IRQ_MASK(i)) { @@ -1355,7 +1339,7 @@ static void cal_get_hwinfo(struct cal_dev *cal) { u32 hwinfo; - cal->revision = reg_read(cal, CAL_HL_REVISION); + cal->revision = cal_read(cal, CAL_HL_REVISION); switch (FIELD_GET(CAL_HL_REVISION_SCHEME_MASK, cal->revision)) { case CAL_HL_REVISION_SCHEME_H08: cal_dbg(3, cal, "CAL HW revision %lu.%lu.%lu (0x%08x)\n", @@ -1372,7 +1356,7 @@ static void cal_get_hwinfo(struct cal_dev *cal) break; } - hwinfo = reg_read(cal, CAL_HL_HWINFO); + hwinfo = cal_read(cal, CAL_HL_HWINFO); if (hwinfo != CAL_HL_HWINFO_VALUE) cal_info(cal, "CAL_HL_HWINFO = 0x%08x, expected 0x%08x\n", hwinfo, CAL_HL_HWINFO_VALUE); diff --git a/drivers/media/platform/ti-vpe/cal.h b/drivers/media/platform/ti-vpe/cal.h index 383fa9295048d..44f2836b6543e 100644 --- a/drivers/media/platform/ti-vpe/cal.h +++ b/drivers/media/platform/ti-vpe/cal.h @@ -11,6 +11,8 @@ #ifndef __TI_CAL_H__ #define __TI_CAL_H__ +#include +#include #include #include #include @@ -203,6 +205,40 @@ extern int cal_video_nr; #define phy_err(phy, fmt, arg...) \ cal_err((phy)->cal, "phy%u: " fmt, (phy)->instance, ##arg) +static inline u32 cal_read(struct cal_dev *cal, u32 offset) +{ + return ioread32(cal->base + offset); +} + +static inline void cal_write(struct cal_dev *cal, u32 offset, u32 val) +{ + iowrite32(val, cal->base + offset); +} + +static inline u32 cal_read_field(struct cal_dev *cal, u32 offset, u32 mask) +{ + return FIELD_GET(mask, cal_read(cal, offset)); +} + +static inline void cal_write_field(struct cal_dev *cal, u32 offset, u32 value, + u32 mask) +{ + u32 val = cal_read(cal, offset); + + val &= ~mask; + val |= FIELD_PREP(mask, value); + cal_write(cal, offset, val); +} + +static inline void cal_set_field(u32 *valp, u32 field, u32 mask) +{ + u32 val = *valp; + + val &= ~mask; + val |= (field << __ffs(mask)) & mask; + *valp = val; +} + void cal_quickdump_regs(struct cal_dev *cal); int cal_camerarx_start(struct cal_camerarx *phy, const struct cal_fmt *fmt); -- GitLab From af30d4f36ef35d23f1ab6c5e4f329c743fb3c360 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 6 Jul 2020 20:36:45 +0200 Subject: [PATCH 0617/1476] media: ti-vpe: cal: Split CAMERARX handling to cal-camerarx.c Implementation of media controller centric device configuration will add code to the CAMERARX support section, which is already quite big. Move it to a separate file to make the code more manageable. No functional change is included. The cal_camerarx_init_regmap() function is kept in cal.c and renamed to cal_init_camerarx_regmap() as it's not specific to one CAMERARX instance, but related to the whole CAL device. Signed-off-by: Laurent Pinchart Reviewed-by: Benoit Parrot Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/ti-vpe/Makefile | 2 +- drivers/media/platform/ti-vpe/cal-camerarx.c | 649 ++++++++++++++++ drivers/media/platform/ti-vpe/cal.c | 739 ++----------------- drivers/media/platform/ti-vpe/cal.h | 5 + 4 files changed, 713 insertions(+), 682 deletions(-) create mode 100644 drivers/media/platform/ti-vpe/cal-camerarx.c diff --git a/drivers/media/platform/ti-vpe/Makefile b/drivers/media/platform/ti-vpe/Makefile index a7bf8f2a13862..ad624056e0392 100644 --- a/drivers/media/platform/ti-vpe/Makefile +++ b/drivers/media/platform/ti-vpe/Makefile @@ -13,4 +13,4 @@ ccflags-$(CONFIG_VIDEO_TI_VPE_DEBUG) += -DDEBUG obj-$(CONFIG_VIDEO_TI_CAL) += ti-cal.o -ti-cal-y := cal.o cal-video.o +ti-cal-y := cal.o cal-camerarx.o cal-video.o diff --git a/drivers/media/platform/ti-vpe/cal-camerarx.c b/drivers/media/platform/ti-vpe/cal-camerarx.c new file mode 100644 index 0000000000000..806cbf175d394 --- /dev/null +++ b/drivers/media/platform/ti-vpe/cal-camerarx.c @@ -0,0 +1,649 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * TI Camera Access Layer (CAL) - CAMERARX + * + * Copyright (c) 2015-2020 Texas Instruments Inc. + * + * Authors: + * Benoit Parrot + * Laurent Pinchart + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "cal.h" +#include "cal_regs.h" + +/* ------------------------------------------------------------------ + * I/O Register Accessors + * ------------------------------------------------------------------ + */ + +static inline u32 camerarx_read(struct cal_camerarx *phy, u32 offset) +{ + return ioread32(phy->base + offset); +} + +static inline void camerarx_write(struct cal_camerarx *phy, u32 offset, u32 val) +{ + iowrite32(val, phy->base + offset); +} + +/* ------------------------------------------------------------------ + * CAMERARX Management + * ------------------------------------------------------------------ + */ + +static s64 cal_camerarx_get_external_rate(struct cal_camerarx *phy) +{ + struct v4l2_ctrl *ctrl; + s64 rate; + + ctrl = v4l2_ctrl_find(phy->sensor->ctrl_handler, V4L2_CID_PIXEL_RATE); + if (!ctrl) { + phy_err(phy, "no pixel rate control in subdev: %s\n", + phy->sensor->name); + return -EPIPE; + } + + rate = v4l2_ctrl_g_ctrl_int64(ctrl); + phy_dbg(3, phy, "sensor Pixel Rate: %llu\n", rate); + + return rate; +} + +static void cal_camerarx_lane_config(struct cal_camerarx *phy) +{ + u32 val = cal_read(phy->cal, CAL_CSI2_COMPLEXIO_CFG(phy->instance)); + u32 lane_mask = CAL_CSI2_COMPLEXIO_CFG_CLOCK_POSITION_MASK; + u32 polarity_mask = CAL_CSI2_COMPLEXIO_CFG_CLOCK_POL_MASK; + struct v4l2_fwnode_bus_mipi_csi2 *mipi_csi2 = + &phy->endpoint.bus.mipi_csi2; + int lane; + + cal_set_field(&val, mipi_csi2->clock_lane + 1, lane_mask); + cal_set_field(&val, mipi_csi2->lane_polarities[0], polarity_mask); + for (lane = 0; lane < mipi_csi2->num_data_lanes; lane++) { + /* + * Every lane are one nibble apart starting with the + * clock followed by the data lanes so shift masks by 4. + */ + lane_mask <<= 4; + polarity_mask <<= 4; + cal_set_field(&val, mipi_csi2->data_lanes[lane] + 1, lane_mask); + cal_set_field(&val, mipi_csi2->lane_polarities[lane + 1], + polarity_mask); + } + + cal_write(phy->cal, CAL_CSI2_COMPLEXIO_CFG(phy->instance), val); + phy_dbg(3, phy, "CAL_CSI2_COMPLEXIO_CFG(%d) = 0x%08x\n", + phy->instance, val); +} + +static void cal_camerarx_enable(struct cal_camerarx *phy) +{ + u32 num_lanes = phy->cal->data->camerarx[phy->instance].num_lanes; + + regmap_field_write(phy->fields[F_CAMMODE], 0); + /* Always enable all lanes at the phy control level */ + regmap_field_write(phy->fields[F_LANEENABLE], (1 << num_lanes) - 1); + /* F_CSI_MODE is not present on every architecture */ + if (phy->fields[F_CSI_MODE]) + regmap_field_write(phy->fields[F_CSI_MODE], 1); + regmap_field_write(phy->fields[F_CTRLCLKEN], 1); +} + +void cal_camerarx_disable(struct cal_camerarx *phy) +{ + regmap_field_write(phy->fields[F_CTRLCLKEN], 0); +} + +/* + * TCLK values are OK at their reset values + */ +#define TCLK_TERM 0 +#define TCLK_MISS 1 +#define TCLK_SETTLE 14 + +static void cal_camerarx_config(struct cal_camerarx *phy, s64 external_rate, + const struct cal_fmt *fmt) +{ + unsigned int reg0, reg1; + unsigned int ths_term, ths_settle; + unsigned int csi2_ddrclk_khz; + struct v4l2_fwnode_bus_mipi_csi2 *mipi_csi2 = + &phy->endpoint.bus.mipi_csi2; + u32 num_lanes = mipi_csi2->num_data_lanes; + + /* DPHY timing configuration */ + + /* + * CSI-2 is DDR and we only count used lanes. + * + * csi2_ddrclk_khz = external_rate / 1000 + * / (2 * num_lanes) * fmt->bpp; + */ + csi2_ddrclk_khz = div_s64(external_rate * fmt->bpp, + 2 * num_lanes * 1000); + + phy_dbg(1, phy, "csi2_ddrclk_khz: %d\n", csi2_ddrclk_khz); + + /* THS_TERM: Programmed value = floor(20 ns/DDRClk period) */ + ths_term = 20 * csi2_ddrclk_khz / 1000000; + phy_dbg(1, phy, "ths_term: %d (0x%02x)\n", ths_term, ths_term); + + /* THS_SETTLE: Programmed value = floor(105 ns/DDRClk period) + 4 */ + ths_settle = (105 * csi2_ddrclk_khz / 1000000) + 4; + phy_dbg(1, phy, "ths_settle: %d (0x%02x)\n", ths_settle, ths_settle); + + reg0 = camerarx_read(phy, CAL_CSI2_PHY_REG0); + cal_set_field(®0, CAL_CSI2_PHY_REG0_HSCLOCKCONFIG_DISABLE, + CAL_CSI2_PHY_REG0_HSCLOCKCONFIG_MASK); + cal_set_field(®0, ths_term, CAL_CSI2_PHY_REG0_THS_TERM_MASK); + cal_set_field(®0, ths_settle, CAL_CSI2_PHY_REG0_THS_SETTLE_MASK); + + phy_dbg(1, phy, "CSI2_%d_REG0 = 0x%08x\n", phy->instance, reg0); + camerarx_write(phy, CAL_CSI2_PHY_REG0, reg0); + + reg1 = camerarx_read(phy, CAL_CSI2_PHY_REG1); + cal_set_field(®1, TCLK_TERM, CAL_CSI2_PHY_REG1_TCLK_TERM_MASK); + cal_set_field(®1, 0xb8, CAL_CSI2_PHY_REG1_DPHY_HS_SYNC_PATTERN_MASK); + cal_set_field(®1, TCLK_MISS, + CAL_CSI2_PHY_REG1_CTRLCLK_DIV_FACTOR_MASK); + cal_set_field(®1, TCLK_SETTLE, CAL_CSI2_PHY_REG1_TCLK_SETTLE_MASK); + + phy_dbg(1, phy, "CSI2_%d_REG1 = 0x%08x\n", phy->instance, reg1); + camerarx_write(phy, CAL_CSI2_PHY_REG1, reg1); +} + +static void cal_camerarx_power(struct cal_camerarx *phy, bool enable) +{ + u32 target_state; + unsigned int i; + + target_state = enable ? CAL_CSI2_COMPLEXIO_CFG_PWR_CMD_STATE_ON : + CAL_CSI2_COMPLEXIO_CFG_PWR_CMD_STATE_OFF; + + cal_write_field(phy->cal, CAL_CSI2_COMPLEXIO_CFG(phy->instance), + target_state, CAL_CSI2_COMPLEXIO_CFG_PWR_CMD_MASK); + + for (i = 0; i < 10; i++) { + u32 current_state; + + current_state = cal_read_field(phy->cal, + CAL_CSI2_COMPLEXIO_CFG(phy->instance), + CAL_CSI2_COMPLEXIO_CFG_PWR_STATUS_MASK); + + if (current_state == target_state) + break; + + usleep_range(1000, 1100); + } + + if (i == 10) + phy_err(phy, "Failed to power %s complexio\n", + enable ? "up" : "down"); +} + +static void cal_camerarx_wait_reset(struct cal_camerarx *phy) +{ + unsigned long timeout; + + timeout = jiffies + msecs_to_jiffies(750); + while (time_before(jiffies, timeout)) { + if (cal_read_field(phy->cal, + CAL_CSI2_COMPLEXIO_CFG(phy->instance), + CAL_CSI2_COMPLEXIO_CFG_RESET_DONE_MASK) == + CAL_CSI2_COMPLEXIO_CFG_RESET_DONE_RESETCOMPLETED) + break; + usleep_range(500, 5000); + } + + if (cal_read_field(phy->cal, CAL_CSI2_COMPLEXIO_CFG(phy->instance), + CAL_CSI2_COMPLEXIO_CFG_RESET_DONE_MASK) != + CAL_CSI2_COMPLEXIO_CFG_RESET_DONE_RESETCOMPLETED) + phy_err(phy, "Timeout waiting for Complex IO reset done\n"); +} + +static void cal_camerarx_wait_stop_state(struct cal_camerarx *phy) +{ + unsigned long timeout; + + timeout = jiffies + msecs_to_jiffies(750); + while (time_before(jiffies, timeout)) { + if (cal_read_field(phy->cal, + CAL_CSI2_TIMING(phy->instance), + CAL_CSI2_TIMING_FORCE_RX_MODE_IO1_MASK) == 0) + break; + usleep_range(500, 5000); + } + + if (cal_read_field(phy->cal, CAL_CSI2_TIMING(phy->instance), + CAL_CSI2_TIMING_FORCE_RX_MODE_IO1_MASK) != 0) + phy_err(phy, "Timeout waiting for stop state\n"); +} + +int cal_camerarx_start(struct cal_camerarx *phy, const struct cal_fmt *fmt) +{ + s64 external_rate; + u32 sscounter; + u32 val; + int ret; + + external_rate = cal_camerarx_get_external_rate(phy); + if (external_rate < 0) + return external_rate; + + ret = v4l2_subdev_call(phy->sensor, core, s_power, 1); + if (ret < 0 && ret != -ENOIOCTLCMD && ret != -ENODEV) { + phy_err(phy, "power on failed in subdev\n"); + return ret; + } + + /* + * CSI-2 PHY Link Initialization Sequence, according to the DRA74xP / + * DRA75xP / DRA76xP / DRA77xP TRM. The DRA71x / DRA72x and the AM65x / + * DRA80xM TRMs have a a slightly simplified sequence. + */ + + /* + * 1. Configure all CSI-2 low level protocol registers to be ready to + * receive signals/data from the CSI-2 PHY. + * + * i.-v. Configure the lanes position and polarity. + */ + cal_camerarx_lane_config(phy); + + /* + * vi.-vii. Configure D-PHY mode, enable the required lanes and + * enable the CAMERARX clock. + */ + cal_camerarx_enable(phy); + + /* + * 2. CSI PHY and link initialization sequence. + * + * a. Deassert the CSI-2 PHY reset. Do not wait for reset completion + * at this point, as it requires the external sensor to send the + * CSI-2 HS clock. + */ + cal_write_field(phy->cal, CAL_CSI2_COMPLEXIO_CFG(phy->instance), + CAL_CSI2_COMPLEXIO_CFG_RESET_CTRL_OPERATIONAL, + CAL_CSI2_COMPLEXIO_CFG_RESET_CTRL_MASK); + phy_dbg(3, phy, "CAL_CSI2_COMPLEXIO_CFG(%d) = 0x%08x De-assert Complex IO Reset\n", + phy->instance, + cal_read(phy->cal, CAL_CSI2_COMPLEXIO_CFG(phy->instance))); + + /* Dummy read to allow SCP reset to complete. */ + camerarx_read(phy, CAL_CSI2_PHY_REG0); + + /* Program the PHY timing parameters. */ + cal_camerarx_config(phy, external_rate, fmt); + + /* + * b. Assert the FORCERXMODE signal. + * + * The stop-state-counter is based on fclk cycles, and we always use + * the x16 and x4 settings, so stop-state-timeout = + * fclk-cycle * 16 * 4 * counter. + * + * Stop-state-timeout must be more than 100us as per CSI-2 spec, so we + * calculate a timeout that's 100us (rounding up). + */ + sscounter = DIV_ROUND_UP(clk_get_rate(phy->cal->fclk), 10000 * 16 * 4); + + val = cal_read(phy->cal, CAL_CSI2_TIMING(phy->instance)); + cal_set_field(&val, 1, CAL_CSI2_TIMING_STOP_STATE_X16_IO1_MASK); + cal_set_field(&val, 1, CAL_CSI2_TIMING_STOP_STATE_X4_IO1_MASK); + cal_set_field(&val, sscounter, + CAL_CSI2_TIMING_STOP_STATE_COUNTER_IO1_MASK); + cal_write(phy->cal, CAL_CSI2_TIMING(phy->instance), val); + phy_dbg(3, phy, "CAL_CSI2_TIMING(%d) = 0x%08x Stop States\n", + phy->instance, + cal_read(phy->cal, CAL_CSI2_TIMING(phy->instance))); + + /* Assert the FORCERXMODE signal. */ + cal_write_field(phy->cal, CAL_CSI2_TIMING(phy->instance), + 1, CAL_CSI2_TIMING_FORCE_RX_MODE_IO1_MASK); + phy_dbg(3, phy, "CAL_CSI2_TIMING(%d) = 0x%08x Force RXMODE\n", + phy->instance, + cal_read(phy->cal, CAL_CSI2_TIMING(phy->instance))); + + /* + * c. Connect pull-down on CSI-2 PHY link (using pad control). + * + * This is not required on DRA71x, DRA72x, AM65x and DRA80xM. Not + * implemented. + */ + + /* + * d. Power up the CSI-2 PHY. + * e. Check whether the state status reaches the ON state. + */ + cal_camerarx_power(phy, true); + + /* + * Start the sensor to enable the CSI-2 HS clock. We can now wait for + * CSI-2 PHY reset to complete. + */ + ret = v4l2_subdev_call(phy->sensor, video, s_stream, 1); + if (ret) { + v4l2_subdev_call(phy->sensor, core, s_power, 0); + phy_err(phy, "stream on failed in subdev\n"); + return ret; + } + + cal_camerarx_wait_reset(phy); + + /* f. Wait for STOPSTATE=1 for all enabled lane modules. */ + cal_camerarx_wait_stop_state(phy); + + phy_dbg(1, phy, "CSI2_%u_REG1 = 0x%08x (bits 31-28 should be set)\n", + phy->instance, camerarx_read(phy, CAL_CSI2_PHY_REG1)); + + /* + * g. Disable pull-down on CSI-2 PHY link (using pad control). + * + * This is not required on DRA71x, DRA72x, AM65x and DRA80xM. Not + * implemented. + */ + + return 0; +} + +void cal_camerarx_stop(struct cal_camerarx *phy) +{ + unsigned int i; + int ret; + + cal_camerarx_power(phy, false); + + /* Assert Complex IO Reset */ + cal_write_field(phy->cal, CAL_CSI2_COMPLEXIO_CFG(phy->instance), + CAL_CSI2_COMPLEXIO_CFG_RESET_CTRL, + CAL_CSI2_COMPLEXIO_CFG_RESET_CTRL_MASK); + + /* Wait for power down completion */ + for (i = 0; i < 10; i++) { + if (cal_read_field(phy->cal, + CAL_CSI2_COMPLEXIO_CFG(phy->instance), + CAL_CSI2_COMPLEXIO_CFG_RESET_DONE_MASK) == + CAL_CSI2_COMPLEXIO_CFG_RESET_DONE_RESETONGOING) + break; + usleep_range(1000, 1100); + } + phy_dbg(3, phy, "CAL_CSI2_COMPLEXIO_CFG(%d) = 0x%08x Complex IO in Reset (%d) %s\n", + phy->instance, + cal_read(phy->cal, CAL_CSI2_COMPLEXIO_CFG(phy->instance)), i, + (i >= 10) ? "(timeout)" : ""); + + /* Disable the phy */ + cal_camerarx_disable(phy); + + if (v4l2_subdev_call(phy->sensor, video, s_stream, 0)) + phy_err(phy, "stream off failed in subdev\n"); + + ret = v4l2_subdev_call(phy->sensor, core, s_power, 0); + if (ret < 0 && ret != -ENOIOCTLCMD && ret != -ENODEV) + phy_err(phy, "power off failed in subdev\n"); +} + +/* + * Errata i913: CSI2 LDO Needs to be disabled when module is powered on + * + * Enabling CSI2 LDO shorts it to core supply. It is crucial the 2 CSI2 + * LDOs on the device are disabled if CSI-2 module is powered on + * (0x4845 B304 | 0x4845 B384 [28:27] = 0x1) or in ULPS (0x4845 B304 + * | 0x4845 B384 [28:27] = 0x2) mode. Common concerns include: high + * current draw on the module supply in active mode. + * + * Errata does not apply when CSI-2 module is powered off + * (0x4845 B304 | 0x4845 B384 [28:27] = 0x0). + * + * SW Workaround: + * Set the following register bits to disable the LDO, + * which is essentially CSI2 REG10 bit 6: + * + * Core 0: 0x4845 B828 = 0x0000 0040 + * Core 1: 0x4845 B928 = 0x0000 0040 + */ +void cal_camerarx_i913_errata(struct cal_camerarx *phy) +{ + u32 reg10 = camerarx_read(phy, CAL_CSI2_PHY_REG10); + + cal_set_field(®10, 1, CAL_CSI2_PHY_REG10_I933_LDO_DISABLE_MASK); + + phy_dbg(1, phy, "CSI2_%d_REG10 = 0x%08x\n", phy->instance, reg10); + camerarx_write(phy, CAL_CSI2_PHY_REG10, reg10); +} + +/* + * Enable the expected IRQ sources + */ +void cal_camerarx_enable_irqs(struct cal_camerarx *phy) +{ + u32 val; + + const u32 cio_err_mask = + CAL_CSI2_COMPLEXIO_IRQ_LANE_ERRORS_MASK | + CAL_CSI2_COMPLEXIO_IRQ_FIFO_OVR_MASK | + CAL_CSI2_COMPLEXIO_IRQ_SHORT_PACKET_MASK | + CAL_CSI2_COMPLEXIO_IRQ_ECC_NO_CORRECTION_MASK; + + /* Enable CIO error irqs */ + cal_write(phy->cal, CAL_HL_IRQENABLE_SET(0), + CAL_HL_IRQ_CIO_MASK(phy->instance)); + cal_write(phy->cal, CAL_CSI2_COMPLEXIO_IRQENABLE(phy->instance), + cio_err_mask); + + /* Always enable OCPO error */ + cal_write(phy->cal, CAL_HL_IRQENABLE_SET(0), CAL_HL_IRQ_OCPO_ERR_MASK); + + /* Enable IRQ_WDMA_END 0/1 */ + val = 0; + cal_set_field(&val, 1, CAL_HL_IRQ_MASK(phy->instance)); + cal_write(phy->cal, CAL_HL_IRQENABLE_SET(1), val); + /* Enable IRQ_WDMA_START 0/1 */ + val = 0; + cal_set_field(&val, 1, CAL_HL_IRQ_MASK(phy->instance)); + cal_write(phy->cal, CAL_HL_IRQENABLE_SET(2), val); + /* Todo: Add VC_IRQ and CSI2_COMPLEXIO_IRQ handling */ + cal_write(phy->cal, CAL_CSI2_VC_IRQENABLE(0), 0xFF000000); +} + +void cal_camerarx_disable_irqs(struct cal_camerarx *phy) +{ + u32 val; + + /* Disable CIO error irqs */ + cal_write(phy->cal, CAL_HL_IRQENABLE_CLR(0), + CAL_HL_IRQ_CIO_MASK(phy->instance)); + cal_write(phy->cal, CAL_CSI2_COMPLEXIO_IRQENABLE(phy->instance), 0); + + /* Disable IRQ_WDMA_END 0/1 */ + val = 0; + cal_set_field(&val, 1, CAL_HL_IRQ_MASK(phy->instance)); + cal_write(phy->cal, CAL_HL_IRQENABLE_CLR(1), val); + /* Disable IRQ_WDMA_START 0/1 */ + val = 0; + cal_set_field(&val, 1, CAL_HL_IRQ_MASK(phy->instance)); + cal_write(phy->cal, CAL_HL_IRQENABLE_CLR(2), val); + /* Todo: Add VC_IRQ and CSI2_COMPLEXIO_IRQ handling */ + cal_write(phy->cal, CAL_CSI2_VC_IRQENABLE(0), 0); +} + +void cal_camerarx_ppi_enable(struct cal_camerarx *phy) +{ + cal_write(phy->cal, CAL_CSI2_PPI_CTRL(phy->instance), BIT(3)); + cal_write_field(phy->cal, CAL_CSI2_PPI_CTRL(phy->instance), + 1, CAL_CSI2_PPI_CTRL_IF_EN_MASK); +} + +void cal_camerarx_ppi_disable(struct cal_camerarx *phy) +{ + cal_write_field(phy->cal, CAL_CSI2_PPI_CTRL(phy->instance), + 0, CAL_CSI2_PPI_CTRL_IF_EN_MASK); +} + +static int cal_camerarx_regmap_init(struct cal_dev *cal, + struct cal_camerarx *phy) +{ + const struct cal_camerarx_data *phy_data; + unsigned int i; + + if (!cal->data) + return -EINVAL; + + phy_data = &cal->data->camerarx[phy->instance]; + + for (i = 0; i < F_MAX_FIELDS; i++) { + struct reg_field field = { + .reg = cal->syscon_camerrx_offset, + .lsb = phy_data->fields[i].lsb, + .msb = phy_data->fields[i].msb, + }; + + /* + * Here we update the reg offset with the + * value found in DT + */ + phy->fields[i] = devm_regmap_field_alloc(cal->dev, + cal->syscon_camerrx, + field); + if (IS_ERR(phy->fields[i])) { + cal_err(cal, "Unable to allocate regmap fields\n"); + return PTR_ERR(phy->fields[i]); + } + } + + return 0; +} + +static int cal_camerarx_parse_dt(struct cal_camerarx *phy) +{ + struct v4l2_fwnode_endpoint *endpoint = &phy->endpoint; + struct device_node *ep_node; + char data_lanes[V4L2_FWNODE_CSI2_MAX_DATA_LANES * 2]; + unsigned int i; + int ret; + + /* + * Find the endpoint node for the port corresponding to the PHY + * instance, and parse its CSI-2-related properties. + */ + ep_node = of_graph_get_endpoint_by_regs(phy->cal->dev->of_node, + phy->instance, 0); + if (!ep_node) { + /* + * The endpoint is not mandatory, not all PHY instances need to + * be connected in DT. + */ + phy_dbg(3, phy, "Port has no endpoint\n"); + return 0; + } + + endpoint->bus_type = V4L2_MBUS_CSI2_DPHY; + ret = v4l2_fwnode_endpoint_parse(of_fwnode_handle(ep_node), endpoint); + if (ret < 0) { + phy_err(phy, "Failed to parse endpoint\n"); + goto done; + } + + for (i = 0; i < endpoint->bus.mipi_csi2.num_data_lanes; i++) { + unsigned int lane = endpoint->bus.mipi_csi2.data_lanes[i]; + + if (lane > 4) { + phy_err(phy, "Invalid position %u for data lane %u\n", + lane, i); + ret = -EINVAL; + goto done; + } + + data_lanes[i*2] = '0' + lane; + data_lanes[i*2+1] = ' '; + } + + data_lanes[i*2-1] = '\0'; + + phy_dbg(3, phy, + "CSI-2 bus: clock lane <%u>, data lanes <%s>, flags 0x%08x\n", + endpoint->bus.mipi_csi2.clock_lane, data_lanes, + endpoint->bus.mipi_csi2.flags); + + /* Retrieve the connected device and store it for later use. */ + phy->sensor_node = of_graph_get_remote_port_parent(ep_node); + if (!phy->sensor_node) { + phy_dbg(3, phy, "Can't get remote parent\n"); + ret = -EINVAL; + goto done; + } + + phy_dbg(1, phy, "Found connected device %pOFn\n", phy->sensor_node); + +done: + of_node_put(ep_node); + return ret; +} + +struct cal_camerarx *cal_camerarx_create(struct cal_dev *cal, + unsigned int instance) +{ + struct platform_device *pdev = to_platform_device(cal->dev); + struct cal_camerarx *phy; + int ret; + + phy = kzalloc(sizeof(*phy), GFP_KERNEL); + if (!phy) + return ERR_PTR(-ENOMEM); + + phy->cal = cal; + phy->instance = instance; + + phy->res = platform_get_resource_byname(pdev, IORESOURCE_MEM, + (instance == 0) ? + "cal_rx_core0" : + "cal_rx_core1"); + phy->base = devm_ioremap_resource(cal->dev, phy->res); + if (IS_ERR(phy->base)) { + cal_err(cal, "failed to ioremap\n"); + ret = PTR_ERR(phy->base); + goto error; + } + + cal_dbg(1, cal, "ioresource %s at %pa - %pa\n", + phy->res->name, &phy->res->start, &phy->res->end); + + ret = cal_camerarx_regmap_init(cal, phy); + if (ret) + goto error; + + ret = cal_camerarx_parse_dt(phy); + if (ret) + goto error; + + return phy; + +error: + kfree(phy); + return ERR_PTR(ret); +} + +void cal_camerarx_destroy(struct cal_camerarx *phy) +{ + if (!phy) + return; + + of_node_put(phy->sensor_node); + kfree(phy); +} diff --git a/drivers/media/platform/ti-vpe/cal.c b/drivers/media/platform/ti-vpe/cal.c index c2252ed36d68c..59a0266b1f399 100644 --- a/drivers/media/platform/ti-vpe/cal.c +++ b/drivers/media/platform/ti-vpe/cal.c @@ -10,12 +10,10 @@ */ #include -#include #include #include #include #include -#include #include #include #include @@ -25,9 +23,7 @@ #include #include #include -#include #include -#include #include #include @@ -155,682 +151,6 @@ void cal_quickdump_regs(struct cal_dev *cal) } } -/* ------------------------------------------------------------------ - * CAMERARX Management - * ------------------------------------------------------------------ - */ - -static inline u32 camerarx_read(struct cal_camerarx *phy, u32 offset) -{ - return ioread32(phy->base + offset); -} - -static inline void camerarx_write(struct cal_camerarx *phy, u32 offset, u32 val) -{ - iowrite32(val, phy->base + offset); -} - -static s64 cal_camerarx_get_external_rate(struct cal_camerarx *phy) -{ - struct v4l2_ctrl *ctrl; - s64 rate; - - ctrl = v4l2_ctrl_find(phy->sensor->ctrl_handler, V4L2_CID_PIXEL_RATE); - if (!ctrl) { - phy_err(phy, "no pixel rate control in subdev: %s\n", - phy->sensor->name); - return -EPIPE; - } - - rate = v4l2_ctrl_g_ctrl_int64(ctrl); - phy_dbg(3, phy, "sensor Pixel Rate: %llu\n", rate); - - return rate; -} - -static void cal_camerarx_lane_config(struct cal_camerarx *phy) -{ - u32 val = cal_read(phy->cal, CAL_CSI2_COMPLEXIO_CFG(phy->instance)); - u32 lane_mask = CAL_CSI2_COMPLEXIO_CFG_CLOCK_POSITION_MASK; - u32 polarity_mask = CAL_CSI2_COMPLEXIO_CFG_CLOCK_POL_MASK; - struct v4l2_fwnode_bus_mipi_csi2 *mipi_csi2 = - &phy->endpoint.bus.mipi_csi2; - int lane; - - cal_set_field(&val, mipi_csi2->clock_lane + 1, lane_mask); - cal_set_field(&val, mipi_csi2->lane_polarities[0], polarity_mask); - for (lane = 0; lane < mipi_csi2->num_data_lanes; lane++) { - /* - * Every lane are one nibble apart starting with the - * clock followed by the data lanes so shift masks by 4. - */ - lane_mask <<= 4; - polarity_mask <<= 4; - cal_set_field(&val, mipi_csi2->data_lanes[lane] + 1, lane_mask); - cal_set_field(&val, mipi_csi2->lane_polarities[lane + 1], - polarity_mask); - } - - cal_write(phy->cal, CAL_CSI2_COMPLEXIO_CFG(phy->instance), val); - phy_dbg(3, phy, "CAL_CSI2_COMPLEXIO_CFG(%d) = 0x%08x\n", - phy->instance, val); -} - -static void cal_camerarx_enable(struct cal_camerarx *phy) -{ - u32 num_lanes = phy->cal->data->camerarx[phy->instance].num_lanes; - - regmap_field_write(phy->fields[F_CAMMODE], 0); - /* Always enable all lanes at the phy control level */ - regmap_field_write(phy->fields[F_LANEENABLE], (1 << num_lanes) - 1); - /* F_CSI_MODE is not present on every architecture */ - if (phy->fields[F_CSI_MODE]) - regmap_field_write(phy->fields[F_CSI_MODE], 1); - regmap_field_write(phy->fields[F_CTRLCLKEN], 1); -} - -static void cal_camerarx_disable(struct cal_camerarx *phy) -{ - regmap_field_write(phy->fields[F_CTRLCLKEN], 0); -} - -/* - * TCLK values are OK at their reset values - */ -#define TCLK_TERM 0 -#define TCLK_MISS 1 -#define TCLK_SETTLE 14 - -static void cal_camerarx_config(struct cal_camerarx *phy, s64 external_rate, - const struct cal_fmt *fmt) -{ - unsigned int reg0, reg1; - unsigned int ths_term, ths_settle; - unsigned int csi2_ddrclk_khz; - struct v4l2_fwnode_bus_mipi_csi2 *mipi_csi2 = - &phy->endpoint.bus.mipi_csi2; - u32 num_lanes = mipi_csi2->num_data_lanes; - - /* DPHY timing configuration */ - - /* - * CSI-2 is DDR and we only count used lanes. - * - * csi2_ddrclk_khz = external_rate / 1000 - * / (2 * num_lanes) * fmt->bpp; - */ - csi2_ddrclk_khz = div_s64(external_rate * fmt->bpp, - 2 * num_lanes * 1000); - - phy_dbg(1, phy, "csi2_ddrclk_khz: %d\n", csi2_ddrclk_khz); - - /* THS_TERM: Programmed value = floor(20 ns/DDRClk period) */ - ths_term = 20 * csi2_ddrclk_khz / 1000000; - phy_dbg(1, phy, "ths_term: %d (0x%02x)\n", ths_term, ths_term); - - /* THS_SETTLE: Programmed value = floor(105 ns/DDRClk period) + 4 */ - ths_settle = (105 * csi2_ddrclk_khz / 1000000) + 4; - phy_dbg(1, phy, "ths_settle: %d (0x%02x)\n", ths_settle, ths_settle); - - reg0 = camerarx_read(phy, CAL_CSI2_PHY_REG0); - cal_set_field(®0, CAL_CSI2_PHY_REG0_HSCLOCKCONFIG_DISABLE, - CAL_CSI2_PHY_REG0_HSCLOCKCONFIG_MASK); - cal_set_field(®0, ths_term, CAL_CSI2_PHY_REG0_THS_TERM_MASK); - cal_set_field(®0, ths_settle, CAL_CSI2_PHY_REG0_THS_SETTLE_MASK); - - phy_dbg(1, phy, "CSI2_%d_REG0 = 0x%08x\n", phy->instance, reg0); - camerarx_write(phy, CAL_CSI2_PHY_REG0, reg0); - - reg1 = camerarx_read(phy, CAL_CSI2_PHY_REG1); - cal_set_field(®1, TCLK_TERM, CAL_CSI2_PHY_REG1_TCLK_TERM_MASK); - cal_set_field(®1, 0xb8, CAL_CSI2_PHY_REG1_DPHY_HS_SYNC_PATTERN_MASK); - cal_set_field(®1, TCLK_MISS, - CAL_CSI2_PHY_REG1_CTRLCLK_DIV_FACTOR_MASK); - cal_set_field(®1, TCLK_SETTLE, CAL_CSI2_PHY_REG1_TCLK_SETTLE_MASK); - - phy_dbg(1, phy, "CSI2_%d_REG1 = 0x%08x\n", phy->instance, reg1); - camerarx_write(phy, CAL_CSI2_PHY_REG1, reg1); -} - -static void cal_camerarx_power(struct cal_camerarx *phy, bool enable) -{ - u32 target_state; - unsigned int i; - - target_state = enable ? CAL_CSI2_COMPLEXIO_CFG_PWR_CMD_STATE_ON : - CAL_CSI2_COMPLEXIO_CFG_PWR_CMD_STATE_OFF; - - cal_write_field(phy->cal, CAL_CSI2_COMPLEXIO_CFG(phy->instance), - target_state, CAL_CSI2_COMPLEXIO_CFG_PWR_CMD_MASK); - - for (i = 0; i < 10; i++) { - u32 current_state; - - current_state = cal_read_field(phy->cal, - CAL_CSI2_COMPLEXIO_CFG(phy->instance), - CAL_CSI2_COMPLEXIO_CFG_PWR_STATUS_MASK); - - if (current_state == target_state) - break; - - usleep_range(1000, 1100); - } - - if (i == 10) - phy_err(phy, "Failed to power %s complexio\n", - enable ? "up" : "down"); -} - -static void cal_camerarx_wait_reset(struct cal_camerarx *phy) -{ - unsigned long timeout; - - timeout = jiffies + msecs_to_jiffies(750); - while (time_before(jiffies, timeout)) { - if (cal_read_field(phy->cal, - CAL_CSI2_COMPLEXIO_CFG(phy->instance), - CAL_CSI2_COMPLEXIO_CFG_RESET_DONE_MASK) == - CAL_CSI2_COMPLEXIO_CFG_RESET_DONE_RESETCOMPLETED) - break; - usleep_range(500, 5000); - } - - if (cal_read_field(phy->cal, CAL_CSI2_COMPLEXIO_CFG(phy->instance), - CAL_CSI2_COMPLEXIO_CFG_RESET_DONE_MASK) != - CAL_CSI2_COMPLEXIO_CFG_RESET_DONE_RESETCOMPLETED) - phy_err(phy, "Timeout waiting for Complex IO reset done\n"); -} - -static void cal_camerarx_wait_stop_state(struct cal_camerarx *phy) -{ - unsigned long timeout; - - timeout = jiffies + msecs_to_jiffies(750); - while (time_before(jiffies, timeout)) { - if (cal_read_field(phy->cal, - CAL_CSI2_TIMING(phy->instance), - CAL_CSI2_TIMING_FORCE_RX_MODE_IO1_MASK) == 0) - break; - usleep_range(500, 5000); - } - - if (cal_read_field(phy->cal, CAL_CSI2_TIMING(phy->instance), - CAL_CSI2_TIMING_FORCE_RX_MODE_IO1_MASK) != 0) - phy_err(phy, "Timeout waiting for stop state\n"); -} - -int cal_camerarx_start(struct cal_camerarx *phy, const struct cal_fmt *fmt) -{ - s64 external_rate; - u32 sscounter; - u32 val; - int ret; - - external_rate = cal_camerarx_get_external_rate(phy); - if (external_rate < 0) - return external_rate; - - ret = v4l2_subdev_call(phy->sensor, core, s_power, 1); - if (ret < 0 && ret != -ENOIOCTLCMD && ret != -ENODEV) { - phy_err(phy, "power on failed in subdev\n"); - return ret; - } - - /* - * CSI-2 PHY Link Initialization Sequence, according to the DRA74xP / - * DRA75xP / DRA76xP / DRA77xP TRM. The DRA71x / DRA72x and the AM65x / - * DRA80xM TRMs have a a slightly simplified sequence. - */ - - /* - * 1. Configure all CSI-2 low level protocol registers to be ready to - * receive signals/data from the CSI-2 PHY. - * - * i.-v. Configure the lanes position and polarity. - */ - cal_camerarx_lane_config(phy); - - /* - * vi.-vii. Configure D-PHY mode, enable the required lanes and - * enable the CAMERARX clock. - */ - cal_camerarx_enable(phy); - - /* - * 2. CSI PHY and link initialization sequence. - * - * a. Deassert the CSI-2 PHY reset. Do not wait for reset completion - * at this point, as it requires the external sensor to send the - * CSI-2 HS clock. - */ - cal_write_field(phy->cal, CAL_CSI2_COMPLEXIO_CFG(phy->instance), - CAL_CSI2_COMPLEXIO_CFG_RESET_CTRL_OPERATIONAL, - CAL_CSI2_COMPLEXIO_CFG_RESET_CTRL_MASK); - phy_dbg(3, phy, "CAL_CSI2_COMPLEXIO_CFG(%d) = 0x%08x De-assert Complex IO Reset\n", - phy->instance, - cal_read(phy->cal, CAL_CSI2_COMPLEXIO_CFG(phy->instance))); - - /* Dummy read to allow SCP reset to complete. */ - camerarx_read(phy, CAL_CSI2_PHY_REG0); - - /* Program the PHY timing parameters. */ - cal_camerarx_config(phy, external_rate, fmt); - - /* - * b. Assert the FORCERXMODE signal. - * - * The stop-state-counter is based on fclk cycles, and we always use - * the x16 and x4 settings, so stop-state-timeout = - * fclk-cycle * 16 * 4 * counter. - * - * Stop-state-timeout must be more than 100us as per CSI-2 spec, so we - * calculate a timeout that's 100us (rounding up). - */ - sscounter = DIV_ROUND_UP(clk_get_rate(phy->cal->fclk), 10000 * 16 * 4); - - val = cal_read(phy->cal, CAL_CSI2_TIMING(phy->instance)); - cal_set_field(&val, 1, CAL_CSI2_TIMING_STOP_STATE_X16_IO1_MASK); - cal_set_field(&val, 1, CAL_CSI2_TIMING_STOP_STATE_X4_IO1_MASK); - cal_set_field(&val, sscounter, - CAL_CSI2_TIMING_STOP_STATE_COUNTER_IO1_MASK); - cal_write(phy->cal, CAL_CSI2_TIMING(phy->instance), val); - phy_dbg(3, phy, "CAL_CSI2_TIMING(%d) = 0x%08x Stop States\n", - phy->instance, - cal_read(phy->cal, CAL_CSI2_TIMING(phy->instance))); - - /* Assert the FORCERXMODE signal. */ - cal_write_field(phy->cal, CAL_CSI2_TIMING(phy->instance), - 1, CAL_CSI2_TIMING_FORCE_RX_MODE_IO1_MASK); - phy_dbg(3, phy, "CAL_CSI2_TIMING(%d) = 0x%08x Force RXMODE\n", - phy->instance, - cal_read(phy->cal, CAL_CSI2_TIMING(phy->instance))); - - /* - * c. Connect pull-down on CSI-2 PHY link (using pad control). - * - * This is not required on DRA71x, DRA72x, AM65x and DRA80xM. Not - * implemented. - */ - - /* - * d. Power up the CSI-2 PHY. - * e. Check whether the state status reaches the ON state. - */ - cal_camerarx_power(phy, true); - - /* - * Start the sensor to enable the CSI-2 HS clock. We can now wait for - * CSI-2 PHY reset to complete. - */ - ret = v4l2_subdev_call(phy->sensor, video, s_stream, 1); - if (ret) { - v4l2_subdev_call(phy->sensor, core, s_power, 0); - phy_err(phy, "stream on failed in subdev\n"); - return ret; - } - - cal_camerarx_wait_reset(phy); - - /* f. Wait for STOPSTATE=1 for all enabled lane modules. */ - cal_camerarx_wait_stop_state(phy); - - phy_dbg(1, phy, "CSI2_%u_REG1 = 0x%08x (bits 31-28 should be set)\n", - phy->instance, camerarx_read(phy, CAL_CSI2_PHY_REG1)); - - /* - * g. Disable pull-down on CSI-2 PHY link (using pad control). - * - * This is not required on DRA71x, DRA72x, AM65x and DRA80xM. Not - * implemented. - */ - - return 0; -} - -void cal_camerarx_stop(struct cal_camerarx *phy) -{ - unsigned int i; - int ret; - - cal_camerarx_power(phy, false); - - /* Assert Complex IO Reset */ - cal_write_field(phy->cal, CAL_CSI2_COMPLEXIO_CFG(phy->instance), - CAL_CSI2_COMPLEXIO_CFG_RESET_CTRL, - CAL_CSI2_COMPLEXIO_CFG_RESET_CTRL_MASK); - - /* Wait for power down completion */ - for (i = 0; i < 10; i++) { - if (cal_read_field(phy->cal, - CAL_CSI2_COMPLEXIO_CFG(phy->instance), - CAL_CSI2_COMPLEXIO_CFG_RESET_DONE_MASK) == - CAL_CSI2_COMPLEXIO_CFG_RESET_DONE_RESETONGOING) - break; - usleep_range(1000, 1100); - } - phy_dbg(3, phy, "CAL_CSI2_COMPLEXIO_CFG(%d) = 0x%08x Complex IO in Reset (%d) %s\n", - phy->instance, - cal_read(phy->cal, CAL_CSI2_COMPLEXIO_CFG(phy->instance)), i, - (i >= 10) ? "(timeout)" : ""); - - /* Disable the phy */ - cal_camerarx_disable(phy); - - if (v4l2_subdev_call(phy->sensor, video, s_stream, 0)) - phy_err(phy, "stream off failed in subdev\n"); - - ret = v4l2_subdev_call(phy->sensor, core, s_power, 0); - if (ret < 0 && ret != -ENOIOCTLCMD && ret != -ENODEV) - phy_err(phy, "power off failed in subdev\n"); -} - -/* - * Errata i913: CSI2 LDO Needs to be disabled when module is powered on - * - * Enabling CSI2 LDO shorts it to core supply. It is crucial the 2 CSI2 - * LDOs on the device are disabled if CSI-2 module is powered on - * (0x4845 B304 | 0x4845 B384 [28:27] = 0x1) or in ULPS (0x4845 B304 - * | 0x4845 B384 [28:27] = 0x2) mode. Common concerns include: high - * current draw on the module supply in active mode. - * - * Errata does not apply when CSI-2 module is powered off - * (0x4845 B304 | 0x4845 B384 [28:27] = 0x0). - * - * SW Workaround: - * Set the following register bits to disable the LDO, - * which is essentially CSI2 REG10 bit 6: - * - * Core 0: 0x4845 B828 = 0x0000 0040 - * Core 1: 0x4845 B928 = 0x0000 0040 - */ -static void cal_camerarx_i913_errata(struct cal_camerarx *phy) -{ - u32 reg10 = camerarx_read(phy, CAL_CSI2_PHY_REG10); - - cal_set_field(®10, 1, CAL_CSI2_PHY_REG10_I933_LDO_DISABLE_MASK); - - phy_dbg(1, phy, "CSI2_%d_REG10 = 0x%08x\n", phy->instance, reg10); - camerarx_write(phy, CAL_CSI2_PHY_REG10, reg10); -} - -/* - * Enable the expected IRQ sources - */ -void cal_camerarx_enable_irqs(struct cal_camerarx *phy) -{ - u32 val; - - const u32 cio_err_mask = - CAL_CSI2_COMPLEXIO_IRQ_LANE_ERRORS_MASK | - CAL_CSI2_COMPLEXIO_IRQ_FIFO_OVR_MASK | - CAL_CSI2_COMPLEXIO_IRQ_SHORT_PACKET_MASK | - CAL_CSI2_COMPLEXIO_IRQ_ECC_NO_CORRECTION_MASK; - - /* Enable CIO error irqs */ - cal_write(phy->cal, CAL_HL_IRQENABLE_SET(0), - CAL_HL_IRQ_CIO_MASK(phy->instance)); - cal_write(phy->cal, CAL_CSI2_COMPLEXIO_IRQENABLE(phy->instance), - cio_err_mask); - - /* Always enable OCPO error */ - cal_write(phy->cal, CAL_HL_IRQENABLE_SET(0), CAL_HL_IRQ_OCPO_ERR_MASK); - - /* Enable IRQ_WDMA_END 0/1 */ - val = 0; - cal_set_field(&val, 1, CAL_HL_IRQ_MASK(phy->instance)); - cal_write(phy->cal, CAL_HL_IRQENABLE_SET(1), val); - /* Enable IRQ_WDMA_START 0/1 */ - val = 0; - cal_set_field(&val, 1, CAL_HL_IRQ_MASK(phy->instance)); - cal_write(phy->cal, CAL_HL_IRQENABLE_SET(2), val); - /* Todo: Add VC_IRQ and CSI2_COMPLEXIO_IRQ handling */ - cal_write(phy->cal, CAL_CSI2_VC_IRQENABLE(0), 0xFF000000); -} - -void cal_camerarx_disable_irqs(struct cal_camerarx *phy) -{ - u32 val; - - /* Disable CIO error irqs */ - cal_write(phy->cal, CAL_HL_IRQENABLE_CLR(0), - CAL_HL_IRQ_CIO_MASK(phy->instance)); - cal_write(phy->cal, CAL_CSI2_COMPLEXIO_IRQENABLE(phy->instance), - 0); - - /* Disable IRQ_WDMA_END 0/1 */ - val = 0; - cal_set_field(&val, 1, CAL_HL_IRQ_MASK(phy->instance)); - cal_write(phy->cal, CAL_HL_IRQENABLE_CLR(1), val); - /* Disable IRQ_WDMA_START 0/1 */ - val = 0; - cal_set_field(&val, 1, CAL_HL_IRQ_MASK(phy->instance)); - cal_write(phy->cal, CAL_HL_IRQENABLE_CLR(2), val); - /* Todo: Add VC_IRQ and CSI2_COMPLEXIO_IRQ handling */ - cal_write(phy->cal, CAL_CSI2_VC_IRQENABLE(0), 0); -} - -void cal_camerarx_ppi_enable(struct cal_camerarx *phy) -{ - cal_write(phy->cal, CAL_CSI2_PPI_CTRL(phy->instance), BIT(3)); - cal_write_field(phy->cal, CAL_CSI2_PPI_CTRL(phy->instance), - 1, CAL_CSI2_PPI_CTRL_IF_EN_MASK); -} - -void cal_camerarx_ppi_disable(struct cal_camerarx *phy) -{ - cal_write_field(phy->cal, CAL_CSI2_PPI_CTRL(phy->instance), - 0, CAL_CSI2_PPI_CTRL_IF_EN_MASK); -} - -static int cal_camerarx_regmap_init(struct cal_dev *cal, - struct cal_camerarx *phy) -{ - const struct cal_camerarx_data *phy_data; - unsigned int i; - - if (!cal->data) - return -EINVAL; - - phy_data = &cal->data->camerarx[phy->instance]; - - for (i = 0; i < F_MAX_FIELDS; i++) { - struct reg_field field = { - .reg = cal->syscon_camerrx_offset, - .lsb = phy_data->fields[i].lsb, - .msb = phy_data->fields[i].msb, - }; - - /* - * Here we update the reg offset with the - * value found in DT - */ - phy->fields[i] = devm_regmap_field_alloc(cal->dev, - cal->syscon_camerrx, - field); - if (IS_ERR(phy->fields[i])) { - cal_err(cal, "Unable to allocate regmap fields\n"); - return PTR_ERR(phy->fields[i]); - } - } - - return 0; -} - -static int cal_camerarx_parse_dt(struct cal_camerarx *phy) -{ - struct v4l2_fwnode_endpoint *endpoint = &phy->endpoint; - struct device_node *ep_node; - char data_lanes[V4L2_FWNODE_CSI2_MAX_DATA_LANES * 2]; - unsigned int i; - int ret; - - /* - * Find the endpoint node for the port corresponding to the PHY - * instance, and parse its CSI-2-related properties. - */ - ep_node = of_graph_get_endpoint_by_regs(phy->cal->dev->of_node, - phy->instance, 0); - if (!ep_node) { - /* - * The endpoint is not mandatory, not all PHY instances need to - * be connected in DT. - */ - phy_dbg(3, phy, "Port has no endpoint\n"); - return 0; - } - - endpoint->bus_type = V4L2_MBUS_CSI2_DPHY; - ret = v4l2_fwnode_endpoint_parse(of_fwnode_handle(ep_node), endpoint); - if (ret < 0) { - phy_err(phy, "Failed to parse endpoint\n"); - goto done; - } - - for (i = 0; i < endpoint->bus.mipi_csi2.num_data_lanes; i++) { - unsigned int lane = endpoint->bus.mipi_csi2.data_lanes[i]; - - if (lane > 4) { - phy_err(phy, "Invalid position %u for data lane %u\n", - lane, i); - ret = -EINVAL; - goto done; - } - - data_lanes[i*2] = '0' + lane; - data_lanes[i*2+1] = ' '; - } - - data_lanes[i*2-1] = '\0'; - - phy_dbg(3, phy, - "CSI-2 bus: clock lane <%u>, data lanes <%s>, flags 0x%08x\n", - endpoint->bus.mipi_csi2.clock_lane, data_lanes, - endpoint->bus.mipi_csi2.flags); - - /* Retrieve the connected device and store it for later use. */ - phy->sensor_node = of_graph_get_remote_port_parent(ep_node); - if (!phy->sensor_node) { - phy_dbg(3, phy, "Can't get remote parent\n"); - ret = -EINVAL; - goto done; - } - - phy_dbg(1, phy, "Found connected device %pOFn\n", phy->sensor_node); - -done: - of_node_put(ep_node); - return ret; -} - -static struct cal_camerarx *cal_camerarx_create(struct cal_dev *cal, - unsigned int instance) -{ - struct platform_device *pdev = to_platform_device(cal->dev); - struct cal_camerarx *phy; - int ret; - - phy = kzalloc(sizeof(*phy), GFP_KERNEL); - if (!phy) - return ERR_PTR(-ENOMEM); - - phy->cal = cal; - phy->instance = instance; - - phy->res = platform_get_resource_byname(pdev, IORESOURCE_MEM, - (instance == 0) ? - "cal_rx_core0" : - "cal_rx_core1"); - phy->base = devm_ioremap_resource(cal->dev, phy->res); - if (IS_ERR(phy->base)) { - cal_err(cal, "failed to ioremap\n"); - ret = PTR_ERR(phy->base); - goto error; - } - - cal_dbg(1, cal, "ioresource %s at %pa - %pa\n", - phy->res->name, &phy->res->start, &phy->res->end); - - ret = cal_camerarx_regmap_init(cal, phy); - if (ret) - goto error; - - ret = cal_camerarx_parse_dt(phy); - if (ret) - goto error; - - return phy; - -error: - kfree(phy); - return ERR_PTR(ret); -} - -static void cal_camerarx_destroy(struct cal_camerarx *phy) -{ - if (!phy) - return; - - of_node_put(phy->sensor_node); - kfree(phy); -} - -static int cal_camerarx_init_regmap(struct cal_dev *cal) -{ - struct platform_device *pdev = to_platform_device(cal->dev); - struct device_node *np = cal->dev->of_node; - struct regmap_config config = { }; - struct regmap *syscon; - struct resource *res; - unsigned int offset; - void __iomem *base; - - syscon = syscon_regmap_lookup_by_phandle_args(np, "ti,camerrx-control", - 1, &offset); - if (!IS_ERR(syscon)) { - cal->syscon_camerrx = syscon; - cal->syscon_camerrx_offset = offset; - return 0; - } - - dev_warn(cal->dev, "failed to get ti,camerrx-control: %ld\n", - PTR_ERR(syscon)); - - /* - * Backward DTS compatibility. If syscon entry is not present then - * check if the camerrx_control resource is present. - */ - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, - "camerrx_control"); - base = devm_ioremap_resource(cal->dev, res); - if (IS_ERR(base)) { - cal_err(cal, "failed to ioremap camerrx_control\n"); - return PTR_ERR(base); - } - - cal_dbg(1, cal, "ioresource %s at %pa - %pa\n", - res->name, &res->start, &res->end); - - config.reg_bits = 32; - config.reg_stride = 4; - config.val_bits = 32; - config.max_register = resource_size(res) - 4; - - syscon = regmap_init_mmio(NULL, base, &config); - if (IS_ERR(syscon)) { - pr_err("regmap init failed\n"); - return PTR_ERR(syscon); - } - - /* - * In this case the base already point to the direct CM register so no - * need for an offset. - */ - cal->syscon_camerrx = syscon; - cal->syscon_camerrx_offset = 0; - - return 0; -} - /* ------------------------------------------------------------------ * Context Management * ------------------------------------------------------------------ @@ -1362,6 +682,63 @@ static void cal_get_hwinfo(struct cal_dev *cal) hwinfo, CAL_HL_HWINFO_VALUE); } +static int cal_init_camerarx_regmap(struct cal_dev *cal) +{ + struct platform_device *pdev = to_platform_device(cal->dev); + struct device_node *np = cal->dev->of_node; + struct regmap_config config = { }; + struct regmap *syscon; + struct resource *res; + unsigned int offset; + void __iomem *base; + + syscon = syscon_regmap_lookup_by_phandle_args(np, "ti,camerrx-control", + 1, &offset); + if (!IS_ERR(syscon)) { + cal->syscon_camerrx = syscon; + cal->syscon_camerrx_offset = offset; + return 0; + } + + dev_warn(cal->dev, "failed to get ti,camerrx-control: %ld\n", + PTR_ERR(syscon)); + + /* + * Backward DTS compatibility. If syscon entry is not present then + * check if the camerrx_control resource is present. + */ + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, + "camerrx_control"); + base = devm_ioremap_resource(cal->dev, res); + if (IS_ERR(base)) { + cal_err(cal, "failed to ioremap camerrx_control\n"); + return PTR_ERR(base); + } + + cal_dbg(1, cal, "ioresource %s at %pa - %pa\n", + res->name, &res->start, &res->end); + + config.reg_bits = 32; + config.reg_stride = 4; + config.val_bits = 32; + config.max_register = resource_size(res) - 4; + + syscon = regmap_init_mmio(NULL, base, &config); + if (IS_ERR(syscon)) { + pr_err("regmap init failed\n"); + return PTR_ERR(syscon); + } + + /* + * In this case the base already point to the direct CM register so no + * need for an offset. + */ + cal->syscon_camerrx = syscon; + cal->syscon_camerrx_offset = 0; + + return 0; +} + static int cal_probe(struct platform_device *pdev) { struct cal_dev *cal; @@ -1391,7 +768,7 @@ static int cal_probe(struct platform_device *pdev) return PTR_ERR(cal->fclk); } - ret = cal_camerarx_init_regmap(cal); + ret = cal_init_camerarx_regmap(cal); if (ret < 0) return ret; diff --git a/drivers/media/platform/ti-vpe/cal.h b/drivers/media/platform/ti-vpe/cal.h index 44f2836b6543e..e496083715d21 100644 --- a/drivers/media/platform/ti-vpe/cal.h +++ b/drivers/media/platform/ti-vpe/cal.h @@ -241,12 +241,17 @@ static inline void cal_set_field(u32 *valp, u32 field, u32 mask) void cal_quickdump_regs(struct cal_dev *cal); +void cal_camerarx_disable(struct cal_camerarx *phy); int cal_camerarx_start(struct cal_camerarx *phy, const struct cal_fmt *fmt); void cal_camerarx_stop(struct cal_camerarx *phy); void cal_camerarx_enable_irqs(struct cal_camerarx *phy); void cal_camerarx_disable_irqs(struct cal_camerarx *phy); void cal_camerarx_ppi_enable(struct cal_camerarx *phy); void cal_camerarx_ppi_disable(struct cal_camerarx *phy); +void cal_camerarx_i913_errata(struct cal_camerarx *phy); +struct cal_camerarx *cal_camerarx_create(struct cal_dev *cal, + unsigned int instance); +void cal_camerarx_destroy(struct cal_camerarx *phy); void cal_ctx_csi2_config(struct cal_ctx *ctx); void cal_ctx_pix_proc_config(struct cal_ctx *ctx); -- GitLab From 24385f9c6ee4e1e118046333b49637e5d337d943 Mon Sep 17 00:00:00 2001 From: Dafna Hirschfeld Date: Fri, 26 Jun 2020 10:51:38 +0200 Subject: [PATCH 0618/1476] media: staging: rkisp1: use a macro for the statistics flags mask The mask of the ready statistics flags is used in several places in the code using bitwise-or. Use a macro for that to make the code more readable. Signed-off-by: Dafna Hirschfeld Acked-by: Helen Koike Reviewed-by: Laurent Pinchart Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/rkisp1/rkisp1-common.h | 4 ++++ drivers/staging/media/rkisp1/rkisp1-isp.c | 5 +---- drivers/staging/media/rkisp1/rkisp1-stats.c | 14 +++----------- 3 files changed, 8 insertions(+), 15 deletions(-) diff --git a/drivers/staging/media/rkisp1/rkisp1-common.h b/drivers/staging/media/rkisp1/rkisp1-common.h index a9ede3f71ff33..43fe4dc2c7c48 100644 --- a/drivers/staging/media/rkisp1/rkisp1-common.h +++ b/drivers/staging/media/rkisp1/rkisp1-common.h @@ -45,6 +45,10 @@ #define RKISP1_MAX_BUS_CLK 8 +#define RKISP1_STATS_MEAS_MASK (RKISP1_CIF_ISP_AWB_DONE | \ + RKISP1_CIF_ISP_AFM_FIN | \ + RKISP1_CIF_ISP_EXP_END | \ + RKISP1_CIF_ISP_HIST_MEASURE_RDY) enum rkisp1_rsz_pad { RKISP1_RSZ_PAD_SINK, RKISP1_RSZ_PAD_SRC, diff --git a/drivers/staging/media/rkisp1/rkisp1-isp.c b/drivers/staging/media/rkisp1/rkisp1-isp.c index 76229ecbc41aa..6ec1e9816e9f2 100644 --- a/drivers/staging/media/rkisp1/rkisp1-isp.c +++ b/drivers/staging/media/rkisp1/rkisp1-isp.c @@ -1139,10 +1139,7 @@ void rkisp1_isp_isr(struct rkisp1_device *rkisp1) /* New frame from the sensor received */ isp_ris = rkisp1_read(rkisp1, RKISP1_CIF_ISP_RIS); - if (isp_ris & (RKISP1_CIF_ISP_AWB_DONE | - RKISP1_CIF_ISP_AFM_FIN | - RKISP1_CIF_ISP_EXP_END | - RKISP1_CIF_ISP_HIST_MEASURE_RDY)) + if (isp_ris & RKISP1_STATS_MEAS_MASK) rkisp1_stats_isr(&rkisp1->stats, isp_ris); } diff --git a/drivers/staging/media/rkisp1/rkisp1-stats.c b/drivers/staging/media/rkisp1/rkisp1-stats.c index 0616793ae3958..b19a6d9cdd4da 100644 --- a/drivers/staging/media/rkisp1/rkisp1-stats.c +++ b/drivers/staging/media/rkisp1/rkisp1-stats.c @@ -396,26 +396,18 @@ void rkisp1_stats_isr(struct rkisp1_stats *stats, u32 isp_ris) struct rkisp1_device *rkisp1 = stats->rkisp1; struct rkisp1_isp_readout_work *work; unsigned int isp_mis_tmp = 0; - u32 val; spin_lock(&stats->irq_lock); - val = RKISP1_CIF_ISP_AWB_DONE | RKISP1_CIF_ISP_AFM_FIN | - RKISP1_CIF_ISP_EXP_END | RKISP1_CIF_ISP_HIST_MEASURE_RDY; - rkisp1_write(rkisp1, val, RKISP1_CIF_ISP_ICR); + rkisp1_write(rkisp1, RKISP1_STATS_MEAS_MASK, RKISP1_CIF_ISP_ICR); isp_mis_tmp = rkisp1_read(rkisp1, RKISP1_CIF_ISP_MIS); - if (isp_mis_tmp & - (RKISP1_CIF_ISP_AWB_DONE | RKISP1_CIF_ISP_AFM_FIN | - RKISP1_CIF_ISP_EXP_END | RKISP1_CIF_ISP_HIST_MEASURE_RDY)) + if (isp_mis_tmp & RKISP1_STATS_MEAS_MASK) rkisp1->debug.stats_error++; if (!stats->is_streaming) goto unlock; - if (isp_ris & (RKISP1_CIF_ISP_AWB_DONE | - RKISP1_CIF_ISP_AFM_FIN | - RKISP1_CIF_ISP_EXP_END | - RKISP1_CIF_ISP_HIST_MEASURE_RDY)) { + if (isp_ris & RKISP1_STATS_MEAS_MASK) { work = kzalloc(sizeof(*work), GFP_ATOMIC); if (work) { INIT_WORK(&work->work, -- GitLab From 46c308a964fe6515845eacb8765a2c2e2b8c1570 Mon Sep 17 00:00:00 2001 From: Dafna Hirschfeld Date: Fri, 26 Jun 2020 10:51:39 +0200 Subject: [PATCH 0619/1476] media: staging: rkisp1: stats: replace spin_lock_irqsave with spin_lock_irq The function 'rkisp1_stats_vb2_stop_streaming' runs in user context therefore it is enough to use spin_lock_irq Signed-off-by: Dafna Hirschfeld Acked-by: Helen Koike Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/rkisp1/rkisp1-stats.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/staging/media/rkisp1/rkisp1-stats.c b/drivers/staging/media/rkisp1/rkisp1-stats.c index b19a6d9cdd4da..d4f0df4342e09 100644 --- a/drivers/staging/media/rkisp1/rkisp1-stats.c +++ b/drivers/staging/media/rkisp1/rkisp1-stats.c @@ -152,13 +152,12 @@ static void rkisp1_stats_vb2_stop_streaming(struct vb2_queue *vq) { struct rkisp1_stats *stats = vq->drv_priv; struct rkisp1_buffer *buf; - unsigned long flags; unsigned int i; /* Make sure no new work queued in isr before draining wq */ - spin_lock_irqsave(&stats->irq_lock, flags); + spin_lock_irq(&stats->irq_lock); stats->is_streaming = false; - spin_unlock_irqrestore(&stats->irq_lock, flags); + spin_unlock_irq(&stats->irq_lock); drain_workqueue(stats->readout_wq); -- GitLab From b512ab7a85268b3d43d8c504a3e84dbdd923c52b Mon Sep 17 00:00:00 2001 From: Dafna Hirschfeld Date: Fri, 26 Jun 2020 10:51:40 +0200 Subject: [PATCH 0620/1476] media: staging: rkisp1: stats: replace locks wq_lock, irq_lock with one lock This patch removes two locks in the rkisp1_stats struct: - The mutex 'wq_lock' that is used to protect the buffers list 'stat' - The spin_lock 'irq_lock' that is used to protect the variable 'is_streaming' It replaces them with one spin_lock 'lock' that protects both the buffers list and the 'is_streaming' variable. In later patch the reading of the statistics will move to the isr so there will be no need for the mutex 'wq_lock' Signed-off-by: Dafna Hirschfeld Acked-by: Helen Koike Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/rkisp1/rkisp1-common.h | 5 ++-- drivers/staging/media/rkisp1/rkisp1-stats.c | 25 +++++++++----------- 2 files changed, 13 insertions(+), 17 deletions(-) diff --git a/drivers/staging/media/rkisp1/rkisp1-common.h b/drivers/staging/media/rkisp1/rkisp1-common.h index 43fe4dc2c7c48..d62f5d6fbc812 100644 --- a/drivers/staging/media/rkisp1/rkisp1-common.h +++ b/drivers/staging/media/rkisp1/rkisp1-common.h @@ -181,7 +181,7 @@ struct rkisp1_capture { /* * struct rkisp1_stats - ISP Statistics device * - * @irq_lock: buffer queue lock + * @lock: locks the buffer list 'stat' and 'is_streaming' * @stat: stats buffer list * @readout_wq: workqueue for statistics information read */ @@ -189,13 +189,12 @@ struct rkisp1_stats { struct rkisp1_vdev_node vnode; struct rkisp1_device *rkisp1; - spinlock_t irq_lock; + spinlock_t lock; /* locks 'is_streaming', and 'stats' */ struct list_head stat; struct v4l2_format vdev_fmt; bool is_streaming; struct workqueue_struct *readout_wq; - struct mutex wq_lock; }; /* diff --git a/drivers/staging/media/rkisp1/rkisp1-stats.c b/drivers/staging/media/rkisp1/rkisp1-stats.c index d4f0df4342e09..58329e6b0598f 100644 --- a/drivers/staging/media/rkisp1/rkisp1-stats.c +++ b/drivers/staging/media/rkisp1/rkisp1-stats.c @@ -133,9 +133,9 @@ static void rkisp1_stats_vb2_buf_queue(struct vb2_buffer *vb) stats_buf->vaddr[0] = vb2_plane_vaddr(vb, 0); - mutex_lock(&stats_dev->wq_lock); + spin_lock_irq(&stats_dev->lock); list_add_tail(&stats_buf->queue, &stats_dev->stat); - mutex_unlock(&stats_dev->wq_lock); + spin_unlock_irq(&stats_dev->lock); } static int rkisp1_stats_vb2_buf_prepare(struct vb2_buffer *vb) @@ -155,13 +155,13 @@ static void rkisp1_stats_vb2_stop_streaming(struct vb2_queue *vq) unsigned int i; /* Make sure no new work queued in isr before draining wq */ - spin_lock_irq(&stats->irq_lock); + spin_lock_irq(&stats->lock); stats->is_streaming = false; - spin_unlock_irq(&stats->irq_lock); + spin_unlock_irq(&stats->lock); drain_workqueue(stats->readout_wq); - mutex_lock(&stats->wq_lock); + spin_lock_irq(&stats->lock); for (i = 0; i < RKISP1_ISP_STATS_REQ_BUFS_MAX; i++) { if (list_empty(&stats->stat)) break; @@ -170,7 +170,7 @@ static void rkisp1_stats_vb2_stop_streaming(struct vb2_queue *vq) list_del(&buf->queue); vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR); } - mutex_unlock(&stats->wq_lock); + spin_unlock_irq(&stats->lock); } static int @@ -340,14 +340,14 @@ rkisp1_stats_send_measurement(struct rkisp1_stats *stats, frame_sequence = meas_work->frame_id; } - mutex_lock(&stats->wq_lock); + spin_lock_irq(&stats->lock); /* get one empty buffer */ if (!list_empty(&stats->stat)) { cur_buf = list_first_entry(&stats->stat, struct rkisp1_buffer, queue); list_del(&cur_buf->queue); } - mutex_unlock(&stats->wq_lock); + spin_unlock_irq(&stats->lock); if (!cur_buf) return; @@ -396,7 +396,7 @@ void rkisp1_stats_isr(struct rkisp1_stats *stats, u32 isp_ris) struct rkisp1_isp_readout_work *work; unsigned int isp_mis_tmp = 0; - spin_lock(&stats->irq_lock); + spin_lock(&stats->lock); rkisp1_write(rkisp1, RKISP1_STATS_MEAS_MASK, RKISP1_CIF_ISP_ICR); @@ -425,7 +425,7 @@ void rkisp1_stats_isr(struct rkisp1_stats *stats, u32 isp_ris) } unlock: - spin_unlock(&stats->irq_lock); + spin_unlock(&stats->lock); } static void rkisp1_init_stats(struct rkisp1_stats *stats) @@ -445,10 +445,9 @@ int rkisp1_stats_register(struct rkisp1_stats *stats, int ret; stats->rkisp1 = rkisp1; - mutex_init(&stats->wq_lock); mutex_init(&node->vlock); INIT_LIST_HEAD(&stats->stat); - spin_lock_init(&stats->irq_lock); + spin_lock_init(&stats->lock); strscpy(vdev->name, RKISP1_STATS_DEV_NAME, sizeof(vdev->name)); @@ -495,7 +494,6 @@ err_cleanup_media_entity: err_release_queue: vb2_queue_release(vdev->queue); mutex_destroy(&node->vlock); - mutex_destroy(&stats->wq_lock); return ret; } @@ -509,5 +507,4 @@ void rkisp1_stats_unregister(struct rkisp1_stats *stats) media_entity_cleanup(&vdev->entity); vb2_queue_release(vdev->queue); mutex_destroy(&node->vlock); - mutex_destroy(&stats->wq_lock); } -- GitLab From 1fcc0244b5e6c4f557eecb656594ce29214da209 Mon Sep 17 00:00:00 2001 From: Dafna Hirschfeld Date: Fri, 26 Jun 2020 10:51:41 +0200 Subject: [PATCH 0621/1476] media: staging: rkisp1: stats: read the stats in the isr Currently the stats are read in a work queue. Defering the reading of the stats is not needed and it is fine to read them inside the irq handler. This patch fixes and remove the TODO item: 'Use threaded interrupt for rkisp1_stats_isr(), remove work queue.' Signed-off-by: Dafna Hirschfeld Acked-by: Helen Koike Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/rkisp1/TODO | 1 - drivers/staging/media/rkisp1/rkisp1-common.h | 3 - drivers/staging/media/rkisp1/rkisp1-stats.c | 87 ++------------------ 3 files changed, 7 insertions(+), 84 deletions(-) diff --git a/drivers/staging/media/rkisp1/TODO b/drivers/staging/media/rkisp1/TODO index c0cbec0a164d2..bdb1b8f735568 100644 --- a/drivers/staging/media/rkisp1/TODO +++ b/drivers/staging/media/rkisp1/TODO @@ -1,5 +1,4 @@ * Fix pad format size for statistics and parameters entities. -* Use threaded interrupt for rkisp1_stats_isr(), remove work queue. * Fix checkpatch errors. * Review and comment every lock * Handle quantization diff --git a/drivers/staging/media/rkisp1/rkisp1-common.h b/drivers/staging/media/rkisp1/rkisp1-common.h index d62f5d6fbc812..2603a5aa5210a 100644 --- a/drivers/staging/media/rkisp1/rkisp1-common.h +++ b/drivers/staging/media/rkisp1/rkisp1-common.h @@ -183,7 +183,6 @@ struct rkisp1_capture { * * @lock: locks the buffer list 'stat' and 'is_streaming' * @stat: stats buffer list - * @readout_wq: workqueue for statistics information read */ struct rkisp1_stats { struct rkisp1_vdev_node vnode; @@ -193,8 +192,6 @@ struct rkisp1_stats { struct list_head stat; struct v4l2_format vdev_fmt; bool is_streaming; - - struct workqueue_struct *readout_wq; }; /* diff --git a/drivers/staging/media/rkisp1/rkisp1-stats.c b/drivers/staging/media/rkisp1/rkisp1-stats.c index 58329e6b0598f..87e4104d20dde 100644 --- a/drivers/staging/media/rkisp1/rkisp1-stats.c +++ b/drivers/staging/media/rkisp1/rkisp1-stats.c @@ -18,21 +18,6 @@ #define RKISP1_ISP_STATS_REQ_BUFS_MIN 2 #define RKISP1_ISP_STATS_REQ_BUFS_MAX 8 -enum rkisp1_isp_readout_cmd { - RKISP1_ISP_READOUT_MEAS, - RKISP1_ISP_READOUT_META, -}; - -struct rkisp1_isp_readout_work { - struct work_struct work; - struct rkisp1_stats *stats; - - unsigned int frame_id; - unsigned int isp_ris; - enum rkisp1_isp_readout_cmd readout; - struct vb2_buffer *vb; -}; - static int rkisp1_stats_enum_fmt_meta_cap(struct file *file, void *priv, struct v4l2_fmtdesc *f) { @@ -154,14 +139,8 @@ static void rkisp1_stats_vb2_stop_streaming(struct vb2_queue *vq) struct rkisp1_buffer *buf; unsigned int i; - /* Make sure no new work queued in isr before draining wq */ spin_lock_irq(&stats->lock); stats->is_streaming = false; - spin_unlock_irq(&stats->lock); - - drain_workqueue(stats->readout_wq); - - spin_lock_irq(&stats->lock); for (i = 0; i < RKISP1_ISP_STATS_REQ_BUFS_MAX; i++) { if (list_empty(&stats->stat)) break; @@ -324,8 +303,7 @@ static void rkisp1_stats_get_bls_meas(struct rkisp1_stats *stats, } static void -rkisp1_stats_send_measurement(struct rkisp1_stats *stats, - struct rkisp1_isp_readout_work *meas_work) +rkisp1_stats_send_measurement(struct rkisp1_stats *stats, u32 isp_ris) { struct rkisp1_stat_buffer *cur_stat_buf; struct rkisp1_buffer *cur_buf = NULL; @@ -333,21 +311,12 @@ rkisp1_stats_send_measurement(struct rkisp1_stats *stats, atomic_read(&stats->rkisp1->isp.frame_sequence); u64 timestamp = ktime_get_ns(); - if (frame_sequence != meas_work->frame_id) { - dev_warn(stats->rkisp1->dev, - "Measurement late(%d, %d)\n", - frame_sequence, meas_work->frame_id); - frame_sequence = meas_work->frame_id; - } - - spin_lock_irq(&stats->lock); /* get one empty buffer */ if (!list_empty(&stats->stat)) { cur_buf = list_first_entry(&stats->stat, struct rkisp1_buffer, queue); list_del(&cur_buf->queue); } - spin_unlock_irq(&stats->lock); if (!cur_buf) return; @@ -355,18 +324,18 @@ rkisp1_stats_send_measurement(struct rkisp1_stats *stats, cur_stat_buf = (struct rkisp1_stat_buffer *)(cur_buf->vaddr[0]); - if (meas_work->isp_ris & RKISP1_CIF_ISP_AWB_DONE) + if (isp_ris & RKISP1_CIF_ISP_AWB_DONE) rkisp1_stats_get_awb_meas(stats, cur_stat_buf); - if (meas_work->isp_ris & RKISP1_CIF_ISP_AFM_FIN) + if (isp_ris & RKISP1_CIF_ISP_AFM_FIN) rkisp1_stats_get_afc_meas(stats, cur_stat_buf); - if (meas_work->isp_ris & RKISP1_CIF_ISP_EXP_END) { + if (isp_ris & RKISP1_CIF_ISP_EXP_END) { rkisp1_stats_get_aec_meas(stats, cur_stat_buf); rkisp1_stats_get_bls_meas(stats, cur_stat_buf); } - if (meas_work->isp_ris & RKISP1_CIF_ISP_HIST_MEASURE_RDY) + if (isp_ris & RKISP1_CIF_ISP_HIST_MEASURE_RDY) rkisp1_stats_get_hst_meas(stats, cur_stat_buf); vb2_set_plane_payload(&cur_buf->vb.vb2_buf, 0, @@ -376,24 +345,9 @@ rkisp1_stats_send_measurement(struct rkisp1_stats *stats, vb2_buffer_done(&cur_buf->vb.vb2_buf, VB2_BUF_STATE_DONE); } -static void rkisp1_stats_readout_work(struct work_struct *work) -{ - struct rkisp1_isp_readout_work *readout_work = - container_of(work, struct rkisp1_isp_readout_work, work); - struct rkisp1_stats *stats = readout_work->stats; - - if (readout_work->readout == RKISP1_ISP_READOUT_MEAS) - rkisp1_stats_send_measurement(stats, readout_work); - - kfree(readout_work); -} - void rkisp1_stats_isr(struct rkisp1_stats *stats, u32 isp_ris) { - unsigned int frame_sequence = - atomic_read(&stats->rkisp1->isp.frame_sequence); struct rkisp1_device *rkisp1 = stats->rkisp1; - struct rkisp1_isp_readout_work *work; unsigned int isp_mis_tmp = 0; spin_lock(&stats->lock); @@ -406,23 +360,8 @@ void rkisp1_stats_isr(struct rkisp1_stats *stats, u32 isp_ris) if (!stats->is_streaming) goto unlock; - if (isp_ris & RKISP1_STATS_MEAS_MASK) { - work = kzalloc(sizeof(*work), GFP_ATOMIC); - if (work) { - INIT_WORK(&work->work, - rkisp1_stats_readout_work); - work->readout = RKISP1_ISP_READOUT_MEAS; - work->stats = stats; - work->frame_id = frame_sequence; - work->isp_ris = isp_ris; - if (!queue_work(stats->readout_wq, - &work->work)) - kfree(work); - } else { - dev_err(stats->rkisp1->dev, - "Could not allocate work\n"); - } - } + if (isp_ris & RKISP1_STATS_MEAS_MASK) + rkisp1_stats_send_measurement(stats, isp_ris); unlock: spin_unlock(&stats->lock); @@ -476,19 +415,8 @@ int rkisp1_stats_register(struct rkisp1_stats *stats, goto err_cleanup_media_entity; } - stats->readout_wq = alloc_workqueue("measurement_queue", - WQ_UNBOUND | WQ_MEM_RECLAIM, - 1); - - if (!stats->readout_wq) { - ret = -ENOMEM; - goto err_unreg_vdev; - } - return 0; -err_unreg_vdev: - video_unregister_device(vdev); err_cleanup_media_entity: media_entity_cleanup(&vdev->entity); err_release_queue: @@ -502,7 +430,6 @@ void rkisp1_stats_unregister(struct rkisp1_stats *stats) struct rkisp1_vdev_node *node = &stats->vnode; struct video_device *vdev = &node->vdev; - destroy_workqueue(stats->readout_wq); video_unregister_device(vdev); media_entity_cleanup(&vdev->entity); vb2_queue_release(vdev->queue); -- GitLab From e4fa530fd4b1f31b46d51b6b245359e0116cb662 Mon Sep 17 00:00:00 2001 From: Dafna Hirschfeld Date: Tue, 7 Jul 2020 20:10:53 +0200 Subject: [PATCH 0622/1476] media: staging: rkisp1: remove duplicate macro definition The macro RKISP1_CIF_ISP_AWB_MODE_YCBCR_EN is defined twice. Remove the second define. Signed-off-by: Dafna Hirschfeld Acked-by: Helen Koike Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/rkisp1/rkisp1-regs.h | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/staging/media/rkisp1/rkisp1-regs.h b/drivers/staging/media/rkisp1/rkisp1-regs.h index 46018f435b6fd..9b8e616ea24cb 100644 --- a/drivers/staging/media/rkisp1/rkisp1-regs.h +++ b/drivers/staging/media/rkisp1/rkisp1-regs.h @@ -398,7 +398,6 @@ #define RKISP1_CIF_ISP_AWB_YMAX_READ(x) (((x) >> 2) & 1) #define RKISP1_CIF_ISP_AWB_MODE_RGB_EN ((1 << 31) | (0x2 << 0)) #define RKISP1_CIF_ISP_AWB_MODE_YCBCR_EN ((0 << 31) | (0x2 << 0)) -#define RKISP1_CIF_ISP_AWB_MODE_YCBCR_EN ((0 << 31) | (0x2 << 0)) #define RKISP1_CIF_ISP_AWB_MODE_MASK_NONE 0xFFFFFFFC #define RKISP1_CIF_ISP_AWB_MODE_READ(x) ((x) & 3) /* ISP_AWB_GAIN_RB, ISP_AWB_GAIN_G */ -- GitLab From 0b62c7db458ba97f93442a5ed39a2d636362d19f Mon Sep 17 00:00:00 2001 From: Dafna Hirschfeld Date: Tue, 7 Jul 2020 20:16:30 +0200 Subject: [PATCH 0623/1476] media: staging: rkisp1: fix typos in file rkisp1-config.h Fix two spelling typos in file rkisp1-config.h. Signed-off-by: Dafna Hirschfeld Acked-by: Helen Koike Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/rkisp1/uapi/rkisp1-config.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/staging/media/rkisp1/uapi/rkisp1-config.h b/drivers/staging/media/rkisp1/uapi/rkisp1-config.h index ca0d031b14ac2..8f9b061e5b6b6 100644 --- a/drivers/staging/media/rkisp1/uapi/rkisp1-config.h +++ b/drivers/staging/media/rkisp1/uapi/rkisp1-config.h @@ -68,7 +68,7 @@ #define RKISP1_CIF_ISP_BLS_FIX_MASK 0x00001fff /* - * Automatic white balance measurments + * Automatic white balance measurements */ #define RKISP1_CIF_ISP_AWB_MAX_GRID 1 #define RKISP1_CIF_ISP_AWB_MAX_FRAMES 7 @@ -408,7 +408,7 @@ struct rkisp1_cif_isp_flt_config { /** * struct rkisp1_cif_isp_bdm_config - Configuration used by Bayer DeMosaic * - * @demosaic_th: threshod for bayer demosaicing texture detection + * @demosaic_th: threshold for bayer demosaicing texture detection */ struct rkisp1_cif_isp_bdm_config { __u8 demosaic_th; -- GitLab From 3ebf271b1dee6df816bd8f2135218640c478dedd Mon Sep 17 00:00:00 2001 From: Ezequiel Garcia Date: Wed, 1 Jul 2020 15:16:02 +0200 Subject: [PATCH 0624/1476] hantro: h264: Remove unused macro definition The generic H264 reference list builder moved all the users of this macro, but left the macro. Remove it. Signed-off-by: Ezequiel Garcia Reviewed-by: Philipp Zabel Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/hantro/hantro_h264.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/staging/media/hantro/hantro_h264.c b/drivers/staging/media/hantro/hantro_h264.c index d561f125085a7..dd935d7009bf1 100644 --- a/drivers/staging/media/hantro/hantro_h264.c +++ b/drivers/staging/media/hantro/hantro_h264.c @@ -22,8 +22,6 @@ #define POC_BUFFER_SIZE 34 #define SCALING_LIST_SIZE (6 * 16 + 2 * 64) -#define HANTRO_CMP(a, b) ((a) < (b) ? -1 : 1) - /* Data structure describing auxiliary buffer format. */ struct hantro_h264_dec_priv_tbl { u32 cabac_table[CABAC_INIT_BUFFER_SIZE]; -- GitLab From 4df3a47e3422a9de1f3ce1a4ba8a0447a73e7567 Mon Sep 17 00:00:00 2001 From: Ezequiel Garcia Date: Wed, 1 Jul 2020 15:16:03 +0200 Subject: [PATCH 0625/1476] hantro: h264: Rename scaling list handling function Commit e17f08e31666 ("media: hantro: Do not reorder H264 scaling list") removed the scaling list reordering, which was wrong and not needed. However, the name of the function stayed, which is confusing for anyone reading the code. Rename from "reorder" to "assemble" which is cleaner. This is just a cosmetic cleanup. Signed-off-by: Ezequiel Garcia Reviewed-by: Philipp Zabel Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/hantro/hantro_h264.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/staging/media/hantro/hantro_h264.c b/drivers/staging/media/hantro/hantro_h264.c index dd935d7009bf1..194d058480777 100644 --- a/drivers/staging/media/hantro/hantro_h264.c +++ b/drivers/staging/media/hantro/hantro_h264.c @@ -193,7 +193,7 @@ static const u32 h264_cabac_table[] = { }; static void -reorder_scaling_list(struct hantro_ctx *ctx) +assemble_scaling_list(struct hantro_ctx *ctx) { const struct hantro_h264_dec_ctrls *ctrls = &ctx->h264_dec.ctrls; const struct v4l2_ctrl_h264_scaling_matrix *scaling = ctrls->scaling; @@ -235,7 +235,7 @@ static void prepare_table(struct hantro_ctx *ctx) tbl->poc[32] = dec_param->top_field_order_cnt; tbl->poc[33] = dec_param->bottom_field_order_cnt; - reorder_scaling_list(ctx); + assemble_scaling_list(ctx); } static bool dpb_entry_match(const struct v4l2_h264_dpb_entry *a, -- GitLab From 21f0315b7b3ee6ca909d81a963744671fb27bf71 Mon Sep 17 00:00:00 2001 From: Ezequiel Garcia Date: Wed, 1 Jul 2020 15:16:04 +0200 Subject: [PATCH 0626/1476] hantro: Rework how encoder and decoder are identified So far we've been using the .buf_finish hook to distinguish decoder from encoder. This is unnecessarily obfuscated. Moreover, we want to move the buf_finish, so use a cleaner scheme to distinguish the driver decoder/encoder type. Signed-off-by: Ezequiel Garcia Reviewed-by: Philipp Zabel Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/hantro/hantro.h | 6 ++--- drivers/staging/media/hantro/hantro_drv.c | 9 +++---- drivers/staging/media/hantro/hantro_v4l2.c | 28 +++++++++++----------- 3 files changed, 20 insertions(+), 23 deletions(-) diff --git a/drivers/staging/media/hantro/hantro.h b/drivers/staging/media/hantro/hantro.h index 3005207fc6fb8..2284e23d8500c 100644 --- a/drivers/staging/media/hantro/hantro.h +++ b/drivers/staging/media/hantro/hantro.h @@ -199,6 +199,7 @@ struct hantro_dev { * * @dev: VPU driver data to which the context belongs. * @fh: V4L2 file handler. + * @is_encoder: Decoder or encoder context? * * @sequence_cap: Sequence counter for capture queue * @sequence_out: Sequence counter for output queue @@ -223,6 +224,7 @@ struct hantro_dev { struct hantro_ctx { struct hantro_dev *dev; struct v4l2_fh fh; + bool is_encoder; u32 sequence_cap; u32 sequence_out; @@ -399,8 +401,6 @@ static inline void hantro_reg_write_s(struct hantro_dev *vpu, vdpu_write(vpu, vdpu_read_mask(vpu, reg, val), reg->base); } -bool hantro_is_encoder_ctx(const struct hantro_ctx *ctx); - void *hantro_get_ctrl(struct hantro_ctx *ctx, u32 id); dma_addr_t hantro_get_ref(struct hantro_ctx *ctx, u64 ts); @@ -420,7 +420,7 @@ static inline bool hantro_needs_postproc(const struct hantro_ctx *ctx, const struct hantro_fmt *fmt) { - return !hantro_is_encoder_ctx(ctx) && fmt->fourcc != V4L2_PIX_FMT_NV12; + return !ctx->is_encoder && fmt->fourcc != V4L2_PIX_FMT_NV12; } static inline dma_addr_t diff --git a/drivers/staging/media/hantro/hantro_drv.c b/drivers/staging/media/hantro/hantro_drv.c index 0db8ad4551601..9145d02e5d3cc 100644 --- a/drivers/staging/media/hantro/hantro_drv.c +++ b/drivers/staging/media/hantro/hantro_drv.c @@ -195,11 +195,6 @@ err_cancel_job: hantro_job_finish(ctx->dev, ctx, 0, VB2_BUF_STATE_ERROR); } -bool hantro_is_encoder_ctx(const struct hantro_ctx *ctx) -{ - return ctx->buf_finish == hantro_enc_buf_finish; -} - static struct v4l2_m2m_ops vpu_m2m_ops = { .device_run = device_run, }; @@ -240,7 +235,7 @@ queue_init(void *priv, struct vb2_queue *src_vq, struct vb2_queue *dst_vq) * * For the DMA destination buffer, we use a bounce buffer. */ - if (hantro_is_encoder_ctx(ctx)) { + if (ctx->is_encoder) { dst_vq->mem_ops = &vb2_vmalloc_memops; } else { dst_vq->bidirectional = true; @@ -420,8 +415,10 @@ static int hantro_open(struct file *filp) if (func->id == MEDIA_ENT_F_PROC_VIDEO_ENCODER) { allowed_codecs = vpu->variant->codec & HANTRO_ENCODERS; ctx->buf_finish = hantro_enc_buf_finish; + ctx->is_encoder = true; } else if (func->id == MEDIA_ENT_F_PROC_VIDEO_DECODER) { allowed_codecs = vpu->variant->codec & HANTRO_DECODERS; + ctx->is_encoder = false; } else { ret = -ENODEV; goto err_ctx_free; diff --git a/drivers/staging/media/hantro/hantro_v4l2.c b/drivers/staging/media/hantro/hantro_v4l2.c index 63859e8a0923e..b668a82d40ad4 100644 --- a/drivers/staging/media/hantro/hantro_v4l2.c +++ b/drivers/staging/media/hantro/hantro_v4l2.c @@ -40,7 +40,7 @@ hantro_get_formats(const struct hantro_ctx *ctx, unsigned int *num_fmts) { const struct hantro_fmt *formats; - if (hantro_is_encoder_ctx(ctx)) { + if (ctx->is_encoder) { formats = ctx->dev->variant->enc_fmts; *num_fmts = ctx->dev->variant->num_enc_fmts; } else { @@ -55,7 +55,7 @@ static const struct hantro_fmt * hantro_get_postproc_formats(const struct hantro_ctx *ctx, unsigned int *num_fmts) { - if (hantro_is_encoder_ctx(ctx)) { + if (ctx->is_encoder) { *num_fmts = 0; return NULL; } @@ -158,7 +158,7 @@ static int vidioc_enum_fmt(struct file *file, void *priv, * not MODE_NONE. * - on the output side we want to filter out all MODE_NONE formats. */ - skip_mode_none = capture == hantro_is_encoder_ctx(ctx); + skip_mode_none = capture == ctx->is_encoder; formats = hantro_get_formats(ctx, &num_fmts); for (i = 0; i < num_fmts; i++) { @@ -240,7 +240,7 @@ static int hantro_try_fmt(const struct hantro_ctx *ctx, bool capture = V4L2_TYPE_IS_CAPTURE(type); bool coded; - coded = capture == hantro_is_encoder_ctx(ctx); + coded = capture == ctx->is_encoder; vpu_debug(4, "trying format %c%c%c%c\n", (pix_mp->pixelformat & 0x7f), @@ -257,7 +257,7 @@ static int hantro_try_fmt(const struct hantro_ctx *ctx, if (coded) { pix_mp->num_planes = 1; vpu_fmt = fmt; - } else if (hantro_is_encoder_ctx(ctx)) { + } else if (ctx->is_encoder) { vpu_fmt = ctx->vpu_dst_fmt; } else { vpu_fmt = ctx->vpu_src_fmt; @@ -330,7 +330,7 @@ hantro_reset_encoded_fmt(struct hantro_ctx *ctx) vpu_fmt = hantro_get_default_fmt(ctx, true); - if (hantro_is_encoder_ctx(ctx)) { + if (ctx->is_encoder) { ctx->vpu_dst_fmt = vpu_fmt; fmt = &ctx->dst_fmt; } else { @@ -341,7 +341,7 @@ hantro_reset_encoded_fmt(struct hantro_ctx *ctx) hantro_reset_fmt(fmt, vpu_fmt); fmt->width = vpu_fmt->frmsize.min_width; fmt->height = vpu_fmt->frmsize.min_height; - if (hantro_is_encoder_ctx(ctx)) + if (ctx->is_encoder) hantro_set_fmt_cap(ctx, fmt); else hantro_set_fmt_out(ctx, fmt); @@ -355,7 +355,7 @@ hantro_reset_raw_fmt(struct hantro_ctx *ctx) raw_vpu_fmt = hantro_get_default_fmt(ctx, false); - if (hantro_is_encoder_ctx(ctx)) { + if (ctx->is_encoder) { ctx->vpu_src_fmt = raw_vpu_fmt; raw_fmt = &ctx->src_fmt; encoded_fmt = &ctx->dst_fmt; @@ -368,7 +368,7 @@ hantro_reset_raw_fmt(struct hantro_ctx *ctx) hantro_reset_fmt(raw_fmt, raw_vpu_fmt); raw_fmt->width = encoded_fmt->width; raw_fmt->width = encoded_fmt->width; - if (hantro_is_encoder_ctx(ctx)) + if (ctx->is_encoder) hantro_set_fmt_out(ctx, raw_fmt); else hantro_set_fmt_cap(ctx, raw_fmt); @@ -409,7 +409,7 @@ static int hantro_set_fmt_out(struct hantro_ctx *ctx, if (ret) return ret; - if (!hantro_is_encoder_ctx(ctx)) { + if (!ctx->is_encoder) { struct vb2_queue *peer_vq; /* @@ -450,7 +450,7 @@ static int hantro_set_fmt_out(struct hantro_ctx *ctx, * Note that hantro_reset_raw_fmt() also propagates size * changes to the raw format. */ - if (!hantro_is_encoder_ctx(ctx)) + if (!ctx->is_encoder) hantro_reset_raw_fmt(ctx); /* Colorimetry information are always propagated. */ @@ -479,7 +479,7 @@ static int hantro_set_fmt_cap(struct hantro_ctx *ctx, if (vb2_is_busy(vq)) return -EBUSY; - if (hantro_is_encoder_ctx(ctx)) { + if (ctx->is_encoder) { struct vb2_queue *peer_vq; /* @@ -512,7 +512,7 @@ static int hantro_set_fmt_cap(struct hantro_ctx *ctx, * Note that hantro_reset_raw_fmt() also propagates size * changes to the raw format. */ - if (hantro_is_encoder_ctx(ctx)) + if (ctx->is_encoder) hantro_reset_raw_fmt(ctx); /* Colorimetry information are always propagated. */ @@ -655,7 +655,7 @@ static bool hantro_vq_is_coded(struct vb2_queue *q) { struct hantro_ctx *ctx = vb2_get_drv_priv(q); - return hantro_is_encoder_ctx(ctx) != V4L2_TYPE_IS_OUTPUT(q->type); + return ctx->is_encoder != V4L2_TYPE_IS_OUTPUT(q->type); } static int hantro_start_streaming(struct vb2_queue *q, unsigned int count) -- GitLab From e765dba11ec26d7ea42974ec4d470b5ce00be3de Mon Sep 17 00:00:00 2001 From: Ezequiel Garcia Date: Wed, 1 Jul 2020 15:16:05 +0200 Subject: [PATCH 0627/1476] hantro: Move hantro_enc_buf_finish to JPEG codec_ops.done hantro_enc_buf_finish is used only for JPEG, and so should be moved to JPEG codec_ops.done. This cleanup is also taking care of addressing a subtle issue: checking the non-NULL bounce buffer using ctx->jpeg_enc, which is a member of a union is confusing and error-prone. Note that the issue is currently innocuous because an encoder context only supports JPEG. The codec_ops.done has an argument that codec-specific code shouldn't need, so drop that as well. Signed-off-by: Ezequiel Garcia Reviewed-by: Philipp Zabel Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/hantro/hantro.h | 7 ---- drivers/staging/media/hantro/hantro_drv.c | 37 ++----------------- .../staging/media/hantro/hantro_h1_jpeg_enc.c | 17 +++++++++ drivers/staging/media/hantro/hantro_hw.h | 3 +- drivers/staging/media/hantro/rk3288_vpu_hw.c | 1 + 5 files changed, 24 insertions(+), 41 deletions(-) diff --git a/drivers/staging/media/hantro/hantro.h b/drivers/staging/media/hantro/hantro.h index 2284e23d8500c..65f9f7ea7dcfc 100644 --- a/drivers/staging/media/hantro/hantro.h +++ b/drivers/staging/media/hantro/hantro.h @@ -212,9 +212,6 @@ struct hantro_dev { * @ctrl_handler: Control handler used to register controls. * @jpeg_quality: User-specified JPEG compression quality. * - * @buf_finish: Buffer finish. This depends on encoder or decoder - * context, and it's called right before - * calling v4l2_m2m_job_finish. * @codec_ops: Set of operations related to codec mode. * @postproc: Post-processing context. * @jpeg_enc: JPEG-encoding context. @@ -237,10 +234,6 @@ struct hantro_ctx { struct v4l2_ctrl_handler ctrl_handler; int jpeg_quality; - int (*buf_finish)(struct hantro_ctx *ctx, - struct vb2_buffer *buf, - unsigned int bytesused); - const struct hantro_codec_ops *codec_ops; struct hantro_postproc_ctx postproc; diff --git a/drivers/staging/media/hantro/hantro_drv.c b/drivers/staging/media/hantro/hantro_drv.c index 9145d02e5d3cc..88b5c5989d83c 100644 --- a/drivers/staging/media/hantro/hantro_drv.c +++ b/drivers/staging/media/hantro/hantro_drv.c @@ -56,37 +56,12 @@ dma_addr_t hantro_get_ref(struct hantro_ctx *ctx, u64 ts) return hantro_get_dec_buf_addr(ctx, buf); } -static int -hantro_enc_buf_finish(struct hantro_ctx *ctx, struct vb2_buffer *buf, - unsigned int bytesused) -{ - size_t avail_size; - - avail_size = vb2_plane_size(buf, 0) - ctx->vpu_dst_fmt->header_size; - if (bytesused > avail_size) - return -EINVAL; - /* - * The bounce buffer is only for the JPEG encoder. - * TODO: Rework the JPEG encoder to eliminate the need - * for a bounce buffer. - */ - if (ctx->jpeg_enc.bounce_buffer.cpu) { - memcpy(vb2_plane_vaddr(buf, 0) + - ctx->vpu_dst_fmt->header_size, - ctx->jpeg_enc.bounce_buffer.cpu, bytesused); - } - buf->planes[0].bytesused = - ctx->vpu_dst_fmt->header_size + bytesused; - return 0; -} - static void hantro_job_finish(struct hantro_dev *vpu, struct hantro_ctx *ctx, unsigned int bytesused, enum vb2_buffer_state result) { struct vb2_v4l2_buffer *src, *dst; - int ret; pm_runtime_mark_last_busy(vpu->dev); pm_runtime_put_autosuspend(vpu->dev); @@ -103,12 +78,6 @@ static void hantro_job_finish(struct hantro_dev *vpu, src->sequence = ctx->sequence_out++; dst->sequence = ctx->sequence_cap++; - if (ctx->buf_finish) { - ret = ctx->buf_finish(ctx, &dst->vb2_buf, bytesused); - if (ret) - result = VB2_BUF_STATE_ERROR; - } - v4l2_m2m_buf_done_and_job_finish(ctx->dev->m2m_dev, ctx->fh.m2m_ctx, result); } @@ -124,8 +93,11 @@ void hantro_irq_done(struct hantro_dev *vpu, unsigned int bytesused, * the timeout expired. The watchdog is running, * and will take care of finishing the job. */ - if (cancel_delayed_work(&vpu->watchdog_work)) + if (cancel_delayed_work(&vpu->watchdog_work)) { + if (result == VB2_BUF_STATE_DONE && ctx->codec_ops->done) + ctx->codec_ops->done(ctx); hantro_job_finish(vpu, ctx, bytesused, result); + } } void hantro_watchdog(struct work_struct *work) @@ -414,7 +386,6 @@ static int hantro_open(struct file *filp) ctx->dev = vpu; if (func->id == MEDIA_ENT_F_PROC_VIDEO_ENCODER) { allowed_codecs = vpu->variant->codec & HANTRO_ENCODERS; - ctx->buf_finish = hantro_enc_buf_finish; ctx->is_encoder = true; } else if (func->id == MEDIA_ENT_F_PROC_VIDEO_DECODER) { allowed_codecs = vpu->variant->codec & HANTRO_DECODERS; diff --git a/drivers/staging/media/hantro/hantro_h1_jpeg_enc.c b/drivers/staging/media/hantro/hantro_h1_jpeg_enc.c index b224184368239..b88dc4ed06db7 100644 --- a/drivers/staging/media/hantro/hantro_h1_jpeg_enc.c +++ b/drivers/staging/media/hantro/hantro_h1_jpeg_enc.c @@ -137,3 +137,20 @@ void hantro_h1_jpeg_enc_run(struct hantro_ctx *ctx) vepu_write(vpu, reg, H1_REG_ENC_CTRL); } + +void hantro_jpeg_enc_done(struct hantro_ctx *ctx) +{ + struct hantro_dev *vpu = ctx->dev; + u32 bytesused = vepu_read(vpu, H1_REG_STR_BUF_LIMIT) / 8; + struct vb2_v4l2_buffer *dst_buf = hantro_get_dst_buf(ctx); + + /* + * TODO: Rework the JPEG encoder to eliminate the need + * for a bounce buffer. + */ + memcpy(vb2_plane_vaddr(&dst_buf->vb2_buf, 0) + + ctx->vpu_dst_fmt->header_size, + ctx->jpeg_enc.bounce_buffer.cpu, bytesused); + vb2_set_plane_payload(&dst_buf->vb2_buf, 0, + ctx->vpu_dst_fmt->header_size + bytesused); +} diff --git a/drivers/staging/media/hantro/hantro_hw.h b/drivers/staging/media/hantro/hantro_hw.h index 4053d8710e045..2d6323cd67322 100644 --- a/drivers/staging/media/hantro/hantro_hw.h +++ b/drivers/staging/media/hantro/hantro_hw.h @@ -138,7 +138,7 @@ struct hantro_codec_ops { int (*init)(struct hantro_ctx *ctx); void (*exit)(struct hantro_ctx *ctx); void (*run)(struct hantro_ctx *ctx); - void (*done)(struct hantro_ctx *ctx, enum vb2_buffer_state); + void (*done)(struct hantro_ctx *ctx); void (*reset)(struct hantro_ctx *ctx); }; @@ -172,6 +172,7 @@ void hantro_h1_jpeg_enc_run(struct hantro_ctx *ctx); void rk3399_vpu_jpeg_enc_run(struct hantro_ctx *ctx); int hantro_jpeg_enc_init(struct hantro_ctx *ctx); void hantro_jpeg_enc_exit(struct hantro_ctx *ctx); +void hantro_jpeg_enc_done(struct hantro_ctx *ctx); dma_addr_t hantro_h264_get_ref_buf(struct hantro_ctx *ctx, unsigned int dpb_idx); diff --git a/drivers/staging/media/hantro/rk3288_vpu_hw.c b/drivers/staging/media/hantro/rk3288_vpu_hw.c index 2f914b37b9e56..b1cf2abb972f9 100644 --- a/drivers/staging/media/hantro/rk3288_vpu_hw.c +++ b/drivers/staging/media/hantro/rk3288_vpu_hw.c @@ -180,6 +180,7 @@ static const struct hantro_codec_ops rk3288_vpu_codec_ops[] = { .run = hantro_h1_jpeg_enc_run, .reset = rk3288_vpu_enc_reset, .init = hantro_jpeg_enc_init, + .done = hantro_jpeg_enc_done, .exit = hantro_jpeg_enc_exit, }, [HANTRO_MODE_H264_DEC] = { -- GitLab From b72a6342dd240ce8e15b7acf1c38c67a0c56092b Mon Sep 17 00:00:00 2001 From: Ezequiel Garcia Date: Wed, 1 Jul 2020 15:16:06 +0200 Subject: [PATCH 0628/1476] hantro: Remove unused bytesused argument The driver doesn't need the bytesused argument. For decoders, the plane bytesused is known and therefore, buf_prepare is used to set it. For encoders, it's handled by the codec_ops.done hook. Signed-off-by: Ezequiel Garcia Reviewed-by: Philipp Zabel Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/hantro/hantro_drv.c | 9 ++++----- drivers/staging/media/hantro/hantro_hw.h | 2 +- drivers/staging/media/hantro/imx8m_vpu_hw.c | 2 +- drivers/staging/media/hantro/rk3288_vpu_hw.c | 7 +++---- drivers/staging/media/hantro/rk3399_vpu_hw.c | 7 +++---- 5 files changed, 12 insertions(+), 15 deletions(-) diff --git a/drivers/staging/media/hantro/hantro_drv.c b/drivers/staging/media/hantro/hantro_drv.c index 88b5c5989d83c..34367b169011d 100644 --- a/drivers/staging/media/hantro/hantro_drv.c +++ b/drivers/staging/media/hantro/hantro_drv.c @@ -58,7 +58,6 @@ dma_addr_t hantro_get_ref(struct hantro_ctx *ctx, u64 ts) static void hantro_job_finish(struct hantro_dev *vpu, struct hantro_ctx *ctx, - unsigned int bytesused, enum vb2_buffer_state result) { struct vb2_v4l2_buffer *src, *dst; @@ -82,7 +81,7 @@ static void hantro_job_finish(struct hantro_dev *vpu, result); } -void hantro_irq_done(struct hantro_dev *vpu, unsigned int bytesused, +void hantro_irq_done(struct hantro_dev *vpu, enum vb2_buffer_state result) { struct hantro_ctx *ctx = @@ -96,7 +95,7 @@ void hantro_irq_done(struct hantro_dev *vpu, unsigned int bytesused, if (cancel_delayed_work(&vpu->watchdog_work)) { if (result == VB2_BUF_STATE_DONE && ctx->codec_ops->done) ctx->codec_ops->done(ctx); - hantro_job_finish(vpu, ctx, bytesused, result); + hantro_job_finish(vpu, ctx, result); } } @@ -111,7 +110,7 @@ void hantro_watchdog(struct work_struct *work) if (ctx) { vpu_err("frame processing timed out!\n"); ctx->codec_ops->reset(ctx); - hantro_job_finish(vpu, ctx, 0, VB2_BUF_STATE_ERROR); + hantro_job_finish(vpu, ctx, VB2_BUF_STATE_ERROR); } } @@ -164,7 +163,7 @@ static void device_run(void *priv) return; err_cancel_job: - hantro_job_finish(ctx->dev, ctx, 0, VB2_BUF_STATE_ERROR); + hantro_job_finish(ctx->dev, ctx, VB2_BUF_STATE_ERROR); } static struct v4l2_m2m_ops vpu_m2m_ops = { diff --git a/drivers/staging/media/hantro/hantro_hw.h b/drivers/staging/media/hantro/hantro_hw.h index 2d6323cd67322..f066de6b592d0 100644 --- a/drivers/staging/media/hantro/hantro_hw.h +++ b/drivers/staging/media/hantro/hantro_hw.h @@ -163,7 +163,7 @@ extern const u32 hantro_vp8_dec_mc_filter[8][6]; void hantro_watchdog(struct work_struct *work); void hantro_run(struct hantro_ctx *ctx); -void hantro_irq_done(struct hantro_dev *vpu, unsigned int bytesused, +void hantro_irq_done(struct hantro_dev *vpu, enum vb2_buffer_state result); void hantro_start_prepare_run(struct hantro_ctx *ctx); void hantro_end_prepare_run(struct hantro_ctx *ctx); diff --git a/drivers/staging/media/hantro/imx8m_vpu_hw.c b/drivers/staging/media/hantro/imx8m_vpu_hw.c index cb2420c5526ee..c222de075ef44 100644 --- a/drivers/staging/media/hantro/imx8m_vpu_hw.c +++ b/drivers/staging/media/hantro/imx8m_vpu_hw.c @@ -143,7 +143,7 @@ static irqreturn_t imx8m_vpu_g1_irq(int irq, void *dev_id) vdpu_write(vpu, 0, G1_REG_INTERRUPT); vdpu_write(vpu, G1_REG_CONFIG_DEC_CLK_GATE_E, G1_REG_CONFIG); - hantro_irq_done(vpu, 0, state); + hantro_irq_done(vpu, state); return IRQ_HANDLED; } diff --git a/drivers/staging/media/hantro/rk3288_vpu_hw.c b/drivers/staging/media/hantro/rk3288_vpu_hw.c index b1cf2abb972f9..7b299ee3e93d7 100644 --- a/drivers/staging/media/hantro/rk3288_vpu_hw.c +++ b/drivers/staging/media/hantro/rk3288_vpu_hw.c @@ -113,17 +113,16 @@ static irqreturn_t rk3288_vepu_irq(int irq, void *dev_id) { struct hantro_dev *vpu = dev_id; enum vb2_buffer_state state; - u32 status, bytesused; + u32 status; status = vepu_read(vpu, H1_REG_INTERRUPT); - bytesused = vepu_read(vpu, H1_REG_STR_BUF_LIMIT) / 8; state = (status & H1_REG_INTERRUPT_FRAME_RDY) ? VB2_BUF_STATE_DONE : VB2_BUF_STATE_ERROR; vepu_write(vpu, 0, H1_REG_INTERRUPT); vepu_write(vpu, 0, H1_REG_AXI_CTRL); - hantro_irq_done(vpu, bytesused, state); + hantro_irq_done(vpu, state); return IRQ_HANDLED; } @@ -141,7 +140,7 @@ static irqreturn_t rk3288_vdpu_irq(int irq, void *dev_id) vdpu_write(vpu, 0, G1_REG_INTERRUPT); vdpu_write(vpu, G1_REG_CONFIG_DEC_CLK_GATE_E, G1_REG_CONFIG); - hantro_irq_done(vpu, 0, state); + hantro_irq_done(vpu, state); return IRQ_HANDLED; } diff --git a/drivers/staging/media/hantro/rk3399_vpu_hw.c b/drivers/staging/media/hantro/rk3399_vpu_hw.c index 9ac1f2cb6a16b..7a7962cf771e0 100644 --- a/drivers/staging/media/hantro/rk3399_vpu_hw.c +++ b/drivers/staging/media/hantro/rk3399_vpu_hw.c @@ -92,17 +92,16 @@ static irqreturn_t rk3399_vepu_irq(int irq, void *dev_id) { struct hantro_dev *vpu = dev_id; enum vb2_buffer_state state; - u32 status, bytesused; + u32 status; status = vepu_read(vpu, VEPU_REG_INTERRUPT); - bytesused = vepu_read(vpu, VEPU_REG_STR_BUF_LIMIT) / 8; state = (status & VEPU_REG_INTERRUPT_FRAME_READY) ? VB2_BUF_STATE_DONE : VB2_BUF_STATE_ERROR; vepu_write(vpu, 0, VEPU_REG_INTERRUPT); vepu_write(vpu, 0, VEPU_REG_AXI_CTRL); - hantro_irq_done(vpu, bytesused, state); + hantro_irq_done(vpu, state); return IRQ_HANDLED; } @@ -120,7 +119,7 @@ static irqreturn_t rk3399_vdpu_irq(int irq, void *dev_id) vdpu_write(vpu, 0, VDPU_REG_INTERRUPT); vdpu_write(vpu, 0, VDPU_REG_AXI_CTRL); - hantro_irq_done(vpu, 0, state); + hantro_irq_done(vpu, state); return IRQ_HANDLED; } -- GitLab From 46d7aaebbe441d5381e35d8e16df784690e65ef3 Mon Sep 17 00:00:00 2001 From: Ezequiel Garcia Date: Wed, 1 Jul 2020 15:16:07 +0200 Subject: [PATCH 0629/1476] hantro: Make sure we don't use post-processor on an encoder Commit 986eee3a5234 ("media: hantro: Prevent encoders from using post-processing") fixed hantro_needs_postproc condition, but missed one case. Encoders don't have any post-processor hardware block, so also can't be disabled. Fix it. Fixes: 986eee3a5234 ("media: hantro: Prevent encoders from using post-processing") Signed-off-by: Ezequiel Garcia Reviewed-by: Philipp Zabel Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/hantro/hantro_drv.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/drivers/staging/media/hantro/hantro_drv.c b/drivers/staging/media/hantro/hantro_drv.c index 34367b169011d..d32b6b1ab70bb 100644 --- a/drivers/staging/media/hantro/hantro_drv.c +++ b/drivers/staging/media/hantro/hantro_drv.c @@ -122,10 +122,12 @@ void hantro_start_prepare_run(struct hantro_ctx *ctx) v4l2_ctrl_request_setup(src_buf->vb2_buf.req_obj.req, &ctx->ctrl_handler); - if (hantro_needs_postproc(ctx, ctx->vpu_dst_fmt)) - hantro_postproc_enable(ctx); - else - hantro_postproc_disable(ctx); + if (!ctx->is_encoder) { + if (hantro_needs_postproc(ctx, ctx->vpu_dst_fmt)) + hantro_postproc_enable(ctx); + else + hantro_postproc_disable(ctx); + } } void hantro_end_prepare_run(struct hantro_ctx *ctx) -- GitLab From 9363aa33f6a9acfd16f98c749f17f6c65d184670 Mon Sep 17 00:00:00 2001 From: Ezequiel Garcia Date: Thu, 9 Jul 2020 18:36:34 +0200 Subject: [PATCH 0630/1476] rkvdec: h264: Refuse to decode unsupported bitstream The hardware only supports 4:2:2, 4:2:0 or 4:0:0 (monochrome), 8-bit or 10-bit depth content. Verify that the SPS refers to a supported bitstream, and refuse unsupported bitstreams by failing at TRY_EXT_CTRLS time. The driver is currently broken on 10-bit and 4:2:2 so disallow those as well. Signed-off-by: Ezequiel Garcia Reviewed-by: Jonas Karlman Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/rkvdec/rkvdec.c | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/drivers/staging/media/rkvdec/rkvdec.c b/drivers/staging/media/rkvdec/rkvdec.c index fd68671f02861..c8151328fb704 100644 --- a/drivers/staging/media/rkvdec/rkvdec.c +++ b/drivers/staging/media/rkvdec/rkvdec.c @@ -27,6 +27,32 @@ #include "rkvdec.h" #include "rkvdec-regs.h" +static int rkvdec_try_ctrl(struct v4l2_ctrl *ctrl) +{ + if (ctrl->id == V4L2_CID_MPEG_VIDEO_H264_SPS) { + const struct v4l2_ctrl_h264_sps *sps = ctrl->p_new.p_h264_sps; + /* + * TODO: The hardware supports 10-bit and 4:2:2 profiles, + * but it's currently broken in the driver. + * Reject them for now, until it's fixed. + */ + if (sps->chroma_format_idc > 1) + /* Only 4:0:0 and 4:2:0 are supported */ + return -EINVAL; + if (sps->bit_depth_luma_minus8 != sps->bit_depth_chroma_minus8) + /* Luma and chroma bit depth mismatch */ + return -EINVAL; + if (sps->bit_depth_luma_minus8 != 0) + /* Only 8-bit is supported */ + return -EINVAL; + } + return 0; +} + +static const struct v4l2_ctrl_ops rkvdec_ctrl_ops = { + .try_ctrl = rkvdec_try_ctrl, +}; + static const struct rkvdec_ctrl_desc rkvdec_h264_ctrl_descs[] = { { .per_request = true, @@ -42,6 +68,7 @@ static const struct rkvdec_ctrl_desc rkvdec_h264_ctrl_descs[] = { .per_request = true, .mandatory = true, .cfg.id = V4L2_CID_MPEG_VIDEO_H264_SPS, + .cfg.ops = &rkvdec_ctrl_ops, }, { .per_request = true, -- GitLab From d70cca7323442026e20c474314518c446cb4766f Mon Sep 17 00:00:00 2001 From: Ezequiel Garcia Date: Thu, 9 Jul 2020 18:36:35 +0200 Subject: [PATCH 0631/1476] hantro: h264: Refuse to decode unsupported bitstream The hardware only supports 4:2:0 or 4:0:0 (monochrome), 8-bit depth content. Verify that the SPS refers to a supported bitstream, and refuse unsupported bitstreams by failing at TRY_EXT_CTRLS time. Given the JPEG compression level control is the only one that needs setting, a specific ops is provided. Signed-off-by: Ezequiel Garcia Reviewed-by: Philipp Zabel Reviewed-by: Jonas Karlman Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/hantro/hantro_drv.c | 29 ++++++++++++++++++++--- 1 file changed, 26 insertions(+), 3 deletions(-) diff --git a/drivers/staging/media/hantro/hantro_drv.c b/drivers/staging/media/hantro/hantro_drv.c index d32b6b1ab70bb..34797507f2142 100644 --- a/drivers/staging/media/hantro/hantro_drv.c +++ b/drivers/staging/media/hantro/hantro_drv.c @@ -229,7 +229,25 @@ queue_init(void *priv, struct vb2_queue *src_vq, struct vb2_queue *dst_vq) return vb2_queue_init(dst_vq); } -static int hantro_s_ctrl(struct v4l2_ctrl *ctrl) +static int hantro_try_ctrl(struct v4l2_ctrl *ctrl) +{ + if (ctrl->id == V4L2_CID_MPEG_VIDEO_H264_SPS) { + const struct v4l2_ctrl_h264_sps *sps = ctrl->p_new.p_h264_sps; + + if (sps->chroma_format_idc > 1) + /* Only 4:0:0 and 4:2:0 are supported */ + return -EINVAL; + if (sps->bit_depth_luma_minus8 != sps->bit_depth_chroma_minus8) + /* Luma and chroma bit depth mismatch */ + return -EINVAL; + if (sps->bit_depth_luma_minus8 != 0) + /* Only 8-bit is supported */ + return -EINVAL; + } + return 0; +} + +static int hantro_jpeg_s_ctrl(struct v4l2_ctrl *ctrl) { struct hantro_ctx *ctx; @@ -250,7 +268,11 @@ static int hantro_s_ctrl(struct v4l2_ctrl *ctrl) } static const struct v4l2_ctrl_ops hantro_ctrl_ops = { - .s_ctrl = hantro_s_ctrl, + .try_ctrl = hantro_try_ctrl, +}; + +static const struct v4l2_ctrl_ops hantro_jpeg_ctrl_ops = { + .s_ctrl = hantro_jpeg_s_ctrl, }; static const struct hantro_ctrl controls[] = { @@ -262,7 +284,7 @@ static const struct hantro_ctrl controls[] = { .max = 100, .step = 1, .def = 50, - .ops = &hantro_ctrl_ops, + .ops = &hantro_jpeg_ctrl_ops, }, }, { .codec = HANTRO_MPEG2_DECODER, @@ -293,6 +315,7 @@ static const struct hantro_ctrl controls[] = { .codec = HANTRO_H264_DECODER, .cfg = { .id = V4L2_CID_MPEG_VIDEO_H264_SPS, + .ops = &hantro_ctrl_ops, }, }, { .codec = HANTRO_H264_DECODER, -- GitLab From 9c487b0b0ea7ff22127fe99a7f67657d8730ff94 Mon Sep 17 00:00:00 2001 From: Evgeny Novikov Date: Fri, 10 Jul 2020 11:02:23 +0200 Subject: [PATCH 0632/1476] media: vpss: clean up resources in init If platform_driver_register() fails within vpss_init() resources are not cleaned up. The patch fixes this issue by introducing the corresponding error handling. Found by Linux Driver Verification project (linuxtesting.org). Signed-off-by: Evgeny Novikov Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/davinci/vpss.c | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/drivers/media/platform/davinci/vpss.c b/drivers/media/platform/davinci/vpss.c index d38d2bbb6f0f8..7000f0bf0b353 100644 --- a/drivers/media/platform/davinci/vpss.c +++ b/drivers/media/platform/davinci/vpss.c @@ -505,19 +505,31 @@ static void vpss_exit(void) static int __init vpss_init(void) { + int ret; + if (!request_mem_region(VPSS_CLK_CTRL, 4, "vpss_clock_control")) return -EBUSY; oper_cfg.vpss_regs_base2 = ioremap(VPSS_CLK_CTRL, 4); if (unlikely(!oper_cfg.vpss_regs_base2)) { - release_mem_region(VPSS_CLK_CTRL, 4); - return -ENOMEM; + ret = -ENOMEM; + goto err_ioremap; } writel(VPSS_CLK_CTRL_VENCCLKEN | - VPSS_CLK_CTRL_DACCLKEN, oper_cfg.vpss_regs_base2); + VPSS_CLK_CTRL_DACCLKEN, oper_cfg.vpss_regs_base2); + + ret = platform_driver_register(&vpss_driver); + if (ret) + goto err_pd_register; + + return 0; - return platform_driver_register(&vpss_driver); +err_pd_register: + iounmap(oper_cfg.vpss_regs_base2); +err_ioremap: + release_mem_region(VPSS_CLK_CTRL, 4); + return ret; } subsys_initcall(vpss_init); module_exit(vpss_exit); -- GitLab From 9dc75e79c0dec0eb9363793efbd5f16d134169dc Mon Sep 17 00:00:00 2001 From: Kaaira Gupta Date: Sun, 5 Jul 2020 15:53:04 +0200 Subject: [PATCH 0633/1476] media: tpg: change char argument to const char Change the argument of type char * to const char * for function tpg_gen_text(). This function should take in a const char * as opposed to char * as it does not make changes to the text. This issue was found while passing the order of colors of tpg generated test image (which is a const char *) to this function. Signed-off-by: Kaaira Gupta Reviewed-by: Helen Koike Reviewed-by: Kieran Bingham Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/common/v4l2-tpg/v4l2-tpg-core.c | 10 +++++----- include/media/tpg/v4l2-tpg.h | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/media/common/v4l2-tpg/v4l2-tpg-core.c b/drivers/media/common/v4l2-tpg/v4l2-tpg-core.c index 50f1e0b28b250..dde22a4cbd6c3 100644 --- a/drivers/media/common/v4l2-tpg/v4l2-tpg-core.c +++ b/drivers/media/common/v4l2-tpg/v4l2-tpg-core.c @@ -1927,34 +1927,34 @@ typedef struct { u16 __; u8 _; } __packed x24; static noinline void tpg_print_str_2(const struct tpg_data *tpg, u8 *basep[TPG_MAX_PLANES][2], unsigned p, unsigned first, unsigned div, unsigned step, - int y, int x, char *text, unsigned len) + int y, int x, const char *text, unsigned len) { PRINTSTR(u8); } static noinline void tpg_print_str_4(const struct tpg_data *tpg, u8 *basep[TPG_MAX_PLANES][2], unsigned p, unsigned first, unsigned div, unsigned step, - int y, int x, char *text, unsigned len) + int y, int x, const char *text, unsigned len) { PRINTSTR(u16); } static noinline void tpg_print_str_6(const struct tpg_data *tpg, u8 *basep[TPG_MAX_PLANES][2], unsigned p, unsigned first, unsigned div, unsigned step, - int y, int x, char *text, unsigned len) + int y, int x, const char *text, unsigned len) { PRINTSTR(x24); } static noinline void tpg_print_str_8(const struct tpg_data *tpg, u8 *basep[TPG_MAX_PLANES][2], unsigned p, unsigned first, unsigned div, unsigned step, - int y, int x, char *text, unsigned len) + int y, int x, const char *text, unsigned len) { PRINTSTR(u32); } void tpg_gen_text(const struct tpg_data *tpg, u8 *basep[TPG_MAX_PLANES][2], - int y, int x, char *text) + int y, int x, const char *text) { unsigned step = V4L2_FIELD_HAS_T_OR_B(tpg->field) ? 2 : 1; unsigned div = step; diff --git a/include/media/tpg/v4l2-tpg.h b/include/media/tpg/v4l2-tpg.h index eb191e85d3634..9749ed4098565 100644 --- a/include/media/tpg/v4l2-tpg.h +++ b/include/media/tpg/v4l2-tpg.h @@ -241,7 +241,7 @@ void tpg_log_status(struct tpg_data *tpg); void tpg_set_font(const u8 *f); void tpg_gen_text(const struct tpg_data *tpg, - u8 *basep[TPG_MAX_PLANES][2], int y, int x, char *text); + u8 *basep[TPG_MAX_PLANES][2], int y, int x, const char *text); void tpg_calc_text_basep(struct tpg_data *tpg, u8 *basep[TPG_MAX_PLANES][2], unsigned p, u8 *vbuf); unsigned tpg_g_interleaved_plane(const struct tpg_data *tpg, unsigned buf_line); -- GitLab From 7a785081a16968a28adce223c572111d2d57a83e Mon Sep 17 00:00:00 2001 From: Kaaira Gupta Date: Sun, 5 Jul 2020 15:53:05 +0200 Subject: [PATCH 0634/1476] media: tpg: Add function to return colors' order of test image Currently there is no method to know the correct order of the colors for a test image generated by tpg. Write a function that returns a string of colors' order given a tpg. It returns a NULL pointer in case of test patterns which do not have a well defined colors' order. Hence add a NULL check for text in tpg_gen_text(). [hverkuil: white -> White (for consistency)] Signed-off-by: Kaaira Gupta Reviewed-by: Kieran Bingham Reviewed-by: Helen Koike Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/common/v4l2-tpg/v4l2-tpg-core.c | 30 +++++++++++++++++-- include/media/tpg/v4l2-tpg.h | 1 + 2 files changed, 29 insertions(+), 2 deletions(-) diff --git a/drivers/media/common/v4l2-tpg/v4l2-tpg-core.c b/drivers/media/common/v4l2-tpg/v4l2-tpg-core.c index dde22a4cbd6c3..630a75e0eeb16 100644 --- a/drivers/media/common/v4l2-tpg/v4l2-tpg-core.c +++ b/drivers/media/common/v4l2-tpg/v4l2-tpg-core.c @@ -1959,12 +1959,14 @@ void tpg_gen_text(const struct tpg_data *tpg, u8 *basep[TPG_MAX_PLANES][2], unsigned step = V4L2_FIELD_HAS_T_OR_B(tpg->field) ? 2 : 1; unsigned div = step; unsigned first = 0; - unsigned len = strlen(text); + unsigned len; unsigned p; - if (font8x16 == NULL || basep == NULL) + if (font8x16 == NULL || basep == NULL || text == NULL) return; + len = strlen(text); + /* Checks if it is possible to show string */ if (y + 16 >= tpg->compose.height || x + 8 >= tpg->compose.width) return; @@ -2006,6 +2008,30 @@ void tpg_gen_text(const struct tpg_data *tpg, u8 *basep[TPG_MAX_PLANES][2], } EXPORT_SYMBOL_GPL(tpg_gen_text); +const char *tpg_g_color_order(const struct tpg_data *tpg) +{ + switch (tpg->pattern) { + case TPG_PAT_75_COLORBAR: + case TPG_PAT_100_COLORBAR: + case TPG_PAT_CSC_COLORBAR: + case TPG_PAT_100_HCOLORBAR: + return "White, yellow, cyan, green, magenta, red, blue, black"; + case TPG_PAT_BLACK: + return "Black"; + case TPG_PAT_WHITE: + return "White"; + case TPG_PAT_RED: + return "Red"; + case TPG_PAT_GREEN: + return "Green"; + case TPG_PAT_BLUE: + return "Blue"; + default: + return NULL; + } +} +EXPORT_SYMBOL_GPL(tpg_g_color_order); + void tpg_update_mv_step(struct tpg_data *tpg) { int factor = tpg->mv_hor_mode > TPG_MOVE_NONE ? -1 : 1; diff --git a/include/media/tpg/v4l2-tpg.h b/include/media/tpg/v4l2-tpg.h index 9749ed4098565..0b0ddb87380ed 100644 --- a/include/media/tpg/v4l2-tpg.h +++ b/include/media/tpg/v4l2-tpg.h @@ -252,6 +252,7 @@ void tpg_fillbuffer(struct tpg_data *tpg, v4l2_std_id std, bool tpg_s_fourcc(struct tpg_data *tpg, u32 fourcc); void tpg_s_crop_compose(struct tpg_data *tpg, const struct v4l2_rect *crop, const struct v4l2_rect *compose); +const char *tpg_g_color_order(const struct tpg_data *tpg); static inline void tpg_s_pattern(struct tpg_data *tpg, enum tpg_pattern pattern) { -- GitLab From 5f3fb5c54d67670fa6743d2434a5bd43a97c01de Mon Sep 17 00:00:00 2001 From: Kaaira Gupta Date: Sun, 5 Jul 2020 15:53:06 +0200 Subject: [PATCH 0635/1476] media: vimc: Add a control to display info on test image Add a control in VIMC to display information such as the correct order of colors for a given test pattern, counter, brightness, hue, saturation, contrast, width and height at sensor over test image. Signed-off-by: Kaaira Gupta Acked-by: Helen Koike Reviewed-by: Kieran Bingham Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/test-drivers/vimc/Kconfig | 2 + drivers/media/test-drivers/vimc/vimc-common.h | 1 + drivers/media/test-drivers/vimc/vimc-core.c | 10 +++ drivers/media/test-drivers/vimc/vimc-sensor.c | 71 +++++++++++++++++++ 4 files changed, 84 insertions(+) diff --git a/drivers/media/test-drivers/vimc/Kconfig b/drivers/media/test-drivers/vimc/Kconfig index 4068a67585f93..da4b2ad6e40c9 100644 --- a/drivers/media/test-drivers/vimc/Kconfig +++ b/drivers/media/test-drivers/vimc/Kconfig @@ -2,6 +2,8 @@ config VIDEO_VIMC tristate "Virtual Media Controller Driver (VIMC)" depends on VIDEO_DEV && VIDEO_V4L2 + select FONT_SUPPORT + select FONT_8x16 select MEDIA_CONTROLLER select VIDEO_V4L2_SUBDEV_API select VIDEOBUF2_VMALLOC diff --git a/drivers/media/test-drivers/vimc/vimc-common.h b/drivers/media/test-drivers/vimc/vimc-common.h index ae163dec2459c..a289434e75ba0 100644 --- a/drivers/media/test-drivers/vimc/vimc-common.h +++ b/drivers/media/test-drivers/vimc/vimc-common.h @@ -20,6 +20,7 @@ #define VIMC_CID_VIMC_CLASS (0x00f00000 | 1) #define VIMC_CID_TEST_PATTERN (VIMC_CID_VIMC_BASE + 0) #define VIMC_CID_MEAN_WIN_SIZE (VIMC_CID_VIMC_BASE + 1) +#define VIMC_CID_OSD_TEXT_MODE (VIMC_CID_VIMC_BASE + 2) #define VIMC_FRAME_MAX_WIDTH 4096 #define VIMC_FRAME_MAX_HEIGHT 2160 diff --git a/drivers/media/test-drivers/vimc/vimc-core.c b/drivers/media/test-drivers/vimc/vimc-core.c index 11210aaa2551d..4b0ae6f51d765 100644 --- a/drivers/media/test-drivers/vimc/vimc-core.c +++ b/drivers/media/test-drivers/vimc/vimc-core.c @@ -5,10 +5,12 @@ * Copyright (C) 2015-2017 Helen Koike */ +#include #include #include #include #include +#include #include #include "vimc-common.h" @@ -263,11 +265,19 @@ err_v4l2_unregister: static int vimc_probe(struct platform_device *pdev) { + const struct font_desc *font = find_font("VGA8x16"); struct vimc_device *vimc; int ret; dev_dbg(&pdev->dev, "probe"); + if (!font) { + dev_err(&pdev->dev, "could not find font\n"); + return -ENODEV; + } + + tpg_set_font(font->data); + vimc = kzalloc(sizeof(*vimc), GFP_KERNEL); if (!vimc) return -ENOMEM; diff --git a/drivers/media/test-drivers/vimc/vimc-sensor.c b/drivers/media/test-drivers/vimc/vimc-sensor.c index a2f09ac9a3604..ba5db5a150b4b 100644 --- a/drivers/media/test-drivers/vimc/vimc-sensor.c +++ b/drivers/media/test-drivers/vimc/vimc-sensor.c @@ -14,11 +14,19 @@ #include "vimc-common.h" +enum vimc_sen_osd_mode { + VIMC_SEN_OSD_SHOW_ALL = 0, + VIMC_SEN_OSD_SHOW_COUNTERS = 1, + VIMC_SEN_OSD_SHOW_NONE = 2 +}; + struct vimc_sen_device { struct vimc_ent_device ved; struct v4l2_subdev sd; struct tpg_data tpg; u8 *frame; + enum vimc_sen_osd_mode osd_value; + u64 start_stream_ts; /* The active format */ struct v4l2_mbus_framefmt mbus_format; struct v4l2_ctrl_handler hdl; @@ -187,8 +195,49 @@ static void *vimc_sen_process_frame(struct vimc_ent_device *ved, { struct vimc_sen_device *vsen = container_of(ved, struct vimc_sen_device, ved); + const unsigned int line_height = 16; + u8 *basep[TPG_MAX_PLANES][2]; + unsigned int line = 1; + char str[100]; tpg_fill_plane_buffer(&vsen->tpg, 0, 0, vsen->frame); + tpg_calc_text_basep(&vsen->tpg, basep, 0, vsen->frame); + switch (vsen->osd_value) { + case VIMC_SEN_OSD_SHOW_ALL: { + const char *order = tpg_g_color_order(&vsen->tpg); + + tpg_gen_text(&vsen->tpg, basep, line++ * line_height, + 16, order); + snprintf(str, sizeof(str), + "brightness %3d, contrast %3d, saturation %3d, hue %d ", + vsen->tpg.brightness, + vsen->tpg.contrast, + vsen->tpg.saturation, + vsen->tpg.hue); + tpg_gen_text(&vsen->tpg, basep, line++ * line_height, 16, str); + snprintf(str, sizeof(str), "sensor size: %dx%d", + vsen->mbus_format.width, + vsen->mbus_format.height); + tpg_gen_text(&vsen->tpg, basep, line++ * line_height, 16, str); + fallthrough; + } + case VIMC_SEN_OSD_SHOW_COUNTERS: { + unsigned int ms; + + ms = div_u64(ktime_get_ns() - vsen->start_stream_ts, 1000000); + snprintf(str, sizeof(str), "%02d:%02d:%02d:%03d", + (ms / (60 * 60 * 1000)) % 24, + (ms / (60 * 1000)) % 60, + (ms / 1000) % 60, + ms % 1000); + tpg_gen_text(&vsen->tpg, basep, line++ * line_height, 16, str); + break; + } + case VIMC_SEN_OSD_SHOW_NONE: + default: + break; + } + return vsen->frame; } @@ -201,6 +250,8 @@ static int vimc_sen_s_stream(struct v4l2_subdev *sd, int enable) const struct vimc_pix_map *vpix; unsigned int frame_size; + vsen->start_stream_ts = ktime_get_ns(); + /* Calculate the frame size */ vpix = vimc_pix_map_by_code(vsen->mbus_format.code); frame_size = vsen->mbus_format.width * vpix->bpp * @@ -269,6 +320,9 @@ static int vimc_sen_s_ctrl(struct v4l2_ctrl *ctrl) case V4L2_CID_SATURATION: tpg_s_saturation(&vsen->tpg, ctrl->val); break; + case VIMC_CID_OSD_TEXT_MODE: + vsen->osd_value = ctrl->val; + break; default: return -EINVAL; } @@ -307,6 +361,22 @@ static const struct v4l2_ctrl_config vimc_sen_ctrl_test_pattern = { .qmenu = tpg_pattern_strings, }; +static const char * const vimc_ctrl_osd_mode_strings[] = { + "All", + "Counters Only", + "None", + NULL, +}; + +static const struct v4l2_ctrl_config vimc_sen_ctrl_osd_mode = { + .ops = &vimc_sen_ctrl_ops, + .id = VIMC_CID_OSD_TEXT_MODE, + .name = "Show Information", + .type = V4L2_CTRL_TYPE_MENU, + .max = ARRAY_SIZE(vimc_ctrl_osd_mode_strings) - 2, + .qmenu = vimc_ctrl_osd_mode_strings, +}; + static struct vimc_ent_device *vimc_sen_add(struct vimc_device *vimc, const char *vcfg_name) { @@ -323,6 +393,7 @@ static struct vimc_ent_device *vimc_sen_add(struct vimc_device *vimc, v4l2_ctrl_new_custom(&vsen->hdl, &vimc_sen_ctrl_class, NULL); v4l2_ctrl_new_custom(&vsen->hdl, &vimc_sen_ctrl_test_pattern, NULL); + v4l2_ctrl_new_custom(&vsen->hdl, &vimc_sen_ctrl_osd_mode, NULL); v4l2_ctrl_new_std(&vsen->hdl, &vimc_sen_ctrl_ops, V4L2_CID_VFLIP, 0, 1, 1, 0); v4l2_ctrl_new_std(&vsen->hdl, &vimc_sen_ctrl_ops, -- GitLab From f0e226885c3c18b2e7e27b8043c5a393405b037e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20Ha=C5=82asa?= Date: Thu, 14 May 2020 12:00:47 +0200 Subject: [PATCH 0636/1476] staging/media: Remove unneeded geometry restrictions from i.MX CSI driver I don't know what minimal image dimensions are, but 32x32 appears to be ok according to the docs. This is needed for small sensors like 80x80 thermal imagers. Signed-off-by: Krzysztof Halasa Reviewed-by: Philipp Zabel Tested-by: Philipp Zabel Acked-by: Steve Longerbeam Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/imx/imx-ic-prp.c | 4 ++-- drivers/staging/media/imx/imx-ic-prpencvf.c | 4 ++-- drivers/staging/media/imx/imx-media-csi.c | 4 ++-- drivers/staging/media/imx/imx-media-vdic.c | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/drivers/staging/media/imx/imx-ic-prp.c b/drivers/staging/media/imx/imx-ic-prp.c index 7658b83466a78..5b09e11b1a0ed 100644 --- a/drivers/staging/media/imx/imx-ic-prp.c +++ b/drivers/staging/media/imx/imx-ic-prp.c @@ -26,8 +26,8 @@ /* * Min/Max supported width and heights. */ -#define MIN_W 176 -#define MIN_H 144 +#define MIN_W 32 +#define MIN_H 32 #define MAX_W 4096 #define MAX_H 4096 #define W_ALIGN 4 /* multiple of 16 pixels */ diff --git a/drivers/staging/media/imx/imx-ic-prpencvf.c b/drivers/staging/media/imx/imx-ic-prpencvf.c index b1f84e0ac4860..74f5de466d5db 100644 --- a/drivers/staging/media/imx/imx-ic-prpencvf.c +++ b/drivers/staging/media/imx/imx-ic-prpencvf.c @@ -35,8 +35,8 @@ * has not requested a planar format, we should allow 8 pixel * alignment at the source pad. */ -#define MIN_W_SINK 176 -#define MIN_H_SINK 144 +#define MIN_W_SINK 32 +#define MIN_H_SINK 32 #define MAX_W_SINK 4096 #define MAX_H_SINK 4096 #define W_ALIGN_SINK 3 /* multiple of 8 pixels */ diff --git a/drivers/staging/media/imx/imx-media-csi.c b/drivers/staging/media/imx/imx-media-csi.c index d7e5b9ed27b83..d92fd804488e9 100644 --- a/drivers/staging/media/imx/imx-media-csi.c +++ b/drivers/staging/media/imx/imx-media-csi.c @@ -33,8 +33,8 @@ * has not requested planar formats, we should allow 8 pixel * alignment. */ -#define MIN_W 176 -#define MIN_H 144 +#define MIN_W 32 +#define MIN_H 32 #define MAX_W 4096 #define MAX_H 4096 #define W_ALIGN 1 /* multiple of 2 pixels */ diff --git a/drivers/staging/media/imx/imx-media-vdic.c b/drivers/staging/media/imx/imx-media-vdic.c index 303b5407fb643..879329f81f79d 100644 --- a/drivers/staging/media/imx/imx-media-vdic.c +++ b/drivers/staging/media/imx/imx-media-vdic.c @@ -49,8 +49,8 @@ struct vdic_pipeline_ops { /* * Min/Max supported width and heights. */ -#define MIN_W 176 -#define MIN_H 144 +#define MIN_W 32 +#define MIN_H 32 #define MAX_W_VDIC 968 #define MAX_H_VDIC 2048 #define W_ALIGN 4 /* multiple of 16 pixels */ -- GitLab From 216964fa6a63c095417cc4001adc240c0e1f1bfb Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Wed, 15 Jul 2020 00:05:49 +0200 Subject: [PATCH 0637/1476] media: media-device.h: drop duplicated word in comment Delete the doubled word "the" in a comment. Signed-off-by: Randy Dunlap Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- include/media/media-device.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/media/media-device.h b/include/media/media-device.h index fa08954307205..1345e6da688a9 100644 --- a/include/media/media-device.h +++ b/include/media/media-device.h @@ -128,7 +128,7 @@ struct media_device_ops { * * Use-case: find tuner entity connected to the decoder * entity and check if it is available, and activate the - * the link between them from @enable_source and deactivate + * link between them from @enable_source and deactivate * from @disable_source. * * .. note:: -- GitLab From f040e0fd29e4a09855c8cff8c6d22b4745a19796 Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Wed, 15 Jul 2020 00:05:50 +0200 Subject: [PATCH 0638/1476] media: media-devnode.h: drop duplicated word in comment Delete the doubled word "the" in a comment. Signed-off-by: Randy Dunlap Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- include/media/media-devnode.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/media/media-devnode.h b/include/media/media-devnode.h index 6393842c6b218..d27c1c646c280 100644 --- a/include/media/media-devnode.h +++ b/include/media/media-devnode.h @@ -39,7 +39,7 @@ struct media_device; * @poll: pointer to the function that implements poll() syscall * @ioctl: pointer to the function that implements ioctl() syscall * @compat_ioctl: pointer to the function that will handle 32 bits userspace - * calls to the the ioctl() syscall on a Kernel compiled with 64 bits. + * calls to the ioctl() syscall on a Kernel compiled with 64 bits. * @open: pointer to the function that implements open() syscall * @release: pointer to the function that will release the resources allocated * by the @open function. -- GitLab From ffb189716081ff7dafb0978d31a290adff966d81 Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Wed, 15 Jul 2020 00:05:51 +0200 Subject: [PATCH 0639/1476] media: dvbdev.h: fix duplicated word in comment Change the doubled word "the" in a comment to "to the". Signed-off-by: Randy Dunlap Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- include/media/dvbdev.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/media/dvbdev.h b/include/media/dvbdev.h index 551325858de3d..1f7b008387f88 100644 --- a/include/media/dvbdev.h +++ b/include/media/dvbdev.h @@ -385,7 +385,7 @@ struct i2c_client; * with dvb_module_probe() should use dvb_module_release() to unbind. * * Return: - * On success, return an &struct i2c_client, pointing the the bound + * On success, return an &struct i2c_client, pointing to the bound * I2C device. %NULL otherwise. * * .. note:: -- GitLab From 91bbbf24c4d1d2b9365c077730bbbaf2ef3f9056 Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Wed, 15 Jul 2020 00:05:52 +0200 Subject: [PATCH 0640/1476] media: media-entity.h: drop duplicated word in comment Delete the doubled word "flag" in a comment. Signed-off-by: Randy Dunlap Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- include/media/media-entity.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/media/media-entity.h b/include/media/media-entity.h index cde80ad029b7b..cbdfcb79d0d06 100644 --- a/include/media/media-entity.h +++ b/include/media/media-entity.h @@ -803,7 +803,7 @@ int __media_entity_setup_link(struct media_link *link, u32 flags); * @flags: the requested new link flags * * The only configurable property is the %MEDIA_LNK_FL_ENABLED link flag - * flag to enable/disable a link. Links marked with the + * to enable/disable a link. Links marked with the * %MEDIA_LNK_FL_IMMUTABLE link flag can not be enabled or disabled. * * When a link is enabled or disabled, the media framework calls the -- GitLab From f2f475e4c5bb5343e35c0973096a391a09677d1a Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Wed, 15 Jul 2020 00:05:53 +0200 Subject: [PATCH 0641/1476] media: v4l2-subdev.h: drop duplicated word in comment Delete the doubled words "the" in comments. Signed-off-by: Randy Dunlap Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- include/media/v4l2-subdev.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/include/media/v4l2-subdev.h b/include/media/v4l2-subdev.h index 2607ea85096ae..1b83a6c46c1ad 100644 --- a/include/media/v4l2-subdev.h +++ b/include/media/v4l2-subdev.h @@ -566,9 +566,9 @@ struct v4l2_subdev_ir_parameters { * * @rx_read: Reads received codes or pulse width data. * The semantics are similar to a non-blocking read() call. - * @rx_g_parameters: Get the current operating parameters and state of the + * @rx_g_parameters: Get the current operating parameters and state of * the IR receiver. - * @rx_s_parameters: Set the current operating parameters and state of the + * @rx_s_parameters: Set the current operating parameters and state of * the IR receiver. It is recommended to call * [rt]x_g_parameters first to fill out the current state, and only change * the fields that need to be changed. Upon return, the actual device @@ -582,9 +582,9 @@ struct v4l2_subdev_ir_parameters { * * @tx_write: Writes codes or pulse width data for transmission. * The semantics are similar to a non-blocking write() call. - * @tx_g_parameters: Get the current operating parameters and state of the + * @tx_g_parameters: Get the current operating parameters and state of * the IR transmitter. - * @tx_s_parameters: Set the current operating parameters and state of the + * @tx_s_parameters: Set the current operating parameters and state of * the IR transmitter. It is recommended to call * [rt]x_g_parameters first to fill out the current state, and only change * the fields that need to be changed. Upon return, the actual device -- GitLab From 94dc765602618fdab8bc4eaa96e3053929d8ff8f Mon Sep 17 00:00:00 2001 From: Michael Tretter Date: Mon, 13 Jul 2020 16:42:18 +0200 Subject: [PATCH 0642/1476] media: allegro: rework mbox handling Add a send/notify abstraction for the mailbox and separate the message handling in the driver from the code to read and write message to the mailbox. This untangles how mails are written into the MCU's SRAM and signaled to the MCU from the protocol between the driver and the firmware. Signed-off-by: Michael Tretter Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- .../staging/media/allegro-dvt/allegro-core.c | 145 +++++++++++------- 1 file changed, 87 insertions(+), 58 deletions(-) diff --git a/drivers/staging/media/allegro-dvt/allegro-core.c b/drivers/staging/media/allegro-dvt/allegro-core.c index 3ed66aae741d5..11333f900302e 100644 --- a/drivers/staging/media/allegro-dvt/allegro-core.c +++ b/drivers/staging/media/allegro-dvt/allegro-core.c @@ -105,9 +105,11 @@ struct allegro_buffer { struct list_head head; }; +struct allegro_dev; struct allegro_channel; struct allegro_mbox { + struct allegro_dev *dev; unsigned int head; unsigned int tail; unsigned int data; @@ -134,8 +136,8 @@ struct allegro_dev { struct completion init_complete; /* The mailbox interface */ - struct allegro_mbox mbox_command; - struct allegro_mbox mbox_status; + struct allegro_mbox *mbox_command; + struct allegro_mbox *mbox_status; /* * The downstream driver limits the users to 64 users, thus I can use @@ -583,12 +585,20 @@ static void allegro_free_buffer(struct allegro_dev *dev, * Mailbox interface to send messages to the MCU. */ -static int allegro_mbox_init(struct allegro_dev *dev, - struct allegro_mbox *mbox, - unsigned int base, size_t size) +static void allegro_mcu_interrupt(struct allegro_dev *dev); +static void allegro_handle_message(struct allegro_dev *dev, + union mcu_msg_response *msg); + +static struct allegro_mbox *allegro_mbox_init(struct allegro_dev *dev, + unsigned int base, size_t size) { + struct allegro_mbox *mbox; + + mbox = devm_kmalloc(&dev->plat_dev->dev, sizeof(*mbox), GFP_KERNEL); if (!mbox) - return -EINVAL; + return ERR_PTR(-ENOMEM); + + mbox->dev = dev; mbox->head = base; mbox->tail = base + 0x4; @@ -599,7 +609,7 @@ static int allegro_mbox_init(struct allegro_dev *dev, regmap_write(dev->sram, mbox->head, 0); regmap_write(dev->sram, mbox->tail, 0); - return 0; + return mbox; } static int allegro_mbox_write(struct allegro_dev *dev, @@ -713,9 +723,50 @@ static ssize_t allegro_mbox_read(struct allegro_dev *dev, return size; } -static void allegro_mcu_interrupt(struct allegro_dev *dev) +/** + * allegro_mbox_send() - Send a message via the mailbox + * @mbox: the mailbox which is used to send the message + * @msg: the message to send + */ +static int allegro_mbox_send(struct allegro_mbox *mbox, void *msg) { - regmap_write(dev->regmap, AL5_MCU_INTERRUPT, BIT(0)); + struct allegro_dev *dev = mbox->dev; + struct mcu_msg_header *header = msg; + ssize_t size = sizeof(*header) + header->length; + int err; + + err = allegro_mbox_write(dev, mbox, msg, size); + if (err) + goto out; + + allegro_mcu_interrupt(dev); + +out: + return err; +} + +/** + * allegro_mbox_notify() - Notify the mailbox about a new message + * @mbox: The allegro_mbox to notify + */ +static void allegro_mbox_notify(struct allegro_mbox *mbox) +{ + struct allegro_dev *dev = mbox->dev; + union mcu_msg_response *msg; + ssize_t size; + + msg = kmalloc(sizeof(*msg), GFP_KERNEL); + if (!msg) + return; + + size = allegro_mbox_read(dev, mbox, msg, sizeof(*msg)); + if (size < 0) + goto out; + + allegro_handle_message(dev, msg); + +out: + kfree(msg); } static void allegro_mcu_send_init(struct allegro_dev *dev, @@ -736,8 +787,7 @@ static void allegro_mcu_send_init(struct allegro_dev *dev, msg.l2_cache[1] = -1; msg.l2_cache[2] = -1; - allegro_mbox_write(dev, &dev->mbox_command, &msg, sizeof(msg)); - allegro_mcu_interrupt(dev); + allegro_mbox_send(dev->mbox_command, &msg); } static u32 v4l2_pixelformat_to_mcu_format(u32 pixelformat) @@ -946,8 +996,7 @@ static int allegro_mcu_send_create_channel(struct allegro_dev *dev, fill_create_channel_param(channel, &msg.param); - allegro_mbox_write(dev, &dev->mbox_command, &msg, sizeof(msg)); - allegro_mcu_interrupt(dev); + allegro_mbox_send(dev->mbox_command, &msg); return 0; } @@ -964,8 +1013,7 @@ static int allegro_mcu_send_destroy_channel(struct allegro_dev *dev, msg.channel_id = channel->mcu_channel_id; - allegro_mbox_write(dev, &dev->mbox_command, &msg, sizeof(msg)); - allegro_mcu_interrupt(dev); + allegro_mbox_send(dev->mbox_command, &msg); return 0; } @@ -991,8 +1039,7 @@ static int allegro_mcu_send_put_stream_buffer(struct allegro_dev *dev, /* copied to mcu_msg_encode_frame_response */ msg.stream_id = stream_id; - allegro_mbox_write(dev, &dev->mbox_command, &msg, sizeof(msg)); - allegro_mcu_interrupt(dev); + allegro_mbox_send(dev->mbox_command, &msg); return 0; } @@ -1021,8 +1068,7 @@ static int allegro_mcu_send_encode_frame(struct allegro_dev *dev, msg.ep2 = 0x0; msg.ep2_v = to_mcu_addr(dev, msg.ep2); - allegro_mbox_write(dev, &dev->mbox_command, &msg, sizeof(msg)); - allegro_mcu_interrupt(dev); + allegro_mbox_send(dev->mbox_command, &msg); return 0; } @@ -1084,12 +1130,8 @@ static int allegro_mcu_push_buffer_internal(struct allegro_channel *channel, buffer++; } - err = allegro_mbox_write(dev, &dev->mbox_command, msg, size); - if (err) - goto out; - allegro_mcu_interrupt(dev); + err = allegro_mbox_send(dev->mbox_command, msg); -out: kfree(msg); return err; } @@ -1681,51 +1723,28 @@ allegro_handle_encode_frame(struct allegro_dev *dev, return 0; } -static int allegro_receive_message(struct allegro_dev *dev) +static void allegro_handle_message(struct allegro_dev *dev, + union mcu_msg_response *msg) { - union mcu_msg_response *msg; - ssize_t size; - int err = 0; - - msg = kmalloc(sizeof(*msg), GFP_KERNEL); - if (!msg) - return -ENOMEM; - - size = allegro_mbox_read(dev, &dev->mbox_status, msg, sizeof(*msg)); - if (size < sizeof(msg->header)) { - v4l2_err(&dev->v4l2_dev, - "invalid mbox message (%zd): must be at least %zu\n", - size, sizeof(msg->header)); - err = -EINVAL; - goto out; - } - switch (msg->header.type) { case MCU_MSG_TYPE_INIT: - err = allegro_handle_init(dev, &msg->init); + allegro_handle_init(dev, &msg->init); break; case MCU_MSG_TYPE_CREATE_CHANNEL: - err = allegro_handle_create_channel(dev, &msg->create_channel); + allegro_handle_create_channel(dev, &msg->create_channel); break; case MCU_MSG_TYPE_DESTROY_CHANNEL: - err = allegro_handle_destroy_channel(dev, - &msg->destroy_channel); + allegro_handle_destroy_channel(dev, &msg->destroy_channel); break; case MCU_MSG_TYPE_ENCODE_FRAME: - err = allegro_handle_encode_frame(dev, &msg->encode_frame); + allegro_handle_encode_frame(dev, &msg->encode_frame); break; default: v4l2_warn(&dev->v4l2_dev, "%s: unknown message %s\n", __func__, msg_type_name(msg->header.type)); - err = -EINVAL; break; } - -out: - kfree(msg); - - return err; } static irqreturn_t allegro_hardirq(int irq, void *data) @@ -1746,7 +1765,7 @@ static irqreturn_t allegro_irq_thread(int irq, void *data) { struct allegro_dev *dev = data; - allegro_receive_message(dev); + allegro_mbox_notify(dev->mbox_status); return IRQ_HANDLED; } @@ -1895,6 +1914,11 @@ static int allegro_mcu_reset(struct allegro_dev *dev) return allegro_mcu_wait_for_sleep(dev); } +static void allegro_mcu_interrupt(struct allegro_dev *dev) +{ + regmap_write(dev->regmap, AL5_MCU_INTERRUPT, BIT(0)); +} + static void allegro_destroy_channel(struct allegro_channel *channel) { struct allegro_dev *dev = channel->dev; @@ -2887,10 +2911,15 @@ static int allegro_mcu_hw_init(struct allegro_dev *dev, { int err; - allegro_mbox_init(dev, &dev->mbox_command, - info->mailbox_cmd, info->mailbox_size); - allegro_mbox_init(dev, &dev->mbox_status, - info->mailbox_status, info->mailbox_size); + dev->mbox_command = allegro_mbox_init(dev, info->mailbox_cmd, + info->mailbox_size); + dev->mbox_status = allegro_mbox_init(dev, info->mailbox_status, + info->mailbox_size); + if (!dev->mbox_command || !dev->mbox_status) { + v4l2_err(&dev->v4l2_dev, + "failed to initialize mailboxes\n"); + return -EIO; + } allegro_mcu_enable_interrupts(dev); -- GitLab From e561f8e24c5ade18f97970c3034ac0f965370c3f Mon Sep 17 00:00:00 2001 From: Michael Tretter Date: Mon, 13 Jul 2020 16:42:19 +0200 Subject: [PATCH 0643/1476] media: allegro: rework read/write to mailbox Rework the functions that read and write the SRAM that is used to communicate with the MCU. As the functions will not operate on structs but on prepared binary buffers, make the buffer stride more explicit. Also, avoid any uses of struct mcu_msg_header to analyze messages in memory, because the header will be made independent of the binary representation in the mailbox. Instead explicitly access the mail size field in the mailbox. As at it, further reduce the dependency between the mailboxes and struct allegro_dev. Signed-off-by: Michael Tretter Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- .../staging/media/allegro-dvt/allegro-core.c | 110 ++++++------------ 1 file changed, 36 insertions(+), 74 deletions(-) diff --git a/drivers/staging/media/allegro-dvt/allegro-core.c b/drivers/staging/media/allegro-dvt/allegro-core.c index 11333f900302e..dc2627df0eec2 100644 --- a/drivers/staging/media/allegro-dvt/allegro-core.c +++ b/drivers/staging/media/allegro-dvt/allegro-core.c @@ -612,49 +612,34 @@ static struct allegro_mbox *allegro_mbox_init(struct allegro_dev *dev, return mbox; } -static int allegro_mbox_write(struct allegro_dev *dev, - struct allegro_mbox *mbox, void *src, size_t size) +static int allegro_mbox_write(struct allegro_mbox *mbox, + const u32 *src, size_t size) { - struct mcu_msg_header *header = src; + struct regmap *sram = mbox->dev->sram; unsigned int tail; size_t size_no_wrap; int err = 0; + int stride = regmap_get_reg_stride(sram); if (!src) return -EINVAL; - if (size > mbox->size) { - v4l2_err(&dev->v4l2_dev, - "message (%zu bytes) too large for mailbox (%zu bytes)\n", - size, mbox->size); - return -EINVAL; - } - - if (header->length != size - sizeof(*header)) { - v4l2_err(&dev->v4l2_dev, - "invalid message length: %u bytes (expected %zu bytes)\n", - header->length, size - sizeof(*header)); + if (size > mbox->size) return -EINVAL; - } - - v4l2_dbg(2, debug, &dev->v4l2_dev, - "write command message: type %s, body length %d\n", - msg_type_name(header->type), header->length); mutex_lock(&mbox->lock); - regmap_read(dev->sram, mbox->tail, &tail); + regmap_read(sram, mbox->tail, &tail); if (tail > mbox->size) { - v4l2_err(&dev->v4l2_dev, - "invalid tail (0x%x): must be smaller than mailbox size (0x%zx)\n", - tail, mbox->size); err = -EIO; goto out; } size_no_wrap = min(size, mbox->size - (size_t)tail); - regmap_bulk_write(dev->sram, mbox->data + tail, src, size_no_wrap / 4); - regmap_bulk_write(dev->sram, mbox->data, - src + size_no_wrap, (size - size_no_wrap) / 4); - regmap_write(dev->sram, mbox->tail, (tail + size) % mbox->size); + regmap_bulk_write(sram, mbox->data + tail, + src, size_no_wrap / stride); + regmap_bulk_write(sram, mbox->data, + src + (size_no_wrap / sizeof(*src)), + (size - size_no_wrap) / stride); + regmap_write(sram, mbox->tail, (tail + size) % mbox->size); out: mutex_unlock(&mbox->lock); @@ -662,40 +647,32 @@ out: return err; } -static ssize_t allegro_mbox_read(struct allegro_dev *dev, - struct allegro_mbox *mbox, - void *dst, size_t nbyte) +static ssize_t allegro_mbox_read(struct allegro_mbox *mbox, + u32 *dst, size_t nbyte) { - struct mcu_msg_header *header; + struct { + u16 length; + u16 type; + } __attribute__ ((__packed__)) *header; + struct regmap *sram = mbox->dev->sram; unsigned int head; ssize_t size; size_t body_no_wrap; + int stride = regmap_get_reg_stride(sram); - regmap_read(dev->sram, mbox->head, &head); - if (head > mbox->size) { - v4l2_err(&dev->v4l2_dev, - "invalid head (0x%x): must be smaller than mailbox size (0x%zx)\n", - head, mbox->size); + regmap_read(sram, mbox->head, &head); + if (head > mbox->size) return -EIO; - } /* Assume that the header does not wrap. */ - regmap_bulk_read(dev->sram, mbox->data + head, - dst, sizeof(*header) / 4); - header = dst; + regmap_bulk_read(sram, mbox->data + head, + dst, sizeof(*header) / stride); + header = (void *)dst; size = header->length + sizeof(*header); - if (size > mbox->size || size & 0x3) { - v4l2_err(&dev->v4l2_dev, - "invalid message length: %zu bytes (maximum %zu bytes)\n", - header->length + sizeof(*header), mbox->size); + if (size > mbox->size || size & 0x3) return -EIO; - } - if (size > nbyte) { - v4l2_err(&dev->v4l2_dev, - "destination buffer too small: %zu bytes (need %zu bytes)\n", - nbyte, size); + if (size > nbyte) return -EINVAL; - } /* * The message might wrap within the mailbox. If the message does not @@ -708,17 +685,14 @@ static ssize_t allegro_mbox_read(struct allegro_dev *dev, */ body_no_wrap = min((size_t)header->length, (size_t)(mbox->size - (head + sizeof(*header)))); - regmap_bulk_read(dev->sram, mbox->data + head + sizeof(*header), - dst + sizeof(*header), body_no_wrap / 4); - regmap_bulk_read(dev->sram, mbox->data, - dst + sizeof(*header) + body_no_wrap, - (header->length - body_no_wrap) / 4); + regmap_bulk_read(sram, mbox->data + head + sizeof(*header), + dst + (sizeof(*header) / sizeof(*dst)), + body_no_wrap / stride); + regmap_bulk_read(sram, mbox->data, + dst + (sizeof(*header) + body_no_wrap) / sizeof(*dst), + (header->length - body_no_wrap) / stride); - regmap_write(dev->sram, mbox->head, (head + size) % mbox->size); - - v4l2_dbg(2, debug, &dev->v4l2_dev, - "read status message: type %s, body length %d\n", - msg_type_name(header->type), header->length); + regmap_write(sram, mbox->head, (head + size) % mbox->size); return size; } @@ -735,7 +709,7 @@ static int allegro_mbox_send(struct allegro_mbox *mbox, void *msg) ssize_t size = sizeof(*header) + header->length; int err; - err = allegro_mbox_write(dev, mbox, msg, size); + err = allegro_mbox_write(mbox, msg, size); if (err) goto out; @@ -759,7 +733,7 @@ static void allegro_mbox_notify(struct allegro_mbox *mbox) if (!msg) return; - size = allegro_mbox_read(dev, mbox, msg, sizeof(*msg)); + size = allegro_mbox_read(mbox, (u32 *)msg, sizeof(*msg)); if (size < 0) goto out; @@ -1604,12 +1578,6 @@ allegro_handle_create_channel(struct allegro_dev *dev, struct allegro_channel *channel; int err = 0; - if (msg->header.length != sizeof(*msg) - sizeof(msg->header)) - v4l2_warn(&dev->v4l2_dev, - "received message has %d bytes, but expected %zu\n", - msg->header.length, - sizeof(*msg) - sizeof(msg->header)); - channel = allegro_find_channel_by_user_id(dev, msg->user_id); if (IS_ERR(channel)) { v4l2_warn(&dev->v4l2_dev, @@ -1703,12 +1671,6 @@ allegro_handle_encode_frame(struct allegro_dev *dev, { struct allegro_channel *channel; - if (msg->header.length != sizeof(*msg) - sizeof(msg->header)) - v4l2_warn(&dev->v4l2_dev, - "received message has %d bytes, but expected %zu\n", - msg->header.length, - sizeof(*msg) - sizeof(msg->header)); - channel = allegro_find_channel_by_channel_id(dev, msg->channel_id); if (IS_ERR(channel)) { v4l2_err(&dev->v4l2_dev, -- GitLab From 3de16839669f60b6049734e3a9f553a2a958c4b5 Mon Sep 17 00:00:00 2001 From: Michael Tretter Date: Mon, 13 Jul 2020 16:42:20 +0200 Subject: [PATCH 0644/1476] media: allegro: add explicit mail encoding and decoding The message format in the mailboxes differ between firmware versions. Therefore, it is necessary to decouple the mailbox format of the driver from the message format of the firmware. This allows to keep a consistent message format in the driver while still supporting various firmware versions. Add an intermediate step to encode and decode message before writing the mails to the mailboxes. On the other hand, this allows to handle optional fields in the messages, which is required for advanced features of the encoder and was not possible until now. Signed-off-by: Michael Tretter Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- .../staging/media/allegro-dvt/allegro-core.c | 28 +- .../staging/media/allegro-dvt/allegro-mail.c | 364 ++++++++++++++++++ .../staging/media/allegro-dvt/allegro-mail.h | 3 + 3 files changed, 391 insertions(+), 4 deletions(-) diff --git a/drivers/staging/media/allegro-dvt/allegro-core.c b/drivers/staging/media/allegro-dvt/allegro-core.c index dc2627df0eec2..6334b351ee3b0 100644 --- a/drivers/staging/media/allegro-dvt/allegro-core.c +++ b/drivers/staging/media/allegro-dvt/allegro-core.c @@ -705,11 +705,20 @@ static ssize_t allegro_mbox_read(struct allegro_mbox *mbox, static int allegro_mbox_send(struct allegro_mbox *mbox, void *msg) { struct allegro_dev *dev = mbox->dev; - struct mcu_msg_header *header = msg; - ssize_t size = sizeof(*header) + header->length; + ssize_t size; int err; + u32 *tmp; + + tmp = kzalloc(mbox->size, GFP_KERNEL); + if (!tmp) { + err = -ENOMEM; + goto out; + } + + size = allegro_encode_mail(tmp, msg); - err = allegro_mbox_write(mbox, msg, size); + err = allegro_mbox_write(mbox, tmp, size); + kfree(tmp); if (err) goto out; @@ -728,18 +737,29 @@ static void allegro_mbox_notify(struct allegro_mbox *mbox) struct allegro_dev *dev = mbox->dev; union mcu_msg_response *msg; ssize_t size; + u32 *tmp; + int err; msg = kmalloc(sizeof(*msg), GFP_KERNEL); if (!msg) return; - size = allegro_mbox_read(mbox, (u32 *)msg, sizeof(*msg)); + tmp = kmalloc(mbox->size, GFP_KERNEL); + if (!tmp) + goto out; + + size = allegro_mbox_read(mbox, tmp, mbox->size); if (size < 0) goto out; + err = allegro_decode_mail(msg, tmp); + if (err) + goto out; + allegro_handle_message(dev, msg); out: + kfree(tmp); kfree(msg); } diff --git a/drivers/staging/media/allegro-dvt/allegro-mail.c b/drivers/staging/media/allegro-dvt/allegro-mail.c index df0d8d26a6fbe..c78367d36e2e5 100644 --- a/drivers/staging/media/allegro-dvt/allegro-mail.c +++ b/drivers/staging/media/allegro-dvt/allegro-mail.c @@ -6,7 +6,9 @@ * Allegro VCU firmware. */ +#include #include +#include #include "allegro-mail.h" @@ -35,3 +37,365 @@ const char *msg_type_name(enum mcu_msg_type type) } } EXPORT_SYMBOL(msg_type_name); + +static ssize_t +allegro_enc_init(u32 *dst, struct mcu_msg_init_request *msg) +{ + unsigned int i = 0; + + dst[i++] = msg->reserved0; + dst[i++] = msg->suballoc_dma; + dst[i++] = msg->suballoc_size; + dst[i++] = msg->l2_cache[0]; + dst[i++] = msg->l2_cache[1]; + dst[i++] = msg->l2_cache[2]; + + return i * sizeof(*dst); +} + +static ssize_t +allegro_encode_channel_config(u32 *dst, struct create_channel_param *param) +{ + unsigned int i = 0; + + dst[i++] = FIELD_PREP(GENMASK(31, 16), param->height) | + FIELD_PREP(GENMASK(15, 0), param->width); + dst[i++] = param->format; + dst[i++] = param->colorspace; + dst[i++] = param->src_mode; + dst[i++] = FIELD_PREP(GENMASK(31, 24), param->codec) | + FIELD_PREP(GENMASK(23, 8), param->constraint_set_flags) | + FIELD_PREP(GENMASK(7, 0), param->profile); + dst[i++] = FIELD_PREP(GENMASK(31, 16), param->tier) | + FIELD_PREP(GENMASK(15, 0), param->level); + dst[i++] = param->sps_param; + dst[i++] = param->pps_param; + dst[i++] = param->enc_option; + dst[i++] = FIELD_PREP(GENMASK(15, 8), param->beta_offset) | + FIELD_PREP(GENMASK(7, 0), param->tc_offset); + dst[i++] = param->unknown11; + dst[i++] = param->unknown12; + dst[i++] = FIELD_PREP(GENMASK(31, 16), param->prefetch_auto) | + FIELD_PREP(GENMASK(15, 0), param->num_slices); + dst[i++] = param->prefetch_mem_offset; + dst[i++] = param->prefetch_mem_size; + dst[i++] = FIELD_PREP(GENMASK(31, 16), param->clip_vrt_range) | + FIELD_PREP(GENMASK(15, 0), param->clip_hrz_range); + dst[i++] = FIELD_PREP(GENMASK(31, 16), param->me_range[1]) | + FIELD_PREP(GENMASK(15, 0), param->me_range[0]); + dst[i++] = FIELD_PREP(GENMASK(31, 16), param->me_range[3]) | + FIELD_PREP(GENMASK(15, 0), param->me_range[2]); + dst[i++] = FIELD_PREP(GENMASK(31, 24), param->min_tu_size) | + FIELD_PREP(GENMASK(23, 16), param->max_tu_size) | + FIELD_PREP(GENMASK(15, 8), param->min_cu_size) | + FIELD_PREP(GENMASK(8, 0), param->max_cu_size); + dst[i++] = FIELD_PREP(GENMASK(15, 8), param->max_transfo_depth_intra) | + FIELD_PREP(GENMASK(7, 0), param->max_transfo_depth_inter); + dst[i++] = param->entropy_mode; + dst[i++] = param->wp_mode; + + dst[i++] = param->rate_control_mode; + dst[i++] = param->initial_rem_delay; + dst[i++] = param->cpb_size; + dst[i++] = FIELD_PREP(GENMASK(31, 16), param->clk_ratio) | + FIELD_PREP(GENMASK(15, 0), param->framerate); + dst[i++] = param->target_bitrate; + dst[i++] = param->max_bitrate; + dst[i++] = FIELD_PREP(GENMASK(31, 16), param->min_qp) | + FIELD_PREP(GENMASK(15, 0), param->initial_qp); + dst[i++] = FIELD_PREP(GENMASK(31, 16), param->ip_delta) | + FIELD_PREP(GENMASK(15, 0), param->max_qp); + dst[i++] = FIELD_PREP(GENMASK(31, 16), param->golden_ref) | + FIELD_PREP(GENMASK(15, 0), param->pb_delta); + dst[i++] = FIELD_PREP(GENMASK(31, 16), param->golden_ref_frequency) | + FIELD_PREP(GENMASK(15, 0), param->golden_delta); + dst[i++] = param->rate_control_option; + + dst[i++] = param->gop_ctrl_mode; + dst[i++] = param->freq_idr; + dst[i++] = param->freq_lt; + dst[i++] = param->gdr_mode; + dst[i++] = FIELD_PREP(GENMASK(31, 24), param->freq_golden_ref) | + FIELD_PREP(GENMASK(23, 16), param->num_b) | + FIELD_PREP(GENMASK(15, 0), param->gop_length); + + dst[i++] = param->subframe_latency; + dst[i++] = param->lda_control_mode; + dst[i++] = param->unknown41; + + return i * sizeof(*dst); +} + +static ssize_t +allegro_enc_create_channel(u32 *dst, struct mcu_msg_create_channel *msg) +{ + struct create_channel_param *param = &msg->param; + ssize_t size = 0; + unsigned int i = 0; + + dst[i++] = msg->user_id; + + size = allegro_encode_channel_config(&dst[i], param); + i += size / sizeof(*dst); + + return i * sizeof(*dst); +} + +static ssize_t +allegro_enc_destroy_channel(u32 *dst, struct mcu_msg_destroy_channel *msg) +{ + unsigned int i = 0; + + dst[i++] = msg->channel_id; + + return i * sizeof(*dst); +} + +static ssize_t +allegro_enc_push_buffers(u32 *dst, struct mcu_msg_push_buffers_internal *msg) +{ + unsigned int i = 0; + struct mcu_msg_push_buffers_internal_buffer *buffer; + unsigned int num_buffers = (msg->header.length - 4) / sizeof(*buffer); + unsigned int j; + + dst[i++] = msg->channel_id; + + for (j = 0; j < num_buffers; j++) { + buffer = &msg->buffer[j]; + dst[i++] = buffer->dma_addr; + dst[i++] = buffer->mcu_addr; + dst[i++] = buffer->size; + } + + return i * sizeof(*dst); +} + +static ssize_t +allegro_enc_put_stream_buffer(u32 *dst, + struct mcu_msg_put_stream_buffer *msg) +{ + unsigned int i = 0; + + dst[i++] = msg->channel_id; + dst[i++] = msg->dma_addr; + dst[i++] = msg->mcu_addr; + dst[i++] = msg->size; + dst[i++] = msg->offset; + dst[i++] = lower_32_bits(msg->stream_id); + dst[i++] = upper_32_bits(msg->stream_id); + + return i * sizeof(*dst); +} + +static ssize_t +allegro_enc_encode_frame(u32 *dst, struct mcu_msg_encode_frame *msg) +{ + unsigned int i = 0; + + dst[i++] = msg->channel_id; + + dst[i++] = msg->reserved; + dst[i++] = msg->encoding_options; + dst[i++] = FIELD_PREP(GENMASK(31, 16), msg->padding) | + FIELD_PREP(GENMASK(15, 0), msg->pps_qp); + dst[i++] = lower_32_bits(msg->user_param); + dst[i++] = upper_32_bits(msg->user_param); + dst[i++] = lower_32_bits(msg->src_handle); + dst[i++] = upper_32_bits(msg->src_handle); + dst[i++] = msg->request_options; + dst[i++] = msg->src_y; + dst[i++] = msg->src_uv; + dst[i++] = msg->stride; + dst[i++] = msg->ep2; + dst[i++] = lower_32_bits(msg->ep2_v); + dst[i++] = upper_32_bits(msg->ep2_v); + + return i * sizeof(*dst); +} + +static ssize_t +allegro_dec_init(struct mcu_msg_init_response *msg, u32 *src) +{ + unsigned int i = 0; + + msg->header.type = FIELD_GET(GENMASK(31, 16), src[i]); + msg->header.length = FIELD_GET(GENMASK(15, 0), src[i++]); + msg->reserved0 = src[i++]; + + return i * sizeof(*src); +} + +static ssize_t +allegro_dec_create_channel(struct mcu_msg_create_channel_response *msg, + u32 *src) +{ + unsigned int i = 0; + + msg->header.type = FIELD_GET(GENMASK(31, 16), src[i]); + msg->header.length = FIELD_GET(GENMASK(15, 0), src[i++]); + msg->channel_id = src[i++]; + msg->user_id = src[i++]; + msg->options = src[i++]; + msg->num_core = src[i++]; + msg->pps_param = src[i++]; + msg->int_buffers_count = src[i++]; + msg->int_buffers_size = src[i++]; + msg->rec_buffers_count = src[i++]; + msg->rec_buffers_size = src[i++]; + msg->reserved = src[i++]; + msg->error_code = src[i++]; + + return i * sizeof(*src); +} + +static ssize_t +allegro_dec_destroy_channel(struct mcu_msg_destroy_channel_response *msg, + u32 *src) +{ + unsigned int i = 0; + + msg->header.type = FIELD_GET(GENMASK(31, 16), src[i]); + msg->header.length = FIELD_GET(GENMASK(15, 0), src[i++]); + msg->channel_id = src[i++]; + + return i * sizeof(*src); +} + +static ssize_t +allegro_dec_encode_frame(struct mcu_msg_encode_frame_response *msg, u32 *src) +{ + unsigned int i = 0; + unsigned int j; + + msg->header.type = FIELD_GET(GENMASK(31, 16), src[i]); + msg->header.length = FIELD_GET(GENMASK(15, 0), src[i++]); + msg->channel_id = src[i++]; + + msg->stream_id = src[i++]; + msg->stream_id |= (((u64)src[i++]) << 32); + msg->user_param = src[i++]; + msg->user_param |= (((u64)src[i++]) << 32); + msg->src_handle = src[i++]; + msg->src_handle |= (((u64)src[i++]) << 32); + msg->skip = FIELD_GET(GENMASK(31, 16), src[i]); + msg->is_ref = FIELD_GET(GENMASK(15, 0), src[i++]); + msg->initial_removal_delay = src[i++]; + msg->dpb_output_delay = src[i++]; + msg->size = src[i++]; + msg->frame_tag_size = src[i++]; + msg->stuffing = src[i++]; + msg->filler = src[i++]; + msg->num_column = FIELD_GET(GENMASK(31, 16), src[i]); + msg->num_row = FIELD_GET(GENMASK(15, 0), src[i++]); + msg->num_ref_idx_l1 = FIELD_GET(GENMASK(31, 24), src[i]); + msg->num_ref_idx_l0 = FIELD_GET(GENMASK(23, 16), src[i]); + msg->qp = FIELD_GET(GENMASK(15, 0), src[i++]); + msg->partition_table_offset = src[i++]; + msg->partition_table_size = src[i++]; + msg->sum_complex = src[i++]; + for (j = 0; j < 4; j++) + msg->tile_width[j] = src[i++]; + for (j = 0; j < 22; j++) + msg->tile_height[j] = src[i++]; + msg->error_code = src[i++]; + msg->slice_type = src[i++]; + msg->pic_struct = src[i++]; + msg->reserved = FIELD_GET(GENMASK(31, 24), src[i]); + msg->is_last_slice = FIELD_GET(GENMASK(23, 16), src[i]); + msg->is_first_slice = FIELD_GET(GENMASK(15, 8), src[i]); + msg->is_idr = FIELD_GET(GENMASK(7, 0), src[i++]); + + msg->reserved1 = FIELD_GET(GENMASK(31, 16), src[i]); + msg->pps_qp = FIELD_GET(GENMASK(15, 0), src[i++]); + + msg->reserved2 = src[i++]; + + return i * sizeof(*src); +} + +/** + * allegro_encode_mail() - Encode allegro messages to firmware format + * @dst: Pointer to the memory that will be filled with data + * @msg: The allegro message that will be encoded + */ +ssize_t allegro_encode_mail(u32 *dst, void *msg) +{ + const struct mcu_msg_header *header = msg; + enum mcu_msg_type type = header->type; + ssize_t size; + + if (!msg || !dst) + return -EINVAL; + + switch (type) { + case MCU_MSG_TYPE_INIT: + size = allegro_enc_init(&dst[1], msg); + break; + case MCU_MSG_TYPE_CREATE_CHANNEL: + size = allegro_enc_create_channel(&dst[1], msg); + break; + case MCU_MSG_TYPE_DESTROY_CHANNEL: + size = allegro_enc_destroy_channel(&dst[1], msg); + break; + case MCU_MSG_TYPE_ENCODE_FRAME: + size = allegro_enc_encode_frame(&dst[1], msg); + break; + case MCU_MSG_TYPE_PUT_STREAM_BUFFER: + size = allegro_enc_put_stream_buffer(&dst[1], msg); + break; + case MCU_MSG_TYPE_PUSH_BUFFER_INTERMEDIATE: + case MCU_MSG_TYPE_PUSH_BUFFER_REFERENCE: + size = allegro_enc_push_buffers(&dst[1], msg); + break; + default: + return -EINVAL; + } + + /* + * The encoded messages might have different length depending on + * the firmware version or certain fields. Therefore, we have to + * set the body length after encoding the message. + */ + dst[0] = FIELD_PREP(GENMASK(31, 16), header->type) | + FIELD_PREP(GENMASK(15, 0), size); + + return size + sizeof(*dst); +} + +/** + * allegro_decode_mail() - Parse allegro messages from the firmware. + * @msg: The mcu_msg_response that will be filled with parsed values. + * @src: Pointer to the memory that will be parsed + * + * The message format in the mailbox depends on the firmware. Parse the + * different formats into a uniform message format that can be used without + * taking care of the firmware version. + */ +int allegro_decode_mail(void *msg, u32 *src) +{ + struct mcu_msg_header *header; + + if (!src || !msg) + return -EINVAL; + + header = (struct mcu_msg_header *)src; + switch (header->type) { + case MCU_MSG_TYPE_INIT: + allegro_dec_init(msg, src); + break; + case MCU_MSG_TYPE_CREATE_CHANNEL: + allegro_dec_create_channel(msg, src); + break; + case MCU_MSG_TYPE_DESTROY_CHANNEL: + allegro_dec_destroy_channel(msg, src); + break; + case MCU_MSG_TYPE_ENCODE_FRAME: + allegro_dec_encode_frame(msg, src); + break; + default: + return -EINVAL; + } + + return 0; +} diff --git a/drivers/staging/media/allegro-dvt/allegro-mail.h b/drivers/staging/media/allegro-dvt/allegro-mail.h index 17db665f8e1ed..457caf50ebe6b 100644 --- a/drivers/staging/media/allegro-dvt/allegro-mail.h +++ b/drivers/staging/media/allegro-dvt/allegro-mail.h @@ -264,4 +264,7 @@ union mcu_msg_response { struct mcu_msg_encode_frame_response encode_frame; }; +int allegro_decode_mail(void *msg, u32 *src); +ssize_t allegro_encode_mail(u32 *dst, void *msg); + #endif -- GitLab From 53c200ff49636de598b2ef5689d548c774b0373a Mon Sep 17 00:00:00 2001 From: Michael Tretter Date: Mon, 13 Jul 2020 16:42:21 +0200 Subject: [PATCH 0645/1476] media: allegro: add field for number of buffers When pushing the buffers for the intermediate and reference frames to the MCU, the driver relied on the message size to calculate the number of buffers. As it is not necessary anymore to keep the messages binary compatible to the firmware, we can just explicitly write the number of buffers into the message. Signed-off-by: Michael Tretter Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/allegro-dvt/allegro-core.c | 1 + drivers/staging/media/allegro-dvt/allegro-mail.c | 2 +- drivers/staging/media/allegro-dvt/allegro-mail.h | 1 + 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/staging/media/allegro-dvt/allegro-core.c b/drivers/staging/media/allegro-dvt/allegro-core.c index 6334b351ee3b0..6745a5fa11677 100644 --- a/drivers/staging/media/allegro-dvt/allegro-core.c +++ b/drivers/staging/media/allegro-dvt/allegro-core.c @@ -1115,6 +1115,7 @@ static int allegro_mcu_push_buffer_internal(struct allegro_channel *channel, msg->header.length = size - sizeof(msg->header); msg->header.type = type; msg->channel_id = channel->mcu_channel_id; + msg->num_buffers = num_buffers; buffer = msg->buffer; list_for_each_entry(al_buffer, list, head) { diff --git a/drivers/staging/media/allegro-dvt/allegro-mail.c b/drivers/staging/media/allegro-dvt/allegro-mail.c index c78367d36e2e5..ba1c3bc587c69 100644 --- a/drivers/staging/media/allegro-dvt/allegro-mail.c +++ b/drivers/staging/media/allegro-dvt/allegro-mail.c @@ -156,7 +156,7 @@ allegro_enc_push_buffers(u32 *dst, struct mcu_msg_push_buffers_internal *msg) { unsigned int i = 0; struct mcu_msg_push_buffers_internal_buffer *buffer; - unsigned int num_buffers = (msg->header.length - 4) / sizeof(*buffer); + unsigned int num_buffers = msg->num_buffers; unsigned int j; dst[i++] = msg->channel_id; diff --git a/drivers/staging/media/allegro-dvt/allegro-mail.h b/drivers/staging/media/allegro-dvt/allegro-mail.h index 457caf50ebe6b..14b54eb524889 100644 --- a/drivers/staging/media/allegro-dvt/allegro-mail.h +++ b/drivers/staging/media/allegro-dvt/allegro-mail.h @@ -169,6 +169,7 @@ struct mcu_msg_push_buffers_internal_buffer { struct mcu_msg_push_buffers_internal { struct mcu_msg_header header; u32 channel_id; + size_t num_buffers; struct mcu_msg_push_buffers_internal_buffer buffer[]; } __attribute__ ((__packed__)); -- GitLab From fc1c13deb1f95467cf9819ef57157b542efcb901 Mon Sep 17 00:00:00 2001 From: Michael Tretter Date: Mon, 13 Jul 2020 16:42:22 +0200 Subject: [PATCH 0646/1476] media: allegro: don't pack MCU messages The messages are now explicitly converted from the struct to the binary representation used by the firmware. Therefore, there is no need to keep the structs packed anymore. Drop the attribute and avoid confusion why the mails are packed. Signed-off-by: Michael Tretter Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- .../staging/media/allegro-dvt/allegro-mail.h | 26 +++++++++---------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/drivers/staging/media/allegro-dvt/allegro-mail.h b/drivers/staging/media/allegro-dvt/allegro-mail.h index 14b54eb524889..ae81e94a2f2cf 100644 --- a/drivers/staging/media/allegro-dvt/allegro-mail.h +++ b/drivers/staging/media/allegro-dvt/allegro-mail.h @@ -25,7 +25,7 @@ const char *msg_type_name(enum mcu_msg_type type); struct mcu_msg_header { u16 length; /* length of the body in bytes */ u16 type; -} __attribute__ ((__packed__)); +}; struct mcu_msg_init_request { struct mcu_msg_header header; @@ -33,12 +33,12 @@ struct mcu_msg_init_request { u32 suballoc_dma; u32 suballoc_size; s32 l2_cache[3]; -} __attribute__ ((__packed__)); +}; struct mcu_msg_init_response { struct mcu_msg_header header; u32 reserved0; -} __attribute__ ((__packed__)); +}; struct create_channel_param { u16 width; @@ -127,13 +127,13 @@ struct create_channel_param { u32 subframe_latency; u32 lda_control_mode; u32 unknown41; -} __attribute__ ((__packed__)); +}; struct mcu_msg_create_channel { struct mcu_msg_header header; u32 user_id; struct create_channel_param param; -} __attribute__ ((__packed__)); +}; struct mcu_msg_create_channel_response { struct mcu_msg_header header; @@ -148,30 +148,30 @@ struct mcu_msg_create_channel_response { u32 rec_buffers_size; u32 reserved; u32 error_code; -} __attribute__ ((__packed__)); +}; struct mcu_msg_destroy_channel { struct mcu_msg_header header; u32 channel_id; -} __attribute__ ((__packed__)); +}; struct mcu_msg_destroy_channel_response { struct mcu_msg_header header; u32 channel_id; -} __attribute__ ((__packed__)); +}; struct mcu_msg_push_buffers_internal_buffer { u32 dma_addr; u32 mcu_addr; u32 size; -} __attribute__ ((__packed__)); +}; struct mcu_msg_push_buffers_internal { struct mcu_msg_header header; u32 channel_id; size_t num_buffers; struct mcu_msg_push_buffers_internal_buffer buffer[]; -} __attribute__ ((__packed__)); +}; struct mcu_msg_put_stream_buffer { struct mcu_msg_header header; @@ -181,7 +181,7 @@ struct mcu_msg_put_stream_buffer { u32 size; u32 offset; u64 stream_id; -} __attribute__ ((__packed__)); +}; struct mcu_msg_encode_frame { struct mcu_msg_header header; @@ -214,7 +214,7 @@ struct mcu_msg_encode_frame { u32 stride; u32 ep2; u64 ep2_v; -} __attribute__ ((__packed__)); +}; struct mcu_msg_encode_frame_response { struct mcu_msg_header header; @@ -255,7 +255,7 @@ struct mcu_msg_encode_frame_response { u16 pps_qp; u16 reserved1; u32 reserved2; -} __attribute__ ((__packed__)); +}; union mcu_msg_response { struct mcu_msg_header header; -- GitLab From 62ed97df7dc1bbb564fcd1c4f795aaa74245c63f Mon Sep 17 00:00:00 2001 From: Michael Tretter Date: Mon, 13 Jul 2020 16:42:23 +0200 Subject: [PATCH 0647/1476] media: allegro: support handling firmware dependent values Like the message format, also the identifiers in the messages differ between firmware versions. This especially affects the identifier for the codec that shall be used. As the messages used by the driver are now independent from the firmware, we can use the values defined by V4L2 as identifiers in the messages. Convert the V4L2 codec format to the respective firmware value when encoding the messages to binary format instead beforehand. Signed-off-by: Michael Tretter Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/allegro-dvt/allegro-core.c | 11 +---------- drivers/staging/media/allegro-dvt/allegro-mail.c | 15 ++++++++++++++- drivers/staging/media/allegro-dvt/allegro-mail.h | 2 +- 3 files changed, 16 insertions(+), 12 deletions(-) diff --git a/drivers/staging/media/allegro-dvt/allegro-core.c b/drivers/staging/media/allegro-dvt/allegro-core.c index 6745a5fa11677..1bbb44140a6c4 100644 --- a/drivers/staging/media/allegro-dvt/allegro-core.c +++ b/drivers/staging/media/allegro-dvt/allegro-core.c @@ -812,15 +812,6 @@ static u32 v4l2_colorspace_to_mcu_colorspace(enum v4l2_colorspace colorspace) } } -static s8 v4l2_pixelformat_to_mcu_codec(u32 pixelformat) -{ - switch (pixelformat) { - case V4L2_PIX_FMT_H264: - default: - return 1; - } -} - static u8 v4l2_profile_to_mcu_profile(enum v4l2_mpeg_video_h264_profile profile) { switch (profile) { @@ -919,7 +910,7 @@ static int fill_create_channel_param(struct allegro_channel *channel, param->src_mode = 0x0; param->profile = v4l2_profile_to_mcu_profile(channel->profile); param->constraint_set_flags = BIT(1); - param->codec = v4l2_pixelformat_to_mcu_codec(channel->codec); + param->codec = channel->codec; param->level = v4l2_level_to_mcu_level(channel->level); param->tier = 0; param->sps_param = BIT(20) | 0x4a; diff --git a/drivers/staging/media/allegro-dvt/allegro-mail.c b/drivers/staging/media/allegro-dvt/allegro-mail.c index ba1c3bc587c69..ce183bd2495ba 100644 --- a/drivers/staging/media/allegro-dvt/allegro-mail.c +++ b/drivers/staging/media/allegro-dvt/allegro-mail.c @@ -9,6 +9,7 @@ #include #include #include +#include #include "allegro-mail.h" @@ -53,17 +54,29 @@ allegro_enc_init(u32 *dst, struct mcu_msg_init_request *msg) return i * sizeof(*dst); } +static inline u32 settings_get_mcu_codec(struct create_channel_param *param) +{ + u32 pixelformat = param->codec; + + switch (pixelformat) { + case V4L2_PIX_FMT_H264: + default: + return 1; + } +} + static ssize_t allegro_encode_channel_config(u32 *dst, struct create_channel_param *param) { unsigned int i = 0; + unsigned int codec = settings_get_mcu_codec(param); dst[i++] = FIELD_PREP(GENMASK(31, 16), param->height) | FIELD_PREP(GENMASK(15, 0), param->width); dst[i++] = param->format; dst[i++] = param->colorspace; dst[i++] = param->src_mode; - dst[i++] = FIELD_PREP(GENMASK(31, 24), param->codec) | + dst[i++] = FIELD_PREP(GENMASK(31, 24), codec) | FIELD_PREP(GENMASK(23, 8), param->constraint_set_flags) | FIELD_PREP(GENMASK(7, 0), param->profile); dst[i++] = FIELD_PREP(GENMASK(31, 16), param->tier) | diff --git a/drivers/staging/media/allegro-dvt/allegro-mail.h b/drivers/staging/media/allegro-dvt/allegro-mail.h index ae81e94a2f2cf..a92a27e270b5c 100644 --- a/drivers/staging/media/allegro-dvt/allegro-mail.h +++ b/drivers/staging/media/allegro-dvt/allegro-mail.h @@ -48,7 +48,7 @@ struct create_channel_param { u32 src_mode; u8 profile; u16 constraint_set_flags; - s8 codec; + u32 codec; u16 level; u16 tier; u32 sps_param; -- GitLab From d30e841216411cb95ffbb56252f8431fd752c54d Mon Sep 17 00:00:00 2001 From: Michael Tretter Date: Mon, 13 Jul 2020 16:42:24 +0200 Subject: [PATCH 0648/1476] media: allegro: encode bit fields separately Even bits in bitfields do not keep their position, but move around or move to other bitfields. Therefore, the driver has to handle bitfields differently depending on the firmware version. Create separate fields for the options that have been in bitfields and handle the bitfields when encoding the message. As a side effect, this makes the messages a bit more readable. Signed-off-by: Michael Tretter Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- .../staging/media/allegro-dvt/allegro-core.c | 15 ++++++--- .../staging/media/allegro-dvt/allegro-mail.c | 22 +++++++++++-- .../staging/media/allegro-dvt/allegro-mail.h | 32 +++++-------------- 3 files changed, 38 insertions(+), 31 deletions(-) diff --git a/drivers/staging/media/allegro-dvt/allegro-core.c b/drivers/staging/media/allegro-dvt/allegro-core.c index 1bbb44140a6c4..e6d64151e4fed 100644 --- a/drivers/staging/media/allegro-dvt/allegro-core.c +++ b/drivers/staging/media/allegro-dvt/allegro-core.c @@ -913,10 +913,17 @@ static int fill_create_channel_param(struct allegro_channel *channel, param->codec = channel->codec; param->level = v4l2_level_to_mcu_level(channel->level); param->tier = 0; - param->sps_param = BIT(20) | 0x4a; - param->pps_param = BIT(2); - param->enc_option = AL_OPT_RDO_COST_MODE | AL_OPT_LF_X_TILE | - AL_OPT_LF_X_SLICE | AL_OPT_LF; + + param->log2_max_poc = 10; + param->log2_max_frame_num = 4; + param->temporal_mvp_enable = 1; + + param->dbf_ovr_en = 1; + param->rdo_cost_mode = 1; + param->lf = 1; + param->lf_x_tile = 1; + param->lf_x_slice = 1; + param->beta_offset = -1; param->tc_offset = -1; param->num_slices = 1; diff --git a/drivers/staging/media/allegro-dvt/allegro-mail.c b/drivers/staging/media/allegro-dvt/allegro-mail.c index ce183bd2495ba..7121f128aff39 100644 --- a/drivers/staging/media/allegro-dvt/allegro-mail.c +++ b/drivers/staging/media/allegro-dvt/allegro-mail.c @@ -69,6 +69,7 @@ static ssize_t allegro_encode_channel_config(u32 *dst, struct create_channel_param *param) { unsigned int i = 0; + u32 val; unsigned int codec = settings_get_mcu_codec(param); dst[i++] = FIELD_PREP(GENMASK(31, 16), param->height) | @@ -81,9 +82,24 @@ allegro_encode_channel_config(u32 *dst, struct create_channel_param *param) FIELD_PREP(GENMASK(7, 0), param->profile); dst[i++] = FIELD_PREP(GENMASK(31, 16), param->tier) | FIELD_PREP(GENMASK(15, 0), param->level); - dst[i++] = param->sps_param; - dst[i++] = param->pps_param; - dst[i++] = param->enc_option; + + val = 0; + val |= param->temporal_mvp_enable ? BIT(20) : 0; + val |= FIELD_PREP(GENMASK(7, 4), param->log2_max_frame_num) | + FIELD_PREP(GENMASK(3, 0), param->log2_max_poc); + dst[i++] = val; + + val = 0; + val |= param->dbf_ovr_en ? BIT(2) : 0; + dst[i++] = val; + + val = 0; + val |= param->lf ? BIT(2) : 0; + val |= param->lf_x_tile ? BIT(3) : 0; + val |= param->lf_x_slice ? BIT(4) : 0; + val |= param->rdo_cost_mode ? BIT(20) : 0; + dst[i++] = val; + dst[i++] = FIELD_PREP(GENMASK(15, 8), param->beta_offset) | FIELD_PREP(GENMASK(7, 0), param->tc_offset); dst[i++] = param->unknown11; diff --git a/drivers/staging/media/allegro-dvt/allegro-mail.h b/drivers/staging/media/allegro-dvt/allegro-mail.h index a92a27e270b5c..239fd8a20a69b 100644 --- a/drivers/staging/media/allegro-dvt/allegro-mail.h +++ b/drivers/staging/media/allegro-dvt/allegro-mail.h @@ -51,30 +51,14 @@ struct create_channel_param { u32 codec; u16 level; u16 tier; - u32 sps_param; - u32 pps_param; - - u32 enc_option; -#define AL_OPT_WPP BIT(0) -#define AL_OPT_TILE BIT(1) -#define AL_OPT_LF BIT(2) -#define AL_OPT_LF_X_SLICE BIT(3) -#define AL_OPT_LF_X_TILE BIT(4) -#define AL_OPT_SCL_LST BIT(5) -#define AL_OPT_CONST_INTRA_PRED BIT(6) -#define AL_OPT_QP_TAB_RELATIVE BIT(7) -#define AL_OPT_FIX_PREDICTOR BIT(8) -#define AL_OPT_CUSTOM_LDA BIT(9) -#define AL_OPT_ENABLE_AUTO_QP BIT(10) -#define AL_OPT_ADAPT_AUTO_QP BIT(11) -#define AL_OPT_TRANSFO_SKIP BIT(13) -#define AL_OPT_FORCE_REC BIT(15) -#define AL_OPT_FORCE_MV_OUT BIT(16) -#define AL_OPT_FORCE_MV_CLIP BIT(17) -#define AL_OPT_LOWLAT_SYNC BIT(18) -#define AL_OPT_LOWLAT_INT BIT(19) -#define AL_OPT_RDO_COST_MODE BIT(20) - + u32 log2_max_poc; + u32 log2_max_frame_num; + u32 temporal_mvp_enable; + u32 dbf_ovr_en; + u32 rdo_cost_mode; + u32 lf; + u32 lf_x_tile; + u32 lf_x_slice; s8 beta_offset; s8 tc_offset; u16 reserved10; -- GitLab From c4756d8774159ef1a9a204d656589c886fcefc5b Mon Sep 17 00:00:00 2001 From: Michael Tretter Date: Mon, 13 Jul 2020 16:42:25 +0200 Subject: [PATCH 0649/1476] media: allegro: add config blob for channel Firmware versions >= 2019.2 do not configure the channel via the mailbox interface anymore, but use a separate chunk of memory that is only referenced by the message. As the configuration must be in a format that is understood by the firmware and this format can change between firmware versions, handle it similar to the message and treat is as a blob. In order to support both methods in the driver, always use a separate blob for the channel configuration and copy that blob into the mailbox if the firmware requires it and otherwise reference it. Signed-off-by: Michael Tretter Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- .../staging/media/allegro-dvt/allegro-core.c | 20 ++++++++++++++++++- .../staging/media/allegro-dvt/allegro-mail.c | 18 +++++++++++------ .../staging/media/allegro-dvt/allegro-mail.h | 9 ++++++++- 3 files changed, 39 insertions(+), 8 deletions(-) diff --git a/drivers/staging/media/allegro-dvt/allegro-core.c b/drivers/staging/media/allegro-dvt/allegro-core.c index e6d64151e4fed..51e85cad99418 100644 --- a/drivers/staging/media/allegro-dvt/allegro-core.c +++ b/drivers/staging/media/allegro-dvt/allegro-core.c @@ -206,6 +206,8 @@ struct allegro_channel { unsigned int cpb_size; unsigned int gop_size; + struct allegro_buffer config_blob; + struct v4l2_ctrl *mpeg_video_h264_profile; struct v4l2_ctrl *mpeg_video_h264_level; struct v4l2_ctrl *mpeg_video_h264_i_frame_qp; @@ -978,6 +980,14 @@ static int allegro_mcu_send_create_channel(struct allegro_dev *dev, struct allegro_channel *channel) { struct mcu_msg_create_channel msg; + struct allegro_buffer *blob = &channel->config_blob; + struct create_channel_param param; + size_t size; + + memset(¶m, 0, sizeof(param)); + fill_create_channel_param(channel, ¶m); + allegro_alloc_buffer(dev, blob, sizeof(struct create_channel_param)); + size = allegro_encode_config_blob(blob->vaddr, ¶m); memset(&msg, 0, sizeof(msg)); @@ -986,7 +996,9 @@ static int allegro_mcu_send_create_channel(struct allegro_dev *dev, msg.user_id = channel->user_id; - fill_create_channel_param(channel, &msg.param); + msg.blob = blob->vaddr; + msg.blob_size = size; + msg.blob_mcu_addr = to_mcu_addr(dev, blob->paddr); allegro_mbox_send(dev->mbox_command, &msg); @@ -1596,6 +1608,7 @@ allegro_handle_create_channel(struct allegro_dev *dev, { struct allegro_channel *channel; int err = 0; + struct create_channel_param param; channel = allegro_find_channel_by_user_id(dev, msg->user_id); if (IS_ERR(channel)) { @@ -1621,6 +1634,11 @@ allegro_handle_create_channel(struct allegro_dev *dev, "user %d: channel has channel id %d\n", channel->user_id, channel->mcu_channel_id); + err = allegro_decode_config_blob(¶m, msg, channel->config_blob.vaddr); + allegro_free_buffer(channel->dev, &channel->config_blob); + if (err) + goto out; + v4l2_dbg(1, debug, &dev->v4l2_dev, "channel %d: intermediate buffers: %d x %d bytes\n", channel->mcu_channel_id, diff --git a/drivers/staging/media/allegro-dvt/allegro-mail.c b/drivers/staging/media/allegro-dvt/allegro-mail.c index 7121f128aff39..c55b9339f9c67 100644 --- a/drivers/staging/media/allegro-dvt/allegro-mail.c +++ b/drivers/staging/media/allegro-dvt/allegro-mail.c @@ -9,6 +9,7 @@ #include #include #include +#include #include #include "allegro-mail.h" @@ -65,8 +66,8 @@ static inline u32 settings_get_mcu_codec(struct create_channel_param *param) } } -static ssize_t -allegro_encode_channel_config(u32 *dst, struct create_channel_param *param) +ssize_t +allegro_encode_config_blob(u32 *dst, struct create_channel_param *param) { unsigned int i = 0; u32 val; @@ -158,18 +159,23 @@ allegro_encode_channel_config(u32 *dst, struct create_channel_param *param) static ssize_t allegro_enc_create_channel(u32 *dst, struct mcu_msg_create_channel *msg) { - struct create_channel_param *param = &msg->param; - ssize_t size = 0; unsigned int i = 0; dst[i++] = msg->user_id; - size = allegro_encode_channel_config(&dst[i], param); - i += size / sizeof(*dst); + memcpy(&dst[i], msg->blob, msg->blob_size); + i += msg->blob_size / sizeof(*dst); return i * sizeof(*dst); } +ssize_t allegro_decode_config_blob(struct create_channel_param *param, + struct mcu_msg_create_channel_response *msg, + u32 *src) +{ + return 0; +} + static ssize_t allegro_enc_destroy_channel(u32 *dst, struct mcu_msg_destroy_channel *msg) { diff --git a/drivers/staging/media/allegro-dvt/allegro-mail.h b/drivers/staging/media/allegro-dvt/allegro-mail.h index 239fd8a20a69b..07ed0a8d3de35 100644 --- a/drivers/staging/media/allegro-dvt/allegro-mail.h +++ b/drivers/staging/media/allegro-dvt/allegro-mail.h @@ -116,7 +116,9 @@ struct create_channel_param { struct mcu_msg_create_channel { struct mcu_msg_header header; u32 user_id; - struct create_channel_param param; + u32 *blob; + size_t blob_size; + u32 blob_mcu_addr; }; struct mcu_msg_create_channel_response { @@ -249,6 +251,11 @@ union mcu_msg_response { struct mcu_msg_encode_frame_response encode_frame; }; +ssize_t allegro_encode_config_blob(u32 *dst, struct create_channel_param *param); +ssize_t allegro_decode_config_blob(struct create_channel_param *param, + struct mcu_msg_create_channel_response *msg, + u32 *src); + int allegro_decode_mail(void *msg, u32 *src); ssize_t allegro_encode_mail(u32 *dst, void *msg); -- GitLab From c7ce107f9d899a15e4b9c24c4839abdbef88a063 Mon Sep 17 00:00:00 2001 From: Michael Tretter Date: Mon, 13 Jul 2020 16:42:26 +0200 Subject: [PATCH 0650/1476] media: allegro: set num_ref_idx using response of configured channels The firmware decides how many reference frames shall be used und returns this information via the config blob. In order to set the number of reference frames in the PPS, the driver has to read these values from the config blob after the channel has been created in the firmware. Signed-off-by: Michael Tretter Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/allegro-dvt/allegro-core.c | 10 ++++++++-- drivers/staging/media/allegro-dvt/allegro-mail.c | 6 +++++- drivers/staging/media/allegro-dvt/allegro-mail.h | 5 ++++- 3 files changed, 17 insertions(+), 4 deletions(-) diff --git a/drivers/staging/media/allegro-dvt/allegro-core.c b/drivers/staging/media/allegro-dvt/allegro-core.c index 51e85cad99418..07deb5ba13dcf 100644 --- a/drivers/staging/media/allegro-dvt/allegro-core.c +++ b/drivers/staging/media/allegro-dvt/allegro-core.c @@ -208,6 +208,9 @@ struct allegro_channel { struct allegro_buffer config_blob; + unsigned int num_ref_idx_l0; + unsigned int num_ref_idx_l1; + struct v4l2_ctrl *mpeg_video_h264_profile; struct v4l2_ctrl *mpeg_video_h264_level; struct v4l2_ctrl *mpeg_video_h264_i_frame_qp; @@ -1336,8 +1339,8 @@ static ssize_t allegro_h264_write_pps(struct allegro_channel *channel, pps->entropy_coding_mode_flag = 0; pps->bottom_field_pic_order_in_frame_present_flag = 0; pps->num_slice_groups_minus1 = 0; - pps->num_ref_idx_l0_default_active_minus1 = 2; - pps->num_ref_idx_l1_default_active_minus1 = 2; + pps->num_ref_idx_l0_default_active_minus1 = channel->num_ref_idx_l0 - 1; + pps->num_ref_idx_l1_default_active_minus1 = channel->num_ref_idx_l1 - 1; pps->weighted_pred_flag = 0; pps->weighted_bipred_idc = 0; pps->pic_init_qp_minus26 = 0; @@ -1639,6 +1642,9 @@ allegro_handle_create_channel(struct allegro_dev *dev, if (err) goto out; + channel->num_ref_idx_l0 = param.num_ref_idx_l0; + channel->num_ref_idx_l1 = param.num_ref_idx_l1; + v4l2_dbg(1, debug, &dev->v4l2_dev, "channel %d: intermediate buffers: %d x %d bytes\n", channel->mcu_channel_id, diff --git a/drivers/staging/media/allegro-dvt/allegro-mail.c b/drivers/staging/media/allegro-dvt/allegro-mail.c index c55b9339f9c67..bb15de0804317 100644 --- a/drivers/staging/media/allegro-dvt/allegro-mail.c +++ b/drivers/staging/media/allegro-dvt/allegro-mail.c @@ -173,6 +173,9 @@ ssize_t allegro_decode_config_blob(struct create_channel_param *param, struct mcu_msg_create_channel_response *msg, u32 *src) { + param->num_ref_idx_l0 = msg->num_ref_idx_l0; + param->num_ref_idx_l1 = msg->num_ref_idx_l1; + return 0; } @@ -273,7 +276,8 @@ allegro_dec_create_channel(struct mcu_msg_create_channel_response *msg, msg->user_id = src[i++]; msg->options = src[i++]; msg->num_core = src[i++]; - msg->pps_param = src[i++]; + msg->num_ref_idx_l0 = FIELD_GET(GENMASK(7, 4), src[i]); + msg->num_ref_idx_l1 = FIELD_GET(GENMASK(11, 8), src[i++]); msg->int_buffers_count = src[i++]; msg->int_buffers_size = src[i++]; msg->rec_buffers_count = src[i++]; diff --git a/drivers/staging/media/allegro-dvt/allegro-mail.h b/drivers/staging/media/allegro-dvt/allegro-mail.h index 07ed0a8d3de35..a4d829f6f99d0 100644 --- a/drivers/staging/media/allegro-dvt/allegro-mail.h +++ b/drivers/staging/media/allegro-dvt/allegro-mail.h @@ -55,6 +55,8 @@ struct create_channel_param { u32 log2_max_frame_num; u32 temporal_mvp_enable; u32 dbf_ovr_en; + u32 num_ref_idx_l0; + u32 num_ref_idx_l1; u32 rdo_cost_mode; u32 lf; u32 lf_x_tile; @@ -127,7 +129,8 @@ struct mcu_msg_create_channel_response { u32 user_id; u32 options; u32 num_core; - u32 pps_param; + u32 num_ref_idx_l0; + u32 num_ref_idx_l1; u32 int_buffers_count; u32 int_buffers_size; u32 rec_buffers_count; -- GitLab From d8fefda89ee292b2871ce523227e5a49d4afc6ea Mon Sep 17 00:00:00 2001 From: Michael Tretter Date: Mon, 13 Jul 2020 16:42:27 +0200 Subject: [PATCH 0651/1476] media: allegro: drop length field from message header The length of the message will be determined when the message is encoded. Writing the size of the struct into the message in the driver won't be the actual length of the message that is send to the firmware. Therefore, drop the length field from the message. Since the header is the same for all response messages, it does not make sense to read the header in each decoding function, but we can simply decode it once before dispatching to the respective functions. Signed-off-by: Michael Tretter Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/allegro-dvt/allegro-core.c | 6 ------ drivers/staging/media/allegro-dvt/allegro-mail.c | 13 ++++--------- drivers/staging/media/allegro-dvt/allegro-mail.h | 3 +-- 3 files changed, 5 insertions(+), 17 deletions(-) diff --git a/drivers/staging/media/allegro-dvt/allegro-core.c b/drivers/staging/media/allegro-dvt/allegro-core.c index 07deb5ba13dcf..85d2c45be9d24 100644 --- a/drivers/staging/media/allegro-dvt/allegro-core.c +++ b/drivers/staging/media/allegro-dvt/allegro-core.c @@ -776,7 +776,6 @@ static void allegro_mcu_send_init(struct allegro_dev *dev, memset(&msg, 0, sizeof(msg)); msg.header.type = MCU_MSG_TYPE_INIT; - msg.header.length = sizeof(msg) - sizeof(msg.header); msg.suballoc_dma = to_mcu_addr(dev, suballoc_dma); msg.suballoc_size = to_mcu_size(dev, suballoc_size); @@ -995,7 +994,6 @@ static int allegro_mcu_send_create_channel(struct allegro_dev *dev, memset(&msg, 0, sizeof(msg)); msg.header.type = MCU_MSG_TYPE_CREATE_CHANNEL; - msg.header.length = sizeof(msg) - sizeof(msg.header); msg.user_id = channel->user_id; @@ -1016,7 +1014,6 @@ static int allegro_mcu_send_destroy_channel(struct allegro_dev *dev, memset(&msg, 0, sizeof(msg)); msg.header.type = MCU_MSG_TYPE_DESTROY_CHANNEL; - msg.header.length = sizeof(msg) - sizeof(msg.header); msg.channel_id = channel->mcu_channel_id; @@ -1036,7 +1033,6 @@ static int allegro_mcu_send_put_stream_buffer(struct allegro_dev *dev, memset(&msg, 0, sizeof(msg)); msg.header.type = MCU_MSG_TYPE_PUT_STREAM_BUFFER; - msg.header.length = sizeof(msg) - sizeof(msg.header); msg.channel_id = channel->mcu_channel_id; msg.dma_addr = to_codec_addr(dev, paddr); @@ -1061,7 +1057,6 @@ static int allegro_mcu_send_encode_frame(struct allegro_dev *dev, memset(&msg, 0, sizeof(msg)); msg.header.type = MCU_MSG_TYPE_ENCODE_FRAME; - msg.header.length = sizeof(msg) - sizeof(msg.header); msg.channel_id = channel->mcu_channel_id; msg.encoding_options = AL_OPT_FORCE_LOAD; @@ -1125,7 +1120,6 @@ static int allegro_mcu_push_buffer_internal(struct allegro_channel *channel, if (!msg) return -ENOMEM; - msg->header.length = size - sizeof(msg->header); msg->header.type = type; msg->channel_id = channel->mcu_channel_id; msg->num_buffers = num_buffers; diff --git a/drivers/staging/media/allegro-dvt/allegro-mail.c b/drivers/staging/media/allegro-dvt/allegro-mail.c index bb15de0804317..da83aa85c8735 100644 --- a/drivers/staging/media/allegro-dvt/allegro-mail.c +++ b/drivers/staging/media/allegro-dvt/allegro-mail.c @@ -257,8 +257,6 @@ allegro_dec_init(struct mcu_msg_init_response *msg, u32 *src) { unsigned int i = 0; - msg->header.type = FIELD_GET(GENMASK(31, 16), src[i]); - msg->header.length = FIELD_GET(GENMASK(15, 0), src[i++]); msg->reserved0 = src[i++]; return i * sizeof(*src); @@ -270,8 +268,6 @@ allegro_dec_create_channel(struct mcu_msg_create_channel_response *msg, { unsigned int i = 0; - msg->header.type = FIELD_GET(GENMASK(31, 16), src[i]); - msg->header.length = FIELD_GET(GENMASK(15, 0), src[i++]); msg->channel_id = src[i++]; msg->user_id = src[i++]; msg->options = src[i++]; @@ -294,8 +290,6 @@ allegro_dec_destroy_channel(struct mcu_msg_destroy_channel_response *msg, { unsigned int i = 0; - msg->header.type = FIELD_GET(GENMASK(31, 16), src[i]); - msg->header.length = FIELD_GET(GENMASK(15, 0), src[i++]); msg->channel_id = src[i++]; return i * sizeof(*src); @@ -307,8 +301,6 @@ allegro_dec_encode_frame(struct mcu_msg_encode_frame_response *msg, u32 *src) unsigned int i = 0; unsigned int j; - msg->header.type = FIELD_GET(GENMASK(31, 16), src[i]); - msg->header.length = FIELD_GET(GENMASK(15, 0), src[i++]); msg->channel_id = src[i++]; msg->stream_id = src[i++]; @@ -418,7 +410,10 @@ int allegro_decode_mail(void *msg, u32 *src) if (!src || !msg) return -EINVAL; - header = (struct mcu_msg_header *)src; + header = msg; + header->type = FIELD_GET(GENMASK(31, 16), src[0]); + + src++; switch (header->type) { case MCU_MSG_TYPE_INIT: allegro_dec_init(msg, src); diff --git a/drivers/staging/media/allegro-dvt/allegro-mail.h b/drivers/staging/media/allegro-dvt/allegro-mail.h index a4d829f6f99d0..397622973c193 100644 --- a/drivers/staging/media/allegro-dvt/allegro-mail.h +++ b/drivers/staging/media/allegro-dvt/allegro-mail.h @@ -23,8 +23,7 @@ enum mcu_msg_type { const char *msg_type_name(enum mcu_msg_type type); struct mcu_msg_header { - u16 length; /* length of the body in bytes */ - u16 type; + enum mcu_msg_type type; }; struct mcu_msg_init_request { -- GitLab From 11146a758317f88034895dd778bf4a2ccac47303 Mon Sep 17 00:00:00 2001 From: Michael Tretter Date: Mon, 13 Jul 2020 16:42:28 +0200 Subject: [PATCH 0652/1476] media: allegro: add a version field to mcu messages In order to distinguish the message format that is expected by the firmware, add a version field to the message header. This allows to encode and decode the messages for the version of the firmware that was loaded by the driver. Signed-off-by: Michael Tretter Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- .../staging/media/allegro-dvt/allegro-core.c | 22 ++++++++++++++----- .../staging/media/allegro-dvt/allegro-mail.h | 6 +++++ 2 files changed, 23 insertions(+), 5 deletions(-) diff --git a/drivers/staging/media/allegro-dvt/allegro-core.c b/drivers/staging/media/allegro-dvt/allegro-core.c index 85d2c45be9d24..ada21ebf93bbb 100644 --- a/drivers/staging/media/allegro-dvt/allegro-core.c +++ b/drivers/staging/media/allegro-dvt/allegro-core.c @@ -130,6 +130,7 @@ struct allegro_dev { struct regmap *regmap; struct regmap *sram; + const struct fw_info *fw_info; struct allegro_buffer firmware; struct allegro_buffer suballocator; @@ -277,6 +278,7 @@ struct fw_info { unsigned int mailbox_cmd; unsigned int mailbox_status; size_t mailbox_size; + enum mcu_msg_version mailbox_version; size_t suballocator_size; }; @@ -288,6 +290,7 @@ static const struct fw_info supported_firmware[] = { .mailbox_cmd = 0x7800, .mailbox_status = 0x7c00, .mailbox_size = 0x400 - 0x8, + .mailbox_version = MCU_MSG_VERSION_2018_2, .suballocator_size = SZ_16M, }, }; @@ -749,6 +752,8 @@ static void allegro_mbox_notify(struct allegro_mbox *mbox) if (!msg) return; + msg->header.version = dev->fw_info->mailbox_version; + tmp = kmalloc(mbox->size, GFP_KERNEL); if (!tmp) goto out; @@ -776,6 +781,7 @@ static void allegro_mcu_send_init(struct allegro_dev *dev, memset(&msg, 0, sizeof(msg)); msg.header.type = MCU_MSG_TYPE_INIT; + msg.header.version = dev->fw_info->mailbox_version; msg.suballoc_dma = to_mcu_addr(dev, suballoc_dma); msg.suballoc_size = to_mcu_size(dev, suballoc_size); @@ -989,11 +995,13 @@ static int allegro_mcu_send_create_channel(struct allegro_dev *dev, memset(¶m, 0, sizeof(param)); fill_create_channel_param(channel, ¶m); allegro_alloc_buffer(dev, blob, sizeof(struct create_channel_param)); + param.version = dev->fw_info->mailbox_version; size = allegro_encode_config_blob(blob->vaddr, ¶m); memset(&msg, 0, sizeof(msg)); msg.header.type = MCU_MSG_TYPE_CREATE_CHANNEL; + msg.header.version = dev->fw_info->mailbox_version; msg.user_id = channel->user_id; @@ -1014,6 +1022,7 @@ static int allegro_mcu_send_destroy_channel(struct allegro_dev *dev, memset(&msg, 0, sizeof(msg)); msg.header.type = MCU_MSG_TYPE_DESTROY_CHANNEL; + msg.header.version = dev->fw_info->mailbox_version; msg.channel_id = channel->mcu_channel_id; @@ -1033,6 +1042,7 @@ static int allegro_mcu_send_put_stream_buffer(struct allegro_dev *dev, memset(&msg, 0, sizeof(msg)); msg.header.type = MCU_MSG_TYPE_PUT_STREAM_BUFFER; + msg.header.version = dev->fw_info->mailbox_version; msg.channel_id = channel->mcu_channel_id; msg.dma_addr = to_codec_addr(dev, paddr); @@ -1057,6 +1067,7 @@ static int allegro_mcu_send_encode_frame(struct allegro_dev *dev, memset(&msg, 0, sizeof(msg)); msg.header.type = MCU_MSG_TYPE_ENCODE_FRAME; + msg.header.version = dev->fw_info->mailbox_version; msg.channel_id = channel->mcu_channel_id; msg.encoding_options = AL_OPT_FORCE_LOAD; @@ -1121,6 +1132,8 @@ static int allegro_mcu_push_buffer_internal(struct allegro_channel *channel, return -ENOMEM; msg->header.type = type; + msg->header.version = dev->fw_info->mailbox_version; + msg->channel_id = channel->mcu_channel_id; msg->num_buffers = num_buffers; @@ -2988,7 +3001,6 @@ static void allegro_fw_callback(const struct firmware *fw, void *context) const char *fw_codec_name = "al5e.fw"; const struct firmware *fw_codec; int err; - const struct fw_info *info; if (!fw) return; @@ -2999,14 +3011,14 @@ static void allegro_fw_callback(const struct firmware *fw, void *context) if (err) goto err_release_firmware; - info = allegro_get_firmware_info(dev, fw, fw_codec); - if (!info) { + dev->fw_info = allegro_get_firmware_info(dev, fw, fw_codec); + if (!dev->fw_info) { v4l2_err(&dev->v4l2_dev, "firmware is not supported\n"); goto err_release_firmware_codec; } v4l2_info(&dev->v4l2_dev, - "using mcu firmware version '%s'\n", info->version); + "using mcu firmware version '%s'\n", dev->fw_info->version); /* Ensure that the mcu is sleeping at the reset vector */ err = allegro_mcu_reset(dev); @@ -3018,7 +3030,7 @@ static void allegro_fw_callback(const struct firmware *fw, void *context) allegro_copy_firmware(dev, fw->data, fw->size); allegro_copy_fw_codec(dev, fw_codec->data, fw_codec->size); - err = allegro_mcu_hw_init(dev, info); + err = allegro_mcu_hw_init(dev, dev->fw_info); if (err) { v4l2_err(&dev->v4l2_dev, "failed to initialize mcu\n"); goto err_free_fw_codec; diff --git a/drivers/staging/media/allegro-dvt/allegro-mail.h b/drivers/staging/media/allegro-dvt/allegro-mail.h index 397622973c193..c095dbfcf104b 100644 --- a/drivers/staging/media/allegro-dvt/allegro-mail.h +++ b/drivers/staging/media/allegro-dvt/allegro-mail.h @@ -20,10 +20,15 @@ enum mcu_msg_type { MCU_MSG_TYPE_PUSH_BUFFER_REFERENCE = 0x000f, }; +enum mcu_msg_version { + MCU_MSG_VERSION_2018_2, +}; + const char *msg_type_name(enum mcu_msg_type type); struct mcu_msg_header { enum mcu_msg_type type; + enum mcu_msg_version version; }; struct mcu_msg_init_request { @@ -40,6 +45,7 @@ struct mcu_msg_init_response { }; struct create_channel_param { + enum mcu_msg_version version; u16 width; u16 height; u32 format; -- GitLab From 42e50290d79475183a728d0c1d07020061f8838b Mon Sep 17 00:00:00 2001 From: Michael Tretter Date: Mon, 13 Jul 2020 16:42:29 +0200 Subject: [PATCH 0653/1476] media: allegro: add support for allegro firmware 2019.2 Encode messages as necessary for the firmware 2019.2 and, thus, support the more recent firmware version in the driver, too. Signed-off-by: Michael Tretter Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- .../staging/media/allegro-dvt/allegro-core.c | 27 ++- .../staging/media/allegro-dvt/allegro-mail.c | 163 +++++++++++++++--- .../staging/media/allegro-dvt/allegro-mail.h | 24 +++ 3 files changed, 186 insertions(+), 28 deletions(-) diff --git a/drivers/staging/media/allegro-dvt/allegro-core.c b/drivers/staging/media/allegro-dvt/allegro-core.c index ada21ebf93bbb..61beae1fca366 100644 --- a/drivers/staging/media/allegro-dvt/allegro-core.c +++ b/drivers/staging/media/allegro-dvt/allegro-core.c @@ -292,6 +292,15 @@ static const struct fw_info supported_firmware[] = { .mailbox_size = 0x400 - 0x8, .mailbox_version = MCU_MSG_VERSION_2018_2, .suballocator_size = SZ_16M, + }, { + .id = 14680, + .id_codec = 126572, + .version = "v2019.2", + .mailbox_cmd = 0x7000, + .mailbox_status = 0x7800, + .mailbox_size = 0x800 - 0x8, + .mailbox_version = MCU_MSG_VERSION_2019_2, + .suballocator_size = SZ_32M, }, }; @@ -930,10 +939,13 @@ static int fill_create_channel_param(struct allegro_channel *channel, param->dbf_ovr_en = 1; param->rdo_cost_mode = 1; + param->custom_lda = 1; param->lf = 1; param->lf_x_tile = 1; param->lf_x_slice = 1; + param->src_bit_depth = 8; + param->beta_offset = -1; param->tc_offset = -1; param->num_slices = 1; @@ -974,13 +986,26 @@ static int fill_create_channel_param(struct allegro_channel *channel, param->golden_ref_frequency = 10; param->rate_control_option = 0x00000000; - param->gop_ctrl_mode = 0x00000000; + param->num_pixel = channel->width + channel->height; + param->max_psnr = 4200; + param->max_pixel_value = 255; + + param->gop_ctrl_mode = 0x00000002; param->freq_idr = channel->gop_size; param->freq_lt = 0; param->gdr_mode = 0x00000000; param->gop_length = channel->gop_size; param->subframe_latency = 0x00000000; + param->lda_factors[0] = 51; + param->lda_factors[1] = 90; + param->lda_factors[2] = 151; + param->lda_factors[3] = 151; + param->lda_factors[4] = 151; + param->lda_factors[5] = 151; + + param->max_num_merge_cand = 5; + return 0; } diff --git a/drivers/staging/media/allegro-dvt/allegro-mail.c b/drivers/staging/media/allegro-dvt/allegro-mail.c index da83aa85c8735..4ac65de124639 100644 --- a/drivers/staging/media/allegro-dvt/allegro-mail.c +++ b/drivers/staging/media/allegro-dvt/allegro-mail.c @@ -44,6 +44,7 @@ static ssize_t allegro_enc_init(u32 *dst, struct mcu_msg_init_request *msg) { unsigned int i = 0; + enum mcu_msg_version version = msg->header.version; dst[i++] = msg->reserved0; dst[i++] = msg->suballoc_dma; @@ -51,33 +52,55 @@ allegro_enc_init(u32 *dst, struct mcu_msg_init_request *msg) dst[i++] = msg->l2_cache[0]; dst[i++] = msg->l2_cache[1]; dst[i++] = msg->l2_cache[2]; + if (version >= MCU_MSG_VERSION_2019_2) { + dst[i++] = -1; + dst[i++] = 0; + } return i * sizeof(*dst); } static inline u32 settings_get_mcu_codec(struct create_channel_param *param) { + enum mcu_msg_version version = param->version; u32 pixelformat = param->codec; - switch (pixelformat) { - case V4L2_PIX_FMT_H264: - default: - return 1; + if (version < MCU_MSG_VERSION_2019_2) { + switch (pixelformat) { + case V4L2_PIX_FMT_H264: + default: + return 1; + } + } else { + switch (pixelformat) { + case V4L2_PIX_FMT_H264: + default: + return 0; + } } } ssize_t allegro_encode_config_blob(u32 *dst, struct create_channel_param *param) { + enum mcu_msg_version version = param->version; unsigned int i = 0; + unsigned int j = 0; u32 val; unsigned int codec = settings_get_mcu_codec(param); + if (version >= MCU_MSG_VERSION_2019_2) + dst[i++] = param->layer_id; dst[i++] = FIELD_PREP(GENMASK(31, 16), param->height) | FIELD_PREP(GENMASK(15, 0), param->width); + if (version >= MCU_MSG_VERSION_2019_2) + dst[i++] = param->videomode; dst[i++] = param->format; - dst[i++] = param->colorspace; + if (version < MCU_MSG_VERSION_2019_2) + dst[i++] = param->colorspace; dst[i++] = param->src_mode; + if (version >= MCU_MSG_VERSION_2019_2) + dst[i++] = param->src_bit_depth; dst[i++] = FIELD_PREP(GENMASK(31, 24), codec) | FIELD_PREP(GENMASK(23, 8), param->constraint_set_flags) | FIELD_PREP(GENMASK(7, 0), param->profile); @@ -94,19 +117,31 @@ allegro_encode_config_blob(u32 *dst, struct create_channel_param *param) val |= param->dbf_ovr_en ? BIT(2) : 0; dst[i++] = val; - val = 0; - val |= param->lf ? BIT(2) : 0; - val |= param->lf_x_tile ? BIT(3) : 0; - val |= param->lf_x_slice ? BIT(4) : 0; - val |= param->rdo_cost_mode ? BIT(20) : 0; - dst[i++] = val; + if (version >= MCU_MSG_VERSION_2019_2) { + val = 0; + val |= param->custom_lda ? BIT(2) : 0; + val |= param->rdo_cost_mode ? BIT(20) : 0; + dst[i++] = val; + + val = 0; + val |= param->lf ? BIT(2) : 0; + val |= param->lf_x_tile ? BIT(3) : 0; + val |= param->lf_x_slice ? BIT(4) : 0; + dst[i++] = val; + } else { + val = 0; + dst[i++] = val; + } dst[i++] = FIELD_PREP(GENMASK(15, 8), param->beta_offset) | FIELD_PREP(GENMASK(7, 0), param->tc_offset); dst[i++] = param->unknown11; dst[i++] = param->unknown12; - dst[i++] = FIELD_PREP(GENMASK(31, 16), param->prefetch_auto) | - FIELD_PREP(GENMASK(15, 0), param->num_slices); + if (version >= MCU_MSG_VERSION_2019_2) + dst[i++] = param->num_slices; + else + dst[i++] = FIELD_PREP(GENMASK(31, 16), param->prefetch_auto) | + FIELD_PREP(GENMASK(15, 0), param->num_slices); dst[i++] = param->prefetch_mem_offset; dst[i++] = param->prefetch_mem_size; dst[i++] = FIELD_PREP(GENMASK(31, 16), param->clip_vrt_range) | @@ -139,19 +174,51 @@ allegro_encode_config_blob(u32 *dst, struct create_channel_param *param) FIELD_PREP(GENMASK(15, 0), param->pb_delta); dst[i++] = FIELD_PREP(GENMASK(31, 16), param->golden_ref_frequency) | FIELD_PREP(GENMASK(15, 0), param->golden_delta); - dst[i++] = param->rate_control_option; + if (version >= MCU_MSG_VERSION_2019_2) + dst[i++] = param->rate_control_option; + else + dst[i++] = 0; + + if (version >= MCU_MSG_VERSION_2019_2) { + dst[i++] = param->num_pixel; + dst[i++] = FIELD_PREP(GENMASK(31, 16), param->max_pixel_value) | + FIELD_PREP(GENMASK(15, 0), param->max_psnr); + for (j = 0; j < 3; j++) + dst[i++] = param->maxpicturesize[j]; + } + + if (version >= MCU_MSG_VERSION_2019_2) + dst[i++] = param->gop_ctrl_mode; + else + dst[i++] = 0; - dst[i++] = param->gop_ctrl_mode; + if (version >= MCU_MSG_VERSION_2019_2) + dst[i++] = FIELD_PREP(GENMASK(31, 24), param->freq_golden_ref) | + FIELD_PREP(GENMASK(23, 16), param->num_b) | + FIELD_PREP(GENMASK(15, 0), param->gop_length); dst[i++] = param->freq_idr; + if (version >= MCU_MSG_VERSION_2019_2) + dst[i++] = param->enable_lt; dst[i++] = param->freq_lt; dst[i++] = param->gdr_mode; - dst[i++] = FIELD_PREP(GENMASK(31, 24), param->freq_golden_ref) | - FIELD_PREP(GENMASK(23, 16), param->num_b) | - FIELD_PREP(GENMASK(15, 0), param->gop_length); + if (version < MCU_MSG_VERSION_2019_2) + dst[i++] = FIELD_PREP(GENMASK(31, 24), param->freq_golden_ref) | + FIELD_PREP(GENMASK(23, 16), param->num_b) | + FIELD_PREP(GENMASK(15, 0), param->gop_length); + + if (version >= MCU_MSG_VERSION_2019_2) + dst[i++] = param->tmpdqp; dst[i++] = param->subframe_latency; dst[i++] = param->lda_control_mode; - dst[i++] = param->unknown41; + if (version < MCU_MSG_VERSION_2019_2) + dst[i++] = param->unknown41; + + if (version >= MCU_MSG_VERSION_2019_2) { + for (j = 0; j < 6; j++) + dst[i++] = param->lda_factors[j]; + dst[i++] = param->max_num_merge_cand; + } return i * sizeof(*dst); } @@ -159,12 +226,20 @@ allegro_encode_config_blob(u32 *dst, struct create_channel_param *param) static ssize_t allegro_enc_create_channel(u32 *dst, struct mcu_msg_create_channel *msg) { + enum mcu_msg_version version = msg->header.version; unsigned int i = 0; dst[i++] = msg->user_id; - memcpy(&dst[i], msg->blob, msg->blob_size); - i += msg->blob_size / sizeof(*dst); + if (version >= MCU_MSG_VERSION_2019_2) { + dst[i++] = msg->blob_mcu_addr; + } else { + memcpy(&dst[i], msg->blob, msg->blob_size); + i += msg->blob_size / sizeof(*dst); + } + + if (version >= MCU_MSG_VERSION_2019_2) + dst[i++] = msg->ep1_addr; return i * sizeof(*dst); } @@ -173,8 +248,15 @@ ssize_t allegro_decode_config_blob(struct create_channel_param *param, struct mcu_msg_create_channel_response *msg, u32 *src) { - param->num_ref_idx_l0 = msg->num_ref_idx_l0; - param->num_ref_idx_l1 = msg->num_ref_idx_l1; + enum mcu_msg_version version = msg->header.version; + + if (version >= MCU_MSG_VERSION_2019_2) { + param->num_ref_idx_l0 = FIELD_GET(GENMASK(7, 4), src[9]); + param->num_ref_idx_l1 = FIELD_GET(GENMASK(11, 8), src[9]); + } else { + param->num_ref_idx_l0 = msg->num_ref_idx_l0; + param->num_ref_idx_l1 = msg->num_ref_idx_l1; + } return 0; } @@ -229,6 +311,7 @@ allegro_enc_put_stream_buffer(u32 *dst, static ssize_t allegro_enc_encode_frame(u32 *dst, struct mcu_msg_encode_frame *msg) { + enum mcu_msg_version version = msg->header.version; unsigned int i = 0; dst[i++] = msg->channel_id; @@ -237,6 +320,14 @@ allegro_enc_encode_frame(u32 *dst, struct mcu_msg_encode_frame *msg) dst[i++] = msg->encoding_options; dst[i++] = FIELD_PREP(GENMASK(31, 16), msg->padding) | FIELD_PREP(GENMASK(15, 0), msg->pps_qp); + + if (version >= MCU_MSG_VERSION_2019_2) { + dst[i++] = 0; + dst[i++] = 0; + dst[i++] = 0; + dst[i++] = 0; + } + dst[i++] = lower_32_bits(msg->user_param); dst[i++] = upper_32_bits(msg->user_param); dst[i++] = lower_32_bits(msg->src_handle); @@ -244,7 +335,11 @@ allegro_enc_encode_frame(u32 *dst, struct mcu_msg_encode_frame *msg) dst[i++] = msg->request_options; dst[i++] = msg->src_y; dst[i++] = msg->src_uv; + if (version >= MCU_MSG_VERSION_2019_2) + dst[i++] = msg->is_10_bit; dst[i++] = msg->stride; + if (version >= MCU_MSG_VERSION_2019_2) + dst[i++] = msg->format; dst[i++] = msg->ep2; dst[i++] = lower_32_bits(msg->ep2_v); dst[i++] = upper_32_bits(msg->ep2_v); @@ -266,14 +361,21 @@ static ssize_t allegro_dec_create_channel(struct mcu_msg_create_channel_response *msg, u32 *src) { + enum mcu_msg_version version = msg->header.version; unsigned int i = 0; msg->channel_id = src[i++]; msg->user_id = src[i++]; - msg->options = src[i++]; - msg->num_core = src[i++]; - msg->num_ref_idx_l0 = FIELD_GET(GENMASK(7, 4), src[i]); - msg->num_ref_idx_l1 = FIELD_GET(GENMASK(11, 8), src[i++]); + /* + * Version >= MCU_MSG_VERSION_2019_2 is handled in + * allegro_decode_config_blob(). + */ + if (version < MCU_MSG_VERSION_2019_2) { + msg->options = src[i++]; + msg->num_core = src[i++]; + msg->num_ref_idx_l0 = FIELD_GET(GENMASK(7, 4), src[i]); + msg->num_ref_idx_l1 = FIELD_GET(GENMASK(11, 8), src[i++]); + } msg->int_buffers_count = src[i++]; msg->int_buffers_size = src[i++]; msg->rec_buffers_count = src[i++]; @@ -298,6 +400,7 @@ allegro_dec_destroy_channel(struct mcu_msg_destroy_channel_response *msg, static ssize_t allegro_dec_encode_frame(struct mcu_msg_encode_frame_response *msg, u32 *src) { + enum mcu_msg_version version = msg->header.version; unsigned int i = 0; unsigned int j; @@ -341,6 +444,12 @@ allegro_dec_encode_frame(struct mcu_msg_encode_frame_response *msg, u32 *src) msg->pps_qp = FIELD_GET(GENMASK(15, 0), src[i++]); msg->reserved2 = src[i++]; + if (version >= MCU_MSG_VERSION_2019_2) { + msg->reserved3 = src[i++]; + msg->reserved4 = src[i++]; + msg->reserved5 = src[i++]; + msg->reserved6 = src[i++]; + } return i * sizeof(*src); } diff --git a/drivers/staging/media/allegro-dvt/allegro-mail.h b/drivers/staging/media/allegro-dvt/allegro-mail.h index c095dbfcf104b..486ecb12b098c 100644 --- a/drivers/staging/media/allegro-dvt/allegro-mail.h +++ b/drivers/staging/media/allegro-dvt/allegro-mail.h @@ -22,6 +22,7 @@ enum mcu_msg_type { enum mcu_msg_version { MCU_MSG_VERSION_2018_2, + MCU_MSG_VERSION_2019_2, }; const char *msg_type_name(enum mcu_msg_type type); @@ -46,11 +47,14 @@ struct mcu_msg_init_response { struct create_channel_param { enum mcu_msg_version version; + u32 layer_id; u16 width; u16 height; + u32 videomode; u32 format; u32 colorspace; u32 src_mode; + u32 src_bit_depth; u8 profile; u16 constraint_set_flags; u32 codec; @@ -59,9 +63,11 @@ struct create_channel_param { u32 log2_max_poc; u32 log2_max_frame_num; u32 temporal_mvp_enable; + u32 enable_reordering; u32 dbf_ovr_en; u32 num_ref_idx_l0; u32 num_ref_idx_l1; + u32 custom_lda; u32 rdo_cost_mode; u32 lf; u32 lf_x_tile; @@ -105,6 +111,10 @@ struct create_channel_param { u16 golden_delta; u16 golden_ref_frequency; u32 rate_control_option; + u32 num_pixel; + u16 max_psnr; + u16 max_pixel_value; + u32 maxpicturesize[3]; /* gop param */ u32 gop_ctrl_mode; @@ -114,10 +124,16 @@ struct create_channel_param { u16 gop_length; u8 num_b; u8 freq_golden_ref; + u32 enable_lt; + u32 tmpdqp; u32 subframe_latency; u32 lda_control_mode; u32 unknown41; + + u32 lda_factors[6]; + + u32 max_num_merge_cand; }; struct mcu_msg_create_channel { @@ -126,6 +142,7 @@ struct mcu_msg_create_channel { u32 *blob; size_t blob_size; u32 blob_mcu_addr; + u32 ep1_addr; }; struct mcu_msg_create_channel_response { @@ -203,9 +220,12 @@ struct mcu_msg_encode_frame { /* u32 scene_change_delay (optional) */ /* rate control param (optional) */ /* gop param (optional) */ + /* dynamic resolution params (optional) */ u32 src_y; u32 src_uv; + u32 is_10_bit; u32 stride; + u32 format; u32 ep2; u64 ep2_v; }; @@ -249,6 +269,10 @@ struct mcu_msg_encode_frame_response { u16 pps_qp; u16 reserved1; u32 reserved2; + u32 reserved3; + u32 reserved4; + u32 reserved5; + u32 reserved6; }; union mcu_msg_response { -- GitLab From c07fe5e020ff51bf4af834932035a5fb9b14efe4 Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Mon, 13 Jul 2020 17:21:45 +0200 Subject: [PATCH 0654/1476] media: cx18: Replace http links with https ones Rationale: Reduces attack surface on kernel devs opening the links for MITM as HTTPS traffic is much harder to manipulate. Deterministic algorithm: For each file: If not .svg: For each line: If doesn't contain `\bxmlns\b`: If neither `\bgnu\.org/license`, nor `\bmozilla\.org/MPL\b`: If both the HTTP and HTTPS versions return 200 OK and serve the same content: Replace HTTP with HTTPS. Signed-off-by: Alexander A. Klimov Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/pci/cx18/cx18-cards.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/pci/cx18/cx18-cards.c b/drivers/media/pci/cx18/cx18-cards.c index ecbe76f1ca632..f5a30959a3678 100644 --- a/drivers/media/pci/cx18/cx18-cards.c +++ b/drivers/media/pci/cx18/cx18-cards.c @@ -35,7 +35,7 @@ static struct cx18_card_tuner_i2c cx18_i2c_nxp = { .tv = { 0x61, 0x60, I2C_CLIENT_END }, }; -/* Please add new PCI IDs to: http://pci-ids.ucw.cz/ +/* Please add new PCI IDs to: https://pci-ids.ucw.cz/ This keeps the PCI ID database up to date. Note that the entries must be added under vendor 0x4444 (Conexant) as subsystem IDs. New vendor IDs should still be added to the vendor ID list. */ -- GitLab From 276e2ee0765941e7fa20973a4c2215df830e8483 Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Mon, 13 Jul 2020 17:31:53 +0200 Subject: [PATCH 0655/1476] media: imon: Replace http links with https ones Rationale: Reduces attack surface on kernel devs opening the links for MITM as HTTPS traffic is much harder to manipulate. Deterministic algorithm: For each file: If not .svg: For each line: If doesn't contain `\bxmlns\b`: If neither `\bgnu\.org/license`, nor `\bmozilla\.org/MPL\b`: If both the HTTP and HTTPS versions return 200 OK and serve the same content: Replace HTTP with HTTPS. Signed-off-by: Alexander A. Klimov Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/rc/imon.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/rc/imon.c b/drivers/media/rc/imon.c index ed95244da894c..a7962ca2ac8e3 100644 --- a/drivers/media/rc/imon.c +++ b/drivers/media/rc/imon.c @@ -795,7 +795,7 @@ static ssize_t show_associate_remote(struct device *d, else strscpy(buf, "closed\n", PAGE_SIZE); - dev_info(d, "Visit http://www.lirc.org/html/imon-24g.html for instructions on how to associate your iMON 2.4G DT/LT remote\n"); + dev_info(d, "Visit https://www.lirc.org/html/imon-24g.html for instructions on how to associate your iMON 2.4G DT/LT remote\n"); mutex_unlock(&ictx->lock); return strlen(buf); } -- GitLab From 1cc94c17670006c4faced28d6111e605d39eb964 Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Mon, 13 Jul 2020 17:42:01 +0200 Subject: [PATCH 0656/1476] media: ivtv: Replace http links with https ones Rationale: Reduces attack surface on kernel devs opening the links for MITM as HTTPS traffic is much harder to manipulate. Deterministic algorithm: For each file: If not .svg: For each line: If doesn't contain `\bxmlns\b`: If neither `\bgnu\.org/license`, nor `\bmozilla\.org/MPL\b`: If both the HTTP and HTTPS versions return 200 OK and serve the same content: Replace HTTP with HTTPS. Signed-off-by: Alexander A. Klimov Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/pci/ivtv/ivtv-cards.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/pci/ivtv/ivtv-cards.c b/drivers/media/pci/ivtv/ivtv-cards.c index ca6daba3a34a1..c8f4ed7ff2c60 100644 --- a/drivers/media/pci/ivtv/ivtv-cards.c +++ b/drivers/media/pci/ivtv/ivtv-cards.c @@ -53,7 +53,7 @@ static struct ivtv_card_tuner_i2c ivtv_i2c_tda8290 = { /********************** card configuration *******************************/ -/* Please add new PCI IDs to: http://pci-ids.ucw.cz/ +/* Please add new PCI IDs to: https://pci-ids.ucw.cz/ This keeps the PCI ID database up to date. Note that the entries must be added under vendor 0x4444 (Conexant) as subsystem IDs. New vendor IDs should still be added to the vendor ID list. */ -- GitLab From e6e059a8884fd4919467d72b2e5d726bd293bbe2 Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Mon, 13 Jul 2020 17:52:10 +0200 Subject: [PATCH 0657/1476] media: rtl2832_sdr: Replace HTTP links with HTTPS ones Rationale: Reduces attack surface on kernel devs opening the links for MITM as HTTPS traffic is much harder to manipulate. Deterministic algorithm: For each file: If not .svg: For each line: If doesn't contain `\bxmlns\b`: If neither `\bgnu\.org/license`, nor `\bmozilla\.org/MPL\b`: If both the HTTP and HTTPS versions return 200 OK and serve the same content: Replace HTTP with HTTPS. Signed-off-by: Alexander A. Klimov Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb-frontends/rtl2832_sdr.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/dvb-frontends/rtl2832_sdr.c b/drivers/media/dvb-frontends/rtl2832_sdr.c index 60d1e59d22921..720756728f2d2 100644 --- a/drivers/media/dvb-frontends/rtl2832_sdr.c +++ b/drivers/media/dvb-frontends/rtl2832_sdr.c @@ -5,7 +5,7 @@ * Copyright (C) 2013 Antti Palosaari * * GNU Radio plugin "gr-kernel" for device usage will be on: - * http://git.linuxtv.org/anttip/gr-kernel.git + * https://git.linuxtv.org/anttip/gr-kernel.git */ #include "rtl2832_sdr.h" -- GitLab From f20c7d9141d34e4a1505ca730743edba629ef7c4 Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Mon, 13 Jul 2020 18:02:19 +0200 Subject: [PATCH 0658/1476] media: tw5864-core: Replace http links with https ones Rationale: Reduces attack surface on kernel devs opening the links for MITM as HTTPS traffic is much harder to manipulate. Deterministic algorithm: For each file: If not .svg: For each line: If doesn't contain `\bxmlns\b`: If neither `\bgnu\.org/license`, nor `\bmozilla\.org/MPL\b`: If both the HTTP and HTTPS versions return 200 OK and serve the same content: Replace HTTP with HTTPS. Signed-off-by: Alexander A. Klimov Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/pci/tw5864/tw5864-core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/pci/tw5864/tw5864-core.c b/drivers/media/pci/tw5864/tw5864-core.c index 66f2a5cabc6ec..282f7dfb7aaff 100644 --- a/drivers/media/pci/tw5864/tw5864-core.c +++ b/drivers/media/pci/tw5864/tw5864-core.c @@ -65,7 +65,7 @@ module_param_array(video_nr, int, NULL, 0444); MODULE_PARM_DESC(video_nr, "video devices numbers array"); /* - * Please add any new PCI IDs to: http://pci-ids.ucw.cz. This keeps + * Please add any new PCI IDs to: https://pci-ids.ucw.cz. This keeps * the PCI ID database up to date. Note that the entries must be * added under vendor 0x1797 (Techwell Inc.) as subsystem IDs. */ -- GitLab From 74cc09fd8d04c56b652cfb332adb61f10bc2c199 Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Mon, 13 Jul 2020 18:12:38 +0200 Subject: [PATCH 0659/1476] media: tw68: Replace http links with https ones Rationale: Reduces attack surface on kernel devs opening the links for MITM as HTTPS traffic is much harder to manipulate. Deterministic algorithm: For each file: If not .svg: For each line: If doesn't contain `\bxmlns\b`: If neither `\bgnu\.org/license`, nor `\bmozilla\.org/MPL\b`: If both the HTTP and HTTPS versions return 200 OK and serve the same content: Replace HTTP with HTTPS. Signed-off-by: Alexander A. Klimov Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/pci/tw68/tw68-core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/pci/tw68/tw68-core.c b/drivers/media/pci/tw68/tw68-core.c index b45f3ffa3de58..30c1ea87f0e87 100644 --- a/drivers/media/pci/tw68/tw68-core.c +++ b/drivers/media/pci/tw68/tw68-core.c @@ -57,7 +57,7 @@ static atomic_t tw68_instance = ATOMIC_INIT(0); /* ------------------------------------------------------------------ */ /* - * Please add any new PCI IDs to: http://pci-ids.ucw.cz. This keeps + * Please add any new PCI IDs to: https://pci-ids.ucw.cz. This keeps * the PCI ID database up to date. Note that the entries must be * added under vendor 0x1797 (Techwell Inc.) as subsystem IDs. */ -- GitLab From 2cc6a9fea031ec8cbd897e5967f6a84df3c9aae3 Mon Sep 17 00:00:00 2001 From: Lukas Bulwahn Date: Tue, 14 Jul 2020 08:30:35 +0200 Subject: [PATCH 0660/1476] media: MAINTAINERS: rectify CHRONTEL CH7322 CEC DRIVER section Commit 7f52faabd2e5 ("media: dt-bindings: Add ch7322 media i2c device") slipped in a typo in the CHRONTEL CH7322 CEC DRIVER section. Hence, ./scripts/get_maintainer.pl --self-test=patterns complains: warning: no file matches F: \ Documentation/devicetree/bindings/media/i2c/chontel,ch7322.yaml Fix the typo to address this warning. Signed-off-by: Lukas Bulwahn Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- MAINTAINERS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MAINTAINERS b/MAINTAINERS index 2730af1265ead..4df1d218c7d69 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -4106,7 +4106,7 @@ M: Jeff Chase L: linux-media@vger.kernel.org S: Maintained T: git git://linuxtv.org/media_tree.git -F: Documentation/devicetree/bindings/media/i2c/chontel,ch7322.yaml +F: Documentation/devicetree/bindings/media/i2c/chrontel,ch7322.yaml F: drivers/media/cec/i2c/ch7322.c CIRRUS LOGIC AUDIO CODEC DRIVERS -- GitLab From aa86e0bbf2ee3fe338798af78f02b5e6088b50c1 Mon Sep 17 00:00:00 2001 From: Dafna Hirschfeld Date: Tue, 14 Jul 2020 14:38:29 +0200 Subject: [PATCH 0661/1476] media: staging: rkisp1: cap: don't set next buffer from rkisp1_vb2_buf_queue The function 'rkisp1_vb2_buf_queue' sets the next buffer directly in case the capture is already streaming but no frame yet arrived from the sensor. This is an optimization that tries to avoid dropping a frame. The call atomic_read(&cap->rkisp1->isp.frame_sequence) is used to check if a frame arrived. Reading the 'frame_sequence' should be avoided outside irq handlers to avoid race conditions. This patch removes this optimization. Dropping of the first frames can be avoided if userspace queues the buffers before start streaming. If userspace starts queueing buffers only after calling 'streamon' he risks frame drops anyway. Signed-off-by: Dafna Hirschfeld Acked-by: Helen Koike Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/rkisp1/rkisp1-capture.c | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/drivers/staging/media/rkisp1/rkisp1-capture.c b/drivers/staging/media/rkisp1/rkisp1-capture.c index 793ec884c8940..572b0949c81f5 100644 --- a/drivers/staging/media/rkisp1/rkisp1-capture.c +++ b/drivers/staging/media/rkisp1/rkisp1-capture.c @@ -743,18 +743,7 @@ static void rkisp1_vb2_buf_queue(struct vb2_buffer *vb) ispbuf->buff_addr[RKISP1_PLANE_CB]); spin_lock_irqsave(&cap->buf.lock, flags); - - /* - * If there's no next buffer assigned, queue this buffer directly - * as the next buffer, and update the memory interface. - */ - if (cap->is_streaming && !cap->buf.next && - atomic_read(&cap->rkisp1->isp.frame_sequence) == -1) { - cap->buf.next = ispbuf; - rkisp1_set_next_buf(cap); - } else { - list_add_tail(&ispbuf->queue, &cap->buf.queue); - } + list_add_tail(&ispbuf->queue, &cap->buf.queue); spin_unlock_irqrestore(&cap->buf.lock, flags); } -- GitLab From 454748e3b3529218a50920e643dab6f149ab2639 Mon Sep 17 00:00:00 2001 From: Dafna Hirschfeld Date: Tue, 14 Jul 2020 14:38:30 +0200 Subject: [PATCH 0662/1476] media: staging: rkisp1: cap: protect buf.curr and buf.next with buf.lock The spinlock buf.lock should protect access to the buffers. This includes the buffers in buf.queue and also buf.curr and buf.next. The function 'rkisp1_set_next_buf' access buf.next therefore it should also be protected with the lock. Signed-off-by: Dafna Hirschfeld Acked-by: Helen Koike Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/rkisp1/rkisp1-capture.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/staging/media/rkisp1/rkisp1-capture.c b/drivers/staging/media/rkisp1/rkisp1-capture.c index 572b0949c81f5..fa3eaeac2a008 100644 --- a/drivers/staging/media/rkisp1/rkisp1-capture.c +++ b/drivers/staging/media/rkisp1/rkisp1-capture.c @@ -617,10 +617,11 @@ static void rkisp1_set_next_buf(struct rkisp1_capture *cap) static void rkisp1_handle_buffer(struct rkisp1_capture *cap) { struct rkisp1_isp *isp = &cap->rkisp1->isp; - struct rkisp1_buffer *curr_buf = cap->buf.curr; + struct rkisp1_buffer *curr_buf; unsigned long flags; spin_lock_irqsave(&cap->buf.lock, flags); + curr_buf = cap->buf.curr; if (curr_buf) { curr_buf->vb.sequence = atomic_read(&isp->frame_sequence); @@ -640,9 +641,9 @@ static void rkisp1_handle_buffer(struct rkisp1_capture *cap) queue); list_del(&cap->buf.next->queue); } - spin_unlock_irqrestore(&cap->buf.lock, flags); rkisp1_set_next_buf(cap); + spin_unlock_irqrestore(&cap->buf.lock, flags); } void rkisp1_capture_isr(struct rkisp1_device *rkisp1) -- GitLab From 23780e14fec91242bb621670c61afa0898407892 Mon Sep 17 00:00:00 2001 From: Dafna Hirschfeld Date: Tue, 14 Jul 2020 14:38:31 +0200 Subject: [PATCH 0663/1476] media: staging: rkisp1: cap: move code that manages the buffers to rkisp1_set_next_buf The function 'rkisp1_set_next_buf' configures the registers according to 'cap->buf.next'. It is called after updating 'cap->buf.next' and 'cap->buf.curr'. This patch moves the code that updates those fields to rkisp1_set_next_buf. This is a preparation for later patch that change a call to 'rkisp1_handle_buffer' with a call to 'rkisp1_set_next_buf'. Signed-off-by: Dafna Hirschfeld Acked-by: Helen Koike Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/rkisp1/rkisp1-capture.c | 30 +++++++++---------- 1 file changed, 14 insertions(+), 16 deletions(-) diff --git a/drivers/staging/media/rkisp1/rkisp1-capture.c b/drivers/staging/media/rkisp1/rkisp1-capture.c index fa3eaeac2a008..7f400aefe5505 100644 --- a/drivers/staging/media/rkisp1/rkisp1-capture.c +++ b/drivers/staging/media/rkisp1/rkisp1-capture.c @@ -575,12 +575,16 @@ static void rkisp1_dummy_buf_destroy(struct rkisp1_capture *cap) static void rkisp1_set_next_buf(struct rkisp1_capture *cap) { - /* - * Use the dummy space allocated by dma_alloc_coherent to - * throw data if there is no available buffer. - */ - if (cap->buf.next) { - u32 *buff_addr = cap->buf.next->buff_addr; + cap->buf.curr = cap->buf.next; + cap->buf.next = NULL; + + if (!list_empty(&cap->buf.queue)) { + u32 *buff_addr; + + cap->buf.next = list_first_entry(&cap->buf.queue, struct rkisp1_buffer, queue); + list_del(&cap->buf.next->queue); + + buff_addr = cap->buf.next->buff_addr; rkisp1_write(cap->rkisp1, buff_addr[RKISP1_PLANE_Y], @@ -592,6 +596,10 @@ static void rkisp1_set_next_buf(struct rkisp1_capture *cap) buff_addr[RKISP1_PLANE_CR], cap->config->mi.cr_base_ad_init); } else { + /* + * Use the dummy space allocated by dma_alloc_coherent to + * throw data if there is no available buffer. + */ rkisp1_write(cap->rkisp1, cap->buf.dummy.dma_addr, cap->config->mi.y_base_ad_init); @@ -632,16 +640,6 @@ static void rkisp1_handle_buffer(struct rkisp1_capture *cap) cap->rkisp1->debug.frame_drop[cap->id]++; } - cap->buf.curr = cap->buf.next; - cap->buf.next = NULL; - - if (!list_empty(&cap->buf.queue)) { - cap->buf.next = list_first_entry(&cap->buf.queue, - struct rkisp1_buffer, - queue); - list_del(&cap->buf.next->queue); - } - rkisp1_set_next_buf(cap); spin_unlock_irqrestore(&cap->buf.lock, flags); } -- GitLab From 20698ed90f018607e7a24c5258b7ed2dffafa7ae Mon Sep 17 00:00:00 2001 From: Dafna Hirschfeld Date: Tue, 14 Jul 2020 14:38:32 +0200 Subject: [PATCH 0664/1476] media: staging: rkisp1: cap: in stream start, replace calls to rkisp1_handle_buffer with rkisp1_set_next_buf The function 'rkisp1_stream_start' calls 'rkisp1_handle_buffer' in order to update the 'buf.curr' and 'buf.next' fields and configure the device before streaming starts. This cause a wrong increment of the debugs field 'frame_drop'. This patch replaces the call to 'rkisp1_handle_buffer' with a call to 'rkisp1_set_next_buf'. Signed-off-by: Dafna Hirschfeld Acked-by: Helen Koike Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/rkisp1/rkisp1-capture.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/staging/media/rkisp1/rkisp1-capture.c b/drivers/staging/media/rkisp1/rkisp1-capture.c index 7f400aefe5505..c05280950ea0f 100644 --- a/drivers/staging/media/rkisp1/rkisp1-capture.c +++ b/drivers/staging/media/rkisp1/rkisp1-capture.c @@ -916,7 +916,7 @@ static void rkisp1_stream_start(struct rkisp1_capture *cap) cap->ops->config(cap); /* Setup a buffer for the next frame */ - rkisp1_handle_buffer(cap); + rkisp1_set_next_buf(cap); cap->ops->enable(cap); /* It's safe to config ACTIVE and SHADOW regs for the * first stream. While when the second is starting, do NOT @@ -931,7 +931,7 @@ static void rkisp1_stream_start(struct rkisp1_capture *cap) /* force cfg update */ rkisp1_write(rkisp1, RKISP1_CIF_MI_INIT_SOFT_UPD, RKISP1_CIF_MI_INIT); - rkisp1_handle_buffer(cap); + rkisp1_set_next_buf(cap); } cap->is_streaming = true; } -- GitLab From 8f611cc154965afdc1d8104e1d7b1b3df4bee7d2 Mon Sep 17 00:00:00 2001 From: Dafna Hirschfeld Date: Mon, 29 Jun 2020 08:57:51 +0200 Subject: [PATCH 0665/1476] media: staging: rkisp1: remove unused field ctrl_handler from struct rkisp1_device The field ctrl_handler in struct rkisp1_device is not used. This patch removes it. Signed-off-by: Dafna Hirschfeld Acked-by: Helen Koike Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/rkisp1/rkisp1-common.h | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/staging/media/rkisp1/rkisp1-common.h b/drivers/staging/media/rkisp1/rkisp1-common.h index 2603a5aa5210a..3f06218146960 100644 --- a/drivers/staging/media/rkisp1/rkisp1-common.h +++ b/drivers/staging/media/rkisp1/rkisp1-common.h @@ -255,7 +255,6 @@ struct rkisp1_device { unsigned int clk_size; struct clk_bulk_data clks[RKISP1_MAX_BUS_CLK]; struct v4l2_device v4l2_dev; - struct v4l2_ctrl_handler ctrl_handler; struct media_device media_dev; struct v4l2_async_notifier notifier; struct rkisp1_sensor_async *active_sensor; -- GitLab From 1b265b4d2c216ec3780125eebacb1035c7a5ed7a Mon Sep 17 00:00:00 2001 From: Dafna Hirschfeld Date: Mon, 29 Jun 2020 08:57:52 +0200 Subject: [PATCH 0666/1476] media: staging: rkisp1: remove unused field alloc_ctx from struct rkisp1_device The field alloc_ctx in struct rkisp1_device is not used. This patch removes it. Signed-off-by: Dafna Hirschfeld Acked-by: Helen Koike Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/rkisp1/rkisp1-common.h | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/staging/media/rkisp1/rkisp1-common.h b/drivers/staging/media/rkisp1/rkisp1-common.h index 3f06218146960..2af8ffa0de52f 100644 --- a/drivers/staging/media/rkisp1/rkisp1-common.h +++ b/drivers/staging/media/rkisp1/rkisp1-common.h @@ -264,7 +264,6 @@ struct rkisp1_device { struct rkisp1_stats stats; struct rkisp1_params params; struct media_pipeline pipe; - struct vb2_alloc_ctx *alloc_ctx; struct mutex stream_lock; struct rkisp1_debug debug; }; -- GitLab From 8d4a696af361af57bb84f3cc063473d241afc638 Mon Sep 17 00:00:00 2001 From: Dafna Hirschfeld Date: Mon, 29 Jun 2020 08:57:53 +0200 Subject: [PATCH 0667/1476] media: staging: rkisp1: set pads array of the resizer to size 2 Currently the 'pads' and 'pad_cfg' arrays of the rkisp1_resizer are of size 'RKISP1_ISP_PAD_MAX' which is 4. But the resizer has only two pads. Change the size of the arrays to 2 by adding and using 'RKISP1_RSZ_PAD_MAX' similar to the way it is done in the isp entity. [hverkuil: fix typos in commit log] Signed-off-by: Dafna Hirschfeld Acked-by: Helen Koike Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/rkisp1/rkisp1-common.h | 5 +++-- drivers/staging/media/rkisp1/rkisp1-resizer.c | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/staging/media/rkisp1/rkisp1-common.h b/drivers/staging/media/rkisp1/rkisp1-common.h index 2af8ffa0de52f..3dc51d703f730 100644 --- a/drivers/staging/media/rkisp1/rkisp1-common.h +++ b/drivers/staging/media/rkisp1/rkisp1-common.h @@ -52,6 +52,7 @@ enum rkisp1_rsz_pad { RKISP1_RSZ_PAD_SINK, RKISP1_RSZ_PAD_SRC, + RKISP1_RSZ_PAD_MAX }; enum rkisp1_stream_id { @@ -219,8 +220,8 @@ struct rkisp1_resizer { struct v4l2_subdev sd; enum rkisp1_stream_id id; struct rkisp1_device *rkisp1; - struct media_pad pads[RKISP1_ISP_PAD_MAX]; - struct v4l2_subdev_pad_config pad_cfg[RKISP1_ISP_PAD_MAX]; + struct media_pad pads[RKISP1_RSZ_PAD_MAX]; + struct v4l2_subdev_pad_config pad_cfg[RKISP1_RSZ_PAD_MAX]; const struct rkisp1_rsz_config *config; enum v4l2_pixel_encoding pixel_enc; struct mutex ops_lock; diff --git a/drivers/staging/media/rkisp1/rkisp1-resizer.c b/drivers/staging/media/rkisp1/rkisp1-resizer.c index 31aea7dfc9766..c66d2a52fd71e 100644 --- a/drivers/staging/media/rkisp1/rkisp1-resizer.c +++ b/drivers/staging/media/rkisp1/rkisp1-resizer.c @@ -735,7 +735,7 @@ static int rkisp1_rsz_register(struct rkisp1_resizer *rsz) rsz->pixel_enc = RKISP1_DEF_PIXEL_ENC; mutex_init(&rsz->ops_lock); - ret = media_entity_pads_init(&sd->entity, 2, pads); + ret = media_entity_pads_init(&sd->entity, RKISP1_RSZ_PAD_MAX, pads); if (ret) return ret; -- GitLab From 8e5652ae8d4addb99db9ccc0112827dfe3026276 Mon Sep 17 00:00:00 2001 From: Andrey Konovalov Date: Tue, 23 Jun 2020 18:52:26 +0200 Subject: [PATCH 0668/1476] media: i2c: imx290: replace msleep(10) with usleep_range(10000, 11000) This fixes checkpatch warnings of "msleep < 20ms can sleep for up to 20ms". Signed-off-by: Andrey Konovalov Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/imx290.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/media/i2c/imx290.c b/drivers/media/i2c/imx290.c index 9c97830164e97..adcddf3204f75 100644 --- a/drivers/media/i2c/imx290.c +++ b/drivers/media/i2c/imx290.c @@ -406,7 +406,7 @@ static int imx290_set_register_array(struct imx290 *imx290, } /* Provide 10ms settle time */ - msleep(10); + usleep_range(10000, 11000); return 0; } @@ -484,14 +484,14 @@ static int imx290_set_ctrl(struct v4l2_ctrl *ctrl) if (ctrl->val) { imx290_write_reg(imx290, IMX290_BLKLEVEL_LOW, 0x00); imx290_write_reg(imx290, IMX290_BLKLEVEL_HIGH, 0x00); - msleep(10); + usleep_range(10000, 11000); imx290_write_reg(imx290, IMX290_PGCTRL, (u8)(IMX290_PGCTRL_REGEN | IMX290_PGCTRL_THRU | IMX290_PGCTRL_MODE(ctrl->val))); } else { imx290_write_reg(imx290, IMX290_PGCTRL, 0x00); - msleep(10); + usleep_range(10000, 11000); if (imx290->bpp == 10) imx290_write_reg(imx290, IMX290_BLKLEVEL_LOW, 0x3c); -- GitLab From 3fa97ae054d3862c52b0a296b1cb36947cc9ab1d Mon Sep 17 00:00:00 2001 From: Dongchun Zhu Date: Tue, 30 Jun 2020 08:22:10 +0200 Subject: [PATCH 0669/1476] media: dt-bindings: media: i2c: Document DW9768 bindings Add DeviceTree Binding Documentation for Dongwoon Anatech DW9768 voice coil actuator. Reviewed-by: Rob Herring Signed-off-by: Dongchun Zhu Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- .../bindings/media/i2c/dongwoon,dw9768.yaml | 100 ++++++++++++++++++ MAINTAINERS | 7 ++ 2 files changed, 107 insertions(+) create mode 100644 Documentation/devicetree/bindings/media/i2c/dongwoon,dw9768.yaml diff --git a/Documentation/devicetree/bindings/media/i2c/dongwoon,dw9768.yaml b/Documentation/devicetree/bindings/media/i2c/dongwoon,dw9768.yaml new file mode 100644 index 0000000000000..cb96e95d7e81c --- /dev/null +++ b/Documentation/devicetree/bindings/media/i2c/dongwoon,dw9768.yaml @@ -0,0 +1,100 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +# Copyright (c) 2020 MediaTek Inc. +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/media/i2c/dongwoon,dw9768.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Dongwoon Anatech DW9768 Voice Coil Motor (VCM) Lens Device Tree Bindings + +maintainers: + - Dongchun Zhu + +description: |- + The Dongwoon DW9768 is a single 10-bit digital-to-analog (DAC) converter + with 100 mA output current sink capability. VCM current is controlled with + a linear mode driver. The DAC is controlled via a 2-wire (I2C-compatible) + serial interface that operates at clock rates up to 1MHz. This chip + integrates Advanced Actuator Control (AAC) technology and is intended for + driving voice coil lenses in camera modules. + +properties: + compatible: + enum: + - dongwoon,dw9768 # for DW9768 VCM + - giantec,gt9769 # for GT9769 VCM + + reg: + maxItems: 1 + + vin-supply: + description: + Definition of the regulator used as Digital I/O voltage supply. + + vdd-supply: + description: + Definition of the regulator used as Digital core voltage supply. + + dongwoon,aac-mode: + description: + Indication of AAC mode select. + allOf: + - $ref: "/schemas/types.yaml#/definitions/uint32" + - enum: + - 1 # AAC2 mode(operation time# 0.48 x Tvib) + - 2 # AAC3 mode(operation time# 0.70 x Tvib) + - 3 # AAC4 mode(operation time# 0.75 x Tvib) + - 5 # AAC8 mode(operation time# 1.13 x Tvib) + default: 2 + + dongwoon,aac-timing: + description: + Number of AAC Timing count that controlled by one 6-bit period of + vibration register AACT[5:0], the unit of which is 100 us. + allOf: + - $ref: "/schemas/types.yaml#/definitions/uint32" + - default: 0x20 + minimum: 0x00 + maximum: 0x3f + + dongwoon,clock-presc: + description: + Indication of VCM internal clock dividing rate select, as one multiple + factor to calculate VCM ring periodic time Tvib. + allOf: + - $ref: "/schemas/types.yaml#/definitions/uint32" + - enum: + - 0 # Dividing Rate - 2 + - 1 # Dividing Rate - 1 + - 2 # Dividing Rate - 1/2 + - 3 # Dividing Rate - 1/4 + - 4 # Dividing Rate - 8 + - 5 # Dividing Rate - 4 + default: 1 + +required: + - compatible + - reg + - vin-supply + - vdd-supply + +additionalProperties: false + +examples: + - | + + i2c { + #address-cells = <1>; + #size-cells = <0>; + + dw9768: camera-lens@c { + compatible = "dongwoon,dw9768"; + reg = <0x0c>; + + vin-supply = <&mt6358_vcamio_reg>; + vdd-supply = <&mt6358_vcama2_reg>; + dongwoon,aac-timing = <0x39>; + }; + }; + +... diff --git a/MAINTAINERS b/MAINTAINERS index 4df1d218c7d69..562069ba2c62c 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -5223,6 +5223,13 @@ T: git git://linuxtv.org/media_tree.git F: Documentation/devicetree/bindings/media/i2c/dongwoon,dw9714.txt F: drivers/media/i2c/dw9714.c +DONGWOON DW9768 LENS VOICE COIL DRIVER +M: Dongchun Zhu +L: linux-media@vger.kernel.org +S: Maintained +T: git git://linuxtv.org/media_tree.git +F: Documentation/devicetree/bindings/media/i2c/dongwoon,dw9768.yaml + DONGWOON DW9807 LENS VOICE COIL DRIVER M: Sakari Ailus L: linux-media@vger.kernel.org -- GitLab From 859891228e56b991f65acc349bbe118b44752beb Mon Sep 17 00:00:00 2001 From: Dongchun Zhu Date: Tue, 30 Jun 2020 08:22:11 +0200 Subject: [PATCH 0670/1476] media: i2c: dw9768: Add DW9768 VCM driver Add a V4L2 sub-device driver for DW9768 voice coil motor, providing control to set the desired focus via IIC serial interface. Signed-off-by: Dongchun Zhu Reviewed-by: Tomasz Figa Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- MAINTAINERS | 1 + drivers/media/i2c/Kconfig | 12 + drivers/media/i2c/Makefile | 1 + drivers/media/i2c/dw9768.c | 554 +++++++++++++++++++++++++++++++++++++ 4 files changed, 568 insertions(+) create mode 100644 drivers/media/i2c/dw9768.c diff --git a/MAINTAINERS b/MAINTAINERS index 562069ba2c62c..dc28f18c62bdf 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -5229,6 +5229,7 @@ L: linux-media@vger.kernel.org S: Maintained T: git git://linuxtv.org/media_tree.git F: Documentation/devicetree/bindings/media/i2c/dongwoon,dw9768.yaml +F: drivers/media/i2c/dw9768.c DONGWOON DW9807 LENS VOICE COIL DRIVER M: Sakari Ailus diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig index 6804ad57ee719..d2d86ced319d7 100644 --- a/drivers/media/i2c/Kconfig +++ b/drivers/media/i2c/Kconfig @@ -1254,6 +1254,18 @@ config VIDEO_DW9714 capability. This is designed for linear control of voice coil motors, controlled via I2C serial interface. +config VIDEO_DW9768 + tristate "DW9768 lens voice coil support" + depends on I2C && VIDEO_V4L2 + select MEDIA_CONTROLLER + select VIDEO_V4L2_SUBDEV_API + select V4L2_FWNODE + help + This is a driver for the DW9768 camera lens voice coil. + DW9768 is a 10 bit DAC with 100mA output current sink + capability. This is designed for linear control of + voice coil motors, controlled via I2C serial interface. + config VIDEO_DW9807_VCM tristate "DW9807 lens voice coil support" depends on I2C && VIDEO_V4L2 diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile index 993acab81b2c3..33ad435bffea3 100644 --- a/drivers/media/i2c/Makefile +++ b/drivers/media/i2c/Makefile @@ -24,6 +24,7 @@ obj-$(CONFIG_VIDEO_SAA6752HS) += saa6752hs.o obj-$(CONFIG_VIDEO_AD5820) += ad5820.o obj-$(CONFIG_VIDEO_AK7375) += ak7375.o obj-$(CONFIG_VIDEO_DW9714) += dw9714.o +obj-$(CONFIG_VIDEO_DW9768) += dw9768.o obj-$(CONFIG_VIDEO_DW9807_VCM) += dw9807-vcm.o obj-$(CONFIG_VIDEO_ADV7170) += adv7170.o obj-$(CONFIG_VIDEO_ADV7175) += adv7175.o diff --git a/drivers/media/i2c/dw9768.c b/drivers/media/i2c/dw9768.c new file mode 100644 index 0000000000000..45cdd924b565d --- /dev/null +++ b/drivers/media/i2c/dw9768.c @@ -0,0 +1,554 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (c) 2020 MediaTek Inc. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DW9768_NAME "dw9768" +#define DW9768_MAX_FOCUS_POS (1024 - 1) +/* + * This sets the minimum granularity for the focus positions. + * A value of 1 gives maximum accuracy for a desired focus position + */ +#define DW9768_FOCUS_STEPS 1 + +/* + * Ring control and Power control register + * Bit[1] RING_EN + * 0: Direct mode + * 1: AAC mode (ringing control mode) + * Bit[0] PD + * 0: Normal operation mode + * 1: Power down mode + * DW9768 requires waiting time of Topr after PD reset takes place. + */ +#define DW9768_RING_PD_CONTROL_REG 0x02 +#define DW9768_PD_MODE_OFF 0x00 +#define DW9768_PD_MODE_EN BIT(0) +#define DW9768_AAC_MODE_EN BIT(1) + +/* + * DW9768 separates two registers to control the VCM position. + * One for MSB value, another is LSB value. + * DAC_MSB: D[9:8] (ADD: 0x03) + * DAC_LSB: D[7:0] (ADD: 0x04) + * D[9:0] DAC data input: positive output current = D[9:0] / 1023 * 100[mA] + */ +#define DW9768_MSB_ADDR 0x03 +#define DW9768_LSB_ADDR 0x04 +#define DW9768_STATUS_ADDR 0x05 + +/* + * AAC mode control & prescale register + * Bit[7:5] Namely AC[2:0], decide the VCM mode and operation time. + * 001 AAC2 0.48 x Tvib + * 010 AAC3 0.70 x Tvib + * 011 AAC4 0.75 x Tvib + * 101 AAC8 1.13 x Tvib + * Bit[2:0] Namely PRESC[2:0], set the internal clock dividing rate as follow. + * 000 2 + * 001 1 + * 010 1/2 + * 011 1/4 + * 100 8 + * 101 4 + */ +#define DW9768_AAC_PRESC_REG 0x06 +#define DW9768_AAC_MODE_SEL_MASK GENMASK(7, 5) +#define DW9768_CLOCK_PRE_SCALE_SEL_MASK GENMASK(2, 0) + +/* + * VCM period of vibration register + * Bit[5:0] Defined as VCM rising periodic time (Tvib) together with PRESC[2:0] + * Tvib = (6.3ms + AACT[5:0] * 0.1ms) * Dividing Rate + * Dividing Rate is the internal clock dividing rate that is defined at + * PRESCALE register (ADD: 0x06) + */ +#define DW9768_AAC_TIME_REG 0x07 + +/* + * DW9768 requires waiting time (delay time) of t_OPR after power-up, + * or in the case of PD reset taking place. + */ +#define DW9768_T_OPR_US 1000 +#define DW9768_TVIB_MS_BASE10 (64 - 1) +#define DW9768_AAC_MODE_DEFAULT 2 +#define DW9768_AAC_TIME_DEFAULT 0x20 +#define DW9768_CLOCK_PRE_SCALE_DEFAULT 1 + +/* + * This acts as the minimum granularity of lens movement. + * Keep this value power of 2, so the control steps can be + * uniformly adjusted for gradual lens movement, with desired + * number of control steps. + */ +#define DW9768_MOVE_STEPS 16 + +static const char * const dw9768_supply_names[] = { + "vin", /* Digital I/O power */ + "vdd", /* Digital core power */ +}; + +/* dw9768 device structure */ +struct dw9768 { + struct regulator_bulk_data supplies[ARRAY_SIZE(dw9768_supply_names)]; + struct v4l2_ctrl_handler ctrls; + struct v4l2_ctrl *focus; + struct v4l2_subdev sd; + + u32 aac_mode; + u32 aac_timing; + u32 clock_presc; + u32 move_delay_us; +}; + +static inline struct dw9768 *sd_to_dw9768(struct v4l2_subdev *subdev) +{ + return container_of(subdev, struct dw9768, sd); +} + +struct regval_list { + u8 reg_num; + u8 value; +}; + +struct dw9768_aac_mode_ot_multi { + u32 aac_mode_enum; + u32 ot_multi_base100; +}; + +struct dw9768_clk_presc_dividing_rate { + u32 clk_presc_enum; + u32 dividing_rate_base100; +}; + +static const struct dw9768_aac_mode_ot_multi aac_mode_ot_multi[] = { + {1, 48}, + {2, 70}, + {3, 75}, + {5, 113}, +}; + +static const struct dw9768_clk_presc_dividing_rate presc_dividing_rate[] = { + {0, 200}, + {1, 100}, + {2, 50}, + {3, 25}, + {4, 800}, + {5, 400}, +}; + +static u32 dw9768_find_ot_multi(u32 aac_mode_param) +{ + u32 cur_ot_multi_base100 = 70; + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(aac_mode_ot_multi); i++) { + if (aac_mode_ot_multi[i].aac_mode_enum == aac_mode_param) { + cur_ot_multi_base100 = + aac_mode_ot_multi[i].ot_multi_base100; + } + } + + return cur_ot_multi_base100; +} + +static u32 dw9768_find_dividing_rate(u32 presc_param) +{ + u32 cur_clk_dividing_rate_base100 = 100; + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(presc_dividing_rate); i++) { + if (presc_dividing_rate[i].clk_presc_enum == presc_param) { + cur_clk_dividing_rate_base100 = + presc_dividing_rate[i].dividing_rate_base100; + } + } + + return cur_clk_dividing_rate_base100; +} + +/* + * DW9768_AAC_PRESC_REG & DW9768_AAC_TIME_REG determine VCM operation time. + * For current VCM mode: AAC3, Operation Time would be 0.70 x Tvib. + * Tvib = (6.3ms + AACT[5:0] * 0.1MS) * Dividing Rate. + * Below is calculation of the operation delay for each step. + */ +static inline u32 dw9768_cal_move_delay(u32 aac_mode_param, u32 presc_param, + u32 aac_timing_param) +{ + u32 Tvib_us; + u32 ot_multi_base100; + u32 clk_dividing_rate_base100; + + ot_multi_base100 = dw9768_find_ot_multi(aac_mode_param); + + clk_dividing_rate_base100 = dw9768_find_dividing_rate(presc_param); + + Tvib_us = (DW9768_TVIB_MS_BASE10 + aac_timing_param) * + clk_dividing_rate_base100; + + return Tvib_us * ot_multi_base100 / 100; +} + +static int dw9768_mod_reg(struct dw9768 *dw9768, u8 reg, u8 mask, u8 val) +{ + struct i2c_client *client = v4l2_get_subdevdata(&dw9768->sd); + int ret; + + ret = i2c_smbus_read_byte_data(client, reg); + if (ret < 0) + return ret; + + val = ((unsigned char)ret & ~mask) | (val & mask); + + return i2c_smbus_write_byte_data(client, reg, val); +} + +static int dw9768_set_dac(struct dw9768 *dw9768, u16 val) +{ + struct i2c_client *client = v4l2_get_subdevdata(&dw9768->sd); + + /* Write VCM position to registers */ + return i2c_smbus_write_word_swapped(client, DW9768_MSB_ADDR, val); +} + +static int dw9768_init(struct dw9768 *dw9768) +{ + struct i2c_client *client = v4l2_get_subdevdata(&dw9768->sd); + int ret, val; + + /* Reset DW9768_RING_PD_CONTROL_REG to default status 0x00 */ + ret = i2c_smbus_write_byte_data(client, DW9768_RING_PD_CONTROL_REG, + DW9768_PD_MODE_OFF); + if (ret < 0) + return ret; + + /* + * DW9769 requires waiting delay time of t_OPR + * after PD reset takes place. + */ + usleep_range(DW9768_T_OPR_US, DW9768_T_OPR_US + 100); + + /* Set DW9768_RING_PD_CONTROL_REG to DW9768_AAC_MODE_EN(0x01) */ + ret = i2c_smbus_write_byte_data(client, DW9768_RING_PD_CONTROL_REG, + DW9768_AAC_MODE_EN); + if (ret < 0) + return ret; + + /* Set AAC mode */ + ret = dw9768_mod_reg(dw9768, DW9768_AAC_PRESC_REG, + DW9768_AAC_MODE_SEL_MASK, + dw9768->aac_mode << 5); + if (ret < 0) + return ret; + + /* Set clock presc */ + if (dw9768->clock_presc != DW9768_CLOCK_PRE_SCALE_DEFAULT) { + ret = dw9768_mod_reg(dw9768, DW9768_AAC_PRESC_REG, + DW9768_CLOCK_PRE_SCALE_SEL_MASK, + dw9768->clock_presc); + if (ret < 0) + return ret; + } + + /* Set AAC Timing */ + if (dw9768->aac_timing != DW9768_AAC_TIME_DEFAULT) { + ret = i2c_smbus_write_byte_data(client, DW9768_AAC_TIME_REG, + dw9768->aac_timing); + if (ret < 0) + return ret; + } + + for (val = dw9768->focus->val % DW9768_MOVE_STEPS; + val <= dw9768->focus->val; + val += DW9768_MOVE_STEPS) { + ret = dw9768_set_dac(dw9768, val); + if (ret) { + dev_err(&client->dev, "I2C failure: %d", ret); + return ret; + } + usleep_range(dw9768->move_delay_us, + dw9768->move_delay_us + 1000); + } + + return 0; +} + +static int dw9768_release(struct dw9768 *dw9768) +{ + struct i2c_client *client = v4l2_get_subdevdata(&dw9768->sd); + int ret, val; + + val = round_down(dw9768->focus->val, DW9768_MOVE_STEPS); + for ( ; val >= 0; val -= DW9768_MOVE_STEPS) { + ret = dw9768_set_dac(dw9768, val); + if (ret) { + dev_err(&client->dev, "I2C write fail: %d", ret); + return ret; + } + usleep_range(dw9768->move_delay_us, + dw9768->move_delay_us + 1000); + } + + ret = i2c_smbus_write_byte_data(client, DW9768_RING_PD_CONTROL_REG, + DW9768_PD_MODE_EN); + if (ret < 0) + return ret; + + /* + * DW9769 requires waiting delay time of t_OPR + * after PD reset takes place. + */ + usleep_range(DW9768_T_OPR_US, DW9768_T_OPR_US + 100); + + return 0; +} + +static int dw9768_runtime_suspend(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct dw9768 *dw9768 = sd_to_dw9768(sd); + + dw9768_release(dw9768); + regulator_bulk_disable(ARRAY_SIZE(dw9768_supply_names), + dw9768->supplies); + + return 0; +} + +static int dw9768_runtime_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct dw9768 *dw9768 = sd_to_dw9768(sd); + int ret; + + ret = regulator_bulk_enable(ARRAY_SIZE(dw9768_supply_names), + dw9768->supplies); + if (ret < 0) { + dev_err(dev, "failed to enable regulators\n"); + return ret; + } + + /* + * The datasheet refers to t_OPR that needs to be waited before sending + * I2C commands after power-up. + */ + usleep_range(DW9768_T_OPR_US, DW9768_T_OPR_US + 100); + + ret = dw9768_init(dw9768); + if (ret < 0) + goto disable_regulator; + + return 0; + +disable_regulator: + regulator_bulk_disable(ARRAY_SIZE(dw9768_supply_names), + dw9768->supplies); + + return ret; +} + +static int dw9768_set_ctrl(struct v4l2_ctrl *ctrl) +{ + struct dw9768 *dw9768 = container_of(ctrl->handler, + struct dw9768, ctrls); + + if (ctrl->id == V4L2_CID_FOCUS_ABSOLUTE) + return dw9768_set_dac(dw9768, ctrl->val); + + return 0; +} + +static const struct v4l2_ctrl_ops dw9768_ctrl_ops = { + .s_ctrl = dw9768_set_ctrl, +}; + +static int dw9768_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) +{ + int ret; + + ret = pm_runtime_get_sync(sd->dev); + if (ret < 0) { + pm_runtime_put_noidle(sd->dev); + return ret; + } + + return 0; +} + +static int dw9768_close(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) +{ + pm_runtime_put(sd->dev); + + return 0; +} + +static const struct v4l2_subdev_internal_ops dw9768_int_ops = { + .open = dw9768_open, + .close = dw9768_close, +}; + +static const struct v4l2_subdev_ops dw9768_ops = { }; + +static int dw9768_init_controls(struct dw9768 *dw9768) +{ + struct v4l2_ctrl_handler *hdl = &dw9768->ctrls; + const struct v4l2_ctrl_ops *ops = &dw9768_ctrl_ops; + + v4l2_ctrl_handler_init(hdl, 1); + + dw9768->focus = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_FOCUS_ABSOLUTE, 0, + DW9768_MAX_FOCUS_POS, + DW9768_FOCUS_STEPS, 0); + + if (hdl->error) + return hdl->error; + + dw9768->sd.ctrl_handler = hdl; + + return 0; +} + +static int dw9768_probe(struct i2c_client *client) +{ + struct device *dev = &client->dev; + struct dw9768 *dw9768; + unsigned int i; + int ret; + + dw9768 = devm_kzalloc(dev, sizeof(*dw9768), GFP_KERNEL); + if (!dw9768) + return -ENOMEM; + + /* Initialize subdev */ + v4l2_i2c_subdev_init(&dw9768->sd, client, &dw9768_ops); + + dw9768->aac_mode = DW9768_AAC_MODE_DEFAULT; + dw9768->aac_timing = DW9768_AAC_TIME_DEFAULT; + dw9768->clock_presc = DW9768_CLOCK_PRE_SCALE_DEFAULT; + + /* Optional indication of AAC mode select */ + fwnode_property_read_u32(dev_fwnode(dev), "dongwoon,aac-mode", + &dw9768->aac_mode); + + /* Optional indication of clock pre-scale select */ + fwnode_property_read_u32(dev_fwnode(dev), "dongwoon,clock-presc", + &dw9768->clock_presc); + + /* Optional indication of AAC Timing */ + fwnode_property_read_u32(dev_fwnode(dev), "dongwoon,aac-timing", + &dw9768->aac_timing); + + dw9768->move_delay_us = dw9768_cal_move_delay(dw9768->aac_mode, + dw9768->clock_presc, + dw9768->aac_timing); + + for (i = 0; i < ARRAY_SIZE(dw9768_supply_names); i++) + dw9768->supplies[i].supply = dw9768_supply_names[i]; + + ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(dw9768_supply_names), + dw9768->supplies); + if (ret) { + dev_err(dev, "failed to get regulators\n"); + return ret; + } + + /* Initialize controls */ + ret = dw9768_init_controls(dw9768); + if (ret) + goto err_free_handler; + + /* Initialize subdev */ + dw9768->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + dw9768->sd.internal_ops = &dw9768_int_ops; + + ret = media_entity_pads_init(&dw9768->sd.entity, 0, NULL); + if (ret < 0) + goto err_free_handler; + + dw9768->sd.entity.function = MEDIA_ENT_F_LENS; + + pm_runtime_enable(dev); + if (!pm_runtime_enabled(dev)) { + ret = dw9768_runtime_resume(dev); + if (ret < 0) { + dev_err(dev, "failed to power on: %d\n", ret); + goto err_clean_entity; + } + } + + ret = v4l2_async_register_subdev(&dw9768->sd); + if (ret < 0) { + dev_err(dev, "failed to register V4L2 subdev: %d", ret); + goto err_power_off; + } + + return 0; + +err_power_off: + if (pm_runtime_enabled(dev)) + pm_runtime_disable(dev); + else + dw9768_runtime_suspend(dev); +err_clean_entity: + media_entity_cleanup(&dw9768->sd.entity); +err_free_handler: + v4l2_ctrl_handler_free(&dw9768->ctrls); + + return ret; +} + +static int dw9768_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct dw9768 *dw9768 = sd_to_dw9768(sd); + + v4l2_async_unregister_subdev(&dw9768->sd); + v4l2_ctrl_handler_free(&dw9768->ctrls); + media_entity_cleanup(&dw9768->sd.entity); + pm_runtime_disable(&client->dev); + if (!pm_runtime_status_suspended(&client->dev)) + dw9768_runtime_suspend(&client->dev); + pm_runtime_set_suspended(&client->dev); + + return 0; +} + +static const struct of_device_id dw9768_of_table[] = { + { .compatible = "dongwoon,dw9768" }, + { .compatible = "giantec,gt9769" }, + {} +}; +MODULE_DEVICE_TABLE(of, dw9768_of_table); + +static const struct dev_pm_ops dw9768_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, + pm_runtime_force_resume) + SET_RUNTIME_PM_OPS(dw9768_runtime_suspend, dw9768_runtime_resume, NULL) +}; + +static struct i2c_driver dw9768_i2c_driver = { + .driver = { + .name = DW9768_NAME, + .pm = &dw9768_pm_ops, + .of_match_table = dw9768_of_table, + }, + .probe_new = dw9768_probe, + .remove = dw9768_remove, +}; +module_i2c_driver(dw9768_i2c_driver); + +MODULE_AUTHOR("Dongchun Zhu "); +MODULE_DESCRIPTION("DW9768 VCM driver"); +MODULE_LICENSE("GPL v2"); -- GitLab From 18f1290188c95307a264ae6bd013b3318de64736 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Fri, 3 Jul 2020 15:33:32 +0200 Subject: [PATCH 0671/1476] media: dt-bindings: media: i2c: Add bindings for Maxim Integrated MAX9286 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The MAX9286 deserializes video data received on up to 4 Gigabit Multimedia Serial Links (GMSL) and outputs them on a CSI-2 port using up to 4 data lanes. Signed-off-by: Laurent Pinchart Signed-off-by: Jacopo Mondi Signed-off-by: Kieran Bingham Signed-off-by: Niklas Söderlund Reviewed-by: Rob Herring Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- .../bindings/media/i2c/maxim,max9286.yaml | 366 ++++++++++++++++++ 1 file changed, 366 insertions(+) create mode 100644 Documentation/devicetree/bindings/media/i2c/maxim,max9286.yaml diff --git a/Documentation/devicetree/bindings/media/i2c/maxim,max9286.yaml b/Documentation/devicetree/bindings/media/i2c/maxim,max9286.yaml new file mode 100644 index 0000000000000..e7b543159d15d --- /dev/null +++ b/Documentation/devicetree/bindings/media/i2c/maxim,max9286.yaml @@ -0,0 +1,366 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +# Copyright (C) 2019 Renesas Electronics Corp. +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/media/i2c/maxim,max9286.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Maxim Integrated Quad GMSL Deserializer + +maintainers: + - Jacopo Mondi + - Kieran Bingham + - Laurent Pinchart + - Niklas Söderlund + +description: | + The MAX9286 deserializer receives video data on up to 4 Gigabit Multimedia + Serial Links (GMSL) and outputs them on a CSI-2 D-PHY port using up to 4 data + lanes. + + In addition to video data, the GMSL links carry a bidirectional control + channel that encapsulates I2C messages. The MAX9286 forwards all I2C traffic + not addressed to itself to the other side of the links, where a GMSL + serializer will output it on a local I2C bus. In the other direction all I2C + traffic received over GMSL by the MAX9286 is output on the local I2C bus. + +properties: + '#address-cells': + const: 1 + + '#size-cells': + const: 0 + + compatible: + const: maxim,max9286 + + reg: + description: I2C device address + maxItems: 1 + + poc-supply: + description: Regulator providing Power over Coax to the cameras + maxItems: 1 + + enable-gpios: + description: GPIO connected to the \#PWDN pin with inverted polarity + maxItems: 1 + + gpio-controller: true + + '#gpio-cells': + const: 2 + + ports: + type: object + description: | + The connections to the MAX9286 GMSL and its endpoint nodes are modelled + using the OF graph bindings in accordance with the video interface + bindings defined in + Documentation/devicetree/bindings/media/video-interfaces.txt. + + The following table lists the port number corresponding to each device + port. + + Port Description + ---------------------------------------- + Port 0 GMSL Input 0 + Port 1 GMSL Input 1 + Port 2 GMSL Input 2 + Port 3 GMSL Input 3 + Port 4 CSI-2 Output + + properties: + '#address-cells': + const: 1 + + '#size-cells': + const: 0 + + port@[0-3]: + type: object + properties: + reg: + enum: [ 0, 1, 2, 3 ] + + endpoint: + type: object + + properties: + remote-endpoint: + description: | + phandle to the remote GMSL source endpoint subnode in the + remote node port. + + required: + - remote-endpoint + + required: + - reg + - endpoint + + additionalProperties: false + + port@4: + type: object + properties: + reg: + const: 4 + + endpoint: + type: object + + properties: + remote-endpoint: + description: phandle to the remote CSI-2 sink endpoint. + + data-lanes: + description: array of physical CSI-2 data lane indexes. + + required: + - remote-endpoint + - data-lanes + + required: + - reg + - endpoint + + additionalProperties: false + + required: + - port@4 + + i2c-mux: + type: object + description: | + Each GMSL link is modelled as a child bus of an i2c bus + multiplexer/switch, in accordance with bindings described in + Documentation/devicetree/bindings/i2c/i2c-mux.txt. + + properties: + '#address-cells': + const: 1 + + '#size-cells': + const: 0 + + patternProperties: + "^i2c@[0-3]$": + type: object + description: | + Child node of the i2c bus multiplexer which represents a GMSL link. + Each serializer device on the GMSL link remote end is represented with + an i2c-mux child node. The MAX9286 chip supports up to 4 GMSL + channels. + + properties: + '#address-cells': + const: 1 + + '#size-cells': + const: 0 + + reg: + description: The index of the GMSL channel. + maxItems: 1 + + patternProperties: + "^camera@[a-f0-9]+$": + type: object + description: | + The remote camera device, composed by a GMSL serializer and a + connected video source. + + properties: + compatible: + description: The remote device compatible string. + + reg: + minItems: 2 + maxItems: 3 + description: | + The I2C addresses to be assigned to the remote devices through + address reprogramming. The number of entries depends on the + requirements of the currently connected remote device. + + port: + type: object + + properties: + endpoint: + type: object + + properties: + remote-endpoint: + description: phandle to the MAX9286 sink endpoint. + + required: + - remote-endpoint + + additionalProperties: false + + required: + - endpoint + + additionalProperties: false + + required: + - compatible + - reg + - port + + additionalProperties: false + + additionalProperties: false + + additionalProperties: false + +required: + - compatible + - reg + - ports + - i2c-mux + - gpio-controller + +additionalProperties: false + +examples: + - | + #include + + i2c@e66d8000 { + #address-cells = <1>; + #size-cells = <0>; + + reg = <0 0xe66d8000>; + + gmsl-deserializer@2c { + compatible = "maxim,max9286"; + reg = <0x2c>; + poc-supply = <&camera_poc_12v>; + enable-gpios = <&gpio 13 GPIO_ACTIVE_HIGH>; + + gpio-controller; + #gpio-cells = <2>; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + + max9286_in0: endpoint { + remote-endpoint = <&rdacm20_out0>; + }; + }; + + port@1 { + reg = <1>; + + max9286_in1: endpoint { + remote-endpoint = <&rdacm20_out1>; + }; + }; + + port@2 { + reg = <2>; + + max9286_in2: endpoint { + remote-endpoint = <&rdacm20_out2>; + }; + }; + + port@3 { + reg = <3>; + + max9286_in3: endpoint { + remote-endpoint = <&rdacm20_out3>; + }; + }; + + port@4 { + reg = <4>; + + max9286_out: endpoint { + data-lanes = <1 2 3 4>; + remote-endpoint = <&csi40_in>; + }; + }; + }; + + i2c-mux { + #address-cells = <1>; + #size-cells = <0>; + + i2c@0 { + #address-cells = <1>; + #size-cells = <0>; + reg = <0>; + + camera@51 { + compatible = "imi,rdacm20"; + reg = <0x51>, <0x61>; + + port { + rdacm20_out0: endpoint { + remote-endpoint = <&max9286_in0>; + }; + }; + + }; + }; + + i2c@1 { + #address-cells = <1>; + #size-cells = <0>; + reg = <1>; + + camera@52 { + compatible = "imi,rdacm20"; + reg = <0x52>, <0x62>; + + port { + rdacm20_out1: endpoint { + remote-endpoint = <&max9286_in1>; + }; + }; + }; + }; + + i2c@2 { + #address-cells = <1>; + #size-cells = <0>; + reg = <2>; + + camera@53 { + compatible = "imi,rdacm20"; + reg = <0x53>, <0x63>; + + port { + rdacm20_out2: endpoint { + remote-endpoint = <&max9286_in2>; + }; + }; + }; + }; + + i2c@3 { + #address-cells = <1>; + #size-cells = <0>; + reg = <3>; + + camera@54 { + compatible = "imi,rdacm20"; + reg = <0x54>, <0x64>; + + port { + rdacm20_out3: endpoint { + remote-endpoint = <&max9286_in3>; + }; + }; + }; + }; + }; + }; + }; -- GitLab From 66d8c9d2422da21ed41f75c03ba0685987b65fe0 Mon Sep 17 00:00:00 2001 From: Kieran Bingham Date: Fri, 12 Jun 2020 16:47:11 +0200 Subject: [PATCH 0672/1476] media: i2c: Add MAX9286 driver MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The MAX9286 is a 4-channel GMSL deserializer with coax or STP input and CSI-2 output. The device supports multicamera streaming applications, and features the ability to synchronise the attached cameras. CSI-2 output can be configured with 1 to 4 lanes, and a control channel is supported over I2C, which implements an I2C mux to facilitate communications with connected cameras across the reverse control channel. Signed-off-by: Jacopo Mondi Signed-off-by: Kieran Bingham Signed-off-by: Laurent Pinchart Signed-off-by: Niklas Söderlund Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- MAINTAINERS | 10 + drivers/media/i2c/Kconfig | 13 + drivers/media/i2c/Makefile | 1 + drivers/media/i2c/max9286.c | 1320 +++++++++++++++++++++++++++++++++++ 4 files changed, 1344 insertions(+) create mode 100644 drivers/media/i2c/max9286.c diff --git a/MAINTAINERS b/MAINTAINERS index dc28f18c62bdf..3bcedb12ab928 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -10438,6 +10438,16 @@ F: Documentation/hwmon/max6697.rst F: drivers/hwmon/max6697.c F: include/linux/platform_data/max6697.h +MAX9286 QUAD GMSL DESERIALIZER DRIVER +M: Jacopo Mondi +M: Kieran Bingham +M: Laurent Pinchart +M: Niklas Söderlund +L: linux-media@vger.kernel.org +S: Maintained +F: Documentation/devicetree/bindings/media/i2c/maxim,max9286.yaml +F: drivers/media/i2c/max9286.c + MAX9860 MONO AUDIO VOICE CODEC DRIVER M: Peter Rosin L: alsa-devel@alsa-project.org (moderated for non-subscribers) diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig index d2d86ced319d7..8471a6c438d47 100644 --- a/drivers/media/i2c/Kconfig +++ b/drivers/media/i2c/Kconfig @@ -464,6 +464,19 @@ config VIDEO_VPX3220 To compile this driver as a module, choose M here: the module will be called vpx3220. +config VIDEO_MAX9286 + tristate "Maxim MAX9286 GMSL deserializer support" + depends on I2C && I2C_MUX + depends on OF + select V4L2_FWNODE + select VIDEO_V4L2_SUBDEV_API + select MEDIA_CONTROLLER + help + This driver supports the Maxim MAX9286 GMSL deserializer. + + To compile this driver as a module, choose M here: the + module will be called max9286. + comment "Video and audio decoders" config VIDEO_SAA717X diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile index 33ad435bffea3..9091f4918d4bf 100644 --- a/drivers/media/i2c/Makefile +++ b/drivers/media/i2c/Makefile @@ -119,6 +119,7 @@ obj-$(CONFIG_VIDEO_IMX274) += imx274.o obj-$(CONFIG_VIDEO_IMX290) += imx290.o obj-$(CONFIG_VIDEO_IMX319) += imx319.o obj-$(CONFIG_VIDEO_IMX355) += imx355.o +obj-$(CONFIG_VIDEO_MAX9286) += max9286.o obj-$(CONFIG_VIDEO_ST_MIPID02) += st-mipid02.o obj-$(CONFIG_SDR_MAX2175) += max2175.o diff --git a/drivers/media/i2c/max9286.c b/drivers/media/i2c/max9286.c new file mode 100644 index 0000000000000..47f280518fdb6 --- /dev/null +++ b/drivers/media/i2c/max9286.c @@ -0,0 +1,1320 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Maxim MAX9286 GMSL Deserializer Driver + * + * Copyright (C) 2017-2019 Jacopo Mondi + * Copyright (C) 2017-2019 Kieran Bingham + * Copyright (C) 2017-2019 Laurent Pinchart + * Copyright (C) 2017-2019 Niklas Söderlund + * Copyright (C) 2016 Renesas Electronics Corporation + * Copyright (C) 2015 Cogent Embedded, Inc. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +/* Register 0x00 */ +#define MAX9286_MSTLINKSEL_AUTO (7 << 5) +#define MAX9286_MSTLINKSEL(n) ((n) << 5) +#define MAX9286_EN_VS_GEN BIT(4) +#define MAX9286_LINKEN(n) (1 << (n)) +/* Register 0x01 */ +#define MAX9286_FSYNCMODE_ECU (3 << 6) +#define MAX9286_FSYNCMODE_EXT (2 << 6) +#define MAX9286_FSYNCMODE_INT_OUT (1 << 6) +#define MAX9286_FSYNCMODE_INT_HIZ (0 << 6) +#define MAX9286_GPIEN BIT(5) +#define MAX9286_ENLMO_RSTFSYNC BIT(2) +#define MAX9286_FSYNCMETH_AUTO (2 << 0) +#define MAX9286_FSYNCMETH_SEMI_AUTO (1 << 0) +#define MAX9286_FSYNCMETH_MANUAL (0 << 0) +#define MAX9286_REG_FSYNC_PERIOD_L 0x06 +#define MAX9286_REG_FSYNC_PERIOD_M 0x07 +#define MAX9286_REG_FSYNC_PERIOD_H 0x08 +/* Register 0x0a */ +#define MAX9286_FWDCCEN(n) (1 << ((n) + 4)) +#define MAX9286_REVCCEN(n) (1 << (n)) +/* Register 0x0c */ +#define MAX9286_HVEN BIT(7) +#define MAX9286_EDC_6BIT_HAMMING (2 << 5) +#define MAX9286_EDC_6BIT_CRC (1 << 5) +#define MAX9286_EDC_1BIT_PARITY (0 << 5) +#define MAX9286_DESEL BIT(4) +#define MAX9286_INVVS BIT(3) +#define MAX9286_INVHS BIT(2) +#define MAX9286_HVSRC_D0 (2 << 0) +#define MAX9286_HVSRC_D14 (1 << 0) +#define MAX9286_HVSRC_D18 (0 << 0) +/* Register 0x0f */ +#define MAX9286_0X0F_RESERVED BIT(3) +/* Register 0x12 */ +#define MAX9286_CSILANECNT(n) (((n) - 1) << 6) +#define MAX9286_CSIDBL BIT(5) +#define MAX9286_DBL BIT(4) +#define MAX9286_DATATYPE_USER_8BIT (11 << 0) +#define MAX9286_DATATYPE_USER_YUV_12BIT (10 << 0) +#define MAX9286_DATATYPE_USER_24BIT (9 << 0) +#define MAX9286_DATATYPE_RAW14 (8 << 0) +#define MAX9286_DATATYPE_RAW11 (7 << 0) +#define MAX9286_DATATYPE_RAW10 (6 << 0) +#define MAX9286_DATATYPE_RAW8 (5 << 0) +#define MAX9286_DATATYPE_YUV422_10BIT (4 << 0) +#define MAX9286_DATATYPE_YUV422_8BIT (3 << 0) +#define MAX9286_DATATYPE_RGB555 (2 << 0) +#define MAX9286_DATATYPE_RGB565 (1 << 0) +#define MAX9286_DATATYPE_RGB888 (0 << 0) +/* Register 0x15 */ +#define MAX9286_VC(n) ((n) << 5) +#define MAX9286_VCTYPE BIT(4) +#define MAX9286_CSIOUTEN BIT(3) +#define MAX9286_0X15_RESV (3 << 0) +/* Register 0x1b */ +#define MAX9286_SWITCHIN(n) (1 << ((n) + 4)) +#define MAX9286_ENEQ(n) (1 << (n)) +/* Register 0x27 */ +#define MAX9286_LOCKED BIT(7) +/* Register 0x31 */ +#define MAX9286_FSYNC_LOCKED BIT(6) +/* Register 0x34 */ +#define MAX9286_I2CLOCACK BIT(7) +#define MAX9286_I2CSLVSH_1046NS_469NS (3 << 5) +#define MAX9286_I2CSLVSH_938NS_352NS (2 << 5) +#define MAX9286_I2CSLVSH_469NS_234NS (1 << 5) +#define MAX9286_I2CSLVSH_352NS_117NS (0 << 5) +#define MAX9286_I2CMSTBT_837KBPS (7 << 2) +#define MAX9286_I2CMSTBT_533KBPS (6 << 2) +#define MAX9286_I2CMSTBT_339KBPS (5 << 2) +#define MAX9286_I2CMSTBT_173KBPS (4 << 2) +#define MAX9286_I2CMSTBT_105KBPS (3 << 2) +#define MAX9286_I2CMSTBT_84KBPS (2 << 2) +#define MAX9286_I2CMSTBT_28KBPS (1 << 2) +#define MAX9286_I2CMSTBT_8KBPS (0 << 2) +#define MAX9286_I2CSLVTO_NONE (3 << 0) +#define MAX9286_I2CSLVTO_1024US (2 << 0) +#define MAX9286_I2CSLVTO_256US (1 << 0) +#define MAX9286_I2CSLVTO_64US (0 << 0) +/* Register 0x3b */ +#define MAX9286_REV_TRF(n) ((n) << 4) +#define MAX9286_REV_AMP(n) ((((n) - 30) / 10) << 1) /* in mV */ +#define MAX9286_REV_AMP_X BIT(0) +/* Register 0x3f */ +#define MAX9286_EN_REV_CFG BIT(6) +#define MAX9286_REV_FLEN(n) ((n) - 20) +/* Register 0x49 */ +#define MAX9286_VIDEO_DETECT_MASK 0x0f +/* Register 0x69 */ +#define MAX9286_LFLTBMONMASKED BIT(7) +#define MAX9286_LOCKMONMASKED BIT(6) +#define MAX9286_AUTOCOMBACKEN BIT(5) +#define MAX9286_AUTOMASKEN BIT(4) +#define MAX9286_MASKLINK(n) ((n) << 0) + +/* + * The sink and source pads are created to match the OF graph port numbers so + * that their indexes can be used interchangeably. + */ +#define MAX9286_NUM_GMSL 4 +#define MAX9286_N_SINKS 4 +#define MAX9286_N_PADS 5 +#define MAX9286_SRC_PAD 4 + +struct max9286_source { + struct v4l2_async_subdev asd; + struct v4l2_subdev *sd; + struct fwnode_handle *fwnode; +}; + +#define asd_to_max9286_source(_asd) \ + container_of(_asd, struct max9286_source, asd) + +struct max9286_priv { + struct i2c_client *client; + struct gpio_desc *gpiod_pwdn; + struct v4l2_subdev sd; + struct media_pad pads[MAX9286_N_PADS]; + struct regulator *regulator; + + struct gpio_chip gpio; + u8 gpio_state; + + struct i2c_mux_core *mux; + unsigned int mux_channel; + bool mux_open; + + struct v4l2_ctrl_handler ctrls; + struct v4l2_ctrl *pixelrate; + + struct v4l2_mbus_framefmt fmt[MAX9286_N_SINKS]; + + /* Protects controls and fmt structures */ + struct mutex mutex; + + unsigned int nsources; + unsigned int source_mask; + unsigned int route_mask; + unsigned int bound_sources; + unsigned int csi2_data_lanes; + struct max9286_source sources[MAX9286_NUM_GMSL]; + struct v4l2_async_notifier notifier; +}; + +static struct max9286_source *next_source(struct max9286_priv *priv, + struct max9286_source *source) +{ + if (!source) + source = &priv->sources[0]; + else + source++; + + for (; source < &priv->sources[MAX9286_NUM_GMSL]; source++) { + if (source->fwnode) + return source; + } + + return NULL; +} + +#define for_each_source(priv, source) \ + for ((source) = NULL; ((source) = next_source((priv), (source))); ) + +#define to_index(priv, source) ((source) - &(priv)->sources[0]) + +static inline struct max9286_priv *sd_to_max9286(struct v4l2_subdev *sd) +{ + return container_of(sd, struct max9286_priv, sd); +} + +/* ----------------------------------------------------------------------------- + * I2C IO + */ + +static int max9286_read(struct max9286_priv *priv, u8 reg) +{ + int ret; + + ret = i2c_smbus_read_byte_data(priv->client, reg); + if (ret < 0) + dev_err(&priv->client->dev, + "%s: register 0x%02x read failed (%d)\n", + __func__, reg, ret); + + return ret; +} + +static int max9286_write(struct max9286_priv *priv, u8 reg, u8 val) +{ + int ret; + + ret = i2c_smbus_write_byte_data(priv->client, reg, val); + if (ret < 0) + dev_err(&priv->client->dev, + "%s: register 0x%02x write failed (%d)\n", + __func__, reg, ret); + + return ret; +} + +/* ----------------------------------------------------------------------------- + * I2C Multiplexer + */ + +static void max9286_i2c_mux_configure(struct max9286_priv *priv, u8 conf) +{ + max9286_write(priv, 0x0a, conf); + + /* + * We must sleep after any change to the forward or reverse channel + * configuration. + */ + usleep_range(3000, 5000); +} + +static void max9286_i2c_mux_open(struct max9286_priv *priv) +{ + /* Open all channels on the MAX9286 */ + max9286_i2c_mux_configure(priv, 0xff); + + priv->mux_open = true; +} + +static void max9286_i2c_mux_close(struct max9286_priv *priv) +{ + /* + * Ensure that both the forward and reverse channel are disabled on the + * mux, and that the channel ID is invalidated to ensure we reconfigure + * on the next max9286_i2c_mux_select() call. + */ + max9286_i2c_mux_configure(priv, 0x00); + + priv->mux_open = false; + priv->mux_channel = -1; +} + +static int max9286_i2c_mux_select(struct i2c_mux_core *muxc, u32 chan) +{ + struct max9286_priv *priv = i2c_mux_priv(muxc); + + /* Channel select is disabled when configured in the opened state. */ + if (priv->mux_open) + return 0; + + if (priv->mux_channel == chan) + return 0; + + priv->mux_channel = chan; + + max9286_i2c_mux_configure(priv, + MAX9286_FWDCCEN(chan) | + MAX9286_REVCCEN(chan)); + + return 0; +} + +static int max9286_i2c_mux_init(struct max9286_priv *priv) +{ + struct max9286_source *source; + int ret; + + if (!i2c_check_functionality(priv->client->adapter, + I2C_FUNC_SMBUS_WRITE_BYTE_DATA)) + return -ENODEV; + + priv->mux = i2c_mux_alloc(priv->client->adapter, &priv->client->dev, + priv->nsources, 0, I2C_MUX_LOCKED, + max9286_i2c_mux_select, NULL); + if (!priv->mux) + return -ENOMEM; + + priv->mux->priv = priv; + + for_each_source(priv, source) { + unsigned int index = to_index(priv, source); + + ret = i2c_mux_add_adapter(priv->mux, 0, index, 0); + if (ret < 0) + goto error; + } + + return 0; + +error: + i2c_mux_del_adapters(priv->mux); + return ret; +} + +static void max9286_configure_i2c(struct max9286_priv *priv, bool localack) +{ + u8 config = MAX9286_I2CSLVSH_469NS_234NS | MAX9286_I2CSLVTO_1024US | + MAX9286_I2CMSTBT_105KBPS; + + if (localack) + config |= MAX9286_I2CLOCACK; + + max9286_write(priv, 0x34, config); + usleep_range(3000, 5000); +} + +/* + * max9286_check_video_links() - Make sure video links are detected and locked + * + * Performs safety checks on video link status. Make sure they are detected + * and all enabled links are locked. + * + * Returns 0 for success, -EIO for errors. + */ +static int max9286_check_video_links(struct max9286_priv *priv) +{ + unsigned int i; + int ret; + + /* + * Make sure valid video links are detected. + * The delay is not characterized in de-serializer manual, wait up + * to 5 ms. + */ + for (i = 0; i < 10; i++) { + ret = max9286_read(priv, 0x49); + if (ret < 0) + return -EIO; + + if ((ret & MAX9286_VIDEO_DETECT_MASK) == priv->source_mask) + break; + + usleep_range(350, 500); + } + + if (i == 10) { + dev_err(&priv->client->dev, + "Unable to detect video links: 0x%02x\n", ret); + return -EIO; + } + + /* Make sure all enabled links are locked (4ms max). */ + for (i = 0; i < 10; i++) { + ret = max9286_read(priv, 0x27); + if (ret < 0) + return -EIO; + + if (ret & MAX9286_LOCKED) + break; + + usleep_range(350, 450); + } + + if (i == 10) { + dev_err(&priv->client->dev, "Not all enabled links locked\n"); + return -EIO; + } + + return 0; +} + +/* + * max9286_check_config_link() - Detect and wait for configuration links + * + * Determine if the configuration channel is up and settled for a link. + * + * Returns 0 for success, -EIO for errors. + */ +static int max9286_check_config_link(struct max9286_priv *priv, + unsigned int source_mask) +{ + unsigned int conflink_mask = (source_mask & 0x0f) << 4; + unsigned int i; + int ret; + + /* + * Make sure requested configuration links are detected. + * The delay is not characterized in the chip manual: wait up + * to 5 milliseconds. + */ + for (i = 0; i < 10; i++) { + ret = max9286_read(priv, 0x49) & 0xf0; + if (ret < 0) + return -EIO; + + if (ret == conflink_mask) + break; + + usleep_range(350, 500); + } + + if (ret != conflink_mask) { + dev_err(&priv->client->dev, + "Unable to detect configuration links: 0x%02x expected 0x%02x\n", + ret, conflink_mask); + return -EIO; + } + + dev_info(&priv->client->dev, + "Successfully detected configuration links after %u loops: 0x%02x\n", + i, conflink_mask); + + return 0; +} + +/* ----------------------------------------------------------------------------- + * V4L2 Subdev + */ + +static int max9286_set_pixelrate(struct max9286_priv *priv) +{ + struct max9286_source *source = NULL; + u64 pixelrate = 0; + + for_each_source(priv, source) { + struct v4l2_ctrl *ctrl; + u64 source_rate = 0; + + /* Pixel rate is mandatory to be reported by sources. */ + ctrl = v4l2_ctrl_find(source->sd->ctrl_handler, + V4L2_CID_PIXEL_RATE); + if (!ctrl) { + pixelrate = 0; + break; + } + + /* All source must report the same pixel rate. */ + source_rate = v4l2_ctrl_g_ctrl_int64(ctrl); + if (!pixelrate) { + pixelrate = source_rate; + } else if (pixelrate != source_rate) { + dev_err(&priv->client->dev, + "Unable to calculate pixel rate\n"); + return -EINVAL; + } + } + + if (!pixelrate) { + dev_err(&priv->client->dev, + "No pixel rate control available in sources\n"); + return -EINVAL; + } + + /* + * The CSI-2 transmitter pixel rate is the single source rate multiplied + * by the number of available sources. + */ + return v4l2_ctrl_s_ctrl_int64(priv->pixelrate, + pixelrate * priv->nsources); +} + +static int max9286_notify_bound(struct v4l2_async_notifier *notifier, + struct v4l2_subdev *subdev, + struct v4l2_async_subdev *asd) +{ + struct max9286_priv *priv = sd_to_max9286(notifier->sd); + struct max9286_source *source = asd_to_max9286_source(asd); + unsigned int index = to_index(priv, source); + unsigned int src_pad; + int ret; + + ret = media_entity_get_fwnode_pad(&subdev->entity, + source->fwnode, + MEDIA_PAD_FL_SOURCE); + if (ret < 0) { + dev_err(&priv->client->dev, + "Failed to find pad for %s\n", subdev->name); + return ret; + } + + priv->bound_sources |= BIT(index); + source->sd = subdev; + src_pad = ret; + + ret = media_create_pad_link(&source->sd->entity, src_pad, + &priv->sd.entity, index, + MEDIA_LNK_FL_ENABLED | + MEDIA_LNK_FL_IMMUTABLE); + if (ret) { + dev_err(&priv->client->dev, + "Unable to link %s:%u -> %s:%u\n", + source->sd->name, src_pad, priv->sd.name, index); + return ret; + } + + dev_dbg(&priv->client->dev, "Bound %s pad: %u on index %u\n", + subdev->name, src_pad, index); + + /* + * We can only register v4l2_async_notifiers, which do not provide a + * means to register a complete callback. bound_sources allows us to + * identify when all remote serializers have completed their probe. + */ + if (priv->bound_sources != priv->source_mask) + return 0; + + /* + * All enabled sources have probed and enabled their reverse control + * channels: + * + * - Verify all configuration links are properly detected + * - Disable auto-ack as communication on the control channel are now + * stable. + */ + max9286_check_config_link(priv, priv->source_mask); + + /* + * Re-configure I2C with local acknowledge disabled after cameras have + * probed. + */ + max9286_configure_i2c(priv, false); + + return max9286_set_pixelrate(priv); +} + +static void max9286_notify_unbind(struct v4l2_async_notifier *notifier, + struct v4l2_subdev *subdev, + struct v4l2_async_subdev *asd) +{ + struct max9286_priv *priv = sd_to_max9286(notifier->sd); + struct max9286_source *source = asd_to_max9286_source(asd); + unsigned int index = to_index(priv, source); + + source->sd = NULL; + priv->bound_sources &= ~BIT(index); +} + +static const struct v4l2_async_notifier_operations max9286_notify_ops = { + .bound = max9286_notify_bound, + .unbind = max9286_notify_unbind, +}; + +static int max9286_v4l2_notifier_register(struct max9286_priv *priv) +{ + struct device *dev = &priv->client->dev; + struct max9286_source *source = NULL; + int ret; + + if (!priv->nsources) + return 0; + + v4l2_async_notifier_init(&priv->notifier); + + for_each_source(priv, source) { + unsigned int i = to_index(priv, source); + + source->asd.match_type = V4L2_ASYNC_MATCH_FWNODE; + source->asd.match.fwnode = source->fwnode; + + ret = v4l2_async_notifier_add_subdev(&priv->notifier, + &source->asd); + if (ret) { + dev_err(dev, "Failed to add subdev for source %d", i); + v4l2_async_notifier_cleanup(&priv->notifier); + return ret; + } + + /* + * Balance the reference counting handled through + * v4l2_async_notifier_cleanup() + */ + fwnode_handle_get(source->fwnode); + } + + priv->notifier.ops = &max9286_notify_ops; + + ret = v4l2_async_subdev_notifier_register(&priv->sd, &priv->notifier); + if (ret) { + dev_err(dev, "Failed to register subdev_notifier"); + v4l2_async_notifier_cleanup(&priv->notifier); + return ret; + } + + return 0; +} + +static void max9286_v4l2_notifier_unregister(struct max9286_priv *priv) +{ + if (!priv->nsources) + return; + + v4l2_async_notifier_unregister(&priv->notifier); + v4l2_async_notifier_cleanup(&priv->notifier); +} + +static int max9286_s_stream(struct v4l2_subdev *sd, int enable) +{ + struct max9286_priv *priv = sd_to_max9286(sd); + struct max9286_source *source; + unsigned int i; + bool sync = false; + int ret; + + if (enable) { + /* + * The frame sync between cameras is transmitted across the + * reverse channel as GPIO. We must open all channels while + * streaming to allow this synchronisation signal to be shared. + */ + max9286_i2c_mux_open(priv); + + /* Start all cameras. */ + for_each_source(priv, source) { + ret = v4l2_subdev_call(source->sd, video, s_stream, 1); + if (ret) + return ret; + } + + ret = max9286_check_video_links(priv); + if (ret) + return ret; + + /* + * Wait until frame synchronization is locked. + * + * Manual says frame sync locking should take ~6 VTS. + * From practical experience at least 8 are required. Give + * 12 complete frames time (~400ms at 30 fps) to achieve frame + * locking before returning error. + */ + for (i = 0; i < 40; i++) { + if (max9286_read(priv, 0x31) & MAX9286_FSYNC_LOCKED) { + sync = true; + break; + } + usleep_range(9000, 11000); + } + + if (!sync) { + dev_err(&priv->client->dev, + "Failed to get frame synchronization\n"); + return -EXDEV; /* Invalid cross-device link */ + } + + /* + * Enable CSI output, VC set according to link number. + * Bit 7 must be set (chip manual says it's 0 and reserved). + */ + max9286_write(priv, 0x15, 0x80 | MAX9286_VCTYPE | + MAX9286_CSIOUTEN | MAX9286_0X15_RESV); + } else { + max9286_write(priv, 0x15, MAX9286_VCTYPE | MAX9286_0X15_RESV); + + /* Stop all cameras. */ + for_each_source(priv, source) + v4l2_subdev_call(source->sd, video, s_stream, 0); + + max9286_i2c_mux_close(priv); + } + + return 0; +} + +static int max9286_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_mbus_code_enum *code) +{ + if (code->pad || code->index > 0) + return -EINVAL; + + code->code = MEDIA_BUS_FMT_UYVY8_1X16; + + return 0; +} + +static struct v4l2_mbus_framefmt * +max9286_get_pad_format(struct max9286_priv *priv, + struct v4l2_subdev_pad_config *cfg, + unsigned int pad, u32 which) +{ + switch (which) { + case V4L2_SUBDEV_FORMAT_TRY: + return v4l2_subdev_get_try_format(&priv->sd, cfg, pad); + case V4L2_SUBDEV_FORMAT_ACTIVE: + return &priv->fmt[pad]; + default: + return NULL; + } +} + +static int max9286_set_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *format) +{ + struct max9286_priv *priv = sd_to_max9286(sd); + struct v4l2_mbus_framefmt *cfg_fmt; + + if (format->pad == MAX9286_SRC_PAD) + return -EINVAL; + + /* Refuse non YUV422 formats as we hardcode DT to 8 bit YUV422 */ + switch (format->format.code) { + case MEDIA_BUS_FMT_UYVY8_1X16: + case MEDIA_BUS_FMT_VYUY8_1X16: + case MEDIA_BUS_FMT_YUYV8_1X16: + case MEDIA_BUS_FMT_YVYU8_1X16: + break; + default: + format->format.code = MEDIA_BUS_FMT_UYVY8_1X16; + break; + } + + cfg_fmt = max9286_get_pad_format(priv, cfg, format->pad, format->which); + if (!cfg_fmt) + return -EINVAL; + + mutex_lock(&priv->mutex); + *cfg_fmt = format->format; + mutex_unlock(&priv->mutex); + + return 0; +} + +static int max9286_get_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *format) +{ + struct max9286_priv *priv = sd_to_max9286(sd); + struct v4l2_mbus_framefmt *cfg_fmt; + unsigned int pad = format->pad; + + /* + * Multiplexed Stream Support: Support link validation by returning the + * format of the first bound link. All links must have the same format, + * as we do not support mixing and matching of cameras connected to the + * max9286. + */ + if (pad == MAX9286_SRC_PAD) + pad = __ffs(priv->bound_sources); + + cfg_fmt = max9286_get_pad_format(priv, cfg, pad, format->which); + if (!cfg_fmt) + return -EINVAL; + + mutex_lock(&priv->mutex); + format->format = *cfg_fmt; + mutex_unlock(&priv->mutex); + + return 0; +} + +static const struct v4l2_subdev_video_ops max9286_video_ops = { + .s_stream = max9286_s_stream, +}; + +static const struct v4l2_subdev_pad_ops max9286_pad_ops = { + .enum_mbus_code = max9286_enum_mbus_code, + .get_fmt = max9286_get_fmt, + .set_fmt = max9286_set_fmt, +}; + +static const struct v4l2_subdev_ops max9286_subdev_ops = { + .video = &max9286_video_ops, + .pad = &max9286_pad_ops, +}; + +static void max9286_init_format(struct v4l2_mbus_framefmt *fmt) +{ + fmt->width = 1280; + fmt->height = 800; + fmt->code = MEDIA_BUS_FMT_UYVY8_1X16; + fmt->colorspace = V4L2_COLORSPACE_SRGB; + fmt->field = V4L2_FIELD_NONE; + fmt->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT; + fmt->quantization = V4L2_QUANTIZATION_DEFAULT; + fmt->xfer_func = V4L2_XFER_FUNC_DEFAULT; +} + +static int max9286_open(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh) +{ + struct v4l2_mbus_framefmt *format; + unsigned int i; + + for (i = 0; i < MAX9286_N_SINKS; i++) { + format = v4l2_subdev_get_try_format(subdev, fh->pad, i); + max9286_init_format(format); + } + + return 0; +} + +static const struct v4l2_subdev_internal_ops max9286_subdev_internal_ops = { + .open = max9286_open, +}; + +static int max9286_s_ctrl(struct v4l2_ctrl *ctrl) +{ + switch (ctrl->id) { + case V4L2_CID_PIXEL_RATE: + return 0; + default: + return -EINVAL; + } +} + +static const struct v4l2_ctrl_ops max9286_ctrl_ops = { + .s_ctrl = max9286_s_ctrl, +}; + +static int max9286_v4l2_register(struct max9286_priv *priv) +{ + struct device *dev = &priv->client->dev; + struct fwnode_handle *ep; + int ret; + int i; + + /* Register v4l2 async notifiers for connected Camera subdevices */ + ret = max9286_v4l2_notifier_register(priv); + if (ret) { + dev_err(dev, "Unable to register V4L2 async notifiers\n"); + return ret; + } + + /* Configure V4L2 for the MAX9286 itself */ + + for (i = 0; i < MAX9286_N_SINKS; i++) + max9286_init_format(&priv->fmt[i]); + + v4l2_i2c_subdev_init(&priv->sd, priv->client, &max9286_subdev_ops); + priv->sd.internal_ops = &max9286_subdev_internal_ops; + priv->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + + v4l2_ctrl_handler_init(&priv->ctrls, 1); + priv->pixelrate = v4l2_ctrl_new_std(&priv->ctrls, + &max9286_ctrl_ops, + V4L2_CID_PIXEL_RATE, + 1, INT_MAX, 1, 50000000); + + priv->sd.ctrl_handler = &priv->ctrls; + ret = priv->ctrls.error; + if (ret) + goto err_async; + + priv->sd.entity.function = MEDIA_ENT_F_VID_IF_BRIDGE; + + priv->pads[MAX9286_SRC_PAD].flags = MEDIA_PAD_FL_SOURCE; + for (i = 0; i < MAX9286_SRC_PAD; i++) + priv->pads[i].flags = MEDIA_PAD_FL_SINK; + ret = media_entity_pads_init(&priv->sd.entity, MAX9286_N_PADS, + priv->pads); + if (ret) + goto err_async; + + ep = fwnode_graph_get_endpoint_by_id(dev_fwnode(dev), MAX9286_SRC_PAD, + 0, 0); + if (!ep) { + dev_err(dev, "Unable to retrieve endpoint on \"port@4\"\n"); + ret = -ENOENT; + goto err_async; + } + priv->sd.fwnode = ep; + + ret = v4l2_async_register_subdev(&priv->sd); + if (ret < 0) { + dev_err(dev, "Unable to register subdevice\n"); + goto err_put_node; + } + + return 0; + +err_put_node: + fwnode_handle_put(ep); +err_async: + max9286_v4l2_notifier_unregister(priv); + + return ret; +} + +static void max9286_v4l2_unregister(struct max9286_priv *priv) +{ + fwnode_handle_put(priv->sd.fwnode); + v4l2_async_unregister_subdev(&priv->sd); + max9286_v4l2_notifier_unregister(priv); +} + +/* ----------------------------------------------------------------------------- + * Probe/Remove + */ + +static int max9286_setup(struct max9286_priv *priv) +{ + /* + * Link ordering values for all enabled links combinations. Orders must + * be assigned sequentially from 0 to the number of enabled links + * without leaving any hole for disabled links. We thus assign orders to + * enabled links first, and use the remaining order values for disabled + * links are all links must have a different order value; + */ + static const u8 link_order[] = { + (3 << 6) | (2 << 4) | (1 << 2) | (0 << 0), /* xxxx */ + (3 << 6) | (2 << 4) | (1 << 2) | (0 << 0), /* xxx0 */ + (3 << 6) | (2 << 4) | (0 << 2) | (1 << 0), /* xx0x */ + (3 << 6) | (2 << 4) | (1 << 2) | (0 << 0), /* xx10 */ + (3 << 6) | (0 << 4) | (2 << 2) | (1 << 0), /* x0xx */ + (3 << 6) | (1 << 4) | (2 << 2) | (0 << 0), /* x1x0 */ + (3 << 6) | (1 << 4) | (0 << 2) | (2 << 0), /* x10x */ + (3 << 6) | (1 << 4) | (1 << 2) | (0 << 0), /* x210 */ + (0 << 6) | (3 << 4) | (2 << 2) | (1 << 0), /* 0xxx */ + (1 << 6) | (3 << 4) | (2 << 2) | (0 << 0), /* 1xx0 */ + (1 << 6) | (3 << 4) | (0 << 2) | (2 << 0), /* 1x0x */ + (2 << 6) | (3 << 4) | (1 << 2) | (0 << 0), /* 2x10 */ + (1 << 6) | (0 << 4) | (3 << 2) | (2 << 0), /* 10xx */ + (2 << 6) | (1 << 4) | (3 << 2) | (0 << 0), /* 21x0 */ + (2 << 6) | (1 << 4) | (0 << 2) | (3 << 0), /* 210x */ + (3 << 6) | (2 << 4) | (1 << 2) | (0 << 0), /* 3210 */ + }; + + /* + * Set the I2C bus speed. + * + * Enable I2C Local Acknowledge during the probe sequences of the camera + * only. This should be disabled after the mux is initialised. + */ + max9286_configure_i2c(priv, true); + + /* + * Reverse channel setup. + * + * - Enable custom reverse channel configuration (through register 0x3f) + * and set the first pulse length to 35 clock cycles. + * - Increase the reverse channel amplitude to 170mV to accommodate the + * high threshold enabled by the serializer driver. + */ + max9286_write(priv, 0x3f, MAX9286_EN_REV_CFG | MAX9286_REV_FLEN(35)); + max9286_write(priv, 0x3b, MAX9286_REV_TRF(1) | MAX9286_REV_AMP(70) | + MAX9286_REV_AMP_X); + usleep_range(2000, 2500); + + /* + * Enable GMSL links, mask unused ones and autodetect link + * used as CSI clock source. + */ + max9286_write(priv, 0x00, MAX9286_MSTLINKSEL_AUTO | priv->route_mask); + max9286_write(priv, 0x0b, link_order[priv->route_mask]); + max9286_write(priv, 0x69, (0xf & ~priv->route_mask)); + + /* + * Video format setup: + * Disable CSI output, VC is set according to Link number. + */ + max9286_write(priv, 0x15, MAX9286_VCTYPE | MAX9286_0X15_RESV); + + /* Enable CSI-2 Lane D0-D3 only, DBL mode, YUV422 8-bit. */ + max9286_write(priv, 0x12, MAX9286_CSIDBL | MAX9286_DBL | + MAX9286_CSILANECNT(priv->csi2_data_lanes) | + MAX9286_DATATYPE_YUV422_8BIT); + + /* Automatic: FRAMESYNC taken from the slowest Link. */ + max9286_write(priv, 0x01, MAX9286_FSYNCMODE_INT_HIZ | + MAX9286_FSYNCMETH_AUTO); + + /* Enable HS/VS encoding, use D14/15 for HS/VS, invert VS. */ + max9286_write(priv, 0x0c, MAX9286_HVEN | MAX9286_INVVS | + MAX9286_HVSRC_D14); + + /* + * The overlap window seems to provide additional validation by tracking + * the delay between vsync and frame sync, generating an error if the + * delay is bigger than the programmed window, though it's not yet clear + * what value should be set. + * + * As it's an optional value and can be disabled, we do so by setting + * a 0 overlap value. + */ + max9286_write(priv, 0x63, 0); + max9286_write(priv, 0x64, 0); + + /* + * Wait for 2ms to allow the link to resynchronize after the + * configuration change. + */ + usleep_range(2000, 5000); + + return 0; +} + +static void max9286_gpio_set(struct gpio_chip *chip, + unsigned int offset, int value) +{ + struct max9286_priv *priv = gpiochip_get_data(chip); + + if (value) + priv->gpio_state |= BIT(offset); + else + priv->gpio_state &= ~BIT(offset); + + max9286_write(priv, 0x0f, MAX9286_0X0F_RESERVED | priv->gpio_state); +} + +static int max9286_gpio_get(struct gpio_chip *chip, unsigned int offset) +{ + struct max9286_priv *priv = gpiochip_get_data(chip); + + return priv->gpio_state & BIT(offset); +} + +static int max9286_register_gpio(struct max9286_priv *priv) +{ + struct device *dev = &priv->client->dev; + struct gpio_chip *gpio = &priv->gpio; + int ret; + + /* Configure the GPIO */ + gpio->label = dev_name(dev); + gpio->parent = dev; + gpio->owner = THIS_MODULE; + gpio->of_node = dev->of_node; + gpio->ngpio = 2; + gpio->base = -1; + gpio->set = max9286_gpio_set; + gpio->get = max9286_gpio_get; + gpio->can_sleep = true; + + /* GPIO values default to high */ + priv->gpio_state = BIT(0) | BIT(1); + + ret = devm_gpiochip_add_data(dev, gpio, priv); + if (ret) + dev_err(dev, "Unable to create gpio_chip\n"); + + return ret; +} + +static int max9286_init(struct device *dev) +{ + struct max9286_priv *priv; + struct i2c_client *client; + int ret; + + client = to_i2c_client(dev); + priv = i2c_get_clientdata(client); + + /* Enable the bus power. */ + ret = regulator_enable(priv->regulator); + if (ret < 0) { + dev_err(&client->dev, "Unable to turn PoC on\n"); + return ret; + } + + ret = max9286_setup(priv); + if (ret) { + dev_err(dev, "Unable to setup max9286\n"); + goto err_regulator; + } + + /* + * Register all V4L2 interactions for the MAX9286 and notifiers for + * any subdevices connected. + */ + ret = max9286_v4l2_register(priv); + if (ret) { + dev_err(dev, "Failed to register with V4L2\n"); + goto err_regulator; + } + + ret = max9286_i2c_mux_init(priv); + if (ret) { + dev_err(dev, "Unable to initialize I2C multiplexer\n"); + goto err_v4l2_register; + } + + /* Leave the mux channels disabled until they are selected. */ + max9286_i2c_mux_close(priv); + + return 0; + +err_v4l2_register: + max9286_v4l2_unregister(priv); +err_regulator: + regulator_disable(priv->regulator); + + return ret; +} + +static void max9286_cleanup_dt(struct max9286_priv *priv) +{ + struct max9286_source *source; + + for_each_source(priv, source) { + fwnode_handle_put(source->fwnode); + source->fwnode = NULL; + } +} + +static int max9286_parse_dt(struct max9286_priv *priv) +{ + struct device *dev = &priv->client->dev; + struct device_node *i2c_mux; + struct device_node *node = NULL; + unsigned int i2c_mux_mask = 0; + + /* Balance the of_node_put() performed by of_find_node_by_name(). */ + of_node_get(dev->of_node); + i2c_mux = of_find_node_by_name(dev->of_node, "i2c-mux"); + if (!i2c_mux) { + dev_err(dev, "Failed to find i2c-mux node\n"); + return -EINVAL; + } + + /* Identify which i2c-mux channels are enabled */ + for_each_child_of_node(i2c_mux, node) { + u32 id = 0; + + of_property_read_u32(node, "reg", &id); + if (id >= MAX9286_NUM_GMSL) + continue; + + if (!of_device_is_available(node)) { + dev_dbg(dev, "Skipping disabled I2C bus port %u\n", id); + continue; + } + + i2c_mux_mask |= BIT(id); + } + of_node_put(node); + of_node_put(i2c_mux); + + /* Parse the endpoints */ + for_each_endpoint_of_node(dev->of_node, node) { + struct max9286_source *source; + struct of_endpoint ep; + + of_graph_parse_endpoint(node, &ep); + dev_dbg(dev, "Endpoint %pOF on port %d", + ep.local_node, ep.port); + + if (ep.port > MAX9286_NUM_GMSL) { + dev_err(dev, "Invalid endpoint %s on port %d", + of_node_full_name(ep.local_node), ep.port); + continue; + } + + /* For the source endpoint just parse the bus configuration. */ + if (ep.port == MAX9286_SRC_PAD) { + struct v4l2_fwnode_endpoint vep = { + .bus_type = V4L2_MBUS_CSI2_DPHY + }; + int ret; + + ret = v4l2_fwnode_endpoint_parse( + of_fwnode_handle(node), &vep); + if (ret) { + of_node_put(node); + return ret; + } + + priv->csi2_data_lanes = + vep.bus.mipi_csi2.num_data_lanes; + + continue; + } + + /* Skip if the corresponding GMSL link is unavailable. */ + if (!(i2c_mux_mask & BIT(ep.port))) + continue; + + if (priv->sources[ep.port].fwnode) { + dev_err(dev, + "Multiple port endpoints are not supported: %d", + ep.port); + + continue; + } + + source = &priv->sources[ep.port]; + source->fwnode = fwnode_graph_get_remote_endpoint( + of_fwnode_handle(node)); + if (!source->fwnode) { + dev_err(dev, + "Endpoint %pOF has no remote endpoint connection\n", + ep.local_node); + + continue; + } + + priv->source_mask |= BIT(ep.port); + priv->nsources++; + } + of_node_put(node); + + priv->route_mask = priv->source_mask; + + return 0; +} + +static int max9286_probe(struct i2c_client *client) +{ + struct max9286_priv *priv; + int ret; + + priv = devm_kzalloc(&client->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + mutex_init(&priv->mutex); + + priv->client = client; + i2c_set_clientdata(client, priv); + + priv->gpiod_pwdn = devm_gpiod_get_optional(&client->dev, "enable", + GPIOD_OUT_HIGH); + if (IS_ERR(priv->gpiod_pwdn)) + return PTR_ERR(priv->gpiod_pwdn); + + gpiod_set_consumer_name(priv->gpiod_pwdn, "max9286-pwdn"); + gpiod_set_value_cansleep(priv->gpiod_pwdn, 1); + + /* Wait at least 4ms before the I2C lines latch to the address */ + if (priv->gpiod_pwdn) + usleep_range(4000, 5000); + + /* + * The MAX9286 starts by default with all ports enabled, we disable all + * ports early to ensure that all channels are disabled if we error out + * and keep the bus consistent. + */ + max9286_i2c_mux_close(priv); + + /* + * The MAX9286 initialises with auto-acknowledge enabled by default. + * This can be invasive to other transactions on the same bus, so + * disable it early. It will be enabled only as and when needed. + */ + max9286_configure_i2c(priv, false); + + ret = max9286_register_gpio(priv); + if (ret) + goto err_powerdown; + + priv->regulator = devm_regulator_get(&client->dev, "poc"); + if (IS_ERR(priv->regulator)) { + if (PTR_ERR(priv->regulator) != -EPROBE_DEFER) + dev_err(&client->dev, + "Unable to get PoC regulator (%ld)\n", + PTR_ERR(priv->regulator)); + ret = PTR_ERR(priv->regulator); + goto err_powerdown; + } + + ret = max9286_parse_dt(priv); + if (ret) + goto err_powerdown; + + ret = max9286_init(&client->dev); + if (ret < 0) + goto err_cleanup_dt; + + return 0; + +err_cleanup_dt: + max9286_cleanup_dt(priv); +err_powerdown: + gpiod_set_value_cansleep(priv->gpiod_pwdn, 0); + + return ret; +} + +static int max9286_remove(struct i2c_client *client) +{ + struct max9286_priv *priv = i2c_get_clientdata(client); + + i2c_mux_del_adapters(priv->mux); + + max9286_v4l2_unregister(priv); + + regulator_disable(priv->regulator); + + gpiod_set_value_cansleep(priv->gpiod_pwdn, 0); + + max9286_cleanup_dt(priv); + + return 0; +} + +static const struct of_device_id max9286_dt_ids[] = { + { .compatible = "maxim,max9286" }, + {}, +}; +MODULE_DEVICE_TABLE(of, max9286_dt_ids); + +static struct i2c_driver max9286_i2c_driver = { + .driver = { + .name = "max9286", + .of_match_table = of_match_ptr(max9286_dt_ids), + }, + .probe_new = max9286_probe, + .remove = max9286_remove, +}; + +module_i2c_driver(max9286_i2c_driver); + +MODULE_DESCRIPTION("Maxim MAX9286 GMSL Deserializer Driver"); +MODULE_AUTHOR("Jacopo Mondi, Kieran Bingham, Laurent Pinchart, Niklas Söderlund, Vladimir Barinov"); +MODULE_LICENSE("GPL"); -- GitLab From e9f817689789e6997dd21fa986e4de950ff1f977 Mon Sep 17 00:00:00 2001 From: Jacopo Mondi Date: Wed, 15 Jul 2020 11:43:41 +0200 Subject: [PATCH 0673/1476] media: dt-bindings: media: i2c: Add bindings for IMI RDACM2x The IMI RDACM20 and IMI RDACM21 are Gigabit Multimedia Serial Link (GMSL) camera capable of transmitting video and I2C control messages on a coax cable physical link for automotive applications. Document their device tree bindings. Signed-off-by: Jacopo Mondi Signed-off-by: Kieran Bingham Reviewed-by: Laurent Pinchart Reviewed-by: Rob Herring Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- .../bindings/media/i2c/imi,rdacm2x-gmsl.yaml | 159 ++++++++++++++++++ .../devicetree/bindings/vendor-prefixes.yaml | 2 + 2 files changed, 161 insertions(+) create mode 100644 Documentation/devicetree/bindings/media/i2c/imi,rdacm2x-gmsl.yaml diff --git a/Documentation/devicetree/bindings/media/i2c/imi,rdacm2x-gmsl.yaml b/Documentation/devicetree/bindings/media/i2c/imi,rdacm2x-gmsl.yaml new file mode 100644 index 0000000000000..5ad4b8c356cf7 --- /dev/null +++ b/Documentation/devicetree/bindings/media/i2c/imi,rdacm2x-gmsl.yaml @@ -0,0 +1,159 @@ +# SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause +# Copyright (C) 2019 Renesas Electronics Corp. +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/media/i2c/imi,rdacm2x-gmsl.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: IMI D&D RDACM20 and RDACM21 Automotive Camera Platforms + +maintainers: + - Jacopo Mondi + - Kieran Bingham + - Laurent Pinchart + - Niklas Söderlund + +description: -| + The IMI D&D RDACM20 and RDACM21 are GMSL-compatible camera designed for + automotive applications. + + The RDACM20 camera module encloses a Maxim Integrated MAX9271 GMSL serializer, + coupled with an OV10635 image sensor and an embedded MCU. Both the MCU and + the image sensor are connected to the serializer local I2C bus and are + accessible by the host SoC by direct addressing. + + The RDACM21 camera module encloses the same serializer, coupled with an + OV10640 image sensor and an OV490 ISP. Only the OV490 ISP is interfaced to + the serializer local I2C bus while the image sensor is not accessible from + the host SoC. + + They both connect to a remote GMSL endpoint through a coaxial cable. + + IMI RDACM20 + +---------------+ +--------------------------------+ + | GMSL | <- Video Stream | <- Video--------\ | + | |< === GMSL Link ====== >|MAX9271<- I2C bus-> <-->OV10635 | + | de-serializer | <- I2C messages -> | \<-->MCU | + +---------------+ +--------------------------------+ + + IMI RDACM21 + +---------------+ +--------------------------------+ + | GMSL | <- Video Stream | <- Video--------\ | + | |< === GMSL Link ====== >|MAX9271<- I2C bus-> <-->OV490 | + | | <- I2C messages -> | | | + | de-serializer | | OV10640 <-------| | + +---------------+ +--------------------------------+ + + Both camera modules serialize video data generated by the embedded camera + sensor on the GMSL serial channel to a remote GMSL de-serializer. They also + receive and transmit I2C messages encapsulated and transmitted on the GMSL + bidirectional control channel. + + All I2C traffic received on the GMSL link not directed to the serializer is + propagated on the local I2C bus to the remote device there connected. All the + I2C traffic generated on the local I2C bus not directed to the serializer is + propagated to the remote de-serializer encapsulated in the GMSL control + channel. + + The RDACM20 and RDACM21 DT node should be a direct child of the GMSL + deserializer's I2C bus corresponding to the GMSL link that the camera is + attached to. + +properties: + '#address-cells': + const: 1 + + '#size-cells': + const: 0 + + compatible: + enum: + - imi,rdacm20 + - imi,rdacm21 + + reg: + description: -| + I2C device addresses, the first to be assigned to the serializer, the + following ones to be assigned to the remote devices. + + For RDACM20 the second entry of the property is assigned to the + OV10635 image sensor and the optional third one to the embedded MCU. + + For RDACM21 the second entry is assigned to the OV490 ISP and the optional + third one ignored. + + minItems: 2 + maxItems: 3 + + port: + type: object + additionalProperties: false + description: -| + Connection to the remote GMSL endpoint are modelled using the OF graph + bindings in accordance with the video interface bindings defined in + Documentation/devicetree/bindings/media/video-interfaces.txt. + + The device node contains a single "port" child node with a single + "endpoint" sub-device. + + properties: + endpoint: + type: object + additionalProperties: false + + properties: + remote-endpoint: + description: -| + phandle to the remote GMSL endpoint sub-node in the remote node + port. + maxItems: 1 + + required: + - remote-endpoint + + required: + - endpoint + +required: + - compatible + - reg + - port + +examples: + - | + i2c@e66d8000 { + #address-cells = <1>; + #size-cells = <0>; + + reg = <0 0xe66d8000>; + + camera@31 { + compatible = "imi,rdacm20"; + reg = <0x31>, <0x41>, <0x51>; + + port { + rdacm20_out0: endpoint { + remote-endpoint = <&max9286_in0>; + }; + }; + }; + }; + + - | + i2c@e66d8000 { + #address-cells = <1>; + #size-cells = <0>; + + reg = <0 0xe66d8000>; + + camera@31 { + compatible = "imi,rdacm21"; + reg = <0x31>, <0x41>; + + port { + rdacm21_out0: endpoint { + remote-endpoint = <&max9286_in0>; + }; + }; + }; + }; diff --git a/Documentation/devicetree/bindings/vendor-prefixes.yaml b/Documentation/devicetree/bindings/vendor-prefixes.yaml index 9aeab66be85fc..8261ede298f86 100644 --- a/Documentation/devicetree/bindings/vendor-prefixes.yaml +++ b/Documentation/devicetree/bindings/vendor-prefixes.yaml @@ -469,6 +469,8 @@ patternProperties: description: ILI Technology Corporation (ILITEK) "^img,.*": description: Imagination Technologies Ltd. + "^imi,.*": + description: Integrated Micro-Electronics Inc. "^incircuit,.*": description: In-Circuit GmbH "^inet-tek,.*": -- GitLab From 34009bffc1c6e6cf7f6a2a85e8e184c6e06193fc Mon Sep 17 00:00:00 2001 From: Jacopo Mondi Date: Fri, 12 Jun 2020 16:47:13 +0200 Subject: [PATCH 0674/1476] media: i2c: Add RDACM20 driver MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The RDACM20 is a GMSL camera supporting 1280x800 resolution images developed by IMI based on an Omnivision 10635 sensor and a Maxim MAX9271 GMSL serializer. The GMSL link carries power, control (I2C) and video data over a single coax cable. Signed-off-by: Jacopo Mondi Signed-off-by: Laurent Pinchart Signed-off-by: Niklas Söderlund Signed-off-by: Kieran Bingham Reviewed-by: Rob Herring Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- MAINTAINERS | 12 + drivers/media/i2c/Kconfig | 13 + drivers/media/i2c/Makefile | 2 + drivers/media/i2c/max9271.c | 341 ++++++++++++++++++ drivers/media/i2c/max9271.h | 224 ++++++++++++ drivers/media/i2c/rdacm20.c | 667 ++++++++++++++++++++++++++++++++++++ 6 files changed, 1259 insertions(+) create mode 100644 drivers/media/i2c/max9271.c create mode 100644 drivers/media/i2c/max9271.h create mode 100644 drivers/media/i2c/rdacm20.c diff --git a/MAINTAINERS b/MAINTAINERS index 3bcedb12ab928..51c4cca11e0bb 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -14412,6 +14412,18 @@ S: Supported T: git git://git.kernel.org/pub/scm/linux/kernel/git/paulmck/linux-rcu.git dev F: tools/testing/selftests/rcutorture +RDACM20 Camera Sensor +M: Jacopo Mondi +M: Kieran Bingham +M: Laurent Pinchart +M: Niklas Söderlund +L: linux-media@vger.kernel.org +S: Maintained +F: Documentation/devicetree/bindings/media/i2c/imi,rdacm2x-gmsl.yaml +F: drivers/media/i2c/rdacm20.c +F: drivers/media/i2c/max9271.c +F: drivers/media/i2c/max9271.h + RDC R-321X SoC M: Florian Fainelli S: Maintained diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig index 8471a6c438d47..48ae60a2c6030 100644 --- a/drivers/media/i2c/Kconfig +++ b/drivers/media/i2c/Kconfig @@ -1171,6 +1171,19 @@ config VIDEO_NOON010PC30 source "drivers/media/i2c/m5mols/Kconfig" +config VIDEO_RDACM20 + tristate "IMI RDACM20 camera support" + depends on I2C + select V4L2_FWNODE + select VIDEO_V4L2_SUBDEV_API + select MEDIA_CONTROLLER + help + This driver supports the IMI RDACM20 GMSL camera, used in + ADAS systems. + + This camera should be used in conjunction with a GMSL + deserialiser such as the MAX9286. + config VIDEO_RJ54N1 tristate "Sharp RJ54N1CB0C sensor support" depends on I2C && VIDEO_V4L2 diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile index 9091f4918d4bf..f0a77473979d8 100644 --- a/drivers/media/i2c/Makefile +++ b/drivers/media/i2c/Makefile @@ -120,6 +120,8 @@ obj-$(CONFIG_VIDEO_IMX290) += imx290.o obj-$(CONFIG_VIDEO_IMX319) += imx319.o obj-$(CONFIG_VIDEO_IMX355) += imx355.o obj-$(CONFIG_VIDEO_MAX9286) += max9286.o +rdacm20-camera_module-objs := rdacm20.o max9271.o +obj-$(CONFIG_VIDEO_RDACM20) += rdacm20-camera_module.o obj-$(CONFIG_VIDEO_ST_MIPID02) += st-mipid02.o obj-$(CONFIG_SDR_MAX2175) += max2175.o diff --git a/drivers/media/i2c/max9271.c b/drivers/media/i2c/max9271.c new file mode 100644 index 0000000000000..0f6f7a092a463 --- /dev/null +++ b/drivers/media/i2c/max9271.c @@ -0,0 +1,341 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2017-2020 Jacopo Mondi + * Copyright (C) 2017-2020 Kieran Bingham + * Copyright (C) 2017-2020 Laurent Pinchart + * Copyright (C) 2017-2020 Niklas Söderlund + * Copyright (C) 2016 Renesas Electronics Corporation + * Copyright (C) 2015 Cogent Embedded, Inc. + * + * This file exports functions to control the Maxim MAX9271 GMSL serializer + * chip. This is not a self-contained driver, as MAX9271 is usually embedded in + * camera modules with at least one image sensor and optional additional + * components, such as uController units or ISPs/DSPs. + * + * Drivers for the camera modules (i.e. rdacm20/21) are expected to use + * functions exported from this library driver to maximize code re-use. + */ + +#include +#include + +#include "max9271.h" + +static int max9271_read(struct max9271_device *dev, u8 reg) +{ + int ret; + + dev_dbg(&dev->client->dev, "%s(0x%02x)\n", __func__, reg); + + ret = i2c_smbus_read_byte_data(dev->client, reg); + if (ret < 0) + dev_dbg(&dev->client->dev, + "%s: register 0x%02x read failed (%d)\n", + __func__, reg, ret); + + return ret; +} + +static int max9271_write(struct max9271_device *dev, u8 reg, u8 val) +{ + int ret; + + dev_dbg(&dev->client->dev, "%s(0x%02x, 0x%02x)\n", __func__, reg, val); + + ret = i2c_smbus_write_byte_data(dev->client, reg, val); + if (ret < 0) + dev_err(&dev->client->dev, + "%s: register 0x%02x write failed (%d)\n", + __func__, reg, ret); + + return ret; +} + +/* + * max9271_pclk_detect() - Detect valid pixel clock from image sensor + * + * Wait up to 10ms for a valid pixel clock. + * + * Returns 0 for success, < 0 for pixel clock not properly detected + */ +static int max9271_pclk_detect(struct max9271_device *dev) +{ + unsigned int i; + int ret; + + for (i = 0; i < 100; i++) { + ret = max9271_read(dev, 0x15); + if (ret < 0) + return ret; + + if (ret & MAX9271_PCLKDET) + return 0; + + usleep_range(50, 100); + } + + dev_err(&dev->client->dev, "Unable to detect valid pixel clock\n"); + + return -EIO; +} + +int max9271_set_serial_link(struct max9271_device *dev, bool enable) +{ + int ret; + u8 val = MAX9271_REVCCEN | MAX9271_FWDCCEN; + + if (enable) { + ret = max9271_pclk_detect(dev); + if (ret) + return ret; + + val |= MAX9271_SEREN; + } else { + val |= MAX9271_CLINKEN; + } + + /* + * The serializer temporarily disables the reverse control channel for + * 350µs after starting/stopping the forward serial link, but the + * deserializer synchronization time isn't clearly documented. + * + * According to the serializer datasheet we should wait 3ms, while + * according to the deserializer datasheet we should wait 5ms. + * + * Short delays here appear to show bit-errors in the writes following. + * Therefore a conservative delay seems best here. + */ + max9271_write(dev, 0x04, val); + usleep_range(5000, 8000); + + return 0; +} +EXPORT_SYMBOL_GPL(max9271_set_serial_link); + +int max9271_configure_i2c(struct max9271_device *dev, u8 i2c_config) +{ + int ret; + + ret = max9271_write(dev, 0x0d, i2c_config); + if (ret) + return ret; + + /* The delay required after an I2C bus configuration change is not + * characterized in the serializer manual. Sleep up to 5msec to + * stay safe. + */ + usleep_range(3500, 5000); + + return 0; +} +EXPORT_SYMBOL_GPL(max9271_configure_i2c); + +int max9271_set_high_threshold(struct max9271_device *dev, bool enable) +{ + int ret; + + ret = max9271_read(dev, 0x08); + if (ret < 0) + return ret; + + /* + * Enable or disable reverse channel high threshold to increase + * immunity to power supply noise. + */ + max9271_write(dev, 0x08, enable ? ret | BIT(0) : ret & ~BIT(0)); + usleep_range(2000, 2500); + + return 0; +} +EXPORT_SYMBOL_GPL(max9271_set_high_threshold); + +int max9271_configure_gmsl_link(struct max9271_device *dev) +{ + /* + * Configure the GMSL link: + * + * - Double input mode, high data rate, 24-bit mode + * - Latch input data on PCLKIN rising edge + * - Enable HS/VS encoding + * - 1-bit parity error detection + * + * TODO: Make the GMSL link configuration parametric. + */ + max9271_write(dev, 0x07, MAX9271_DBL | MAX9271_HVEN | + MAX9271_EDC_1BIT_PARITY); + usleep_range(5000, 8000); + + /* + * Adjust spread spectrum to +4% and auto-detect pixel clock + * and serial link rate. + */ + max9271_write(dev, 0x02, MAX9271_SPREAD_SPECT_4 | MAX9271_R02_RES | + MAX9271_PCLK_AUTODETECT | MAX9271_SERIAL_AUTODETECT); + usleep_range(5000, 8000); + + return 0; +} +EXPORT_SYMBOL_GPL(max9271_configure_gmsl_link); + +int max9271_set_gpios(struct max9271_device *dev, u8 gpio_mask) +{ + int ret; + + ret = max9271_read(dev, 0x0f); + if (ret < 0) + return 0; + + ret |= gpio_mask; + ret = max9271_write(dev, 0x0f, ret); + if (ret < 0) { + dev_err(&dev->client->dev, "Failed to set gpio (%d)\n", ret); + return ret; + } + + usleep_range(3500, 5000); + + return 0; +} +EXPORT_SYMBOL_GPL(max9271_set_gpios); + +int max9271_clear_gpios(struct max9271_device *dev, u8 gpio_mask) +{ + int ret; + + ret = max9271_read(dev, 0x0f); + if (ret < 0) + return 0; + + ret &= ~gpio_mask; + ret = max9271_write(dev, 0x0f, ret); + if (ret < 0) { + dev_err(&dev->client->dev, "Failed to clear gpio (%d)\n", ret); + return ret; + } + + usleep_range(3500, 5000); + + return 0; +} +EXPORT_SYMBOL_GPL(max9271_clear_gpios); + +int max9271_enable_gpios(struct max9271_device *dev, u8 gpio_mask) +{ + int ret; + + ret = max9271_read(dev, 0x0f); + if (ret < 0) + return 0; + + /* BIT(0) reserved: GPO is always enabled. */ + ret |= gpio_mask | BIT(0); + ret = max9271_write(dev, 0x0e, ret); + if (ret < 0) { + dev_err(&dev->client->dev, "Failed to enable gpio (%d)\n", ret); + return ret; + } + + usleep_range(3500, 5000); + + return 0; +} +EXPORT_SYMBOL_GPL(max9271_enable_gpios); + +int max9271_disable_gpios(struct max9271_device *dev, u8 gpio_mask) +{ + int ret; + + ret = max9271_read(dev, 0x0f); + if (ret < 0) + return 0; + + /* BIT(0) reserved: GPO cannot be disabled */ + ret &= (~gpio_mask | BIT(0)); + ret = max9271_write(dev, 0x0e, ret); + if (ret < 0) { + dev_err(&dev->client->dev, "Failed to disable gpio (%d)\n", ret); + return ret; + } + + usleep_range(3500, 5000); + + return 0; +} +EXPORT_SYMBOL_GPL(max9271_disable_gpios); + +int max9271_verify_id(struct max9271_device *dev) +{ + int ret; + + ret = max9271_read(dev, 0x1e); + if (ret < 0) { + dev_err(&dev->client->dev, "MAX9271 ID read failed (%d)\n", + ret); + return ret; + } + + if (ret != MAX9271_ID) { + dev_err(&dev->client->dev, "MAX9271 ID mismatch (0x%02x)\n", + ret); + return -ENXIO; + } + + return 0; +} +EXPORT_SYMBOL_GPL(max9271_verify_id); + +int max9271_set_address(struct max9271_device *dev, u8 addr) +{ + int ret; + + ret = max9271_write(dev, 0x00, addr << 1); + if (ret < 0) { + dev_err(&dev->client->dev, + "MAX9271 I2C address change failed (%d)\n", ret); + return ret; + } + usleep_range(3500, 5000); + + return 0; +} +EXPORT_SYMBOL_GPL(max9271_set_address); + +int max9271_set_deserializer_address(struct max9271_device *dev, u8 addr) +{ + int ret; + + ret = max9271_write(dev, 0x01, addr << 1); + if (ret < 0) { + dev_err(&dev->client->dev, + "MAX9271 deserializer address set failed (%d)\n", ret); + return ret; + } + usleep_range(3500, 5000); + + return 0; +} +EXPORT_SYMBOL_GPL(max9271_set_deserializer_address); + +int max9271_set_translation(struct max9271_device *dev, u8 source, u8 dest) +{ + int ret; + + ret = max9271_write(dev, 0x09, source << 1); + if (ret < 0) { + dev_err(&dev->client->dev, + "MAX9271 I2C translation setup failed (%d)\n", ret); + return ret; + } + usleep_range(3500, 5000); + + ret = max9271_write(dev, 0x0a, dest << 1); + if (ret < 0) { + dev_err(&dev->client->dev, + "MAX9271 I2C translation setup failed (%d)\n", ret); + return ret; + } + usleep_range(3500, 5000); + + return 0; +} +EXPORT_SYMBOL_GPL(max9271_set_translation); diff --git a/drivers/media/i2c/max9271.h b/drivers/media/i2c/max9271.h new file mode 100644 index 0000000000000..d78fb21441e99 --- /dev/null +++ b/drivers/media/i2c/max9271.h @@ -0,0 +1,224 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (C) 2017-2020 Jacopo Mondi + * Copyright (C) 2017-2020 Kieran Bingham + * Copyright (C) 2017-2020 Laurent Pinchart + * Copyright (C) 2017-2020 Niklas Söderlund + * Copyright (C) 2016 Renesas Electronics Corporation + * Copyright (C) 2015 Cogent Embedded, Inc. + */ + +#include + +#define MAX9271_DEFAULT_ADDR 0x40 + +/* Register 0x02 */ +#define MAX9271_SPREAD_SPECT_0 (0 << 5) +#define MAX9271_SPREAD_SPECT_05 (1 << 5) +#define MAX9271_SPREAD_SPECT_15 (2 << 5) +#define MAX9271_SPREAD_SPECT_1 (5 << 5) +#define MAX9271_SPREAD_SPECT_2 (3 << 5) +#define MAX9271_SPREAD_SPECT_3 (6 << 5) +#define MAX9271_SPREAD_SPECT_4 (7 << 5) +#define MAX9271_R02_RES BIT(4) +#define MAX9271_PCLK_AUTODETECT (3 << 2) +#define MAX9271_SERIAL_AUTODETECT (0x03) +/* Register 0x04 */ +#define MAX9271_SEREN BIT(7) +#define MAX9271_CLINKEN BIT(6) +#define MAX9271_PRBSEN BIT(5) +#define MAX9271_SLEEP BIT(4) +#define MAX9271_INTTYPE_I2C (0 << 2) +#define MAX9271_INTTYPE_UART (1 << 2) +#define MAX9271_INTTYPE_NONE (2 << 2) +#define MAX9271_REVCCEN BIT(1) +#define MAX9271_FWDCCEN BIT(0) +/* Register 0x07 */ +#define MAX9271_DBL BIT(7) +#define MAX9271_DRS BIT(6) +#define MAX9271_BWS BIT(5) +#define MAX9271_ES BIT(4) +#define MAX9271_HVEN BIT(2) +#define MAX9271_EDC_1BIT_PARITY (0 << 0) +#define MAX9271_EDC_6BIT_CRC (1 << 0) +#define MAX9271_EDC_6BIT_HAMMING (2 << 0) +/* Register 0x08 */ +#define MAX9271_INVVS BIT(7) +#define MAX9271_INVHS BIT(6) +#define MAX9271_REV_LOGAIN BIT(3) +#define MAX9271_REV_HIVTH BIT(0) +/* Register 0x09 */ +#define MAX9271_ID 0x09 +/* Register 0x0d */ +#define MAX9271_I2CLOCACK BIT(7) +#define MAX9271_I2CSLVSH_1046NS_469NS (3 << 5) +#define MAX9271_I2CSLVSH_938NS_352NS (2 << 5) +#define MAX9271_I2CSLVSH_469NS_234NS (1 << 5) +#define MAX9271_I2CSLVSH_352NS_117NS (0 << 5) +#define MAX9271_I2CMSTBT_837KBPS (7 << 2) +#define MAX9271_I2CMSTBT_533KBPS (6 << 2) +#define MAX9271_I2CMSTBT_339KBPS (5 << 2) +#define MAX9271_I2CMSTBT_173KBPS (4 << 2) +#define MAX9271_I2CMSTBT_105KBPS (3 << 2) +#define MAX9271_I2CMSTBT_84KBPS (2 << 2) +#define MAX9271_I2CMSTBT_28KBPS (1 << 2) +#define MAX9271_I2CMSTBT_8KBPS (0 << 2) +#define MAX9271_I2CSLVTO_NONE (3 << 0) +#define MAX9271_I2CSLVTO_1024US (2 << 0) +#define MAX9271_I2CSLVTO_256US (1 << 0) +#define MAX9271_I2CSLVTO_64US (0 << 0) +/* Register 0x0f */ +#define MAX9271_GPIO5OUT BIT(5) +#define MAX9271_GPIO4OUT BIT(4) +#define MAX9271_GPIO3OUT BIT(3) +#define MAX9271_GPIO2OUT BIT(2) +#define MAX9271_GPIO1OUT BIT(1) +#define MAX9271_GPO BIT(0) +/* Register 0x15 */ +#define MAX9271_PCLKDET BIT(0) + +/** + * struct max9271_device - max9271 device + * @client: The i2c client for the max9271 instance + */ +struct max9271_device { + struct i2c_client *client; +}; + +/** + * max9271_set_serial_link() - Enable/disable serial link + * @dev: The max9271 device + * @enable: Serial link enable/disable flag + * + * Return 0 on success or a negative error code on failure + */ +int max9271_set_serial_link(struct max9271_device *dev, bool enable); + +/** + * max9271_configure_i2c() - Configure I2C bus parameters + * @dev: The max9271 device + * @i2c_config: The I2C bus configuration bit mask + * + * Configure MAX9271 I2C interface. The bus configuration provided in the + * @i2c_config parameter shall be assembled using bit values defined by the + * MAX9271_I2C* macros. + * + * Return 0 on success or a negative error code on failure + */ +int max9271_configure_i2c(struct max9271_device *dev, u8 i2c_config); + +/** + * max9271_set_high_threshold() - Enable or disable reverse channel high + * threshold + * @dev: The max9271 device + * @enable: High threshold enable/disable flag + * + * Return 0 on success or a negative error code on failure + */ +int max9271_set_high_threshold(struct max9271_device *dev, bool enable); + +/** + * max9271_configure_gmsl_link() - Configure the GMSL link + * @dev: The max9271 device + * + * FIXME: the GMSL link configuration is currently hardcoded and performed + * by programming registers 0x04, 0x07 and 0x02. + * + * Return 0 on success or a negative error code on failure + */ +int max9271_configure_gmsl_link(struct max9271_device *dev); + +/** + * max9271_set_gpios() - Set gpio lines to physical high value + * @dev: The max9271 device + * @gpio_mask: The mask of gpio lines to set to high value + * + * The @gpio_mask parameter shall be assembled using the MAX9271_GP[IO|O]* + * bit values. + * + * Return 0 on success or a negative error code on failure + */ +int max9271_set_gpios(struct max9271_device *dev, u8 gpio_mask); + +/** + * max9271_clear_gpios() - Set gpio lines to physical low value + * @dev: The max9271 device + * @gpio_mask: The mask of gpio lines to set to low value + * + * The @gpio_mask parameter shall be assembled using the MAX9271_GP[IO|O]* + * bit values. + * + * Return 0 on success or a negative error code on failure + */ +int max9271_clear_gpios(struct max9271_device *dev, u8 gpio_mask); + +/** + * max9271_enable_gpios() - Enable gpio lines + * @dev: The max9271 device + * @gpio_mask: The mask of gpio lines to enable + * + * The @gpio_mask parameter shall be assembled using the MAX9271_GPIO* + * bit values. GPO line is always enabled by default. + * + * Return 0 on success or a negative error code on failure + */ +int max9271_enable_gpios(struct max9271_device *dev, u8 gpio_mask); + +/** + * max9271_disable_gpios() - Disable gpio lines + * @dev: The max9271 device + * @gpio_mask: The mask of gpio lines to disable + * + * The @gpio_mask parameter shall be assembled using the MAX9271_GPIO* + * bit values. GPO line is always enabled by default and cannot be disabled. + * + * Return 0 on success or a negative error code on failure + */ +int max9271_disable_gpios(struct max9271_device *dev, u8 gpio_mask); + +/** + * max9271_verify_id() - Read and verify MAX9271 id + * @dev: The max9271 device + * + * Return 0 on success or a negative error code on failure + */ +int max9271_verify_id(struct max9271_device *dev); + +/** + * max9271_set_address() - Program a new I2C address + * @dev: The max9271 device + * @addr: The new I2C address in 7-bit format + * + * This function only takes care of programming the new I2C address @addr to + * in the MAX9271 chip registers, it is responsiblity of the caller to set + * the i2c address client to the @addr value to be able to communicate with + * the MAX9271 chip using the I2C framework APIs after this function returns. + * + * Return 0 on success or a negative error code on failure + */ +int max9271_set_address(struct max9271_device *dev, u8 addr); + +/** + * max9271_set_deserializer_address() - Program the remote deserializer address + * @dev: The max9271 device + * @addr: The deserializer I2C address in 7-bit format + * + * Return 0 on success or a negative error code on failure + */ +int max9271_set_deserializer_address(struct max9271_device *dev, u8 addr); + +/** + * max9271_set_translation() - Program I2C address translation + * @dev: The max9271 device + * @source: The I2C source address + * @dest: The I2C destination address + * + * Program address translation from @source to @dest. This is required to + * communicate with local devices that do not support address reprogramming. + * + * TODO: The device supports translation of two address, this function currently + * supports a single one. + * + * Return 0 on success or a negative error code on failure + */ +int max9271_set_translation(struct max9271_device *dev, u8 source, u8 dest); diff --git a/drivers/media/i2c/rdacm20.c b/drivers/media/i2c/rdacm20.c new file mode 100644 index 0000000000000..1ed928c4ca70f --- /dev/null +++ b/drivers/media/i2c/rdacm20.c @@ -0,0 +1,667 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * IMI RDACM20 GMSL Camera Driver + * + * Copyright (C) 2017-2020 Jacopo Mondi + * Copyright (C) 2017-2020 Kieran Bingham + * Copyright (C) 2017-2019 Laurent Pinchart + * Copyright (C) 2017-2019 Niklas Söderlund + * Copyright (C) 2016 Renesas Electronics Corporation + * Copyright (C) 2015 Cogent Embedded, Inc. + */ + +/* + * The camera is made of an Omnivision OV10635 sensor connected to a Maxim + * MAX9271 GMSL serializer. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "max9271.h" + +#define OV10635_I2C_ADDRESS 0x30 + +#define OV10635_SOFTWARE_RESET 0x0103 +#define OV10635_PID 0x300a +#define OV10635_VER 0x300b +#define OV10635_SC_CMMN_SCCB_ID 0x300c +#define OV10635_SC_CMMN_SCCB_ID_SELECT BIT(0) +#define OV10635_VERSION 0xa635 + +#define OV10635_WIDTH 1280 +#define OV10635_HEIGHT 800 + +/* VTS = PCLK / FPS / HTS / 2 (= 88MHz / 1572 / 30 / 2) */ +#define OV10635_HTS 1572 +/* FPS = 29,9998 */ +#define OV10635_VTS 933 + +/* + * As the drivers supports a single MEDIA_BUS_FMT_UYVY8_2X8 format we + * can harcode the pixel rate. + * + * PCLK is fed through the system clock, programmed @88MHz. + * MEDIA_BUS_FMT_UYVY8_2X8 format = 2 samples per pixel. + * + * Pixelrate = PCLK / 2 + * FPS = (OV10635_VTS * OV10635_HTS) / PixelRate + * = 29,9998 + */ +#define OV10635_PIXEL_RATE (44000000) + +static const struct ov10635_reg { + u16 reg; + u8 val; +} ov10635_regs_wizard[] = { + { 0x301b, 0xff }, { 0x301c, 0xff }, { 0x301a, 0xff }, { 0x3011, 0x42 }, + { 0x6900, 0x0c }, { 0x6901, 0x19 }, { 0x3503, 0x10 }, { 0x3025, 0x03 }, + { 0x3003, 0x16 }, { 0x3004, 0x30 }, { 0x3005, 0x40 }, { 0x3006, 0x91 }, + { 0x3600, 0x74 }, { 0x3601, 0x2b }, { 0x3612, 0x00 }, { 0x3611, 0x67 }, + { 0x3633, 0xca }, { 0x3602, 0xaf }, { 0x3603, 0x04 }, { 0x3630, 0x28 }, + { 0x3631, 0x16 }, { 0x3714, 0x10 }, { 0x371d, 0x01 }, { 0x4300, 0x3a }, + { 0x3007, 0x01 }, { 0x3024, 0x03 }, { 0x3020, 0x0a }, { 0x3702, 0x0d }, + { 0x3703, 0x20 }, { 0x3704, 0x15 }, { 0x3709, 0xa8 }, { 0x370c, 0xc7 }, + { 0x370d, 0x80 }, { 0x3712, 0x00 }, { 0x3713, 0x20 }, { 0x3715, 0x04 }, + { 0x381d, 0x40 }, { 0x381c, 0x00 }, { 0x3822, 0x50 }, { 0x3824, 0x10 }, + { 0x3815, 0x8c }, { 0x3804, 0x05 }, { 0x3805, 0x1f }, { 0x3800, 0x00 }, + { 0x3801, 0x00 }, { 0x3806, 0x03 }, { 0x3807, 0x28 }, { 0x3802, 0x00 }, + { 0x3803, 0x07 }, { 0x3808, 0x05 }, { 0x3809, 0x00 }, { 0x380a, 0x03 }, + { 0x380b, 0x20 }, { 0x380c, OV10635_HTS >> 8 }, + { 0x380d, OV10635_HTS & 0xff }, { 0x380e, OV10635_VTS >> 8 }, + { 0x380f, OV10635_VTS & 0xff }, { 0x3813, 0x02 }, { 0x3811, 0x08 }, + { 0x381f, 0x0c }, { 0x3819, 0x04 }, { 0x3804, 0x01 }, { 0x3805, 0x00 }, + { 0x3828, 0x03 }, { 0x3829, 0x10 }, { 0x382a, 0x10 }, { 0x3621, 0x63 }, + { 0x5005, 0x08 }, { 0x56d5, 0x00 }, { 0x56d6, 0x80 }, { 0x56d7, 0x00 }, + { 0x56d8, 0x00 }, { 0x56d9, 0x00 }, { 0x56da, 0x80 }, { 0x56db, 0x00 }, + { 0x56dc, 0x00 }, { 0x56e8, 0x00 }, { 0x56e9, 0x7f }, { 0x56ea, 0x00 }, + { 0x56eb, 0x7f }, { 0x5100, 0x00 }, { 0x5101, 0x80 }, { 0x5102, 0x00 }, + { 0x5103, 0x80 }, { 0x5104, 0x00 }, { 0x5105, 0x80 }, { 0x5106, 0x00 }, + { 0x5107, 0x80 }, { 0x5108, 0x00 }, { 0x5109, 0x00 }, { 0x510a, 0x00 }, + { 0x510b, 0x00 }, { 0x510c, 0x00 }, { 0x510d, 0x00 }, { 0x510e, 0x00 }, + { 0x510f, 0x00 }, { 0x5110, 0x00 }, { 0x5111, 0x80 }, { 0x5112, 0x00 }, + { 0x5113, 0x80 }, { 0x5114, 0x00 }, { 0x5115, 0x80 }, { 0x5116, 0x00 }, + { 0x5117, 0x80 }, { 0x5118, 0x00 }, { 0x5119, 0x00 }, { 0x511a, 0x00 }, + { 0x511b, 0x00 }, { 0x511c, 0x00 }, { 0x511d, 0x00 }, { 0x511e, 0x00 }, + { 0x511f, 0x00 }, { 0x56d0, 0x00 }, { 0x5006, 0x04 }, { 0x5608, 0x05 }, + { 0x52d7, 0x06 }, { 0x528d, 0x08 }, { 0x5293, 0x12 }, { 0x52d3, 0x12 }, + { 0x5288, 0x06 }, { 0x5289, 0x20 }, { 0x52c8, 0x06 }, { 0x52c9, 0x20 }, + { 0x52cd, 0x04 }, { 0x5381, 0x00 }, { 0x5382, 0xff }, { 0x5589, 0x76 }, + { 0x558a, 0x47 }, { 0x558b, 0xef }, { 0x558c, 0xc9 }, { 0x558d, 0x49 }, + { 0x558e, 0x30 }, { 0x558f, 0x67 }, { 0x5590, 0x3f }, { 0x5591, 0xf0 }, + { 0x5592, 0x10 }, { 0x55a2, 0x6d }, { 0x55a3, 0x55 }, { 0x55a4, 0xc3 }, + { 0x55a5, 0xb5 }, { 0x55a6, 0x43 }, { 0x55a7, 0x38 }, { 0x55a8, 0x5f }, + { 0x55a9, 0x4b }, { 0x55aa, 0xf0 }, { 0x55ab, 0x10 }, { 0x5581, 0x52 }, + { 0x5300, 0x01 }, { 0x5301, 0x00 }, { 0x5302, 0x00 }, { 0x5303, 0x0e }, + { 0x5304, 0x00 }, { 0x5305, 0x0e }, { 0x5306, 0x00 }, { 0x5307, 0x36 }, + { 0x5308, 0x00 }, { 0x5309, 0xd9 }, { 0x530a, 0x00 }, { 0x530b, 0x0f }, + { 0x530c, 0x00 }, { 0x530d, 0x2c }, { 0x530e, 0x00 }, { 0x530f, 0x59 }, + { 0x5310, 0x00 }, { 0x5311, 0x7b }, { 0x5312, 0x00 }, { 0x5313, 0x22 }, + { 0x5314, 0x00 }, { 0x5315, 0xd5 }, { 0x5316, 0x00 }, { 0x5317, 0x13 }, + { 0x5318, 0x00 }, { 0x5319, 0x18 }, { 0x531a, 0x00 }, { 0x531b, 0x26 }, + { 0x531c, 0x00 }, { 0x531d, 0xdc }, { 0x531e, 0x00 }, { 0x531f, 0x02 }, + { 0x5320, 0x00 }, { 0x5321, 0x24 }, { 0x5322, 0x00 }, { 0x5323, 0x56 }, + { 0x5324, 0x00 }, { 0x5325, 0x85 }, { 0x5326, 0x00 }, { 0x5327, 0x20 }, + { 0x5609, 0x01 }, { 0x560a, 0x40 }, { 0x560b, 0x01 }, { 0x560c, 0x40 }, + { 0x560d, 0x00 }, { 0x560e, 0xfa }, { 0x560f, 0x00 }, { 0x5610, 0xfa }, + { 0x5611, 0x02 }, { 0x5612, 0x80 }, { 0x5613, 0x02 }, { 0x5614, 0x80 }, + { 0x5615, 0x01 }, { 0x5616, 0x2c }, { 0x5617, 0x01 }, { 0x5618, 0x2c }, + { 0x563b, 0x01 }, { 0x563c, 0x01 }, { 0x563d, 0x01 }, { 0x563e, 0x01 }, + { 0x563f, 0x03 }, { 0x5640, 0x03 }, { 0x5641, 0x03 }, { 0x5642, 0x05 }, + { 0x5643, 0x09 }, { 0x5644, 0x05 }, { 0x5645, 0x05 }, { 0x5646, 0x05 }, + { 0x5647, 0x05 }, { 0x5651, 0x00 }, { 0x5652, 0x80 }, { 0x521a, 0x01 }, + { 0x521b, 0x03 }, { 0x521c, 0x06 }, { 0x521d, 0x0a }, { 0x521e, 0x0e }, + { 0x521f, 0x12 }, { 0x5220, 0x16 }, { 0x5223, 0x02 }, { 0x5225, 0x04 }, + { 0x5227, 0x08 }, { 0x5229, 0x0c }, { 0x522b, 0x12 }, { 0x522d, 0x18 }, + { 0x522f, 0x1e }, { 0x5241, 0x04 }, { 0x5242, 0x01 }, { 0x5243, 0x03 }, + { 0x5244, 0x06 }, { 0x5245, 0x0a }, { 0x5246, 0x0e }, { 0x5247, 0x12 }, + { 0x5248, 0x16 }, { 0x524a, 0x03 }, { 0x524c, 0x04 }, { 0x524e, 0x08 }, + { 0x5250, 0x0c }, { 0x5252, 0x12 }, { 0x5254, 0x18 }, { 0x5256, 0x1e }, + /* fifo_line_length = 2*hts */ + { 0x4606, (2 * OV10635_HTS) >> 8 }, { 0x4607, (2 * OV10635_HTS) & 0xff }, + /* fifo_hsync_start = 2*(hts - xres) */ + { 0x460a, (2 * (OV10635_HTS - OV10635_WIDTH)) >> 8 }, + { 0x460b, (2 * (OV10635_HTS - OV10635_WIDTH)) & 0xff }, + { 0x460c, 0x00 }, { 0x4620, 0x0e }, + /* BT601: 0x08 is also acceptable as HS/VS mode */ + { 0x4700, 0x04 }, { 0x4701, 0x00 }, { 0x4702, 0x01 }, { 0x4004, 0x04 }, + { 0x4005, 0x18 }, { 0x4001, 0x06 }, { 0x4050, 0x22 }, { 0x4051, 0x24 }, + { 0x4052, 0x02 }, { 0x4057, 0x9c }, { 0x405a, 0x00 }, { 0x4202, 0x02 }, + { 0x3023, 0x10 }, { 0x0100, 0x01 }, { 0x0100, 0x01 }, { 0x6f10, 0x07 }, + { 0x6f11, 0x82 }, { 0x6f12, 0x04 }, { 0x6f13, 0x00 }, { 0xd000, 0x19 }, + { 0xd001, 0xa0 }, { 0xd002, 0x00 }, { 0xd003, 0x01 }, { 0xd004, 0xa9 }, + { 0xd005, 0xad }, { 0xd006, 0x10 }, { 0xd007, 0x40 }, { 0xd008, 0x44 }, + { 0xd009, 0x00 }, { 0xd00a, 0x68 }, { 0xd00b, 0x00 }, { 0xd00c, 0x15 }, + { 0xd00d, 0x00 }, { 0xd00e, 0x00 }, { 0xd00f, 0x00 }, { 0xd040, 0x9c }, + { 0xd041, 0x21 }, { 0xd042, 0xff }, { 0xd043, 0xf8 }, { 0xd044, 0xd4 }, + { 0xd045, 0x01 }, { 0xd046, 0x48 }, { 0xd047, 0x00 }, { 0xd048, 0xd4 }, + { 0xd049, 0x01 }, { 0xd04a, 0x50 }, { 0xd04b, 0x04 }, { 0xd04c, 0x18 }, + { 0xd04d, 0x60 }, { 0xd04e, 0x00 }, { 0xd04f, 0x01 }, { 0xd050, 0xa8 }, + { 0xd051, 0x63 }, { 0xd052, 0x02 }, { 0xd053, 0xa4 }, { 0xd054, 0x85 }, + { 0xd055, 0x43 }, { 0xd056, 0x00 }, { 0xd057, 0x00 }, { 0xd058, 0x18 }, + { 0xd059, 0x60 }, { 0xd05a, 0x00 }, { 0xd05b, 0x01 }, { 0xd05c, 0xa8 }, + { 0xd05d, 0x63 }, { 0xd05e, 0x03 }, { 0xd05f, 0xf0 }, { 0xd060, 0x98 }, + { 0xd061, 0xa3 }, { 0xd062, 0x00 }, { 0xd063, 0x00 }, { 0xd064, 0x8c }, + { 0xd065, 0x6a }, { 0xd066, 0x00 }, { 0xd067, 0x6e }, { 0xd068, 0xe5 }, + { 0xd069, 0x85 }, { 0xd06a, 0x18 }, { 0xd06b, 0x00 }, { 0xd06c, 0x10 }, + { 0xd06d, 0x00 }, { 0xd06e, 0x00 }, { 0xd06f, 0x10 }, { 0xd070, 0x9c }, + { 0xd071, 0x80 }, { 0xd072, 0x00 }, { 0xd073, 0x03 }, { 0xd074, 0x18 }, + { 0xd075, 0x60 }, { 0xd076, 0x00 }, { 0xd077, 0x01 }, { 0xd078, 0xa8 }, + { 0xd079, 0x63 }, { 0xd07a, 0x07 }, { 0xd07b, 0x80 }, { 0xd07c, 0x07 }, + { 0xd07d, 0xff }, { 0xd07e, 0xf9 }, { 0xd07f, 0x03 }, { 0xd080, 0x8c }, + { 0xd081, 0x63 }, { 0xd082, 0x00 }, { 0xd083, 0x00 }, { 0xd084, 0xa5 }, + { 0xd085, 0x6b }, { 0xd086, 0x00 }, { 0xd087, 0xff }, { 0xd088, 0x18 }, + { 0xd089, 0x80 }, { 0xd08a, 0x00 }, { 0xd08b, 0x01 }, { 0xd08c, 0xa8 }, + { 0xd08d, 0x84 }, { 0xd08e, 0x01 }, { 0xd08f, 0x04 }, { 0xd090, 0xe1 }, + { 0xd091, 0x6b }, { 0xd092, 0x58 }, { 0xd093, 0x00 }, { 0xd094, 0x94 }, + { 0xd095, 0x6a }, { 0xd096, 0x00 }, { 0xd097, 0x70 }, { 0xd098, 0xe1 }, + { 0xd099, 0x6b }, { 0xd09a, 0x20 }, { 0xd09b, 0x00 }, { 0xd09c, 0x95 }, + { 0xd09d, 0x6b }, { 0xd09e, 0x00 }, { 0xd09f, 0x00 }, { 0xd0a0, 0xe4 }, + { 0xd0a1, 0x8b }, { 0xd0a2, 0x18 }, { 0xd0a3, 0x00 }, { 0xd0a4, 0x0c }, + { 0xd0a5, 0x00 }, { 0xd0a6, 0x00 }, { 0xd0a7, 0x23 }, { 0xd0a8, 0x15 }, + { 0xd0a9, 0x00 }, { 0xd0aa, 0x00 }, { 0xd0ab, 0x00 }, { 0xd0ac, 0x18 }, + { 0xd0ad, 0x60 }, { 0xd0ae, 0x80 }, { 0xd0af, 0x06 }, { 0xd0b0, 0xa8 }, + { 0xd0b1, 0x83 }, { 0xd0b2, 0x40 }, { 0xd0b3, 0x08 }, { 0xd0b4, 0xa8 }, + { 0xd0b5, 0xe3 }, { 0xd0b6, 0x38 }, { 0xd0b7, 0x2a }, { 0xd0b8, 0xa8 }, + { 0xd0b9, 0xc3 }, { 0xd0ba, 0x40 }, { 0xd0bb, 0x09 }, { 0xd0bc, 0xa8 }, + { 0xd0bd, 0xa3 }, { 0xd0be, 0x38 }, { 0xd0bf, 0x29 }, { 0xd0c0, 0x8c }, + { 0xd0c1, 0x65 }, { 0xd0c2, 0x00 }, { 0xd0c3, 0x00 }, { 0xd0c4, 0xd8 }, + { 0xd0c5, 0x04 }, { 0xd0c6, 0x18 }, { 0xd0c7, 0x00 }, { 0xd0c8, 0x8c }, + { 0xd0c9, 0x67 }, { 0xd0ca, 0x00 }, { 0xd0cb, 0x00 }, { 0xd0cc, 0xd8 }, + { 0xd0cd, 0x06 }, { 0xd0ce, 0x18 }, { 0xd0cf, 0x00 }, { 0xd0d0, 0x18 }, + { 0xd0d1, 0x60 }, { 0xd0d2, 0x80 }, { 0xd0d3, 0x06 }, { 0xd0d4, 0xa8 }, + { 0xd0d5, 0xe3 }, { 0xd0d6, 0x67 }, { 0xd0d7, 0x02 }, { 0xd0d8, 0xa9 }, + { 0xd0d9, 0x03 }, { 0xd0da, 0x67 }, { 0xd0db, 0x03 }, { 0xd0dc, 0xa8 }, + { 0xd0dd, 0xc3 }, { 0xd0de, 0x3d }, { 0xd0df, 0x05 }, { 0xd0e0, 0x8c }, + { 0xd0e1, 0x66 }, { 0xd0e2, 0x00 }, { 0xd0e3, 0x00 }, { 0xd0e4, 0xb8 }, + { 0xd0e5, 0x63 }, { 0xd0e6, 0x00 }, { 0xd0e7, 0x18 }, { 0xd0e8, 0xb8 }, + { 0xd0e9, 0x63 }, { 0xd0ea, 0x00 }, { 0xd0eb, 0x98 }, { 0xd0ec, 0xbc }, + { 0xd0ed, 0x03 }, { 0xd0ee, 0x00 }, { 0xd0ef, 0x00 }, { 0xd0f0, 0x10 }, + { 0xd0f1, 0x00 }, { 0xd0f2, 0x00 }, { 0xd0f3, 0x16 }, { 0xd0f4, 0xb8 }, + { 0xd0f5, 0x83 }, { 0xd0f6, 0x00 }, { 0xd0f7, 0x19 }, { 0xd0f8, 0x8c }, + { 0xd0f9, 0x67 }, { 0xd0fa, 0x00 }, { 0xd0fb, 0x00 }, { 0xd0fc, 0xb8 }, + { 0xd0fd, 0xa4 }, { 0xd0fe, 0x00 }, { 0xd0ff, 0x98 }, { 0xd100, 0xb8 }, + { 0xd101, 0x83 }, { 0xd102, 0x00 }, { 0xd103, 0x08 }, { 0xd104, 0x8c }, + { 0xd105, 0x68 }, { 0xd106, 0x00 }, { 0xd107, 0x00 }, { 0xd108, 0xe0 }, + { 0xd109, 0x63 }, { 0xd10a, 0x20 }, { 0xd10b, 0x04 }, { 0xd10c, 0xe0 }, + { 0xd10d, 0x65 }, { 0xd10e, 0x18 }, { 0xd10f, 0x00 }, { 0xd110, 0xa4 }, + { 0xd111, 0x83 }, { 0xd112, 0xff }, { 0xd113, 0xff }, { 0xd114, 0xb8 }, + { 0xd115, 0x64 }, { 0xd116, 0x00 }, { 0xd117, 0x48 }, { 0xd118, 0xd8 }, + { 0xd119, 0x07 }, { 0xd11a, 0x18 }, { 0xd11b, 0x00 }, { 0xd11c, 0xd8 }, + { 0xd11d, 0x08 }, { 0xd11e, 0x20 }, { 0xd11f, 0x00 }, { 0xd120, 0x9c }, + { 0xd121, 0x60 }, { 0xd122, 0x00 }, { 0xd123, 0x00 }, { 0xd124, 0xd8 }, + { 0xd125, 0x06 }, { 0xd126, 0x18 }, { 0xd127, 0x00 }, { 0xd128, 0x00 }, + { 0xd129, 0x00 }, { 0xd12a, 0x00 }, { 0xd12b, 0x08 }, { 0xd12c, 0x15 }, + { 0xd12d, 0x00 }, { 0xd12e, 0x00 }, { 0xd12f, 0x00 }, { 0xd130, 0x8c }, + { 0xd131, 0x6a }, { 0xd132, 0x00 }, { 0xd133, 0x76 }, { 0xd134, 0xbc }, + { 0xd135, 0x23 }, { 0xd136, 0x00 }, { 0xd137, 0x00 }, { 0xd138, 0x13 }, + { 0xd139, 0xff }, { 0xd13a, 0xff }, { 0xd13b, 0xe6 }, { 0xd13c, 0x18 }, + { 0xd13d, 0x60 }, { 0xd13e, 0x80 }, { 0xd13f, 0x06 }, { 0xd140, 0x03 }, + { 0xd141, 0xff }, { 0xd142, 0xff }, { 0xd143, 0xdd }, { 0xd144, 0xa8 }, + { 0xd145, 0x83 }, { 0xd146, 0x40 }, { 0xd147, 0x08 }, { 0xd148, 0x85 }, + { 0xd149, 0x21 }, { 0xd14a, 0x00 }, { 0xd14b, 0x00 }, { 0xd14c, 0x85 }, + { 0xd14d, 0x41 }, { 0xd14e, 0x00 }, { 0xd14f, 0x04 }, { 0xd150, 0x44 }, + { 0xd151, 0x00 }, { 0xd152, 0x48 }, { 0xd153, 0x00 }, { 0xd154, 0x9c }, + { 0xd155, 0x21 }, { 0xd156, 0x00 }, { 0xd157, 0x08 }, { 0x6f0e, 0x03 }, + { 0x6f0f, 0x00 }, { 0x460e, 0x08 }, { 0x460f, 0x01 }, { 0x4610, 0x00 }, + { 0x4611, 0x01 }, { 0x4612, 0x00 }, { 0x4613, 0x01 }, + /* 8 bits */ + { 0x4605, 0x08 }, + /* Swap data bits order [9:0] -> [0:9] */ + { 0x4709, 0x10 }, { 0x4608, 0x00 }, { 0x4609, 0x08 }, { 0x6804, 0x00 }, + { 0x6805, 0x06 }, { 0x6806, 0x00 }, { 0x5120, 0x00 }, { 0x3510, 0x00 }, + { 0x3504, 0x00 }, { 0x6800, 0x00 }, { 0x6f0d, 0x01 }, + /* PCLK falling edge */ + { 0x4708, 0x01 }, { 0x5000, 0xff }, { 0x5001, 0xbf }, { 0x5002, 0x7e }, + { 0x503d, 0x00 }, { 0xc450, 0x01 }, { 0xc452, 0x04 }, { 0xc453, 0x00 }, + { 0xc454, 0x00 }, { 0xc455, 0x01 }, { 0xc456, 0x01 }, { 0xc457, 0x00 }, + { 0xc458, 0x00 }, { 0xc459, 0x00 }, { 0xc45b, 0x00 }, { 0xc45c, 0x01 }, + { 0xc45d, 0x00 }, { 0xc45e, 0x00 }, { 0xc45f, 0x00 }, { 0xc460, 0x00 }, + { 0xc461, 0x01 }, { 0xc462, 0x01 }, { 0xc464, 0x03 }, { 0xc465, 0x00 }, + { 0xc466, 0x8a }, { 0xc467, 0x00 }, { 0xc468, 0x86 }, { 0xc469, 0x00 }, + { 0xc46a, 0x40 }, { 0xc46b, 0x50 }, { 0xc46c, 0x30 }, { 0xc46d, 0x28 }, + { 0xc46e, 0x60 }, { 0xc46f, 0x40 }, { 0xc47c, 0x01 }, { 0xc47d, 0x38 }, + { 0xc47e, 0x00 }, { 0xc47f, 0x00 }, { 0xc480, 0x00 }, { 0xc481, 0xff }, + { 0xc482, 0x00 }, { 0xc483, 0x40 }, { 0xc484, 0x00 }, { 0xc485, 0x18 }, + { 0xc486, 0x00 }, { 0xc487, 0x18 }, + { 0xc488, (OV10635_VTS - 8) * 16 >> 8}, + { 0xc489, (OV10635_VTS - 8) * 16 & 0xff}, + { 0xc48a, (OV10635_VTS - 8) * 16 >> 8}, + { 0xc48b, (OV10635_VTS - 8) * 16 & 0xff}, { 0xc48c, 0x00 }, + { 0xc48d, 0x04 }, { 0xc48e, 0x00 }, { 0xc48f, 0x04 }, { 0xc490, 0x03 }, + { 0xc492, 0x20 }, { 0xc493, 0x08 }, { 0xc498, 0x02 }, { 0xc499, 0x00 }, + { 0xc49a, 0x02 }, { 0xc49b, 0x00 }, { 0xc49c, 0x02 }, { 0xc49d, 0x00 }, + { 0xc49e, 0x02 }, { 0xc49f, 0x60 }, { 0xc4a0, 0x03 }, { 0xc4a1, 0x00 }, + { 0xc4a2, 0x04 }, { 0xc4a3, 0x00 }, { 0xc4a4, 0x00 }, { 0xc4a5, 0x10 }, + { 0xc4a6, 0x00 }, { 0xc4a7, 0x40 }, { 0xc4a8, 0x00 }, { 0xc4a9, 0x80 }, + { 0xc4aa, 0x0d }, { 0xc4ab, 0x00 }, { 0xc4ac, 0x0f }, { 0xc4ad, 0xc0 }, + { 0xc4b4, 0x01 }, { 0xc4b5, 0x01 }, { 0xc4b6, 0x00 }, { 0xc4b7, 0x01 }, + { 0xc4b8, 0x00 }, { 0xc4b9, 0x01 }, { 0xc4ba, 0x01 }, { 0xc4bb, 0x00 }, + { 0xc4bc, 0x01 }, { 0xc4bd, 0x60 }, { 0xc4be, 0x02 }, { 0xc4bf, 0x33 }, + { 0xc4c8, 0x03 }, { 0xc4c9, 0xd0 }, { 0xc4ca, 0x0e }, { 0xc4cb, 0x00 }, + { 0xc4cc, 0x0e }, { 0xc4cd, 0x51 }, { 0xc4ce, 0x0e }, { 0xc4cf, 0x51 }, + { 0xc4d0, 0x04 }, { 0xc4d1, 0x80 }, { 0xc4e0, 0x04 }, { 0xc4e1, 0x02 }, + { 0xc4e2, 0x01 }, { 0xc4e4, 0x10 }, { 0xc4e5, 0x20 }, { 0xc4e6, 0x30 }, + { 0xc4e7, 0x40 }, { 0xc4e8, 0x50 }, { 0xc4e9, 0x60 }, { 0xc4ea, 0x70 }, + { 0xc4eb, 0x80 }, { 0xc4ec, 0x90 }, { 0xc4ed, 0xa0 }, { 0xc4ee, 0xb0 }, + { 0xc4ef, 0xc0 }, { 0xc4f0, 0xd0 }, { 0xc4f1, 0xe0 }, { 0xc4f2, 0xf0 }, + { 0xc4f3, 0x80 }, { 0xc4f4, 0x00 }, { 0xc4f5, 0x20 }, { 0xc4f6, 0x02 }, + { 0xc4f7, 0x00 }, { 0xc4f8, 0x00 }, { 0xc4f9, 0x00 }, { 0xc4fa, 0x00 }, + { 0xc4fb, 0x01 }, { 0xc4fc, 0x01 }, { 0xc4fd, 0x00 }, { 0xc4fe, 0x04 }, + { 0xc4ff, 0x02 }, { 0xc500, 0x48 }, { 0xc501, 0x74 }, { 0xc502, 0x58 }, + { 0xc503, 0x80 }, { 0xc504, 0x05 }, { 0xc505, 0x80 }, { 0xc506, 0x03 }, + { 0xc507, 0x80 }, { 0xc508, 0x01 }, { 0xc509, 0xc0 }, { 0xc50a, 0x01 }, + { 0xc50b, 0xa0 }, { 0xc50c, 0x01 }, { 0xc50d, 0x2c }, { 0xc50e, 0x01 }, + { 0xc50f, 0x0a }, { 0xc510, 0x00 }, { 0xc511, 0x00 }, { 0xc512, 0xe5 }, + { 0xc513, 0x14 }, { 0xc514, 0x04 }, { 0xc515, 0x00 }, { 0xc518, OV10635_VTS >> 8}, + { 0xc519, OV10635_VTS & 0xff}, { 0xc51a, OV10635_HTS >> 8}, + { 0xc51b, OV10635_HTS & 0xff}, { 0xc2e0, 0x00 }, { 0xc2e1, 0x51 }, + { 0xc2e2, 0x00 }, { 0xc2e3, 0xd6 }, { 0xc2e4, 0x01 }, { 0xc2e5, 0x5e }, + { 0xc2e9, 0x01 }, { 0xc2ea, 0x7a }, { 0xc2eb, 0x90 }, { 0xc2ed, 0x00 }, + { 0xc2ee, 0x7a }, { 0xc2ef, 0x64 }, { 0xc308, 0x00 }, { 0xc309, 0x00 }, + { 0xc30a, 0x00 }, { 0xc30c, 0x00 }, { 0xc30d, 0x01 }, { 0xc30e, 0x00 }, + { 0xc30f, 0x00 }, { 0xc310, 0x01 }, { 0xc311, 0x60 }, { 0xc312, 0xff }, + { 0xc313, 0x08 }, { 0xc314, 0x01 }, { 0xc315, 0x00 }, { 0xc316, 0xff }, + { 0xc317, 0x0b }, { 0xc318, 0x00 }, { 0xc319, 0x0c }, { 0xc31a, 0x00 }, + { 0xc31b, 0xe0 }, { 0xc31c, 0x00 }, { 0xc31d, 0x14 }, { 0xc31e, 0x00 }, + { 0xc31f, 0xc5 }, { 0xc320, 0xff }, { 0xc321, 0x4b }, { 0xc322, 0xff }, + { 0xc323, 0xf0 }, { 0xc324, 0xff }, { 0xc325, 0xe8 }, { 0xc326, 0x00 }, + { 0xc327, 0x46 }, { 0xc328, 0xff }, { 0xc329, 0xd2 }, { 0xc32a, 0xff }, + { 0xc32b, 0xe4 }, { 0xc32c, 0xff }, { 0xc32d, 0xbb }, { 0xc32e, 0x00 }, + { 0xc32f, 0x61 }, { 0xc330, 0xff }, { 0xc331, 0xf9 }, { 0xc332, 0x00 }, + { 0xc333, 0xd9 }, { 0xc334, 0x00 }, { 0xc335, 0x2e }, { 0xc336, 0x00 }, + { 0xc337, 0xb1 }, { 0xc338, 0xff }, { 0xc339, 0x64 }, { 0xc33a, 0xff }, + { 0xc33b, 0xeb }, { 0xc33c, 0xff }, { 0xc33d, 0xe8 }, { 0xc33e, 0x00 }, + { 0xc33f, 0x48 }, { 0xc340, 0xff }, { 0xc341, 0xd0 }, { 0xc342, 0xff }, + { 0xc343, 0xed }, { 0xc344, 0xff }, { 0xc345, 0xad }, { 0xc346, 0x00 }, + { 0xc347, 0x66 }, { 0xc348, 0x01 }, { 0xc349, 0x00 }, { 0x6700, 0x04 }, + { 0x6701, 0x7b }, { 0x6702, 0xfd }, { 0x6703, 0xf9 }, { 0x6704, 0x3d }, + { 0x6705, 0x71 }, { 0x6706, 0x78 }, { 0x6708, 0x05 }, { 0x6f06, 0x6f }, + { 0x6f07, 0x00 }, { 0x6f0a, 0x6f }, { 0x6f0b, 0x00 }, { 0x6f00, 0x03 }, + { 0xc34c, 0x01 }, { 0xc34d, 0x00 }, { 0xc34e, 0x46 }, { 0xc34f, 0x55 }, + { 0xc350, 0x00 }, { 0xc351, 0x40 }, { 0xc352, 0x00 }, { 0xc353, 0xff }, + { 0xc354, 0x04 }, { 0xc355, 0x08 }, { 0xc356, 0x01 }, { 0xc357, 0xef }, + { 0xc358, 0x30 }, { 0xc359, 0x01 }, { 0xc35a, 0x64 }, { 0xc35b, 0x46 }, + { 0xc35c, 0x00 }, { 0x3042, 0xf0 }, { 0x3042, 0xf0 }, { 0x3042, 0xf0 }, + { 0x3042, 0xf0 }, { 0x3042, 0xf0 }, { 0x3042, 0xf0 }, { 0x3042, 0xf0 }, + { 0x3042, 0xf0 }, { 0x3042, 0xf0 }, { 0x3042, 0xf0 }, { 0x3042, 0xf0 }, + { 0x3042, 0xf0 }, { 0x3042, 0xf0 }, { 0x3042, 0xf0 }, { 0x3042, 0xf0 }, + { 0x3042, 0xf0 }, { 0x3042, 0xf0 }, { 0x3042, 0xf0 }, { 0x3042, 0xf0 }, + { 0x3042, 0xf0 }, { 0x3042, 0xf0 }, { 0x3042, 0xf0 }, { 0x3042, 0xf0 }, + { 0x3042, 0xf0 }, { 0x3042, 0xf0 }, { 0x3042, 0xf0 }, { 0xc261, 0x01 }, + { 0x301b, 0xf0 }, { 0x301c, 0xf0 }, { 0x301a, 0xf0 }, { 0x6f00, 0xc3 }, + { 0xc46a, 0x30 }, { 0xc46d, 0x20 }, { 0xc464, 0x84 }, { 0xc465, 0x00 }, + { 0x6f00, 0x03 }, { 0x6f00, 0x43 }, { 0x381c, 0x00 }, { 0x381d, 0x40 }, + { 0xc454, 0x01 }, { 0x6f00, 0xc3 }, { 0xc454, 0x00 }, { 0xc4b1, 0x02 }, + { 0xc4b2, 0x01 }, { 0xc4b3, 0x03 }, { 0x6f00, 0x03 }, { 0x6f00, 0x43 }, + /* enable FSIN (FRAMESYNC input) functionality */ + { 0x3832, (0x0d + 2 * 0x20 + 0x15 + 38) >> 8 }, + { 0x3833, (0x0d + 2 * 0x20 + 0x15 + 38) & 0xff }, + { 0x3834, OV10635_VTS >> 8 }, { 0x3835, OV10635_VTS & 0xff }, + { 0x302e, 0x01 }, +}; + +struct rdacm20_device { + struct device *dev; + struct max9271_device *serializer; + struct i2c_client *sensor; + struct v4l2_subdev sd; + struct media_pad pad; + struct v4l2_ctrl_handler ctrls; + u32 addrs[2]; +}; + +static inline struct rdacm20_device *sd_to_rdacm20(struct v4l2_subdev *sd) +{ + return container_of(sd, struct rdacm20_device, sd); +} + +static inline struct rdacm20_device *i2c_to_rdacm20(struct i2c_client *client) +{ + return sd_to_rdacm20(i2c_get_clientdata(client)); +} + +static int ov10635_read16(struct rdacm20_device *dev, u16 reg) +{ + u8 buf[2] = { reg >> 8, reg & 0xff }; + int ret; + + ret = i2c_master_send(dev->sensor, buf, 2); + if (ret != 2) { + dev_dbg(dev->dev, "%s: register 0x%04x write failed (%d)\n", + __func__, reg, ret); + return ret; + } + + ret = i2c_master_recv(dev->sensor, buf, 2); + if (ret < 0) { + dev_dbg(dev->dev, "%s: register 0x%04x read failed (%d)\n", + __func__, reg, ret); + return ret; + } + + return (buf[0] << 8) | buf[1]; +} + +static int __ov10635_write(struct rdacm20_device *dev, u16 reg, u8 val) +{ + u8 buf[3] = { reg >> 8, reg & 0xff, val }; + int ret; + + dev_dbg(dev->dev, "%s(0x%04x, 0x%02x)\n", __func__, reg, val); + + ret = i2c_master_send(dev->sensor, buf, 3); + return ret < 0 ? ret : 0; +} + +static int ov10635_write(struct rdacm20_device *dev, u16 reg, u8 val) +{ + int ret; + + ret = __ov10635_write(dev, reg, val); + if (ret < 0) + dev_err(dev->dev, "%s: register 0x%04x write failed (%d)\n", + __func__, reg, ret); + + return ret; +} + +static int ov10635_set_regs(struct rdacm20_device *dev, + const struct ov10635_reg *regs, + unsigned int nr_regs) +{ + unsigned int i; + int ret; + + for (i = 0; i < nr_regs; i++) { + ret = __ov10635_write(dev, regs[i].reg, regs[i].val); + if (ret) { + dev_err(dev->dev, + "%s: register %u (0x%04x) write failed (%d)\n", + __func__, i, regs[i].reg, ret); + return ret; + } + } + + return 0; +} + +static int rdacm20_s_stream(struct v4l2_subdev *sd, int enable) +{ + struct rdacm20_device *dev = sd_to_rdacm20(sd); + + return max9271_set_serial_link(dev->serializer, enable); +} + +static int rdacm20_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_mbus_code_enum *code) +{ + if (code->pad || code->index > 0) + return -EINVAL; + + code->code = MEDIA_BUS_FMT_UYVY8_2X8; + + return 0; +} + +static int rdacm20_get_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *format) +{ + struct v4l2_mbus_framefmt *mf = &format->format; + + if (format->pad) + return -EINVAL; + + mf->width = OV10635_WIDTH; + mf->height = OV10635_HEIGHT; + mf->code = MEDIA_BUS_FMT_UYVY8_2X8; + mf->colorspace = V4L2_COLORSPACE_RAW; + mf->field = V4L2_FIELD_NONE; + mf->ycbcr_enc = V4L2_YCBCR_ENC_601; + mf->quantization = V4L2_QUANTIZATION_FULL_RANGE; + mf->xfer_func = V4L2_XFER_FUNC_NONE; + + return 0; +} + +static struct v4l2_subdev_video_ops rdacm20_video_ops = { + .s_stream = rdacm20_s_stream, +}; + +static const struct v4l2_subdev_pad_ops rdacm20_subdev_pad_ops = { + .enum_mbus_code = rdacm20_enum_mbus_code, + .get_fmt = rdacm20_get_fmt, + .set_fmt = rdacm20_get_fmt, +}; + +static struct v4l2_subdev_ops rdacm20_subdev_ops = { + .video = &rdacm20_video_ops, + .pad = &rdacm20_subdev_pad_ops, +}; + +static int rdacm20_initialize(struct rdacm20_device *dev) +{ + unsigned int retry = 3; + int ret; + + /* Verify communication with the MAX9271: ping to wakeup. */ + dev->serializer->client->addr = MAX9271_DEFAULT_ADDR; + i2c_smbus_read_byte(dev->serializer->client); + + /* Serial link disabled during config as it needs a valid pixel clock. */ + ret = max9271_set_serial_link(dev->serializer, false); + if (ret) + return ret; + + /* + * Ensure that we have a good link configuration before attempting to + * identify the device. + */ + max9271_configure_i2c(dev->serializer, MAX9271_I2CSLVSH_469NS_234NS | + MAX9271_I2CSLVTO_1024US | + MAX9271_I2CMSTBT_105KBPS); + + max9271_configure_gmsl_link(dev->serializer); + + ret = max9271_verify_id(dev->serializer); + if (ret < 0) + return ret; + + ret = max9271_set_address(dev->serializer, dev->addrs[0]); + if (ret < 0) + return ret; + dev->serializer->client->addr = dev->addrs[0]; + + /* + * Reset the sensor by cycling the OV10635 reset signal connected to the + * MAX9271 GPIO1 and verify communication with the OV10635. + */ + max9271_clear_gpios(dev->serializer, MAX9271_GPIO1OUT); + usleep_range(10000, 15000); + max9271_set_gpios(dev->serializer, MAX9271_GPIO1OUT); + usleep_range(10000, 15000); + +again: + ret = ov10635_read16(dev, OV10635_PID); + if (ret < 0) { + if (retry--) + goto again; + + dev_err(dev->dev, "OV10635 ID read failed (%d)\n", + ret); + return -ENXIO; + } + + if (ret != OV10635_VERSION) { + if (retry--) + goto again; + + dev_err(dev->dev, "OV10635 ID mismatch (0x%04x)\n", + ret); + return -ENXIO; + } + + /* Change the sensor I2C address. */ + ret = ov10635_write(dev, OV10635_SC_CMMN_SCCB_ID, + (dev->addrs[1] << 1) | + OV10635_SC_CMMN_SCCB_ID_SELECT); + if (ret < 0) { + dev_err(dev->dev, + "OV10635 I2C address change failed (%d)\n", ret); + return ret; + } + dev->sensor->addr = dev->addrs[1]; + usleep_range(3500, 5000); + + /* Program the 0V10635 initial configuration. */ + ret = ov10635_set_regs(dev, ov10635_regs_wizard, + ARRAY_SIZE(ov10635_regs_wizard)); + if (ret) + return ret; + + dev_info(dev->dev, "Identified MAX9271 + OV10635 device\n"); + + return 0; +} + +static int rdacm20_probe(struct i2c_client *client) +{ + struct rdacm20_device *dev; + struct fwnode_handle *ep; + int ret; + + dev = devm_kzalloc(&client->dev, sizeof(*dev), GFP_KERNEL); + if (!dev) + return -ENOMEM; + dev->dev = &client->dev; + + dev->serializer = devm_kzalloc(&client->dev, sizeof(*dev->serializer), + GFP_KERNEL); + if (!dev->serializer) + return -ENOMEM; + + dev->serializer->client = client; + + ret = of_property_read_u32_array(client->dev.of_node, "reg", + dev->addrs, 2); + if (ret < 0) { + dev_err(dev->dev, "Invalid DT reg property: %d\n", ret); + return -EINVAL; + } + + /* Create the dummy I2C client for the sensor. */ + dev->sensor = i2c_new_dummy_device(client->adapter, + OV10635_I2C_ADDRESS); + if (IS_ERR(dev->sensor)) { + ret = PTR_ERR(dev->sensor); + goto error; + } + + /* Initialize the hardware. */ + ret = rdacm20_initialize(dev); + if (ret < 0) + goto error; + + /* Initialize and register the subdevice. */ + v4l2_i2c_subdev_init(&dev->sd, client, &rdacm20_subdev_ops); + dev->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + + v4l2_ctrl_handler_init(&dev->ctrls, 1); + v4l2_ctrl_new_std(&dev->ctrls, NULL, V4L2_CID_PIXEL_RATE, + OV10635_PIXEL_RATE, OV10635_PIXEL_RATE, 1, + OV10635_PIXEL_RATE); + dev->sd.ctrl_handler = &dev->ctrls; + + ret = dev->ctrls.error; + if (ret) + goto error_free_ctrls; + + dev->pad.flags = MEDIA_PAD_FL_SOURCE; + dev->sd.entity.flags |= MEDIA_ENT_F_CAM_SENSOR; + ret = media_entity_pads_init(&dev->sd.entity, 1, &dev->pad); + if (ret < 0) + goto error_free_ctrls; + + ep = fwnode_graph_get_next_endpoint(dev_fwnode(&client->dev), NULL); + if (!ep) { + dev_err(&client->dev, + "Unable to get endpoint in node %pOF\n", + client->dev.of_node); + ret = -ENOENT; + goto error_free_ctrls; + } + dev->sd.fwnode = ep; + + ret = v4l2_async_register_subdev(&dev->sd); + if (ret) + goto error_put_node; + + return 0; + +error_put_node: + fwnode_handle_put(ep); +error_free_ctrls: + v4l2_ctrl_handler_free(&dev->ctrls); +error: + media_entity_cleanup(&dev->sd.entity); + if (dev->sensor) + i2c_unregister_device(dev->sensor); + + dev_err(&client->dev, "probe failed\n"); + + return ret; +} + +static int rdacm20_remove(struct i2c_client *client) +{ + struct rdacm20_device *dev = i2c_to_rdacm20(client); + + fwnode_handle_put(dev->sd.fwnode); + v4l2_async_unregister_subdev(&dev->sd); + v4l2_ctrl_handler_free(&dev->ctrls); + media_entity_cleanup(&dev->sd.entity); + i2c_unregister_device(dev->sensor); + + return 0; +} + +static void rdacm20_shutdown(struct i2c_client *client) +{ + struct rdacm20_device *dev = i2c_to_rdacm20(client); + + /* make sure stream off during shutdown (reset/reboot) */ + rdacm20_s_stream(&dev->sd, 0); +} + +static const struct of_device_id rdacm20_of_ids[] = { + { .compatible = "imi,rdacm20", }, + { } +}; +MODULE_DEVICE_TABLE(of, rdacm20_of_ids); + +static struct i2c_driver rdacm20_i2c_driver = { + .driver = { + .name = "rdacm20", + .of_match_table = rdacm20_of_ids, + }, + .probe_new = rdacm20_probe, + .remove = rdacm20_remove, + .shutdown = rdacm20_shutdown, +}; + +module_i2c_driver(rdacm20_i2c_driver); + +MODULE_DESCRIPTION("GMSL Camera driver for RDACM20"); +MODULE_AUTHOR("Vladimir Barinov"); +MODULE_LICENSE("GPL"); -- GitLab From 35694afc92646ac24d7f3ef34a7387876d998fe7 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Wed, 1 Jul 2020 08:21:37 +0200 Subject: [PATCH 0675/1476] media: device property: Add a function to test is a fwnode is a graph endpoint MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Drivers may need to test if a fwnode is a graph endpoint. To avoid hand-written solutions that wouldn't work for all fwnode types, add a new fwnode_graph_is_endpoint() function for this purpose. We don't need to wire it up to different backends for OF and ACPI for now, as the implementation can simply be based on checkout the presence of a remote-endpoint property. Signed-off-by: Laurent Pinchart Reviewed-by: Niklas Söderlund Reviewed-by: Lad Prabhakar Reviewed-by: Kieran Bingham Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- include/linux/property.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/include/linux/property.h b/include/linux/property.h index 10d03572f52e5..9f805c4428195 100644 --- a/include/linux/property.h +++ b/include/linux/property.h @@ -389,6 +389,11 @@ struct fwnode_handle * fwnode_graph_get_remote_node(const struct fwnode_handle *fwnode, u32 port, u32 endpoint); +static inline bool fwnode_graph_is_endpoint(struct fwnode_handle *fwnode) +{ + return fwnode_property_present(fwnode, "remote-endpoint"); +} + /* * Fwnode lookup flags * -- GitLab From b98158d837efa0b2f2f59ee2ff77f4791f978d74 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Wed, 1 Jul 2020 08:21:38 +0200 Subject: [PATCH 0676/1476] media: v4l2-async: Accept endpoints and devices for fwnode matching MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit fwnode matching was designed to match on nodes corresponding to a device. Some drivers, however, needed to match on endpoints, and have passed endpoint fwnodes to v4l2-async. This works when both the subdev and the notifier use the same fwnode types (endpoint or device), but makes drivers that use different types incompatible. Fix this by extending the fwnode match to handle fwnodes of different types. When the types (deduced from the presence of remote endpoints) are different, retrieve the device fwnode for the side that provides an endpoint fwnode, and compare it with the device fwnode provided by the other side. This allows interoperability between all drivers, regardless of which type of fwnode they use for matching. Signed-off-by: Laurent Pinchart Tested-by: Lad Prabhakar Reviewed-by: Kieran Bingham Reviewed-by: Jacopo Mondi Tested-by: Niklas Söderlund Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-async.c | 43 +++++++++++++++++++++++++++- 1 file changed, 42 insertions(+), 1 deletion(-) diff --git a/drivers/media/v4l2-core/v4l2-async.c b/drivers/media/v4l2-core/v4l2-async.c index 8bde33c21ce45..e9c3d41c9449d 100644 --- a/drivers/media/v4l2-core/v4l2-async.c +++ b/drivers/media/v4l2-core/v4l2-async.c @@ -71,7 +71,48 @@ static bool match_devname(struct v4l2_subdev *sd, static bool match_fwnode(struct v4l2_subdev *sd, struct v4l2_async_subdev *asd) { - return sd->fwnode == asd->match.fwnode; + struct fwnode_handle *other_fwnode; + struct fwnode_handle *dev_fwnode; + bool asd_fwnode_is_ep; + bool sd_fwnode_is_ep; + + /* + * Both the subdev and the async subdev can provide either an endpoint + * fwnode or a device fwnode. Start with the simple case of direct + * fwnode matching. + */ + if (sd->fwnode == asd->match.fwnode) + return true; + + /* + * Otherwise, check if the sd fwnode and the asd fwnode refer to an + * endpoint or a device. If they're of the same type, there's no match. + * Technically speaking this checks if the nodes refer to a connected + * endpoint, which is the simplest check that works for both OF and + * ACPI. This won't make a difference, as drivers should not try to + * match unconnected endpoints. + */ + sd_fwnode_is_ep = fwnode_graph_is_endpoint(sd->fwnode); + asd_fwnode_is_ep = fwnode_graph_is_endpoint(asd->match.fwnode); + + if (sd_fwnode_is_ep == asd_fwnode_is_ep) + return false; + + /* + * The sd and asd fwnodes are of different types. Get the device fwnode + * parent of the endpoint fwnode, and compare it with the other fwnode. + */ + if (sd_fwnode_is_ep) { + dev_fwnode = fwnode_graph_get_port_parent(sd->fwnode); + other_fwnode = asd->match.fwnode; + } else { + dev_fwnode = fwnode_graph_get_port_parent(asd->match.fwnode); + other_fwnode = sd->fwnode; + } + + fwnode_handle_put(dev_fwnode); + + return dev_fwnode == other_fwnode; } static bool match_custom(struct v4l2_subdev *sd, struct v4l2_async_subdev *asd) -- GitLab From 3e33392a9561fd64515049317041646ab3bf32aa Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Wed, 1 Jul 2020 08:21:39 +0200 Subject: [PATCH 0677/1476] media: v4l2-async: Pass notifier pointer to match functions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The notifier is useful to match functions to access information about the device matching a subdev. This will be used to print messages using the correct struct device and driver name. Signed-off-by: Laurent Pinchart Tested-by: Lad Prabhakar Reviewed-by: Kieran Bingham Reviewed-by: Jacopo Mondi Tested-by: Niklas Söderlund Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-async.c | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/drivers/media/v4l2-core/v4l2-async.c b/drivers/media/v4l2-core/v4l2-async.c index e9c3d41c9449d..60833822ee891 100644 --- a/drivers/media/v4l2-core/v4l2-async.c +++ b/drivers/media/v4l2-core/v4l2-async.c @@ -50,7 +50,8 @@ static int v4l2_async_notifier_call_complete(struct v4l2_async_notifier *n) return n->ops->complete(n); } -static bool match_i2c(struct v4l2_subdev *sd, struct v4l2_async_subdev *asd) +static bool match_i2c(struct v4l2_async_notifier *notifier, + struct v4l2_subdev *sd, struct v4l2_async_subdev *asd) { #if IS_ENABLED(CONFIG_I2C) struct i2c_client *client = i2c_verify_client(sd->dev); @@ -63,13 +64,14 @@ static bool match_i2c(struct v4l2_subdev *sd, struct v4l2_async_subdev *asd) #endif } -static bool match_devname(struct v4l2_subdev *sd, - struct v4l2_async_subdev *asd) +static bool match_devname(struct v4l2_async_notifier *notifier, + struct v4l2_subdev *sd, struct v4l2_async_subdev *asd) { return !strcmp(asd->match.device_name, dev_name(sd->dev)); } -static bool match_fwnode(struct v4l2_subdev *sd, struct v4l2_async_subdev *asd) +static bool match_fwnode(struct v4l2_async_notifier *notifier, + struct v4l2_subdev *sd, struct v4l2_async_subdev *asd) { struct fwnode_handle *other_fwnode; struct fwnode_handle *dev_fwnode; @@ -115,7 +117,8 @@ static bool match_fwnode(struct v4l2_subdev *sd, struct v4l2_async_subdev *asd) return dev_fwnode == other_fwnode; } -static bool match_custom(struct v4l2_subdev *sd, struct v4l2_async_subdev *asd) +static bool match_custom(struct v4l2_async_notifier *notifier, + struct v4l2_subdev *sd, struct v4l2_async_subdev *asd) { if (!asd->match.custom.match) /* Match always */ @@ -132,7 +135,8 @@ static struct v4l2_async_subdev * v4l2_async_find_match(struct v4l2_async_notifier *notifier, struct v4l2_subdev *sd) { - bool (*match)(struct v4l2_subdev *sd, struct v4l2_async_subdev *asd); + bool (*match)(struct v4l2_async_notifier *notifier, + struct v4l2_subdev *sd, struct v4l2_async_subdev *asd); struct v4l2_async_subdev *asd; list_for_each_entry(asd, ¬ifier->waiting, list) { @@ -157,7 +161,7 @@ v4l2_async_find_match(struct v4l2_async_notifier *notifier, } /* match cannot be NULL here */ - if (match(sd, asd)) + if (match(notifier, sd, asd)) return asd; } -- GitLab From e80cdf0a3843fe570c891cb976feb0c136b19fb7 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Wed, 1 Jul 2020 08:21:40 +0200 Subject: [PATCH 0678/1476] media: v4l2-async: Log message in case of heterogeneous fwnode match MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When a notifier supplies a device fwnode and a subdev supplies an endpoint fwnode, incorrect matches may occur if multiple subdevs correspond to the same device fwnode. This can't be handled transparently in the framework, and requires the notifier to switch to endpoint fwnodes. Log a message to notify of this problem. A second message is added to help accelerating the transition to endpoint matching. Signed-off-by: Laurent Pinchart Tested-by: Lad Prabhakar Reviewed-by: Kieran Bingham Reviewed-by: Jacopo Mondi Tested-by: Niklas Söderlund Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-async.c | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/drivers/media/v4l2-core/v4l2-async.c b/drivers/media/v4l2-core/v4l2-async.c index 60833822ee891..e3ab003a6c851 100644 --- a/drivers/media/v4l2-core/v4l2-async.c +++ b/drivers/media/v4l2-core/v4l2-async.c @@ -77,6 +77,7 @@ static bool match_fwnode(struct v4l2_async_notifier *notifier, struct fwnode_handle *dev_fwnode; bool asd_fwnode_is_ep; bool sd_fwnode_is_ep; + struct device *dev; /* * Both the subdev and the async subdev can provide either an endpoint @@ -114,7 +115,28 @@ static bool match_fwnode(struct v4l2_async_notifier *notifier, fwnode_handle_put(dev_fwnode); - return dev_fwnode == other_fwnode; + if (dev_fwnode != other_fwnode) + return false; + + /* + * We have a heterogeneous match. Retrieve the struct device of the side + * that matched on a device fwnode to print its driver name. + */ + if (sd_fwnode_is_ep) + dev = notifier->v4l2_dev ? notifier->v4l2_dev->dev + : notifier->sd->dev; + else + dev = sd->dev; + + if (dev && dev->driver) { + if (sd_fwnode_is_ep) + dev_warn(dev, "Driver %s uses device fwnode, incorrect match may occur\n", + dev->driver->name); + dev_notice(dev, "Consider updating driver %s to match on endpoints\n", + dev->driver->name); + } + + return true; } static bool match_custom(struct v4l2_async_notifier *notifier, -- GitLab From 65c5fac1a2b74c120746d0aba810a873be89d0f8 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Fri, 15 May 2020 02:25:28 +0200 Subject: [PATCH 0679/1476] media: dt-bindings: media: renesas,fcp: Convert binding to YAML MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Convert the Renesas R-Car FCP text binding to YAML. Signed-off-by: Laurent Pinchart Reviewed-by: Geert Uytterhoeven Reviewed-by: Niklas Söderlund Reviewed-by: Rob Herring Signed-off-by: Mauro Carvalho Chehab --- .../devicetree/bindings/media/renesas,fcp.txt | 34 ----------- .../bindings/media/renesas,fcp.yaml | 56 +++++++++++++++++++ MAINTAINERS | 2 +- 3 files changed, 57 insertions(+), 35 deletions(-) delete mode 100644 Documentation/devicetree/bindings/media/renesas,fcp.txt create mode 100644 Documentation/devicetree/bindings/media/renesas,fcp.yaml diff --git a/Documentation/devicetree/bindings/media/renesas,fcp.txt b/Documentation/devicetree/bindings/media/renesas,fcp.txt deleted file mode 100644 index 79c37395b3964..0000000000000 --- a/Documentation/devicetree/bindings/media/renesas,fcp.txt +++ /dev/null @@ -1,34 +0,0 @@ -Renesas R-Car Frame Compression Processor (FCP) ------------------------------------------------ - -The FCP is a companion module of video processing modules in the Renesas R-Car -Gen3 and RZ/G2 SoCs. It provides data compression and decompression, data -caching, and conversion of AXI transactions in order to reduce the memory -bandwidth. - -There are three types of FCP: FCP for Codec (FCPC), FCP for VSP (FCPV) and FCP -for FDP (FCPF). Their configuration and behaviour depend on the module they -are paired with. These DT bindings currently support the FCPV and FCPF. - - - compatible: Must be one or more of the following - - - "renesas,fcpv" for generic compatible 'FCP for VSP' - - "renesas,fcpf" for generic compatible 'FCP for FDP' - - - reg: the register base and size for the device registers - - clocks: Reference to the functional clock - -Optional properties: - - power-domains : power-domain property defined with a power domain specifier - to respective power domain. - - -Device node example -------------------- - - fcpvd1: fcp@fea2f000 { - compatible = "renesas,fcpv"; - reg = <0 0xfea2f000 0 0x200>; - clocks = <&cpg CPG_MOD 602>; - power-domains = <&sysc R8A7795_PD_A3VP>; - }; diff --git a/Documentation/devicetree/bindings/media/renesas,fcp.yaml b/Documentation/devicetree/bindings/media/renesas,fcp.yaml new file mode 100644 index 0000000000000..ec0cb42ca4648 --- /dev/null +++ b/Documentation/devicetree/bindings/media/renesas,fcp.yaml @@ -0,0 +1,56 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/media/renesas,fcp.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Renesas R-Car Frame Compression Processor (FCP) + +maintainers: + - Laurent Pinchart + +description: | + The FCP is a companion module of video processing modules in the Renesas + R-Car Gen3 and RZ/G2 SoCs. It provides data compression and decompression, + data caching, and conversion of AXI transactions in order to reduce the + memory bandwidth. + + There are three types of FCP: FCP for Codec (FCPC), FCP for VSP (FCPV) and + FCP for FDP (FCPF). Their configuration and behaviour depend on the module + they are paired with. These DT bindings currently support the FCPV and FCPF. + +properties: + compatible: + enum: + - renesas,fcpv # FCP for VSP + - renesas,fcpf # FCP for FDP + + reg: + maxItems: 1 + + clocks: + maxItems: 1 + + power-domains: + maxItems: 1 + +required: + - compatible + - reg + - clocks + +additionalProperties: false + +examples: + # R8A7795 (R-Car H3) FCP for VSP-D1 + - | + #include + #include + + fcp@fea2f000 { + compatible = "renesas,fcpv"; + reg = <0xfea2f000 0x200>; + clocks = <&cpg CPG_MOD 602>; + power-domains = <&sysc R8A7795_PD_ALWAYS_ON>; + }; +... diff --git a/MAINTAINERS b/MAINTAINERS index 51c4cca11e0bb..0ab9d4d4728ba 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -10726,7 +10726,7 @@ L: linux-media@vger.kernel.org L: linux-renesas-soc@vger.kernel.org S: Supported T: git git://linuxtv.org/media_tree.git -F: Documentation/devicetree/bindings/media/renesas,fcp.txt +F: Documentation/devicetree/bindings/media/renesas,fcp.yaml F: drivers/media/platform/rcar-fcp.c F: include/media/rcar-fcp.h -- GitLab From 89b85a6d10602bca2793d69b4f19749c9258b816 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Fri, 15 May 2020 03:08:55 +0200 Subject: [PATCH 0680/1476] media: dt-bindings: media: renesas,fcp: Make power-domains mandatory MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit All DT source files in the kernel tree specify the power-domains property. Make it mandatory. Signed-off-by: Laurent Pinchart Reviewed-by: Geert Uytterhoeven Reviewed-by: Niklas Söderlund Acked-by: Rob Herring Signed-off-by: Mauro Carvalho Chehab --- Documentation/devicetree/bindings/media/renesas,fcp.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/media/renesas,fcp.yaml b/Documentation/devicetree/bindings/media/renesas,fcp.yaml index ec0cb42ca4648..a41a344382ad6 100644 --- a/Documentation/devicetree/bindings/media/renesas,fcp.yaml +++ b/Documentation/devicetree/bindings/media/renesas,fcp.yaml @@ -38,6 +38,7 @@ required: - compatible - reg - clocks + - power-domains additionalProperties: false -- GitLab From 08cdb85b853047e18344822cc257b5fa49707a27 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Fri, 15 May 2020 03:08:55 +0200 Subject: [PATCH 0681/1476] media: dt-bindings: media: renesas,fcp: Add resets and iommus properties MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The resets and iommus properties are used in DT sources in the kernel tree. Document them, and make resets mandatory. The iommus property is optional as not all platforms wire the FCP to a functional IOMMU. Signed-off-by: Laurent Pinchart Reviewed-by: Geert Uytterhoeven Reviewed-by: Niklas Söderlund Acked-by: Rob Herring Signed-off-by: Mauro Carvalho Chehab --- Documentation/devicetree/bindings/media/renesas,fcp.yaml | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/Documentation/devicetree/bindings/media/renesas,fcp.yaml b/Documentation/devicetree/bindings/media/renesas,fcp.yaml index a41a344382ad6..43f2fed8cd33b 100644 --- a/Documentation/devicetree/bindings/media/renesas,fcp.yaml +++ b/Documentation/devicetree/bindings/media/renesas,fcp.yaml @@ -31,14 +31,21 @@ properties: clocks: maxItems: 1 + iommus: + maxItems: 1 + power-domains: maxItems: 1 + resets: + maxItems: 1 + required: - compatible - reg - clocks - power-domains + - resets additionalProperties: false @@ -53,5 +60,7 @@ examples: reg = <0xfea2f000 0x200>; clocks = <&cpg CPG_MOD 602>; power-domains = <&sysc R8A7795_PD_ALWAYS_ON>; + resets = <&cpg 602>; + iommus = <&ipmmu_vi0 9>; }; ... -- GitLab From 5166e28ae4a6e84a2fcb6e89176d24ae00b09466 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Fri, 15 May 2020 02:25:28 +0200 Subject: [PATCH 0682/1476] media: dt-bindings: media: renesas,fdp1: Convert binding to YAML MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Convert the Renesas R-Car FDP1 text binding to YAML. Signed-off-by: Laurent Pinchart Reviewed-by: Geert Uytterhoeven Reviewed-by: Niklas Söderlund Reviewed-by: Rob Herring Signed-off-by: Mauro Carvalho Chehab --- .../bindings/media/renesas,fdp1.txt | 37 ----------- .../bindings/media/renesas,fdp1.yaml | 63 +++++++++++++++++++ MAINTAINERS | 2 +- 3 files changed, 64 insertions(+), 38 deletions(-) delete mode 100644 Documentation/devicetree/bindings/media/renesas,fdp1.txt create mode 100644 Documentation/devicetree/bindings/media/renesas,fdp1.yaml diff --git a/Documentation/devicetree/bindings/media/renesas,fdp1.txt b/Documentation/devicetree/bindings/media/renesas,fdp1.txt deleted file mode 100644 index 8dd1007bb573a..0000000000000 --- a/Documentation/devicetree/bindings/media/renesas,fdp1.txt +++ /dev/null @@ -1,37 +0,0 @@ -Renesas R-Car Fine Display Processor (FDP1) -------------------------------------------- - -The FDP1 is a de-interlacing module which converts interlaced video to -progressive video. It is capable of performing pixel format conversion between -YCbCr/YUV formats and RGB formats. Only YCbCr/YUV formats are supported as -an input to the module. - -Required properties: - - - compatible: must be "renesas,fdp1" - - reg: the register base and size for the device registers - - interrupts : interrupt specifier for the FDP1 instance - - clocks: reference to the functional clock - -Optional properties: - - - power-domains: reference to the power domain that the FDP1 belongs to, if - any. - - renesas,fcp: a phandle referencing the FCP that handles memory accesses - for the FDP1. Not needed on Gen2, mandatory on Gen3. - -Please refer to the binding documentation for the clock and/or power domain -providers for more details. - - -Device node example -------------------- - - fdp1@fe940000 { - compatible = "renesas,fdp1"; - reg = <0 0xfe940000 0 0x2400>; - interrupts = ; - clocks = <&cpg CPG_MOD 119>; - power-domains = <&sysc R8A7795_PD_A3VP>; - renesas,fcp = <&fcpf0>; - }; diff --git a/Documentation/devicetree/bindings/media/renesas,fdp1.yaml b/Documentation/devicetree/bindings/media/renesas,fdp1.yaml new file mode 100644 index 0000000000000..f4db96a1f53cd --- /dev/null +++ b/Documentation/devicetree/bindings/media/renesas,fdp1.yaml @@ -0,0 +1,63 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/media/renesas,fdp1.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Renesas R-Car Fine Display Processor (FDP1) + +maintainers: + - Laurent Pinchart + +description: + The FDP1 is a de-interlacing module which converts interlaced video to + progressive video. It is capable of performing pixel format conversion + between YCbCr/YUV formats and RGB formats. Only YCbCr/YUV formats are + supported as an input to the module. + +properties: + compatible: + enum: + - renesas,fdp1 + + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + + clocks: + maxItems: 1 + + power-domains: + maxItems: 1 + + renesas,fcp: + $ref: /schemas/types.yaml#/definitions/phandle + description: + A phandle referencing the FCP that handles memory accesses for the FDP1. + Not allowed on R-Car Gen2, mandatory on R-Car Gen3. + +required: + - compatible + - reg + - interrupts + - clocks + +additionalProperties: false + +examples: + - | + #include + #include + #include + + fdp1@fe940000 { + compatible = "renesas,fdp1"; + reg = <0xfe940000 0x2400>; + interrupts = ; + clocks = <&cpg CPG_MOD 119>; + power-domains = <&sysc R8A7795_PD_A3VP>; + renesas,fcp = <&fcpf0>; + }; +... diff --git a/MAINTAINERS b/MAINTAINERS index 0ab9d4d4728ba..3187590d23a7b 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -10736,7 +10736,7 @@ L: linux-media@vger.kernel.org L: linux-renesas-soc@vger.kernel.org S: Supported T: git git://linuxtv.org/media_tree.git -F: Documentation/devicetree/bindings/media/renesas,fdp1.txt +F: Documentation/devicetree/bindings/media/renesas,fdp1.yaml F: drivers/media/platform/rcar_fdp1.c MEDIA DRIVERS FOR RENESAS - VIN -- GitLab From f4e9654a79ca015f041cb8b3848d6557309a9ba1 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Fri, 15 May 2020 03:08:55 +0200 Subject: [PATCH 0683/1476] media: dt-bindings: media: renesas,fdp1: Make power-domains mandatory MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit All DT source files in the kernel tree specify the power-domains property. Make it mandatory. Signed-off-by: Laurent Pinchart Reviewed-by: Geert Uytterhoeven Reviewed-by: Niklas Söderlund Acked-by: Rob Herring Signed-off-by: Mauro Carvalho Chehab --- Documentation/devicetree/bindings/media/renesas,fdp1.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/media/renesas,fdp1.yaml b/Documentation/devicetree/bindings/media/renesas,fdp1.yaml index f4db96a1f53cd..39184bd21a277 100644 --- a/Documentation/devicetree/bindings/media/renesas,fdp1.yaml +++ b/Documentation/devicetree/bindings/media/renesas,fdp1.yaml @@ -43,6 +43,7 @@ required: - reg - interrupts - clocks + - power-domains additionalProperties: false -- GitLab From 6249bda68f88faf63411d9aaafca6f90d546b622 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Fri, 15 May 2020 03:08:55 +0200 Subject: [PATCH 0684/1476] media: dt-bindings: media: renesas,fdp1: Add resets property MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The resets property is used in DT sources in the kernel tree. Document it and make it mandatory. Signed-off-by: Laurent Pinchart Reviewed-by: Geert Uytterhoeven Reviewed-by: Niklas Söderlund Acked-by: Rob Herring Signed-off-by: Mauro Carvalho Chehab --- Documentation/devicetree/bindings/media/renesas,fdp1.yaml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Documentation/devicetree/bindings/media/renesas,fdp1.yaml b/Documentation/devicetree/bindings/media/renesas,fdp1.yaml index 39184bd21a277..2a27a7296feab 100644 --- a/Documentation/devicetree/bindings/media/renesas,fdp1.yaml +++ b/Documentation/devicetree/bindings/media/renesas,fdp1.yaml @@ -32,6 +32,9 @@ properties: power-domains: maxItems: 1 + resets: + maxItems: 1 + renesas,fcp: $ref: /schemas/types.yaml#/definitions/phandle description: @@ -44,6 +47,7 @@ required: - interrupts - clocks - power-domains + - resets additionalProperties: false @@ -59,6 +63,7 @@ examples: interrupts = ; clocks = <&cpg CPG_MOD 119>; power-domains = <&sysc R8A7795_PD_A3VP>; + resets = <&cpg 119>; renesas,fcp = <&fcpf0>; }; ... -- GitLab From 6a72c8d3cdeb582c3574f85e98394cd81b1d7640 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Fri, 15 May 2020 02:25:28 +0200 Subject: [PATCH 0685/1476] media: dt-bindings: media: renesas,vsp1: Convert binding to YAML MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Convert the Renesas R-Car VSP1 text binding to YAML. Signed-off-by: Laurent Pinchart Reviewed-by: Geert Uytterhoeven Reviewed-by: Niklas Söderlund Reviewed-by: Rob Herring Signed-off-by: Mauro Carvalho Chehab --- .../bindings/media/renesas,vsp1.txt | 30 ------- .../bindings/media/renesas,vsp1.yaml | 83 +++++++++++++++++++ MAINTAINERS | 2 +- 3 files changed, 84 insertions(+), 31 deletions(-) delete mode 100644 Documentation/devicetree/bindings/media/renesas,vsp1.txt create mode 100644 Documentation/devicetree/bindings/media/renesas,vsp1.yaml diff --git a/Documentation/devicetree/bindings/media/renesas,vsp1.txt b/Documentation/devicetree/bindings/media/renesas,vsp1.txt deleted file mode 100644 index cd5a955b2ea00..0000000000000 --- a/Documentation/devicetree/bindings/media/renesas,vsp1.txt +++ /dev/null @@ -1,30 +0,0 @@ -* Renesas VSP Video Processing Engine - -The VSP is a video processing engine that supports up-/down-scaling, alpha -blending, color space conversion and various other image processing features. -It can be found in the Renesas R-Car Gen2, R-Car Gen3, RZ/G1, and RZ/G2 SoCs. - -Required properties: - - - compatible: Must contain one of the following values - - "renesas,vsp1" for the R-Car Gen2 and RZ/G1 VSP1 - - "renesas,vsp2" for the R-Car Gen3 and RZ/G2 VSP2 - - - reg: Base address and length of the registers block for the VSP. - - interrupts: VSP interrupt specifier. - - clocks: A phandle + clock-specifier pair for the VSP functional clock. - -Optional properties: - - - renesas,fcp: A phandle referencing the FCP that handles memory accesses - for the VSP. Not needed on Gen2, mandatory on Gen3. - - -Example: R8A7790 (R-Car H2) VSP1-S node - - vsp@fe928000 { - compatible = "renesas,vsp1"; - reg = <0 0xfe928000 0 0x8000>; - interrupts = <0 267 IRQ_TYPE_LEVEL_HIGH>; - clocks = <&mstp1_clks R8A7790_CLK_VSP1_S>; - }; diff --git a/Documentation/devicetree/bindings/media/renesas,vsp1.yaml b/Documentation/devicetree/bindings/media/renesas,vsp1.yaml new file mode 100644 index 0000000000000..65e8ee61ce908 --- /dev/null +++ b/Documentation/devicetree/bindings/media/renesas,vsp1.yaml @@ -0,0 +1,83 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/media/renesas,vsp1.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Renesas VSP Video Processing Engine + +maintainers: + - Laurent Pinchart + +description: + The VSP is a video processing engine that supports up-/down-scaling, alpha + blending, color space conversion and various other image processing features. + It can be found in the Renesas R-Car Gen2, R-Car Gen3, RZ/G1, and RZ/G2 SoCs. + +properties: + compatible: + enum: + - renesas,vsp1 # R-Car Gen2 and RZ/G1 + - renesas,vsp2 # R-Car Gen3 and RZ/G2 + + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + + clocks: + maxItems: 1 + + renesas,fcp: + $ref: /schemas/types.yaml#/definitions/phandle + description: + A phandle referencing the FCP that handles memory accesses for the VSP. + +required: + - compatible + - reg + - interrupts + - clocks + +additionalProperties: false + +if: + properties: + compatible: + items: + - const: renesas,vsp1 +then: + properties: + renesas,fcp: false +else: + required: + - renesas,fcp + +examples: + # R8A7790 (R-Car H2) VSP1-S + - | + #include + #include + + vsp@fe928000 { + compatible = "renesas,vsp1"; + reg = <0xfe928000 0x8000>; + interrupts = ; + clocks = <&cpg CPG_MOD 131>; + }; + + # R8A77951 (R-Car H3) VSP2-BC + - | + #include + #include + + vsp@fe920000 { + compatible = "renesas,vsp2"; + reg = <0xfe920000 0x8000>; + interrupts = ; + clocks = <&cpg CPG_MOD 624>; + + renesas,fcp = <&fcpvb1>; + }; +... diff --git a/MAINTAINERS b/MAINTAINERS index 3187590d23a7b..5392f00cec46d 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -10756,7 +10756,7 @@ L: linux-media@vger.kernel.org L: linux-renesas-soc@vger.kernel.org S: Supported T: git git://linuxtv.org/media_tree.git -F: Documentation/devicetree/bindings/media/renesas,vsp1.txt +F: Documentation/devicetree/bindings/media/renesas,vsp1.yaml F: drivers/media/platform/vsp1/ MEDIA DRIVERS FOR ST STV0910 DEMODULATOR ICs -- GitLab From cc4fcf11a8d9d249db479f1ef16e6d26c830b245 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Fri, 15 May 2020 02:25:28 +0200 Subject: [PATCH 0686/1476] media: dt-bindings: media: renesas,vsp1: Add power-domains and resets MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The power-domains and resets properties are used in all DT sources in the kernel but are absent from the bindings. Document them and make them mandatory. Signed-off-by: Laurent Pinchart Reviewed-by: Geert Uytterhoeven Reviewed-by: Niklas Söderlund Acked-by: Rob Herring Signed-off-by: Mauro Carvalho Chehab --- .../devicetree/bindings/media/renesas,vsp1.yaml | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/Documentation/devicetree/bindings/media/renesas,vsp1.yaml b/Documentation/devicetree/bindings/media/renesas,vsp1.yaml index 65e8ee61ce908..990e9c1dbc431 100644 --- a/Documentation/devicetree/bindings/media/renesas,vsp1.yaml +++ b/Documentation/devicetree/bindings/media/renesas,vsp1.yaml @@ -29,6 +29,12 @@ properties: clocks: maxItems: 1 + power-domains: + maxItems: 1 + + resets: + maxItems: 1 + renesas,fcp: $ref: /schemas/types.yaml#/definitions/phandle description: @@ -39,6 +45,8 @@ required: - reg - interrupts - clocks + - power-domains + - resets additionalProperties: false @@ -59,24 +67,30 @@ examples: - | #include #include + #include vsp@fe928000 { compatible = "renesas,vsp1"; reg = <0xfe928000 0x8000>; interrupts = ; clocks = <&cpg CPG_MOD 131>; + power-domains = <&sysc R8A7790_PD_ALWAYS_ON>; + resets = <&cpg 131>; }; # R8A77951 (R-Car H3) VSP2-BC - | #include #include + #include vsp@fe920000 { compatible = "renesas,vsp2"; reg = <0xfe920000 0x8000>; interrupts = ; clocks = <&cpg CPG_MOD 624>; + power-domains = <&sysc R8A7795_PD_A3VP>; + resets = <&cpg 624>; renesas,fcp = <&fcpvb1>; }; -- GitLab From 2856bbc55bda13d41eab904234be30e52ceeeae9 Mon Sep 17 00:00:00 2001 From: Vaibhav Gupta Date: Fri, 17 Jul 2020 09:34:31 +0200 Subject: [PATCH 0687/1476] media: sta2x11: use generic power management With legacy PM, drivers themselves were responsible for managing the device's power states and takes care of register states. After upgrading to the generic structure, PCI core will take care of required tasks and drivers should do only device-specific operations. Thus, there is no need to call the PCI helper functions like pci_enable_device(), pci_save/restore_sate(), etc. Compile-tested only. Signed-off-by: Vaibhav Gupta Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/pci/sta2x11/sta2x11_vip.c | 63 ++++++------------------- 1 file changed, 15 insertions(+), 48 deletions(-) diff --git a/drivers/media/pci/sta2x11/sta2x11_vip.c b/drivers/media/pci/sta2x11/sta2x11_vip.c index 798574cfad35e..0fdb0fd6e7646 100644 --- a/drivers/media/pci/sta2x11/sta2x11_vip.c +++ b/drivers/media/pci/sta2x11/sta2x11_vip.c @@ -1167,21 +1167,18 @@ static void sta2x11_vip_remove_one(struct pci_dev *pdev) */ } -#ifdef CONFIG_PM - /** * sta2x11_vip_suspend - set device into power save mode - * @pdev: PCI device - * @state: new state of device + * @dev_d: PCI device * * all relevant registers are saved and an attempt to set a new state is made. * * return value: 0 always indicate success, * even if device could not be disabled. (workaround for hardware problem) */ -static int sta2x11_vip_suspend(struct pci_dev *pdev, pm_message_t state) +static int __maybe_unused sta2x11_vip_suspend(struct device *dev_d) { - struct v4l2_device *v4l2_dev = pci_get_drvdata(pdev); + struct v4l2_device *v4l2_dev = dev_get_drvdata(dev_d); struct sta2x11_vip *vip = container_of(v4l2_dev, struct sta2x11_vip, v4l2_dev); unsigned long flags; @@ -1198,15 +1195,8 @@ static int sta2x11_vip_suspend(struct pci_dev *pdev, pm_message_t state) vip->register_save_area[SAVE_COUNT + IRQ_COUNT + i] = reg_read(vip, registers_to_save[i]); spin_unlock_irqrestore(&vip->slock, flags); - /* save pci state */ - pci_save_state(pdev); - if (pci_set_power_state(pdev, pci_choose_state(pdev, state))) { - /* - * do not call pci_disable_device on sta2x11 because it - * break all other Bus masters on this EP - */ - vip->disabled = 1; - } + + vip->disabled = 1; pr_info("VIP: suspend\n"); return 0; @@ -1214,45 +1204,23 @@ static int sta2x11_vip_suspend(struct pci_dev *pdev, pm_message_t state) /** * sta2x11_vip_resume - resume device operation - * @pdev : PCI device - * - * re-enable device, set PCI state to powered and restore registers. - * resume normal device operation afterwards. + * @dev_d : PCI device * * return value: 0, no error. * * other, could not set device to power on state. */ -static int sta2x11_vip_resume(struct pci_dev *pdev) +static int __maybe_unused sta2x11_vip_resume(struct device *dev_d) { - struct v4l2_device *v4l2_dev = pci_get_drvdata(pdev); + struct v4l2_device *v4l2_dev = dev_get_drvdata(dev_d); struct sta2x11_vip *vip = container_of(v4l2_dev, struct sta2x11_vip, v4l2_dev); unsigned long flags; - int ret, i; + int i; pr_info("VIP: resume\n"); - /* restore pci state */ - if (vip->disabled) { - ret = pci_enable_device(pdev); - if (ret) { - pr_warn("VIP: Can't enable device.\n"); - return ret; - } - vip->disabled = 0; - } - ret = pci_set_power_state(pdev, PCI_D0); - if (ret) { - /* - * do not call pci_disable_device on sta2x11 because it - * break all other Bus masters on this EP - */ - pr_warn("VIP: Can't enable device.\n"); - vip->disabled = 1; - return ret; - } - pci_restore_state(pdev); + vip->disabled = 0; spin_lock_irqsave(&vip->slock, flags); for (i = 1; i < SAVE_COUNT; i++) @@ -1266,22 +1234,21 @@ static int sta2x11_vip_resume(struct pci_dev *pdev) return 0; } -#endif - static const struct pci_device_id sta2x11_vip_pci_tbl[] = { {PCI_DEVICE(PCI_VENDOR_ID_STMICRO, PCI_DEVICE_ID_STMICRO_VIP)}, {0,} }; +static SIMPLE_DEV_PM_OPS(sta2x11_vip_pm_ops, + sta2x11_vip_suspend, + sta2x11_vip_resume); + static struct pci_driver sta2x11_vip_driver = { .name = KBUILD_MODNAME, .probe = sta2x11_vip_init_one, .remove = sta2x11_vip_remove_one, .id_table = sta2x11_vip_pci_tbl, -#ifdef CONFIG_PM - .suspend = sta2x11_vip_suspend, - .resume = sta2x11_vip_resume, -#endif + .driver.pm = &sta2x11_vip_pm_ops, }; static int __init sta2x11_vip_init_module(void) -- GitLab From d6f6582890a43e2a88364aeeeb5414d75f7553d5 Mon Sep 17 00:00:00 2001 From: Vaibhav Gupta Date: Fri, 17 Jul 2020 09:34:32 +0200 Subject: [PATCH 0688/1476] media: cx23885: use generic power management The .suspend() and .resume() callbacks are not defined for this driver. Still, their power management structure follows the legacy framework. To bring it under the generic framework, simply remove the binding of callbacks from struct "pci_driver". Compile-tested only. Signed-off-by: Vaibhav Gupta Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/pci/cx23885/cx23885-core.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/media/pci/cx23885/cx23885-core.c b/drivers/media/pci/cx23885/cx23885-core.c index 7e0b0b7cc2a35..4b0c53f61fb7c 100644 --- a/drivers/media/pci/cx23885/cx23885-core.c +++ b/drivers/media/pci/cx23885/cx23885-core.c @@ -2235,9 +2235,6 @@ static struct pci_driver cx23885_pci_driver = { .id_table = cx23885_pci_tbl, .probe = cx23885_initdev, .remove = cx23885_finidev, - /* TODO */ - .suspend = NULL, - .resume = NULL, }; static int __init cx23885_init(void) -- GitLab From fc1f0e67f68f78da8426632e7ff3703b93148dba Mon Sep 17 00:00:00 2001 From: Vaibhav Gupta Date: Fri, 17 Jul 2020 09:34:33 +0200 Subject: [PATCH 0689/1476] media: cx25821: use generic power management The .suspend() and .resume() callbacks are not defined for this driver. Still, their power management structure follows the legacy framework. To bring it under the generic framework, simply remove the binding of callbacks from struct "pci_driver". Compile-tested only. Signed-off-by: Vaibhav Gupta Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/pci/cx25821/cx25821-core.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/media/pci/cx25821/cx25821-core.c b/drivers/media/pci/cx25821/cx25821-core.c index 41be22ce66f3e..55018d9e439fb 100644 --- a/drivers/media/pci/cx25821/cx25821-core.c +++ b/drivers/media/pci/cx25821/cx25821-core.c @@ -1374,9 +1374,6 @@ static struct pci_driver cx25821_pci_driver = { .id_table = cx25821_pci_tbl, .probe = cx25821_initdev, .remove = cx25821_finidev, - /* TODO */ - .suspend = NULL, - .resume = NULL, }; static int __init cx25821_init(void) -- GitLab From a28b2ed9ad0551a18aaa2fa6ba8dd3d3e8cfb27f Mon Sep 17 00:00:00 2001 From: Vaibhav Gupta Date: Fri, 17 Jul 2020 09:34:34 +0200 Subject: [PATCH 0690/1476] media: cx88: use generic power management With legacy PM, drivers themselves were responsible for managing the device's power states and takes care of register states. After upgrading to the generic structure, PCI core will take care of required tasks and drivers should do only device-specific operations. The driver was invoking PCI helper functions like pci_save/restore_state(), pci_enable/disable_device() and pci_set_power_state(), which is not recommended. Compile-tested only. [hverkuil: fixes checkpatch alignment warning] Signed-off-by: Vaibhav Gupta Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/pci/cx88/cx88-video.c | 60 ++++++++--------------------- 1 file changed, 16 insertions(+), 44 deletions(-) diff --git a/drivers/media/pci/cx88/cx88-video.c b/drivers/media/pci/cx88/cx88-video.c index ba0e9660a047c..e7fd7516787c6 100644 --- a/drivers/media/pci/cx88/cx88-video.c +++ b/drivers/media/pci/cx88/cx88-video.c @@ -385,8 +385,7 @@ static int start_video_dma(struct cx8800_dev *dev, return 0; } -#ifdef CONFIG_PM -static int stop_video_dma(struct cx8800_dev *dev) +static int __maybe_unused stop_video_dma(struct cx8800_dev *dev) { struct cx88_core *core = dev->core; @@ -402,8 +401,8 @@ static int stop_video_dma(struct cx8800_dev *dev) return 0; } -static int restart_video_queue(struct cx8800_dev *dev, - struct cx88_dmaqueue *q) +static int __maybe_unused restart_video_queue(struct cx8800_dev *dev, + struct cx88_dmaqueue *q) { struct cx88_buffer *buf; @@ -415,7 +414,6 @@ static int restart_video_queue(struct cx8800_dev *dev, } return 0; } -#endif /* ------------------------------------------------------------------ */ @@ -1551,10 +1549,9 @@ static void cx8800_finidev(struct pci_dev *pci_dev) kfree(dev); } -#ifdef CONFIG_PM -static int cx8800_suspend(struct pci_dev *pci_dev, pm_message_t state) +static int __maybe_unused cx8800_suspend(struct device *dev_d) { - struct cx8800_dev *dev = pci_get_drvdata(pci_dev); + struct cx8800_dev *dev = dev_get_drvdata(dev_d); struct cx88_core *core = dev->core; unsigned long flags; @@ -1575,40 +1572,17 @@ static int cx8800_suspend(struct pci_dev *pci_dev, pm_message_t state) /* FIXME -- shutdown device */ cx88_shutdown(core); - pci_save_state(pci_dev); - if (pci_set_power_state(pci_dev, - pci_choose_state(pci_dev, state)) != 0) { - pci_disable_device(pci_dev); - dev->state.disabled = 1; - } + dev->state.disabled = 1; return 0; } -static int cx8800_resume(struct pci_dev *pci_dev) +static int __maybe_unused cx8800_resume(struct device *dev_d) { - struct cx8800_dev *dev = pci_get_drvdata(pci_dev); + struct cx8800_dev *dev = dev_get_drvdata(dev_d); struct cx88_core *core = dev->core; unsigned long flags; - int err; - if (dev->state.disabled) { - err = pci_enable_device(pci_dev); - if (err) { - pr_err("can't enable device\n"); - return err; - } - - dev->state.disabled = 0; - } - err = pci_set_power_state(pci_dev, PCI_D0); - if (err) { - pr_err("can't set power state\n"); - pci_disable_device(pci_dev); - dev->state.disabled = 1; - - return err; - } - pci_restore_state(pci_dev); + dev->state.disabled = 0; /* FIXME: re-initialize hardware */ cx88_reset(core); @@ -1631,7 +1605,6 @@ static int cx8800_resume(struct pci_dev *pci_dev) return 0; } -#endif /* ----------------------------------------------------------- */ @@ -1647,15 +1620,14 @@ static const struct pci_device_id cx8800_pci_tbl[] = { }; MODULE_DEVICE_TABLE(pci, cx8800_pci_tbl); +static SIMPLE_DEV_PM_OPS(cx8800_pm_ops, cx8800_suspend, cx8800_resume); + static struct pci_driver cx8800_pci_driver = { - .name = "cx8800", - .id_table = cx8800_pci_tbl, - .probe = cx8800_initdev, - .remove = cx8800_finidev, -#ifdef CONFIG_PM - .suspend = cx8800_suspend, - .resume = cx8800_resume, -#endif + .name = "cx8800", + .id_table = cx8800_pci_tbl, + .probe = cx8800_initdev, + .remove = cx8800_finidev, + .driver.pm = &cx8800_pm_ops, }; module_pci_driver(cx8800_pci_driver); -- GitLab From 9c4a8489ab7c29f46beedc5c288a2eacb431c229 Mon Sep 17 00:00:00 2001 From: Vaibhav Gupta Date: Fri, 17 Jul 2020 09:34:35 +0200 Subject: [PATCH 0691/1476] media: meye: use generic power management With legacy PM, drivers themselves were responsible for managing the device's power states and takes care of register states. After upgrading to the generic structure, PCI core will take care of required tasks and drivers should do only device-specific operations. The driver was invoking PCI helper functions like pci_save/restore_state() which is not recommended. Compile-tested only. Signed-off-by: Vaibhav Gupta Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/pci/meye/meye.c | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/drivers/media/pci/meye/meye.c b/drivers/media/pci/meye/meye.c index 73e064e6f56df..7fb3b1853b872 100644 --- a/drivers/media/pci/meye/meye.c +++ b/drivers/media/pci/meye/meye.c @@ -1528,19 +1528,16 @@ static const struct v4l2_ctrl_ops meye_ctrl_ops = { .s_ctrl = meye_s_ctrl, }; -#ifdef CONFIG_PM -static int meye_suspend(struct pci_dev *pdev, pm_message_t state) +static int __maybe_unused meye_suspend(struct device *dev) { - pci_save_state(pdev); meye.pm_mchip_mode = meye.mchip_mode; mchip_hic_stop(); mchip_set(MCHIP_MM_INTA, 0x0); return 0; } -static int meye_resume(struct pci_dev *pdev) +static int __maybe_unused meye_resume(struct device *dev) { - pci_restore_state(pdev); pci_write_config_word(meye.mchip_dev, MCHIP_PCI_SOFTRESET_SET, 1); mchip_delay(MCHIP_HIC_CMD, 0); @@ -1562,7 +1559,6 @@ static int meye_resume(struct pci_dev *pdev) } return 0; } -#endif static int meye_probe(struct pci_dev *pcidev, const struct pci_device_id *ent) { @@ -1788,15 +1784,14 @@ static const struct pci_device_id meye_pci_tbl[] = { MODULE_DEVICE_TABLE(pci, meye_pci_tbl); +static SIMPLE_DEV_PM_OPS(meye_pm_ops, meye_suspend, meye_resume); + static struct pci_driver meye_driver = { .name = "meye", .id_table = meye_pci_tbl, .probe = meye_probe, .remove = meye_remove, -#ifdef CONFIG_PM - .suspend = meye_suspend, - .resume = meye_resume, -#endif + .driver.pm = &meye_pm_ops, }; static int __init meye_init(void) -- GitLab From 68e966a3796ed69314ed106a4cea891a4dec48f6 Mon Sep 17 00:00:00 2001 From: Vaibhav Gupta Date: Fri, 17 Jul 2020 09:34:36 +0200 Subject: [PATCH 0692/1476] media: tw68: use generic power management With legacy PM, drivers themselves were responsible for managing the device's power states and takes care of register states. After upgrading to the generic structure, PCI core will take care of required tasks and drivers should do only device-specific operations. The driver was invoking PCI helper functions like pci_save/restore_state() which is not recommended. Compile-Tested only. Signed-off-by: Vaibhav Gupta Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/pci/tw68/tw68-core.c | 30 +++++++++++------------------- 1 file changed, 11 insertions(+), 19 deletions(-) diff --git a/drivers/media/pci/tw68/tw68-core.c b/drivers/media/pci/tw68/tw68-core.c index 30c1ea87f0e87..bf15fa7c0ea18 100644 --- a/drivers/media/pci/tw68/tw68-core.c +++ b/drivers/media/pci/tw68/tw68-core.c @@ -359,10 +359,9 @@ static void tw68_finidev(struct pci_dev *pci_dev) v4l2_device_unregister(&dev->v4l2_dev); } -#ifdef CONFIG_PM - -static int tw68_suspend(struct pci_dev *pci_dev , pm_message_t state) +static int __maybe_unused tw68_suspend(struct device *dev_d) { + struct pci_dev *pci_dev = to_pci_dev(dev_d); struct v4l2_device *v4l2_dev = pci_get_drvdata(pci_dev); struct tw68_dev *dev = container_of(v4l2_dev, struct tw68_dev, v4l2_dev); @@ -373,24 +372,19 @@ static int tw68_suspend(struct pci_dev *pci_dev , pm_message_t state) synchronize_irq(pci_dev->irq); - pci_save_state(pci_dev); - pci_set_power_state(pci_dev, pci_choose_state(pci_dev, state)); vb2_discard_done(&dev->vidq); return 0; } -static int tw68_resume(struct pci_dev *pci_dev) +static int __maybe_unused tw68_resume(struct device *dev_d) { - struct v4l2_device *v4l2_dev = pci_get_drvdata(pci_dev); + struct v4l2_device *v4l2_dev = dev_get_drvdata(dev_d); struct tw68_dev *dev = container_of(v4l2_dev, struct tw68_dev, v4l2_dev); struct tw68_buf *buf; unsigned long flags; - pci_set_power_state(pci_dev, PCI_D0); - pci_restore_state(pci_dev); - /* Do things that are done in tw68_initdev , except of initializing memory structures.*/ @@ -408,19 +402,17 @@ static int tw68_resume(struct pci_dev *pci_dev) return 0; } -#endif /* ----------------------------------------------------------- */ +static SIMPLE_DEV_PM_OPS(tw68_pm_ops, tw68_suspend, tw68_resume); + static struct pci_driver tw68_pci_driver = { - .name = "tw68", - .id_table = tw68_pci_tbl, - .probe = tw68_initdev, - .remove = tw68_finidev, -#ifdef CONFIG_PM - .suspend = tw68_suspend, - .resume = tw68_resume -#endif + .name = "tw68", + .id_table = tw68_pci_tbl, + .probe = tw68_initdev, + .remove = tw68_finidev, + .driver.pm = &tw68_pm_ops, }; module_pci_driver(tw68_pci_driver); -- GitLab From 1c79e41b115f57f281b54b5bc699d0e01dbcd111 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Thu, 16 Jul 2020 13:32:57 +0200 Subject: [PATCH 0693/1476] media: colorspaces-details.rst: fix V4L2_COLORSPACE_JPEG description The description was missing V4L2_XFER_FUNC_SRGB in the description of what V4L2_COLORSPACE_JPEG stands for. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- Documentation/userspace-api/media/v4l/colorspaces-details.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Documentation/userspace-api/media/v4l/colorspaces-details.rst b/Documentation/userspace-api/media/v4l/colorspaces-details.rst index 79ed6f4f76eb3..300c5d2e7d0f0 100644 --- a/Documentation/userspace-api/media/v4l/colorspaces-details.rst +++ b/Documentation/userspace-api/media/v4l/colorspaces-details.rst @@ -767,8 +767,8 @@ scaled to [-128…128] and then clipped to [-128…127]. information. So if something other than sRGB is used, then the driver will have to set that information explicitly. Effectively ``V4L2_COLORSPACE_JPEG`` can be considered to be an abbreviation for - ``V4L2_COLORSPACE_SRGB``, ``V4L2_YCBCR_ENC_601`` and - ``V4L2_QUANTIZATION_FULL_RANGE``. + ``V4L2_COLORSPACE_SRGB``, ``V4L2_XFER_FUNC_SRGB``, ``V4L2_YCBCR_ENC_601`` + and ``V4L2_QUANTIZATION_FULL_RANGE``. *************************************** Detailed Transfer Function Descriptions -- GitLab From c89ee8ed3289c651f0521ff812c73c82803bdab8 Mon Sep 17 00:00:00 2001 From: Ezequiel Garcia Date: Fri, 17 Jul 2020 05:39:57 +0200 Subject: [PATCH 0694/1476] media: videobuf2: Print videobuf2 buffer state by name For debugging purposes, seeing the state integer representation is really inconvenient. Improve this and be developer-friendly by printing the state name instead. Suggested-by: Nicolas Dufresne Signed-off-by: Ezequiel Garcia Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- .../media/common/videobuf2/videobuf2-core.c | 35 ++++++++++++++----- 1 file changed, 27 insertions(+), 8 deletions(-) diff --git a/drivers/media/common/videobuf2/videobuf2-core.c b/drivers/media/common/videobuf2/videobuf2-core.c index abaf28e057ebe..f544d3393e9d6 100644 --- a/drivers/media/common/videobuf2/videobuf2-core.c +++ b/drivers/media/common/videobuf2/videobuf2-core.c @@ -191,6 +191,23 @@ module_param(debug, int, 0644); static void __vb2_queue_cancel(struct vb2_queue *q); static void __enqueue_in_driver(struct vb2_buffer *vb); +static const char *vb2_state_name(enum vb2_buffer_state s) +{ + static const char * const state_names[] = { + [VB2_BUF_STATE_DEQUEUED] = "dequeued", + [VB2_BUF_STATE_IN_REQUEST] = "in request", + [VB2_BUF_STATE_PREPARING] = "preparing", + [VB2_BUF_STATE_QUEUED] = "queued", + [VB2_BUF_STATE_ACTIVE] = "active", + [VB2_BUF_STATE_DONE] = "done", + [VB2_BUF_STATE_ERROR] = "error", + }; + + if ((unsigned int)(s) < ARRAY_SIZE(state_names)) + return state_names[s]; + return "unknown"; +} + /* * __vb2_buf_mem_alloc() - allocate video memory for the given buffer */ @@ -1015,8 +1032,8 @@ void vb2_buffer_done(struct vb2_buffer *vb, enum vb2_buffer_state state) */ vb->cnt_buf_done++; #endif - dprintk(q, 4, "done processing on buffer %d, state: %d\n", - vb->index, state); + dprintk(q, 4, "done processing on buffer %d, state: %s\n", + vb->index, vb2_state_name(state)); if (state != VB2_BUF_STATE_QUEUED) __vb2_buf_mem_finish(vb); @@ -1490,8 +1507,8 @@ int vb2_core_prepare_buf(struct vb2_queue *q, unsigned int index, void *pb) vb = q->bufs[index]; if (vb->state != VB2_BUF_STATE_DEQUEUED) { - dprintk(q, 1, "invalid buffer state %d\n", - vb->state); + dprintk(q, 1, "invalid buffer state %s\n", + vb2_state_name(vb->state)); return -EINVAL; } if (vb->prepared) { @@ -1670,7 +1687,8 @@ int vb2_core_qbuf(struct vb2_queue *q, unsigned int index, void *pb, dprintk(q, 1, "buffer still being prepared\n"); return -EINVAL; default: - dprintk(q, 1, "invalid buffer state %d\n", vb->state); + dprintk(q, 1, "invalid buffer state %s\n", + vb2_state_name(vb->state)); return -EINVAL; } @@ -1884,7 +1902,8 @@ int vb2_core_dqbuf(struct vb2_queue *q, unsigned int *pindex, void *pb, dprintk(q, 3, "returning done buffer with errors\n"); break; default: - dprintk(q, 1, "invalid buffer state\n"); + dprintk(q, 1, "invalid buffer state %s\n", + vb2_state_name(vb->state)); return -EINVAL; } @@ -1915,8 +1934,8 @@ int vb2_core_dqbuf(struct vb2_queue *q, unsigned int *pindex, void *pb, media_request_put(vb->request); vb->request = NULL; - dprintk(q, 2, "dqbuf of buffer %d, with state %d\n", - vb->index, vb->state); + dprintk(q, 2, "dqbuf of buffer %d, state: %s\n", + vb->index, vb2_state_name(vb->state)); return 0; -- GitLab From 8ecbde62c063d7d499144d3491496383a16de7f2 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 17 Jul 2020 10:08:51 +0200 Subject: [PATCH 0695/1476] media: v4l2-subdev.h: keep * together with the type Having the '*' in the next line separated from the type makes it hard to see that these functions return a pointer to that type. Instead, keep it next to the type name so it is clear that it is a pointer to that type. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- include/media/v4l2-subdev.h | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/include/media/v4l2-subdev.h b/include/media/v4l2-subdev.h index 1b83a6c46c1ad..b855721879b82 100644 --- a/include/media/v4l2-subdev.h +++ b/include/media/v4l2-subdev.h @@ -930,10 +930,10 @@ struct v4l2_subdev_fh { * @cfg: pointer to &struct v4l2_subdev_pad_config array. * @pad: index of the pad in the @cfg array. */ -static inline struct v4l2_mbus_framefmt -*v4l2_subdev_get_try_format(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, - unsigned int pad) +static inline struct v4l2_mbus_framefmt * +v4l2_subdev_get_try_format(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + unsigned int pad) { if (WARN_ON(pad >= sd->entity.num_pads)) pad = 0; @@ -948,10 +948,10 @@ static inline struct v4l2_mbus_framefmt * @cfg: pointer to &struct v4l2_subdev_pad_config array. * @pad: index of the pad in the @cfg array. */ -static inline struct v4l2_rect -*v4l2_subdev_get_try_crop(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, - unsigned int pad) +static inline struct v4l2_rect * +v4l2_subdev_get_try_crop(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + unsigned int pad) { if (WARN_ON(pad >= sd->entity.num_pads)) pad = 0; @@ -966,15 +966,16 @@ static inline struct v4l2_rect * @cfg: pointer to &struct v4l2_subdev_pad_config array. * @pad: index of the pad in the @cfg array. */ -static inline struct v4l2_rect -*v4l2_subdev_get_try_compose(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, - unsigned int pad) +static inline struct v4l2_rect * +v4l2_subdev_get_try_compose(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + unsigned int pad) { if (WARN_ON(pad >= sd->entity.num_pads)) pad = 0; return &cfg[pad].try_compose; } + #endif extern const struct v4l2_file_operations v4l2_subdev_fops; -- GitLab From 8f2a4a9d5ff5202d0b3e3a144ebb9b67aabadd29 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 17 Jul 2020 10:10:07 +0200 Subject: [PATCH 0696/1476] media: dvbdev.h: keep * together with the type Having the '*' in the next line separated from the type makes it hard to see that these functions return a pointer to that type. Instead, keep it next to the type name so it is clear that it is a pointer to that type. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- include/media/dvbdev.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/media/dvbdev.h b/include/media/dvbdev.h index 1f7b008387f88..e547cbeee4310 100644 --- a/include/media/dvbdev.h +++ b/include/media/dvbdev.h @@ -293,8 +293,8 @@ static inline void dvb_register_media_controller(struct dvb_adapter *adap, * * @adap: pointer to &struct dvb_adapter */ -static inline struct media_device -*dvb_get_media_controller(struct dvb_adapter *adap) +static inline struct media_device * +dvb_get_media_controller(struct dvb_adapter *adap) { return adap->mdev; } -- GitLab From 4b545ebfc371552cda7ce97a4beb485da9ef89cd Mon Sep 17 00:00:00 2001 From: Lad Prabhakar Date: Thu, 16 Jul 2020 18:18:27 +0100 Subject: [PATCH 0697/1476] dt-bindings: dma: renesas,usb-dmac: Add binding for r8a774e1 Add binding for R8A774E1 SoC (RZ/G2H). Signed-off-by: Lad Prabhakar Reviewed-by: Marian-Cristian Rotariu Link: https://lore.kernel.org/r/1594919915-5225-13-git-send-email-prabhakar.mahadev-lad.rj@bp.renesas.com Signed-off-by: Vinod Koul --- Documentation/devicetree/bindings/dma/renesas,usb-dmac.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/dma/renesas,usb-dmac.yaml b/Documentation/devicetree/bindings/dma/renesas,usb-dmac.yaml index 03aea6ae651f2..ab287c652b2cc 100644 --- a/Documentation/devicetree/bindings/dma/renesas,usb-dmac.yaml +++ b/Documentation/devicetree/bindings/dma/renesas,usb-dmac.yaml @@ -24,6 +24,7 @@ properties: - renesas,r8a774a1-usb-dmac # RZ/G2M - renesas,r8a774b1-usb-dmac # RZ/G2N - renesas,r8a774c0-usb-dmac # RZ/G2E + - renesas,r8a774e1-usb-dmac # RZ/G2H - renesas,r8a7790-usb-dmac # R-Car H2 - renesas,r8a7791-usb-dmac # R-Car M2-W - renesas,r8a7793-usb-dmac # R-Car M2-N -- GitLab From e7b856dfcec6d3bf028adee8c65342d7035914a1 Mon Sep 17 00:00:00 2001 From: Nicolas Chauvet Date: Sat, 18 Jul 2020 12:07:10 +0200 Subject: [PATCH 0698/1476] PCI: tegra: Revert tegra124 raw_violation_fixup As reported in https://bugzilla.kernel.org/206217 , raw_violation_fixup is causing more harm than good in some common use-cases. This patch is a partial revert of commit: 191cd6fb5d2c ("PCI: tegra: Add SW fixup for RAW violations") and fixes the following regression since then. * Description: When both the NIC and MMC are used one can see the following message: NETDEV WATCHDOG: enp1s0 (r8169): transmit queue 0 timed out and pcieport 0000:00:02.0: AER: Uncorrected (Non-Fatal) error received: 0000:01:00.0 r8169 0000:01:00.0: AER: PCIe Bus Error: severity=Uncorrected (Non-Fatal), type=Transaction Layer, (Requester ID) r8169 0000:01:00.0: AER: device [10ec:8168] error status/mask=00004000/00400000 r8169 0000:01:00.0: AER: [14] CmpltTO (First) r8169 0000:01:00.0: AER: can't recover (no error_detected callback) pcieport 0000:00:02.0: AER: device recovery failed After that, the ethernet NIC is not functional anymore even after reloading the r8169 module. After a reboot, this is reproducible by copying a large file over the NIC to the MMC. For some reason this is not reproducible when files are copied to a tmpfs. * Little background on the fixup, by Manikanta Maddireddy: "In the internal testing with dGPU on Tegra124, CmplTO is reported by dGPU. This happened because FIFO queue in AFI(AXI to PCIe) module get full by upstream posted writes. Back to back upstream writes interleaved with infrequent reads, triggers RAW violation and CmpltTO. This is fixed by reducing the posted write credits and by changing updateFC timer frequency. These settings are fixed after stress test. In the current case, RTL NIC is also reporting CmplTO. These settings seems to be aggravating the issue instead of fixing it." Link: https://lore.kernel.org/r/20200718100710.15398-1-kwizart@gmail.com Fixes: 191cd6fb5d2c ("PCI: tegra: Add SW fixup for RAW violations") Signed-off-by: Nicolas Chauvet Signed-off-by: Lorenzo Pieralisi Reviewed-by: Manikanta Maddireddy Cc: stable@vger.kernel.org --- drivers/pci/controller/pci-tegra.c | 32 ------------------------------ 1 file changed, 32 deletions(-) diff --git a/drivers/pci/controller/pci-tegra.c b/drivers/pci/controller/pci-tegra.c index 235b456698fc7..b532d5082fb6f 100644 --- a/drivers/pci/controller/pci-tegra.c +++ b/drivers/pci/controller/pci-tegra.c @@ -181,13 +181,6 @@ #define AFI_PEXBIAS_CTRL_0 0x168 -#define RP_PRIV_XP_DL 0x00000494 -#define RP_PRIV_XP_DL_GEN2_UPD_FC_TSHOLD (0x1ff << 1) - -#define RP_RX_HDR_LIMIT 0x00000e00 -#define RP_RX_HDR_LIMIT_PW_MASK (0xff << 8) -#define RP_RX_HDR_LIMIT_PW (0x0e << 8) - #define RP_ECTL_2_R1 0x00000e84 #define RP_ECTL_2_R1_RX_CTLE_1C_MASK 0xffff @@ -323,7 +316,6 @@ struct tegra_pcie_soc { bool program_uphy; bool update_clamp_threshold; bool program_deskew_time; - bool raw_violation_fixup; bool update_fc_timer; bool has_cache_bars; struct { @@ -659,23 +651,6 @@ static void tegra_pcie_apply_sw_fixup(struct tegra_pcie_port *port) writel(value, port->base + RP_VEND_CTL0); } - /* Fixup for read after write violation. */ - if (soc->raw_violation_fixup) { - value = readl(port->base + RP_RX_HDR_LIMIT); - value &= ~RP_RX_HDR_LIMIT_PW_MASK; - value |= RP_RX_HDR_LIMIT_PW; - writel(value, port->base + RP_RX_HDR_LIMIT); - - value = readl(port->base + RP_PRIV_XP_DL); - value |= RP_PRIV_XP_DL_GEN2_UPD_FC_TSHOLD; - writel(value, port->base + RP_PRIV_XP_DL); - - value = readl(port->base + RP_VEND_XP); - value &= ~RP_VEND_XP_UPDATE_FC_THRESHOLD_MASK; - value |= soc->update_fc_threshold; - writel(value, port->base + RP_VEND_XP); - } - if (soc->update_fc_timer) { value = readl(port->base + RP_VEND_XP); value &= ~RP_VEND_XP_UPDATE_FC_THRESHOLD_MASK; @@ -2416,7 +2391,6 @@ static const struct tegra_pcie_soc tegra20_pcie = { .program_uphy = true, .update_clamp_threshold = false, .program_deskew_time = false, - .raw_violation_fixup = false, .update_fc_timer = false, .has_cache_bars = true, .ectl.enable = false, @@ -2446,7 +2420,6 @@ static const struct tegra_pcie_soc tegra30_pcie = { .program_uphy = true, .update_clamp_threshold = false, .program_deskew_time = false, - .raw_violation_fixup = false, .update_fc_timer = false, .has_cache_bars = false, .ectl.enable = false, @@ -2459,8 +2432,6 @@ static const struct tegra_pcie_soc tegra124_pcie = { .pads_pll_ctl = PADS_PLL_CTL_TEGRA30, .tx_ref_sel = PADS_PLL_CTL_TXCLKREF_BUF_EN, .pads_refclk_cfg0 = 0x44ac44ac, - /* FC threshold is bit[25:18] */ - .update_fc_threshold = 0x03fc0000, .has_pex_clkreq_en = true, .has_pex_bias_ctrl = true, .has_intr_prsnt_sense = true, @@ -2470,7 +2441,6 @@ static const struct tegra_pcie_soc tegra124_pcie = { .program_uphy = true, .update_clamp_threshold = true, .program_deskew_time = false, - .raw_violation_fixup = true, .update_fc_timer = false, .has_cache_bars = false, .ectl.enable = false, @@ -2494,7 +2464,6 @@ static const struct tegra_pcie_soc tegra210_pcie = { .program_uphy = true, .update_clamp_threshold = true, .program_deskew_time = true, - .raw_violation_fixup = false, .update_fc_timer = true, .has_cache_bars = false, .ectl = { @@ -2536,7 +2505,6 @@ static const struct tegra_pcie_soc tegra186_pcie = { .program_uphy = false, .update_clamp_threshold = false, .program_deskew_time = false, - .raw_violation_fixup = false, .update_fc_timer = false, .has_cache_bars = false, .ectl.enable = false, -- GitLab From 9a77c4a0a12597c661be374b8d566516c0341570 Mon Sep 17 00:00:00 2001 From: Nicholas Piggin Date: Mon, 20 Jul 2020 10:12:58 +0530 Subject: [PATCH 0699/1476] powerpc/prom: Enable Radix GTSE in cpu pa-features When '029ab30b4c0a ("powerpc/mm: Enable radix GTSE only if supported.")' made GTSE an MMU feature, it was enabled by default in powerpc-cpu-features but was missed in pa-features. This causes random memory corruption during boot of PowerNV kernels where CONFIG_PPC_DT_CPU_FTRS isn't enabled. Fixes: 029ab30b4c0a ("powerpc/mm: Enable radix GTSE only if supported.") Reported-by: Qian Cai Signed-off-by: Nicholas Piggin Signed-off-by: Bharata B Rao [mpe: Unwrap long line] Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20200720044258.863574-1-bharata@linux.ibm.com --- arch/powerpc/kernel/prom.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/powerpc/kernel/prom.c b/arch/powerpc/kernel/prom.c index 9cc49f265c86e..033d43819ed89 100644 --- a/arch/powerpc/kernel/prom.c +++ b/arch/powerpc/kernel/prom.c @@ -163,7 +163,7 @@ static struct ibm_pa_feature { { .pabyte = 0, .pabit = 6, .cpu_features = CPU_FTR_NOEXECUTE }, { .pabyte = 1, .pabit = 2, .mmu_features = MMU_FTR_CI_LARGE_PAGE }, #ifdef CONFIG_PPC_RADIX_MMU - { .pabyte = 40, .pabit = 0, .mmu_features = MMU_FTR_TYPE_RADIX }, + { .pabyte = 40, .pabit = 0, .mmu_features = MMU_FTR_TYPE_RADIX | MMU_FTR_GTSE }, #endif { .pabyte = 1, .pabit = 1, .invert = 1, .cpu_features = CPU_FTR_NODSISRALIGN }, { .pabyte = 5, .pabit = 0, .cpu_features = CPU_FTR_REAL_LE, -- GitLab From 645d5ce2f7d6cb4dcf6a4e087fb550e238d24283 Mon Sep 17 00:00:00 2001 From: "Aneesh Kumar K.V" Date: Thu, 9 Jul 2020 18:49:22 +0530 Subject: [PATCH 0700/1476] powerpc/mm/radix: Fix PTE/PMD fragment count for early page table mappings We can hit the following BUG_ON during memory unplug: kernel BUG at arch/powerpc/mm/book3s64/pgtable.c:342! Oops: Exception in kernel mode, sig: 5 [#1] LE PAGE_SIZE=64K MMU=Radix SMP NR_CPUS=2048 NUMA pSeries NIP [c000000000093308] pmd_fragment_free+0x48/0xc0 LR [c00000000147bfec] remove_pagetable+0x578/0x60c Call Trace: 0xc000008050000000 (unreliable) remove_pagetable+0x384/0x60c radix__remove_section_mapping+0x18/0x2c remove_section_mapping+0x1c/0x3c arch_remove_memory+0x11c/0x180 try_remove_memory+0x120/0x1b0 __remove_memory+0x20/0x40 dlpar_remove_lmb+0xc0/0x114 dlpar_memory+0x8b0/0xb20 handle_dlpar_errorlog+0xc0/0x190 pseries_hp_work_fn+0x2c/0x60 process_one_work+0x30c/0x810 worker_thread+0x98/0x540 kthread+0x1c4/0x1d0 ret_from_kernel_thread+0x5c/0x74 This occurs when unplug is attempted for such memory which has been mapped using memblock pages as part of early kernel page table setup. We wouldn't have initialized the PMD or PTE fragment count for those PMD or PTE pages. This can be fixed by allocating memory in PAGE_SIZE granularity during early page table allocation. This makes sure a specific page is not shared for another memblock allocation and we can free them correctly on removing page-table pages. Since we now do PAGE_SIZE allocations for both PUD table and PMD table (Note that PTE table allocation is already of PAGE_SIZE), we end up allocating more memory for the same amount of system RAM. Here is a comparision of how much more we need for a 64T and 2G system after this patch: 1. 64T system ------------- 64T RAM would need 64G for vmemmap with struct page size being 64B. 128 PUD tables for 64T memory (1G mappings) 1 PUD table and 64 PMD tables for 64G vmemmap (2M mappings) With default PUD[PMD]_TABLE_SIZE(4K), (128+1+64)*4K=772K With PAGE_SIZE(64K) table allocations, (128+1+64)*64K=12352K 2. 2G system ------------ 2G RAM would need 2M for vmemmap with struct page size being 64B. 1 PUD table for 2G memory (1G mapping) 1 PUD table and 1 PMD table for 2M vmemmap (2M mappings) With default PUD[PMD]_TABLE_SIZE(4K), (1+1+1)*4K=12K With new PAGE_SIZE(64K) table allocations, (1+1+1)*64K=192K Signed-off-by: Bharata B Rao Signed-off-by: Aneesh Kumar K.V Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20200709131925.922266-2-aneesh.kumar@linux.ibm.com --- arch/powerpc/include/asm/book3s/64/pgalloc.h | 16 +++++++++++++++- arch/powerpc/mm/book3s64/pgtable.c | 5 ++++- arch/powerpc/mm/book3s64/radix_pgtable.c | 15 +++++++++++---- arch/powerpc/mm/pgtable-frag.c | 3 +++ 4 files changed, 33 insertions(+), 6 deletions(-) diff --git a/arch/powerpc/include/asm/book3s/64/pgalloc.h b/arch/powerpc/include/asm/book3s/64/pgalloc.h index 69c5b051734f0..e1af0b394cebe 100644 --- a/arch/powerpc/include/asm/book3s/64/pgalloc.h +++ b/arch/powerpc/include/asm/book3s/64/pgalloc.h @@ -107,9 +107,23 @@ static inline pud_t *pud_alloc_one(struct mm_struct *mm, unsigned long addr) return pud; } +static inline void __pud_free(pud_t *pud) +{ + struct page *page = virt_to_page(pud); + + /* + * Early pud pages allocated via memblock allocator + * can't be directly freed to slab + */ + if (PageReserved(page)) + free_reserved_page(page); + else + kmem_cache_free(PGT_CACHE(PUD_CACHE_INDEX), pud); +} + static inline void pud_free(struct mm_struct *mm, pud_t *pud) { - kmem_cache_free(PGT_CACHE(PUD_CACHE_INDEX), pud); + return __pud_free(pud); } static inline void pud_populate(struct mm_struct *mm, pud_t *pud, pmd_t *pmd) diff --git a/arch/powerpc/mm/book3s64/pgtable.c b/arch/powerpc/mm/book3s64/pgtable.c index c58ad1049909e..85de5b574dd70 100644 --- a/arch/powerpc/mm/book3s64/pgtable.c +++ b/arch/powerpc/mm/book3s64/pgtable.c @@ -339,6 +339,9 @@ void pmd_fragment_free(unsigned long *pmd) { struct page *page = virt_to_page(pmd); + if (PageReserved(page)) + return free_reserved_page(page); + BUG_ON(atomic_read(&page->pt_frag_refcount) <= 0); if (atomic_dec_and_test(&page->pt_frag_refcount)) { pgtable_pmd_page_dtor(page); @@ -356,7 +359,7 @@ static inline void pgtable_free(void *table, int index) pmd_fragment_free(table); break; case PUD_INDEX: - kmem_cache_free(PGT_CACHE(PUD_CACHE_INDEX), table); + __pud_free(table); break; #if defined(CONFIG_PPC_4K_PAGES) && defined(CONFIG_HUGETLB_PAGE) /* 16M hugepd directory at pud level */ diff --git a/arch/powerpc/mm/book3s64/radix_pgtable.c b/arch/powerpc/mm/book3s64/radix_pgtable.c index bb00e0cba1195..85806a6bed4da 100644 --- a/arch/powerpc/mm/book3s64/radix_pgtable.c +++ b/arch/powerpc/mm/book3s64/radix_pgtable.c @@ -56,6 +56,13 @@ static __ref void *early_alloc_pgtable(unsigned long size, int nid, return ptr; } +/* + * When allocating pud or pmd pointers, we allocate a complete page + * of PAGE_SIZE rather than PUD_TABLE_SIZE or PMD_TABLE_SIZE. This + * is to ensure that the page obtained from the memblock allocator + * can be completely used as page table page and can be freed + * correctly when the page table entries are removed. + */ static int early_map_kernel_page(unsigned long ea, unsigned long pa, pgprot_t flags, unsigned int map_page_size, @@ -72,8 +79,8 @@ static int early_map_kernel_page(unsigned long ea, unsigned long pa, pgdp = pgd_offset_k(ea); p4dp = p4d_offset(pgdp, ea); if (p4d_none(*p4dp)) { - pudp = early_alloc_pgtable(PUD_TABLE_SIZE, nid, - region_start, region_end); + pudp = early_alloc_pgtable(PAGE_SIZE, nid, + region_start, region_end); p4d_populate(&init_mm, p4dp, pudp); } pudp = pud_offset(p4dp, ea); @@ -82,8 +89,8 @@ static int early_map_kernel_page(unsigned long ea, unsigned long pa, goto set_the_pte; } if (pud_none(*pudp)) { - pmdp = early_alloc_pgtable(PMD_TABLE_SIZE, nid, - region_start, region_end); + pmdp = early_alloc_pgtable(PAGE_SIZE, nid, region_start, + region_end); pud_populate(&init_mm, pudp, pmdp); } pmdp = pmd_offset(pudp, ea); diff --git a/arch/powerpc/mm/pgtable-frag.c b/arch/powerpc/mm/pgtable-frag.c index ee4bd6d386021..97ae4935da799 100644 --- a/arch/powerpc/mm/pgtable-frag.c +++ b/arch/powerpc/mm/pgtable-frag.c @@ -110,6 +110,9 @@ void pte_fragment_free(unsigned long *table, int kernel) { struct page *page = virt_to_page(table); + if (PageReserved(page)) + return free_reserved_page(page); + BUG_ON(atomic_read(&page->pt_frag_refcount) <= 0); if (atomic_dec_and_test(&page->pt_frag_refcount)) { if (!kernel) -- GitLab From 9ce8853b4a735c8115f55ac0e9c2b27a4c8f80b5 Mon Sep 17 00:00:00 2001 From: Bharata B Rao Date: Thu, 9 Jul 2020 18:49:23 +0530 Subject: [PATCH 0701/1476] powerpc/mm/radix: Free PUD table when freeing pagetable remove_pagetable() isn't freeing PUD table. This causes memory leak during memory unplug. Fix this. Fixes: 4b5d62ca17a1 ("powerpc/mm: add radix__remove_section_mapping()") Signed-off-by: Bharata B Rao Signed-off-by: Aneesh Kumar K.V Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20200709131925.922266-3-aneesh.kumar@linux.ibm.com --- arch/powerpc/mm/book3s64/radix_pgtable.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/arch/powerpc/mm/book3s64/radix_pgtable.c b/arch/powerpc/mm/book3s64/radix_pgtable.c index 85806a6bed4da..46ad2da3087ae 100644 --- a/arch/powerpc/mm/book3s64/radix_pgtable.c +++ b/arch/powerpc/mm/book3s64/radix_pgtable.c @@ -707,6 +707,21 @@ static void free_pmd_table(pmd_t *pmd_start, pud_t *pud) pud_clear(pud); } +static void free_pud_table(pud_t *pud_start, p4d_t *p4d) +{ + pud_t *pud; + int i; + + for (i = 0; i < PTRS_PER_PUD; i++) { + pud = pud_start + i; + if (!pud_none(*pud)) + return; + } + + pud_free(&init_mm, pud_start); + p4d_clear(p4d); +} + struct change_mapping_params { pte_t *pte; unsigned long start; @@ -881,6 +896,7 @@ static void __meminit remove_pagetable(unsigned long start, unsigned long end) pud_base = (pud_t *)p4d_page_vaddr(*p4d); remove_pud_table(pud_base, addr, next); + free_pud_table(pud_base, p4d); } spin_unlock(&init_mm.page_table_lock); -- GitLab From d6d6ebfc5dbb4008be21baa4ec2ad45606578966 Mon Sep 17 00:00:00 2001 From: Bharata B Rao Date: Thu, 9 Jul 2020 18:49:24 +0530 Subject: [PATCH 0702/1476] powerpc/mm/radix: Remove split_kernel_mapping() We split the page table mapping on memory unplug if the linear range was mapped with huge page mapping (for ex: 1G) The page table splitting code has a few issues: 1. Recursive locking -------------------- Memory unplug path takes cpu_hotplug_lock and calls stop_machine() for splitting the mappings. However stop_machine() takes cpu_hotplug_lock again causing deadlock. 2. BUG: sleeping function called from in_atomic() context --------------------------------------------------------- Memory unplug path (remove_pagetable) takes init_mm.page_table_lock spinlock and later calls stop_machine() which does wait_for_completion() 3. Bad unlock unbalance ----------------------- Memory unplug path takes init_mm.page_table_lock spinlock and calls stop_machine(). The stop_machine thread function runs in a different thread context (migration thread) which tries to release and reaquire ptl. Releasing ptl from a different thread than which acquired it causes bad unlock unbalance. These problems can be avoided if we avoid mapping hot-plugged memory with 1G mapping, thereby removing the need for splitting them during unplug. The kernel always make sure the minimum unplug request is SUBSECTION_SIZE for device memory and SECTION_SIZE for regular memory. In preparation for such a change remove page table splitting support. This essentially is a revert of commit 4dd5f8a99e791 ("powerpc/mm/radix: Split linear mapping on hot-unplug") Signed-off-by: Bharata B Rao Signed-off-by: Aneesh Kumar K.V Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20200709131925.922266-4-aneesh.kumar@linux.ibm.com --- arch/powerpc/mm/book3s64/radix_pgtable.c | 95 +++++------------------- 1 file changed, 19 insertions(+), 76 deletions(-) diff --git a/arch/powerpc/mm/book3s64/radix_pgtable.c b/arch/powerpc/mm/book3s64/radix_pgtable.c index 46ad2da3087ae..d5a01b9aadc9f 100644 --- a/arch/powerpc/mm/book3s64/radix_pgtable.c +++ b/arch/powerpc/mm/book3s64/radix_pgtable.c @@ -15,7 +15,6 @@ #include #include #include -#include #include #include @@ -722,32 +721,6 @@ static void free_pud_table(pud_t *pud_start, p4d_t *p4d) p4d_clear(p4d); } -struct change_mapping_params { - pte_t *pte; - unsigned long start; - unsigned long end; - unsigned long aligned_start; - unsigned long aligned_end; -}; - -static int __meminit stop_machine_change_mapping(void *data) -{ - struct change_mapping_params *params = - (struct change_mapping_params *)data; - - if (!data) - return -1; - - spin_unlock(&init_mm.page_table_lock); - pte_clear(&init_mm, params->aligned_start, params->pte); - create_physical_mapping(__pa(params->aligned_start), - __pa(params->start), -1, PAGE_KERNEL); - create_physical_mapping(__pa(params->end), __pa(params->aligned_end), - -1, PAGE_KERNEL); - spin_lock(&init_mm.page_table_lock); - return 0; -} - static void remove_pte_table(pte_t *pte_start, unsigned long addr, unsigned long end) { @@ -776,52 +749,6 @@ static void remove_pte_table(pte_t *pte_start, unsigned long addr, } } -/* - * clear the pte and potentially split the mapping helper - */ -static void __meminit split_kernel_mapping(unsigned long addr, unsigned long end, - unsigned long size, pte_t *pte) -{ - unsigned long mask = ~(size - 1); - unsigned long aligned_start = addr & mask; - unsigned long aligned_end = addr + size; - struct change_mapping_params params; - bool split_region = false; - - if ((end - addr) < size) { - /* - * We're going to clear the PTE, but not flushed - * the mapping, time to remap and flush. The - * effects if visible outside the processor or - * if we are running in code close to the - * mapping we cleared, we are in trouble. - */ - if (overlaps_kernel_text(aligned_start, addr) || - overlaps_kernel_text(end, aligned_end)) { - /* - * Hack, just return, don't pte_clear - */ - WARN_ONCE(1, "Linear mapping %lx->%lx overlaps kernel " - "text, not splitting\n", addr, end); - return; - } - split_region = true; - } - - if (split_region) { - params.pte = pte; - params.start = addr; - params.end = end; - params.aligned_start = addr & ~(size - 1); - params.aligned_end = min_t(unsigned long, aligned_end, - (unsigned long)__va(memblock_end_of_DRAM())); - stop_machine(stop_machine_change_mapping, ¶ms, NULL); - return; - } - - pte_clear(&init_mm, addr, pte); -} - static void remove_pmd_table(pmd_t *pmd_start, unsigned long addr, unsigned long end) { @@ -837,7 +764,12 @@ static void remove_pmd_table(pmd_t *pmd_start, unsigned long addr, continue; if (pmd_is_leaf(*pmd)) { - split_kernel_mapping(addr, end, PMD_SIZE, (pte_t *)pmd); + if (!IS_ALIGNED(addr, PMD_SIZE) || + !IS_ALIGNED(next, PMD_SIZE)) { + WARN_ONCE(1, "%s: unaligned range\n", __func__); + continue; + } + pte_clear(&init_mm, addr, (pte_t *)pmd); continue; } @@ -862,7 +794,12 @@ static void remove_pud_table(pud_t *pud_start, unsigned long addr, continue; if (pud_is_leaf(*pud)) { - split_kernel_mapping(addr, end, PUD_SIZE, (pte_t *)pud); + if (!IS_ALIGNED(addr, PUD_SIZE) || + !IS_ALIGNED(next, PUD_SIZE)) { + WARN_ONCE(1, "%s: unaligned range\n", __func__); + continue; + } + pte_clear(&init_mm, addr, (pte_t *)pud); continue; } @@ -890,7 +827,13 @@ static void __meminit remove_pagetable(unsigned long start, unsigned long end) continue; if (p4d_is_leaf(*p4d)) { - split_kernel_mapping(addr, end, P4D_SIZE, (pte_t *)p4d); + if (!IS_ALIGNED(addr, P4D_SIZE) || + !IS_ALIGNED(next, P4D_SIZE)) { + WARN_ONCE(1, "%s: unaligned range\n", __func__); + continue; + } + + pte_clear(&init_mm, addr, (pte_t *)pgd); continue; } -- GitLab From af9d00e93a4f062c5f160325d7b8f33336f6744e Mon Sep 17 00:00:00 2001 From: "Aneesh Kumar K.V" Date: Thu, 9 Jul 2020 18:49:25 +0530 Subject: [PATCH 0703/1476] powerpc/mm/radix: Create separate mappings for hot-plugged memory To enable memory unplug without splitting kernel page table mapping, we force the max mapping size to the LMB size. LMB size is the unit in which hypervisor will do memory add/remove operation. Pseries systems supports max LMB size of 256MB. Hence on pseries, we now end up mapping memory with 2M page size instead of 1G. To improve that we want hypervisor to hint the kernel about the hotplug memory range. That was added that as part of commit b6eca183e23e ("powerpc/kernel: Enables memory hot-remove after reboot on pseries guests") But PowerVM doesn't provide that hint yet. Once we get PowerVM updated, we can then force the 2M mapping only to hot-pluggable memory region using memblock_is_hotpluggable(). Till then let's depend on LMB size for finding the mapping page size for linear range. With this change KVM guest will also be doing linear mapping with 2M page size. The actual TLB benefit of mapping guest page table entries with hugepage size can only be materialized if the partition scoped entries are also using the same or higher page size. A guest using 1G hugetlbfs backing guest memory can have a performance impact with the above change. Signed-off-by: Bharata B Rao Signed-off-by: Aneesh Kumar K.V [mpe: Fold in fix from Aneesh spotted by lkp@intel.com] Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20200709131925.922266-5-aneesh.kumar@linux.ibm.com --- arch/powerpc/include/asm/book3s/64/mmu.h | 5 ++ arch/powerpc/mm/book3s64/radix_pgtable.c | 91 +++++++++++++++++++++--- arch/powerpc/platforms/powernv/setup.c | 10 ++- 3 files changed, 94 insertions(+), 12 deletions(-) diff --git a/arch/powerpc/include/asm/book3s/64/mmu.h b/arch/powerpc/include/asm/book3s/64/mmu.h index 5393a535240c7..15aae924f41c1 100644 --- a/arch/powerpc/include/asm/book3s/64/mmu.h +++ b/arch/powerpc/include/asm/book3s/64/mmu.h @@ -82,6 +82,11 @@ extern unsigned int mmu_pid_bits; /* Base PID to allocate from */ extern unsigned int mmu_base_pid; +/* + * memory block size used with radix translation. + */ +extern unsigned int __ro_after_init radix_mem_block_size; + #define PRTB_SIZE_SHIFT (mmu_pid_bits + 4) #define PRTB_ENTRIES (1ul << mmu_pid_bits) diff --git a/arch/powerpc/mm/book3s64/radix_pgtable.c b/arch/powerpc/mm/book3s64/radix_pgtable.c index d5a01b9aadc9f..c5bf2ef73c36c 100644 --- a/arch/powerpc/mm/book3s64/radix_pgtable.c +++ b/arch/powerpc/mm/book3s64/radix_pgtable.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #include @@ -33,6 +34,7 @@ unsigned int mmu_pid_bits; unsigned int mmu_base_pid; +unsigned int radix_mem_block_size __ro_after_init; static __ref void *early_alloc_pgtable(unsigned long size, int nid, unsigned long region_start, unsigned long region_end) @@ -265,6 +267,7 @@ static unsigned long next_boundary(unsigned long addr, unsigned long end) static int __meminit create_physical_mapping(unsigned long start, unsigned long end, + unsigned long max_mapping_size, int nid, pgprot_t _prot) { unsigned long vaddr, addr, mapping_size = 0; @@ -278,6 +281,8 @@ static int __meminit create_physical_mapping(unsigned long start, int rc; gap = next_boundary(addr, end) - addr; + if (gap > max_mapping_size) + gap = max_mapping_size; previous_size = mapping_size; prev_exec = exec; @@ -328,8 +333,9 @@ static void __init radix_init_pgtable(void) /* We don't support slb for radix */ mmu_slb_size = 0; + /* - * Create the linear mapping, using standard page size for now + * Create the linear mapping */ for_each_memblock(memory, reg) { /* @@ -345,6 +351,7 @@ static void __init radix_init_pgtable(void) WARN_ON(create_physical_mapping(reg->base, reg->base + reg->size, + radix_mem_block_size, -1, PAGE_KERNEL)); } @@ -485,6 +492,57 @@ static int __init radix_dt_scan_page_sizes(unsigned long node, return 1; } +#ifdef CONFIG_MEMORY_HOTPLUG +static int __init probe_memory_block_size(unsigned long node, const char *uname, int + depth, void *data) +{ + unsigned long *mem_block_size = (unsigned long *)data; + const __be64 *prop; + int len; + + if (depth != 1) + return 0; + + if (strcmp(uname, "ibm,dynamic-reconfiguration-memory")) + return 0; + + prop = of_get_flat_dt_prop(node, "ibm,lmb-size", &len); + if (!prop || len < sizeof(__be64)) + /* + * Nothing in the device tree + */ + *mem_block_size = MIN_MEMORY_BLOCK_SIZE; + else + *mem_block_size = be64_to_cpup(prop); + return 1; +} + +static unsigned long radix_memory_block_size(void) +{ + unsigned long mem_block_size = MIN_MEMORY_BLOCK_SIZE; + + /* + * OPAL firmware feature is set by now. Hence we are ok + * to test OPAL feature. + */ + if (firmware_has_feature(FW_FEATURE_OPAL)) + mem_block_size = 1UL * 1024 * 1024 * 1024; + else + of_scan_flat_dt(probe_memory_block_size, &mem_block_size); + + return mem_block_size; +} + +#else /* CONFIG_MEMORY_HOTPLUG */ + +static unsigned long radix_memory_block_size(void) +{ + return 1UL * 1024 * 1024 * 1024; +} + +#endif /* CONFIG_MEMORY_HOTPLUG */ + + void __init radix__early_init_devtree(void) { int rc; @@ -493,17 +551,27 @@ void __init radix__early_init_devtree(void) * Try to find the available page sizes in the device-tree */ rc = of_scan_flat_dt(radix_dt_scan_page_sizes, NULL); - if (rc != 0) /* Found */ - goto found; + if (!rc) { + /* + * No page size details found in device tree. + * Let's assume we have page 4k and 64k support + */ + mmu_psize_defs[MMU_PAGE_4K].shift = 12; + mmu_psize_defs[MMU_PAGE_4K].ap = 0x0; + + mmu_psize_defs[MMU_PAGE_64K].shift = 16; + mmu_psize_defs[MMU_PAGE_64K].ap = 0x5; + } + /* - * let's assume we have page 4k and 64k support + * Max mapping size used when mapping pages. We don't use + * ppc_md.memory_block_size() here because this get called + * early and we don't have machine probe called yet. Also + * the pseries implementation only check for ibm,lmb-size. + * All hypervisor supporting radix do expose that device + * tree node. */ - mmu_psize_defs[MMU_PAGE_4K].shift = 12; - mmu_psize_defs[MMU_PAGE_4K].ap = 0x0; - - mmu_psize_defs[MMU_PAGE_64K].shift = 16; - mmu_psize_defs[MMU_PAGE_64K].ap = 0x5; -found: + radix_mem_block_size = radix_memory_block_size(); return; } @@ -855,7 +923,8 @@ int __meminit radix__create_section_mapping(unsigned long start, return -1; } - return create_physical_mapping(__pa(start), __pa(end), nid, prot); + return create_physical_mapping(__pa(start), __pa(end), + radix_mem_block_size, nid, prot); } int __meminit radix__remove_section_mapping(unsigned long start, unsigned long end) diff --git a/arch/powerpc/platforms/powernv/setup.c b/arch/powerpc/platforms/powernv/setup.c index 3bc188da82ba7..7fcb886230810 100644 --- a/arch/powerpc/platforms/powernv/setup.c +++ b/arch/powerpc/platforms/powernv/setup.c @@ -399,7 +399,15 @@ static void pnv_kexec_cpu_down(int crash_shutdown, int secondary) #ifdef CONFIG_MEMORY_HOTPLUG_SPARSE static unsigned long pnv_memory_block_size(void) { - return 256UL * 1024 * 1024; + /* + * We map the kernel linear region with 1GB large pages on radix. For + * memory hot unplug to work our memory block size must be at least + * this size. + */ + if (radix_enabled()) + return radix_mem_block_size; + else + return 256UL * 1024 * 1024; } #endif -- GitLab From c37a63afc429ce959402168f67e4f094ab639ace Mon Sep 17 00:00:00 2001 From: Santosh Sivaraj Date: Thu, 9 Jul 2020 19:21:41 +0530 Subject: [PATCH 0704/1476] powerpc/mce: Add MCE notification chain Introduce notification chain which lets us know about uncorrected memory errors(UE). This would help prospective users in pmem or nvdimm subsystem to track bad blocks for better handling of persistent memory allocations. Signed-off-by: Santosh Sivaraj Signed-off-by: Ganesh Goudar Reviewed-by: Christophe Leroy Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20200709135142.721504-1-santosh@fossix.org --- arch/powerpc/include/asm/mce.h | 2 ++ arch/powerpc/kernel/mce.c | 15 +++++++++++++++ 2 files changed, 17 insertions(+) diff --git a/arch/powerpc/include/asm/mce.h b/arch/powerpc/include/asm/mce.h index 376a395daf329..7bdd0cd4f2de0 100644 --- a/arch/powerpc/include/asm/mce.h +++ b/arch/powerpc/include/asm/mce.h @@ -220,6 +220,8 @@ extern void machine_check_print_event_info(struct machine_check_event *evt, unsigned long addr_to_pfn(struct pt_regs *regs, unsigned long addr); extern void mce_common_process_ue(struct pt_regs *regs, struct mce_error_info *mce_err); +int mce_register_notifier(struct notifier_block *nb); +int mce_unregister_notifier(struct notifier_block *nb); #ifdef CONFIG_PPC_BOOK3S_64 void flush_and_reload_slb(void); #endif /* CONFIG_PPC_BOOK3S_64 */ diff --git a/arch/powerpc/kernel/mce.c b/arch/powerpc/kernel/mce.c index fd90c0eda2290..b7b3ed4e61937 100644 --- a/arch/powerpc/kernel/mce.c +++ b/arch/powerpc/kernel/mce.c @@ -49,6 +49,20 @@ static struct irq_work mce_ue_event_irq_work = { DECLARE_WORK(mce_ue_event_work, machine_process_ue_event); +static BLOCKING_NOTIFIER_HEAD(mce_notifier_list); + +int mce_register_notifier(struct notifier_block *nb) +{ + return blocking_notifier_chain_register(&mce_notifier_list, nb); +} +EXPORT_SYMBOL_GPL(mce_register_notifier); + +int mce_unregister_notifier(struct notifier_block *nb) +{ + return blocking_notifier_chain_unregister(&mce_notifier_list, nb); +} +EXPORT_SYMBOL_GPL(mce_unregister_notifier); + static void mce_set_error_info(struct machine_check_event *mce, struct mce_error_info *mce_err) { @@ -278,6 +292,7 @@ static void machine_process_ue_event(struct work_struct *work) while (__this_cpu_read(mce_ue_count) > 0) { index = __this_cpu_read(mce_ue_count) - 1; evt = this_cpu_ptr(&mce_ue_event_queue[index]); + blocking_notifier_call_chain(&mce_notifier_list, 0, evt); #ifdef CONFIG_MEMORY_FAILURE /* * This should probably queued elsewhere, but -- GitLab From 85343a8da2d969df1a10ada8f7cb857d52ea70a6 Mon Sep 17 00:00:00 2001 From: Santosh Sivaraj Date: Thu, 9 Jul 2020 19:21:42 +0530 Subject: [PATCH 0705/1476] powerpc/papr/scm: Add bad memory ranges to nvdimm bad ranges Subscribe to the MCE notification and add the physical address which generated a memory error to nvdimm bad range. Signed-off-by: Santosh Sivaraj Reviewed-by: Mahesh Salgaonkar Reviewed-by: Christophe Leroy Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20200709135142.721504-2-santosh@fossix.org --- arch/powerpc/platforms/pseries/papr_scm.c | 96 ++++++++++++++++++++++- 1 file changed, 95 insertions(+), 1 deletion(-) diff --git a/arch/powerpc/platforms/pseries/papr_scm.c b/arch/powerpc/platforms/pseries/papr_scm.c index 66c19c0fe5667..8fd441d32487d 100644 --- a/arch/powerpc/platforms/pseries/papr_scm.c +++ b/arch/powerpc/platforms/pseries/papr_scm.c @@ -13,9 +13,11 @@ #include #include #include +#include #include #include +#include #define BIND_ANY_ADDR (~0ul) @@ -80,6 +82,7 @@ struct papr_scm_priv { struct resource res; struct nd_region *region; struct nd_interleave_set nd_set; + struct list_head region_list; /* Protect dimm health data from concurrent read/writes */ struct mutex health_mutex; @@ -91,6 +94,9 @@ struct papr_scm_priv { u64 health_bitmap; }; +LIST_HEAD(papr_nd_regions); +DEFINE_MUTEX(papr_ndr_lock); + static int drc_pmem_bind(struct papr_scm_priv *p) { unsigned long ret[PLPAR_HCALL_BUFSIZE]; @@ -759,6 +765,10 @@ static int papr_scm_nvdimm_init(struct papr_scm_priv *p) dev_info(dev, "Region registered with target node %d and online node %d", target_nid, online_nid); + mutex_lock(&papr_ndr_lock); + list_add_tail(&p->region_list, &papr_nd_regions); + mutex_unlock(&papr_ndr_lock); + return 0; err: nvdimm_bus_unregister(p->bus); @@ -766,6 +776,68 @@ err: nvdimm_bus_unregister(p->bus); return -ENXIO; } +static void papr_scm_add_badblock(struct nd_region *region, + struct nvdimm_bus *bus, u64 phys_addr) +{ + u64 aligned_addr = ALIGN_DOWN(phys_addr, L1_CACHE_BYTES); + + if (nvdimm_bus_add_badrange(bus, aligned_addr, L1_CACHE_BYTES)) { + pr_err("Bad block registration for 0x%llx failed\n", phys_addr); + return; + } + + pr_debug("Add memory range (0x%llx - 0x%llx) as bad range\n", + aligned_addr, aligned_addr + L1_CACHE_BYTES); + + nvdimm_region_notify(region, NVDIMM_REVALIDATE_POISON); +} + +static int handle_mce_ue(struct notifier_block *nb, unsigned long val, + void *data) +{ + struct machine_check_event *evt = data; + struct papr_scm_priv *p; + u64 phys_addr; + bool found = false; + + if (evt->error_type != MCE_ERROR_TYPE_UE) + return NOTIFY_DONE; + + if (list_empty(&papr_nd_regions)) + return NOTIFY_DONE; + + /* + * The physical address obtained here is PAGE_SIZE aligned, so get the + * exact address from the effective address + */ + phys_addr = evt->u.ue_error.physical_address + + (evt->u.ue_error.effective_address & ~PAGE_MASK); + + if (!evt->u.ue_error.physical_address_provided || + !is_zone_device_page(pfn_to_page(phys_addr >> PAGE_SHIFT))) + return NOTIFY_DONE; + + /* mce notifier is called from a process context, so mutex is safe */ + mutex_lock(&papr_ndr_lock); + list_for_each_entry(p, &papr_nd_regions, region_list) { + if (phys_addr >= p->res.start && phys_addr <= p->res.end) { + found = true; + break; + } + } + + if (found) + papr_scm_add_badblock(p->region, p->bus, phys_addr); + + mutex_unlock(&papr_ndr_lock); + + return found ? NOTIFY_OK : NOTIFY_DONE; +} + +static struct notifier_block mce_ue_nb = { + .notifier_call = handle_mce_ue +}; + static int papr_scm_probe(struct platform_device *pdev) { struct device_node *dn = pdev->dev.of_node; @@ -866,6 +938,10 @@ static int papr_scm_remove(struct platform_device *pdev) { struct papr_scm_priv *p = platform_get_drvdata(pdev); + mutex_lock(&papr_ndr_lock); + list_del(&p->region_list); + mutex_unlock(&papr_ndr_lock); + nvdimm_bus_unregister(p->bus); drc_pmem_unbind(p); kfree(p->bus_desc.provider_name); @@ -889,7 +965,25 @@ static struct platform_driver papr_scm_driver = { }, }; -module_platform_driver(papr_scm_driver); +static int __init papr_scm_init(void) +{ + int ret; + + ret = platform_driver_register(&papr_scm_driver); + if (!ret) + mce_register_notifier(&mce_ue_nb); + + return ret; +} +module_init(papr_scm_init); + +static void __exit papr_scm_exit(void) +{ + mce_unregister_notifier(&mce_ue_nb); + platform_driver_unregister(&papr_scm_driver); +} +module_exit(papr_scm_exit); + MODULE_DEVICE_TABLE(of, papr_scm_match); MODULE_LICENSE("GPL"); MODULE_AUTHOR("IBM Corporation"); -- GitLab From d79e7a5f26f1d179cbb915a8bf2469b6d7431c29 Mon Sep 17 00:00:00 2001 From: "Aneesh Kumar K.V" Date: Thu, 9 Jul 2020 08:59:24 +0530 Subject: [PATCH 0706/1476] powerpc/book3s64/pkeys: Use PVR check instead of cpu feature We are wrongly using CPU_FTRS_POWER8 to check for P8 support. Instead, we should use PVR value. Now considering we are using CPU_FTRS_POWER8, that implies we returned true for P9 with older firmware. Keep the same behavior by checking for P9 PVR value. Fixes: cf43d3b26452 ("powerpc: Enable pkey subsystem") Signed-off-by: Aneesh Kumar K.V Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20200709032946.881753-2-aneesh.kumar@linux.ibm.com --- arch/powerpc/mm/book3s64/pkeys.c | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/arch/powerpc/mm/book3s64/pkeys.c b/arch/powerpc/mm/book3s64/pkeys.c index d174106bab676..82ace6acb0aa9 100644 --- a/arch/powerpc/mm/book3s64/pkeys.c +++ b/arch/powerpc/mm/book3s64/pkeys.c @@ -83,13 +83,17 @@ static int pkey_initialize(void) scan_pkey_feature(); /* - * Let's assume 32 pkeys on P8 bare metal, if its not defined by device - * tree. We make this exception since skiboot forgot to expose this - * property on power8. + * Let's assume 32 pkeys on P8/P9 bare metal, if its not defined by device + * tree. We make this exception since some version of skiboot forgot to + * expose this property on power8/9. */ - if (!pkeys_devtree_defined && !firmware_has_feature(FW_FEATURE_LPAR) && - cpu_has_feature(CPU_FTRS_POWER8)) - pkeys_total = 32; + if (!pkeys_devtree_defined && !firmware_has_feature(FW_FEATURE_LPAR)) { + unsigned long pvr = mfspr(SPRN_PVR); + + if (PVR_VER(pvr) == PVR_POWER8 || PVR_VER(pvr) == PVR_POWER8E || + PVR_VER(pvr) == PVR_POWER8NVL || PVR_VER(pvr) == PVR_POWER9) + pkeys_total = 32; + } /* * Adjust the upper limit, based on the number of bits supported by -- GitLab From 33699023f51f96ac9be38747e64967ea05e00bab Mon Sep 17 00:00:00 2001 From: "Aneesh Kumar K.V" Date: Thu, 9 Jul 2020 08:59:25 +0530 Subject: [PATCH 0707/1476] powerpc/book3s64/pkeys: Fixup bit numbering This number the pkey bit such that it is easy to follow. PKEY_BIT0 is the lower order bit. This makes further changes easy to follow. No functional change in this patch other than linux page table for hash translation now maps pkeys differently. Signed-off-by: Aneesh Kumar K.V Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20200709032946.881753-3-aneesh.kumar@linux.ibm.com --- arch/powerpc/include/asm/book3s/64/hash-4k.h | 9 +++---- arch/powerpc/include/asm/book3s/64/hash-64k.h | 8 +++---- arch/powerpc/include/asm/book3s/64/mmu-hash.h | 8 +++---- arch/powerpc/include/asm/pkeys.h | 24 +++++++++---------- 4 files changed, 25 insertions(+), 24 deletions(-) diff --git a/arch/powerpc/include/asm/book3s/64/hash-4k.h b/arch/powerpc/include/asm/book3s/64/hash-4k.h index 3f9ae3585ab98..f889d56bf8cf9 100644 --- a/arch/powerpc/include/asm/book3s/64/hash-4k.h +++ b/arch/powerpc/include/asm/book3s/64/hash-4k.h @@ -57,11 +57,12 @@ #define H_PMD_FRAG_NR (PAGE_SIZE >> H_PMD_FRAG_SIZE_SHIFT) /* memory key bits, only 8 keys supported */ -#define H_PTE_PKEY_BIT0 0 -#define H_PTE_PKEY_BIT1 0 +#define H_PTE_PKEY_BIT4 0 +#define H_PTE_PKEY_BIT3 0 #define H_PTE_PKEY_BIT2 _RPAGE_RSV3 -#define H_PTE_PKEY_BIT3 _RPAGE_RSV4 -#define H_PTE_PKEY_BIT4 _RPAGE_RSV5 +#define H_PTE_PKEY_BIT1 _RPAGE_RSV4 +#define H_PTE_PKEY_BIT0 _RPAGE_RSV5 + /* * On all 4K setups, remap_4k_pfn() equates to remap_pfn_range() diff --git a/arch/powerpc/include/asm/book3s/64/hash-64k.h b/arch/powerpc/include/asm/book3s/64/hash-64k.h index 0729c034e56f0..0a15fd14cf727 100644 --- a/arch/powerpc/include/asm/book3s/64/hash-64k.h +++ b/arch/powerpc/include/asm/book3s/64/hash-64k.h @@ -36,11 +36,11 @@ #define H_PAGE_HASHPTE _RPAGE_RPN43 /* PTE has associated HPTE */ /* memory key bits. */ -#define H_PTE_PKEY_BIT0 _RPAGE_RSV1 -#define H_PTE_PKEY_BIT1 _RPAGE_RSV2 +#define H_PTE_PKEY_BIT4 _RPAGE_RSV1 +#define H_PTE_PKEY_BIT3 _RPAGE_RSV2 #define H_PTE_PKEY_BIT2 _RPAGE_RSV3 -#define H_PTE_PKEY_BIT3 _RPAGE_RSV4 -#define H_PTE_PKEY_BIT4 _RPAGE_RSV5 +#define H_PTE_PKEY_BIT1 _RPAGE_RSV4 +#define H_PTE_PKEY_BIT0 _RPAGE_RSV5 /* * We need to differentiate between explicit huge page and THP huge diff --git a/arch/powerpc/include/asm/book3s/64/mmu-hash.h b/arch/powerpc/include/asm/book3s/64/mmu-hash.h index 3fa1b962dc279..58fcc959f9d5a 100644 --- a/arch/powerpc/include/asm/book3s/64/mmu-hash.h +++ b/arch/powerpc/include/asm/book3s/64/mmu-hash.h @@ -86,8 +86,8 @@ #define HPTE_R_PP0 ASM_CONST(0x8000000000000000) #define HPTE_R_TS ASM_CONST(0x4000000000000000) #define HPTE_R_KEY_HI ASM_CONST(0x3000000000000000) -#define HPTE_R_KEY_BIT0 ASM_CONST(0x2000000000000000) -#define HPTE_R_KEY_BIT1 ASM_CONST(0x1000000000000000) +#define HPTE_R_KEY_BIT4 ASM_CONST(0x2000000000000000) +#define HPTE_R_KEY_BIT3 ASM_CONST(0x1000000000000000) #define HPTE_R_RPN_SHIFT 12 #define HPTE_R_RPN ASM_CONST(0x0ffffffffffff000) #define HPTE_R_RPN_3_0 ASM_CONST(0x01fffffffffff000) @@ -103,8 +103,8 @@ #define HPTE_R_R ASM_CONST(0x0000000000000100) #define HPTE_R_KEY_LO ASM_CONST(0x0000000000000e00) #define HPTE_R_KEY_BIT2 ASM_CONST(0x0000000000000800) -#define HPTE_R_KEY_BIT3 ASM_CONST(0x0000000000000400) -#define HPTE_R_KEY_BIT4 ASM_CONST(0x0000000000000200) +#define HPTE_R_KEY_BIT1 ASM_CONST(0x0000000000000400) +#define HPTE_R_KEY_BIT0 ASM_CONST(0x0000000000000200) #define HPTE_R_KEY (HPTE_R_KEY_LO | HPTE_R_KEY_HI) #define HPTE_V_1TB_SEG ASM_CONST(0x4000000000000000) diff --git a/arch/powerpc/include/asm/pkeys.h b/arch/powerpc/include/asm/pkeys.h index 2fe6cae14d105..fec358782c048 100644 --- a/arch/powerpc/include/asm/pkeys.h +++ b/arch/powerpc/include/asm/pkeys.h @@ -35,11 +35,11 @@ static inline u64 vmflag_to_pte_pkey_bits(u64 vm_flags) if (static_branch_likely(&pkey_disabled)) return 0x0UL; - return (((vm_flags & VM_PKEY_BIT0) ? H_PTE_PKEY_BIT4 : 0x0UL) | - ((vm_flags & VM_PKEY_BIT1) ? H_PTE_PKEY_BIT3 : 0x0UL) | + return (((vm_flags & VM_PKEY_BIT0) ? H_PTE_PKEY_BIT0 : 0x0UL) | + ((vm_flags & VM_PKEY_BIT1) ? H_PTE_PKEY_BIT1 : 0x0UL) | ((vm_flags & VM_PKEY_BIT2) ? H_PTE_PKEY_BIT2 : 0x0UL) | - ((vm_flags & VM_PKEY_BIT3) ? H_PTE_PKEY_BIT1 : 0x0UL) | - ((vm_flags & VM_PKEY_BIT4) ? H_PTE_PKEY_BIT0 : 0x0UL)); + ((vm_flags & VM_PKEY_BIT3) ? H_PTE_PKEY_BIT3 : 0x0UL) | + ((vm_flags & VM_PKEY_BIT4) ? H_PTE_PKEY_BIT4 : 0x0UL)); } static inline int vma_pkey(struct vm_area_struct *vma) @@ -53,20 +53,20 @@ static inline int vma_pkey(struct vm_area_struct *vma) static inline u64 pte_to_hpte_pkey_bits(u64 pteflags) { - return (((pteflags & H_PTE_PKEY_BIT0) ? HPTE_R_KEY_BIT0 : 0x0UL) | - ((pteflags & H_PTE_PKEY_BIT1) ? HPTE_R_KEY_BIT1 : 0x0UL) | - ((pteflags & H_PTE_PKEY_BIT2) ? HPTE_R_KEY_BIT2 : 0x0UL) | + return (((pteflags & H_PTE_PKEY_BIT4) ? HPTE_R_KEY_BIT4 : 0x0UL) | ((pteflags & H_PTE_PKEY_BIT3) ? HPTE_R_KEY_BIT3 : 0x0UL) | - ((pteflags & H_PTE_PKEY_BIT4) ? HPTE_R_KEY_BIT4 : 0x0UL)); + ((pteflags & H_PTE_PKEY_BIT2) ? HPTE_R_KEY_BIT2 : 0x0UL) | + ((pteflags & H_PTE_PKEY_BIT1) ? HPTE_R_KEY_BIT1 : 0x0UL) | + ((pteflags & H_PTE_PKEY_BIT0) ? HPTE_R_KEY_BIT0 : 0x0UL)); } static inline u16 pte_to_pkey_bits(u64 pteflags) { - return (((pteflags & H_PTE_PKEY_BIT0) ? 0x10 : 0x0UL) | - ((pteflags & H_PTE_PKEY_BIT1) ? 0x8 : 0x0UL) | + return (((pteflags & H_PTE_PKEY_BIT4) ? 0x10 : 0x0UL) | + ((pteflags & H_PTE_PKEY_BIT3) ? 0x8 : 0x0UL) | ((pteflags & H_PTE_PKEY_BIT2) ? 0x4 : 0x0UL) | - ((pteflags & H_PTE_PKEY_BIT3) ? 0x2 : 0x0UL) | - ((pteflags & H_PTE_PKEY_BIT4) ? 0x1 : 0x0UL)); + ((pteflags & H_PTE_PKEY_BIT1) ? 0x2 : 0x0UL) | + ((pteflags & H_PTE_PKEY_BIT0) ? 0x1 : 0x0UL)); } #define pkey_alloc_mask(pkey) (0x1 << pkey) -- GitLab From b9658f83e721ddfcee3e08b16a6628420de424c3 Mon Sep 17 00:00:00 2001 From: "Aneesh Kumar K.V" Date: Thu, 9 Jul 2020 08:59:26 +0530 Subject: [PATCH 0708/1476] powerpc/book3s64/pkeys: pkeys are supported only on hash on book3s. Signed-off-by: Aneesh Kumar K.V Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20200709032946.881753-4-aneesh.kumar@linux.ibm.com --- .../powerpc/include/asm/book3s/64/hash-pkey.h | 32 ++++++++++++++++ arch/powerpc/include/asm/book3s/64/pkeys.h | 25 +++++++++++++ arch/powerpc/include/asm/pkeys.h | 37 ++++--------------- 3 files changed, 64 insertions(+), 30 deletions(-) create mode 100644 arch/powerpc/include/asm/book3s/64/hash-pkey.h create mode 100644 arch/powerpc/include/asm/book3s/64/pkeys.h diff --git a/arch/powerpc/include/asm/book3s/64/hash-pkey.h b/arch/powerpc/include/asm/book3s/64/hash-pkey.h new file mode 100644 index 0000000000000..795010897e5d1 --- /dev/null +++ b/arch/powerpc/include/asm/book3s/64/hash-pkey.h @@ -0,0 +1,32 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _ASM_POWERPC_BOOK3S_64_HASH_PKEY_H +#define _ASM_POWERPC_BOOK3S_64_HASH_PKEY_H + +static inline u64 hash__vmflag_to_pte_pkey_bits(u64 vm_flags) +{ + return (((vm_flags & VM_PKEY_BIT0) ? H_PTE_PKEY_BIT0 : 0x0UL) | + ((vm_flags & VM_PKEY_BIT1) ? H_PTE_PKEY_BIT1 : 0x0UL) | + ((vm_flags & VM_PKEY_BIT2) ? H_PTE_PKEY_BIT2 : 0x0UL) | + ((vm_flags & VM_PKEY_BIT3) ? H_PTE_PKEY_BIT3 : 0x0UL) | + ((vm_flags & VM_PKEY_BIT4) ? H_PTE_PKEY_BIT4 : 0x0UL)); +} + +static inline u64 pte_to_hpte_pkey_bits(u64 pteflags) +{ + return (((pteflags & H_PTE_PKEY_BIT4) ? HPTE_R_KEY_BIT4 : 0x0UL) | + ((pteflags & H_PTE_PKEY_BIT3) ? HPTE_R_KEY_BIT3 : 0x0UL) | + ((pteflags & H_PTE_PKEY_BIT2) ? HPTE_R_KEY_BIT2 : 0x0UL) | + ((pteflags & H_PTE_PKEY_BIT1) ? HPTE_R_KEY_BIT1 : 0x0UL) | + ((pteflags & H_PTE_PKEY_BIT0) ? HPTE_R_KEY_BIT0 : 0x0UL)); +} + +static inline u16 hash__pte_to_pkey_bits(u64 pteflags) +{ + return (((pteflags & H_PTE_PKEY_BIT4) ? 0x10 : 0x0UL) | + ((pteflags & H_PTE_PKEY_BIT3) ? 0x8 : 0x0UL) | + ((pteflags & H_PTE_PKEY_BIT2) ? 0x4 : 0x0UL) | + ((pteflags & H_PTE_PKEY_BIT1) ? 0x2 : 0x0UL) | + ((pteflags & H_PTE_PKEY_BIT0) ? 0x1 : 0x0UL)); +} + +#endif diff --git a/arch/powerpc/include/asm/book3s/64/pkeys.h b/arch/powerpc/include/asm/book3s/64/pkeys.h new file mode 100644 index 0000000000000..8174662a91731 --- /dev/null +++ b/arch/powerpc/include/asm/book3s/64/pkeys.h @@ -0,0 +1,25 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ + +#ifndef _ASM_POWERPC_BOOK3S_64_PKEYS_H +#define _ASM_POWERPC_BOOK3S_64_PKEYS_H + +#include + +static inline u64 vmflag_to_pte_pkey_bits(u64 vm_flags) +{ + if (static_branch_likely(&pkey_disabled)) + return 0x0UL; + + if (radix_enabled()) + BUG(); + return hash__vmflag_to_pte_pkey_bits(vm_flags); +} + +static inline u16 pte_to_pkey_bits(u64 pteflags) +{ + if (radix_enabled()) + BUG(); + return hash__pte_to_pkey_bits(pteflags); +} + +#endif /*_ASM_POWERPC_KEYS_H */ diff --git a/arch/powerpc/include/asm/pkeys.h b/arch/powerpc/include/asm/pkeys.h index fec358782c048..d06ec0948964c 100644 --- a/arch/powerpc/include/asm/pkeys.h +++ b/arch/powerpc/include/asm/pkeys.h @@ -25,23 +25,18 @@ extern u32 reserved_allocation_mask; /* bits set for reserved keys */ PKEY_DISABLE_WRITE | \ PKEY_DISABLE_EXECUTE) +#ifdef CONFIG_PPC_BOOK3S_64 +#include +#else +#error "Not supported" +#endif + + static inline u64 pkey_to_vmflag_bits(u16 pkey) { return (((u64)pkey << VM_PKEY_SHIFT) & ARCH_VM_PKEY_FLAGS); } -static inline u64 vmflag_to_pte_pkey_bits(u64 vm_flags) -{ - if (static_branch_likely(&pkey_disabled)) - return 0x0UL; - - return (((vm_flags & VM_PKEY_BIT0) ? H_PTE_PKEY_BIT0 : 0x0UL) | - ((vm_flags & VM_PKEY_BIT1) ? H_PTE_PKEY_BIT1 : 0x0UL) | - ((vm_flags & VM_PKEY_BIT2) ? H_PTE_PKEY_BIT2 : 0x0UL) | - ((vm_flags & VM_PKEY_BIT3) ? H_PTE_PKEY_BIT3 : 0x0UL) | - ((vm_flags & VM_PKEY_BIT4) ? H_PTE_PKEY_BIT4 : 0x0UL)); -} - static inline int vma_pkey(struct vm_area_struct *vma) { if (static_branch_likely(&pkey_disabled)) @@ -51,24 +46,6 @@ static inline int vma_pkey(struct vm_area_struct *vma) #define arch_max_pkey() pkeys_total -static inline u64 pte_to_hpte_pkey_bits(u64 pteflags) -{ - return (((pteflags & H_PTE_PKEY_BIT4) ? HPTE_R_KEY_BIT4 : 0x0UL) | - ((pteflags & H_PTE_PKEY_BIT3) ? HPTE_R_KEY_BIT3 : 0x0UL) | - ((pteflags & H_PTE_PKEY_BIT2) ? HPTE_R_KEY_BIT2 : 0x0UL) | - ((pteflags & H_PTE_PKEY_BIT1) ? HPTE_R_KEY_BIT1 : 0x0UL) | - ((pteflags & H_PTE_PKEY_BIT0) ? HPTE_R_KEY_BIT0 : 0x0UL)); -} - -static inline u16 pte_to_pkey_bits(u64 pteflags) -{ - return (((pteflags & H_PTE_PKEY_BIT4) ? 0x10 : 0x0UL) | - ((pteflags & H_PTE_PKEY_BIT3) ? 0x8 : 0x0UL) | - ((pteflags & H_PTE_PKEY_BIT2) ? 0x4 : 0x0UL) | - ((pteflags & H_PTE_PKEY_BIT1) ? 0x2 : 0x0UL) | - ((pteflags & H_PTE_PKEY_BIT0) ? 0x1 : 0x0UL)); -} - #define pkey_alloc_mask(pkey) (0x1 << pkey) #define mm_pkey_allocation_map(mm) (mm->context.pkey_allocation_map) -- GitLab From ee8b39331f89950b0a011c7965db5694f0153166 Mon Sep 17 00:00:00 2001 From: "Aneesh Kumar K.V" Date: Thu, 9 Jul 2020 08:59:27 +0530 Subject: [PATCH 0709/1476] powerpc/book3s64/pkeys: Move pkey related bits in the linux page table To keep things simple, all the pkey related bits are kept together in linux page table for 64K config with hash translation. With hash-4k kernel requires 4 bits to store slots details. This is done by overloading some of the RPN bits for storing the slot details. Due to this PKEY_BIT0 on the 4K config is used for storing hash slot details. 64K before |....|RSV1| RSV2| RSV3 | RSV4 | RPN44| RPN43 |.... | RSV5| |....| P4 | P3 | P2 | P1 | Busy | HASHPTE |.... | P0 | after |....|RSV1| RSV2| RSV3 | RSV4 | RPN44 | RPN43 |.... | RSV5 | |....| P4 | P3 | P2 | P1 | P0 | HASHPTE |.... | Busy | 4k before |....| RSV1 | RSV2 | RSV3 | RSV4 | RPN44| RPN43.... | RSV5| |....| Busy | HASHPTE | P2 | P1 | F_SEC| F_GIX.... | P0 | after |....| RSV1 | RSV2| RSV3 | RSV4 | Free | RPN43.... | RSV5 | |....| HASHPTE | P2 | P1 | P0 | F_SEC| F_GIX.... | BUSY | Signed-off-by: Aneesh Kumar K.V Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20200709032946.881753-5-aneesh.kumar@linux.ibm.com --- arch/powerpc/include/asm/book3s/64/hash-4k.h | 16 ++++++++-------- arch/powerpc/include/asm/book3s/64/hash-64k.h | 12 ++++++------ arch/powerpc/include/asm/book3s/64/pgtable.h | 17 ++++++++--------- 3 files changed, 22 insertions(+), 23 deletions(-) diff --git a/arch/powerpc/include/asm/book3s/64/hash-4k.h b/arch/powerpc/include/asm/book3s/64/hash-4k.h index f889d56bf8cf9..082b988087011 100644 --- a/arch/powerpc/include/asm/book3s/64/hash-4k.h +++ b/arch/powerpc/include/asm/book3s/64/hash-4k.h @@ -34,11 +34,11 @@ #define H_PUD_TABLE_SIZE (sizeof(pud_t) << H_PUD_INDEX_SIZE) #define H_PGD_TABLE_SIZE (sizeof(pgd_t) << H_PGD_INDEX_SIZE) -#define H_PAGE_F_GIX_SHIFT 53 -#define H_PAGE_F_SECOND _RPAGE_RPN44 /* HPTE is in 2ndary HPTEG */ -#define H_PAGE_F_GIX (_RPAGE_RPN43 | _RPAGE_RPN42 | _RPAGE_RPN41) -#define H_PAGE_BUSY _RPAGE_RSV1 /* software: PTE & hash are busy */ -#define H_PAGE_HASHPTE _RPAGE_RSV2 /* software: PTE & hash are busy */ +#define H_PAGE_F_GIX_SHIFT _PAGE_PA_MAX +#define H_PAGE_F_SECOND _RPAGE_PKEY_BIT0 /* HPTE is in 2ndary HPTEG */ +#define H_PAGE_F_GIX (_RPAGE_RPN43 | _RPAGE_RPN42 | _RPAGE_RPN41) +#define H_PAGE_BUSY _RPAGE_RSV1 +#define H_PAGE_HASHPTE _RPAGE_PKEY_BIT4 /* PTE flags to conserve for HPTE identification */ #define _PAGE_HPTEFLAGS (H_PAGE_BUSY | H_PAGE_HASHPTE | \ @@ -59,9 +59,9 @@ /* memory key bits, only 8 keys supported */ #define H_PTE_PKEY_BIT4 0 #define H_PTE_PKEY_BIT3 0 -#define H_PTE_PKEY_BIT2 _RPAGE_RSV3 -#define H_PTE_PKEY_BIT1 _RPAGE_RSV4 -#define H_PTE_PKEY_BIT0 _RPAGE_RSV5 +#define H_PTE_PKEY_BIT2 _RPAGE_PKEY_BIT3 +#define H_PTE_PKEY_BIT1 _RPAGE_PKEY_BIT2 +#define H_PTE_PKEY_BIT0 _RPAGE_PKEY_BIT1 /* diff --git a/arch/powerpc/include/asm/book3s/64/hash-64k.h b/arch/powerpc/include/asm/book3s/64/hash-64k.h index 0a15fd14cf727..f20de1149ebe6 100644 --- a/arch/powerpc/include/asm/book3s/64/hash-64k.h +++ b/arch/powerpc/include/asm/book3s/64/hash-64k.h @@ -32,15 +32,15 @@ */ #define H_PAGE_COMBO _RPAGE_RPN0 /* this is a combo 4k page */ #define H_PAGE_4K_PFN _RPAGE_RPN1 /* PFN is for a single 4k page */ -#define H_PAGE_BUSY _RPAGE_RPN44 /* software: PTE & hash are busy */ +#define H_PAGE_BUSY _RPAGE_RSV1 /* software: PTE & hash are busy */ #define H_PAGE_HASHPTE _RPAGE_RPN43 /* PTE has associated HPTE */ /* memory key bits. */ -#define H_PTE_PKEY_BIT4 _RPAGE_RSV1 -#define H_PTE_PKEY_BIT3 _RPAGE_RSV2 -#define H_PTE_PKEY_BIT2 _RPAGE_RSV3 -#define H_PTE_PKEY_BIT1 _RPAGE_RSV4 -#define H_PTE_PKEY_BIT0 _RPAGE_RSV5 +#define H_PTE_PKEY_BIT4 _RPAGE_PKEY_BIT4 +#define H_PTE_PKEY_BIT3 _RPAGE_PKEY_BIT3 +#define H_PTE_PKEY_BIT2 _RPAGE_PKEY_BIT2 +#define H_PTE_PKEY_BIT1 _RPAGE_PKEY_BIT1 +#define H_PTE_PKEY_BIT0 _RPAGE_PKEY_BIT0 /* * We need to differentiate between explicit huge page and THP huge diff --git a/arch/powerpc/include/asm/book3s/64/pgtable.h b/arch/powerpc/include/asm/book3s/64/pgtable.h index 25c3cb8272c05..495fc0ccb4531 100644 --- a/arch/powerpc/include/asm/book3s/64/pgtable.h +++ b/arch/powerpc/include/asm/book3s/64/pgtable.h @@ -32,11 +32,13 @@ #define _RPAGE_SW1 0x00800 #define _RPAGE_SW2 0x00400 #define _RPAGE_SW3 0x00200 -#define _RPAGE_RSV1 0x1000000000000000UL -#define _RPAGE_RSV2 0x0800000000000000UL -#define _RPAGE_RSV3 0x0400000000000000UL -#define _RPAGE_RSV4 0x0200000000000000UL -#define _RPAGE_RSV5 0x00040UL +#define _RPAGE_RSV1 0x00040UL + +#define _RPAGE_PKEY_BIT4 0x1000000000000000UL +#define _RPAGE_PKEY_BIT3 0x0800000000000000UL +#define _RPAGE_PKEY_BIT2 0x0400000000000000UL +#define _RPAGE_PKEY_BIT1 0x0200000000000000UL +#define _RPAGE_PKEY_BIT0 0x0100000000000000UL #define _PAGE_PTE 0x4000000000000000UL /* distinguishes PTEs from pointers */ #define _PAGE_PRESENT 0x8000000000000000UL /* pte contains a translation */ @@ -58,13 +60,12 @@ */ #define _RPAGE_RPN0 0x01000 #define _RPAGE_RPN1 0x02000 -#define _RPAGE_RPN44 0x0100000000000000UL #define _RPAGE_RPN43 0x0080000000000000UL #define _RPAGE_RPN42 0x0040000000000000UL #define _RPAGE_RPN41 0x0020000000000000UL /* Max physical address bit as per radix table */ -#define _RPAGE_PA_MAX 57 +#define _RPAGE_PA_MAX 56 /* * Max physical address bit we will use for now. @@ -125,8 +126,6 @@ _PAGE_ACCESSED | _PAGE_SPECIAL | _PAGE_PTE | \ _PAGE_SOFT_DIRTY | _PAGE_DEVMAP) -#define H_PTE_PKEY (H_PTE_PKEY_BIT0 | H_PTE_PKEY_BIT1 | H_PTE_PKEY_BIT2 | \ - H_PTE_PKEY_BIT3 | H_PTE_PKEY_BIT4) /* * We define 2 sets of base prot bits, one for basic pages (ie, * cacheable kernel and user pages) and one for non cacheable -- GitLab From 1f404058e2911afe08417ef82f17aba6adccfc63 Mon Sep 17 00:00:00 2001 From: "Aneesh Kumar K.V" Date: Thu, 9 Jul 2020 08:59:28 +0530 Subject: [PATCH 0710/1476] powerpc/book3s64/pkeys: Explain key 1 reservation details This explains the details w.r.t key 1. Signed-off-by: Aneesh Kumar K.V Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20200709032946.881753-6-aneesh.kumar@linux.ibm.com --- arch/powerpc/mm/book3s64/pkeys.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/arch/powerpc/mm/book3s64/pkeys.c b/arch/powerpc/mm/book3s64/pkeys.c index 82ace6acb0aa9..ee27f18fac322 100644 --- a/arch/powerpc/mm/book3s64/pkeys.c +++ b/arch/powerpc/mm/book3s64/pkeys.c @@ -128,7 +128,10 @@ static int pkey_initialize(void) #else os_reserved = 0; #endif - /* Bits are in LE format. */ + /* + * key 1 is recommended not to be used. PowerISA(3.0) page 1015, + * programming note. + */ reserved_allocation_mask = (0x1 << 1) | (0x1 << execute_only_key); /* register mask is in BE format */ -- GitLab From f491fe3fb41eafc7a159874040e032ad41ade210 Mon Sep 17 00:00:00 2001 From: "Aneesh Kumar K.V" Date: Thu, 9 Jul 2020 08:59:29 +0530 Subject: [PATCH 0711/1476] powerpc/book3s64/pkeys: Simplify the key initialization Add documentation explaining the execute_only_key. The reservation and initialization mask details are also explained in this patch. No functional change in this patch. Signed-off-by: Aneesh Kumar K.V Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20200709032946.881753-7-aneesh.kumar@linux.ibm.com --- arch/powerpc/mm/book3s64/pkeys.c | 199 ++++++++++++++++++------------- 1 file changed, 116 insertions(+), 83 deletions(-) diff --git a/arch/powerpc/mm/book3s64/pkeys.c b/arch/powerpc/mm/book3s64/pkeys.c index ee27f18fac322..2dea8c29df57c 100644 --- a/arch/powerpc/mm/book3s64/pkeys.c +++ b/arch/powerpc/mm/book3s64/pkeys.c @@ -15,48 +15,80 @@ DEFINE_STATIC_KEY_TRUE(pkey_disabled); int pkeys_total; /* Total pkeys as per device tree */ u32 initial_allocation_mask; /* Bits set for the initially allocated keys */ -u32 reserved_allocation_mask; /* Bits set for reserved keys */ +/* + * Keys marked in the reservation list cannot be allocated by userspace + */ +u32 reserved_allocation_mask; static bool pkey_execute_disable_supported; -static bool pkeys_devtree_defined; /* property exported by device tree */ -static u64 pkey_amr_mask; /* Bits in AMR not to be touched */ -static u64 pkey_iamr_mask; /* Bits in AMR not to be touched */ -static u64 pkey_uamor_mask; /* Bits in UMOR not to be touched */ +/* + * Even if we allocate keys with sys_pkey_alloc(), we need to make sure + * other thread still find the access denied using the same keys. + */ +static u64 default_amr = ~0x0UL; +static u64 default_iamr = 0x5555555555555555UL; + +/* Allow all keys to be modified by default */ +static u64 default_uamor = ~0x0UL; +/* + * Key used to implement PROT_EXEC mmap. Denies READ/WRITE + * We pick key 2 because 0 is special key and 1 is reserved as per ISA. + */ static int execute_only_key = 2; + #define AMR_BITS_PER_PKEY 2 #define AMR_RD_BIT 0x1UL #define AMR_WR_BIT 0x2UL #define IAMR_EX_BIT 0x1UL -#define PKEY_REG_BITS (sizeof(u64)*8) +#define PKEY_REG_BITS (sizeof(u64) * 8) #define pkeyshift(pkey) (PKEY_REG_BITS - ((pkey+1) * AMR_BITS_PER_PKEY)) -static void scan_pkey_feature(void) +static int scan_pkey_feature(void) { u32 vals[2]; + int pkeys_total = 0; struct device_node *cpu; + /* + * Pkey is not supported with Radix translation. + */ + if (radix_enabled()) + return 0; + cpu = of_find_node_by_type(NULL, "cpu"); if (!cpu) - return; + return 0; if (of_property_read_u32_array(cpu, - "ibm,processor-storage-keys", vals, 2)) - return; + "ibm,processor-storage-keys", vals, 2) == 0) { + /* + * Since any pkey can be used for data or execute, we will + * just treat all keys as equal and track them as one entity. + */ + pkeys_total = vals[0]; + } else { + + /* + * Let's assume 32 pkeys on P8/P9 bare metal, if its not defined by device + * tree. We make this exception since some version of skiboot forgot to + * expose this property on power8/9. + */ + if (!firmware_has_feature(FW_FEATURE_LPAR)) { + unsigned long pvr = mfspr(SPRN_PVR); + + if (PVR_VER(pvr) == PVR_POWER8 || PVR_VER(pvr) == PVR_POWER8E || + PVR_VER(pvr) == PVR_POWER8NVL || PVR_VER(pvr) == PVR_POWER9) + pkeys_total = 32; + } + } /* - * Since any pkey can be used for data or execute, we will just treat - * all keys as equal and track them as one entity. + * Adjust the upper limit, based on the number of bits supported by + * arch-neutral code. */ - pkeys_total = vals[0]; - pkeys_devtree_defined = true; -} - -static inline bool pkey_mmu_enabled(void) -{ - if (firmware_has_feature(FW_FEATURE_LPAR)) - return pkeys_total; - else - return cpu_has_feature(CPU_FTR_PKEY); + pkeys_total = min_t(int, pkeys_total, + ((ARCH_VM_PKEY_FLAGS >> VM_PKEY_SHIFT) + 1)); + return pkeys_total; } static int pkey_initialize(void) @@ -80,35 +112,13 @@ static int pkey_initialize(void) != (sizeof(u64) * BITS_PER_BYTE)); /* scan the device tree for pkey feature */ - scan_pkey_feature(); - - /* - * Let's assume 32 pkeys on P8/P9 bare metal, if its not defined by device - * tree. We make this exception since some version of skiboot forgot to - * expose this property on power8/9. - */ - if (!pkeys_devtree_defined && !firmware_has_feature(FW_FEATURE_LPAR)) { - unsigned long pvr = mfspr(SPRN_PVR); - - if (PVR_VER(pvr) == PVR_POWER8 || PVR_VER(pvr) == PVR_POWER8E || - PVR_VER(pvr) == PVR_POWER8NVL || PVR_VER(pvr) == PVR_POWER9) - pkeys_total = 32; - } - - /* - * Adjust the upper limit, based on the number of bits supported by - * arch-neutral code. - */ - pkeys_total = min_t(int, pkeys_total, - ((ARCH_VM_PKEY_FLAGS >> VM_PKEY_SHIFT)+1)); - - if (!pkey_mmu_enabled() || radix_enabled() || !pkeys_total) - static_branch_enable(&pkey_disabled); - else + pkeys_total = scan_pkey_feature(); + if (pkeys_total) static_branch_disable(&pkey_disabled); - - if (static_branch_likely(&pkey_disabled)) + else { + static_branch_enable(&pkey_disabled); return 0; + } /* * The device tree cannot be relied to indicate support for @@ -122,48 +132,71 @@ static int pkey_initialize(void) #ifdef CONFIG_PPC_4K_PAGES /* * The OS can manage only 8 pkeys due to its inability to represent them - * in the Linux 4K PTE. + * in the Linux 4K PTE. Mark all other keys reserved. */ os_reserved = pkeys_total - 8; #else os_reserved = 0; #endif - /* - * key 1 is recommended not to be used. PowerISA(3.0) page 1015, - * programming note. - */ - reserved_allocation_mask = (0x1 << 1) | (0x1 << execute_only_key); - - /* register mask is in BE format */ - pkey_amr_mask = ~0x0ul; - pkey_amr_mask &= ~(0x3ul << pkeyshift(0)); - - pkey_iamr_mask = ~0x0ul; - pkey_iamr_mask &= ~(0x3ul << pkeyshift(0)); - pkey_iamr_mask &= ~(0x3ul << pkeyshift(execute_only_key)); - - pkey_uamor_mask = ~0x0ul; - pkey_uamor_mask &= ~(0x3ul << pkeyshift(0)); - pkey_uamor_mask &= ~(0x3ul << pkeyshift(execute_only_key)); - - /* mark the rest of the keys as reserved and hence unavailable */ - for (i = (pkeys_total - os_reserved); i < pkeys_total; i++) { - reserved_allocation_mask |= (0x1 << i); - pkey_uamor_mask &= ~(0x3ul << pkeyshift(i)); - } - initial_allocation_mask = reserved_allocation_mask | (0x1 << 0); if (unlikely((pkeys_total - os_reserved) <= execute_only_key)) { /* * Insufficient number of keys to support * execute only key. Mark it unavailable. - * Any AMR, UAMOR, IAMR bit set for - * this key is irrelevant since this key - * can never be allocated. */ execute_only_key = -1; + } else { + /* + * Mark the execute_only_pkey as not available for + * user allocation via pkey_alloc. + */ + reserved_allocation_mask |= (0x1 << execute_only_key); + + /* + * Deny READ/WRITE for execute_only_key. + * Allow execute in IAMR. + */ + default_amr |= (0x3ul << pkeyshift(execute_only_key)); + default_iamr &= ~(0x1ul << pkeyshift(execute_only_key)); + + /* + * Clear the uamor bits for this key. + */ + default_uamor &= ~(0x3ul << pkeyshift(execute_only_key)); } + /* + * Allow access for only key 0. And prevent any other modification. + */ + default_amr &= ~(0x3ul << pkeyshift(0)); + default_iamr &= ~(0x1ul << pkeyshift(0)); + default_uamor &= ~(0x3ul << pkeyshift(0)); + /* + * key 0 is special in that we want to consider it an allocated + * key which is preallocated. We don't allow changing AMR bits + * w.r.t key 0. But one can pkey_free(key0) + */ + initial_allocation_mask |= (0x1 << 0); + + /* + * key 1 is recommended not to be used. PowerISA(3.0) page 1015, + * programming note. + */ + reserved_allocation_mask |= (0x1 << 1); + + /* + * Prevent the usage of OS reserved the keys. Update UAMOR + * for those keys. + */ + for (i = (pkeys_total - os_reserved); i < pkeys_total; i++) { + reserved_allocation_mask |= (0x1 << i); + default_uamor &= ~(0x3ul << pkeyshift(i)); + } + /* + * Prevent the allocation of reserved keys too. + */ + initial_allocation_mask |= reserved_allocation_mask; + return 0; } @@ -305,13 +338,13 @@ void thread_pkey_regs_init(struct thread_struct *thread) if (static_branch_likely(&pkey_disabled)) return; - thread->amr = pkey_amr_mask; - thread->iamr = pkey_iamr_mask; - thread->uamor = pkey_uamor_mask; + thread->amr = default_amr; + thread->iamr = default_iamr; + thread->uamor = default_uamor; - write_uamor(pkey_uamor_mask); - write_amr(pkey_amr_mask); - write_iamr(pkey_iamr_mask); + write_amr(default_amr); + write_iamr(default_iamr); + write_uamor(default_uamor); } int __execute_only_pkey(struct mm_struct *mm) -- GitLab From 718d9b380174eb8fe16d67769395737b79654a02 Mon Sep 17 00:00:00 2001 From: "Aneesh Kumar K.V" Date: Thu, 9 Jul 2020 08:59:30 +0530 Subject: [PATCH 0712/1476] powerpc/book3s64/pkeys: Prevent key 1 modification from userspace. Key 1 is marked reserved by ISA. Setup uamor to prevent userspace modification of the same. Signed-off-by: Aneesh Kumar K.V Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20200709032946.881753-8-aneesh.kumar@linux.ibm.com --- arch/powerpc/mm/book3s64/pkeys.c | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/powerpc/mm/book3s64/pkeys.c b/arch/powerpc/mm/book3s64/pkeys.c index 2dea8c29df57c..e2bf93901bd0f 100644 --- a/arch/powerpc/mm/book3s64/pkeys.c +++ b/arch/powerpc/mm/book3s64/pkeys.c @@ -183,6 +183,7 @@ static int pkey_initialize(void) * programming note. */ reserved_allocation_mask |= (0x1 << 1); + default_uamor &= ~(0x3ul << pkeyshift(1)); /* * Prevent the usage of OS reserved the keys. Update UAMOR -- GitLab From a24204c307962214996627e3f4caa8772b9b0cf4 Mon Sep 17 00:00:00 2001 From: "Aneesh Kumar K.V" Date: Thu, 9 Jul 2020 08:59:31 +0530 Subject: [PATCH 0713/1476] powerpc/book3s64/pkeys: kill cpu feature key CPU_FTR_PKEY We don't use CPU_FTR_PKEY anymore. Remove the feature bit and mark it free. Signed-off-by: Aneesh Kumar K.V Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20200709032946.881753-9-aneesh.kumar@linux.ibm.com --- arch/powerpc/include/asm/cputable.h | 13 ++++++------- arch/powerpc/kernel/dt_cpu_ftrs.c | 6 ------ 2 files changed, 6 insertions(+), 13 deletions(-) diff --git a/arch/powerpc/include/asm/cputable.h b/arch/powerpc/include/asm/cputable.h index bac2252c839e2..dd0a2e77a6953 100644 --- a/arch/powerpc/include/asm/cputable.h +++ b/arch/powerpc/include/asm/cputable.h @@ -198,7 +198,7 @@ static inline void cpu_feature_keys_init(void) { } #define CPU_FTR_STCX_CHECKS_ADDRESS LONG_ASM_CONST(0x0000000080000000) #define CPU_FTR_POPCNTB LONG_ASM_CONST(0x0000000100000000) #define CPU_FTR_POPCNTD LONG_ASM_CONST(0x0000000200000000) -#define CPU_FTR_PKEY LONG_ASM_CONST(0x0000000400000000) +/* LONG_ASM_CONST(0x0000000400000000) Free */ #define CPU_FTR_VMX_COPY LONG_ASM_CONST(0x0000000800000000) #define CPU_FTR_TM LONG_ASM_CONST(0x0000001000000000) #define CPU_FTR_CFAR LONG_ASM_CONST(0x0000002000000000) @@ -438,7 +438,7 @@ static inline void cpu_feature_keys_init(void) { } CPU_FTR_DSCR | CPU_FTR_SAO | CPU_FTR_ASYM_SMT | \ CPU_FTR_STCX_CHECKS_ADDRESS | CPU_FTR_POPCNTB | CPU_FTR_POPCNTD | \ CPU_FTR_CFAR | CPU_FTR_HVMODE | \ - CPU_FTR_VMX_COPY | CPU_FTR_HAS_PPR | CPU_FTR_DABRX | CPU_FTR_PKEY) + CPU_FTR_VMX_COPY | CPU_FTR_HAS_PPR | CPU_FTR_DABRX ) #define CPU_FTRS_POWER8 (CPU_FTR_LWSYNC | \ CPU_FTR_PPCAS_ARCH_V2 | CPU_FTR_CTRL | CPU_FTR_ARCH_206 |\ CPU_FTR_MMCRA | CPU_FTR_SMT | \ @@ -448,7 +448,7 @@ static inline void cpu_feature_keys_init(void) { } CPU_FTR_STCX_CHECKS_ADDRESS | CPU_FTR_POPCNTB | CPU_FTR_POPCNTD | \ CPU_FTR_CFAR | CPU_FTR_HVMODE | CPU_FTR_VMX_COPY | \ CPU_FTR_DBELL | CPU_FTR_HAS_PPR | CPU_FTR_DAWR | \ - CPU_FTR_ARCH_207S | CPU_FTR_TM_COMP | CPU_FTR_PKEY) + CPU_FTR_ARCH_207S | CPU_FTR_TM_COMP ) #define CPU_FTRS_POWER8E (CPU_FTRS_POWER8 | CPU_FTR_PMAO_BUG) #define CPU_FTRS_POWER9 (CPU_FTR_LWSYNC | \ CPU_FTR_PPCAS_ARCH_V2 | CPU_FTR_CTRL | CPU_FTR_ARCH_206 |\ @@ -459,8 +459,8 @@ static inline void cpu_feature_keys_init(void) { } CPU_FTR_STCX_CHECKS_ADDRESS | CPU_FTR_POPCNTB | CPU_FTR_POPCNTD | \ CPU_FTR_CFAR | CPU_FTR_HVMODE | CPU_FTR_VMX_COPY | \ CPU_FTR_DBELL | CPU_FTR_HAS_PPR | CPU_FTR_ARCH_207S | \ - CPU_FTR_TM_COMP | CPU_FTR_ARCH_300 | CPU_FTR_PKEY | \ - CPU_FTR_P9_TLBIE_STQ_BUG | CPU_FTR_P9_TLBIE_ERAT_BUG | CPU_FTR_P9_TIDR) + CPU_FTR_TM_COMP | CPU_FTR_ARCH_300 | CPU_FTR_P9_TLBIE_STQ_BUG | \ + CPU_FTR_P9_TLBIE_ERAT_BUG | CPU_FTR_P9_TIDR) #define CPU_FTRS_POWER9_DD2_0 (CPU_FTRS_POWER9 | CPU_FTR_P9_RADIX_PREFETCH_BUG) #define CPU_FTRS_POWER9_DD2_1 (CPU_FTRS_POWER9 | \ CPU_FTR_P9_RADIX_PREFETCH_BUG | \ @@ -477,8 +477,7 @@ static inline void cpu_feature_keys_init(void) { } CPU_FTR_STCX_CHECKS_ADDRESS | CPU_FTR_POPCNTB | CPU_FTR_POPCNTD | \ CPU_FTR_CFAR | CPU_FTR_HVMODE | CPU_FTR_VMX_COPY | \ CPU_FTR_DBELL | CPU_FTR_HAS_PPR | CPU_FTR_ARCH_207S | \ - CPU_FTR_TM_COMP | CPU_FTR_ARCH_300 | CPU_FTR_PKEY | \ - CPU_FTR_ARCH_31) + CPU_FTR_TM_COMP | CPU_FTR_ARCH_300 | CPU_FTR_ARCH_31) #define CPU_FTRS_CELL (CPU_FTR_LWSYNC | \ CPU_FTR_PPCAS_ARCH_V2 | CPU_FTR_CTRL | \ CPU_FTR_ALTIVEC_COMP | CPU_FTR_MMCRA | CPU_FTR_SMT | \ diff --git a/arch/powerpc/kernel/dt_cpu_ftrs.c b/arch/powerpc/kernel/dt_cpu_ftrs.c index ac650c233cd97..554bec785f6a2 100644 --- a/arch/powerpc/kernel/dt_cpu_ftrs.c +++ b/arch/powerpc/kernel/dt_cpu_ftrs.c @@ -776,12 +776,6 @@ static __init void cpufeatures_cpu_quirks(void) } update_tlbie_feature_flag(version); - /* - * PKEY was not in the initial base or feature node - * specification, but it should become optional in the next - * cpu feature version sequence. - */ - cur_cpu_spec->cpu_features |= CPU_FTR_PKEY; } static void __init cpufeatures_setup_finished(void) -- GitLab From a4678d4b477c3d2901f101986ca01406f3b7eaea Mon Sep 17 00:00:00 2001 From: "Aneesh Kumar K.V" Date: Thu, 9 Jul 2020 08:59:32 +0530 Subject: [PATCH 0714/1476] powerpc/book3s64/pkeys: Simplify pkey disable branch Make the default value FALSE (pkey enabled) and set to TRUE when we find the total number of keys supported to be zero. Signed-off-by: Aneesh Kumar K.V Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20200709032946.881753-10-aneesh.kumar@linux.ibm.com --- arch/powerpc/include/asm/pkeys.h | 2 +- arch/powerpc/mm/book3s64/pkeys.c | 7 +++---- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/arch/powerpc/include/asm/pkeys.h b/arch/powerpc/include/asm/pkeys.h index d06ec0948964c..f984bfac814ab 100644 --- a/arch/powerpc/include/asm/pkeys.h +++ b/arch/powerpc/include/asm/pkeys.h @@ -11,7 +11,7 @@ #include #include -DECLARE_STATIC_KEY_TRUE(pkey_disabled); +DECLARE_STATIC_KEY_FALSE(pkey_disabled); extern int pkeys_total; /* total pkeys as per device tree */ extern u32 initial_allocation_mask; /* bits set for the initially allocated keys */ extern u32 reserved_allocation_mask; /* bits set for reserved keys */ diff --git a/arch/powerpc/mm/book3s64/pkeys.c b/arch/powerpc/mm/book3s64/pkeys.c index e2bf93901bd0f..8bdb56ce8b434 100644 --- a/arch/powerpc/mm/book3s64/pkeys.c +++ b/arch/powerpc/mm/book3s64/pkeys.c @@ -12,7 +12,7 @@ #include #include -DEFINE_STATIC_KEY_TRUE(pkey_disabled); +DEFINE_STATIC_KEY_FALSE(pkey_disabled); int pkeys_total; /* Total pkeys as per device tree */ u32 initial_allocation_mask; /* Bits set for the initially allocated keys */ /* @@ -113,9 +113,8 @@ static int pkey_initialize(void) /* scan the device tree for pkey feature */ pkeys_total = scan_pkey_feature(); - if (pkeys_total) - static_branch_disable(&pkey_disabled); - else { + if (!pkeys_total) { + /* No support for pkey. Mark it disabled */ static_branch_enable(&pkey_disabled); return 0; } -- GitLab From c529afd7cbc71ae1dc44a31efc7c1c9db3c3a143 Mon Sep 17 00:00:00 2001 From: "Aneesh Kumar K.V" Date: Thu, 9 Jul 2020 08:59:33 +0530 Subject: [PATCH 0715/1476] powerpc/book3s64/pkeys: Convert pkey_total to num_pkey num_pkey now represents max number of keys supported such that we return to userspace 0 - num_pkey - 1. Signed-off-by: Aneesh Kumar K.V Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20200709032946.881753-11-aneesh.kumar@linux.ibm.com --- arch/powerpc/include/asm/pkeys.h | 7 +++++-- arch/powerpc/mm/book3s64/pkeys.c | 14 +++++++------- 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/arch/powerpc/include/asm/pkeys.h b/arch/powerpc/include/asm/pkeys.h index f984bfac814ab..26b20061512f2 100644 --- a/arch/powerpc/include/asm/pkeys.h +++ b/arch/powerpc/include/asm/pkeys.h @@ -12,7 +12,7 @@ #include DECLARE_STATIC_KEY_FALSE(pkey_disabled); -extern int pkeys_total; /* total pkeys as per device tree */ +extern int num_pkey; extern u32 initial_allocation_mask; /* bits set for the initially allocated keys */ extern u32 reserved_allocation_mask; /* bits set for reserved keys */ @@ -44,7 +44,10 @@ static inline int vma_pkey(struct vm_area_struct *vma) return (vma->vm_flags & ARCH_VM_PKEY_FLAGS) >> VM_PKEY_SHIFT; } -#define arch_max_pkey() pkeys_total +static inline int arch_max_pkey(void) +{ + return num_pkey; +} #define pkey_alloc_mask(pkey) (0x1 << pkey) diff --git a/arch/powerpc/mm/book3s64/pkeys.c b/arch/powerpc/mm/book3s64/pkeys.c index 8bdb56ce8b434..65926c00c8a6e 100644 --- a/arch/powerpc/mm/book3s64/pkeys.c +++ b/arch/powerpc/mm/book3s64/pkeys.c @@ -13,7 +13,7 @@ #include DEFINE_STATIC_KEY_FALSE(pkey_disabled); -int pkeys_total; /* Total pkeys as per device tree */ +int num_pkey; /* Max number of pkeys supported */ u32 initial_allocation_mask; /* Bits set for the initially allocated keys */ /* * Keys marked in the reservation list cannot be allocated by userspace @@ -93,7 +93,7 @@ static int scan_pkey_feature(void) static int pkey_initialize(void) { - int os_reserved, i; + int pkeys_total, i; /* * We define PKEY_DISABLE_EXECUTE in addition to the arch-neutral @@ -133,12 +133,12 @@ static int pkey_initialize(void) * The OS can manage only 8 pkeys due to its inability to represent them * in the Linux 4K PTE. Mark all other keys reserved. */ - os_reserved = pkeys_total - 8; + num_pkey = min(8, pkeys_total); #else - os_reserved = 0; + num_pkey = pkeys_total; #endif - if (unlikely((pkeys_total - os_reserved) <= execute_only_key)) { + if (unlikely(num_pkey <= execute_only_key)) { /* * Insufficient number of keys to support * execute only key. Mark it unavailable. @@ -185,10 +185,10 @@ static int pkey_initialize(void) default_uamor &= ~(0x3ul << pkeyshift(1)); /* - * Prevent the usage of OS reserved the keys. Update UAMOR + * Prevent the usage of OS reserved keys. Update UAMOR * for those keys. */ - for (i = (pkeys_total - os_reserved); i < pkeys_total; i++) { + for (i = num_pkey; i < pkeys_total; i++) { reserved_allocation_mask |= (0x1 << i); default_uamor &= ~(0x3ul << pkeyshift(i)); } -- GitLab From 3c8ab47362fe9a74f61b48efe957666a423c55a2 Mon Sep 17 00:00:00 2001 From: "Aneesh Kumar K.V" Date: Thu, 9 Jul 2020 08:59:34 +0530 Subject: [PATCH 0716/1476] powerpc/book3s64/pkeys: Make initial_allocation_mask static initial_allocation_mask is not used outside this file. Also mark reserved_allocation_mask and initial_allocation_mask __ro_after_init; Signed-off-by: Aneesh Kumar K.V Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20200709032946.881753-12-aneesh.kumar@linux.ibm.com --- arch/powerpc/include/asm/pkeys.h | 1 - arch/powerpc/mm/book3s64/pkeys.c | 7 +++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/arch/powerpc/include/asm/pkeys.h b/arch/powerpc/include/asm/pkeys.h index 26b20061512f2..dd32e30f6767b 100644 --- a/arch/powerpc/include/asm/pkeys.h +++ b/arch/powerpc/include/asm/pkeys.h @@ -13,7 +13,6 @@ DECLARE_STATIC_KEY_FALSE(pkey_disabled); extern int num_pkey; -extern u32 initial_allocation_mask; /* bits set for the initially allocated keys */ extern u32 reserved_allocation_mask; /* bits set for reserved keys */ #define ARCH_VM_PKEY_FLAGS (VM_PKEY_BIT0 | VM_PKEY_BIT1 | VM_PKEY_BIT2 | \ diff --git a/arch/powerpc/mm/book3s64/pkeys.c b/arch/powerpc/mm/book3s64/pkeys.c index 65926c00c8a6e..e9fd0db7fb3c6 100644 --- a/arch/powerpc/mm/book3s64/pkeys.c +++ b/arch/powerpc/mm/book3s64/pkeys.c @@ -14,11 +14,14 @@ DEFINE_STATIC_KEY_FALSE(pkey_disabled); int num_pkey; /* Max number of pkeys supported */ -u32 initial_allocation_mask; /* Bits set for the initially allocated keys */ /* * Keys marked in the reservation list cannot be allocated by userspace */ -u32 reserved_allocation_mask; +u32 reserved_allocation_mask __ro_after_init; + +/* Bits set for the initially allocated keys */ +static u32 initial_allocation_mask __ro_after_init; + static bool pkey_execute_disable_supported; /* * Even if we allocate keys with sys_pkey_alloc(), we need to make sure -- GitLab From 3e4352aeb8b17eb1040ba288f586620e8294389d Mon Sep 17 00:00:00 2001 From: "Aneesh Kumar K.V" Date: Thu, 9 Jul 2020 08:59:35 +0530 Subject: [PATCH 0717/1476] powerpc/book3s64/pkeys: Mark all the pkeys above max pkey as reserved The hypervisor can return less than max allowed pkey (for ex: 31) instead of 32. We should mark all the pkeys above max allowed as reserved so that we avoid the allocation of the wrong pkey(for ex: key 31 in the above case) by userspace. Signed-off-by: Aneesh Kumar K.V Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20200709032946.881753-13-aneesh.kumar@linux.ibm.com --- arch/powerpc/mm/book3s64/pkeys.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/arch/powerpc/mm/book3s64/pkeys.c b/arch/powerpc/mm/book3s64/pkeys.c index e9fd0db7fb3c6..d84bf55369a3a 100644 --- a/arch/powerpc/mm/book3s64/pkeys.c +++ b/arch/powerpc/mm/book3s64/pkeys.c @@ -189,9 +189,10 @@ static int pkey_initialize(void) /* * Prevent the usage of OS reserved keys. Update UAMOR - * for those keys. + * for those keys. Also mark the rest of the bits in the + * 32 bit mask as reserved. */ - for (i = num_pkey; i < pkeys_total; i++) { + for (i = num_pkey; i < 32 ; i++) { reserved_allocation_mask |= (0x1 << i); default_uamor &= ~(0x3ul << pkeyshift(i)); } -- GitLab From d3cd91fb8d2e202cf8ebb6f271898aaf37ecda8f Mon Sep 17 00:00:00 2001 From: "Aneesh Kumar K.V" Date: Thu, 9 Jul 2020 08:59:36 +0530 Subject: [PATCH 0718/1476] powerpc/book3s64/pkeys: Add MMU_FTR_PKEY Parse storage keys related device tree entry in early_init_devtree and enable MMU feature MMU_FTR_PKEY if pkeys are supported. MMU feature is used instead of CPU feature because this enables us to group MMU_FTR_KUAP and MMU_FTR_PKEY in asm feature fixup code. Signed-off-by: Aneesh Kumar K.V Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20200709032946.881753-14-aneesh.kumar@linux.ibm.com --- arch/powerpc/include/asm/book3s/64/mmu.h | 6 +++ arch/powerpc/include/asm/mmu.h | 10 +++++ arch/powerpc/kernel/prom.c | 5 +++ arch/powerpc/mm/book3s64/pkeys.c | 52 ++++++++++++++---------- 4 files changed, 51 insertions(+), 22 deletions(-) diff --git a/arch/powerpc/include/asm/book3s/64/mmu.h b/arch/powerpc/include/asm/book3s/64/mmu.h index 15aae924f41c1..55442d45c5979 100644 --- a/arch/powerpc/include/asm/book3s/64/mmu.h +++ b/arch/powerpc/include/asm/book3s/64/mmu.h @@ -214,6 +214,12 @@ extern int mmu_io_psize; void mmu_early_init_devtree(void); void hash__early_init_devtree(void); void radix__early_init_devtree(void); +#ifdef CONFIG_PPC_MEM_KEYS +void pkey_early_init_devtree(void); +#else +static inline void pkey_early_init_devtree(void) {} +#endif + extern void hash__early_init_mmu(void); extern void radix__early_init_mmu(void); static inline void __init early_init_mmu(void) diff --git a/arch/powerpc/include/asm/mmu.h b/arch/powerpc/include/asm/mmu.h index 884d519959346..17cbaf1326315 100644 --- a/arch/powerpc/include/asm/mmu.h +++ b/arch/powerpc/include/asm/mmu.h @@ -28,6 +28,11 @@ * Individual features below. */ +/* + * Support for memory protection keys. + */ +#define MMU_FTR_PKEY ASM_CONST(0x00000800) + /* Guest Translation Shootdown Enable */ #define MMU_FTR_GTSE ASM_CONST(0x00001000) @@ -181,6 +186,9 @@ enum { MMU_FTR_RADIX_KUAP | #endif /* CONFIG_PPC_KUAP */ #endif /* CONFIG_PPC_RADIX_MMU */ +#ifdef CONFIG_PPC_MEM_KEYS + MMU_FTR_PKEY | +#endif 0, }; @@ -360,6 +368,8 @@ extern void setup_initial_memory_limit(phys_addr_t first_memblock_base, phys_addr_t first_memblock_size); static inline void mmu_early_init_devtree(void) { } +static inline void pkey_early_init_devtree(void) {} + extern void *abatron_pteptrs[2]; #endif /* __ASSEMBLY__ */ #endif diff --git a/arch/powerpc/kernel/prom.c b/arch/powerpc/kernel/prom.c index 033d43819ed89..dfe0103ad1317 100644 --- a/arch/powerpc/kernel/prom.c +++ b/arch/powerpc/kernel/prom.c @@ -815,6 +815,11 @@ void __init early_init_devtree(void *params) /* Now try to figure out if we are running on LPAR and so on */ pseries_probe_fw_features(); + /* + * Initialize pkey features and default AMR/IAMR values + */ + pkey_early_init_devtree(); + #ifdef CONFIG_PPC_PS3 /* Identify PS3 firmware */ if (of_flat_dt_is_compatible(of_get_flat_dt_root(), "sony,ps3")) diff --git a/arch/powerpc/mm/book3s64/pkeys.c b/arch/powerpc/mm/book3s64/pkeys.c index d84bf55369a3a..028b17c2c5113 100644 --- a/arch/powerpc/mm/book3s64/pkeys.c +++ b/arch/powerpc/mm/book3s64/pkeys.c @@ -10,7 +10,8 @@ #include #include #include -#include +#include + DEFINE_STATIC_KEY_FALSE(pkey_disabled); int num_pkey; /* Max number of pkeys supported */ @@ -46,31 +47,38 @@ static int execute_only_key = 2; #define PKEY_REG_BITS (sizeof(u64) * 8) #define pkeyshift(pkey) (PKEY_REG_BITS - ((pkey+1) * AMR_BITS_PER_PKEY)) +static int __init dt_scan_storage_keys(unsigned long node, + const char *uname, int depth, + void *data) +{ + const char *type = of_get_flat_dt_prop(node, "device_type", NULL); + const __be32 *prop; + int *pkeys_total = (int *) data; + + /* We are scanning "cpu" nodes only */ + if (type == NULL || strcmp(type, "cpu") != 0) + return 0; + + prop = of_get_flat_dt_prop(node, "ibm,processor-storage-keys", NULL); + if (!prop) + return 0; + *pkeys_total = be32_to_cpu(prop[0]); + return 1; +} + static int scan_pkey_feature(void) { - u32 vals[2]; + int ret; int pkeys_total = 0; - struct device_node *cpu; /* * Pkey is not supported with Radix translation. */ - if (radix_enabled()) - return 0; - - cpu = of_find_node_by_type(NULL, "cpu"); - if (!cpu) + if (early_radix_enabled()) return 0; - if (of_property_read_u32_array(cpu, - "ibm,processor-storage-keys", vals, 2) == 0) { - /* - * Since any pkey can be used for data or execute, we will - * just treat all keys as equal and track them as one entity. - */ - pkeys_total = vals[0]; - } else { - + ret = of_scan_flat_dt(dt_scan_storage_keys, &pkeys_total); + if (ret == 0) { /* * Let's assume 32 pkeys on P8/P9 bare metal, if its not defined by device * tree. We make this exception since some version of skiboot forgot to @@ -94,7 +102,7 @@ static int scan_pkey_feature(void) return pkeys_total; } -static int pkey_initialize(void) +void __init pkey_early_init_devtree(void) { int pkeys_total, i; @@ -119,9 +127,11 @@ static int pkey_initialize(void) if (!pkeys_total) { /* No support for pkey. Mark it disabled */ static_branch_enable(&pkey_disabled); - return 0; + return; } + cur_cpu_spec->mmu_features |= MMU_FTR_PKEY; + /* * The device tree cannot be relied to indicate support for * execute_disable support. Instead we use a PVR check. @@ -201,11 +211,9 @@ static int pkey_initialize(void) */ initial_allocation_mask |= reserved_allocation_mask; - return 0; + return; } -arch_initcall(pkey_initialize); - void pkey_mm_init(struct mm_struct *mm) { if (static_branch_likely(&pkey_disabled)) -- GitLab From e10cc8715d180509a367d3ab25d40e4a1612cb2f Mon Sep 17 00:00:00 2001 From: "Aneesh Kumar K.V" Date: Thu, 9 Jul 2020 08:59:37 +0530 Subject: [PATCH 0719/1476] powerpc/book3s64/kuep: Add MMU_FTR_KUEP This will be used to enable/disable Kernel Userspace Execution Prevention (KUEP). Signed-off-by: Aneesh Kumar K.V Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20200709032946.881753-15-aneesh.kumar@linux.ibm.com --- arch/powerpc/include/asm/mmu.h | 9 +++++++++ arch/powerpc/mm/book3s64/radix_pgtable.c | 4 +++- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/arch/powerpc/include/asm/mmu.h b/arch/powerpc/include/asm/mmu.h index 17cbaf1326315..255a1837e9f7f 100644 --- a/arch/powerpc/include/asm/mmu.h +++ b/arch/powerpc/include/asm/mmu.h @@ -28,6 +28,11 @@ * Individual features below. */ +/* + * Support for KUEP feature. + */ +#define MMU_FTR_KUEP ASM_CONST(0x00000400) + /* * Support for memory protection keys. */ @@ -189,6 +194,10 @@ enum { #ifdef CONFIG_PPC_MEM_KEYS MMU_FTR_PKEY | #endif +#ifdef CONFIG_PPC_KUEP + MMU_FTR_KUEP | +#endif /* CONFIG_PPC_KUAP */ + 0, }; diff --git a/arch/powerpc/mm/book3s64/radix_pgtable.c b/arch/powerpc/mm/book3s64/radix_pgtable.c index c5bf2ef73c36c..3536675dd61ec 100644 --- a/arch/powerpc/mm/book3s64/radix_pgtable.c +++ b/arch/powerpc/mm/book3s64/radix_pgtable.c @@ -593,8 +593,10 @@ void setup_kuep(bool disabled) if (disabled || !early_radix_enabled()) return; - if (smp_processor_id() == boot_cpuid) + if (smp_processor_id() == boot_cpuid) { pr_info("Activating Kernel Userspace Execution Prevention\n"); + cur_cpu_spec->mmu_features |= MMU_FTR_KUEP; + } /* * Radix always uses key0 of the IAMR to determine if an access is -- GitLab From 2daf298de728dc37f32d0749fa4f59db36fa7d96 Mon Sep 17 00:00:00 2001 From: "Aneesh Kumar K.V" Date: Thu, 9 Jul 2020 08:59:38 +0530 Subject: [PATCH 0720/1476] powerpc/book3s64/pkeys: Use pkey_execute_disable_supported Use pkey_execute_disable_supported to check for execute key support instead of pkey_disabled. Signed-off-by: Aneesh Kumar K.V Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20200709032946.881753-16-aneesh.kumar@linux.ibm.com --- arch/powerpc/include/asm/pkeys.h | 10 +--------- arch/powerpc/mm/book3s64/pkeys.c | 6 +++--- 2 files changed, 4 insertions(+), 12 deletions(-) diff --git a/arch/powerpc/include/asm/pkeys.h b/arch/powerpc/include/asm/pkeys.h index dd32e30f6767b..f44a14d64d47c 100644 --- a/arch/powerpc/include/asm/pkeys.h +++ b/arch/powerpc/include/asm/pkeys.h @@ -126,15 +126,7 @@ static inline int mm_pkey_free(struct mm_struct *mm, int pkey) * Try to dedicate one of the protection keys to be used as an * execute-only protection key. */ -extern int __execute_only_pkey(struct mm_struct *mm); -static inline int execute_only_pkey(struct mm_struct *mm) -{ - if (static_branch_likely(&pkey_disabled)) - return -1; - - return __execute_only_pkey(mm); -} - +extern int execute_only_pkey(struct mm_struct *mm); extern int __arch_override_mprotect_pkey(struct vm_area_struct *vma, int prot, int pkey); static inline int arch_override_mprotect_pkey(struct vm_area_struct *vma, diff --git a/arch/powerpc/mm/book3s64/pkeys.c b/arch/powerpc/mm/book3s64/pkeys.c index 028b17c2c5113..9969bc82e2cef 100644 --- a/arch/powerpc/mm/book3s64/pkeys.c +++ b/arch/powerpc/mm/book3s64/pkeys.c @@ -23,7 +23,6 @@ u32 reserved_allocation_mask __ro_after_init; /* Bits set for the initially allocated keys */ static u32 initial_allocation_mask __ro_after_init; -static bool pkey_execute_disable_supported; /* * Even if we allocate keys with sys_pkey_alloc(), we need to make sure * other thread still find the access denied using the same keys. @@ -38,6 +37,7 @@ static u64 default_uamor = ~0x0UL; * We pick key 2 because 0 is special key and 1 is reserved as per ISA. */ static int execute_only_key = 2; +static bool pkey_execute_disable_supported; #define AMR_BITS_PER_PKEY 2 @@ -151,7 +151,7 @@ void __init pkey_early_init_devtree(void) num_pkey = pkeys_total; #endif - if (unlikely(num_pkey <= execute_only_key)) { + if (unlikely(num_pkey <= execute_only_key) || !pkey_execute_disable_supported) { /* * Insufficient number of keys to support * execute only key. Mark it unavailable. @@ -359,7 +359,7 @@ void thread_pkey_regs_init(struct thread_struct *thread) write_uamor(default_uamor); } -int __execute_only_pkey(struct mm_struct *mm) +int execute_only_pkey(struct mm_struct *mm) { return mm->context.execute_only_pkey; } -- GitLab From f7045a45115b17fe695ea7075f5213706f202edb Mon Sep 17 00:00:00 2001 From: "Aneesh Kumar K.V" Date: Thu, 9 Jul 2020 08:59:39 +0530 Subject: [PATCH 0721/1476] powerpc/book3s64/pkeys: Use MMU_FTR_PKEY instead of pkey_disabled static key Instead of pkey_disabled static key use mmu feature MMU_FTR_PKEY. Signed-off-by: Aneesh Kumar K.V Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20200709032946.881753-17-aneesh.kumar@linux.ibm.com --- arch/powerpc/include/asm/book3s/64/pkeys.h | 2 +- arch/powerpc/include/asm/pkeys.h | 14 ++++++-------- arch/powerpc/mm/book3s64/pkeys.c | 17 +++++++---------- 3 files changed, 14 insertions(+), 19 deletions(-) diff --git a/arch/powerpc/include/asm/book3s/64/pkeys.h b/arch/powerpc/include/asm/book3s/64/pkeys.h index 8174662a91731..5b178139f3c08 100644 --- a/arch/powerpc/include/asm/book3s/64/pkeys.h +++ b/arch/powerpc/include/asm/book3s/64/pkeys.h @@ -7,7 +7,7 @@ static inline u64 vmflag_to_pte_pkey_bits(u64 vm_flags) { - if (static_branch_likely(&pkey_disabled)) + if (!mmu_has_feature(MMU_FTR_PKEY)) return 0x0UL; if (radix_enabled()) diff --git a/arch/powerpc/include/asm/pkeys.h b/arch/powerpc/include/asm/pkeys.h index f44a14d64d47c..a7951049e1299 100644 --- a/arch/powerpc/include/asm/pkeys.h +++ b/arch/powerpc/include/asm/pkeys.h @@ -11,7 +11,6 @@ #include #include -DECLARE_STATIC_KEY_FALSE(pkey_disabled); extern int num_pkey; extern u32 reserved_allocation_mask; /* bits set for reserved keys */ @@ -38,7 +37,7 @@ static inline u64 pkey_to_vmflag_bits(u16 pkey) static inline int vma_pkey(struct vm_area_struct *vma) { - if (static_branch_likely(&pkey_disabled)) + if (!mmu_has_feature(MMU_FTR_PKEY)) return 0; return (vma->vm_flags & ARCH_VM_PKEY_FLAGS) >> VM_PKEY_SHIFT; } @@ -93,9 +92,8 @@ static inline int mm_pkey_alloc(struct mm_struct *mm) u32 all_pkeys_mask = (u32)(~(0x0)); int ret; - if (static_branch_likely(&pkey_disabled)) + if (!mmu_has_feature(MMU_FTR_PKEY)) return -1; - /* * Are we out of pkeys? We must handle this specially because ffz() * behavior is undefined if there are no zeros. @@ -111,7 +109,7 @@ static inline int mm_pkey_alloc(struct mm_struct *mm) static inline int mm_pkey_free(struct mm_struct *mm, int pkey) { - if (static_branch_likely(&pkey_disabled)) + if (!mmu_has_feature(MMU_FTR_PKEY)) return -1; if (!mm_pkey_is_allocated(mm, pkey)) @@ -132,7 +130,7 @@ extern int __arch_override_mprotect_pkey(struct vm_area_struct *vma, static inline int arch_override_mprotect_pkey(struct vm_area_struct *vma, int prot, int pkey) { - if (static_branch_likely(&pkey_disabled)) + if (!mmu_has_feature(MMU_FTR_PKEY)) return 0; /* @@ -150,7 +148,7 @@ extern int __arch_set_user_pkey_access(struct task_struct *tsk, int pkey, static inline int arch_set_user_pkey_access(struct task_struct *tsk, int pkey, unsigned long init_val) { - if (static_branch_likely(&pkey_disabled)) + if (!mmu_has_feature(MMU_FTR_PKEY)) return -EINVAL; /* @@ -167,7 +165,7 @@ static inline int arch_set_user_pkey_access(struct task_struct *tsk, int pkey, static inline bool arch_pkeys_enabled(void) { - return !static_branch_likely(&pkey_disabled); + return mmu_has_feature(MMU_FTR_PKEY); } extern void pkey_mm_init(struct mm_struct *mm); diff --git a/arch/powerpc/mm/book3s64/pkeys.c b/arch/powerpc/mm/book3s64/pkeys.c index 9969bc82e2cef..2c1bddccd67b1 100644 --- a/arch/powerpc/mm/book3s64/pkeys.c +++ b/arch/powerpc/mm/book3s64/pkeys.c @@ -12,8 +12,6 @@ #include #include - -DEFINE_STATIC_KEY_FALSE(pkey_disabled); int num_pkey; /* Max number of pkeys supported */ /* * Keys marked in the reservation list cannot be allocated by userspace @@ -126,7 +124,6 @@ void __init pkey_early_init_devtree(void) pkeys_total = scan_pkey_feature(); if (!pkeys_total) { /* No support for pkey. Mark it disabled */ - static_branch_enable(&pkey_disabled); return; } @@ -216,7 +213,7 @@ void __init pkey_early_init_devtree(void) void pkey_mm_init(struct mm_struct *mm) { - if (static_branch_likely(&pkey_disabled)) + if (!mmu_has_feature(MMU_FTR_PKEY)) return; mm_pkey_allocation_map(mm) = initial_allocation_mask; mm->context.execute_only_pkey = execute_only_key; @@ -320,7 +317,7 @@ int __arch_set_user_pkey_access(struct task_struct *tsk, int pkey, void thread_pkey_regs_save(struct thread_struct *thread) { - if (static_branch_likely(&pkey_disabled)) + if (!mmu_has_feature(MMU_FTR_PKEY)) return; /* @@ -334,7 +331,7 @@ void thread_pkey_regs_save(struct thread_struct *thread) void thread_pkey_regs_restore(struct thread_struct *new_thread, struct thread_struct *old_thread) { - if (static_branch_likely(&pkey_disabled)) + if (!mmu_has_feature(MMU_FTR_PKEY)) return; if (old_thread->amr != new_thread->amr) @@ -347,7 +344,7 @@ void thread_pkey_regs_restore(struct thread_struct *new_thread, void thread_pkey_regs_init(struct thread_struct *thread) { - if (static_branch_likely(&pkey_disabled)) + if (!mmu_has_feature(MMU_FTR_PKEY)) return; thread->amr = default_amr; @@ -418,7 +415,7 @@ static bool pkey_access_permitted(int pkey, bool write, bool execute) bool arch_pte_access_permitted(u64 pte, bool write, bool execute) { - if (static_branch_likely(&pkey_disabled)) + if (!mmu_has_feature(MMU_FTR_PKEY)) return true; return pkey_access_permitted(pte_to_pkey_bits(pte), write, execute); @@ -435,7 +432,7 @@ bool arch_pte_access_permitted(u64 pte, bool write, bool execute) bool arch_vma_access_permitted(struct vm_area_struct *vma, bool write, bool execute, bool foreign) { - if (static_branch_likely(&pkey_disabled)) + if (!mmu_has_feature(MMU_FTR_PKEY)) return true; /* * Do not enforce our key-permissions on a foreign vma. @@ -448,7 +445,7 @@ bool arch_vma_access_permitted(struct vm_area_struct *vma, bool write, void arch_dup_pkeys(struct mm_struct *oldmm, struct mm_struct *mm) { - if (static_branch_likely(&pkey_disabled)) + if (!mmu_has_feature(MMU_FTR_PKEY)) return; /* Duplicate the oldmm pkey state in mm: */ -- GitLab From 7cdd3745f2d75aecc2b61368e2563ae54bfac59a Mon Sep 17 00:00:00 2001 From: "Aneesh Kumar K.V" Date: Thu, 9 Jul 2020 08:59:40 +0530 Subject: [PATCH 0722/1476] powerpc/book3s64/keys: Print information during boot. Signed-off-by: Aneesh Kumar K.V Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20200709032946.881753-18-aneesh.kumar@linux.ibm.com --- arch/powerpc/mm/book3s64/pkeys.c | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/powerpc/mm/book3s64/pkeys.c b/arch/powerpc/mm/book3s64/pkeys.c index 2c1bddccd67b1..e776544a4d115 100644 --- a/arch/powerpc/mm/book3s64/pkeys.c +++ b/arch/powerpc/mm/book3s64/pkeys.c @@ -208,6 +208,7 @@ void __init pkey_early_init_devtree(void) */ initial_allocation_mask |= reserved_allocation_mask; + pr_info("Enabling pkeys with max key count %d\n", num_pkey); return; } -- GitLab From 000a42b35a54372597f0657f6b9875b38c641864 Mon Sep 17 00:00:00 2001 From: "Aneesh Kumar K.V" Date: Thu, 9 Jul 2020 08:59:41 +0530 Subject: [PATCH 0723/1476] powerpc/book3s64/keys/kuap: Reset AMR/IAMR values on kexec As we kexec across kernels that use AMR/IAMR for different purposes we need to ensure that new kernels get kexec'd with a reset value of AMR/IAMR. For ex: the new kernel can use key 0 for kernel mapping and the old AMR value prevents access to key 0. This patch also removes reset if IAMR and AMOR in kexec_sequence. Reset of AMOR is not needed and the IAMR reset is partial (it doesn't do the reset on secondary cpus) and is redundant with this patch. Signed-off-by: Aneesh Kumar K.V Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20200709032946.881753-19-aneesh.kumar@linux.ibm.com --- arch/powerpc/include/asm/book3s/64/kexec.h | 23 ++++++++++++++++++++++ arch/powerpc/include/asm/kexec.h | 12 +++++++++++ arch/powerpc/kernel/misc_64.S | 14 ------------- arch/powerpc/kexec/core_64.c | 2 ++ arch/powerpc/mm/book3s64/pgtable.c | 3 +++ 5 files changed, 40 insertions(+), 14 deletions(-) create mode 100644 arch/powerpc/include/asm/book3s/64/kexec.h diff --git a/arch/powerpc/include/asm/book3s/64/kexec.h b/arch/powerpc/include/asm/book3s/64/kexec.h new file mode 100644 index 0000000000000..6b5c3a248ba2f --- /dev/null +++ b/arch/powerpc/include/asm/book3s/64/kexec.h @@ -0,0 +1,23 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +#ifndef _ASM_POWERPC_BOOK3S_64_KEXEC_H_ +#define _ASM_POWERPC_BOOK3S_64_KEXEC_H_ + + +#define reset_sprs reset_sprs +static inline void reset_sprs(void) +{ + if (cpu_has_feature(CPU_FTR_ARCH_206)) { + mtspr(SPRN_AMR, 0); + mtspr(SPRN_UAMOR, 0); + } + + if (cpu_has_feature(CPU_FTR_ARCH_207S)) { + mtspr(SPRN_IAMR, 0); + } + + /* Do we need isync()? We are going via a kexec reset */ + isync(); +} + +#endif diff --git a/arch/powerpc/include/asm/kexec.h b/arch/powerpc/include/asm/kexec.h index c684768187538..89f7e3462292c 100644 --- a/arch/powerpc/include/asm/kexec.h +++ b/arch/powerpc/include/asm/kexec.h @@ -150,6 +150,18 @@ static inline void crash_send_ipi(void (*crash_ipi_callback)(struct pt_regs *)) } #endif /* CONFIG_KEXEC_CORE */ + +#ifdef CONFIG_PPC_BOOK3S_64 +#include +#endif + +#ifndef reset_sprs +#define reset_sprs reset_sprs +static inline void reset_sprs(void) +{ +} +#endif + #endif /* ! __ASSEMBLY__ */ #endif /* __KERNEL__ */ #endif /* _ASM_POWERPC_KEXEC_H */ diff --git a/arch/powerpc/kernel/misc_64.S b/arch/powerpc/kernel/misc_64.S index 1864605eca29c..7bb46ad982072 100644 --- a/arch/powerpc/kernel/misc_64.S +++ b/arch/powerpc/kernel/misc_64.S @@ -413,20 +413,6 @@ _GLOBAL(kexec_sequence) li r0,0 std r0,16(r1) -BEGIN_FTR_SECTION - /* - * This is the best time to turn AMR/IAMR off. - * key 0 is used in radix for supervisor<->user - * protection, but on hash key 0 is reserved - * ideally we want to enter with a clean state. - * NOTE, we rely on r0 being 0 from above. - */ - mtspr SPRN_IAMR,r0 -BEGIN_FTR_SECTION_NESTED(42) - mtspr SPRN_AMOR,r0 -END_FTR_SECTION_NESTED_IFSET(CPU_FTR_HVMODE, 42) -END_FTR_SECTION_IFSET(CPU_FTR_ARCH_300) - /* save regs for local vars on new stack. * yes, we won't go back, but ... */ diff --git a/arch/powerpc/kexec/core_64.c b/arch/powerpc/kexec/core_64.c index b4184092172a6..8a449b2d8715d 100644 --- a/arch/powerpc/kexec/core_64.c +++ b/arch/powerpc/kexec/core_64.c @@ -152,6 +152,8 @@ static void kexec_smp_down(void *arg) if (ppc_md.kexec_cpu_down) ppc_md.kexec_cpu_down(0, 1); + reset_sprs(); + kexec_smp_wait(); /* NOTREACHED */ } diff --git a/arch/powerpc/mm/book3s64/pgtable.c b/arch/powerpc/mm/book3s64/pgtable.c index 85de5b574dd70..e18ae50a275c8 100644 --- a/arch/powerpc/mm/book3s64/pgtable.c +++ b/arch/powerpc/mm/book3s64/pgtable.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #include @@ -165,6 +166,8 @@ void mmu_cleanup_all(void) radix__mmu_cleanup_all(); else if (mmu_hash_ops.hpte_clear_all) mmu_hash_ops.hpte_clear_all(); + + reset_sprs(); } #ifdef CONFIG_MEMORY_HOTPLUG -- GitLab From e0d8e991be641ba0034c67785bf86f6c097869d6 Mon Sep 17 00:00:00 2001 From: "Aneesh Kumar K.V" Date: Thu, 9 Jul 2020 08:59:42 +0530 Subject: [PATCH 0724/1476] powerpc/book3s64/kuap: Move UAMOR setup to key init function UAMOR values are not application-specific. The kernel initializes its value based on different reserved keys. Remove the thread-specific UAMOR value and don't switch the UAMOR on context switch. Move UAMOR initialization to key initialization code and remove thread_struct.uamor because it is not used anymore. Before commit: 4a4a5e5d2aad ("powerpc/pkeys: key allocation/deallocation must not change pkey registers") we used to update uamor based on key allocation and free. Signed-off-by: Aneesh Kumar K.V Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20200709032946.881753-20-aneesh.kumar@linux.ibm.com --- arch/powerpc/include/asm/book3s/64/pkeys.h | 2 ++ arch/powerpc/include/asm/processor.h | 1 - arch/powerpc/kernel/ptrace/ptrace-view.c | 27 +++++++++++++++----- arch/powerpc/kernel/smp.c | 1 + arch/powerpc/mm/book3s64/hash_utils.c | 4 +++ arch/powerpc/mm/book3s64/pkeys.c | 29 +++++++++------------- arch/powerpc/mm/book3s64/radix_pgtable.c | 4 +++ 7 files changed, 44 insertions(+), 24 deletions(-) diff --git a/arch/powerpc/include/asm/book3s/64/pkeys.h b/arch/powerpc/include/asm/book3s/64/pkeys.h index 5b178139f3c08..b7d9f4267bcd0 100644 --- a/arch/powerpc/include/asm/book3s/64/pkeys.h +++ b/arch/powerpc/include/asm/book3s/64/pkeys.h @@ -5,6 +5,8 @@ #include +extern u64 __ro_after_init default_uamor; + static inline u64 vmflag_to_pte_pkey_bits(u64 vm_flags) { if (!mmu_has_feature(MMU_FTR_PKEY)) diff --git a/arch/powerpc/include/asm/processor.h b/arch/powerpc/include/asm/processor.h index 52a67835057ab..6ac12168f1fe8 100644 --- a/arch/powerpc/include/asm/processor.h +++ b/arch/powerpc/include/asm/processor.h @@ -237,7 +237,6 @@ struct thread_struct { #ifdef CONFIG_PPC_MEM_KEYS unsigned long amr; unsigned long iamr; - unsigned long uamor; #endif #ifdef CONFIG_KVM_BOOK3S_32_HANDLER void* kvm_shadow_vcpu; /* KVM internal data */ diff --git a/arch/powerpc/kernel/ptrace/ptrace-view.c b/arch/powerpc/kernel/ptrace/ptrace-view.c index caeb5822a8f4a..ac7d480cb9c1f 100644 --- a/arch/powerpc/kernel/ptrace/ptrace-view.c +++ b/arch/powerpc/kernel/ptrace/ptrace-view.c @@ -488,14 +488,21 @@ static int pkey_active(struct task_struct *target, const struct user_regset *reg static int pkey_get(struct task_struct *target, const struct user_regset *regset, unsigned int pos, unsigned int count, void *kbuf, void __user *ubuf) { + int ret; + BUILD_BUG_ON(TSO(amr) + sizeof(unsigned long) != TSO(iamr)); - BUILD_BUG_ON(TSO(iamr) + sizeof(unsigned long) != TSO(uamor)); if (!arch_pkeys_enabled()) return -ENODEV; - return user_regset_copyout(&pos, &count, &kbuf, &ubuf, &target->thread.amr, - 0, ELF_NPKEY * sizeof(unsigned long)); + ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, &target->thread.amr, + 0, 2 * sizeof(unsigned long)); + if (ret) + return ret; + + ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, &default_uamor, + 2 * sizeof(unsigned long), 3 * sizeof(unsigned long)); + return ret; } static int pkey_set(struct task_struct *target, const struct user_regset *regset, @@ -517,9 +524,17 @@ static int pkey_set(struct task_struct *target, const struct user_regset *regset if (ret) return ret; - /* UAMOR determines which bits of the AMR can be set from userspace. */ - target->thread.amr = (new_amr & target->thread.uamor) | - (target->thread.amr & ~target->thread.uamor); + /* + * UAMOR determines which bits of the AMR can be set from userspace. + * UAMOR value 0b11 indicates that the AMR value can be modified + * from userspace. If the kernel is using a specific key, we avoid + * userspace modifying the AMR value for that key by masking them + * via UAMOR 0b00. + * + * Pick the AMR values for the keys that kernel is using. This + * will be indicated by the ~default_uamor bits. + */ + target->thread.amr = (new_amr & default_uamor) | (target->thread.amr & ~default_uamor); return 0; } diff --git a/arch/powerpc/kernel/smp.c b/arch/powerpc/kernel/smp.c index 73199470c2654..8261999c7d520 100644 --- a/arch/powerpc/kernel/smp.c +++ b/arch/powerpc/kernel/smp.c @@ -59,6 +59,7 @@ #include #include #include +#include #ifdef DEBUG #include diff --git a/arch/powerpc/mm/book3s64/hash_utils.c b/arch/powerpc/mm/book3s64/hash_utils.c index eec6f4e5e4816..9dfb0ceed5e32 100644 --- a/arch/powerpc/mm/book3s64/hash_utils.c +++ b/arch/powerpc/mm/book3s64/hash_utils.c @@ -1110,6 +1110,10 @@ void hash__early_init_mmu_secondary(void) if (cpu_has_feature(CPU_FTR_ARCH_206) && cpu_has_feature(CPU_FTR_HVMODE)) tlbiel_all(); + +#ifdef CONFIG_PPC_MEM_KEYS + mtspr(SPRN_UAMOR, default_uamor); +#endif } #endif /* CONFIG_SMP */ diff --git a/arch/powerpc/mm/book3s64/pkeys.c b/arch/powerpc/mm/book3s64/pkeys.c index e776544a4d115..d45864a77066b 100644 --- a/arch/powerpc/mm/book3s64/pkeys.c +++ b/arch/powerpc/mm/book3s64/pkeys.c @@ -27,9 +27,7 @@ static u32 initial_allocation_mask __ro_after_init; */ static u64 default_amr = ~0x0UL; static u64 default_iamr = 0x5555555555555555UL; - -/* Allow all keys to be modified by default */ -static u64 default_uamor = ~0x0UL; +u64 default_uamor __ro_after_init; /* * Key used to implement PROT_EXEC mmap. Denies READ/WRITE * We pick key 2 because 0 is special key and 1 is reserved as per ISA. @@ -122,10 +120,11 @@ void __init pkey_early_init_devtree(void) /* scan the device tree for pkey feature */ pkeys_total = scan_pkey_feature(); - if (!pkeys_total) { - /* No support for pkey. Mark it disabled */ - return; - } + if (!pkeys_total) + goto out; + + /* Allow all keys to be modified by default */ + default_uamor = ~0x0UL; cur_cpu_spec->mmu_features |= MMU_FTR_PKEY; @@ -209,6 +208,12 @@ void __init pkey_early_init_devtree(void) initial_allocation_mask |= reserved_allocation_mask; pr_info("Enabling pkeys with max key count %d\n", num_pkey); +out: + /* + * Setup uamor on boot cpu + */ + mtspr(SPRN_UAMOR, default_uamor); + return; } @@ -251,11 +256,6 @@ static inline u64 read_uamor(void) return mfspr(SPRN_UAMOR); } -static inline void write_uamor(u64 value) -{ - mtspr(SPRN_UAMOR, value); -} - static bool is_pkey_enabled(int pkey) { u64 uamor = read_uamor(); @@ -326,7 +326,6 @@ void thread_pkey_regs_save(struct thread_struct *thread) */ thread->amr = read_amr(); thread->iamr = read_iamr(); - thread->uamor = read_uamor(); } void thread_pkey_regs_restore(struct thread_struct *new_thread, @@ -339,8 +338,6 @@ void thread_pkey_regs_restore(struct thread_struct *new_thread, write_amr(new_thread->amr); if (old_thread->iamr != new_thread->iamr) write_iamr(new_thread->iamr); - if (old_thread->uamor != new_thread->uamor) - write_uamor(new_thread->uamor); } void thread_pkey_regs_init(struct thread_struct *thread) @@ -350,11 +347,9 @@ void thread_pkey_regs_init(struct thread_struct *thread) thread->amr = default_amr; thread->iamr = default_iamr; - thread->uamor = default_uamor; write_amr(default_amr); write_iamr(default_iamr); - write_uamor(default_uamor); } int execute_only_pkey(struct mm_struct *mm) diff --git a/arch/powerpc/mm/book3s64/radix_pgtable.c b/arch/powerpc/mm/book3s64/radix_pgtable.c index 3536675dd61ec..a8f46a6862196 100644 --- a/arch/powerpc/mm/book3s64/radix_pgtable.c +++ b/arch/powerpc/mm/book3s64/radix_pgtable.c @@ -620,6 +620,10 @@ void setup_kuap(bool disabled) /* Make sure userspace can't change the AMR */ mtspr(SPRN_UAMOR, 0); + + /* + * Set the default kernel AMR values on all cpus. + */ mtspr(SPRN_AMR, AMR_KUAP_BLOCKED); isync(); } -- GitLab From 14110af606965ce07abe4d121c100241c2e73b86 Mon Sep 17 00:00:00 2001 From: Tiezhu Yang Date: Sat, 18 Jul 2020 17:39:36 +0800 Subject: [PATCH 0725/1476] PCI: loongson: Use DECLARE_PCI_FIXUP_EARLY for bridge_class_quirk() According to the datasheet of Loongson LS7A bridge chip, the old version of Loongson LS7A PCIE port has a wrong value about PCI class which is 0x060000, the correct value should be 0x060400, this bug can be fixed by "dev->class = PCI_CLASS_BRIDGE_PCI << 8;" at the software level and it was fixed in hardware in the latest LS7A versions. In order to maintain downward compatibility, use DECLARE_PCI_FIXUP_EARLY instead of DECLARE_PCI_FIXUP_HEADER for bridge_class_quirk() to fix it as early as possible. Otherwise, in the function pci_setup_device(), the related code about "dev->class" such as "class = dev->class >> 8;" and "dev->transparent = ((dev->class & 0xff) == 1);" maybe get wrong value without EARLY fixup. Link: https://lore.kernel.org/r/1595065176-460-1-git-send-email-yangtiezhu@loongson.cn Fixes: 1f58cca5cf2b ("PCI: Add Loongson PCI Controller support") Signed-off-by: Tiezhu Yang Signed-off-by: Lorenzo Pieralisi --- drivers/pci/controller/pci-loongson.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/pci/controller/pci-loongson.c b/drivers/pci/controller/pci-loongson.c index 459009c8a4a02..58b862aaa6e94 100644 --- a/drivers/pci/controller/pci-loongson.c +++ b/drivers/pci/controller/pci-loongson.c @@ -37,11 +37,11 @@ static void bridge_class_quirk(struct pci_dev *dev) { dev->class = PCI_CLASS_BRIDGE_PCI << 8; } -DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_LOONGSON, +DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_LOONGSON, DEV_PCIE_PORT_0, bridge_class_quirk); -DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_LOONGSON, +DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_LOONGSON, DEV_PCIE_PORT_1, bridge_class_quirk); -DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_LOONGSON, +DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_LOONGSON, DEV_PCIE_PORT_2, bridge_class_quirk); static void system_bus_quirk(struct pci_dev *pdev) -- GitLab From 4f7f590b152444c1403ece9eeeddd1e8b22ba04e Mon Sep 17 00:00:00 2001 From: yangerkun Date: Fri, 19 Jun 2020 17:10:39 -0400 Subject: [PATCH 0726/1476] dm dust: report some message results directly back to user Some messages (queryblock, countbadblocks, removebadblock) are best reported directly to user directly. Do so with DMEMIT. [Bryan: maintain __func__ output in DMEMIT messages] Signed-off-by: yangerkun Signed-off-by: Bryan Gurney Signed-off-by: Mike Snitzer --- .../admin-guide/device-mapper/dm-dust.rst | 15 ++++----- drivers/md/dm-dust.c | 31 +++++++++++-------- 2 files changed, 26 insertions(+), 20 deletions(-) diff --git a/Documentation/admin-guide/device-mapper/dm-dust.rst b/Documentation/admin-guide/device-mapper/dm-dust.rst index b6e7e7ead831a..cf8079e368de9 100644 --- a/Documentation/admin-guide/device-mapper/dm-dust.rst +++ b/Documentation/admin-guide/device-mapper/dm-dust.rst @@ -69,10 +69,11 @@ Create the dm-dust device: $ sudo dmsetup create dust1 --table '0 33552384 dust /dev/vdb1 0 4096' Check the status of the read behavior ("bypass" indicates that all I/O -will be passed through to the underlying device):: +will be passed through to the underlying device; "verbose" indicates that +bad block additions, removals, and remaps will be verbosely logged):: $ sudo dmsetup status dust1 - 0 33552384 dust 252:17 bypass + 0 33552384 dust 252:17 bypass verbose $ sudo dd if=/dev/mapper/dust1 of=/dev/null bs=512 count=128 iflag=direct 128+0 records in @@ -164,7 +165,7 @@ following message command:: A message will print with the number of bad blocks currently configured on the device:: - kernel: device-mapper: dust: countbadblocks: 895 badblock(s) found + countbadblocks: 895 badblock(s) found Querying for specific bad blocks -------------------------------- @@ -176,11 +177,11 @@ following message command:: The following message will print if the block is in the list:: - device-mapper: dust: queryblock: block 72 found in badblocklist + dust_query_block: block 72 found in badblocklist The following message will print if the block is not in the list:: - device-mapper: dust: queryblock: block 72 not found in badblocklist + dust_query_block: block 72 not found in badblocklist The "queryblock" message command will work in both the "enabled" and "disabled" modes, allowing the verification of whether a block @@ -198,12 +199,12 @@ following message command:: After clearing the bad block list, the following message will appear:: - kernel: device-mapper: dust: clearbadblocks: badblocks cleared + dust_clear_badblocks: badblocks cleared If there were no bad blocks to clear, the following message will appear:: - kernel: device-mapper: dust: clearbadblocks: no badblocks found + dust_clear_badblocks: no badblocks found Message commands list --------------------- diff --git a/drivers/md/dm-dust.c b/drivers/md/dm-dust.c index ff03b90072c54..f1f2dd6a4e84b 100644 --- a/drivers/md/dm-dust.c +++ b/drivers/md/dm-dust.c @@ -138,20 +138,22 @@ static int dust_add_block(struct dust_device *dd, unsigned long long block, return 0; } -static int dust_query_block(struct dust_device *dd, unsigned long long block) +static int dust_query_block(struct dust_device *dd, unsigned long long block, char *result, + unsigned int maxlen, unsigned int *sz_ptr) { struct badblock *bblock; unsigned long flags; + unsigned int sz = *sz_ptr; spin_lock_irqsave(&dd->dust_lock, flags); bblock = dust_rb_search(&dd->badblocklist, block); if (bblock != NULL) - DMINFO("%s: block %llu found in badblocklist", __func__, block); + DMEMIT("%s: block %llu found in badblocklist", __func__, block); else - DMINFO("%s: block %llu not found in badblocklist", __func__, block); + DMEMIT("%s: block %llu not found in badblocklist", __func__, block); spin_unlock_irqrestore(&dd->dust_lock, flags); - return 0; + return 1; } static int __dust_map_read(struct dust_device *dd, sector_t thisblock) @@ -259,11 +261,13 @@ static bool __dust_clear_badblocks(struct rb_root *tree, return true; } -static int dust_clear_badblocks(struct dust_device *dd) +static int dust_clear_badblocks(struct dust_device *dd, char *result, unsigned int maxlen, + unsigned int *sz_ptr) { unsigned long flags; struct rb_root badblocklist; unsigned long long badblock_count; + unsigned int sz = *sz_ptr; spin_lock_irqsave(&dd->dust_lock, flags); badblocklist = dd->badblocklist; @@ -273,11 +277,11 @@ static int dust_clear_badblocks(struct dust_device *dd) spin_unlock_irqrestore(&dd->dust_lock, flags); if (!__dust_clear_badblocks(&badblocklist, badblock_count)) - DMINFO("%s: no badblocks found", __func__); + DMEMIT("%s: no badblocks found", __func__); else - DMINFO("%s: badblocks cleared", __func__); + DMEMIT("%s: badblocks cleared", __func__); - return 0; + return 1; } /* @@ -383,7 +387,7 @@ static void dust_dtr(struct dm_target *ti) } static int dust_message(struct dm_target *ti, unsigned int argc, char **argv, - char *result_buf, unsigned int maxlen) + char *result, unsigned int maxlen) { struct dust_device *dd = ti->private; sector_t size = i_size_read(dd->dev->bdev->bd_inode) >> SECTOR_SHIFT; @@ -393,6 +397,7 @@ static int dust_message(struct dm_target *ti, unsigned int argc, char **argv, unsigned char wr_fail_cnt; unsigned int tmp_ui; unsigned long flags; + unsigned int sz = 0; char dummy; if (argc == 1) { @@ -410,12 +415,12 @@ static int dust_message(struct dm_target *ti, unsigned int argc, char **argv, r = 0; } else if (!strcasecmp(argv[0], "countbadblocks")) { spin_lock_irqsave(&dd->dust_lock, flags); - DMINFO("countbadblocks: %llu badblock(s) found", + DMEMIT("countbadblocks: %llu badblock(s) found", dd->badblock_count); spin_unlock_irqrestore(&dd->dust_lock, flags); - r = 0; + r = 1; } else if (!strcasecmp(argv[0], "clearbadblocks")) { - r = dust_clear_badblocks(dd); + r = dust_clear_badblocks(dd, result, maxlen, &sz); } else if (!strcasecmp(argv[0], "quiet")) { if (!dd->quiet_mode) dd->quiet_mode = true; @@ -441,7 +446,7 @@ static int dust_message(struct dm_target *ti, unsigned int argc, char **argv, else if (!strcasecmp(argv[0], "removebadblock")) r = dust_remove_block(dd, block); else if (!strcasecmp(argv[0], "queryblock")) - r = dust_query_block(dd, block); + r = dust_query_block(dd, block, result, maxlen, &sz); else invalid_msg = true; -- GitLab From 0c248ea27fc88cf8a3035ba0ed75b210be9abf80 Mon Sep 17 00:00:00 2001 From: yangerkun Date: Fri, 19 Jun 2020 17:12:42 -0400 Subject: [PATCH 0727/1476] dm dust: add interface to list all badblocks This interface may help anyone who want to know all badblocks without querying for each block. [Bryan: DMEMIT message if no blocks are in the bad block list.] Signed-off-by: yangerkun Signed-off-by: Bryan Gurney Signed-off-by: Mike Snitzer --- .../admin-guide/device-mapper/dm-dust.rst | 17 ++++++++++++ drivers/md/dm-dust.c | 27 +++++++++++++++++++ 2 files changed, 44 insertions(+) diff --git a/Documentation/admin-guide/device-mapper/dm-dust.rst b/Documentation/admin-guide/device-mapper/dm-dust.rst index cf8079e368de9..e35ec8cd2f88a 100644 --- a/Documentation/admin-guide/device-mapper/dm-dust.rst +++ b/Documentation/admin-guide/device-mapper/dm-dust.rst @@ -206,6 +206,22 @@ appear:: dust_clear_badblocks: no badblocks found +Listing the bad block list +-------------------------- + +To list all bad blocks in the bad block list (using an example device +with blocks 1 and 2 in the bad block list), run the following message +command:: + + $ sudo dmsetup message dust1 0 listbadblocks + 1 + 2 + +If there are no bad blocks in the bad block list, the command will +execute with no output:: + + $ sudo dmsetup message dust1 0 listbadblocks + Message commands list --------------------- @@ -224,6 +240,7 @@ Single argument message commands:: countbadblocks clearbadblocks + listbadblocks disable enable quiet diff --git a/drivers/md/dm-dust.c b/drivers/md/dm-dust.c index f1f2dd6a4e84b..072ea913cebc7 100644 --- a/drivers/md/dm-dust.c +++ b/drivers/md/dm-dust.c @@ -284,6 +284,31 @@ static int dust_clear_badblocks(struct dust_device *dd, char *result, unsigned i return 1; } +static int dust_list_badblocks(struct dust_device *dd, char *result, unsigned int maxlen, + unsigned int *sz_ptr) +{ + unsigned long flags; + struct rb_root badblocklist; + struct rb_node *node; + struct badblock *bblk; + unsigned int sz = *sz_ptr; + unsigned long long num = 0; + + spin_lock_irqsave(&dd->dust_lock, flags); + badblocklist = dd->badblocklist; + for (node = rb_first(&badblocklist); node; node = rb_next(node)) { + bblk = rb_entry(node, struct badblock, node); + DMEMIT("%llu\n", bblk->bb); + num++; + } + + spin_unlock_irqrestore(&dd->dust_lock, flags); + if (!num) + DMEMIT("No blocks in badblocklist"); + + return 1; +} + /* * Target parameters: * @@ -427,6 +452,8 @@ static int dust_message(struct dm_target *ti, unsigned int argc, char **argv, else dd->quiet_mode = false; r = 0; + } else if (!strcasecmp(argv[0], "listbadblocks")) { + r = dust_list_badblocks(dd, result, maxlen, &sz); } else { invalid_msg = true; } -- GitLab From e766668c6cd49d741cfb49eaeb38998ba34d27bc Mon Sep 17 00:00:00 2001 From: Ming Lei Date: Fri, 19 Jun 2020 16:42:14 +0800 Subject: [PATCH 0728/1476] dm rq: don't call blk_mq_queue_stopped() in dm_stop_queue() dm_stop_queue() only uses blk_mq_quiesce_queue() so it doesn't formally stop the blk-mq queue; therefore there is no point making the blk_mq_queue_stopped() check -- it will never be stopped. In addition, even though dm_stop_queue() actually tries to quiesce hw queues via blk_mq_quiesce_queue(), checking with blk_queue_quiesced() to avoid unnecessary queue quiesce isn't reliable because: the QUEUE_FLAG_QUIESCED flag is set before synchronize_rcu() and dm_stop_queue() may be called when synchronize_rcu() from another blk_mq_quiesce_queue() is in-progress. Fixes: 7b17c2f7292ba ("dm: Fix a race condition related to stopping and starting queues") Signed-off-by: Ming Lei Signed-off-by: Mike Snitzer --- drivers/md/dm-rq.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/md/dm-rq.c b/drivers/md/dm-rq.c index 85e0daabad49c..20745e2e34b94 100644 --- a/drivers/md/dm-rq.c +++ b/drivers/md/dm-rq.c @@ -70,9 +70,6 @@ void dm_start_queue(struct request_queue *q) void dm_stop_queue(struct request_queue *q) { - if (blk_mq_queue_stopped(q)) - return; - blk_mq_quiesce_queue(q); } -- GitLab From 70704c33db841d847b16d8ae7e29ae3b8a479e75 Mon Sep 17 00:00:00 2001 From: Mikulas Patocka Date: Fri, 3 Jul 2020 10:26:46 -0400 Subject: [PATCH 0729/1476] dm bufio: do buffer cleanup from a workqueue Until now, DM bufio's waiting for IO from reclaim context in its shrinker has caused kswapd to block; which results in systemic IO stalls and even deadlock, e.g.: https://www.redhat.com/archives/dm-devel/2020-March/msg00025.html Here is Dave Chinner's problem description that motivated this fix, from: https://lore.kernel.org/linux-fsdevel/20190809215733.GZ7777@dread.disaster.area/ "Waiting for IO in kswapd reclaim context is considered harmful - kswapd context shrinker reclaim should be as non-blocking as possible, and any back-off to wait for IO to complete should be done by the high level reclaim core once it's completed an entire reclaim scan cycle of everything.... What follows from that, and is pertinent in this situation, is that if you don't block kswapd, then other reclaim contexts are not going to get stuck waiting for it regardless of the reclaim context they use." Continued elsewhere: "The only way to fix this problem once and for all is to stop using the shrinker as a mechanism to issue and wait on IO. If you need background writeback of dirty buffers, do it from a WQ_MEM_RECLAIM workqueue that isn't directly in the memory reclaim path and so can issue writeback and block safely from a GFP_KERNEL context. Kick the workqueue from the shrinker context, but get rid of the IO submission and waiting from the shrinker and all the GFP_NOFS memory reclaim recursion problems go away." As such, this commit moves buffer cleanup to a workqueue. Suggested-by: Dave Chinner Reported-by: Tahsin Erdogan Signed-off-by: Mikulas Patocka Tested-by: Gabriel Krisman Bertazi Signed-off-by: Mike Snitzer --- drivers/md/dm-bufio.c | 60 +++++++++++++++++++++++++++++-------------- 1 file changed, 41 insertions(+), 19 deletions(-) diff --git a/drivers/md/dm-bufio.c b/drivers/md/dm-bufio.c index 6d1565021d74c..9c1a86bde658e 100644 --- a/drivers/md/dm-bufio.c +++ b/drivers/md/dm-bufio.c @@ -108,7 +108,10 @@ struct dm_bufio_client { int async_write_error; struct list_head client_list; + struct shrinker shrinker; + struct work_struct shrink_work; + atomic_long_t need_shrink; }; /* @@ -1634,8 +1637,7 @@ static unsigned long get_retain_buffers(struct dm_bufio_client *c) return retain_bytes; } -static unsigned long __scan(struct dm_bufio_client *c, unsigned long nr_to_scan, - gfp_t gfp_mask) +static void __scan(struct dm_bufio_client *c) { int l; struct dm_buffer *b, *tmp; @@ -1646,42 +1648,58 @@ static unsigned long __scan(struct dm_bufio_client *c, unsigned long nr_to_scan, for (l = 0; l < LIST_SIZE; l++) { list_for_each_entry_safe_reverse(b, tmp, &c->lru[l], lru_list) { - if (__try_evict_buffer(b, gfp_mask)) + if (count - freed <= retain_target) + atomic_long_set(&c->need_shrink, 0); + if (!atomic_long_read(&c->need_shrink)) + return; + if (__try_evict_buffer(b, GFP_KERNEL)) { + atomic_long_dec(&c->need_shrink); freed++; - if (!--nr_to_scan || ((count - freed) <= retain_target)) - return freed; + } cond_resched(); } } - return freed; } -static unsigned long -dm_bufio_shrink_scan(struct shrinker *shrink, struct shrink_control *sc) +static void shrink_work(struct work_struct *w) +{ + struct dm_bufio_client *c = container_of(w, struct dm_bufio_client, shrink_work); + + dm_bufio_lock(c); + __scan(c); + dm_bufio_unlock(c); +} + +static unsigned long dm_bufio_shrink_scan(struct shrinker *shrink, struct shrink_control *sc) { struct dm_bufio_client *c; - unsigned long freed; c = container_of(shrink, struct dm_bufio_client, shrinker); - if (sc->gfp_mask & __GFP_FS) - dm_bufio_lock(c); - else if (!dm_bufio_trylock(c)) - return SHRINK_STOP; + atomic_long_add(sc->nr_to_scan, &c->need_shrink); + queue_work(dm_bufio_wq, &c->shrink_work); - freed = __scan(c, sc->nr_to_scan, sc->gfp_mask); - dm_bufio_unlock(c); - return freed; + return sc->nr_to_scan; } -static unsigned long -dm_bufio_shrink_count(struct shrinker *shrink, struct shrink_control *sc) +static unsigned long dm_bufio_shrink_count(struct shrinker *shrink, struct shrink_control *sc) { struct dm_bufio_client *c = container_of(shrink, struct dm_bufio_client, shrinker); unsigned long count = READ_ONCE(c->n_buffers[LIST_CLEAN]) + READ_ONCE(c->n_buffers[LIST_DIRTY]); unsigned long retain_target = get_retain_buffers(c); + unsigned long queued_for_cleanup = atomic_long_read(&c->need_shrink); + + if (unlikely(count < retain_target)) + count = 0; + else + count -= retain_target; - return (count < retain_target) ? 0 : (count - retain_target); + if (unlikely(count < queued_for_cleanup)) + count = 0; + else + count -= queued_for_cleanup; + + return count; } /* @@ -1772,6 +1790,9 @@ struct dm_bufio_client *dm_bufio_client_create(struct block_device *bdev, unsign __free_buffer_wake(b); } + INIT_WORK(&c->shrink_work, shrink_work); + atomic_long_set(&c->need_shrink, 0); + c->shrinker.count_objects = dm_bufio_shrink_count; c->shrinker.scan_objects = dm_bufio_shrink_scan; c->shrinker.seeks = 1; @@ -1817,6 +1838,7 @@ void dm_bufio_client_destroy(struct dm_bufio_client *c) drop_buffers(c); unregister_shrinker(&c->shrinker); + flush_work(&c->shrink_work); mutex_lock(&dm_bufio_clients_lock); -- GitLab From 39d42fa96ba1b7d2544db3f8ed5da8fb0d5cb877 Mon Sep 17 00:00:00 2001 From: Ignat Korchagin Date: Mon, 6 Jul 2020 18:37:31 +0100 Subject: [PATCH 0730/1476] dm crypt: add flags to optionally bypass kcryptd workqueues This is a follow up to [1] that detailed latency problems associated with dm-crypt's use of workqueues when processing IO. Current dm-crypt implementation creates a significant IO performance overhead (at least on small IO block sizes) for both latency and throughput. We suspect offloading IO request processing into workqueues and async threads is more harmful these days with the modern fast storage. I also did some digging into the dm-crypt git history and much of this async processing is not needed anymore, because the reasons it was added are mostly gone from the kernel. More details can be found in [2] (see "Git archeology" section). This change adds DM_CRYPT_NO_READ_WORKQUEUE and DM_CRYPT_NO_WRITE_WORKQUEUE flags for read and write BIOs, which direct dm-crypt to not offload crypto operations into kcryptd workqueues. In addition, writes are not buffered to be sorted in the dm-crypt red-black tree, but dispatched immediately. For cases, where crypto operations cannot happen (hard interrupt context, for example the read path of some NVME drivers), we offload the work to a tasklet rather than a workqueue. These flags only ensure no async BIO processing in the dm-crypt module. It is worth noting that some Crypto API implementations may offload encryption into their own workqueues, which are independent of the dm-crypt and its configuration. However upon enabling these new flags dm-crypt will instruct Crypto API not to backlog crypto requests. To give an idea of the performance gains for certain workloads, consider the script, and results when tested against various devices, detailed here: https://www.redhat.com/archives/dm-devel/2020-July/msg00138.html [1]: https://www.spinics.net/lists/dm-crypt/msg07516.html [2]: https://blog.cloudflare.com/speeding-up-linux-disk-encryption/ Signed-off-by: Ignat Korchagin Reviewed-by: Damien Le Moal Reviewed-by: Bob Liu Signed-off-by: Mike Snitzer --- drivers/md/dm-crypt.c | 50 ++++++++++++++++++++++++++++++++++++------- 1 file changed, 42 insertions(+), 8 deletions(-) diff --git a/drivers/md/dm-crypt.c b/drivers/md/dm-crypt.c index 000ddfab5ba05..7536ecb2c95df 100644 --- a/drivers/md/dm-crypt.c +++ b/drivers/md/dm-crypt.c @@ -69,6 +69,7 @@ struct dm_crypt_io { u8 *integrity_metadata; bool integrity_metadata_from_pool; struct work_struct work; + struct tasklet_struct tasklet; struct convert_context ctx; @@ -127,7 +128,8 @@ struct iv_elephant_private { * and encrypts / decrypts at the same time. */ enum flags { DM_CRYPT_SUSPENDED, DM_CRYPT_KEY_VALID, - DM_CRYPT_SAME_CPU, DM_CRYPT_NO_OFFLOAD }; + DM_CRYPT_SAME_CPU, DM_CRYPT_NO_OFFLOAD, + DM_CRYPT_NO_READ_WORKQUEUE, DM_CRYPT_NO_WRITE_WORKQUEUE }; enum cipher_flags { CRYPT_MODE_INTEGRITY_AEAD, /* Use authenticated mode for cihper */ @@ -1523,7 +1525,7 @@ static void crypt_free_req(struct crypt_config *cc, void *req, struct bio *base_ * Encrypt / decrypt data from one bio to another one (can be the same one) */ static blk_status_t crypt_convert(struct crypt_config *cc, - struct convert_context *ctx) + struct convert_context *ctx, bool atomic) { unsigned int tag_offset = 0; unsigned int sector_step = cc->sector_size >> SECTOR_SHIFT; @@ -1566,7 +1568,8 @@ static blk_status_t crypt_convert(struct crypt_config *cc, atomic_dec(&ctx->cc_pending); ctx->cc_sector += sector_step; tag_offset++; - cond_resched(); + if (!atomic) + cond_resched(); continue; /* * There was a data integrity error. @@ -1892,7 +1895,8 @@ static void kcryptd_crypt_write_io_submit(struct dm_crypt_io *io, int async) clone->bi_iter.bi_sector = cc->start + io->sector; - if (likely(!async) && test_bit(DM_CRYPT_NO_OFFLOAD, &cc->flags)) { + if ((likely(!async) && test_bit(DM_CRYPT_NO_OFFLOAD, &cc->flags)) || + test_bit(DM_CRYPT_NO_WRITE_WORKQUEUE, &cc->flags)) { generic_make_request(clone); return; } @@ -1941,7 +1945,8 @@ static void kcryptd_crypt_write_convert(struct dm_crypt_io *io) sector += bio_sectors(clone); crypt_inc_pending(io); - r = crypt_convert(cc, &io->ctx); + r = crypt_convert(cc, &io->ctx, + test_bit(DM_CRYPT_NO_WRITE_WORKQUEUE, &cc->flags)); if (r) io->error = r; crypt_finished = atomic_dec_and_test(&io->ctx.cc_pending); @@ -1971,7 +1976,8 @@ static void kcryptd_crypt_read_convert(struct dm_crypt_io *io) crypt_convert_init(cc, &io->ctx, io->base_bio, io->base_bio, io->sector); - r = crypt_convert(cc, &io->ctx); + r = crypt_convert(cc, &io->ctx, + test_bit(DM_CRYPT_NO_READ_WORKQUEUE, &cc->flags)); if (r) io->error = r; @@ -2031,10 +2037,28 @@ static void kcryptd_crypt(struct work_struct *work) kcryptd_crypt_write_convert(io); } +static void kcryptd_crypt_tasklet(unsigned long work) +{ + kcryptd_crypt((struct work_struct *)work); +} + static void kcryptd_queue_crypt(struct dm_crypt_io *io) { struct crypt_config *cc = io->cc; + if ((bio_data_dir(io->base_bio) == READ && test_bit(DM_CRYPT_NO_READ_WORKQUEUE, &cc->flags)) || + (bio_data_dir(io->base_bio) == WRITE && test_bit(DM_CRYPT_NO_WRITE_WORKQUEUE, &cc->flags))) { + if (in_irq()) { + /* Crypto API's "skcipher_walk_first() refuses to work in hard IRQ context */ + tasklet_init(&io->tasklet, kcryptd_crypt_tasklet, (unsigned long)&io->work); + tasklet_schedule(&io->tasklet); + return; + } + + kcryptd_crypt(&io->work); + return; + } + INIT_WORK(&io->work, kcryptd_crypt); queue_work(cc->crypt_queue, &io->work); } @@ -2838,7 +2862,7 @@ static int crypt_ctr_optional(struct dm_target *ti, unsigned int argc, char **ar struct crypt_config *cc = ti->private; struct dm_arg_set as; static const struct dm_arg _args[] = { - {0, 6, "Invalid number of feature args"}, + {0, 8, "Invalid number of feature args"}, }; unsigned int opt_params, val; const char *opt_string, *sval; @@ -2868,6 +2892,10 @@ static int crypt_ctr_optional(struct dm_target *ti, unsigned int argc, char **ar else if (!strcasecmp(opt_string, "submit_from_crypt_cpus")) set_bit(DM_CRYPT_NO_OFFLOAD, &cc->flags); + else if (!strcasecmp(opt_string, "no_read_workqueue")) + set_bit(DM_CRYPT_NO_READ_WORKQUEUE, &cc->flags); + else if (!strcasecmp(opt_string, "no_write_workqueue")) + set_bit(DM_CRYPT_NO_WRITE_WORKQUEUE, &cc->flags); else if (sscanf(opt_string, "integrity:%u:", &val) == 1) { if (val == 0 || val > MAX_TAG_SIZE) { ti->error = "Invalid integrity arguments"; @@ -3196,6 +3224,8 @@ static void crypt_status(struct dm_target *ti, status_type_t type, num_feature_args += !!ti->num_discard_bios; num_feature_args += test_bit(DM_CRYPT_SAME_CPU, &cc->flags); num_feature_args += test_bit(DM_CRYPT_NO_OFFLOAD, &cc->flags); + num_feature_args += test_bit(DM_CRYPT_NO_READ_WORKQUEUE, &cc->flags); + num_feature_args += test_bit(DM_CRYPT_NO_WRITE_WORKQUEUE, &cc->flags); num_feature_args += cc->sector_size != (1 << SECTOR_SHIFT); num_feature_args += test_bit(CRYPT_IV_LARGE_SECTORS, &cc->cipher_flags); if (cc->on_disk_tag_size) @@ -3208,6 +3238,10 @@ static void crypt_status(struct dm_target *ti, status_type_t type, DMEMIT(" same_cpu_crypt"); if (test_bit(DM_CRYPT_NO_OFFLOAD, &cc->flags)) DMEMIT(" submit_from_crypt_cpus"); + if (test_bit(DM_CRYPT_NO_READ_WORKQUEUE, &cc->flags)) + DMEMIT(" no_read_workqueue"); + if (test_bit(DM_CRYPT_NO_WRITE_WORKQUEUE, &cc->flags)) + DMEMIT(" no_write_workqueue"); if (cc->on_disk_tag_size) DMEMIT(" integrity:%u:%s", cc->on_disk_tag_size, cc->cipher_auth); if (cc->sector_size != (1 << SECTOR_SHIFT)) @@ -3320,7 +3354,7 @@ static void crypt_io_hints(struct dm_target *ti, struct queue_limits *limits) static struct target_type crypt_target = { .name = "crypt", - .version = {1, 21, 0}, + .version = {1, 22, 0}, .module = THIS_MODULE, .ctr = crypt_ctr, .dtr = crypt_dtr, -- GitLab From 8e225f04d2dd6c1ca1eaecd5b04eaa90284df507 Mon Sep 17 00:00:00 2001 From: Damien Le Moal Date: Wed, 8 Jul 2020 18:28:08 +0900 Subject: [PATCH 0731/1476] dm crypt: Enable zoned block device support Enable support for zoned block devices. This is done by: 1) implementing the target report_zones method. 2) adding the DM_TARGET_ZONED_HM flag to the target features. 3) setting DM_CRYPT_NO_WRITE_WORKQUEUE flag to avoid IO processing via workqueue. 4) Introducing inline write encryption completion to preserve write ordering. The last point is implemented by introducing the internal flag DM_CRYPT_WRITE_INLINE. When set, kcryptd_crypt_write_convert() always waits inline for the completion of a write request encryption if the request is not already completed once crypt_convert() returns. Completion of write request encryption is signaled using the restart completion by kcryptd_async_done(). This mechanism allows using ciphers that have an asynchronous implementation, isolating dm-crypt from any potential request completion reordering for these ciphers. Signed-off-by: Damien Le Moal Signed-off-by: Mike Snitzer --- drivers/md/dm-crypt.c | 83 +++++++++++++++++++++++++++++++++++++++---- 1 file changed, 76 insertions(+), 7 deletions(-) diff --git a/drivers/md/dm-crypt.c b/drivers/md/dm-crypt.c index 7536ecb2c95df..bad05c5ed3b5a 100644 --- a/drivers/md/dm-crypt.c +++ b/drivers/md/dm-crypt.c @@ -129,7 +129,8 @@ struct iv_elephant_private { */ enum flags { DM_CRYPT_SUSPENDED, DM_CRYPT_KEY_VALID, DM_CRYPT_SAME_CPU, DM_CRYPT_NO_OFFLOAD, - DM_CRYPT_NO_READ_WORKQUEUE, DM_CRYPT_NO_WRITE_WORKQUEUE }; + DM_CRYPT_NO_READ_WORKQUEUE, DM_CRYPT_NO_WRITE_WORKQUEUE, + DM_CRYPT_WRITE_INLINE }; enum cipher_flags { CRYPT_MODE_INTEGRITY_AEAD, /* Use authenticated mode for cihper */ @@ -1919,9 +1920,32 @@ static void kcryptd_crypt_write_io_submit(struct dm_crypt_io *io, int async) spin_unlock_irqrestore(&cc->write_thread_lock, flags); } +static bool kcryptd_crypt_write_inline(struct crypt_config *cc, + struct convert_context *ctx) + +{ + if (!test_bit(DM_CRYPT_WRITE_INLINE, &cc->flags)) + return false; + + /* + * Note: zone append writes (REQ_OP_ZONE_APPEND) do not have ordering + * constraints so they do not need to be issued inline by + * kcryptd_crypt_write_convert(). + */ + switch (bio_op(ctx->bio_in)) { + case REQ_OP_WRITE: + case REQ_OP_WRITE_SAME: + case REQ_OP_WRITE_ZEROES: + return true; + default: + return false; + } +} + static void kcryptd_crypt_write_convert(struct dm_crypt_io *io) { struct crypt_config *cc = io->cc; + struct convert_context *ctx = &io->ctx; struct bio *clone; int crypt_finished; sector_t sector = io->sector; @@ -1931,7 +1955,7 @@ static void kcryptd_crypt_write_convert(struct dm_crypt_io *io) * Prevent io from disappearing until this function completes. */ crypt_inc_pending(io); - crypt_convert_init(cc, &io->ctx, NULL, io->base_bio, sector); + crypt_convert_init(cc, ctx, NULL, io->base_bio, sector); clone = crypt_alloc_buffer(io, io->base_bio->bi_iter.bi_size); if (unlikely(!clone)) { @@ -1945,11 +1969,16 @@ static void kcryptd_crypt_write_convert(struct dm_crypt_io *io) sector += bio_sectors(clone); crypt_inc_pending(io); - r = crypt_convert(cc, &io->ctx, + r = crypt_convert(cc, ctx, test_bit(DM_CRYPT_NO_WRITE_WORKQUEUE, &cc->flags)); if (r) io->error = r; - crypt_finished = atomic_dec_and_test(&io->ctx.cc_pending); + crypt_finished = atomic_dec_and_test(&ctx->cc_pending); + if (!crypt_finished && kcryptd_crypt_write_inline(cc, ctx)) { + /* Wait for completion signaled by kcryptd_async_done() */ + wait_for_completion(&ctx->restart); + crypt_finished = 1; + } /* Encryption was already finished, submit io now */ if (crypt_finished) { @@ -2021,10 +2050,21 @@ static void kcryptd_async_done(struct crypto_async_request *async_req, if (!atomic_dec_and_test(&ctx->cc_pending)) return; - if (bio_data_dir(io->base_bio) == READ) + /* + * The request is fully completed: for inline writes, let + * kcryptd_crypt_write_convert() do the IO submission. + */ + if (bio_data_dir(io->base_bio) == READ) { kcryptd_crypt_read_done(io); - else - kcryptd_crypt_write_io_submit(io, 1); + return; + } + + if (kcryptd_crypt_write_inline(cc, ctx)) { + complete(&ctx->restart); + return; + } + + kcryptd_crypt_write_io_submit(io, 1); } static void kcryptd_crypt(struct work_struct *work) @@ -2936,6 +2976,21 @@ static int crypt_ctr_optional(struct dm_target *ti, unsigned int argc, char **ar return 0; } +#ifdef CONFIG_BLK_DEV_ZONED + +static int crypt_report_zones(struct dm_target *ti, + struct dm_report_zones_args *args, unsigned int nr_zones) +{ + struct crypt_config *cc = ti->private; + sector_t sector = cc->start + dm_target_offset(ti, args->next_sector); + + args->start = cc->start; + return blkdev_report_zones(cc->dev->bdev, sector, nr_zones, + dm_report_zones_cb, args); +} + +#endif + /* * Construct an encryption mapping: * [|:::] @@ -3069,6 +3124,16 @@ static int crypt_ctr(struct dm_target *ti, unsigned int argc, char **argv) } cc->start = tmpll; + /* + * For zoned block devices, we need to preserve the issuer write + * ordering. To do so, disable write workqueues and force inline + * encryption completion. + */ + if (bdev_is_zoned(cc->dev->bdev)) { + set_bit(DM_CRYPT_NO_WRITE_WORKQUEUE, &cc->flags); + set_bit(DM_CRYPT_WRITE_INLINE, &cc->flags); + } + if (crypt_integrity_aead(cc) || cc->integrity_iv_size) { ret = crypt_integrity_ctr(cc, ti); if (ret) @@ -3358,6 +3423,10 @@ static struct target_type crypt_target = { .module = THIS_MODULE, .ctr = crypt_ctr, .dtr = crypt_dtr, +#ifdef CONFIG_BLK_DEV_ZONED + .features = DM_TARGET_ZONED_HM, + .report_zones = crypt_report_zones, +#endif .map = crypt_map, .status = crypt_status, .postsuspend = crypt_postsuspend, -- GitLab From 6c4411f14d1afa8ead90cd4cf18a308c43ac6908 Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Sat, 18 Jul 2020 17:28:30 -0700 Subject: [PATCH 0732/1476] clk: : drop a duplicated word Drop the repeated word "not" in a comment. Signed-off-by: Randy Dunlap Cc: Michael Turquette Cc: Stephen Boyd Cc: linux-clk@vger.kernel.org Link: https://lore.kernel.org/r/20200719002830.20319-1-rdunlap@infradead.org Signed-off-by: Stephen Boyd --- include/linux/clk-provider.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/linux/clk-provider.h b/include/linux/clk-provider.h index bd1ee9039558d..6f815be99b777 100644 --- a/include/linux/clk-provider.h +++ b/include/linux/clk-provider.h @@ -189,7 +189,7 @@ struct clk_duty { * and >= numerator) Return 0 on success, otherwise -EERROR. * * @init: Perform platform-specific initialization magic. - * This is not not used by any of the basic clock types. + * This is not used by any of the basic clock types. * This callback exist for HW which needs to perform some * initialisation magic for CCF to get an accurate view of the * clock. It may also be used dynamic resource allocation is -- GitLab From 044f507dc0a3070985592d84707a9d69746d84c6 Mon Sep 17 00:00:00 2001 From: Sivaprakash Murugesan Date: Thu, 16 Jul 2020 11:02:50 +0530 Subject: [PATCH 0733/1476] clk: qcom: ipq8074: Add correct index for PCIe clocks The PCIe clocks GCC_PCIE0_AXI_S_BRIDGE_CLK, GCC_PCIE0_RCHNG_CLK_SRC, GCC_PCIE0_RCHNG_CLK are wrongly added to the gcc reset group. Move them to the gcc clock group. Reported-by: kernel test robot Signed-off-by: Sivaprakash Murugesan Link: https://lore.kernel.org/r/1594877570-9280-1-git-send-email-sivaprak@codeaurora.org Fixes: e7fb524cfcca ("dt-bindings: clock: qcom: ipq8074: Add missing bindings for PCIe") Signed-off-by: Stephen Boyd --- include/dt-bindings/clock/qcom,gcc-ipq8074.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/include/dt-bindings/clock/qcom,gcc-ipq8074.h b/include/dt-bindings/clock/qcom,gcc-ipq8074.h index e3e018565addf..8e2bec1c91bf9 100644 --- a/include/dt-bindings/clock/qcom,gcc-ipq8074.h +++ b/include/dt-bindings/clock/qcom,gcc-ipq8074.h @@ -230,6 +230,9 @@ #define GCC_GP1_CLK 221 #define GCC_GP2_CLK 222 #define GCC_GP3_CLK 223 +#define GCC_PCIE0_AXI_S_BRIDGE_CLK 224 +#define GCC_PCIE0_RCHNG_CLK_SRC 225 +#define GCC_PCIE0_RCHNG_CLK 226 #define GCC_BLSP1_BCR 0 #define GCC_BLSP1_QUP1_BCR 1 @@ -363,8 +366,5 @@ #define GCC_PCIE1_AHB_ARES 129 #define GCC_PCIE1_AXI_MASTER_STICKY_ARES 130 #define GCC_PCIE0_AXI_SLAVE_STICKY_ARES 131 -#define GCC_PCIE0_AXI_S_BRIDGE_CLK 132 -#define GCC_PCIE0_RCHNG_CLK_SRC 133 -#define GCC_PCIE0_RCHNG_CLK 134 #endif -- GitLab From 8607fa169178a4a38f232612e036def96b5ecd1d Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Tue, 14 Jul 2020 22:21:55 +0800 Subject: [PATCH 0734/1476] clk: qcom: msm8996: Make symbol 'cpu_msm8996_clks' static The sparse tool complains as follows: drivers/clk/qcom/clk-cpu-8996.c:341:19: warning: symbol 'cpu_msm8996_clks' was not declared. Should it be static? This variable is not used outside of clk-cpu-8996.c, so this commit marks it static. Fixes: 03e342dc45c9 ("clk: qcom: Add CPU clock driver for msm8996") Reported-by: Hulk Robot Signed-off-by: Wei Yongjun Link: https://lore.kernel.org/r/20200714142155.35085-1-weiyongjun1@huawei.com Signed-off-by: Stephen Boyd --- drivers/clk/qcom/clk-cpu-8996.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/clk/qcom/clk-cpu-8996.c b/drivers/clk/qcom/clk-cpu-8996.c index 77a2d2806e581..4a4fde8dd12dd 100644 --- a/drivers/clk/qcom/clk-cpu-8996.c +++ b/drivers/clk/qcom/clk-cpu-8996.c @@ -338,7 +338,7 @@ static const struct regmap_config cpu_msm8996_regmap_config = { .val_format_endian = REGMAP_ENDIAN_LITTLE, }; -struct clk_regmap *cpu_msm8996_clks[] = { +static struct clk_regmap *cpu_msm8996_clks[] = { &perfcl_pll.clkr, &pwrcl_pll.clkr, &perfcl_alt_pll.clkr, -- GitLab From 5ce728fa78ddbef667fd757ad008d33e39e7312a Mon Sep 17 00:00:00 2001 From: Ansuel Smith Date: Thu, 16 Jul 2020 04:28:10 +0200 Subject: [PATCH 0735/1476] ipq806x: gcc: add support for child probe Add support for child probing needed for tsens driver that share the same regs of gcc for this platform. Signed-off-by: Ansuel Smith Reviewed-by: Amit Kucheria Link: https://lore.kernel.org/r/20200716022817.30439-2-ansuelsmth@gmail.com Signed-off-by: Stephen Boyd --- drivers/clk/qcom/gcc-ipq806x.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/clk/qcom/gcc-ipq806x.c b/drivers/clk/qcom/gcc-ipq806x.c index a8456e09c44d1..d6b7adb4be380 100644 --- a/drivers/clk/qcom/gcc-ipq806x.c +++ b/drivers/clk/qcom/gcc-ipq806x.c @@ -3089,7 +3089,7 @@ static int gcc_ipq806x_probe(struct platform_device *pdev) regmap_write(regmap, 0x3cf8, 8); regmap_write(regmap, 0x3d18, 8); - return 0; + return of_platform_populate(pdev->dev.of_node, NULL, NULL, &pdev->dev); } static struct platform_driver gcc_ipq806x_driver = { -- GitLab From 9c3df2b1993da9ab1110702d7b2815d5cd8c02f3 Mon Sep 17 00:00:00 2001 From: Taniya Das Date: Wed, 15 Jul 2020 12:24:10 +0530 Subject: [PATCH 0736/1476] clk: qcom: gcc: Make disp gpll0 branch aon for sc7180/sdm845 The display gpll0 branch clock inside GCC needs to always be enabled. Otherwise the AHB clk (disp_cc_mdss_ahb_clk_src) for the display clk controller (dispcc) will stop clocking while sourcing from gpll0 when this branch inside GCC is turned off during unused clk disabling. We can never turn this branch off because the AHB clk for the display subsystem is needed to read/write any registers inside the display subsystem including clk related ones. This makes this branch a really easy way to turn off AHB access to the display subsystem and cause all sorts of mayhem. Let's just make the clk ops keep the clk enabled forever and ignore any attempts to disable this clk so that dispcc accesses keep working. Signed-off-by: Taniya Das Reported-by: Evan Green Link: https://lore.kernel.org/r/1594796050-14511-1-git-send-email-tdas@codeaurora.org Fixes: 17269568f726 ("clk: qcom: Add Global Clock controller (GCC) driver for SC7180") Fixes: 06391eddb60a ("clk: qcom: Add Global Clock controller (GCC) driver for SDM845") [sboyd@kernel.org: Fill out commit text more] Signed-off-by: Stephen Boyd --- drivers/clk/qcom/gcc-sc7180.c | 2 +- drivers/clk/qcom/gcc-sdm845.c | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/clk/qcom/gcc-sc7180.c b/drivers/clk/qcom/gcc-sc7180.c index ca4383e3a02a7..538677befb86f 100644 --- a/drivers/clk/qcom/gcc-sc7180.c +++ b/drivers/clk/qcom/gcc-sc7180.c @@ -1061,7 +1061,7 @@ static struct clk_branch gcc_disp_gpll0_clk_src = { .hw = &gpll0.clkr.hw, }, .num_parents = 1, - .ops = &clk_branch2_ops, + .ops = &clk_branch2_aon_ops, }, }, }; diff --git a/drivers/clk/qcom/gcc-sdm845.c b/drivers/clk/qcom/gcc-sdm845.c index f6ce888098be9..90f7febaf5288 100644 --- a/drivers/clk/qcom/gcc-sdm845.c +++ b/drivers/clk/qcom/gcc-sdm845.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 /* - * Copyright (c) 2018, The Linux Foundation. All rights reserved. + * Copyright (c) 2018, 2020, The Linux Foundation. All rights reserved. */ #include @@ -1344,7 +1344,7 @@ static struct clk_branch gcc_disp_gpll0_clk_src = { "gpll0", }, .num_parents = 1, - .ops = &clk_branch2_ops, + .ops = &clk_branch2_aon_ops, }, }, }; -- GitLab From 70e380250c3621c55ff218cbaf2272830d9dbb1d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pali=20Roh=C3=A1r?= Date: Thu, 2 Jul 2020 10:30:36 +0200 Subject: [PATCH 0737/1476] PCI: aardvark: Don't touch PCIe registers if no card connected MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When there is no PCIe card connected and advk_pcie_rd_conf() or advk_pcie_wr_conf() is called for PCI bus which doesn't belong to emulated root bridge, the aardvark driver throws the following error message: advk-pcie d0070000.pcie: config read/write timed out Obviously accessing PCIe registers of disconnected card is not possible. Extend check in advk_pcie_valid_device() function for validating availability of PCIe bus. If PCIe link is down, then the device is marked as Not Found and the driver does not try to access these registers. This is just an optimization to prevent accessing PCIe registers when card is disconnected. Trying to access PCIe registers of disconnected card does not cause any crash, kernel just needs to wait for a timeout. So if card disappear immediately after checking for PCIe link (before accessing PCIe registers), it does not cause any problems. Link: https://lore.kernel.org/r/20200702083036.12230-1-pali@kernel.org Signed-off-by: Pali Rohár Signed-off-by: Lorenzo Pieralisi --- drivers/pci/controller/pci-aardvark.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/pci/controller/pci-aardvark.c b/drivers/pci/controller/pci-aardvark.c index 7872035dbb03c..1258f45cbc11a 100644 --- a/drivers/pci/controller/pci-aardvark.c +++ b/drivers/pci/controller/pci-aardvark.c @@ -644,6 +644,13 @@ static bool advk_pcie_valid_device(struct advk_pcie *pcie, struct pci_bus *bus, if ((bus->number == pcie->root_bus_nr) && PCI_SLOT(devfn) != 0) return false; + /* + * If the link goes down after we check for link-up, nothing bad + * happens but the config access times out. + */ + if (bus->number != pcie->root_bus_nr && !advk_pcie_link_up(pcie)) + return false; + return true; } -- GitLab From 9a11f12e0a6c374b3ef1ce81e32ce477d28eb1b8 Mon Sep 17 00:00:00 2001 From: "Aneesh Kumar K.V" Date: Thu, 9 Jul 2020 08:59:43 +0530 Subject: [PATCH 0738/1476] selftests/powerpc: ptrace-pkey: Rename variables to make it easier to follow code Rename variable to indicate that they are invalid values which we will use to test ptrace update of pkeys. Signed-off-by: Aneesh Kumar K.V Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20200709032946.881753-21-aneesh.kumar@linux.ibm.com --- .../selftests/powerpc/ptrace/ptrace-pkey.c | 26 +++++++++---------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/tools/testing/selftests/powerpc/ptrace/ptrace-pkey.c b/tools/testing/selftests/powerpc/ptrace/ptrace-pkey.c index 904c04f8c919b..a32a8034f9565 100644 --- a/tools/testing/selftests/powerpc/ptrace/ptrace-pkey.c +++ b/tools/testing/selftests/powerpc/ptrace/ptrace-pkey.c @@ -44,7 +44,7 @@ struct shared_info { unsigned long amr2; /* AMR value that ptrace should refuse to write to the child. */ - unsigned long amr3; + unsigned long invalid_amr; /* IAMR value the parent expects to read from the child. */ unsigned long expected_iamr; @@ -57,8 +57,8 @@ struct shared_info { * (even though they're valid ones) because userspace doesn't have * access to those registers. */ - unsigned long new_iamr; - unsigned long new_uamor; + unsigned long invalid_iamr; + unsigned long invalid_uamor; }; static int sys_pkey_alloc(unsigned long flags, unsigned long init_access_rights) @@ -100,7 +100,7 @@ static int child(struct shared_info *info) info->amr1 |= 3ul << pkeyshift(pkey1); info->amr2 |= 3ul << pkeyshift(pkey2); - info->amr3 |= info->amr2 | 3ul << pkeyshift(pkey3); + info->invalid_amr |= info->amr2 | 3ul << pkeyshift(pkey3); if (disable_execute) info->expected_iamr |= 1ul << pkeyshift(pkey1); @@ -111,8 +111,8 @@ static int child(struct shared_info *info) info->expected_uamor |= 3ul << pkeyshift(pkey1) | 3ul << pkeyshift(pkey2); - info->new_iamr |= 1ul << pkeyshift(pkey1) | 1ul << pkeyshift(pkey2); - info->new_uamor |= 3ul << pkeyshift(pkey1); + info->invalid_iamr |= 1ul << pkeyshift(pkey1) | 1ul << pkeyshift(pkey2); + info->invalid_uamor |= 3ul << pkeyshift(pkey1); /* * We won't use pkey3. We just want a plausible but invalid key to test @@ -196,9 +196,9 @@ static int parent(struct shared_info *info, pid_t pid) PARENT_SKIP_IF_UNSUPPORTED(ret, &info->child_sync); PARENT_FAIL_IF(ret, &info->child_sync); - info->amr1 = info->amr2 = info->amr3 = regs[0]; - info->expected_iamr = info->new_iamr = regs[1]; - info->expected_uamor = info->new_uamor = regs[2]; + info->amr1 = info->amr2 = info->invalid_amr = regs[0]; + info->expected_iamr = info->invalid_iamr = regs[1]; + info->expected_uamor = info->invalid_uamor = regs[2]; /* Wake up child so that it can set itself up. */ ret = prod_child(&info->child_sync); @@ -234,10 +234,10 @@ static int parent(struct shared_info *info, pid_t pid) return ret; /* Write invalid AMR value in child. */ - ret = ptrace_write_regs(pid, NT_PPC_PKEY, &info->amr3, 1); + ret = ptrace_write_regs(pid, NT_PPC_PKEY, &info->invalid_amr, 1); PARENT_FAIL_IF(ret, &info->child_sync); - printf("%-30s AMR: %016lx\n", ptrace_write_running, info->amr3); + printf("%-30s AMR: %016lx\n", ptrace_write_running, info->invalid_amr); /* Wake up child so that it can verify it didn't change. */ ret = prod_child(&info->child_sync); @@ -249,7 +249,7 @@ static int parent(struct shared_info *info, pid_t pid) /* Try to write to IAMR. */ regs[0] = info->amr1; - regs[1] = info->new_iamr; + regs[1] = info->invalid_iamr; ret = ptrace_write_regs(pid, NT_PPC_PKEY, regs, 2); PARENT_FAIL_IF(!ret, &info->child_sync); @@ -257,7 +257,7 @@ static int parent(struct shared_info *info, pid_t pid) ptrace_write_running, regs[0], regs[1]); /* Try to write to IAMR and UAMOR. */ - regs[2] = info->new_uamor; + regs[2] = info->invalid_uamor; ret = ptrace_write_regs(pid, NT_PPC_PKEY, regs, 3); PARENT_FAIL_IF(!ret, &info->child_sync); -- GitLab From 0eaa3b5ca7b5a76e3783639c828498343be66a01 Mon Sep 17 00:00:00 2001 From: "Aneesh Kumar K.V" Date: Thu, 9 Jul 2020 08:59:44 +0530 Subject: [PATCH 0739/1476] selftests/powerpc: ptrace-pkey: Update the test to mark an invalid pkey correctly Signed-off-by: Aneesh Kumar K.V Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20200709032946.881753-22-aneesh.kumar@linux.ibm.com --- .../selftests/powerpc/ptrace/ptrace-pkey.c | 30 ++++++++----------- 1 file changed, 12 insertions(+), 18 deletions(-) diff --git a/tools/testing/selftests/powerpc/ptrace/ptrace-pkey.c b/tools/testing/selftests/powerpc/ptrace/ptrace-pkey.c index a32a8034f9565..80dc5085a412d 100644 --- a/tools/testing/selftests/powerpc/ptrace/ptrace-pkey.c +++ b/tools/testing/selftests/powerpc/ptrace/ptrace-pkey.c @@ -66,11 +66,6 @@ static int sys_pkey_alloc(unsigned long flags, unsigned long init_access_rights) return syscall(__NR_pkey_alloc, flags, init_access_rights); } -static int sys_pkey_free(int pkey) -{ - return syscall(__NR_pkey_free, pkey); -} - static int child(struct shared_info *info) { unsigned long reg; @@ -100,7 +95,11 @@ static int child(struct shared_info *info) info->amr1 |= 3ul << pkeyshift(pkey1); info->amr2 |= 3ul << pkeyshift(pkey2); - info->invalid_amr |= info->amr2 | 3ul << pkeyshift(pkey3); + /* + * invalid amr value where we try to force write + * things which are deined by a uamor setting. + */ + info->invalid_amr = info->amr2 | (~0x0UL & ~info->expected_uamor); if (disable_execute) info->expected_iamr |= 1ul << pkeyshift(pkey1); @@ -111,17 +110,12 @@ static int child(struct shared_info *info) info->expected_uamor |= 3ul << pkeyshift(pkey1) | 3ul << pkeyshift(pkey2); - info->invalid_iamr |= 1ul << pkeyshift(pkey1) | 1ul << pkeyshift(pkey2); - info->invalid_uamor |= 3ul << pkeyshift(pkey1); - /* - * We won't use pkey3. We just want a plausible but invalid key to test - * whether ptrace will let us write to AMR bits we are not supposed to. - * - * This also tests whether the kernel restores the UAMOR permissions - * after a key is freed. + * Create an IAMR value different from expected value. + * Kernel will reject an IAMR and UAMOR change. */ - sys_pkey_free(pkey3); + info->invalid_iamr = info->expected_iamr | (1ul << pkeyshift(pkey1) | 1ul << pkeyshift(pkey2)); + info->invalid_uamor = info->expected_uamor & ~(0x3ul << pkeyshift(pkey1)); printf("%-30s AMR: %016lx pkey1: %d pkey2: %d pkey3: %d\n", user_write, info->amr1, pkey1, pkey2, pkey3); @@ -196,9 +190,9 @@ static int parent(struct shared_info *info, pid_t pid) PARENT_SKIP_IF_UNSUPPORTED(ret, &info->child_sync); PARENT_FAIL_IF(ret, &info->child_sync); - info->amr1 = info->amr2 = info->invalid_amr = regs[0]; - info->expected_iamr = info->invalid_iamr = regs[1]; - info->expected_uamor = info->invalid_uamor = regs[2]; + info->amr1 = info->amr2 = regs[0]; + info->expected_iamr = regs[1]; + info->expected_uamor = regs[2]; /* Wake up child so that it can set itself up. */ ret = prod_child(&info->child_sync); -- GitLab From 3563b9bea0ca7f53e4218b5e268550341a49f333 Mon Sep 17 00:00:00 2001 From: "Aneesh Kumar K.V" Date: Thu, 9 Jul 2020 08:59:45 +0530 Subject: [PATCH 0740/1476] selftests/powerpc: ptrace-pkey: Don't update expected UAMOR value With commit 4a4a5e5d2aad ("powerpc/pkeys: key allocation/deallocation must not change pkey registers") we are not updating UAMOR on key allocation. So don't update the expected uamor value in the test. Fixes: 4a4a5e5d2aad ("powerpc/pkeys: key allocation/deallocation must not change pkey registers") Signed-off-by: Aneesh Kumar K.V Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20200709032946.881753-23-aneesh.kumar@linux.ibm.com --- tools/testing/selftests/powerpc/ptrace/ptrace-pkey.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/tools/testing/selftests/powerpc/ptrace/ptrace-pkey.c b/tools/testing/selftests/powerpc/ptrace/ptrace-pkey.c index 80dc5085a412d..bc454f8991246 100644 --- a/tools/testing/selftests/powerpc/ptrace/ptrace-pkey.c +++ b/tools/testing/selftests/powerpc/ptrace/ptrace-pkey.c @@ -101,15 +101,20 @@ static int child(struct shared_info *info) */ info->invalid_amr = info->amr2 | (~0x0UL & ~info->expected_uamor); + /* + * if PKEY_DISABLE_EXECUTE succeeded we should update the expected_iamr + */ if (disable_execute) info->expected_iamr |= 1ul << pkeyshift(pkey1); else info->expected_iamr &= ~(1ul << pkeyshift(pkey1)); - info->expected_iamr &= ~(1ul << pkeyshift(pkey2) | 1ul << pkeyshift(pkey3)); + /* + * We allocated pkey2 and pkey 3 above. Clear the IAMR bits. + */ + info->expected_iamr &= ~(1ul << pkeyshift(pkey2)); + info->expected_iamr &= ~(1ul << pkeyshift(pkey3)); - info->expected_uamor |= 3ul << pkeyshift(pkey1) | - 3ul << pkeyshift(pkey2); /* * Create an IAMR value different from expected value. * Kernel will reject an IAMR and UAMOR change. -- GitLab From 482b9b3948675df60c015b2155011c1f93234992 Mon Sep 17 00:00:00 2001 From: "Aneesh Kumar K.V" Date: Thu, 9 Jul 2020 08:59:46 +0530 Subject: [PATCH 0741/1476] powerpc/book3s64/pkeys: Remove is_pkey_enabled() There is only one caller to this function and the function is wrongly named. Avoid further confusion w.r.t name and open code this at the only call site. Also remove read_uamor(). There are no users for the same after this. Signed-off-by: Aneesh Kumar K.V Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20200709032946.881753-24-aneesh.kumar@linux.ibm.com --- arch/powerpc/mm/book3s64/pkeys.c | 31 +++++++++++-------------------- 1 file changed, 11 insertions(+), 20 deletions(-) diff --git a/arch/powerpc/mm/book3s64/pkeys.c b/arch/powerpc/mm/book3s64/pkeys.c index d45864a77066b..792b36aa9619e 100644 --- a/arch/powerpc/mm/book3s64/pkeys.c +++ b/arch/powerpc/mm/book3s64/pkeys.c @@ -251,25 +251,6 @@ static inline void write_iamr(u64 value) mtspr(SPRN_IAMR, value); } -static inline u64 read_uamor(void) -{ - return mfspr(SPRN_UAMOR); -} - -static bool is_pkey_enabled(int pkey) -{ - u64 uamor = read_uamor(); - u64 pkey_bits = 0x3ul << pkeyshift(pkey); - u64 uamor_pkey_bits = (uamor & pkey_bits); - - /* - * Both the bits in UAMOR corresponding to the key should be set or - * reset. - */ - WARN_ON(uamor_pkey_bits && (uamor_pkey_bits != pkey_bits)); - return !!(uamor_pkey_bits); -} - static inline void init_amr(int pkey, u8 init_bits) { u64 new_amr_bits = (((u64)init_bits & 0x3UL) << pkeyshift(pkey)); @@ -295,8 +276,18 @@ int __arch_set_user_pkey_access(struct task_struct *tsk, int pkey, { u64 new_amr_bits = 0x0ul; u64 new_iamr_bits = 0x0ul; + u64 pkey_bits, uamor_pkey_bits; - if (!is_pkey_enabled(pkey)) + /* + * Check whether the key is disabled by UAMOR. + */ + pkey_bits = 0x3ul << pkeyshift(pkey); + uamor_pkey_bits = (default_uamor & pkey_bits); + + /* + * Both the bits in UAMOR corresponding to the key should be set + */ + if (uamor_pkey_bits != pkey_bits) return -EINVAL; if (init_val & PKEY_DISABLE_EXECUTE) { -- GitLab From 9d1ebe9a98c1d7bf7cfbe1dba0052230c042ecdb Mon Sep 17 00:00:00 2001 From: Michael Ellerman Date: Thu, 16 Jul 2020 22:21:42 +1000 Subject: [PATCH 0742/1476] selftests/powerpc: Run per_event_excludes test on Power8 or later The per_event_excludes test wants to run on Power8 or later. But currently it checks that AT_BASE_PLATFORM *equals* power8, which means it only runs on Power8. Fix it to check for the ISA 2.07 feature, which will be set on Power8 and later CPUs. Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20200716122142.3776261-1-mpe@ellerman.id.au --- tools/testing/selftests/powerpc/pmu/per_event_excludes.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/tools/testing/selftests/powerpc/pmu/per_event_excludes.c b/tools/testing/selftests/powerpc/pmu/per_event_excludes.c index 2756fe2efdc58..2d37942bf72b4 100644 --- a/tools/testing/selftests/powerpc/pmu/per_event_excludes.c +++ b/tools/testing/selftests/powerpc/pmu/per_event_excludes.c @@ -12,6 +12,8 @@ #include #include +#include + #include "event.h" #include "lib.h" #include "utils.h" @@ -23,12 +25,9 @@ static int per_event_excludes(void) { struct event *e, events[4]; - char *platform; int i; - platform = (char *)get_auxv_entry(AT_BASE_PLATFORM); - FAIL_IF(!platform); - SKIP_IF(strcmp(platform, "power8") != 0); + SKIP_IF(!have_hwcap2(PPC_FEATURE2_ARCH_2_07)); /* * We need to create the events disabled, otherwise the running/enabled -- GitLab From 38b407be172d3d15afdbfe172691b7caad98120f Mon Sep 17 00:00:00 2001 From: Michael Ellerman Date: Fri, 8 May 2020 23:06:33 +1000 Subject: [PATCH 0743/1476] powerpc/spufs: Rework fcheck() usage Currently the spu coredump code triggers an RCU warning: ============================= WARNING: suspicious RCU usage 5.7.0-rc3-01755-g7cd49f0b7ec7 #1 Not tainted ----------------------------- include/linux/fdtable.h:95 suspicious rcu_dereference_check() usage! other info that might help us debug this: rcu_scheduler_active = 2, debug_locks = 1 1 lock held by spu-coredump/1343: #0: c0000007fa22f430 (sb_writers#2){.+.+}-{0:0}, at: .do_coredump+0x1010/0x13c8 stack backtrace: CPU: 0 PID: 1343 Comm: spu-coredump Not tainted 5.7.0-rc3-01755-g7cd49f0b7ec7 #1 Call Trace: .dump_stack+0xec/0x15c (unreliable) .lockdep_rcu_suspicious+0x120/0x144 .coredump_next_context+0x148/0x158 .spufs_coredump_extra_notes_size+0x54/0x190 .elf_coredump_extra_notes_size+0x34/0x50 .elf_core_dump+0xe48/0x19d0 .do_coredump+0xe50/0x13c8 .get_signal+0x864/0xd88 .do_notify_resume+0x158/0x3c8 .interrupt_exit_user_prepare+0x19c/0x208 interrupt_return+0x14/0x1c0 This comes from fcheck_files() via fcheck(). It's pretty clearly documented that fcheck() must be wrapped with rcu_read_lock(), adding that fixes the RCU warning. hch points out that once we've released the RCU read lock the file may be closed and freed, which would leave us with a pointer to a freed spu_context. To avoid that, take a reference to the spu_context while we hold the RCU read lock, and drop that reference later once we're done with the context. Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20200508130633.2532759-1-mpe@ellerman.id.au --- arch/powerpc/platforms/cell/spufs/coredump.c | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/arch/powerpc/platforms/cell/spufs/coredump.c b/arch/powerpc/platforms/cell/spufs/coredump.c index 3b75e8f60609c..40dd7b483f06b 100644 --- a/arch/powerpc/platforms/cell/spufs/coredump.c +++ b/arch/powerpc/platforms/cell/spufs/coredump.c @@ -66,13 +66,20 @@ static int match_context(const void *v, struct file *file, unsigned fd) */ static struct spu_context *coredump_next_context(int *fd) { + struct spu_context *ctx; struct file *file; int n = iterate_fd(current->files, *fd, match_context, NULL); if (!n) return NULL; *fd = n - 1; + + rcu_read_lock(); file = fcheck(*fd); - return SPUFS_I(file_inode(file))->i_ctx; + ctx = SPUFS_I(file_inode(file))->i_ctx; + get_spu_context(ctx); + rcu_read_unlock(); + + return ctx; } int spufs_coredump_extra_notes_size(void) @@ -83,17 +90,23 @@ int spufs_coredump_extra_notes_size(void) fd = 0; while ((ctx = coredump_next_context(&fd)) != NULL) { rc = spu_acquire_saved(ctx); - if (rc) + if (rc) { + put_spu_context(ctx); break; + } + rc = spufs_ctx_note_size(ctx, fd); spu_release_saved(ctx); - if (rc < 0) + if (rc < 0) { + put_spu_context(ctx); break; + } size += rc; /* start searching the next fd next time */ fd++; + put_spu_context(ctx); } return size; -- GitLab From c8ed9fc9d29e24dafd08971e6a0c6b302a8ade2d Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Sat, 18 Jul 2020 12:39:58 +0200 Subject: [PATCH 0744/1476] powerpc: Replace HTTP links with HTTPS ones Rationale: Reduces attack surface on kernel devs opening the links for MITM as HTTPS traffic is much harder to manipulate. Deterministic algorithm: For each file: If not .svg: For each line: If doesn't contain `\bxmlns\b`: For each link, `\bhttp://[^# \t\r\n]*(?:\w|/)`: If neither `\bgnu\.org/license`, nor `\bmozilla\.org/MPL\b`: If both the HTTP and HTTPS versions return 200 OK and serve the same content: Replace HTTP with HTTPS. Signed-off-by: Alexander A. Klimov Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20200718103958.5455-1-grandmaster@al2klimov.de --- Documentation/powerpc/mpc52xx.rst | 2 +- arch/powerpc/crypto/crc32-vpmsum_core.S | 2 +- arch/powerpc/include/asm/hydra.h | 2 +- drivers/crypto/vmx/aesp8-ppc.pl | 2 +- drivers/crypto/vmx/ghashp8-ppc.pl | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Documentation/powerpc/mpc52xx.rst b/Documentation/powerpc/mpc52xx.rst index 8676ac63e0770..30260707c3fe8 100644 --- a/Documentation/powerpc/mpc52xx.rst +++ b/Documentation/powerpc/mpc52xx.rst @@ -2,7 +2,7 @@ Linux 2.6.x on MPC52xx family ============================= -For the latest info, go to http://www.246tNt.com/mpc52xx/ +For the latest info, go to https://www.246tNt.com/mpc52xx/ To compile/use : diff --git a/arch/powerpc/crypto/crc32-vpmsum_core.S b/arch/powerpc/crypto/crc32-vpmsum_core.S index c3524eba4d0d5..a16a717c809cd 100644 --- a/arch/powerpc/crypto/crc32-vpmsum_core.S +++ b/arch/powerpc/crypto/crc32-vpmsum_core.S @@ -19,7 +19,7 @@ * We then use fixed point Barrett reduction to compute a mod n over GF(2) * for n = CRC using POWER8 instructions. We use x = 32. * - * http://en.wikipedia.org/wiki/Barrett_reduction + * https://en.wikipedia.org/wiki/Barrett_reduction * * Copyright (C) 2015 Anton Blanchard , IBM */ diff --git a/arch/powerpc/include/asm/hydra.h b/arch/powerpc/include/asm/hydra.h index b3b0f2d020f0c..ae02eb53d6ef8 100644 --- a/arch/powerpc/include/asm/hydra.h +++ b/arch/powerpc/include/asm/hydra.h @@ -10,7 +10,7 @@ * * © Copyright 1995 Apple Computer, Inc. All rights reserved. * - * It's available online from http://www.cpu.lu/~mlan/ftp/MacTech.pdf + * It's available online from https://www.cpu.lu/~mlan/ftp/MacTech.pdf * You can obtain paper copies of this book from computer bookstores or by * writing Morgan Kaufmann Publishers, Inc., 340 Pine Street, Sixth Floor, San * Francisco, CA 94104. Reference ISBN 1-55860-393-X. diff --git a/drivers/crypto/vmx/aesp8-ppc.pl b/drivers/crypto/vmx/aesp8-ppc.pl index db874367b602e..50a0a18f35da3 100644 --- a/drivers/crypto/vmx/aesp8-ppc.pl +++ b/drivers/crypto/vmx/aesp8-ppc.pl @@ -50,7 +50,7 @@ # Written by Andy Polyakov for the OpenSSL # project. The module is, however, dual licensed under OpenSSL and # CRYPTOGAMS licenses depending on where you obtain it. For further -# details see http://www.openssl.org/~appro/cryptogams/. +# details see https://www.openssl.org/~appro/cryptogams/. # ==================================================================== # # This module implements support for AES instructions as per PowerISA diff --git a/drivers/crypto/vmx/ghashp8-ppc.pl b/drivers/crypto/vmx/ghashp8-ppc.pl index 38b06503ede0c..09bba1852eec1 100644 --- a/drivers/crypto/vmx/ghashp8-ppc.pl +++ b/drivers/crypto/vmx/ghashp8-ppc.pl @@ -13,7 +13,7 @@ # Written by Andy Polyakov for the OpenSSL # project. The module is, however, dual licensed under OpenSSL and # CRYPTOGAMS licenses depending on where you obtain it. For further -# details see http://www.openssl.org/~appro/cryptogams/. +# details see https://www.openssl.org/~appro/cryptogams/. # ==================================================================== # # GHASH for for PowerISA v2.07. -- GitLab From a7beab413e2e67dd1abe6bdd0001576892a89e81 Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Fri, 17 Jul 2020 20:35:22 +0200 Subject: [PATCH 0745/1476] macintosh/adb: Replace HTTP links with HTTPS ones Rationale: Reduces attack surface on kernel devs opening the links for MITM as HTTPS traffic is much harder to manipulate. Deterministic algorithm: For each file: If not .svg: For each line: If doesn't contain `\bxmlns\b`: For each link, `\bhttp://[^# \t\r\n]*(?:\w|/)`: If neither `\bgnu\.org/license`, nor `\bmozilla\.org/MPL\b`: If both the HTTP and HTTPS versions return 200 OK and serve the same content: Replace HTTP with HTTPS. Signed-off-by: Alexander A. Klimov Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20200717183522.77605-1-grandmaster@al2klimov.de --- drivers/macintosh/adb.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/macintosh/adb.c b/drivers/macintosh/adb.c index e49d1f287a175..73b3961890397 100644 --- a/drivers/macintosh/adb.c +++ b/drivers/macintosh/adb.c @@ -163,7 +163,7 @@ static int adb_scan_bus(void) * See if anybody actually moved. This is suggested * by HW TechNote 01: * - * http://developer.apple.com/technotes/hw/hw_01.html + * https://developer.apple.com/technotes/hw/hw_01.html */ adb_request(&req, NULL, ADBREQ_SYNC | ADBREQ_REPLY, 1, (highFree << 4) | 0xf); -- GitLab From 1666e5ea2f838f4266e50e4f3d973c0824256429 Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Fri, 17 Jul 2020 20:29:40 +0200 Subject: [PATCH 0746/1476] macintosh/therm_adt746x: Replace HTTP links with HTTPS ones Rationale: Reduces attack surface on kernel devs opening the links for MITM as HTTPS traffic is much harder to manipulate. Deterministic algorithm: For each file: If not .svg: For each line: If doesn't contain `\bxmlns\b`: For each link, `\bhttp://[^# \t\r\n]*(?:\w|/)`: If neither `\bgnu\.org/license`, nor `\bmozilla\.org/MPL\b`: If both the HTTP and HTTPS versions return 200 OK and serve the same content: Replace HTTP with HTTPS. Signed-off-by: Alexander A. Klimov Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20200717182940.75484-1-grandmaster@al2klimov.de --- drivers/macintosh/therm_adt746x.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/macintosh/therm_adt746x.c b/drivers/macintosh/therm_adt746x.c index 8f7725dc2166e..7e218437730ce 100644 --- a/drivers/macintosh/therm_adt746x.c +++ b/drivers/macintosh/therm_adt746x.c @@ -5,8 +5,8 @@ * Copyright (C) 2003, 2004 Colin Leroy, Rasmus Rohde, Benjamin Herrenschmidt * * Documentation from 115254175ADT7467_pra.pdf and 3686221171167ADT7460_b.pdf - * http://www.onsemi.com/PowerSolutions/product.do?id=ADT7467 - * http://www.onsemi.com/PowerSolutions/product.do?id=ADT7460 + * https://www.onsemi.com/PowerSolutions/product.do?id=ADT7467 + * https://www.onsemi.com/PowerSolutions/product.do?id=ADT7460 * */ -- GitLab From a3f3f8aa1f72dafe1450ccf8cbdfb1d12d42853a Mon Sep 17 00:00:00 2001 From: YueHaibing Date: Fri, 17 Jul 2020 19:27:14 +0800 Subject: [PATCH 0747/1476] powerpc: Remove unneeded inline functions Both of those functions are only called from 64-bit only code, so the stubs should not be needed at all. Suggested-by: Michael Ellerman Signed-off-by: YueHaibing Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20200717112714.19304-1-yuehaibing@huawei.com --- arch/powerpc/include/asm/mmu_context.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/arch/powerpc/include/asm/mmu_context.h b/arch/powerpc/include/asm/mmu_context.h index 1a474f6b1992a..7f3658a973846 100644 --- a/arch/powerpc/include/asm/mmu_context.h +++ b/arch/powerpc/include/asm/mmu_context.h @@ -218,8 +218,6 @@ static inline void inc_mm_active_cpus(struct mm_struct *mm) { } static inline void dec_mm_active_cpus(struct mm_struct *mm) { } static inline void mm_context_add_copro(struct mm_struct *mm) { } static inline void mm_context_remove_copro(struct mm_struct *mm) { } -static inline void mm_context_add_vas_windows(struct mm_struct *mm) { } -static inline void mm_context_remove_vas_windows(struct mm_struct *mm) { } #endif -- GitLab From f4ac1774f2cba44994ce9ac0a65772e4656ac2df Mon Sep 17 00:00:00 2001 From: Nicholas Piggin Date: Fri, 3 Jul 2020 11:19:56 +1000 Subject: [PATCH 0748/1476] powerpc: Remove stale calc_vm_prot_bits() comment This comment is wrong, we wouldn't use calc_vm_prot_bits() here because we are being called by calc_vm_prot_bits() to modify its behaviour. Signed-off-by: Nicholas Piggin Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20200703011958.1166620-2-npiggin@gmail.com --- arch/powerpc/include/asm/mman.h | 4 ---- 1 file changed, 4 deletions(-) diff --git a/arch/powerpc/include/asm/mman.h b/arch/powerpc/include/asm/mman.h index d610c2e07b28e..4ba303ea27f59 100644 --- a/arch/powerpc/include/asm/mman.h +++ b/arch/powerpc/include/asm/mman.h @@ -13,10 +13,6 @@ #include #include -/* - * This file is included by linux/mman.h, so we can't use cacl_vm_prot_bits() - * here. How important is the optimization? - */ static inline unsigned long arch_calc_vm_prot_bits(unsigned long prot, unsigned long pkey) { -- GitLab From 5c9fa16e8abd342ce04dc830c1ebb2a03abf6c05 Mon Sep 17 00:00:00 2001 From: Nicholas Piggin Date: Fri, 3 Jul 2020 11:19:57 +1000 Subject: [PATCH 0749/1476] powerpc/64s: Remove PROT_SAO support ISA v3.1 does not support the SAO storage control attribute required to implement PROT_SAO. PROT_SAO was used by specialised system software (Lx86) that has been discontinued for about 7 years, and is not thought to be used elsewhere, so removal should not cause problems. We rather remove it than keep support for older processors, because live migrating guest partitions to newer processors may not be possible if SAO is in use (or worse allowed with silent races). - PROT_SAO stays in the uapi header so code using it would still build. - arch_validate_prot() is removed, the generic version rejects PROT_SAO so applications would get a failure at mmap() time. Signed-off-by: Nicholas Piggin [mpe: Drop KVM change for the time being] Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20200703011958.1166620-3-npiggin@gmail.com --- arch/powerpc/include/asm/book3s/64/pgtable.h | 8 ++-- arch/powerpc/include/asm/cputable.h | 10 ++--- arch/powerpc/include/asm/mman.h | 26 ++---------- arch/powerpc/include/asm/nohash/64/pgtable.h | 2 - arch/powerpc/include/uapi/asm/mman.h | 2 +- arch/powerpc/kernel/dt_cpu_ftrs.c | 2 +- arch/powerpc/mm/book3s64/hash_utils.c | 2 - include/linux/mm.h | 2 - include/trace/events/mmflags.h | 2 - mm/ksm.c | 4 -- tools/testing/selftests/powerpc/mm/.gitignore | 1 - tools/testing/selftests/powerpc/mm/Makefile | 4 +- tools/testing/selftests/powerpc/mm/prot_sao.c | 42 ------------------- 13 files changed, 17 insertions(+), 90 deletions(-) delete mode 100644 tools/testing/selftests/powerpc/mm/prot_sao.c diff --git a/arch/powerpc/include/asm/book3s/64/pgtable.h b/arch/powerpc/include/asm/book3s/64/pgtable.h index 495fc0ccb4531..6de56c3b33c42 100644 --- a/arch/powerpc/include/asm/book3s/64/pgtable.h +++ b/arch/powerpc/include/asm/book3s/64/pgtable.h @@ -20,9 +20,13 @@ #define _PAGE_RW (_PAGE_READ | _PAGE_WRITE) #define _PAGE_RWX (_PAGE_READ | _PAGE_WRITE | _PAGE_EXEC) #define _PAGE_PRIVILEGED 0x00008 /* kernel access only */ -#define _PAGE_SAO 0x00010 /* Strong access order */ + +#define _PAGE_CACHE_CTL 0x00030 /* Bits for the folowing cache modes */ + /* No bits set is normal cacheable memory */ + /* 0x00010 unused, is SAO bit on radix POWER9 */ #define _PAGE_NON_IDEMPOTENT 0x00020 /* non idempotent memory */ #define _PAGE_TOLERANT 0x00030 /* tolerant memory, cache inhibited */ + #define _PAGE_DIRTY 0x00080 /* C: page changed */ #define _PAGE_ACCESSED 0x00100 /* R: page referenced */ /* @@ -824,8 +828,6 @@ static inline void __set_pte_at(struct mm_struct *mm, unsigned long addr, return hash__set_pte_at(mm, addr, ptep, pte, percpu); } -#define _PAGE_CACHE_CTL (_PAGE_SAO | _PAGE_NON_IDEMPOTENT | _PAGE_TOLERANT) - #define pgprot_noncached pgprot_noncached static inline pgprot_t pgprot_noncached(pgprot_t prot) { diff --git a/arch/powerpc/include/asm/cputable.h b/arch/powerpc/include/asm/cputable.h index dd0a2e77a6953..a461c33008047 100644 --- a/arch/powerpc/include/asm/cputable.h +++ b/arch/powerpc/include/asm/cputable.h @@ -191,7 +191,7 @@ static inline void cpu_feature_keys_init(void) { } #define CPU_FTR_SPURR LONG_ASM_CONST(0x0000000001000000) #define CPU_FTR_DSCR LONG_ASM_CONST(0x0000000002000000) #define CPU_FTR_VSX LONG_ASM_CONST(0x0000000004000000) -#define CPU_FTR_SAO LONG_ASM_CONST(0x0000000008000000) +// Free LONG_ASM_CONST(0x0000000008000000) #define CPU_FTR_CP_USE_DCBTZ LONG_ASM_CONST(0x0000000010000000) #define CPU_FTR_UNALIGNED_LD_STD LONG_ASM_CONST(0x0000000020000000) #define CPU_FTR_ASYM_SMT LONG_ASM_CONST(0x0000000040000000) @@ -435,7 +435,7 @@ static inline void cpu_feature_keys_init(void) { } CPU_FTR_MMCRA | CPU_FTR_SMT | \ CPU_FTR_COHERENT_ICACHE | \ CPU_FTR_PURR | CPU_FTR_SPURR | CPU_FTR_REAL_LE | \ - CPU_FTR_DSCR | CPU_FTR_SAO | CPU_FTR_ASYM_SMT | \ + CPU_FTR_DSCR | CPU_FTR_ASYM_SMT | \ CPU_FTR_STCX_CHECKS_ADDRESS | CPU_FTR_POPCNTB | CPU_FTR_POPCNTD | \ CPU_FTR_CFAR | CPU_FTR_HVMODE | \ CPU_FTR_VMX_COPY | CPU_FTR_HAS_PPR | CPU_FTR_DABRX ) @@ -444,7 +444,7 @@ static inline void cpu_feature_keys_init(void) { } CPU_FTR_MMCRA | CPU_FTR_SMT | \ CPU_FTR_COHERENT_ICACHE | \ CPU_FTR_PURR | CPU_FTR_SPURR | CPU_FTR_REAL_LE | \ - CPU_FTR_DSCR | CPU_FTR_SAO | \ + CPU_FTR_DSCR | \ CPU_FTR_STCX_CHECKS_ADDRESS | CPU_FTR_POPCNTB | CPU_FTR_POPCNTD | \ CPU_FTR_CFAR | CPU_FTR_HVMODE | CPU_FTR_VMX_COPY | \ CPU_FTR_DBELL | CPU_FTR_HAS_PPR | CPU_FTR_DAWR | \ @@ -455,7 +455,7 @@ static inline void cpu_feature_keys_init(void) { } CPU_FTR_MMCRA | CPU_FTR_SMT | \ CPU_FTR_COHERENT_ICACHE | \ CPU_FTR_PURR | CPU_FTR_SPURR | CPU_FTR_REAL_LE | \ - CPU_FTR_DSCR | CPU_FTR_SAO | \ + CPU_FTR_DSCR | \ CPU_FTR_STCX_CHECKS_ADDRESS | CPU_FTR_POPCNTB | CPU_FTR_POPCNTD | \ CPU_FTR_CFAR | CPU_FTR_HVMODE | CPU_FTR_VMX_COPY | \ CPU_FTR_DBELL | CPU_FTR_HAS_PPR | CPU_FTR_ARCH_207S | \ @@ -473,7 +473,7 @@ static inline void cpu_feature_keys_init(void) { } CPU_FTR_MMCRA | CPU_FTR_SMT | \ CPU_FTR_COHERENT_ICACHE | \ CPU_FTR_PURR | CPU_FTR_SPURR | CPU_FTR_REAL_LE | \ - CPU_FTR_DSCR | CPU_FTR_SAO | \ + CPU_FTR_DSCR | \ CPU_FTR_STCX_CHECKS_ADDRESS | CPU_FTR_POPCNTB | CPU_FTR_POPCNTD | \ CPU_FTR_CFAR | CPU_FTR_HVMODE | CPU_FTR_VMX_COPY | \ CPU_FTR_DBELL | CPU_FTR_HAS_PPR | CPU_FTR_ARCH_207S | \ diff --git a/arch/powerpc/include/asm/mman.h b/arch/powerpc/include/asm/mman.h index 4ba303ea27f59..7c07728af300b 100644 --- a/arch/powerpc/include/asm/mman.h +++ b/arch/powerpc/include/asm/mman.h @@ -13,38 +13,20 @@ #include #include +#ifdef CONFIG_PPC_MEM_KEYS static inline unsigned long arch_calc_vm_prot_bits(unsigned long prot, unsigned long pkey) { -#ifdef CONFIG_PPC_MEM_KEYS - return (((prot & PROT_SAO) ? VM_SAO : 0) | pkey_to_vmflag_bits(pkey)); -#else - return ((prot & PROT_SAO) ? VM_SAO : 0); -#endif + return pkey_to_vmflag_bits(pkey); } #define arch_calc_vm_prot_bits(prot, pkey) arch_calc_vm_prot_bits(prot, pkey) static inline pgprot_t arch_vm_get_page_prot(unsigned long vm_flags) { -#ifdef CONFIG_PPC_MEM_KEYS - return (vm_flags & VM_SAO) ? - __pgprot(_PAGE_SAO | vmflag_to_pte_pkey_bits(vm_flags)) : - __pgprot(0 | vmflag_to_pte_pkey_bits(vm_flags)); -#else - return (vm_flags & VM_SAO) ? __pgprot(_PAGE_SAO) : __pgprot(0); -#endif + return __pgprot(vmflag_to_pte_pkey_bits(vm_flags)); } #define arch_vm_get_page_prot(vm_flags) arch_vm_get_page_prot(vm_flags) - -static inline bool arch_validate_prot(unsigned long prot, unsigned long addr) -{ - if (prot & ~(PROT_READ | PROT_WRITE | PROT_EXEC | PROT_SEM | PROT_SAO)) - return false; - if ((prot & PROT_SAO) && !cpu_has_feature(CPU_FTR_SAO)) - return false; - return true; -} -#define arch_validate_prot arch_validate_prot +#endif #endif /* CONFIG_PPC64 */ #endif /* _ASM_POWERPC_MMAN_H */ diff --git a/arch/powerpc/include/asm/nohash/64/pgtable.h b/arch/powerpc/include/asm/nohash/64/pgtable.h index 6cb8aa3571917..59ee9fa4ae09d 100644 --- a/arch/powerpc/include/asm/nohash/64/pgtable.h +++ b/arch/powerpc/include/asm/nohash/64/pgtable.h @@ -82,8 +82,6 @@ */ #include -#define _PAGE_SAO 0 - #define PTE_RPN_MASK (~((1UL << PTE_RPN_SHIFT) - 1)) /* diff --git a/arch/powerpc/include/uapi/asm/mman.h b/arch/powerpc/include/uapi/asm/mman.h index c0c737215b00b..3a700351fecaf 100644 --- a/arch/powerpc/include/uapi/asm/mman.h +++ b/arch/powerpc/include/uapi/asm/mman.h @@ -11,7 +11,7 @@ #include -#define PROT_SAO 0x10 /* Strong Access Ordering */ +#define PROT_SAO 0x10 /* Unsupported since v5.9 */ #define MAP_RENAME MAP_ANONYMOUS /* In SunOS terminology */ #define MAP_NORESERVE 0x40 /* don't reserve swap pages */ diff --git a/arch/powerpc/kernel/dt_cpu_ftrs.c b/arch/powerpc/kernel/dt_cpu_ftrs.c index 554bec785f6a2..9aa8537f7da2d 100644 --- a/arch/powerpc/kernel/dt_cpu_ftrs.c +++ b/arch/powerpc/kernel/dt_cpu_ftrs.c @@ -622,7 +622,7 @@ static struct dt_cpu_feature_match __initdata {"processor-control-facility-v3", feat_enable_dbell, CPU_FTR_DBELL}, {"processor-utilization-of-resources-register", feat_enable_purr, 0}, {"no-execute", feat_enable, 0}, - {"strong-access-ordering", feat_enable, CPU_FTR_SAO}, + /* strong-access-ordering is unused */ {"cache-inhibited-large-page", feat_enable_large_ci, 0}, {"coprocessor-icswx", feat_enable, 0}, {"hypervisor-virtualization-interrupt", feat_enable_hvi, 0}, diff --git a/arch/powerpc/mm/book3s64/hash_utils.c b/arch/powerpc/mm/book3s64/hash_utils.c index 9dfb0ceed5e32..6f9f346a5f65a 100644 --- a/arch/powerpc/mm/book3s64/hash_utils.c +++ b/arch/powerpc/mm/book3s64/hash_utils.c @@ -232,8 +232,6 @@ unsigned long htab_convert_pte_flags(unsigned long pteflags) rflags |= HPTE_R_I; else if ((pteflags & _PAGE_CACHE_CTL) == _PAGE_NON_IDEMPOTENT) rflags |= (HPTE_R_I | HPTE_R_G); - else if ((pteflags & _PAGE_CACHE_CTL) == _PAGE_SAO) - rflags |= (HPTE_R_W | HPTE_R_I | HPTE_R_M); else /* * Add memory coherence if cache inhibited is not set diff --git a/include/linux/mm.h b/include/linux/mm.h index dc7b87310c103..6c8333d6c991a 100644 --- a/include/linux/mm.h +++ b/include/linux/mm.h @@ -317,8 +317,6 @@ extern unsigned int kobjsize(const void *objp); #if defined(CONFIG_X86) # define VM_PAT VM_ARCH_1 /* PAT reserves whole VMA at once (x86) */ -#elif defined(CONFIG_PPC) -# define VM_SAO VM_ARCH_1 /* Strong Access Ordering (powerpc) */ #elif defined(CONFIG_PARISC) # define VM_GROWSUP VM_ARCH_1 #elif defined(CONFIG_IA64) diff --git a/include/trace/events/mmflags.h b/include/trace/events/mmflags.h index 5fb7520343863..939092dbcb8bf 100644 --- a/include/trace/events/mmflags.h +++ b/include/trace/events/mmflags.h @@ -114,8 +114,6 @@ IF_HAVE_PG_IDLE(PG_idle, "idle" ) #if defined(CONFIG_X86) #define __VM_ARCH_SPECIFIC_1 {VM_PAT, "pat" } -#elif defined(CONFIG_PPC) -#define __VM_ARCH_SPECIFIC_1 {VM_SAO, "sao" } #elif defined(CONFIG_PARISC) || defined(CONFIG_IA64) #define __VM_ARCH_SPECIFIC_1 {VM_GROWSUP, "growsup" } #elif !defined(CONFIG_MMU) diff --git a/mm/ksm.c b/mm/ksm.c index 4102034cd55a1..d1cfa18689b57 100644 --- a/mm/ksm.c +++ b/mm/ksm.c @@ -2452,10 +2452,6 @@ int ksm_madvise(struct vm_area_struct *vma, unsigned long start, if (vma_is_dax(vma)) return 0; -#ifdef VM_SAO - if (*vm_flags & VM_SAO) - return 0; -#endif #ifdef VM_SPARC_ADI if (*vm_flags & VM_SPARC_ADI) return 0; diff --git a/tools/testing/selftests/powerpc/mm/.gitignore b/tools/testing/selftests/powerpc/mm/.gitignore index 8f841f925baa5..8d041f508a517 100644 --- a/tools/testing/selftests/powerpc/mm/.gitignore +++ b/tools/testing/selftests/powerpc/mm/.gitignore @@ -2,7 +2,6 @@ hugetlb_vs_thp_test subpage_prot tempfile -prot_sao segv_errors wild_bctr large_vm_fork_separation diff --git a/tools/testing/selftests/powerpc/mm/Makefile b/tools/testing/selftests/powerpc/mm/Makefile index f9fa0ba7435c4..5a86d59441dcb 100644 --- a/tools/testing/selftests/powerpc/mm/Makefile +++ b/tools/testing/selftests/powerpc/mm/Makefile @@ -2,7 +2,7 @@ noarg: $(MAKE) -C ../ -TEST_GEN_PROGS := hugetlb_vs_thp_test subpage_prot prot_sao segv_errors wild_bctr \ +TEST_GEN_PROGS := hugetlb_vs_thp_test subpage_prot segv_errors wild_bctr \ large_vm_fork_separation bad_accesses pkey_exec_prot TEST_GEN_PROGS_EXTENDED := tlbie_test TEST_GEN_FILES := tempfile @@ -12,8 +12,6 @@ include ../../lib.mk $(TEST_GEN_PROGS): ../harness.c ../utils.c -$(OUTPUT)/prot_sao: ../utils.c - $(OUTPUT)/wild_bctr: CFLAGS += -m64 $(OUTPUT)/large_vm_fork_separation: CFLAGS += -m64 $(OUTPUT)/bad_accesses: CFLAGS += -m64 diff --git a/tools/testing/selftests/powerpc/mm/prot_sao.c b/tools/testing/selftests/powerpc/mm/prot_sao.c deleted file mode 100644 index e2eed65b77359..0000000000000 --- a/tools/testing/selftests/powerpc/mm/prot_sao.c +++ /dev/null @@ -1,42 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Copyright 2016, Michael Ellerman, IBM Corp. - */ - -#include -#include -#include -#include - -#include - -#include "utils.h" - -#define SIZE (64 * 1024) - -int test_prot_sao(void) -{ - char *p; - - /* 2.06 or later should support SAO */ - SKIP_IF(!have_hwcap(PPC_FEATURE_ARCH_2_06)); - - /* - * Ensure we can ask for PROT_SAO. - * We can't really verify that it does the right thing, but at least we - * confirm the kernel will accept it. - */ - p = mmap(NULL, SIZE, PROT_READ | PROT_WRITE | PROT_SAO, - MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); - FAIL_IF(p == MAP_FAILED); - - /* Write to the mapping, to at least cause a fault */ - memset(p, 0xaa, SIZE); - - return 0; -} - -int main(void) -{ - return test_harness(test_prot_sao, "prot-sao"); -} -- GitLab From 63396ada804c676e070bd1b8663046f18698ab27 Mon Sep 17 00:00:00 2001 From: Nicholas Piggin Date: Fri, 3 Jul 2020 11:19:58 +1000 Subject: [PATCH 0750/1476] powerpc/64s/hash: Disable subpage_prot syscall by default The subpage_prot syscall was added for specialised system software (Lx86) that has been discontinued for about 7 years, and is not thought to be used elsewhere, so disable it by default. Signed-off-by: Nicholas Piggin Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20200703011958.1166620-4-npiggin@gmail.com --- arch/powerpc/Kconfig | 7 +++++-- arch/powerpc/configs/powernv_defconfig | 1 - arch/powerpc/configs/pseries_defconfig | 1 - 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/arch/powerpc/Kconfig b/arch/powerpc/Kconfig index 51abc59c3334e..70adf69f8b1a5 100644 --- a/arch/powerpc/Kconfig +++ b/arch/powerpc/Kconfig @@ -833,13 +833,16 @@ config FORCE_MAX_ZONEORDER this in mind when choosing a value for this option. config PPC_SUBPAGE_PROT - bool "Support setting protections for 4k subpages" + bool "Support setting protections for 4k subpages (subpage_prot syscall)" + default n depends on PPC_BOOK3S_64 && PPC_64K_PAGES help - This option adds support for a system call to allow user programs + This option adds support for system call to allow user programs to set access permissions (read/write, readonly, or no access) on the 4k subpages of each 64k page. + If unsure, say N here. + config PPC_COPRO_BASE bool diff --git a/arch/powerpc/configs/powernv_defconfig b/arch/powerpc/configs/powernv_defconfig index 2de9aadf0f501..afc0dd73a1e64 100644 --- a/arch/powerpc/configs/powernv_defconfig +++ b/arch/powerpc/configs/powernv_defconfig @@ -64,7 +64,6 @@ CONFIG_HWPOISON_INJECT=m CONFIG_TRANSPARENT_HUGEPAGE=y CONFIG_DEFERRED_STRUCT_PAGE_INIT=y CONFIG_PPC_64K_PAGES=y -CONFIG_PPC_SUBPAGE_PROT=y CONFIG_SCHED_SMT=y CONFIG_PM=y CONFIG_HOTPLUG_PCI=y diff --git a/arch/powerpc/configs/pseries_defconfig b/arch/powerpc/configs/pseries_defconfig index dfa4a726333bd..894e8d85fb487 100644 --- a/arch/powerpc/configs/pseries_defconfig +++ b/arch/powerpc/configs/pseries_defconfig @@ -57,7 +57,6 @@ CONFIG_MEMORY_HOTREMOVE=y CONFIG_KSM=y CONFIG_TRANSPARENT_HUGEPAGE=y CONFIG_PPC_64K_PAGES=y -CONFIG_PPC_SUBPAGE_PROT=y CONFIG_SCHED_SMT=y CONFIG_HOTPLUG_PCI=y CONFIG_HOTPLUG_PCI_RPA=m -- GitLab From 3c9450c053f88e525b2db1e6990cdf34d14e7696 Mon Sep 17 00:00:00 2001 From: Madhavan Srinivasan Date: Sun, 14 Jun 2020 14:06:04 +0530 Subject: [PATCH 0751/1476] powerpc/perf: Fix missing is_sier_aviable() during build Compilation error: arch/powerpc/perf/perf_regs.c:80:undefined reference to `.is_sier_available' Currently is_sier_available() is part of core-book3s.c, which is added to build based on CONFIG_PPC_PERF_CTRS. A config with CONFIG_PERF_EVENTS and without CONFIG_PPC_PERF_CTRS will have a build break because of missing is_sier_available(). In practice it only breaks when CONFIG_FSL_EMB_PERF_EVENT=n because that also guards the usage of is_sier_available(). That only happens with CONFIG_PPC_BOOK3E_64=y and CONFIG_FSL_SOC_BOOKE=n. Patch adds is_sier_available() in asm/perf_event.h to fix the build break for configs missing CONFIG_PPC_PERF_CTRS. Fixes: 333804dc3b7a ("powerpc/perf: Update perf_regs structure to include SIER") Reported-by: Aneesh Kumar K.V Signed-off-by: Madhavan Srinivasan [mpe: Add detail about CONFIG_FSL_SOC_BOOKE] Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20200614083604.302611-1-maddy@linux.ibm.com --- arch/powerpc/include/asm/perf_event.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/arch/powerpc/include/asm/perf_event.h b/arch/powerpc/include/asm/perf_event.h index eed3954082fa2..1e8b2e1ec1db6 100644 --- a/arch/powerpc/include/asm/perf_event.h +++ b/arch/powerpc/include/asm/perf_event.h @@ -12,6 +12,8 @@ #ifdef CONFIG_PPC_PERF_CTRS #include +#else +static inline bool is_sier_available(void) { return false; } #endif #ifdef CONFIG_FSL_EMB_PERF_EVENT -- GitLab From 78d76819e6f04672989506e7792895a51438516e Mon Sep 17 00:00:00 2001 From: Athira Rajeev Date: Fri, 17 Jul 2020 10:38:13 -0400 Subject: [PATCH 0752/1476] powerpc/perf: Update cpu_hw_event to use `struct` for storing MMCR registers core-book3s currently uses array to store the MMCR registers as part of per-cpu `cpu_hw_events`. This patch does a clean up to use `struct` to store mmcr regs instead of array. This will make code easier to read and reduces chance of any subtle bug that may come in the future, say when new registers are added. Patch updates all relevant code that was using MMCR array ( cpuhw->mmcr[x]) to use newly introduced `struct`. This includes the PMU driver code for supported platforms (power5 to power9) and ISA macros for counter support functions. Signed-off-by: Athira Rajeev Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/1594996707-3727-2-git-send-email-atrajeev@linux.vnet.ibm.com --- arch/powerpc/include/asm/perf_event_server.h | 10 +++- arch/powerpc/perf/core-book3s.c | 53 +++++++++----------- arch/powerpc/perf/isa207-common.c | 20 ++++---- arch/powerpc/perf/isa207-common.h | 4 +- arch/powerpc/perf/mpc7450-pmu.c | 21 +++++--- arch/powerpc/perf/power5+-pmu.c | 17 ++++--- arch/powerpc/perf/power5-pmu.c | 17 ++++--- arch/powerpc/perf/power6-pmu.c | 16 +++--- arch/powerpc/perf/power7-pmu.c | 17 ++++--- arch/powerpc/perf/ppc970-pmu.c | 24 ++++----- 10 files changed, 105 insertions(+), 94 deletions(-) diff --git a/arch/powerpc/include/asm/perf_event_server.h b/arch/powerpc/include/asm/perf_event_server.h index 3e9703f44c7c0..f9a36680339d1 100644 --- a/arch/powerpc/include/asm/perf_event_server.h +++ b/arch/powerpc/include/asm/perf_event_server.h @@ -17,6 +17,12 @@ struct perf_event; +struct mmcr_regs { + unsigned long mmcr0; + unsigned long mmcr1; + unsigned long mmcr2; + unsigned long mmcra; +}; /* * This struct provides the constants and functions needed to * describe the PMU on a particular POWER-family CPU. @@ -28,7 +34,7 @@ struct power_pmu { unsigned long add_fields; unsigned long test_adder; int (*compute_mmcr)(u64 events[], int n_ev, - unsigned int hwc[], unsigned long mmcr[], + unsigned int hwc[], struct mmcr_regs *mmcr, struct perf_event *pevents[]); int (*get_constraint)(u64 event_id, unsigned long *mskp, unsigned long *valp); @@ -41,7 +47,7 @@ struct power_pmu { unsigned long group_constraint_val; u64 (*bhrb_filter_map)(u64 branch_sample_type); void (*config_bhrb)(u64 pmu_bhrb_filter); - void (*disable_pmc)(unsigned int pmc, unsigned long mmcr[]); + void (*disable_pmc)(unsigned int pmc, struct mmcr_regs *mmcr); int (*limited_pmc_event)(u64 event_id); u32 flags; const struct attribute_group **attr_groups; diff --git a/arch/powerpc/perf/core-book3s.c b/arch/powerpc/perf/core-book3s.c index cd6a742ac6ef5..18b1b6aae692f 100644 --- a/arch/powerpc/perf/core-book3s.c +++ b/arch/powerpc/perf/core-book3s.c @@ -37,12 +37,7 @@ struct cpu_hw_events { struct perf_event *event[MAX_HWEVENTS]; u64 events[MAX_HWEVENTS]; unsigned int flags[MAX_HWEVENTS]; - /* - * The order of the MMCR array is: - * - 64-bit, MMCR0, MMCR1, MMCRA, MMCR2 - * - 32-bit, MMCR0, MMCR1, MMCR2 - */ - unsigned long mmcr[4]; + struct mmcr_regs mmcr; struct perf_event *limited_counter[MAX_LIMITED_HWCOUNTERS]; u8 limited_hwidx[MAX_LIMITED_HWCOUNTERS]; u64 alternatives[MAX_HWEVENTS][MAX_EVENT_ALTERNATIVES]; @@ -121,7 +116,7 @@ static void ebb_event_add(struct perf_event *event) { } static void ebb_switch_out(unsigned long mmcr0) { } static unsigned long ebb_switch_in(bool ebb, struct cpu_hw_events *cpuhw) { - return cpuhw->mmcr[0]; + return cpuhw->mmcr.mmcr0; } static inline void power_pmu_bhrb_enable(struct perf_event *event) {} @@ -590,7 +585,7 @@ static void ebb_switch_out(unsigned long mmcr0) static unsigned long ebb_switch_in(bool ebb, struct cpu_hw_events *cpuhw) { - unsigned long mmcr0 = cpuhw->mmcr[0]; + unsigned long mmcr0 = cpuhw->mmcr.mmcr0; if (!ebb) goto out; @@ -624,7 +619,7 @@ static unsigned long ebb_switch_in(bool ebb, struct cpu_hw_events *cpuhw) * unfreeze counters, it should not set exclude_xxx in its events and * instead manage the MMCR2 entirely by itself. */ - mtspr(SPRN_MMCR2, cpuhw->mmcr[3] | current->thread.mmcr2); + mtspr(SPRN_MMCR2, cpuhw->mmcr.mmcr2 | current->thread.mmcr2); out: return mmcr0; } @@ -1232,9 +1227,9 @@ static void power_pmu_disable(struct pmu *pmu) /* * Disable instruction sampling if it was enabled */ - if (cpuhw->mmcr[2] & MMCRA_SAMPLE_ENABLE) { + if (cpuhw->mmcr.mmcra & MMCRA_SAMPLE_ENABLE) { mtspr(SPRN_MMCRA, - cpuhw->mmcr[2] & ~MMCRA_SAMPLE_ENABLE); + cpuhw->mmcr.mmcra & ~MMCRA_SAMPLE_ENABLE); mb(); isync(); } @@ -1308,18 +1303,18 @@ static void power_pmu_enable(struct pmu *pmu) * (possibly updated for removal of events). */ if (!cpuhw->n_added) { - mtspr(SPRN_MMCRA, cpuhw->mmcr[2] & ~MMCRA_SAMPLE_ENABLE); - mtspr(SPRN_MMCR1, cpuhw->mmcr[1]); + mtspr(SPRN_MMCRA, cpuhw->mmcr.mmcra & ~MMCRA_SAMPLE_ENABLE); + mtspr(SPRN_MMCR1, cpuhw->mmcr.mmcr1); goto out_enable; } /* * Clear all MMCR settings and recompute them for the new set of events. */ - memset(cpuhw->mmcr, 0, sizeof(cpuhw->mmcr)); + memset(&cpuhw->mmcr, 0, sizeof(cpuhw->mmcr)); if (ppmu->compute_mmcr(cpuhw->events, cpuhw->n_events, hwc_index, - cpuhw->mmcr, cpuhw->event)) { + &cpuhw->mmcr, cpuhw->event)) { /* shouldn't ever get here */ printk(KERN_ERR "oops compute_mmcr failed\n"); goto out; @@ -1333,11 +1328,11 @@ static void power_pmu_enable(struct pmu *pmu) */ event = cpuhw->event[0]; if (event->attr.exclude_user) - cpuhw->mmcr[0] |= MMCR0_FCP; + cpuhw->mmcr.mmcr0 |= MMCR0_FCP; if (event->attr.exclude_kernel) - cpuhw->mmcr[0] |= freeze_events_kernel; + cpuhw->mmcr.mmcr0 |= freeze_events_kernel; if (event->attr.exclude_hv) - cpuhw->mmcr[0] |= MMCR0_FCHV; + cpuhw->mmcr.mmcr0 |= MMCR0_FCHV; } /* @@ -1346,12 +1341,12 @@ static void power_pmu_enable(struct pmu *pmu) * Then unfreeze the events. */ ppc_set_pmu_inuse(1); - mtspr(SPRN_MMCRA, cpuhw->mmcr[2] & ~MMCRA_SAMPLE_ENABLE); - mtspr(SPRN_MMCR1, cpuhw->mmcr[1]); - mtspr(SPRN_MMCR0, (cpuhw->mmcr[0] & ~(MMCR0_PMC1CE | MMCR0_PMCjCE)) + mtspr(SPRN_MMCRA, cpuhw->mmcr.mmcra & ~MMCRA_SAMPLE_ENABLE); + mtspr(SPRN_MMCR1, cpuhw->mmcr.mmcr1); + mtspr(SPRN_MMCR0, (cpuhw->mmcr.mmcr0 & ~(MMCR0_PMC1CE | MMCR0_PMCjCE)) | MMCR0_FC); if (ppmu->flags & PPMU_ARCH_207S) - mtspr(SPRN_MMCR2, cpuhw->mmcr[3]); + mtspr(SPRN_MMCR2, cpuhw->mmcr.mmcr2); /* * Read off any pre-existing events that need to move @@ -1402,7 +1397,7 @@ static void power_pmu_enable(struct pmu *pmu) perf_event_update_userpage(event); } cpuhw->n_limited = n_lim; - cpuhw->mmcr[0] |= MMCR0_PMXE | MMCR0_FCECE; + cpuhw->mmcr.mmcr0 |= MMCR0_PMXE | MMCR0_FCECE; out_enable: pmao_restore_workaround(ebb); @@ -1418,9 +1413,9 @@ static void power_pmu_enable(struct pmu *pmu) /* * Enable instruction sampling if necessary */ - if (cpuhw->mmcr[2] & MMCRA_SAMPLE_ENABLE) { + if (cpuhw->mmcr.mmcra & MMCRA_SAMPLE_ENABLE) { mb(); - mtspr(SPRN_MMCRA, cpuhw->mmcr[2]); + mtspr(SPRN_MMCRA, cpuhw->mmcr.mmcra); } out: @@ -1550,7 +1545,7 @@ static void power_pmu_del(struct perf_event *event, int ef_flags) cpuhw->flags[i-1] = cpuhw->flags[i]; } --cpuhw->n_events; - ppmu->disable_pmc(event->hw.idx - 1, cpuhw->mmcr); + ppmu->disable_pmc(event->hw.idx - 1, &cpuhw->mmcr); if (event->hw.idx) { write_pmc(event->hw.idx, 0); event->hw.idx = 0; @@ -1571,7 +1566,7 @@ static void power_pmu_del(struct perf_event *event, int ef_flags) } if (cpuhw->n_events == 0) { /* disable exceptions if no events are running */ - cpuhw->mmcr[0] &= ~(MMCR0_PMXE | MMCR0_FCECE); + cpuhw->mmcr.mmcr0 &= ~(MMCR0_PMXE | MMCR0_FCECE); } if (has_branch_stack(event)) @@ -2240,7 +2235,7 @@ static void __perf_event_interrupt(struct pt_regs *regs) * XXX might want to use MSR.PM to keep the events frozen until * we get back out of this interrupt. */ - write_mmcr0(cpuhw, cpuhw->mmcr[0]); + write_mmcr0(cpuhw, cpuhw->mmcr.mmcr0); if (nmi) nmi_exit(); @@ -2262,7 +2257,7 @@ static int power_pmu_prepare_cpu(unsigned int cpu) if (ppmu) { memset(cpuhw, 0, sizeof(*cpuhw)); - cpuhw->mmcr[0] = MMCR0_FC; + cpuhw->mmcr.mmcr0 = MMCR0_FC; } return 0; } diff --git a/arch/powerpc/perf/isa207-common.c b/arch/powerpc/perf/isa207-common.c index 4c86da5eb28ab..2fe63f2de4b09 100644 --- a/arch/powerpc/perf/isa207-common.c +++ b/arch/powerpc/perf/isa207-common.c @@ -363,7 +363,7 @@ int isa207_get_constraint(u64 event, unsigned long *maskp, unsigned long *valp) } int isa207_compute_mmcr(u64 event[], int n_ev, - unsigned int hwc[], unsigned long mmcr[], + unsigned int hwc[], struct mmcr_regs *mmcr, struct perf_event *pevents[]) { unsigned long mmcra, mmcr1, mmcr2, unit, combine, psel, cache, val; @@ -464,30 +464,30 @@ int isa207_compute_mmcr(u64 event[], int n_ev, } /* Return MMCRx values */ - mmcr[0] = 0; + mmcr->mmcr0 = 0; /* pmc_inuse is 1-based */ if (pmc_inuse & 2) - mmcr[0] = MMCR0_PMC1CE; + mmcr->mmcr0 = MMCR0_PMC1CE; if (pmc_inuse & 0x7c) - mmcr[0] |= MMCR0_PMCjCE; + mmcr->mmcr0 |= MMCR0_PMCjCE; /* If we're not using PMC 5 or 6, freeze them */ if (!(pmc_inuse & 0x60)) - mmcr[0] |= MMCR0_FC56; + mmcr->mmcr0 |= MMCR0_FC56; - mmcr[1] = mmcr1; - mmcr[2] = mmcra; - mmcr[3] = mmcr2; + mmcr->mmcr1 = mmcr1; + mmcr->mmcra = mmcra; + mmcr->mmcr2 = mmcr2; return 0; } -void isa207_disable_pmc(unsigned int pmc, unsigned long mmcr[]) +void isa207_disable_pmc(unsigned int pmc, struct mmcr_regs *mmcr) { if (pmc <= 3) - mmcr[1] &= ~(0xffUL << MMCR1_PMCSEL_SHIFT(pmc + 1)); + mmcr->mmcr1 &= ~(0xffUL << MMCR1_PMCSEL_SHIFT(pmc + 1)); } static int find_alternative(u64 event, const unsigned int ev_alt[][MAX_ALT], int size) diff --git a/arch/powerpc/perf/isa207-common.h b/arch/powerpc/perf/isa207-common.h index 63fd4f3f60138..df968fde65a1f 100644 --- a/arch/powerpc/perf/isa207-common.h +++ b/arch/powerpc/perf/isa207-common.h @@ -217,9 +217,9 @@ int isa207_get_constraint(u64 event, unsigned long *maskp, unsigned long *valp); int isa207_compute_mmcr(u64 event[], int n_ev, - unsigned int hwc[], unsigned long mmcr[], + unsigned int hwc[], struct mmcr_regs *mmcr, struct perf_event *pevents[]); -void isa207_disable_pmc(unsigned int pmc, unsigned long mmcr[]); +void isa207_disable_pmc(unsigned int pmc, struct mmcr_regs *mmcr); int isa207_get_alternatives(u64 event, u64 alt[], int size, unsigned int flags, const unsigned int ev_alt[][MAX_ALT]); void isa207_get_mem_data_src(union perf_mem_data_src *dsrc, u32 flags, diff --git a/arch/powerpc/perf/mpc7450-pmu.c b/arch/powerpc/perf/mpc7450-pmu.c index 4d5ef92511d19..826de253aa4dc 100644 --- a/arch/powerpc/perf/mpc7450-pmu.c +++ b/arch/powerpc/perf/mpc7450-pmu.c @@ -257,7 +257,7 @@ static const u32 pmcsel_mask[N_COUNTER] = { * Compute MMCR0/1/2 values for a set of events. */ static int mpc7450_compute_mmcr(u64 event[], int n_ev, unsigned int hwc[], - unsigned long mmcr[], + struct mmcr_regs *mmcr, struct perf_event *pevents[]) { u8 event_index[N_CLASSES][N_COUNTER]; @@ -321,9 +321,16 @@ static int mpc7450_compute_mmcr(u64 event[], int n_ev, unsigned int hwc[], mmcr0 |= MMCR0_PMCnCE; /* Return MMCRx values */ - mmcr[0] = mmcr0; - mmcr[1] = mmcr1; - mmcr[2] = mmcr2; + mmcr->mmcr0 = mmcr0; + mmcr->mmcr1 = mmcr1; + mmcr->mmcr2 = mmcr2; + /* + * 32-bit doesn't have an MMCRA and uses SPRN_MMCR2 to define + * SPRN_MMCRA. So assign mmcra of cpu_hw_events with `mmcr2` + * value to ensure that any write to this SPRN_MMCRA will + * use mmcr2 value. + */ + mmcr->mmcra = mmcr2; return 0; } @@ -331,12 +338,12 @@ static int mpc7450_compute_mmcr(u64 event[], int n_ev, unsigned int hwc[], * Disable counting by a PMC. * Note that the pmc argument is 0-based here, not 1-based. */ -static void mpc7450_disable_pmc(unsigned int pmc, unsigned long mmcr[]) +static void mpc7450_disable_pmc(unsigned int pmc, struct mmcr_regs *mmcr) { if (pmc <= 1) - mmcr[0] &= ~(pmcsel_mask[pmc] << pmcsel_shift[pmc]); + mmcr->mmcr0 &= ~(pmcsel_mask[pmc] << pmcsel_shift[pmc]); else - mmcr[1] &= ~(pmcsel_mask[pmc] << pmcsel_shift[pmc]); + mmcr->mmcr1 &= ~(pmcsel_mask[pmc] << pmcsel_shift[pmc]); } static int mpc7450_generic_events[] = { diff --git a/arch/powerpc/perf/power5+-pmu.c b/arch/powerpc/perf/power5+-pmu.c index f8574547fc6b2..5f0821e54f042 100644 --- a/arch/powerpc/perf/power5+-pmu.c +++ b/arch/powerpc/perf/power5+-pmu.c @@ -448,7 +448,8 @@ static int power5p_marked_instr_event(u64 event) } static int power5p_compute_mmcr(u64 event[], int n_ev, - unsigned int hwc[], unsigned long mmcr[], struct perf_event *pevents[]) + unsigned int hwc[], struct mmcr_regs *mmcr, + struct perf_event *pevents[]) { unsigned long mmcr1 = 0; unsigned long mmcra = 0; @@ -586,20 +587,20 @@ static int power5p_compute_mmcr(u64 event[], int n_ev, } /* Return MMCRx values */ - mmcr[0] = 0; + mmcr->mmcr0 = 0; if (pmc_inuse & 1) - mmcr[0] = MMCR0_PMC1CE; + mmcr->mmcr0 = MMCR0_PMC1CE; if (pmc_inuse & 0x3e) - mmcr[0] |= MMCR0_PMCjCE; - mmcr[1] = mmcr1; - mmcr[2] = mmcra; + mmcr->mmcr0 |= MMCR0_PMCjCE; + mmcr->mmcr1 = mmcr1; + mmcr->mmcra = mmcra; return 0; } -static void power5p_disable_pmc(unsigned int pmc, unsigned long mmcr[]) +static void power5p_disable_pmc(unsigned int pmc, struct mmcr_regs *mmcr) { if (pmc <= 3) - mmcr[1] &= ~(0x7fUL << MMCR1_PMCSEL_SH(pmc)); + mmcr->mmcr1 &= ~(0x7fUL << MMCR1_PMCSEL_SH(pmc)); } static int power5p_generic_events[] = { diff --git a/arch/powerpc/perf/power5-pmu.c b/arch/powerpc/perf/power5-pmu.c index da52ecab7c9aa..426021d4b44c3 100644 --- a/arch/powerpc/perf/power5-pmu.c +++ b/arch/powerpc/perf/power5-pmu.c @@ -379,7 +379,8 @@ static int power5_marked_instr_event(u64 event) } static int power5_compute_mmcr(u64 event[], int n_ev, - unsigned int hwc[], unsigned long mmcr[], struct perf_event *pevents[]) + unsigned int hwc[], struct mmcr_regs *mmcr, + struct perf_event *pevents[]) { unsigned long mmcr1 = 0; unsigned long mmcra = MMCRA_SDAR_DCACHE_MISS | MMCRA_SDAR_ERAT_MISS; @@ -528,20 +529,20 @@ static int power5_compute_mmcr(u64 event[], int n_ev, } /* Return MMCRx values */ - mmcr[0] = 0; + mmcr->mmcr0 = 0; if (pmc_inuse & 1) - mmcr[0] = MMCR0_PMC1CE; + mmcr->mmcr0 = MMCR0_PMC1CE; if (pmc_inuse & 0x3e) - mmcr[0] |= MMCR0_PMCjCE; - mmcr[1] = mmcr1; - mmcr[2] = mmcra; + mmcr->mmcr0 |= MMCR0_PMCjCE; + mmcr->mmcr1 = mmcr1; + mmcr->mmcra = mmcra; return 0; } -static void power5_disable_pmc(unsigned int pmc, unsigned long mmcr[]) +static void power5_disable_pmc(unsigned int pmc, struct mmcr_regs *mmcr) { if (pmc <= 3) - mmcr[1] &= ~(0x7fUL << MMCR1_PMCSEL_SH(pmc)); + mmcr->mmcr1 &= ~(0x7fUL << MMCR1_PMCSEL_SH(pmc)); } static int power5_generic_events[] = { diff --git a/arch/powerpc/perf/power6-pmu.c b/arch/powerpc/perf/power6-pmu.c index 3929cacf72ed7..e343a51a989eb 100644 --- a/arch/powerpc/perf/power6-pmu.c +++ b/arch/powerpc/perf/power6-pmu.c @@ -171,7 +171,7 @@ static int power6_marked_instr_event(u64 event) * Assign PMC numbers and compute MMCR1 value for a set of events */ static int p6_compute_mmcr(u64 event[], int n_ev, - unsigned int hwc[], unsigned long mmcr[], struct perf_event *pevents[]) + unsigned int hwc[], struct mmcr_regs *mmcr, struct perf_event *pevents[]) { unsigned long mmcr1 = 0; unsigned long mmcra = MMCRA_SDAR_DCACHE_MISS | MMCRA_SDAR_ERAT_MISS; @@ -243,13 +243,13 @@ static int p6_compute_mmcr(u64 event[], int n_ev, if (pmc < 4) mmcr1 |= (unsigned long)psel << MMCR1_PMCSEL_SH(pmc); } - mmcr[0] = 0; + mmcr->mmcr0 = 0; if (pmc_inuse & 1) - mmcr[0] = MMCR0_PMC1CE; + mmcr->mmcr0 = MMCR0_PMC1CE; if (pmc_inuse & 0xe) - mmcr[0] |= MMCR0_PMCjCE; - mmcr[1] = mmcr1; - mmcr[2] = mmcra; + mmcr->mmcr0 |= MMCR0_PMCjCE; + mmcr->mmcr1 = mmcr1; + mmcr->mmcra = mmcra; return 0; } @@ -457,11 +457,11 @@ static int p6_get_alternatives(u64 event, unsigned int flags, u64 alt[]) return nalt; } -static void p6_disable_pmc(unsigned int pmc, unsigned long mmcr[]) +static void p6_disable_pmc(unsigned int pmc, struct mmcr_regs *mmcr) { /* Set PMCxSEL to 0 to disable PMCx */ if (pmc <= 3) - mmcr[1] &= ~(0xffUL << MMCR1_PMCSEL_SH(pmc)); + mmcr->mmcr1 &= ~(0xffUL << MMCR1_PMCSEL_SH(pmc)); } static int power6_generic_events[] = { diff --git a/arch/powerpc/perf/power7-pmu.c b/arch/powerpc/perf/power7-pmu.c index a137813a3076d..315233691314a 100644 --- a/arch/powerpc/perf/power7-pmu.c +++ b/arch/powerpc/perf/power7-pmu.c @@ -242,7 +242,8 @@ static int power7_marked_instr_event(u64 event) } static int power7_compute_mmcr(u64 event[], int n_ev, - unsigned int hwc[], unsigned long mmcr[], struct perf_event *pevents[]) + unsigned int hwc[], struct mmcr_regs *mmcr, + struct perf_event *pevents[]) { unsigned long mmcr1 = 0; unsigned long mmcra = MMCRA_SDAR_DCACHE_MISS | MMCRA_SDAR_ERAT_MISS; @@ -298,20 +299,20 @@ static int power7_compute_mmcr(u64 event[], int n_ev, } /* Return MMCRx values */ - mmcr[0] = 0; + mmcr->mmcr0 = 0; if (pmc_inuse & 1) - mmcr[0] = MMCR0_PMC1CE; + mmcr->mmcr0 = MMCR0_PMC1CE; if (pmc_inuse & 0x3e) - mmcr[0] |= MMCR0_PMCjCE; - mmcr[1] = mmcr1; - mmcr[2] = mmcra; + mmcr->mmcr0 |= MMCR0_PMCjCE; + mmcr->mmcr1 = mmcr1; + mmcr->mmcra = mmcra; return 0; } -static void power7_disable_pmc(unsigned int pmc, unsigned long mmcr[]) +static void power7_disable_pmc(unsigned int pmc, struct mmcr_regs *mmcr) { if (pmc <= 3) - mmcr[1] &= ~(0xffUL << MMCR1_PMCSEL_SH(pmc)); + mmcr->mmcr1 &= ~(0xffUL << MMCR1_PMCSEL_SH(pmc)); } static int power7_generic_events[] = { diff --git a/arch/powerpc/perf/ppc970-pmu.c b/arch/powerpc/perf/ppc970-pmu.c index 4035d93d87ab0..89a90ab6a6c87 100644 --- a/arch/powerpc/perf/ppc970-pmu.c +++ b/arch/powerpc/perf/ppc970-pmu.c @@ -253,7 +253,8 @@ static int p970_get_alternatives(u64 event, unsigned int flags, u64 alt[]) } static int p970_compute_mmcr(u64 event[], int n_ev, - unsigned int hwc[], unsigned long mmcr[], struct perf_event *pevents[]) + unsigned int hwc[], struct mmcr_regs *mmcr, + struct perf_event *pevents[]) { unsigned long mmcr0 = 0, mmcr1 = 0, mmcra = 0; unsigned int pmc, unit, byte, psel; @@ -393,27 +394,26 @@ static int p970_compute_mmcr(u64 event[], int n_ev, mmcra |= 0x2000; /* mark only one IOP per PPC instruction */ /* Return MMCRx values */ - mmcr[0] = mmcr0; - mmcr[1] = mmcr1; - mmcr[2] = mmcra; + mmcr->mmcr0 = mmcr0; + mmcr->mmcr1 = mmcr1; + mmcr->mmcra = mmcra; return 0; } -static void p970_disable_pmc(unsigned int pmc, unsigned long mmcr[]) +static void p970_disable_pmc(unsigned int pmc, struct mmcr_regs *mmcr) { - int shift, i; + int shift; + /* + * Setting the PMCxSEL field to 0x08 disables PMC x. + */ if (pmc <= 1) { shift = MMCR0_PMC1SEL_SH - 7 * pmc; - i = 0; + mmcr->mmcr0 = (mmcr->mmcr0 & ~(0x1fUL << shift)) | (0x08UL << shift); } else { shift = MMCR1_PMC3SEL_SH - 5 * (pmc - 2); - i = 1; + mmcr->mmcr1 = (mmcr->mmcr1 & ~(0x1fUL << shift)) | (0x08UL << shift); } - /* - * Setting the PMCxSEL field to 0x08 disables PMC x. - */ - mmcr[i] = (mmcr[i] & ~(0x1fUL << shift)) | (0x08UL << shift); } static int ppc970_generic_events[] = { -- GitLab From 98cc1b93724aa85bd828269855d48d884c702726 Mon Sep 17 00:00:00 2001 From: Dan Murphy Date: Mon, 20 Jul 2020 15:43:57 -0500 Subject: [PATCH 0753/1476] power_supply: Add additional health properties to the header Add HEALTH_WARM, HEALTH_COOL and HEALTH_HOT to the health enum. HEALTH_WARM, HEALTH_COOL, and HEALTH_HOT properties are taken from JEITA specification JISC8712:2015 Acked-by: Andrew F. Davis Tested-by: Guru Das Srinagesh Signed-off-by: Dan Murphy Signed-off-by: Sebastian Reichel --- Documentation/ABI/testing/sysfs-class-power | 3 ++- drivers/power/supply/power_supply_sysfs.c | 3 +++ include/linux/power_supply.h | 3 +++ 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/Documentation/ABI/testing/sysfs-class-power b/Documentation/ABI/testing/sysfs-class-power index 216d61a22f1e7..40213c73bc9c4 100644 --- a/Documentation/ABI/testing/sysfs-class-power +++ b/Documentation/ABI/testing/sysfs-class-power @@ -205,7 +205,8 @@ Description: Valid values: "Unknown", "Good", "Overheat", "Dead", "Over voltage", "Unspecified failure", "Cold", "Watchdog timer expire", "Safety timer expire", - "Over current", "Calibration required" + "Over current", "Calibration required", "Warm", + "Cool", "Hot" What: /sys/class/power_supply//precharge_current Date: June 2017 diff --git a/drivers/power/supply/power_supply_sysfs.c b/drivers/power/supply/power_supply_sysfs.c index af0cad253f521..b903cb4dca2be 100644 --- a/drivers/power/supply/power_supply_sysfs.c +++ b/drivers/power/supply/power_supply_sysfs.c @@ -101,6 +101,9 @@ static const char * const POWER_SUPPLY_HEALTH_TEXT[] = { [POWER_SUPPLY_HEALTH_SAFETY_TIMER_EXPIRE] = "Safety timer expire", [POWER_SUPPLY_HEALTH_OVERCURRENT] = "Over current", [POWER_SUPPLY_HEALTH_CALIBRATION_REQUIRED] = "Calibration required", + [POWER_SUPPLY_HEALTH_WARM] = "Warm", + [POWER_SUPPLY_HEALTH_COOL] = "Cool", + [POWER_SUPPLY_HEALTH_HOT] = "Hot", }; static const char * const POWER_SUPPLY_TECHNOLOGY_TEXT[] = { diff --git a/include/linux/power_supply.h b/include/linux/power_supply.h index ac1345a48ad05..b5ee35d3c3046 100644 --- a/include/linux/power_supply.h +++ b/include/linux/power_supply.h @@ -62,6 +62,9 @@ enum { POWER_SUPPLY_HEALTH_SAFETY_TIMER_EXPIRE, POWER_SUPPLY_HEALTH_OVERCURRENT, POWER_SUPPLY_HEALTH_CALIBRATION_REQUIRED, + POWER_SUPPLY_HEALTH_WARM, + POWER_SUPPLY_HEALTH_COOL, + POWER_SUPPLY_HEALTH_HOT, }; enum { -- GitLab From 8f7bda7a666df40d8e6d590159c30498795f413a Mon Sep 17 00:00:00 2001 From: Ricardo Rivera-Matos Date: Mon, 20 Jul 2020 15:43:59 -0500 Subject: [PATCH 0754/1476] dt-bindings: power: Add the bindings for the bq2515x family of chargers. The BQ2515X family of devices are highly integrated battery management ICs that integrate the most common functions for wearable devices namely a charger, an output voltage rail, ADC for battery and system monitoring, and a push-button controller. Datasheets: http://www.ti.com/lit/ds/symlink/bq25150.pdf http://www.ti.com/lit/ds/symlink/bq25155.pdf Reviewed-by: Rob Herring Signed-off-by: Ricardo Rivera-Matos Signed-off-by: Sebastian Reichel --- .../bindings/power/supply/bq2515x.yaml | 93 +++++++++++++++++++ 1 file changed, 93 insertions(+) create mode 100644 Documentation/devicetree/bindings/power/supply/bq2515x.yaml diff --git a/Documentation/devicetree/bindings/power/supply/bq2515x.yaml b/Documentation/devicetree/bindings/power/supply/bq2515x.yaml new file mode 100644 index 0000000000000..75a56773be4a0 --- /dev/null +++ b/Documentation/devicetree/bindings/power/supply/bq2515x.yaml @@ -0,0 +1,93 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +# Copyright (C) 2020 Texas Instruments Incorporated +%YAML 1.2 +--- +$id: "http://devicetree.org/schemas/power/supply/bq2515x.yaml#" +$schema: "http://devicetree.org/meta-schemas/core.yaml#" + +title: TI bq2515x 500-mA Linear charger family + +maintainers: + - Dan Murphy + - Ricardo Rivera-Matos + +description: | + The BQ2515x family is a highly integrated battery charge management IC that + integrates the most common functions for wearable devices, namely a charger, + an output voltage rail, ADC for battery and system monitoring, and + push-button controller. + + Specifications about the charger can be found at: + http://www.ti.com/lit/ds/symlink/bq25150.pdf + http://www.ti.com/lit/ds/symlink/bq25155.pdf + +properties: + compatible: + enum: + - ti,bq25150 + - ti,bq25155 + + reg: + maxItems: 1 + description: I2C address of the charger. + + ac-detect-gpios: + description: | + GPIO used for connecting the bq2515x device PG (AC Detect) + pin. + maxItems: 1 + + reset-gpios: + description: GPIO used for hardware reset. + maxItems: 1 + + powerdown-gpios: + description: GPIO used for low power mode of IC. + maxItems: 1 + + charge-enable-gpios: + description: GPIO used to turn on and off charging. + maxItems: 1 + + input-current-limit-microamp: + $ref: /schemas/types.yaml#/definitions/uint32 + description: Maximum input current in micro Amps. + minimum: 50000 + maximum: 500000 + + monitored-battery: + $ref: /schemas/types.yaml#/definitions/phandle + description: phandle to the battery node being monitored + +required: + - compatible + - reg + - monitored-battery + +additionalProperties: false + +examples: + - | + bat: battery { + compatible = "simple-battery"; + constant-charge-current-max-microamp = <50000>; + precharge-current-microamp = <2500>; + constant-charge-voltage-max-microvolt = <4000000>; + }; + #include + i2c0 { + #address-cells = <1>; + #size-cells = <0>; + + bq25150: charger@6b { + compatible = "ti,bq25150"; + reg = <0x6b>; + monitored-battery = <&bat>; + input-current-limit-microamp = <100000>; + + ac-detect-gpios = <&gpio1 28 GPIO_ACTIVE_HIGH>; + reset-gpios = <&gpio0 14 GPIO_ACTIVE_HIGH>; + powerdown-gpios = <&gpio0 15 GPIO_ACTIVE_HIGH>; + charge-enable-gpios = <&gpio0 13 GPIO_ACTIVE_LOW>; + }; + }; -- GitLab From 44908459275e056bf43054d503adee7e3a6c9f2f Mon Sep 17 00:00:00 2001 From: Ricardo Rivera-Matos Date: Mon, 20 Jul 2020 15:44:00 -0500 Subject: [PATCH 0755/1476] power: supply: bq25150 introduce the bq25150 Introduce the bq2515x family of chargers. The BQ2515X family of devices are highly integrated battery management ICs that integrate the most common functions for wearable devices namely a charger, an output voltage rail, ADC for battery and system monitoring, and a push-button controller. Datasheets: bq25150 - http://www.ti.com/lit/ds/symlink/bq25150.pdf bq25155 - http://www.ti.com/lit/ds/symlink/bq25155.pdf Signed-off-by: Ricardo Rivera-Matos Signed-off-by: Sebastian Reichel --- drivers/power/supply/Kconfig | 13 + drivers/power/supply/Makefile | 1 + drivers/power/supply/bq2515x_charger.c | 1169 ++++++++++++++++++++++++ 3 files changed, 1183 insertions(+) create mode 100644 drivers/power/supply/bq2515x_charger.c diff --git a/drivers/power/supply/Kconfig b/drivers/power/supply/Kconfig index 44d3c8512fb8d..faf2830aa1527 100644 --- a/drivers/power/supply/Kconfig +++ b/drivers/power/supply/Kconfig @@ -610,6 +610,19 @@ config CHARGER_BQ24735 help Say Y to enable support for the TI BQ24735 battery charger. +config CHARGER_BQ2515X + tristate "TI BQ2515X battery charger family" + depends on I2C + depends on GPIOLIB || COMPILE_TEST + select REGMAP_I2C + help + Say Y to enable support for the TI BQ2515X family of battery + charging integrated circuits. The BQ2515X are highly integrated + battery charge management ICs that integrate the most common + functions for wearable devices, namely a charger, an output voltage + rail, ADC for battery and system monitoring, and push-button + controller. + config CHARGER_BQ25890 tristate "TI BQ25890 battery charger driver" depends on I2C diff --git a/drivers/power/supply/Makefile b/drivers/power/supply/Makefile index b9644663e435c..b3c694a651144 100644 --- a/drivers/power/supply/Makefile +++ b/drivers/power/supply/Makefile @@ -82,6 +82,7 @@ obj-$(CONFIG_CHARGER_BQ2415X) += bq2415x_charger.o obj-$(CONFIG_CHARGER_BQ24190) += bq24190_charger.o obj-$(CONFIG_CHARGER_BQ24257) += bq24257_charger.o obj-$(CONFIG_CHARGER_BQ24735) += bq24735-charger.o +obj-$(CONFIG_CHARGER_BQ2515X) += bq2515x_charger.o obj-$(CONFIG_CHARGER_BQ25890) += bq25890_charger.o obj-$(CONFIG_CHARGER_SMB347) += smb347-charger.o obj-$(CONFIG_CHARGER_TPS65090) += tps65090-charger.o diff --git a/drivers/power/supply/bq2515x_charger.c b/drivers/power/supply/bq2515x_charger.c new file mode 100644 index 0000000000000..63e8e317af56c --- /dev/null +++ b/drivers/power/supply/bq2515x_charger.c @@ -0,0 +1,1169 @@ +// SPDX-License-Identifier: GPL-2.0 +// BQ2515X Battery Charger Driver +// Copyright (C) 2020 Texas Instruments Incorporated - http://www.ti.com/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define BQ2515X_MANUFACTURER "Texas Instruments" + +#define BQ2515X_STAT0 0x00 +#define BQ2515X_STAT1 0x01 +#define BQ2515X_STAT2 0x02 +#define BQ2515X_FLAG0 0x03 +#define BQ2515X_FLAG1 0x04 +#define BQ2515X_FLAG2 0x05 +#define BQ2515X_FLAG3 0x06 +#define BQ2515X_MASK0 0x07 +#define BQ2515X_MASK1 0x08 +#define BQ2515X_MASK2 0x09 +#define BQ2515X_MASK3 0x0a +#define BQ2515X_VBAT_CTRL 0x12 +#define BQ2515X_ICHG_CTRL 0x13 +#define BQ2515X_PCHRGCTRL 0x14 +#define BQ2515X_TERMCTRL 0x15 +#define BQ2515X_BUVLO 0x16 +#define BQ2515X_CHARGERCTRL0 0x17 +#define BQ2515X_CHARGERCTRL1 0x18 +#define BQ2515X_ILIMCTRL 0x19 +#define BQ2515X_LDOCTRL 0x1d +#define BQ2515X_MRCTRL 0x30 +#define BQ2515X_ICCTRL0 0x35 +#define BQ2515X_ICCTRL1 0x36 +#define BQ2515X_ICCTRL2 0x37 +#define BQ2515X_ADCCTRL0 0x40 +#define BQ2515X_ADCCTRL1 0x41 +#define BQ2515X_ADC_VBAT_M 0x42 +#define BQ2515X_ADC_VBAT_L 0x43 +#define BQ2515X_ADC_TS_M 0x44 +#define BQ2515X_ADC_TS_L 0x45 +#define BQ2515X_ADC_ICHG_M 0x46 +#define BQ2515X_ADC_ICHG_L 0x47 +#define BQ2515X_ADC_ADCIN_M 0x48 +#define BQ2515X_ADC_ADCIN_L 0x49 +#define BQ2515X_ADC_VIN_M 0x4a +#define BQ2515X_ADC_VIN_L 0x4b +#define BQ2515X_ADC_PMID_M 0x4c +#define BQ2515X_ADC_PMID_L 0x4d +#define BQ2515X_ADC_IIN_M 0x4e +#define BQ2515X_ADC_IIN_L 0x4f +#define BQ2515X_ADC_COMP1_M 0x52 +#define BQ2515X_ADC_COMP1_L 0X53 +#define BQ2515X_ADC_COMP2_M 0X54 +#define BQ2515X_ADC_COMP2_L 0x55 +#define BQ2515X_ADC_COMP3_M 0x56 +#define BQ2515X_ADC_COMP3_L 0x57 +#define BQ2515X_ADC_READ_EN 0x58 +#define BQ2515X_TS_FASTCHGCTRL 0x61 +#define BQ2515X_TS_COLD 0x62 +#define BQ2515X_TS_COOL 0x63 +#define BQ2515X_TS_WARM 0x64 +#define BQ2515X_TS_HOT 0x65 +#define BQ2515X_DEVICE_ID 0x6f + +#define BQ2515X_DEFAULT_ICHG_UA 10000 +#define BQ25150_DEFAULT_ILIM_UA 100000 +#define BQ25155_DEFAULT_ILIM_UA 500000 +#define BQ2515X_DEFAULT_VBAT_REG_UV 4200000 +#define BQ2515X_DEFAULT_IPRECHARGE_UA 2500 + +#define BQ2515X_DIVISOR 65536 +#define BQ2515X_VBAT_BASE_VOLT 3600000 +#define BQ2515X_VBAT_REG_MAX 4600000 +#define BQ2515X_VBAT_REG_MIN 3600000 +#define BQ2515X_VBAT_STEP_UV 10000 +#define BQ2515X_UV_FACTOR 1000000 +#define BQ2515X_VBAT_MULTIPLIER 6 +#define BQ2515X_ICHG_DIVISOR 52429 +#define BQ2515X_ICHG_CURR_STEP_THRESH_UA 318750 +#define BQ2515X_ICHG_MIN_UA 0 +#define BQ2515X_ICHG_MAX_UA 500000 +#define BQ2515X_ICHG_RNG_1B0_UA 1250 +#define BQ2515X_ICHG_RNG_1B1_UA 2500 +#define BQ2515X_VLOWV_SEL_1B0_UV 3000000 +#define BQ2515X_VLOWV_SEL_1B1_UV 2800000 +#define BQ2515X_PRECHRG_ICHRG_RNGE_1875_UA 18750 +#define BQ2515X_PRECHRG_ICHRG_RNGE_3750_UA 37500 +#define BQ2515X_TWAKE2_MIN_US 1700000 +#define BQ2515X_TWAKE2_MAX_US 2300000 + +#define BQ2515X_ILIM_150MA 0x2 +#define BQ2515X_ILIM_MASK 0x7 +#define BQ2515X_ILIM_MIN 50000 +#define BQ2515X_ILIM_MAX 600000 +#define BQ2515X_HEALTH_MASK 0xf +#define BQ2515X_ICHGRNG_MASK 0x80 +#define BQ2515X_STAT0_MASK 0x0f +#define BQ2515X_STAT1_MASK 0x1f +#define BQ2515X_PRECHARGE_MASK 0x1f + +#define BQ2515X_TS_HOT_STAT BIT(0) +#define BQ2515X_TS_WARM_STAT BIT(1) +#define BQ2515X_TS_COOL_STAT BIT(2) +#define BQ2515X_TS_COLD_STAT BIT(3) +#define BQ2515X_SAFETY_TIMER_EXP BIT(5) + +#define BQ2515X_EN_VBAT_READ BIT(3) +#define BQ2515X_EN_ICHG_READ BIT(5) + +#define BQ2515X_VIN_GOOD BIT(0) +#define BQ2515X_CHRG_DONE BIT(5) +#define BQ2515X_CV_CHRG_MODE BIT(6) + +#define BQ2515X_VIN_OVP_FAULT_STAT BIT(7) + +#define BQ2515X_WATCHDOG_DISABLE BIT(4) + +#define BQ2515X_ICHARGE_RANGE BIT(7) + +#define BQ2515X_VLOWV_SEL BIT(5) + +#define BQ2515X_CHARGER_DISABLE BIT(0) + +#define BQ2515X_HWRESET_14S_WD BIT(1) + +static const int bq2515x_ilim_lvl_values[] = { + 50000, 100000, 150000, 200000, 300000, 400000, 500000, 600000 +}; + +/** + * struct bq2515x_init_data - + * @ilim: input current limit + * @ichg: fast charge current + * @vbatreg: battery regulation voltage + * @iprechg: precharge current + */ +struct bq2515x_init_data { + int ilim; + int ichg; + int vbatreg; + int iprechg; +}; + +enum bq2515x_id { + BQ25150, + BQ25155, +}; + +/** + * struct bq2515x_device - + * @mains: mains properties + * @battery: battery properties + * @regmap: register map structure + * @dev: device structure + * + * @reset_gpio: manual reset (MR) pin + * @powerdown_gpio: low power mode pin + * @ac_detect_gpio: power good (PG) pin + * @ce_gpio: charge enable (CE) pin + * + * @model_name: string value describing device model + * @device_id: value of device_id + * @mains_online: boolean value indicating power supply online + * + * @bq2515x_init_data init_data: charger initialization data structure + */ +struct bq2515x_device { + struct power_supply *mains; + struct power_supply *battery; + struct regmap *regmap; + struct device *dev; + + struct gpio_desc *reset_gpio; + struct gpio_desc *powerdown_gpio; + struct gpio_desc *ac_detect_gpio; + struct gpio_desc *ce_gpio; + + char model_name[I2C_NAME_SIZE]; + int device_id; + bool mains_online; + + struct bq2515x_init_data init_data; +}; + +static struct reg_default bq25150_reg_defaults[] = { + {BQ2515X_FLAG0, 0x0}, + {BQ2515X_FLAG1, 0x0}, + {BQ2515X_FLAG2, 0x0}, + {BQ2515X_FLAG3, 0x0}, + {BQ2515X_MASK0, 0x0}, + {BQ2515X_MASK1, 0x0}, + {BQ2515X_MASK2, 0x71}, + {BQ2515X_MASK3, 0x0}, + {BQ2515X_VBAT_CTRL, 0x3C}, + {BQ2515X_ICHG_CTRL, 0x8}, + {BQ2515X_PCHRGCTRL, 0x2}, + {BQ2515X_TERMCTRL, 0x14}, + {BQ2515X_BUVLO, 0x0}, + {BQ2515X_CHARGERCTRL0, 0x82}, + {BQ2515X_CHARGERCTRL1, 0x42}, + {BQ2515X_ILIMCTRL, 0x1}, + {BQ2515X_LDOCTRL, 0xB0}, + {BQ2515X_MRCTRL, 0x2A}, + {BQ2515X_ICCTRL0, 0x10}, + {BQ2515X_ICCTRL1, 0x0}, + {BQ2515X_ICCTRL2, 0x0}, + {BQ2515X_ADCCTRL0, 0x2}, + {BQ2515X_ADCCTRL1, 0x40}, + {BQ2515X_ADC_COMP1_M, 0x23}, + {BQ2515X_ADC_COMP1_L, 0x20}, + {BQ2515X_ADC_COMP2_M, 0x38}, + {BQ2515X_ADC_COMP2_L, 0x90}, + {BQ2515X_ADC_COMP3_M, 0x0}, + {BQ2515X_ADC_COMP3_L, 0x0}, + {BQ2515X_ADC_READ_EN, 0x0}, + {BQ2515X_TS_FASTCHGCTRL, 0x34}, + {BQ2515X_TS_COLD, 0x7C}, + {BQ2515X_TS_COOL, 0x6D}, + {BQ2515X_TS_WARM, 0x38}, + {BQ2515X_TS_HOT, 0x27}, + {BQ2515X_DEVICE_ID, 0x20}, +}; + +static struct reg_default bq25155_reg_defaults[] = { + {BQ2515X_FLAG0, 0x0}, + {BQ2515X_FLAG1, 0x0}, + {BQ2515X_FLAG2, 0x0}, + {BQ2515X_FLAG3, 0x0}, + {BQ2515X_MASK0, 0x0}, + {BQ2515X_MASK1, 0x0}, + {BQ2515X_MASK2, 0x71}, + {BQ2515X_MASK3, 0x0}, + {BQ2515X_VBAT_CTRL, 0x3C}, + {BQ2515X_ICHG_CTRL, 0x8}, + {BQ2515X_PCHRGCTRL, 0x2}, + {BQ2515X_TERMCTRL, 0x14}, + {BQ2515X_BUVLO, 0x0}, + {BQ2515X_CHARGERCTRL0, 0x82}, + {BQ2515X_CHARGERCTRL1, 0xC2}, + {BQ2515X_ILIMCTRL, 0x6}, + {BQ2515X_LDOCTRL, 0xB0}, + {BQ2515X_MRCTRL, 0x2A}, + {BQ2515X_ICCTRL0, 0x10}, + {BQ2515X_ICCTRL1, 0x0}, + {BQ2515X_ICCTRL2, 0x40}, + {BQ2515X_ADCCTRL0, 0x2}, + {BQ2515X_ADCCTRL1, 0x40}, + {BQ2515X_ADC_COMP1_M, 0x23}, + {BQ2515X_ADC_COMP1_L, 0x20}, + {BQ2515X_ADC_COMP2_M, 0x38}, + {BQ2515X_ADC_COMP2_L, 0x90}, + {BQ2515X_ADC_COMP3_M, 0x0}, + {BQ2515X_ADC_COMP3_L, 0x0}, + {BQ2515X_ADC_READ_EN, 0x0}, + {BQ2515X_TS_FASTCHGCTRL, 0x34}, + {BQ2515X_TS_COLD, 0x7C}, + {BQ2515X_TS_COOL, 0x6D}, + {BQ2515X_TS_WARM, 0x38}, + {BQ2515X_TS_HOT, 0x27}, + {BQ2515X_DEVICE_ID, 0x35}, +}; + +static int bq2515x_wake_up(struct bq2515x_device *bq2515x) +{ + int ret; + int val; + + /* Read the STAT register if we can read it then the device is out + * of ship mode. If the register cannot be read then attempt to wake + * it up and enable the ADC. + */ + ret = regmap_read(bq2515x->regmap, BQ2515X_STAT0, &val); + if (ret) + return ret; + + /* Need to toggle LP and bring device out of ship mode. The device + * will exit the ship mode when the MR pin is held low for at least + * t_WAKE2 as shown in section 8.3.7.1 of the datasheet. + */ + gpiod_set_value_cansleep(bq2515x->powerdown_gpio, 0); + + gpiod_set_value_cansleep(bq2515x->reset_gpio, 0); + usleep_range(BQ2515X_TWAKE2_MIN_US, BQ2515X_TWAKE2_MAX_US); + gpiod_set_value_cansleep(bq2515x->reset_gpio, 1); + + return regmap_write(bq2515x->regmap, BQ2515X_ADC_READ_EN, + (BQ2515X_EN_VBAT_READ | BQ2515X_EN_ICHG_READ)); +} + +static int bq2515x_update_ps_status(struct bq2515x_device *bq2515x) +{ + bool dc = false; + unsigned int val; + int ret; + + if (bq2515x->ac_detect_gpio) + val = gpiod_get_value_cansleep(bq2515x->ac_detect_gpio); + else { + ret = regmap_read(bq2515x->regmap, BQ2515X_STAT0, &val); + if (ret) + return ret; + } + + dc = val & BQ2515X_VIN_GOOD; + + ret = bq2515x->mains_online != dc; + + bq2515x->mains_online = dc; + + return ret; +} + +static int bq2515x_disable_watchdog_timers(struct bq2515x_device *bq2515x) +{ + int ret; + + ret = regmap_update_bits(bq2515x->regmap, BQ2515X_CHARGERCTRL0, + BQ2515X_WATCHDOG_DISABLE, BQ2515X_WATCHDOG_DISABLE); + if (ret) + return ret; + + return regmap_update_bits(bq2515x->regmap, BQ2515X_ICCTRL2, + BQ2515X_HWRESET_14S_WD, 0); +} + +static int bq2515x_get_battery_voltage_now(struct bq2515x_device *bq2515x) +{ + int ret; + int vbat_msb; + int vbat_lsb; + uint32_t vbat_measurement; + + if (!bq2515x->mains_online) + bq2515x_wake_up(bq2515x); + + ret = regmap_read(bq2515x->regmap, BQ2515X_ADC_VBAT_M, &vbat_msb); + if (ret) + return ret; + + ret = regmap_read(bq2515x->regmap, BQ2515X_ADC_VBAT_L, &vbat_lsb); + if (ret) + return ret; + + vbat_measurement = (vbat_msb << 8) | vbat_lsb; + + return vbat_measurement * (BQ2515X_UV_FACTOR / BQ2515X_DIVISOR) * + BQ2515X_VBAT_MULTIPLIER; +} + +static int bq2515x_get_battery_current_now(struct bq2515x_device *bq2515x) +{ + int ret; + int ichg_msb; + int ichg_lsb; + uint32_t ichg_measurement; + u16 ichg_multiplier = BQ2515X_ICHG_RNG_1B0_UA; + unsigned int ichg_reg_code, reg_code; + unsigned int icharge_range = 0, pchrgctrl; + unsigned int buvlo, vlowv_sel, vlowv = BQ2515X_VLOWV_SEL_1B0_UV; + + if (!bq2515x->mains_online) + return -ENODATA; + + ret = regmap_read(bq2515x->regmap, BQ2515X_ADC_ICHG_M, &ichg_msb); + if (ret) + return ret; + + ret = regmap_read(bq2515x->regmap, BQ2515X_ADC_ICHG_L, &ichg_lsb); + if (ret) + return ret; + + ichg_measurement = (ichg_msb << 8) | ichg_lsb; + + ret = regmap_read(bq2515x->regmap, BQ2515X_BUVLO, &buvlo); + if (ret) + return ret; + + vlowv_sel = buvlo & BQ2515X_VLOWV_SEL; + + if (vlowv_sel) + vlowv = BQ2515X_VLOWV_SEL_1B1_UV; + + if (bq2515x_get_battery_voltage_now(bq2515x) < vlowv) { + ret = regmap_read(bq2515x->regmap, BQ2515X_PCHRGCTRL, + &pchrgctrl); + if (ret) + return ret; + + reg_code = pchrgctrl & BQ2515X_PRECHARGE_MASK; + } else { + ret = regmap_read(bq2515x->regmap, BQ2515X_ICHG_CTRL, + &ichg_reg_code); + if (ret) + return ret; + + reg_code = ichg_reg_code; + } + + ret = regmap_read(bq2515x->regmap, BQ2515X_PCHRGCTRL, &pchrgctrl); + if (ret) + return ret; + + icharge_range = pchrgctrl & BQ2515X_ICHARGE_RANGE; + + if (icharge_range) + ichg_multiplier = BQ2515X_ICHG_RNG_1B1_UA; + + return reg_code * (ichg_multiplier * ichg_measurement / + BQ2515X_ICHG_DIVISOR); +} + +static bool bq2515x_get_charge_disable(struct bq2515x_device *bq2515x) +{ + int ret; + int ce_pin; + int icctrl2; + int charger_disable; + + ce_pin = gpiod_get_value_cansleep(bq2515x->ce_gpio); + + ret = regmap_read(bq2515x->regmap, BQ2515X_ICCTRL2, &icctrl2); + if (ret) + return ret; + + charger_disable = icctrl2 & BQ2515X_CHARGER_DISABLE; + + if (charger_disable || ce_pin) + return true; + + return false; +} + +static int bq2515x_set_charge_disable(struct bq2515x_device *bq2515x, int val) +{ + gpiod_set_value_cansleep(bq2515x->ce_gpio, val); + + return regmap_update_bits(bq2515x->regmap, BQ2515X_ICCTRL2, + BQ2515X_CHARGER_DISABLE, val); +} + +static int bq2515x_get_const_charge_current(struct bq2515x_device *bq2515x) +{ + int ret; + u16 ichg_multiplier = BQ2515X_ICHG_RNG_1B0_UA; + unsigned int ichg_reg_code; + unsigned int pchrgctrl; + unsigned int icharge_range; + + ret = regmap_read(bq2515x->regmap, BQ2515X_ICHG_CTRL, &ichg_reg_code); + if (ret) + return ret; + + ret = regmap_read(bq2515x->regmap, BQ2515X_PCHRGCTRL, &pchrgctrl); + if (ret) + return ret; + + icharge_range = pchrgctrl & BQ2515X_ICHARGE_RANGE; + + if (icharge_range) + ichg_multiplier = BQ2515X_ICHG_RNG_1B1_UA; + + return ichg_reg_code * ichg_multiplier; +} + +static int bq2515x_set_const_charge_current(struct bq2515x_device *bq2515x, + int val) +{ + int ret; + unsigned int ichg_reg_code; + u16 ichg_multiplier = BQ2515X_ICHG_RNG_1B0_UA; + unsigned int icharge_range = 0; + + if (val > BQ2515X_ICHG_MAX_UA || val < BQ2515X_ICHG_MIN_UA) + return -EINVAL; + + if (val > BQ2515X_ICHG_CURR_STEP_THRESH_UA) { + ichg_multiplier = BQ2515X_ICHG_RNG_1B1_UA; + icharge_range = BQ2515X_ICHARGE_RANGE; + } + + bq2515x_set_charge_disable(bq2515x, 1); + + ret = regmap_update_bits(bq2515x->regmap, BQ2515X_PCHRGCTRL, + BQ2515X_ICHARGE_RANGE, icharge_range); + if (ret) + return ret; + + ichg_reg_code = val / ichg_multiplier; + + ret = regmap_write(bq2515x->regmap, BQ2515X_ICHG_CTRL, ichg_reg_code); + if (ret) + return ret; + + return bq2515x_set_charge_disable(bq2515x, 0); +} + +static int bq2515x_get_precharge_current(struct bq2515x_device *bq2515x) +{ + int ret; + unsigned int pchrgctrl; + unsigned int icharge_range; + u16 precharge_multiplier = BQ2515X_ICHG_RNG_1B0_UA; + unsigned int precharge_reg_code; + + ret = regmap_read(bq2515x->regmap, BQ2515X_PCHRGCTRL, &pchrgctrl); + if (ret) + return ret; + + icharge_range = pchrgctrl & BQ2515X_ICHARGE_RANGE; + + if (icharge_range) + precharge_multiplier = BQ2515X_ICHG_RNG_1B1_UA; + + precharge_reg_code = pchrgctrl & BQ2515X_PRECHARGE_MASK; + + return precharge_reg_code * precharge_multiplier; +} + +static int bq2515x_set_precharge_current(struct bq2515x_device *bq2515x, + int val) +{ + int ret; + unsigned int pchrgctrl; + unsigned int icharge_range; + unsigned int precharge_reg_code; + unsigned int precharge_multiplier = BQ2515X_ICHG_RNG_1B0_UA; + unsigned int precharge_max_ua = BQ2515X_PRECHRG_ICHRG_RNGE_1875_UA; + + ret = regmap_read(bq2515x->regmap, BQ2515X_PCHRGCTRL, &pchrgctrl); + if (ret) + return ret; + + icharge_range = pchrgctrl & BQ2515X_ICHARGE_RANGE; + + if (icharge_range) { + precharge_max_ua = BQ2515X_PRECHRG_ICHRG_RNGE_3750_UA; + precharge_multiplier = BQ2515X_ICHG_RNG_1B1_UA; + } else { + precharge_max_ua = BQ2515X_PRECHRG_ICHRG_RNGE_1875_UA; + precharge_multiplier = BQ2515X_ICHG_RNG_1B0_UA; + } + if (val > precharge_max_ua || val < BQ2515X_ICHG_MIN_UA) + return -EINVAL; + + precharge_reg_code = val / precharge_multiplier; + + ret = bq2515x_set_charge_disable(bq2515x, 1); + if (ret) + return ret; + + ret = regmap_update_bits(bq2515x->regmap, BQ2515X_PCHRGCTRL, + BQ2515X_PRECHARGE_MASK, precharge_reg_code); + if (ret) + return ret; + + return bq2515x_set_charge_disable(bq2515x, 0); +} + +static int bq2515x_charging_status(struct bq2515x_device *bq2515x, + union power_supply_propval *val) +{ + bool status0_no_fault; + bool status1_no_fault; + bool ce_status; + bool charge_done; + unsigned int status; + int ret; + + if (!bq2515x->mains_online) { + val->intval = POWER_SUPPLY_STATUS_DISCHARGING; + return 0; + } + + ret = regmap_read(bq2515x->regmap, BQ2515X_STAT0, &status); + if (ret) + return ret; + + /* + * The code block below is used to determine if any faults from the + * STAT0 register are disbaling charging or if the charge has completed + * according to the CHARGE_DONE_STAT bit. + */ + if (((status & BQ2515X_STAT0_MASK) == true) & + ((status & BQ2515X_CHRG_DONE) == false)) { + status0_no_fault = true; + charge_done = false; + } else if (status & BQ2515X_CHRG_DONE) { + charge_done = true; + status0_no_fault = false; + } else { + status0_no_fault = false; + charge_done = false; + } + + ret = regmap_read(bq2515x->regmap, BQ2515X_STAT1, &status); + if (ret) + return ret; + /* + * The code block below is used to determine if any faults from the + * STAT1 register are disbaling charging + */ + if ((status & BQ2515X_STAT1_MASK) == false) + status1_no_fault = true; + else + status1_no_fault = false; + + ce_status = (!bq2515x_get_charge_disable(bq2515x)); + + /* + * If there are no faults and charging is enabled, then status is + * charging. Otherwise, if charging is complete, then status is full. + * Otherwise, if a fault exists or charging is disabled, then status is + * not charging + */ + if (status0_no_fault & status1_no_fault & ce_status) + val->intval = POWER_SUPPLY_STATUS_CHARGING; + else if (charge_done) + val->intval = POWER_SUPPLY_STATUS_FULL; + else if (!(status0_no_fault & status1_no_fault & ce_status)) + val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING; + + return 0; +} + +static int bq2515x_get_batt_reg(struct bq2515x_device *bq2515x) +{ + int vbat_reg_code; + int ret; + + ret = regmap_read(bq2515x->regmap, BQ2515X_VBAT_CTRL, &vbat_reg_code); + if (ret) + return ret; + + return BQ2515X_VBAT_BASE_VOLT + vbat_reg_code * BQ2515X_VBAT_STEP_UV; +} + +static int bq2515x_set_batt_reg(struct bq2515x_device *bq2515x, int val) +{ + int vbat_reg_code; + + if (val > BQ2515X_VBAT_REG_MAX || val < BQ2515X_VBAT_REG_MIN) + return -EINVAL; + + vbat_reg_code = (val - BQ2515X_VBAT_BASE_VOLT) / BQ2515X_VBAT_STEP_UV; + + return regmap_write(bq2515x->regmap, BQ2515X_VBAT_CTRL, vbat_reg_code); +} + +static int bq2515x_get_ilim_lvl(struct bq2515x_device *bq2515x) +{ + int ret; + int ilimctrl; + + ret = regmap_read(bq2515x->regmap, BQ2515X_ILIMCTRL, &ilimctrl); + if (ret) + return ret; + + return bq2515x_ilim_lvl_values[ilimctrl & BQ2515X_ILIM_MASK]; +} + +static int bq2515x_set_ilim_lvl(struct bq2515x_device *bq2515x, int val) +{ + int i = 0; + unsigned int array_size = ARRAY_SIZE(bq2515x_ilim_lvl_values); + + for (i = array_size - 1; i > 0; i--) { + if (val >= bq2515x_ilim_lvl_values[i]) + break; + } + return regmap_write(bq2515x->regmap, BQ2515X_ILIMCTRL, i); +} + +static int bq2515x_power_supply_property_is_writeable(struct power_supply *psy, + enum power_supply_property prop) +{ + switch (prop) { + case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT: + case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE: + case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT: + case POWER_SUPPLY_PROP_PRECHARGE_CURRENT: + return true; + default: + return false; + } +} + +static int bq2515x_charger_get_health(struct bq2515x_device *bq2515x, + union power_supply_propval *val) +{ + int health = POWER_SUPPLY_HEALTH_GOOD; + int ret; + unsigned int stat1; + unsigned int flag3; + + if (!bq2515x->mains_online) + bq2515x_wake_up(bq2515x); + + ret = regmap_read(bq2515x->regmap, BQ2515X_FLAG3, &flag3); + if (ret) + return ret; + + ret = regmap_read(bq2515x->regmap, BQ2515X_STAT1, &stat1); + if (ret) + return ret; + + if (stat1 & BQ2515X_HEALTH_MASK) { + switch (stat1 & BQ2515X_HEALTH_MASK) { + case BQ2515X_TS_HOT_STAT: + health = POWER_SUPPLY_HEALTH_HOT; + break; + case BQ2515X_TS_WARM_STAT: + health = POWER_SUPPLY_HEALTH_WARM; + break; + case BQ2515X_TS_COOL_STAT: + health = POWER_SUPPLY_HEALTH_COOL; + break; + case BQ2515X_TS_COLD_STAT: + health = POWER_SUPPLY_HEALTH_COLD; + break; + default: + health = POWER_SUPPLY_HEALTH_UNKNOWN; + break; + } + } + + if (stat1 & BQ2515X_VIN_OVP_FAULT_STAT) + health = POWER_SUPPLY_HEALTH_OVERVOLTAGE; + + if (flag3 & BQ2515X_SAFETY_TIMER_EXP) + health = POWER_SUPPLY_HEALTH_SAFETY_TIMER_EXPIRE; + + val->intval = health; + return 0; +} + +static int bq2515x_mains_set_property(struct power_supply *psy, + enum power_supply_property prop, + const union power_supply_propval *val) +{ + struct bq2515x_device *bq2515x = power_supply_get_drvdata(psy); + int ret; + + switch (prop) { + case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE: + ret = bq2515x_set_batt_reg(bq2515x, val->intval); + break; + + case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT: + ret = bq2515x_set_const_charge_current(bq2515x, val->intval); + break; + + case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT: + ret = bq2515x_set_ilim_lvl(bq2515x, val->intval); + break; + + case POWER_SUPPLY_PROP_PRECHARGE_CURRENT: + ret = bq2515x_set_precharge_current(bq2515x, val->intval); + break; + + default: + return -EINVAL; + } + + return ret; +} + +static int bq2515x_mains_get_property(struct power_supply *psy, + enum power_supply_property prop, + union power_supply_propval *val) +{ + struct bq2515x_device *bq2515x = power_supply_get_drvdata(psy); + int ret = 0; + + switch (prop) { + + case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT: + ret = bq2515x_get_const_charge_current(bq2515x); + if (ret < 0) + return ret; + + val->intval = ret; + break; + + case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE: + ret = bq2515x_get_batt_reg(bq2515x); + if (ret < 0) + return ret; + val->intval = ret; + break; + + case POWER_SUPPLY_PROP_PRECHARGE_CURRENT: + ret = bq2515x_get_precharge_current(bq2515x); + if (ret < 0) + return ret; + val->intval = ret; + break; + + case POWER_SUPPLY_PROP_ONLINE: + val->intval = bq2515x->mains_online; + break; + + case POWER_SUPPLY_PROP_HEALTH: + ret = bq2515x_charger_get_health(bq2515x, val); + if (ret) + val->intval = POWER_SUPPLY_HEALTH_UNKNOWN; + break; + + case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT: + ret = bq2515x_get_ilim_lvl(bq2515x); + if (ret < 0) + return ret; + val->intval = ret; + break; + + case POWER_SUPPLY_PROP_MODEL_NAME: + val->strval = bq2515x->model_name; + break; + + case POWER_SUPPLY_PROP_MANUFACTURER: + val->strval = BQ2515X_MANUFACTURER; + break; + + case POWER_SUPPLY_PROP_STATUS: + ret = bq2515x_charging_status(bq2515x, val); + if (ret) + return ret; + break; + + default: + return -EINVAL; + } + + return ret; +} + +static int bq2515x_battery_get_property(struct power_supply *psy, + enum power_supply_property prop, + union power_supply_propval *val) +{ + struct bq2515x_device *bq2515x = power_supply_get_drvdata(psy); + int ret; + + ret = bq2515x_update_ps_status(bq2515x); + if (ret) + return ret; + + switch (prop) { + + case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX: + ret = bq2515x->init_data.vbatreg; + if (ret < 0) + return ret; + val->intval = ret; + break; + + case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX: + ret = bq2515x->init_data.ichg; + if (ret < 0) + return ret; + val->intval = ret; + break; + + case POWER_SUPPLY_PROP_VOLTAGE_NOW: + ret = bq2515x_get_battery_voltage_now(bq2515x); + if (ret < 0) + return ret; + val->intval = ret; + break; + + case POWER_SUPPLY_PROP_CURRENT_NOW: + ret = bq2515x_get_battery_current_now(bq2515x); + if (ret < 0) + return ret; + val->intval = ret; + break; + + default: + return -EINVAL; + } + return 0; +} + +static enum power_supply_property bq2515x_battery_properties[] = { + POWER_SUPPLY_PROP_VOLTAGE_NOW, + POWER_SUPPLY_PROP_CURRENT_NOW, + POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX, + POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX, +}; + +static enum power_supply_property bq2515x_mains_properties[] = { + POWER_SUPPLY_PROP_ONLINE, + POWER_SUPPLY_PROP_STATUS, + POWER_SUPPLY_PROP_HEALTH, + POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT, + POWER_SUPPLY_PROP_MODEL_NAME, + POWER_SUPPLY_PROP_MANUFACTURER, + POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE, + POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT, + POWER_SUPPLY_PROP_PRECHARGE_CURRENT, +}; + +static struct power_supply_desc bq2515x_mains_desc = { + .name = "bq2515x-mains", + .type = POWER_SUPPLY_TYPE_MAINS, + .get_property = bq2515x_mains_get_property, + .set_property = bq2515x_mains_set_property, + .properties = bq2515x_mains_properties, + .num_properties = ARRAY_SIZE(bq2515x_mains_properties), + .property_is_writeable = bq2515x_power_supply_property_is_writeable, +}; + +static struct power_supply_desc bq2515x_battery_desc = { + .name = "bq2515x-battery", + .type = POWER_SUPPLY_TYPE_BATTERY, + .get_property = bq2515x_battery_get_property, + .properties = bq2515x_battery_properties, + .num_properties = ARRAY_SIZE(bq2515x_battery_properties), + .property_is_writeable = bq2515x_power_supply_property_is_writeable, +}; + +static int bq2515x_power_supply_register(struct bq2515x_device *bq2515x, + struct device *dev, struct power_supply_config psy_cfg) +{ + bq2515x->mains = devm_power_supply_register(bq2515x->dev, + &bq2515x_mains_desc, + &psy_cfg); + if (IS_ERR(bq2515x->mains)) + return -EINVAL; + + bq2515x->battery = devm_power_supply_register(bq2515x->dev, + &bq2515x_battery_desc, + &psy_cfg); + if (IS_ERR(bq2515x->battery)) + return -EINVAL; + + return 0; +} + +static int bq2515x_hw_init(struct bq2515x_device *bq2515x) +{ + int ret; + struct power_supply_battery_info bat_info = { }; + + ret = bq2515x_disable_watchdog_timers(bq2515x); + if (ret) + return ret; + + if (bq2515x->init_data.ilim) { + ret = bq2515x_set_ilim_lvl(bq2515x, bq2515x->init_data.ilim); + if (ret) + return ret; + } + + ret = power_supply_get_battery_info(bq2515x->mains, &bat_info); + if (ret) { + dev_warn(bq2515x->dev, "battery info missing, default values will be applied\n"); + + bq2515x->init_data.ichg = BQ2515X_DEFAULT_ICHG_UA; + + bq2515x->init_data.vbatreg = BQ2515X_DEFAULT_VBAT_REG_UV; + + bq2515x->init_data.iprechg = BQ2515X_DEFAULT_IPRECHARGE_UA; + + } else { + bq2515x->init_data.ichg = + bat_info.constant_charge_current_max_ua; + + bq2515x->init_data.vbatreg = + bat_info.constant_charge_voltage_max_uv; + + bq2515x->init_data.iprechg = + bat_info.precharge_current_ua; + } + + ret = bq2515x_set_const_charge_current(bq2515x, + bq2515x->init_data.ichg); + if (ret) + return ret; + + ret = bq2515x_set_batt_reg(bq2515x, bq2515x->init_data.vbatreg); + if (ret) + return ret; + + return bq2515x_set_precharge_current(bq2515x, + bq2515x->init_data.iprechg); +} + +static int bq2515x_read_properties(struct bq2515x_device *bq2515x) +{ + int ret; + + ret = device_property_read_u32(bq2515x->dev, + "input-current-limit-microamp", + &bq2515x->init_data.ilim); + if (ret) { + switch (bq2515x->device_id) { + case BQ25150: + bq2515x->init_data.ilim = BQ25150_DEFAULT_ILIM_UA; + break; + case BQ25155: + bq2515x->init_data.ilim = BQ25155_DEFAULT_ILIM_UA; + break; + } + } + + bq2515x->ac_detect_gpio = devm_gpiod_get_optional(bq2515x->dev, + "ac-detect", GPIOD_IN); + if (IS_ERR(bq2515x->ac_detect_gpio)) { + ret = PTR_ERR(bq2515x->ac_detect_gpio); + dev_err(bq2515x->dev, "Failed to get ac detect"); + return ret; + } + + bq2515x->reset_gpio = devm_gpiod_get_optional(bq2515x->dev, + "reset", GPIOD_OUT_LOW); + if (IS_ERR(bq2515x->reset_gpio)) { + ret = PTR_ERR(bq2515x->reset_gpio); + dev_err(bq2515x->dev, "Failed to get reset"); + return ret; + } + + bq2515x->powerdown_gpio = devm_gpiod_get_optional(bq2515x->dev, + "powerdown", GPIOD_OUT_LOW); + if (IS_ERR(bq2515x->powerdown_gpio)) { + ret = PTR_ERR(bq2515x->powerdown_gpio); + dev_err(bq2515x->dev, "Failed to get powerdown"); + return ret; + } + + bq2515x->ce_gpio = devm_gpiod_get_optional(bq2515x->dev, + "charge-enable", + GPIOD_OUT_LOW); + if (IS_ERR(bq2515x->ce_gpio)) { + ret = PTR_ERR(bq2515x->ce_gpio); + dev_err(bq2515x->dev, "Failed to get ce"); + return ret; + } + + return 0; +} + +static bool bq2515x_volatile_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case BQ2515X_STAT0 ... BQ2515X_FLAG3: + case BQ2515X_ADC_VBAT_M ... BQ2515X_ADC_IIN_L: + return true; + default: + return false; + } +} + +static const struct regmap_config bq25150_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + + .max_register = BQ2515X_DEVICE_ID, + .reg_defaults = bq25150_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(bq25150_reg_defaults), + .cache_type = REGCACHE_RBTREE, + .volatile_reg = bq2515x_volatile_register, +}; + +static const struct regmap_config bq25155_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + + .max_register = BQ2515X_DEVICE_ID, + .reg_defaults = bq25155_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(bq25155_reg_defaults), + .cache_type = REGCACHE_RBTREE, + .volatile_reg = bq2515x_volatile_register, +}; + +static int bq2515x_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct device *dev = &client->dev; + struct bq2515x_device *bq2515x; + struct power_supply_config charger_cfg = {}; + int ret; + + bq2515x = devm_kzalloc(dev, sizeof(*bq2515x), GFP_KERNEL); + if (!bq2515x) + return -ENOMEM; + + bq2515x->dev = dev; + + strncpy(bq2515x->model_name, id->name, I2C_NAME_SIZE); + + bq2515x->device_id = id->driver_data; + + switch (bq2515x->device_id) { + case BQ25150: + bq2515x->regmap = devm_regmap_init_i2c(client, + &bq25150_regmap_config); + break; + case BQ25155: + bq2515x->regmap = devm_regmap_init_i2c(client, + &bq25155_regmap_config); + break; + } + + if (IS_ERR(bq2515x->regmap)) { + dev_err(dev, "failed to allocate register map\n"); + return PTR_ERR(bq2515x->regmap); + } + + i2c_set_clientdata(client, bq2515x); + + charger_cfg.drv_data = bq2515x; + charger_cfg.of_node = dev->of_node; + + ret = bq2515x_read_properties(bq2515x); + if (ret) { + dev_err(dev, "Failed to read device tree properties %d\n", + ret); + return ret; + } + + ret = bq2515x_power_supply_register(bq2515x, dev, charger_cfg); + if (ret) { + dev_err(dev, "failed to register power supply\n"); + return ret; + } + + ret = bq2515x_hw_init(bq2515x); + if (ret) { + dev_err(dev, "Cannot initialize the chip\n"); + return ret; + } + + return 0; +} + +static const struct i2c_device_id bq2515x_i2c_ids[] = { + { "bq25150", BQ25150, }, + { "bq25155", BQ25155, }, + {}, +}; +MODULE_DEVICE_TABLE(i2c, bq2515x_i2c_ids); + +static const struct of_device_id bq2515x_of_match[] = { + { .compatible = "ti,bq25150", }, + { .compatible = "ti,bq25155", }, + { }, +}; +MODULE_DEVICE_TABLE(of, bq2515x_of_match); + +static struct i2c_driver bq2515x_driver = { + .driver = { + .name = "bq2515x-charger", + .of_match_table = bq2515x_of_match, + }, + .probe = bq2515x_probe, + .id_table = bq2515x_i2c_ids, +}; +module_i2c_driver(bq2515x_driver); + +MODULE_AUTHOR("Dan Murphy "); +MODULE_AUTHOR("Ricardo Rivera-Matos "); +MODULE_DESCRIPTION("BQ2515X charger driver"); +MODULE_LICENSE("GPL v2"); -- GitLab From 7e4a145e5b675d5a9182f756950f001eaa256795 Mon Sep 17 00:00:00 2001 From: Athira Rajeev Date: Fri, 17 Jul 2020 10:38:14 -0400 Subject: [PATCH 0756/1476] KVM: PPC: Book3S HV: Cleanup updates for kvm vcpu MMCR Currently `kvm_vcpu_arch` stores all Monitor Mode Control registers in a flat array in order: mmcr0, mmcr1, mmcra, mmcr2, mmcrs Split this to give mmcra and mmcrs its own entries in vcpu and use a flat array for mmcr0 to mmcr2. This patch implements this cleanup to make code easier to read. Signed-off-by: Athira Rajeev [mpe: Fix MMCRA/MMCR2 uapi breakage as noted by paulus] Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/1594996707-3727-3-git-send-email-atrajeev@linux.vnet.ibm.com --- arch/powerpc/include/asm/kvm_host.h | 4 +++- arch/powerpc/kernel/asm-offsets.c | 2 ++ arch/powerpc/kvm/book3s_hv.c | 22 ++++++++++++++++++++-- arch/powerpc/kvm/book3s_hv_rmhandlers.S | 12 ++++++------ 4 files changed, 31 insertions(+), 9 deletions(-) diff --git a/arch/powerpc/include/asm/kvm_host.h b/arch/powerpc/include/asm/kvm_host.h index 7e2d061d04451..80b0005a72243 100644 --- a/arch/powerpc/include/asm/kvm_host.h +++ b/arch/powerpc/include/asm/kvm_host.h @@ -637,7 +637,9 @@ struct kvm_vcpu_arch { u32 ccr1; u32 dbsr; - u64 mmcr[5]; + u64 mmcr[3]; /* MMCR0, MMCR1, MMCR2 */ + u64 mmcra; + u64 mmcrs; u32 pmc[8]; u32 spmc[2]; u64 siar; diff --git a/arch/powerpc/kernel/asm-offsets.c b/arch/powerpc/kernel/asm-offsets.c index 6657dc6b23360..6fa4853691dec 100644 --- a/arch/powerpc/kernel/asm-offsets.c +++ b/arch/powerpc/kernel/asm-offsets.c @@ -559,6 +559,8 @@ int main(void) OFFSET(VCPU_IRQ_PENDING, kvm_vcpu, arch.irq_pending); OFFSET(VCPU_DBELL_REQ, kvm_vcpu, arch.doorbell_request); OFFSET(VCPU_MMCR, kvm_vcpu, arch.mmcr); + OFFSET(VCPU_MMCRA, kvm_vcpu, arch.mmcra); + OFFSET(VCPU_MMCRS, kvm_vcpu, arch.mmcrs); OFFSET(VCPU_PMC, kvm_vcpu, arch.pmc); OFFSET(VCPU_SPMC, kvm_vcpu, arch.spmc); OFFSET(VCPU_SIAR, kvm_vcpu, arch.siar); diff --git a/arch/powerpc/kvm/book3s_hv.c b/arch/powerpc/kvm/book3s_hv.c index 6bf66649ab92f..b10bb404f0d54 100644 --- a/arch/powerpc/kvm/book3s_hv.c +++ b/arch/powerpc/kvm/book3s_hv.c @@ -1679,10 +1679,19 @@ static int kvmppc_get_one_reg_hv(struct kvm_vcpu *vcpu, u64 id, case KVM_REG_PPC_UAMOR: *val = get_reg_val(id, vcpu->arch.uamor); break; - case KVM_REG_PPC_MMCR0 ... KVM_REG_PPC_MMCRS: + case KVM_REG_PPC_MMCR0 ... KVM_REG_PPC_MMCR1: i = id - KVM_REG_PPC_MMCR0; *val = get_reg_val(id, vcpu->arch.mmcr[i]); break; + case KVM_REG_PPC_MMCR2: + *val = get_reg_val(id, vcpu->arch.mmcr[2]); + break; + case KVM_REG_PPC_MMCRA: + *val = get_reg_val(id, vcpu->arch.mmcra); + break; + case KVM_REG_PPC_MMCRS: + *val = get_reg_val(id, vcpu->arch.mmcrs); + break; case KVM_REG_PPC_PMC1 ... KVM_REG_PPC_PMC8: i = id - KVM_REG_PPC_PMC1; *val = get_reg_val(id, vcpu->arch.pmc[i]); @@ -1900,10 +1909,19 @@ static int kvmppc_set_one_reg_hv(struct kvm_vcpu *vcpu, u64 id, case KVM_REG_PPC_UAMOR: vcpu->arch.uamor = set_reg_val(id, *val); break; - case KVM_REG_PPC_MMCR0 ... KVM_REG_PPC_MMCRS: + case KVM_REG_PPC_MMCR0 ... KVM_REG_PPC_MMCR1: i = id - KVM_REG_PPC_MMCR0; vcpu->arch.mmcr[i] = set_reg_val(id, *val); break; + case KVM_REG_PPC_MMCR2: + vcpu->arch.mmcr[2] = set_reg_val(id, *val); + break; + case KVM_REG_PPC_MMCRA: + vcpu->arch.mmcra = set_reg_val(id, *val); + break; + case KVM_REG_PPC_MMCRS: + vcpu->arch.mmcrs = set_reg_val(id, *val); + break; case KVM_REG_PPC_PMC1 ... KVM_REG_PPC_PMC8: i = id - KVM_REG_PPC_PMC1; vcpu->arch.pmc[i] = set_reg_val(id, *val); diff --git a/arch/powerpc/kvm/book3s_hv_rmhandlers.S b/arch/powerpc/kvm/book3s_hv_rmhandlers.S index 71943892c81c6..702eaa2530421 100644 --- a/arch/powerpc/kvm/book3s_hv_rmhandlers.S +++ b/arch/powerpc/kvm/book3s_hv_rmhandlers.S @@ -3428,7 +3428,7 @@ END_FTR_SECTION_IFSET(CPU_FTR_PMAO_BUG) mtspr SPRN_PMC6, r9 ld r3, VCPU_MMCR(r4) ld r5, VCPU_MMCR + 8(r4) - ld r6, VCPU_MMCR + 16(r4) + ld r6, VCPU_MMCRA(r4) ld r7, VCPU_SIAR(r4) ld r8, VCPU_SDAR(r4) mtspr SPRN_MMCR1, r5 @@ -3436,14 +3436,14 @@ END_FTR_SECTION_IFSET(CPU_FTR_PMAO_BUG) mtspr SPRN_SIAR, r7 mtspr SPRN_SDAR, r8 BEGIN_FTR_SECTION - ld r5, VCPU_MMCR + 24(r4) + ld r5, VCPU_MMCR + 16(r4) ld r6, VCPU_SIER(r4) mtspr SPRN_MMCR2, r5 mtspr SPRN_SIER, r6 BEGIN_FTR_SECTION_NESTED(96) lwz r7, VCPU_PMC + 24(r4) lwz r8, VCPU_PMC + 28(r4) - ld r9, VCPU_MMCR + 32(r4) + ld r9, VCPU_MMCRS(r4) mtspr SPRN_SPMC1, r7 mtspr SPRN_SPMC2, r8 mtspr SPRN_MMCRS, r9 @@ -3551,9 +3551,9 @@ END_FTR_SECTION_IFSET(CPU_FTR_ARCH_207S) mfspr r8, SPRN_SDAR std r4, VCPU_MMCR(r9) std r5, VCPU_MMCR + 8(r9) - std r6, VCPU_MMCR + 16(r9) + std r6, VCPU_MMCRA(r9) BEGIN_FTR_SECTION - std r10, VCPU_MMCR + 24(r9) + std r10, VCPU_MMCR + 16(r9) END_FTR_SECTION_IFSET(CPU_FTR_ARCH_207S) std r7, VCPU_SIAR(r9) std r8, VCPU_SDAR(r9) @@ -3578,7 +3578,7 @@ BEGIN_FTR_SECTION_NESTED(96) mfspr r8, SPRN_MMCRS stw r6, VCPU_PMC + 24(r9) stw r7, VCPU_PMC + 28(r9) - std r8, VCPU_MMCR + 32(r9) + std r8, VCPU_MMCRS(r9) lis r4, 0x8000 mtspr SPRN_MMCRS, r4 END_FTR_SECTION_NESTED(CPU_FTR_ARCH_300, 0, 96) -- GitLab From 9d4fc86dcd510dab5521a6c891f9bf379b85a7e0 Mon Sep 17 00:00:00 2001 From: Athira Rajeev Date: Fri, 17 Jul 2020 10:38:15 -0400 Subject: [PATCH 0757/1476] powerpc/perf: Update Power PMU cache_events to u64 type Events of type PERF_TYPE_HW_CACHE was described for Power PMU as: int (*cache_events)[type][op][result]; where type, op, result values unpacked from the event attribute config value is used to generate the raw event code at runtime. So far the event code values which used to create these cache-related events were within 32 bit and `int` type worked. In power10, some of the event codes are of 64-bit value and hence update the Power PMU cache_events to `u64` type in `power_pmu` struct. Also propagate this change to existing all PMU driver code paths which are using ppmu->cache_events. Signed-off-by: Athira Rajeev Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/1594996707-3727-4-git-send-email-atrajeev@linux.vnet.ibm.com --- arch/powerpc/include/asm/perf_event_server.h | 2 +- arch/powerpc/perf/core-book3s.c | 2 +- arch/powerpc/perf/generic-compat-pmu.c | 2 +- arch/powerpc/perf/mpc7450-pmu.c | 2 +- arch/powerpc/perf/power5+-pmu.c | 2 +- arch/powerpc/perf/power5-pmu.c | 2 +- arch/powerpc/perf/power6-pmu.c | 2 +- arch/powerpc/perf/power7-pmu.c | 2 +- arch/powerpc/perf/power8-pmu.c | 2 +- arch/powerpc/perf/power9-pmu.c | 2 +- arch/powerpc/perf/ppc970-pmu.c | 2 +- 11 files changed, 11 insertions(+), 11 deletions(-) diff --git a/arch/powerpc/include/asm/perf_event_server.h b/arch/powerpc/include/asm/perf_event_server.h index f9a36680339d1..14b8dc1305988 100644 --- a/arch/powerpc/include/asm/perf_event_server.h +++ b/arch/powerpc/include/asm/perf_event_server.h @@ -53,7 +53,7 @@ struct power_pmu { const struct attribute_group **attr_groups; int n_generic; int *generic_events; - int (*cache_events)[PERF_COUNT_HW_CACHE_MAX] + u64 (*cache_events)[PERF_COUNT_HW_CACHE_MAX] [PERF_COUNT_HW_CACHE_OP_MAX] [PERF_COUNT_HW_CACHE_RESULT_MAX]; diff --git a/arch/powerpc/perf/core-book3s.c b/arch/powerpc/perf/core-book3s.c index 18b1b6aae692f..f4d07b5efd433 100644 --- a/arch/powerpc/perf/core-book3s.c +++ b/arch/powerpc/perf/core-book3s.c @@ -1790,7 +1790,7 @@ static void hw_perf_event_destroy(struct perf_event *event) static int hw_perf_cache_event(u64 config, u64 *eventp) { unsigned long type, op, result; - int ev; + u64 ev; if (!ppmu->cache_events) return -EINVAL; diff --git a/arch/powerpc/perf/generic-compat-pmu.c b/arch/powerpc/perf/generic-compat-pmu.c index 5e5a54d5588e9..eb8a6aaf4cc14 100644 --- a/arch/powerpc/perf/generic-compat-pmu.c +++ b/arch/powerpc/perf/generic-compat-pmu.c @@ -101,7 +101,7 @@ static int compat_generic_events[] = { * 0 means not supported, -1 means nonsensical, other values * are event codes. */ -static int generic_compat_cache_events[C(MAX)][C(OP_MAX)][C(RESULT_MAX)] = { +static u64 generic_compat_cache_events[C(MAX)][C(OP_MAX)][C(RESULT_MAX)] = { [ C(L1D) ] = { [ C(OP_READ) ] = { [ C(RESULT_ACCESS) ] = 0, diff --git a/arch/powerpc/perf/mpc7450-pmu.c b/arch/powerpc/perf/mpc7450-pmu.c index 826de253aa4dc..1919e9df9165a 100644 --- a/arch/powerpc/perf/mpc7450-pmu.c +++ b/arch/powerpc/perf/mpc7450-pmu.c @@ -361,7 +361,7 @@ static int mpc7450_generic_events[] = { * 0 means not supported, -1 means nonsensical, other values * are event codes. */ -static int mpc7450_cache_events[C(MAX)][C(OP_MAX)][C(RESULT_MAX)] = { +static u64 mpc7450_cache_events[C(MAX)][C(OP_MAX)][C(RESULT_MAX)] = { [C(L1D)] = { /* RESULT_ACCESS RESULT_MISS */ [C(OP_READ)] = { 0, 0x225 }, [C(OP_WRITE)] = { 0, 0x227 }, diff --git a/arch/powerpc/perf/power5+-pmu.c b/arch/powerpc/perf/power5+-pmu.c index 5f0821e54f042..a62b2cd7914fd 100644 --- a/arch/powerpc/perf/power5+-pmu.c +++ b/arch/powerpc/perf/power5+-pmu.c @@ -619,7 +619,7 @@ static int power5p_generic_events[] = { * 0 means not supported, -1 means nonsensical, other values * are event codes. */ -static int power5p_cache_events[C(MAX)][C(OP_MAX)][C(RESULT_MAX)] = { +static u64 power5p_cache_events[C(MAX)][C(OP_MAX)][C(RESULT_MAX)] = { [C(L1D)] = { /* RESULT_ACCESS RESULT_MISS */ [C(OP_READ)] = { 0x1c10a8, 0x3c1088 }, [C(OP_WRITE)] = { 0x2c10a8, 0xc10c3 }, diff --git a/arch/powerpc/perf/power5-pmu.c b/arch/powerpc/perf/power5-pmu.c index 426021d4b44c3..8732b587cf71d 100644 --- a/arch/powerpc/perf/power5-pmu.c +++ b/arch/powerpc/perf/power5-pmu.c @@ -561,7 +561,7 @@ static int power5_generic_events[] = { * 0 means not supported, -1 means nonsensical, other values * are event codes. */ -static int power5_cache_events[C(MAX)][C(OP_MAX)][C(RESULT_MAX)] = { +static u64 power5_cache_events[C(MAX)][C(OP_MAX)][C(RESULT_MAX)] = { [C(L1D)] = { /* RESULT_ACCESS RESULT_MISS */ [C(OP_READ)] = { 0x4c1090, 0x3c1088 }, [C(OP_WRITE)] = { 0x3c1090, 0xc10c3 }, diff --git a/arch/powerpc/perf/power6-pmu.c b/arch/powerpc/perf/power6-pmu.c index e343a51a989eb..0e318cf87129d 100644 --- a/arch/powerpc/perf/power6-pmu.c +++ b/arch/powerpc/perf/power6-pmu.c @@ -481,7 +481,7 @@ static int power6_generic_events[] = { * are event codes. * The "DTLB" and "ITLB" events relate to the DERAT and IERAT. */ -static int power6_cache_events[C(MAX)][C(OP_MAX)][C(RESULT_MAX)] = { +static u64 power6_cache_events[C(MAX)][C(OP_MAX)][C(RESULT_MAX)] = { [C(L1D)] = { /* RESULT_ACCESS RESULT_MISS */ [C(OP_READ)] = { 0x280030, 0x80080 }, [C(OP_WRITE)] = { 0x180032, 0x80088 }, diff --git a/arch/powerpc/perf/power7-pmu.c b/arch/powerpc/perf/power7-pmu.c index 315233691314a..5e0bf09cf077d 100644 --- a/arch/powerpc/perf/power7-pmu.c +++ b/arch/powerpc/perf/power7-pmu.c @@ -333,7 +333,7 @@ static int power7_generic_events[] = { * 0 means not supported, -1 means nonsensical, other values * are event codes. */ -static int power7_cache_events[C(MAX)][C(OP_MAX)][C(RESULT_MAX)] = { +static u64 power7_cache_events[C(MAX)][C(OP_MAX)][C(RESULT_MAX)] = { [C(L1D)] = { /* RESULT_ACCESS RESULT_MISS */ [C(OP_READ)] = { 0xc880, 0x400f0 }, [C(OP_WRITE)] = { 0, 0x300f0 }, diff --git a/arch/powerpc/perf/power8-pmu.c b/arch/powerpc/perf/power8-pmu.c index 3a5fcc20ff316..5282e8415ddfe 100644 --- a/arch/powerpc/perf/power8-pmu.c +++ b/arch/powerpc/perf/power8-pmu.c @@ -253,7 +253,7 @@ static void power8_config_bhrb(u64 pmu_bhrb_filter) * 0 means not supported, -1 means nonsensical, other values * are event codes. */ -static int power8_cache_events[C(MAX)][C(OP_MAX)][C(RESULT_MAX)] = { +static u64 power8_cache_events[C(MAX)][C(OP_MAX)][C(RESULT_MAX)] = { [ C(L1D) ] = { [ C(OP_READ) ] = { [ C(RESULT_ACCESS) ] = PM_LD_REF_L1, diff --git a/arch/powerpc/perf/power9-pmu.c b/arch/powerpc/perf/power9-pmu.c index 08c3ef7961982..05dae38b969a5 100644 --- a/arch/powerpc/perf/power9-pmu.c +++ b/arch/powerpc/perf/power9-pmu.c @@ -310,7 +310,7 @@ static void power9_config_bhrb(u64 pmu_bhrb_filter) * 0 means not supported, -1 means nonsensical, other values * are event codes. */ -static int power9_cache_events[C(MAX)][C(OP_MAX)][C(RESULT_MAX)] = { +static u64 power9_cache_events[C(MAX)][C(OP_MAX)][C(RESULT_MAX)] = { [ C(L1D) ] = { [ C(OP_READ) ] = { [ C(RESULT_ACCESS) ] = PM_LD_REF_L1, diff --git a/arch/powerpc/perf/ppc970-pmu.c b/arch/powerpc/perf/ppc970-pmu.c index 89a90ab6a6c87..d35223fb112c0 100644 --- a/arch/powerpc/perf/ppc970-pmu.c +++ b/arch/powerpc/perf/ppc970-pmu.c @@ -432,7 +432,7 @@ static int ppc970_generic_events[] = { * 0 means not supported, -1 means nonsensical, other values * are event codes. */ -static int ppc970_cache_events[C(MAX)][C(OP_MAX)][C(RESULT_MAX)] = { +static u64 ppc970_cache_events[C(MAX)][C(OP_MAX)][C(RESULT_MAX)] = { [C(L1D)] = { /* RESULT_ACCESS RESULT_MISS */ [C(OP_READ)] = { 0x8810, 0x3810 }, [C(OP_WRITE)] = { 0x7810, 0x813 }, -- GitLab From c718547e4a92d74089f862457adf1f617c498e16 Mon Sep 17 00:00:00 2001 From: Madhavan Srinivasan Date: Fri, 17 Jul 2020 10:38:16 -0400 Subject: [PATCH 0758/1476] powerpc/perf: Add support for ISA3.1 PMU SPRs PowerISA v3.1 includes new performance monitoring unit(PMU) special purpose registers (SPRs). They are Monitor Mode Control Register 3 (MMCR3) Sampled Instruction Event Register 2 (SIER2) Sampled Instruction Event Register 3 (SIER3) MMCR3 is added for further sampling related configuration control. SIER2/SIER3 are added to provide additional information about the sampled instruction. Patch adds new PPMU flag called "PPMU_ARCH_31" to support handling of these new SPRs, updates the struct thread_struct to include these new SPRs, include MMCR3 in struct mmcr_regs. This is needed to support programming of MMCR3 SPR during event_enable/disable. Patch also adds the sysfs support for the MMCR3 SPR along with SPRN_ macros for these new pmu SPRs. Signed-off-by: Madhavan Srinivasan [mpe: Rename to PPMU_ARCH_31 as noted by jpn] Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/1594996707-3727-5-git-send-email-atrajeev@linux.vnet.ibm.com --- arch/powerpc/include/asm/perf_event_server.h | 2 ++ arch/powerpc/include/asm/processor.h | 4 +++ arch/powerpc/include/asm/reg.h | 6 ++++ arch/powerpc/kernel/sysfs.c | 8 ++++++ arch/powerpc/perf/core-book3s.c | 29 ++++++++++++++++++++ 5 files changed, 49 insertions(+) diff --git a/arch/powerpc/include/asm/perf_event_server.h b/arch/powerpc/include/asm/perf_event_server.h index 14b8dc1305988..86c9eb064b22b 100644 --- a/arch/powerpc/include/asm/perf_event_server.h +++ b/arch/powerpc/include/asm/perf_event_server.h @@ -22,6 +22,7 @@ struct mmcr_regs { unsigned long mmcr1; unsigned long mmcr2; unsigned long mmcra; + unsigned long mmcr3; }; /* * This struct provides the constants and functions needed to @@ -75,6 +76,7 @@ struct power_pmu { #define PPMU_HAS_SIER 0x00000040 /* Has SIER */ #define PPMU_ARCH_207S 0x00000080 /* PMC is architecture v2.07S */ #define PPMU_NO_SIAR 0x00000100 /* Do not use SIAR */ +#define PPMU_ARCH_31 0x00000200 /* Has MMCR3, SIER2 and SIER3 */ /* * Values for flags to get_alternatives() diff --git a/arch/powerpc/include/asm/processor.h b/arch/powerpc/include/asm/processor.h index 6ac12168f1fe8..ed0d633ab5aa4 100644 --- a/arch/powerpc/include/asm/processor.h +++ b/arch/powerpc/include/asm/processor.h @@ -271,6 +271,10 @@ struct thread_struct { unsigned mmcr0; unsigned used_ebb; + unsigned long mmcr3; + unsigned long sier2; + unsigned long sier3; + #endif }; diff --git a/arch/powerpc/include/asm/reg.h b/arch/powerpc/include/asm/reg.h index 88e6c78100d9b..21a1b2d68121f 100644 --- a/arch/powerpc/include/asm/reg.h +++ b/arch/powerpc/include/asm/reg.h @@ -876,7 +876,9 @@ #define MMCR0_FCHV 0x00000001UL /* freeze conditions in hypervisor mode */ #define SPRN_MMCR1 798 #define SPRN_MMCR2 785 +#define SPRN_MMCR3 754 #define SPRN_UMMCR2 769 +#define SPRN_UMMCR3 738 #define SPRN_MMCRA 0x312 #define MMCRA_SDSYNC 0x80000000UL /* SDAR synced with SIAR */ #define MMCRA_SDAR_DCACHE_MISS 0x40000000UL @@ -918,6 +920,10 @@ #define SIER_SIHV 0x1000000 /* Sampled MSR_HV */ #define SIER_SIAR_VALID 0x0400000 /* SIAR contents valid */ #define SIER_SDAR_VALID 0x0200000 /* SDAR contents valid */ +#define SPRN_SIER2 752 +#define SPRN_SIER3 753 +#define SPRN_USIER2 736 +#define SPRN_USIER3 737 #define SPRN_SIAR 796 #define SPRN_SDAR 797 #define SPRN_TACR 888 diff --git a/arch/powerpc/kernel/sysfs.c b/arch/powerpc/kernel/sysfs.c index 571b3259697e4..46b4ebc33db77 100644 --- a/arch/powerpc/kernel/sysfs.c +++ b/arch/powerpc/kernel/sysfs.c @@ -622,8 +622,10 @@ SYSFS_PMCSETUP(pmc7, SPRN_PMC7); SYSFS_PMCSETUP(pmc8, SPRN_PMC8); SYSFS_PMCSETUP(mmcra, SPRN_MMCRA); +SYSFS_PMCSETUP(mmcr3, SPRN_MMCR3); static DEVICE_ATTR(mmcra, 0600, show_mmcra, store_mmcra); +static DEVICE_ATTR(mmcr3, 0600, show_mmcr3, store_mmcr3); #endif /* HAS_PPC_PMC56 */ @@ -886,6 +888,9 @@ static int register_cpu_online(unsigned int cpu) #ifdef CONFIG_PMU_SYSFS if (cpu_has_feature(CPU_FTR_MMCRA)) device_create_file(s, &dev_attr_mmcra); + + if (cpu_has_feature(CPU_FTR_ARCH_31)) + device_create_file(s, &dev_attr_mmcr3); #endif /* CONFIG_PMU_SYSFS */ if (cpu_has_feature(CPU_FTR_PURR)) { @@ -980,6 +985,9 @@ static int unregister_cpu_online(unsigned int cpu) #ifdef CONFIG_PMU_SYSFS if (cpu_has_feature(CPU_FTR_MMCRA)) device_remove_file(s, &dev_attr_mmcra); + + if (cpu_has_feature(CPU_FTR_ARCH_31)) + device_remove_file(s, &dev_attr_mmcr3); #endif /* CONFIG_PMU_SYSFS */ if (cpu_has_feature(CPU_FTR_PURR)) { diff --git a/arch/powerpc/perf/core-book3s.c b/arch/powerpc/perf/core-book3s.c index f4d07b5efd433..cedd558f3c136 100644 --- a/arch/powerpc/perf/core-book3s.c +++ b/arch/powerpc/perf/core-book3s.c @@ -72,6 +72,11 @@ static unsigned int freeze_events_kernel = MMCR0_FCS; /* * 32-bit doesn't have MMCRA but does have an MMCR2, * and a few other names are different. + * Also 32-bit doesn't have MMCR3, SIER2 and SIER3. + * Define them as zero knowing that any code path accessing + * these registers (via mtspr/mfspr) are done under ppmu flag + * check for PPMU_ARCH_31 and we will not enter that code path + * for 32-bit. */ #ifdef CONFIG_PPC32 @@ -85,6 +90,9 @@ static unsigned int freeze_events_kernel = MMCR0_FCS; #define MMCR0_PMCC_U6 0 #define SPRN_MMCRA SPRN_MMCR2 +#define SPRN_MMCR3 0 +#define SPRN_SIER2 0 +#define SPRN_SIER3 0 #define MMCRA_SAMPLE_ENABLE 0 static inline unsigned long perf_ip_adjust(struct pt_regs *regs) @@ -581,6 +589,11 @@ static void ebb_switch_out(unsigned long mmcr0) current->thread.sdar = mfspr(SPRN_SDAR); current->thread.mmcr0 = mmcr0 & MMCR0_USER_MASK; current->thread.mmcr2 = mfspr(SPRN_MMCR2) & MMCR2_USER_MASK; + if (ppmu->flags & PPMU_ARCH_31) { + current->thread.mmcr3 = mfspr(SPRN_MMCR3); + current->thread.sier2 = mfspr(SPRN_SIER2); + current->thread.sier3 = mfspr(SPRN_SIER3); + } } static unsigned long ebb_switch_in(bool ebb, struct cpu_hw_events *cpuhw) @@ -620,6 +633,12 @@ static unsigned long ebb_switch_in(bool ebb, struct cpu_hw_events *cpuhw) * instead manage the MMCR2 entirely by itself. */ mtspr(SPRN_MMCR2, cpuhw->mmcr.mmcr2 | current->thread.mmcr2); + + if (ppmu->flags & PPMU_ARCH_31) { + mtspr(SPRN_MMCR3, current->thread.mmcr3); + mtspr(SPRN_SIER2, current->thread.sier2); + mtspr(SPRN_SIER3, current->thread.sier3); + } out: return mmcr0; } @@ -840,6 +859,11 @@ void perf_event_print_debug(void) pr_info("EBBRR: %016lx BESCR: %016lx\n", mfspr(SPRN_EBBRR), mfspr(SPRN_BESCR)); } + + if (ppmu->flags & PPMU_ARCH_31) { + pr_info("MMCR3: %016lx SIER2: %016lx SIER3: %016lx\n", + mfspr(SPRN_MMCR3), mfspr(SPRN_SIER2), mfspr(SPRN_SIER3)); + } #endif pr_info("SIAR: %016lx SDAR: %016lx SIER: %016lx\n", mfspr(SPRN_SIAR), sdar, sier); @@ -1305,6 +1329,8 @@ static void power_pmu_enable(struct pmu *pmu) if (!cpuhw->n_added) { mtspr(SPRN_MMCRA, cpuhw->mmcr.mmcra & ~MMCRA_SAMPLE_ENABLE); mtspr(SPRN_MMCR1, cpuhw->mmcr.mmcr1); + if (ppmu->flags & PPMU_ARCH_31) + mtspr(SPRN_MMCR3, cpuhw->mmcr.mmcr3); goto out_enable; } @@ -1348,6 +1374,9 @@ static void power_pmu_enable(struct pmu *pmu) if (ppmu->flags & PPMU_ARCH_207S) mtspr(SPRN_MMCR2, cpuhw->mmcr.mmcr2); + if (ppmu->flags & PPMU_ARCH_31) + mtspr(SPRN_MMCR3, cpuhw->mmcr.mmcr3); + /* * Read off any pre-existing events that need to move * to another PMC. -- GitLab From 5752fe0b811bb3cee531c52074921c6dd09dc42d Mon Sep 17 00:00:00 2001 From: Athira Rajeev Date: Fri, 17 Jul 2020 10:38:17 -0400 Subject: [PATCH 0759/1476] KVM: PPC: Book3S HV: Save/restore new PMU registers Power ISA v3.1 has added new performance monitoring unit (PMU) special purpose registers (SPRs). They are: Monitor Mode Control Register 3 (MMCR3) Sampled Instruction Event Register A (SIER2) Sampled Instruction Event Register B (SIER3) Add support to save/restore these new SPRs while entering/exiting guest. Also include changes to support KVM_REG_PPC_MMCR3/SIER2/SIER3. Add new SPRs to KVM API documentation. Signed-off-by: Athira Rajeev Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/1594996707-3727-6-git-send-email-atrajeev@linux.vnet.ibm.com --- Documentation/virt/kvm/api.rst | 3 +++ arch/powerpc/include/asm/kvm_book3s_asm.h | 2 +- arch/powerpc/include/asm/kvm_host.h | 4 ++-- arch/powerpc/include/uapi/asm/kvm.h | 5 +++++ arch/powerpc/kernel/asm-offsets.c | 3 +++ arch/powerpc/kvm/book3s_hv.c | 22 +++++++++++++++++++-- arch/powerpc/kvm/book3s_hv_interrupts.S | 8 ++++++++ arch/powerpc/kvm/book3s_hv_rmhandlers.S | 24 +++++++++++++++++++++++ tools/arch/powerpc/include/uapi/asm/kvm.h | 5 +++++ 9 files changed, 71 insertions(+), 5 deletions(-) diff --git a/Documentation/virt/kvm/api.rst b/Documentation/virt/kvm/api.rst index 426f94582b7a1..8cb6eb76c1292 100644 --- a/Documentation/virt/kvm/api.rst +++ b/Documentation/virt/kvm/api.rst @@ -2156,9 +2156,12 @@ registers, find a list below: PPC KVM_REG_PPC_MMCRA 64 PPC KVM_REG_PPC_MMCR2 64 PPC KVM_REG_PPC_MMCRS 64 + PPC KVM_REG_PPC_MMCR3 64 PPC KVM_REG_PPC_SIAR 64 PPC KVM_REG_PPC_SDAR 64 PPC KVM_REG_PPC_SIER 64 + PPC KVM_REG_PPC_SIER2 64 + PPC KVM_REG_PPC_SIER3 64 PPC KVM_REG_PPC_PMC1 32 PPC KVM_REG_PPC_PMC2 32 PPC KVM_REG_PPC_PMC3 32 diff --git a/arch/powerpc/include/asm/kvm_book3s_asm.h b/arch/powerpc/include/asm/kvm_book3s_asm.h index 45704f2716e2d..078f4648ea27f 100644 --- a/arch/powerpc/include/asm/kvm_book3s_asm.h +++ b/arch/powerpc/include/asm/kvm_book3s_asm.h @@ -119,7 +119,7 @@ struct kvmppc_host_state { void __iomem *xive_tima_virt; u32 saved_xirr; u64 dabr; - u64 host_mmcr[7]; /* MMCR 0,1,A, SIAR, SDAR, MMCR2, SIER */ + u64 host_mmcr[10]; /* MMCR 0,1,A, SIAR, SDAR, MMCR2, SIER, MMCR3, SIER2/3 */ u32 host_pmc[8]; u64 host_purr; u64 host_spurr; diff --git a/arch/powerpc/include/asm/kvm_host.h b/arch/powerpc/include/asm/kvm_host.h index 80b0005a72243..e020d269416d6 100644 --- a/arch/powerpc/include/asm/kvm_host.h +++ b/arch/powerpc/include/asm/kvm_host.h @@ -637,14 +637,14 @@ struct kvm_vcpu_arch { u32 ccr1; u32 dbsr; - u64 mmcr[3]; /* MMCR0, MMCR1, MMCR2 */ + u64 mmcr[4]; /* MMCR0, MMCR1, MMCR2, MMCR3 */ u64 mmcra; u64 mmcrs; u32 pmc[8]; u32 spmc[2]; u64 siar; u64 sdar; - u64 sier; + u64 sier[3]; #ifdef CONFIG_PPC_TRANSACTIONAL_MEM u64 tfhar; u64 texasr; diff --git a/arch/powerpc/include/uapi/asm/kvm.h b/arch/powerpc/include/uapi/asm/kvm.h index 264e266a85bf6..c3af3f324c5ad 100644 --- a/arch/powerpc/include/uapi/asm/kvm.h +++ b/arch/powerpc/include/uapi/asm/kvm.h @@ -640,6 +640,11 @@ struct kvm_ppc_cpu_char { #define KVM_REG_PPC_ONLINE (KVM_REG_PPC | KVM_REG_SIZE_U32 | 0xbf) #define KVM_REG_PPC_PTCR (KVM_REG_PPC | KVM_REG_SIZE_U64 | 0xc0) +/* POWER10 registers */ +#define KVM_REG_PPC_MMCR3 (KVM_REG_PPC | KVM_REG_SIZE_U64 | 0xc1) +#define KVM_REG_PPC_SIER2 (KVM_REG_PPC | KVM_REG_SIZE_U64 | 0xc2) +#define KVM_REG_PPC_SIER3 (KVM_REG_PPC | KVM_REG_SIZE_U64 | 0xc3) + /* Transactional Memory checkpointed state: * This is all GPRs, all VSX regs and a subset of SPRs */ diff --git a/arch/powerpc/kernel/asm-offsets.c b/arch/powerpc/kernel/asm-offsets.c index 6fa4853691dec..8711c2164b45a 100644 --- a/arch/powerpc/kernel/asm-offsets.c +++ b/arch/powerpc/kernel/asm-offsets.c @@ -698,6 +698,9 @@ int main(void) HSTATE_FIELD(HSTATE_SDAR, host_mmcr[4]); HSTATE_FIELD(HSTATE_MMCR2, host_mmcr[5]); HSTATE_FIELD(HSTATE_SIER, host_mmcr[6]); + HSTATE_FIELD(HSTATE_MMCR3, host_mmcr[7]); + HSTATE_FIELD(HSTATE_SIER2, host_mmcr[8]); + HSTATE_FIELD(HSTATE_SIER3, host_mmcr[9]); HSTATE_FIELD(HSTATE_PMC1, host_pmc[0]); HSTATE_FIELD(HSTATE_PMC2, host_pmc[1]); HSTATE_FIELD(HSTATE_PMC3, host_pmc[2]); diff --git a/arch/powerpc/kvm/book3s_hv.c b/arch/powerpc/kvm/book3s_hv.c index b10bb404f0d54..0a0df31987f75 100644 --- a/arch/powerpc/kvm/book3s_hv.c +++ b/arch/powerpc/kvm/book3s_hv.c @@ -1692,6 +1692,9 @@ static int kvmppc_get_one_reg_hv(struct kvm_vcpu *vcpu, u64 id, case KVM_REG_PPC_MMCRS: *val = get_reg_val(id, vcpu->arch.mmcrs); break; + case KVM_REG_PPC_MMCR3: + *val = get_reg_val(id, vcpu->arch.mmcr[3]); + break; case KVM_REG_PPC_PMC1 ... KVM_REG_PPC_PMC8: i = id - KVM_REG_PPC_PMC1; *val = get_reg_val(id, vcpu->arch.pmc[i]); @@ -1707,7 +1710,13 @@ static int kvmppc_get_one_reg_hv(struct kvm_vcpu *vcpu, u64 id, *val = get_reg_val(id, vcpu->arch.sdar); break; case KVM_REG_PPC_SIER: - *val = get_reg_val(id, vcpu->arch.sier); + *val = get_reg_val(id, vcpu->arch.sier[0]); + break; + case KVM_REG_PPC_SIER2: + *val = get_reg_val(id, vcpu->arch.sier[1]); + break; + case KVM_REG_PPC_SIER3: + *val = get_reg_val(id, vcpu->arch.sier[2]); break; case KVM_REG_PPC_IAMR: *val = get_reg_val(id, vcpu->arch.iamr); @@ -1922,6 +1931,9 @@ static int kvmppc_set_one_reg_hv(struct kvm_vcpu *vcpu, u64 id, case KVM_REG_PPC_MMCRS: vcpu->arch.mmcrs = set_reg_val(id, *val); break; + case KVM_REG_PPC_MMCR3: + *val = get_reg_val(id, vcpu->arch.mmcr[3]); + break; case KVM_REG_PPC_PMC1 ... KVM_REG_PPC_PMC8: i = id - KVM_REG_PPC_PMC1; vcpu->arch.pmc[i] = set_reg_val(id, *val); @@ -1937,7 +1949,13 @@ static int kvmppc_set_one_reg_hv(struct kvm_vcpu *vcpu, u64 id, vcpu->arch.sdar = set_reg_val(id, *val); break; case KVM_REG_PPC_SIER: - vcpu->arch.sier = set_reg_val(id, *val); + vcpu->arch.sier[0] = set_reg_val(id, *val); + break; + case KVM_REG_PPC_SIER2: + vcpu->arch.sier[1] = set_reg_val(id, *val); + break; + case KVM_REG_PPC_SIER3: + vcpu->arch.sier[2] = set_reg_val(id, *val); break; case KVM_REG_PPC_IAMR: vcpu->arch.iamr = set_reg_val(id, *val); diff --git a/arch/powerpc/kvm/book3s_hv_interrupts.S b/arch/powerpc/kvm/book3s_hv_interrupts.S index 63fd81f3039dd..59822cba454d2 100644 --- a/arch/powerpc/kvm/book3s_hv_interrupts.S +++ b/arch/powerpc/kvm/book3s_hv_interrupts.S @@ -140,6 +140,14 @@ BEGIN_FTR_SECTION std r8, HSTATE_MMCR2(r13) std r9, HSTATE_SIER(r13) END_FTR_SECTION_IFSET(CPU_FTR_ARCH_207S) +BEGIN_FTR_SECTION + mfspr r5, SPRN_MMCR3 + mfspr r6, SPRN_SIER2 + mfspr r7, SPRN_SIER3 + std r5, HSTATE_MMCR3(r13) + std r6, HSTATE_SIER2(r13) + std r7, HSTATE_SIER3(r13) +END_FTR_SECTION_IFSET(CPU_FTR_ARCH_31) mfspr r3, SPRN_PMC1 mfspr r5, SPRN_PMC2 mfspr r6, SPRN_PMC3 diff --git a/arch/powerpc/kvm/book3s_hv_rmhandlers.S b/arch/powerpc/kvm/book3s_hv_rmhandlers.S index 702eaa2530421..799d6d0f4eade 100644 --- a/arch/powerpc/kvm/book3s_hv_rmhandlers.S +++ b/arch/powerpc/kvm/book3s_hv_rmhandlers.S @@ -3435,6 +3435,14 @@ END_FTR_SECTION_IFSET(CPU_FTR_PMAO_BUG) mtspr SPRN_MMCRA, r6 mtspr SPRN_SIAR, r7 mtspr SPRN_SDAR, r8 +BEGIN_FTR_SECTION + ld r5, VCPU_MMCR + 24(r4) + ld r6, VCPU_SIER + 8(r4) + ld r7, VCPU_SIER + 16(r4) + mtspr SPRN_MMCR3, r5 + mtspr SPRN_SIER2, r6 + mtspr SPRN_SIER3, r7 +END_FTR_SECTION_IFSET(CPU_FTR_ARCH_31) BEGIN_FTR_SECTION ld r5, VCPU_MMCR + 16(r4) ld r6, VCPU_SIER(r4) @@ -3496,6 +3504,14 @@ BEGIN_FTR_SECTION mtspr SPRN_MMCR2, r8 mtspr SPRN_SIER, r9 END_FTR_SECTION_IFSET(CPU_FTR_ARCH_207S) +BEGIN_FTR_SECTION + ld r5, HSTATE_MMCR3(r13) + ld r6, HSTATE_SIER2(r13) + ld r7, HSTATE_SIER3(r13) + mtspr SPRN_MMCR3, r5 + mtspr SPRN_SIER2, r6 + mtspr SPRN_SIER3, r7 +END_FTR_SECTION_IFSET(CPU_FTR_ARCH_31) mtspr SPRN_MMCR0, r3 isync mtlr r0 @@ -3555,6 +3571,14 @@ END_FTR_SECTION_IFSET(CPU_FTR_ARCH_207S) BEGIN_FTR_SECTION std r10, VCPU_MMCR + 16(r9) END_FTR_SECTION_IFSET(CPU_FTR_ARCH_207S) +BEGIN_FTR_SECTION + mfspr r5, SPRN_MMCR3 + mfspr r6, SPRN_SIER2 + mfspr r7, SPRN_SIER3 + std r5, VCPU_MMCR + 24(r9) + std r6, VCPU_SIER + 8(r9) + std r7, VCPU_SIER + 16(r9) +END_FTR_SECTION_IFSET(CPU_FTR_ARCH_31) std r7, VCPU_SIAR(r9) std r8, VCPU_SDAR(r9) mfspr r3, SPRN_PMC1 diff --git a/tools/arch/powerpc/include/uapi/asm/kvm.h b/tools/arch/powerpc/include/uapi/asm/kvm.h index 264e266a85bf6..c3af3f324c5ad 100644 --- a/tools/arch/powerpc/include/uapi/asm/kvm.h +++ b/tools/arch/powerpc/include/uapi/asm/kvm.h @@ -640,6 +640,11 @@ struct kvm_ppc_cpu_char { #define KVM_REG_PPC_ONLINE (KVM_REG_PPC | KVM_REG_SIZE_U32 | 0xbf) #define KVM_REG_PPC_PTCR (KVM_REG_PPC | KVM_REG_SIZE_U64 | 0xc0) +/* POWER10 registers */ +#define KVM_REG_PPC_MMCR3 (KVM_REG_PPC | KVM_REG_SIZE_U64 | 0xc1) +#define KVM_REG_PPC_SIER2 (KVM_REG_PPC | KVM_REG_SIZE_U64 | 0xc2) +#define KVM_REG_PPC_SIER3 (KVM_REG_PPC | KVM_REG_SIZE_U64 | 0xc3) + /* Transactional Memory checkpointed state: * This is all GPRs, all VSX regs and a subset of SPRs */ -- GitLab From 1979ae8c7215718c7a98f038bad0122034ad6529 Mon Sep 17 00:00:00 2001 From: Madhavan Srinivasan Date: Fri, 17 Jul 2020 10:38:18 -0400 Subject: [PATCH 0760/1476] powerpc/xmon: Add PowerISA v3.1 PMU SPRs PowerISA v3.1 added three new perfromance monitoring unit (PMU) speical purpose register (SPR). They are Monitor Mode Control Register 3 (MMCR3), Sampled Instruction Event Register 2 (SIER2), Sampled Instruction Event Register 3 (SIER3). Patch here adds a new dump function dump_310_sprs to print these SPR values. Signed-off-by: Madhavan Srinivasan Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/1594996707-3727-7-git-send-email-atrajeev@linux.vnet.ibm.com --- arch/powerpc/xmon/xmon.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/arch/powerpc/xmon/xmon.c b/arch/powerpc/xmon/xmon.c index d27944e38b044..f08b2ef1995a1 100644 --- a/arch/powerpc/xmon/xmon.c +++ b/arch/powerpc/xmon/xmon.c @@ -2029,6 +2029,18 @@ static void dump_300_sprs(void) #endif } +static void dump_310_sprs(void) +{ +#ifdef CONFIG_PPC64 + if (!cpu_has_feature(CPU_FTR_ARCH_31)) + return; + + printf("mmcr3 = %.16lx, sier2 = %.16lx, sier3 = %.16lx\n", + mfspr(SPRN_MMCR3), mfspr(SPRN_SIER2), mfspr(SPRN_SIER3)); + +#endif +} + static void dump_one_spr(int spr, bool show_unimplemented) { unsigned long val; @@ -2083,6 +2095,7 @@ static void super_regs(void) dump_206_sprs(); dump_207_sprs(); dump_300_sprs(); + dump_310_sprs(); return; } -- GitLab From 9908c826d5ed150637a3a4c0eec5146a0c438f21 Mon Sep 17 00:00:00 2001 From: Madhavan Srinivasan Date: Fri, 17 Jul 2020 10:38:19 -0400 Subject: [PATCH 0761/1476] powerpc/perf: Add Power10 PMU feature to DT CPU features Add Power10 feature function to DT CPU features, along with a Power10 specific init() to initialize PMU SPRs, sets the oprofile_cpu_type and cpu_features. This will enable performance monitoring unit (PMU) for Power10 in CPU features with "performance-monitor-power10". For Power ISA v3.1, BHRB disable is controlled via Monitor Mode Control Register A (MMCRA) bit, namely "BHRB Recording Disable (BHRBRD)". This patch initializes MMCRA BHRBRD to disable BHRB feature at boot for Power10. Signed-off-by: Madhavan Srinivasan [mpe: Move MMCRA_BHRB_DISABLE as noted by jpn, drop CPU setup changes] Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/1594996707-3727-8-git-send-email-atrajeev@linux.vnet.ibm.com --- arch/powerpc/include/asm/reg.h | 1 + arch/powerpc/kernel/dt_cpu_ftrs.c | 26 ++++++++++++++++++++++++++ arch/powerpc/perf/core-book3s.c | 1 + 3 files changed, 28 insertions(+) diff --git a/arch/powerpc/include/asm/reg.h b/arch/powerpc/include/asm/reg.h index 21a1b2d68121f..97efbe2973fab 100644 --- a/arch/powerpc/include/asm/reg.h +++ b/arch/powerpc/include/asm/reg.h @@ -888,6 +888,7 @@ #define MMCRA_SLOT 0x07000000UL /* SLOT bits (37-39) */ #define MMCRA_SLOT_SHIFT 24 #define MMCRA_SAMPLE_ENABLE 0x00000001UL /* enable sampling */ +#define MMCRA_BHRB_DISABLE 0x2000000000UL // BHRB disable bit for ISA v3.1 #define POWER6_MMCRA_SDSYNC 0x0000080000000000ULL /* SDAR/SIAR synced */ #define POWER6_MMCRA_SIHV 0x0000040000000000ULL #define POWER6_MMCRA_SIPR 0x0000020000000000ULL diff --git a/arch/powerpc/kernel/dt_cpu_ftrs.c b/arch/powerpc/kernel/dt_cpu_ftrs.c index 9aa8537f7da2d..0d65d95977780 100644 --- a/arch/powerpc/kernel/dt_cpu_ftrs.c +++ b/arch/powerpc/kernel/dt_cpu_ftrs.c @@ -450,6 +450,31 @@ static int __init feat_enable_pmu_power9(struct dt_cpu_feature *f) return 1; } +static void init_pmu_power10(void) +{ + init_pmu_power9(); + + mtspr(SPRN_MMCR3, 0); + mtspr(SPRN_MMCRA, MMCRA_BHRB_DISABLE); +} + +static int __init feat_enable_pmu_power10(struct dt_cpu_feature *f) +{ + hfscr_pmu_enable(); + + init_pmu_power10(); + init_pmu_registers = init_pmu_power10; + + cur_cpu_spec->cpu_features |= CPU_FTR_MMCRA; + cur_cpu_spec->cpu_user_features |= PPC_FEATURE_PSERIES_PERFMON_COMPAT; + + cur_cpu_spec->num_pmcs = 6; + cur_cpu_spec->pmc_type = PPC_PMC_IBM; + cur_cpu_spec->oprofile_cpu_type = "ppc64/power10"; + + return 1; +} + static int __init feat_enable_tm(struct dt_cpu_feature *f) { #ifdef CONFIG_PPC_TRANSACTIONAL_MEM @@ -639,6 +664,7 @@ static struct dt_cpu_feature_match __initdata {"pc-relative-addressing", feat_enable, 0}, {"machine-check-power9", feat_enable_mce_power9, 0}, {"performance-monitor-power9", feat_enable_pmu_power9, 0}, + {"performance-monitor-power10", feat_enable_pmu_power10, 0}, {"event-based-branch-v3", feat_enable, 0}, {"random-number-generator", feat_enable, 0}, {"system-call-vectored", feat_disable, 0}, diff --git a/arch/powerpc/perf/core-book3s.c b/arch/powerpc/perf/core-book3s.c index cedd558f3c136..277d975b478c3 100644 --- a/arch/powerpc/perf/core-book3s.c +++ b/arch/powerpc/perf/core-book3s.c @@ -94,6 +94,7 @@ static unsigned int freeze_events_kernel = MMCR0_FCS; #define SPRN_SIER2 0 #define SPRN_SIER3 0 #define MMCRA_SAMPLE_ENABLE 0 +#define MMCRA_BHRB_DISABLE 0 static inline unsigned long perf_ip_adjust(struct pt_regs *regs) { -- GitLab From a64e697cef23b3d24bac700f6d66c8e2bf8efccc Mon Sep 17 00:00:00 2001 From: Athira Rajeev Date: Fri, 17 Jul 2020 10:38:20 -0400 Subject: [PATCH 0762/1476] powerpc/perf: power10 Performance Monitoring support Base enablement patch to register performance monitoring hardware support for power10. Patch introduce the raw event encoding format, defines the supported list of events, config fields for the event attributes and their corresponding bit values which are exported via sysfs. Patch also enhances the support function in isa207_common.c to include power10 pmu hardware. Reported-by: kernel test robot Signed-off-by: Madhavan Srinivasan Signed-off-by: Athira Rajeev Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/1594996707-3727-9-git-send-email-atrajeev@linux.vnet.ibm.com --- arch/powerpc/perf/Makefile | 2 +- arch/powerpc/perf/core-book3s.c | 2 + arch/powerpc/perf/internal.h | 1 + arch/powerpc/perf/isa207-common.c | 59 +++- arch/powerpc/perf/isa207-common.h | 33 +- arch/powerpc/perf/power10-events-list.h | 70 ++++ arch/powerpc/perf/power10-pmu.c | 410 ++++++++++++++++++++++++ 7 files changed, 566 insertions(+), 11 deletions(-) create mode 100644 arch/powerpc/perf/power10-events-list.h create mode 100644 arch/powerpc/perf/power10-pmu.c diff --git a/arch/powerpc/perf/Makefile b/arch/powerpc/perf/Makefile index 53d614e985375..c02854dea2b20 100644 --- a/arch/powerpc/perf/Makefile +++ b/arch/powerpc/perf/Makefile @@ -9,7 +9,7 @@ obj-$(CONFIG_PPC_PERF_CTRS) += core-book3s.o bhrb.o obj64-$(CONFIG_PPC_PERF_CTRS) += ppc970-pmu.o power5-pmu.o \ power5+-pmu.o power6-pmu.o power7-pmu.o \ isa207-common.o power8-pmu.o power9-pmu.o \ - generic-compat-pmu.o + generic-compat-pmu.o power10-pmu.o obj32-$(CONFIG_PPC_PERF_CTRS) += mpc7450-pmu.o obj-$(CONFIG_PPC_POWERNV) += imc-pmu.o diff --git a/arch/powerpc/perf/core-book3s.c b/arch/powerpc/perf/core-book3s.c index 277d975b478c3..6bffffa22ee83 100644 --- a/arch/powerpc/perf/core-book3s.c +++ b/arch/powerpc/perf/core-book3s.c @@ -2333,6 +2333,8 @@ static int __init init_ppc64_pmu(void) return 0; else if (!init_power9_pmu()) return 0; + else if (!init_power10_pmu()) + return 0; else if (!init_ppc970_pmu()) return 0; else diff --git a/arch/powerpc/perf/internal.h b/arch/powerpc/perf/internal.h index f755c64da1377..80bbf72bfec28 100644 --- a/arch/powerpc/perf/internal.h +++ b/arch/powerpc/perf/internal.h @@ -9,4 +9,5 @@ extern int init_power6_pmu(void); extern int init_power7_pmu(void); extern int init_power8_pmu(void); extern int init_power9_pmu(void); +extern int init_power10_pmu(void); extern int init_generic_compat_pmu(void); diff --git a/arch/powerpc/perf/isa207-common.c b/arch/powerpc/perf/isa207-common.c index 2fe63f2de4b09..77643f3ae4645 100644 --- a/arch/powerpc/perf/isa207-common.c +++ b/arch/powerpc/perf/isa207-common.c @@ -55,7 +55,9 @@ static bool is_event_valid(u64 event) { u64 valid_mask = EVENT_VALID_MASK; - if (cpu_has_feature(CPU_FTR_ARCH_300)) + if (cpu_has_feature(CPU_FTR_ARCH_31)) + valid_mask = p10_EVENT_VALID_MASK; + else if (cpu_has_feature(CPU_FTR_ARCH_300)) valid_mask = p9_EVENT_VALID_MASK; return !(event & ~valid_mask); @@ -69,6 +71,14 @@ static inline bool is_event_marked(u64 event) return false; } +static unsigned long sdar_mod_val(u64 event) +{ + if (cpu_has_feature(CPU_FTR_ARCH_31)) + return p10_SDAR_MODE(event); + + return p9_SDAR_MODE(event); +} + static void mmcra_sdar_mode(u64 event, unsigned long *mmcra) { /* @@ -79,7 +89,7 @@ static void mmcra_sdar_mode(u64 event, unsigned long *mmcra) * MMCRA[SDAR_MODE] will be programmed as "0b01" for continous sampling * mode and will be un-changed when setting MMCRA[63] (Marked events). * - * Incase of Power9: + * Incase of Power9/power10: * Marked event: MMCRA[SDAR_MODE] will be set to 0b00 ('No Updates'), * or if group already have any marked events. * For rest @@ -90,8 +100,8 @@ static void mmcra_sdar_mode(u64 event, unsigned long *mmcra) if (cpu_has_feature(CPU_FTR_ARCH_300)) { if (is_event_marked(event) || (*mmcra & MMCRA_SAMPLE_ENABLE)) *mmcra &= MMCRA_SDAR_MODE_NO_UPDATES; - else if (p9_SDAR_MODE(event)) - *mmcra |= p9_SDAR_MODE(event) << MMCRA_SDAR_MODE_SHIFT; + else if (sdar_mod_val(event)) + *mmcra |= sdar_mod_val(event) << MMCRA_SDAR_MODE_SHIFT; else *mmcra |= MMCRA_SDAR_MODE_DCACHE; } else @@ -134,7 +144,11 @@ static bool is_thresh_cmp_valid(u64 event) /* * Check the mantissa upper two bits are not zero, unless the * exponent is also zero. See the THRESH_CMP_MANTISSA doc. + * Power10: thresh_cmp is replaced by l2_l3 event select. */ + if (cpu_has_feature(CPU_FTR_ARCH_31)) + return false; + cmp = (event >> EVENT_THR_CMP_SHIFT) & EVENT_THR_CMP_MASK; exp = cmp >> 7; @@ -251,7 +265,12 @@ int isa207_get_constraint(u64 event, unsigned long *maskp, unsigned long *valp) pmc = (event >> EVENT_PMC_SHIFT) & EVENT_PMC_MASK; unit = (event >> EVENT_UNIT_SHIFT) & EVENT_UNIT_MASK; - cache = (event >> EVENT_CACHE_SEL_SHIFT) & EVENT_CACHE_SEL_MASK; + if (cpu_has_feature(CPU_FTR_ARCH_31)) + cache = (event >> EVENT_CACHE_SEL_SHIFT) & + p10_EVENT_CACHE_SEL_MASK; + else + cache = (event >> EVENT_CACHE_SEL_SHIFT) & + EVENT_CACHE_SEL_MASK; ebb = (event >> EVENT_EBB_SHIFT) & EVENT_EBB_MASK; if (pmc) { @@ -283,7 +302,10 @@ int isa207_get_constraint(u64 event, unsigned long *maskp, unsigned long *valp) } if (unit >= 6 && unit <= 9) { - if (cpu_has_feature(CPU_FTR_ARCH_300)) { + if (cpu_has_feature(CPU_FTR_ARCH_31) && (unit == 6)) { + mask |= CNST_L2L3_GROUP_MASK; + value |= CNST_L2L3_GROUP_VAL(event >> p10_L2L3_EVENT_SHIFT); + } else if (cpu_has_feature(CPU_FTR_ARCH_300)) { mask |= CNST_CACHE_GROUP_MASK; value |= CNST_CACHE_GROUP_VAL(event & 0xff); @@ -367,6 +389,7 @@ int isa207_compute_mmcr(u64 event[], int n_ev, struct perf_event *pevents[]) { unsigned long mmcra, mmcr1, mmcr2, unit, combine, psel, cache, val; + unsigned long mmcr3; unsigned int pmc, pmc_inuse; int i; @@ -379,7 +402,7 @@ int isa207_compute_mmcr(u64 event[], int n_ev, pmc_inuse |= 1 << pmc; } - mmcra = mmcr1 = mmcr2 = 0; + mmcra = mmcr1 = mmcr2 = mmcr3 = 0; /* Second pass: assign PMCs, set all MMCR1 fields */ for (i = 0; i < n_ev; ++i) { @@ -438,8 +461,17 @@ int isa207_compute_mmcr(u64 event[], int n_ev, mmcra |= val << MMCRA_THR_CTL_SHIFT; val = (event[i] >> EVENT_THR_SEL_SHIFT) & EVENT_THR_SEL_MASK; mmcra |= val << MMCRA_THR_SEL_SHIFT; - val = (event[i] >> EVENT_THR_CMP_SHIFT) & EVENT_THR_CMP_MASK; - mmcra |= thresh_cmp_val(val); + if (!cpu_has_feature(CPU_FTR_ARCH_31)) { + val = (event[i] >> EVENT_THR_CMP_SHIFT) & + EVENT_THR_CMP_MASK; + mmcra |= thresh_cmp_val(val); + } + } + + if (cpu_has_feature(CPU_FTR_ARCH_31) && (unit == 6)) { + val = (event[i] >> p10_L2L3_EVENT_SHIFT) & + p10_EVENT_L2L3_SEL_MASK; + mmcr2 |= val << p10_L2L3_SEL_SHIFT; } if (event[i] & EVENT_WANTS_BHRB) { @@ -460,6 +492,14 @@ int isa207_compute_mmcr(u64 event[], int n_ev, mmcr2 |= MMCR2_FCS(pmc); } + if (cpu_has_feature(CPU_FTR_ARCH_31)) { + if (pmc <= 4) { + val = (event[i] >> p10_EVENT_MMCR3_SHIFT) & + p10_EVENT_MMCR3_MASK; + mmcr3 |= val << MMCR3_SHIFT(pmc); + } + } + hwc[i] = pmc - 1; } @@ -480,6 +520,7 @@ int isa207_compute_mmcr(u64 event[], int n_ev, mmcr->mmcr1 = mmcr1; mmcr->mmcra = mmcra; mmcr->mmcr2 = mmcr2; + mmcr->mmcr3 = mmcr3; return 0; } diff --git a/arch/powerpc/perf/isa207-common.h b/arch/powerpc/perf/isa207-common.h index df968fde65a1f..044de65e96b92 100644 --- a/arch/powerpc/perf/isa207-common.h +++ b/arch/powerpc/perf/isa207-common.h @@ -87,6 +87,31 @@ EVENT_LINUX_MASK | \ EVENT_PSEL_MASK)) +/* Contants to support power10 raw encoding format */ +#define p10_SDAR_MODE_SHIFT 22 +#define p10_SDAR_MODE_MASK 0x3ull +#define p10_SDAR_MODE(v) (((v) >> p10_SDAR_MODE_SHIFT) & \ + p10_SDAR_MODE_MASK) +#define p10_EVENT_L2L3_SEL_MASK 0x1f +#define p10_L2L3_SEL_SHIFT 3 +#define p10_L2L3_EVENT_SHIFT 40 +#define p10_EVENT_THRESH_MASK 0xffffull +#define p10_EVENT_CACHE_SEL_MASK 0x3ull +#define p10_EVENT_MMCR3_MASK 0x7fffull +#define p10_EVENT_MMCR3_SHIFT 45 + +#define p10_EVENT_VALID_MASK \ + ((p10_SDAR_MODE_MASK << p10_SDAR_MODE_SHIFT | \ + (p10_EVENT_THRESH_MASK << EVENT_THRESH_SHIFT) | \ + (EVENT_SAMPLE_MASK << EVENT_SAMPLE_SHIFT) | \ + (p10_EVENT_CACHE_SEL_MASK << EVENT_CACHE_SEL_SHIFT) | \ + (EVENT_PMC_MASK << EVENT_PMC_SHIFT) | \ + (EVENT_UNIT_MASK << EVENT_UNIT_SHIFT) | \ + (p9_EVENT_COMBINE_MASK << p9_EVENT_COMBINE_SHIFT) | \ + (p10_EVENT_MMCR3_MASK << p10_EVENT_MMCR3_SHIFT) | \ + (EVENT_MARKED_MASK << EVENT_MARKED_SHIFT) | \ + EVENT_LINUX_MASK | \ + EVENT_PSEL_MASK)) /* * Layout of constraint bits: * @@ -135,6 +160,9 @@ #define CNST_CACHE_PMC4_VAL (1ull << 54) #define CNST_CACHE_PMC4_MASK CNST_CACHE_PMC4_VAL +#define CNST_L2L3_GROUP_VAL(v) (((v) & 0x1full) << 55) +#define CNST_L2L3_GROUP_MASK CNST_L2L3_GROUP_VAL(0x1f) + /* * For NC we are counting up to 4 events. This requires three bits, and we need * the fifth event to overflow and set the 4th bit. To achieve that we bias the @@ -191,7 +219,7 @@ #define MMCRA_THR_CTR_EXP(v) (((v) >> MMCRA_THR_CTR_EXP_SHIFT) &\ MMCRA_THR_CTR_EXP_MASK) -/* MMCR1 Threshold Compare bit constant for power9 */ +/* MMCRA Threshold Compare bit constant for power9 */ #define p9_MMCRA_THR_CMP_SHIFT 45 /* Bits in MMCR2 for PowerISA v2.07 */ @@ -202,6 +230,9 @@ #define MAX_ALT 2 #define MAX_PMU_COUNTERS 6 +/* Bits in MMCR3 for PowerISA v3.10 */ +#define MMCR3_SHIFT(pmc) (49 - (15 * ((pmc) - 1))) + #define ISA207_SIER_TYPE_SHIFT 15 #define ISA207_SIER_TYPE_MASK (0x7ull << ISA207_SIER_TYPE_SHIFT) diff --git a/arch/powerpc/perf/power10-events-list.h b/arch/powerpc/perf/power10-events-list.h new file mode 100644 index 0000000000000..60c1b8111082d --- /dev/null +++ b/arch/powerpc/perf/power10-events-list.h @@ -0,0 +1,70 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Performance counter support for POWER10 processors. + * + * Copyright 2020 Madhavan Srinivasan, IBM Corporation. + * Copyright 2020 Athira Rajeev, IBM Corporation. + */ + +/* + * Power10 event codes. + */ +EVENT(PM_RUN_CYC, 0x600f4); +EVENT(PM_DISP_STALL_CYC, 0x100f8); +EVENT(PM_EXEC_STALL, 0x30008); +EVENT(PM_RUN_INST_CMPL, 0x500fa); +EVENT(PM_BR_CMPL, 0x4d05e); +EVENT(PM_BR_MPRED_CMPL, 0x400f6); + +/* All L1 D cache load references counted at finish, gated by reject */ +EVENT(PM_LD_REF_L1, 0x100fc); +/* Load Missed L1 */ +EVENT(PM_LD_MISS_L1, 0x3e054); +/* Store Missed L1 */ +EVENT(PM_ST_MISS_L1, 0x300f0); +/* L1 cache data prefetches */ +EVENT(PM_LD_PREFETCH_CACHE_LINE_MISS, 0x1002c); +/* Demand iCache Miss */ +EVENT(PM_L1_ICACHE_MISS, 0x200fc); +/* Instruction fetches from L1 */ +EVENT(PM_INST_FROM_L1, 0x04080); +/* Instruction Demand sectors wriittent into IL1 */ +EVENT(PM_INST_FROM_L1MISS, 0x03f00000001c040); +/* Instruction prefetch written into IL1 */ +EVENT(PM_IC_PREF_REQ, 0x040a0); +/* The data cache was reloaded from local core's L3 due to a demand load */ +EVENT(PM_DATA_FROM_L3, 0x01340000001c040); +/* Demand LD - L3 Miss (not L2 hit and not L3 hit) */ +EVENT(PM_DATA_FROM_L3MISS, 0x300fe); +/* Data PTEG reload */ +EVENT(PM_DTLB_MISS, 0x300fc); +/* ITLB Reloaded */ +EVENT(PM_ITLB_MISS, 0x400fc); + +EVENT(PM_RUN_CYC_ALT, 0x0001e); +EVENT(PM_RUN_INST_CMPL_ALT, 0x00002); + +/* + * Memory Access Events + * + * Primary PMU event used here is PM_MRK_INST_CMPL (0x401e0) + * To enable capturing of memory profiling, these MMCRA bits + * needs to be programmed and corresponding raw event format + * encoding. + * + * MMCRA bits encoding needed are + * SM (Sampling Mode) + * EM (Eligibility for Random Sampling) + * TECE (Threshold Event Counter Event) + * TS (Threshold Start Event) + * TE (Threshold End Event) + * + * Corresponding Raw Encoding bits: + * sample [EM,SM] + * thresh_sel (TECE) + * thresh start (TS) + * thresh end (TE) + */ + +EVENT(MEM_LOADS, 0x34340401e0); +EVENT(MEM_STORES, 0x343c0401e0); diff --git a/arch/powerpc/perf/power10-pmu.c b/arch/powerpc/perf/power10-pmu.c new file mode 100644 index 0000000000000..4b65e95c1e013 --- /dev/null +++ b/arch/powerpc/perf/power10-pmu.c @@ -0,0 +1,410 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Performance counter support for POWER10 processors. + * + * Copyright 2020 Madhavan Srinivasan, IBM Corporation. + * Copyright 2020 Athira Rajeev, IBM Corporation. + */ + +#define pr_fmt(fmt) "power10-pmu: " fmt + +#include "isa207-common.h" +#include "internal.h" + +/* + * Raw event encoding for Power10: + * + * 60 56 52 48 44 40 36 32 + * | - - - - | - - - - | - - - - | - - - - | - - - - | - - - - | - - - - | - - - - | + * | | [ ] [ src_match ] [ src_mask ] | [ ] [ l2l3_sel ] [ thresh_ctl ] + * | | | | | | + * | | *- IFM (Linux) | | thresh start/stop -* + * | *- BHRB (Linux) | src_sel + * *- EBB (Linux) *invert_bit + * + * 28 24 20 16 12 8 4 0 + * | - - - - | - - - - | - - - - | - - - - | - - - - | - - - - | - - - - | - - - - | + * [ ] [ sample ] [ ] [ ] [ pmc ] [unit ] [ ] m [ pmcxsel ] + * | | | | | | + * | | | | | *- mark + * | | | *- L1/L2/L3 cache_sel | + * | | sdar_mode | + * | *- sampling mode for marked events *- combine + * | + * *- thresh_sel + * + * Below uses IBM bit numbering. + * + * MMCR1[x:y] = unit (PMCxUNIT) + * MMCR1[24] = pmc1combine[0] + * MMCR1[25] = pmc1combine[1] + * MMCR1[26] = pmc2combine[0] + * MMCR1[27] = pmc2combine[1] + * MMCR1[28] = pmc3combine[0] + * MMCR1[29] = pmc3combine[1] + * MMCR1[30] = pmc4combine[0] + * MMCR1[31] = pmc4combine[1] + * + * if pmc == 3 and unit == 0 and pmcxsel[0:6] == 0b0101011 + * MMCR1[20:27] = thresh_ctl + * else if pmc == 4 and unit == 0xf and pmcxsel[0:6] == 0b0101001 + * MMCR1[20:27] = thresh_ctl + * else + * MMCRA[48:55] = thresh_ctl (THRESH START/END) + * + * if thresh_sel: + * MMCRA[45:47] = thresh_sel + * + * if l2l3_sel: + * MMCR2[56:60] = l2l3_sel[0:4] + * + * MMCR1[16] = cache_sel[0] + * MMCR1[17] = cache_sel[1] + * + * if mark: + * MMCRA[63] = 1 (SAMPLE_ENABLE) + * MMCRA[57:59] = sample[0:2] (RAND_SAMP_ELIG) + * MMCRA[61:62] = sample[3:4] (RAND_SAMP_MODE) + * + * if EBB and BHRB: + * MMCRA[32:33] = IFM + * + * MMCRA[SDAR_MODE] = sdar_mode[0:1] + */ + +/* + * Some power10 event codes. + */ +#define EVENT(_name, _code) enum{_name = _code} + +#include "power10-events-list.h" + +#undef EVENT + +/* MMCRA IFM bits - POWER10 */ +#define POWER10_MMCRA_IFM1 0x0000000040000000UL +#define POWER10_MMCRA_BHRB_MASK 0x00000000C0000000UL + +/* Table of alternatives, sorted by column 0 */ +static const unsigned int power10_event_alternatives[][MAX_ALT] = { + { PM_RUN_CYC_ALT, PM_RUN_CYC }, + { PM_RUN_INST_CMPL_ALT, PM_RUN_INST_CMPL }, +}; + +static int power10_get_alternatives(u64 event, unsigned int flags, u64 alt[]) +{ + int num_alt = 0; + + num_alt = isa207_get_alternatives(event, alt, + ARRAY_SIZE(power10_event_alternatives), flags, + power10_event_alternatives); + + return num_alt; +} + +GENERIC_EVENT_ATTR(cpu-cycles, PM_RUN_CYC); +GENERIC_EVENT_ATTR(instructions, PM_RUN_INST_CMPL); +GENERIC_EVENT_ATTR(branch-instructions, PM_BR_CMPL); +GENERIC_EVENT_ATTR(branch-misses, PM_BR_MPRED_CMPL); +GENERIC_EVENT_ATTR(cache-references, PM_LD_REF_L1); +GENERIC_EVENT_ATTR(cache-misses, PM_LD_MISS_L1); +GENERIC_EVENT_ATTR(mem-loads, MEM_LOADS); +GENERIC_EVENT_ATTR(mem-stores, MEM_STORES); + +CACHE_EVENT_ATTR(L1-dcache-load-misses, PM_LD_MISS_L1); +CACHE_EVENT_ATTR(L1-dcache-loads, PM_LD_REF_L1); +CACHE_EVENT_ATTR(L1-dcache-prefetches, PM_LD_PREFETCH_CACHE_LINE_MISS); +CACHE_EVENT_ATTR(L1-dcache-store-misses, PM_ST_MISS_L1); +CACHE_EVENT_ATTR(L1-icache-load-misses, PM_L1_ICACHE_MISS); +CACHE_EVENT_ATTR(L1-icache-loads, PM_INST_FROM_L1); +CACHE_EVENT_ATTR(L1-icache-prefetches, PM_IC_PREF_REQ); +CACHE_EVENT_ATTR(LLC-load-misses, PM_DATA_FROM_L3MISS); +CACHE_EVENT_ATTR(LLC-loads, PM_DATA_FROM_L3); +CACHE_EVENT_ATTR(branch-load-misses, PM_BR_MPRED_CMPL); +CACHE_EVENT_ATTR(branch-loads, PM_BR_CMPL); +CACHE_EVENT_ATTR(dTLB-load-misses, PM_DTLB_MISS); +CACHE_EVENT_ATTR(iTLB-load-misses, PM_ITLB_MISS); + +static struct attribute *power10_events_attr[] = { + GENERIC_EVENT_PTR(PM_RUN_CYC), + GENERIC_EVENT_PTR(PM_RUN_INST_CMPL), + GENERIC_EVENT_PTR(PM_BR_CMPL), + GENERIC_EVENT_PTR(PM_BR_MPRED_CMPL), + GENERIC_EVENT_PTR(PM_LD_REF_L1), + GENERIC_EVENT_PTR(PM_LD_MISS_L1), + GENERIC_EVENT_PTR(MEM_LOADS), + GENERIC_EVENT_PTR(MEM_STORES), + CACHE_EVENT_PTR(PM_LD_MISS_L1), + CACHE_EVENT_PTR(PM_LD_REF_L1), + CACHE_EVENT_PTR(PM_LD_PREFETCH_CACHE_LINE_MISS), + CACHE_EVENT_PTR(PM_ST_MISS_L1), + CACHE_EVENT_PTR(PM_L1_ICACHE_MISS), + CACHE_EVENT_PTR(PM_INST_FROM_L1), + CACHE_EVENT_PTR(PM_IC_PREF_REQ), + CACHE_EVENT_PTR(PM_DATA_FROM_L3MISS), + CACHE_EVENT_PTR(PM_DATA_FROM_L3), + CACHE_EVENT_PTR(PM_BR_MPRED_CMPL), + CACHE_EVENT_PTR(PM_BR_CMPL), + CACHE_EVENT_PTR(PM_DTLB_MISS), + CACHE_EVENT_PTR(PM_ITLB_MISS), + NULL +}; + +static struct attribute_group power10_pmu_events_group = { + .name = "events", + .attrs = power10_events_attr, +}; + +PMU_FORMAT_ATTR(event, "config:0-59"); +PMU_FORMAT_ATTR(pmcxsel, "config:0-7"); +PMU_FORMAT_ATTR(mark, "config:8"); +PMU_FORMAT_ATTR(combine, "config:10-11"); +PMU_FORMAT_ATTR(unit, "config:12-15"); +PMU_FORMAT_ATTR(pmc, "config:16-19"); +PMU_FORMAT_ATTR(cache_sel, "config:20-21"); +PMU_FORMAT_ATTR(sdar_mode, "config:22-23"); +PMU_FORMAT_ATTR(sample_mode, "config:24-28"); +PMU_FORMAT_ATTR(thresh_sel, "config:29-31"); +PMU_FORMAT_ATTR(thresh_stop, "config:32-35"); +PMU_FORMAT_ATTR(thresh_start, "config:36-39"); +PMU_FORMAT_ATTR(l2l3_sel, "config:40-44"); +PMU_FORMAT_ATTR(src_sel, "config:45-46"); +PMU_FORMAT_ATTR(invert_bit, "config:47"); +PMU_FORMAT_ATTR(src_mask, "config:48-53"); +PMU_FORMAT_ATTR(src_match, "config:54-59"); + +static struct attribute *power10_pmu_format_attr[] = { + &format_attr_event.attr, + &format_attr_pmcxsel.attr, + &format_attr_mark.attr, + &format_attr_combine.attr, + &format_attr_unit.attr, + &format_attr_pmc.attr, + &format_attr_cache_sel.attr, + &format_attr_sdar_mode.attr, + &format_attr_sample_mode.attr, + &format_attr_thresh_sel.attr, + &format_attr_thresh_stop.attr, + &format_attr_thresh_start.attr, + &format_attr_l2l3_sel.attr, + &format_attr_src_sel.attr, + &format_attr_invert_bit.attr, + &format_attr_src_mask.attr, + &format_attr_src_match.attr, + NULL, +}; + +static struct attribute_group power10_pmu_format_group = { + .name = "format", + .attrs = power10_pmu_format_attr, +}; + +static const struct attribute_group *power10_pmu_attr_groups[] = { + &power10_pmu_format_group, + &power10_pmu_events_group, + NULL, +}; + +static int power10_generic_events[] = { + [PERF_COUNT_HW_CPU_CYCLES] = PM_RUN_CYC, + [PERF_COUNT_HW_INSTRUCTIONS] = PM_RUN_INST_CMPL, + [PERF_COUNT_HW_BRANCH_INSTRUCTIONS] = PM_BR_CMPL, + [PERF_COUNT_HW_BRANCH_MISSES] = PM_BR_MPRED_CMPL, + [PERF_COUNT_HW_CACHE_REFERENCES] = PM_LD_REF_L1, + [PERF_COUNT_HW_CACHE_MISSES] = PM_LD_MISS_L1, +}; + +static u64 power10_bhrb_filter_map(u64 branch_sample_type) +{ + u64 pmu_bhrb_filter = 0; + + /* BHRB and regular PMU events share the same privilege state + * filter configuration. BHRB is always recorded along with a + * regular PMU event. As the privilege state filter is handled + * in the basic PMC configuration of the accompanying regular + * PMU event, we ignore any separate BHRB specific request. + */ + + /* No branch filter requested */ + if (branch_sample_type & PERF_SAMPLE_BRANCH_ANY) + return pmu_bhrb_filter; + + /* Invalid branch filter options - HW does not support */ + if (branch_sample_type & PERF_SAMPLE_BRANCH_ANY_RETURN) + return -1; + + if (branch_sample_type & PERF_SAMPLE_BRANCH_IND_CALL) + return -1; + + if (branch_sample_type & PERF_SAMPLE_BRANCH_CALL) + return -1; + + if (branch_sample_type & PERF_SAMPLE_BRANCH_ANY_CALL) { + pmu_bhrb_filter |= POWER10_MMCRA_IFM1; + return pmu_bhrb_filter; + } + + /* Every thing else is unsupported */ + return -1; +} + +static void power10_config_bhrb(u64 pmu_bhrb_filter) +{ + pmu_bhrb_filter &= POWER10_MMCRA_BHRB_MASK; + + /* Enable BHRB filter in PMU */ + mtspr(SPRN_MMCRA, (mfspr(SPRN_MMCRA) | pmu_bhrb_filter)); +} + +#define C(x) PERF_COUNT_HW_CACHE_##x + +/* + * Table of generalized cache-related events. + * 0 means not supported, -1 means nonsensical, other values + * are event codes. + */ +static u64 power10_cache_events[C(MAX)][C(OP_MAX)][C(RESULT_MAX)] = { + [C(L1D)] = { + [C(OP_READ)] = { + [C(RESULT_ACCESS)] = PM_LD_REF_L1, + [C(RESULT_MISS)] = PM_LD_MISS_L1, + }, + [C(OP_WRITE)] = { + [C(RESULT_ACCESS)] = 0, + [C(RESULT_MISS)] = PM_ST_MISS_L1, + }, + [C(OP_PREFETCH)] = { + [C(RESULT_ACCESS)] = PM_LD_PREFETCH_CACHE_LINE_MISS, + [C(RESULT_MISS)] = 0, + }, + }, + [C(L1I)] = { + [C(OP_READ)] = { + [C(RESULT_ACCESS)] = PM_INST_FROM_L1, + [C(RESULT_MISS)] = PM_L1_ICACHE_MISS, + }, + [C(OP_WRITE)] = { + [C(RESULT_ACCESS)] = PM_INST_FROM_L1MISS, + [C(RESULT_MISS)] = -1, + }, + [C(OP_PREFETCH)] = { + [C(RESULT_ACCESS)] = PM_IC_PREF_REQ, + [C(RESULT_MISS)] = 0, + }, + }, + [C(LL)] = { + [C(OP_READ)] = { + [C(RESULT_ACCESS)] = PM_DATA_FROM_L3, + [C(RESULT_MISS)] = PM_DATA_FROM_L3MISS, + }, + [C(OP_WRITE)] = { + [C(RESULT_ACCESS)] = -1, + [C(RESULT_MISS)] = -1, + }, + [C(OP_PREFETCH)] = { + [C(RESULT_ACCESS)] = -1, + [C(RESULT_MISS)] = 0, + }, + }, + [C(DTLB)] = { + [C(OP_READ)] = { + [C(RESULT_ACCESS)] = 0, + [C(RESULT_MISS)] = PM_DTLB_MISS, + }, + [C(OP_WRITE)] = { + [C(RESULT_ACCESS)] = -1, + [C(RESULT_MISS)] = -1, + }, + [C(OP_PREFETCH)] = { + [C(RESULT_ACCESS)] = -1, + [C(RESULT_MISS)] = -1, + }, + }, + [C(ITLB)] = { + [C(OP_READ)] = { + [C(RESULT_ACCESS)] = 0, + [C(RESULT_MISS)] = PM_ITLB_MISS, + }, + [C(OP_WRITE)] = { + [C(RESULT_ACCESS)] = -1, + [C(RESULT_MISS)] = -1, + }, + [C(OP_PREFETCH)] = { + [C(RESULT_ACCESS)] = -1, + [C(RESULT_MISS)] = -1, + }, + }, + [C(BPU)] = { + [C(OP_READ)] = { + [C(RESULT_ACCESS)] = PM_BR_CMPL, + [C(RESULT_MISS)] = PM_BR_MPRED_CMPL, + }, + [C(OP_WRITE)] = { + [C(RESULT_ACCESS)] = -1, + [C(RESULT_MISS)] = -1, + }, + [C(OP_PREFETCH)] = { + [C(RESULT_ACCESS)] = -1, + [C(RESULT_MISS)] = -1, + }, + }, + [C(NODE)] = { + [C(OP_READ)] = { + [C(RESULT_ACCESS)] = -1, + [C(RESULT_MISS)] = -1, + }, + [C(OP_WRITE)] = { + [C(RESULT_ACCESS)] = -1, + [C(RESULT_MISS)] = -1, + }, + [C(OP_PREFETCH)] = { + [C(RESULT_ACCESS)] = -1, + [C(RESULT_MISS)] = -1, + }, + }, +}; + +#undef C + +static struct power_pmu power10_pmu = { + .name = "POWER10", + .n_counter = MAX_PMU_COUNTERS, + .add_fields = ISA207_ADD_FIELDS, + .test_adder = ISA207_TEST_ADDER, + .group_constraint_mask = CNST_CACHE_PMC4_MASK, + .group_constraint_val = CNST_CACHE_PMC4_VAL, + .compute_mmcr = isa207_compute_mmcr, + .config_bhrb = power10_config_bhrb, + .bhrb_filter_map = power10_bhrb_filter_map, + .get_constraint = isa207_get_constraint, + .get_alternatives = power10_get_alternatives, + .get_mem_data_src = isa207_get_mem_data_src, + .get_mem_weight = isa207_get_mem_weight, + .disable_pmc = isa207_disable_pmc, + .flags = PPMU_HAS_SIER | PPMU_ARCH_207S | + PPMU_ARCH_31, + .n_generic = ARRAY_SIZE(power10_generic_events), + .generic_events = power10_generic_events, + .cache_events = &power10_cache_events, + .attr_groups = power10_pmu_attr_groups, + .bhrb_nr = 32, +}; + +int init_power10_pmu(void) +{ + int rc; + + /* Comes from cpu_specs[] */ + if (!cur_cpu_spec->oprofile_cpu_type || + strcmp(cur_cpu_spec->oprofile_cpu_type, "ppc64/power10")) + return -ENODEV; + + rc = register_power_pmu(&power10_pmu); + if (rc) + return rc; + + /* Tell userspace that EBB is supported */ + cur_cpu_spec->cpu_user_features2 |= PPC_FEATURE2_EBB; + + return 0; +} -- GitLab From bfe3b1945d5e0531103b3d4ab3a367a1a156d99a Mon Sep 17 00:00:00 2001 From: Athira Rajeev Date: Fri, 17 Jul 2020 10:38:21 -0400 Subject: [PATCH 0763/1476] powerpc/perf: Ignore the BHRB kernel address filtering for P10 Commit bb19af816025 ("powerpc/perf: Prevent kernel address leak to userspace via BHRB buffer") added a check in bhrb_read() to filter the kernel address from BHRB buffer. This patch modified it to avoid that check for PowerISA v3.1 based processors, since PowerISA v3.1 allows only MSR[PR]=1 address to be written to BHRB buffer. Signed-off-by: Athira Rajeev Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/1594996707-3727-10-git-send-email-atrajeev@linux.vnet.ibm.com --- arch/powerpc/perf/core-book3s.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/arch/powerpc/perf/core-book3s.c b/arch/powerpc/perf/core-book3s.c index 6bffffa22ee83..e31629fb6e7eb 100644 --- a/arch/powerpc/perf/core-book3s.c +++ b/arch/powerpc/perf/core-book3s.c @@ -470,8 +470,11 @@ static void power_pmu_bhrb_read(struct perf_event *event, struct cpu_hw_events * * addresses at this point. Check the privileges before * exporting it to userspace (avoid exposure of regions * where we could have speculative execution) + * Incase of ISA v3.1, BHRB will capture only user-space + * addresses, hence include a check before filtering code */ - if (is_kernel_addr(addr) && perf_allow_kernel(&event->attr) != 0) + if (!(ppmu->flags & PPMU_ARCH_31) && + is_kernel_addr(addr) && perf_allow_kernel(&event->attr) != 0) continue; /* Branches are read most recent first (ie. mfbhrb 0 is -- GitLab From 80350a4bac992e3404067d31ff901ae9ff76aaa8 Mon Sep 17 00:00:00 2001 From: Athira Rajeev Date: Fri, 17 Jul 2020 10:38:22 -0400 Subject: [PATCH 0764/1476] powerpc/perf: Add Power10 BHRB filter support for PERF_SAMPLE_BRANCH_IND_CALL/COND PowerISA v3.1 introduce filtering support for PERF_SAMPLE_BRANCH_IND_CALL/COND. The patch adds BHRB filter support for "ind_call" and "cond" in power10_bhrb_filter_map(). Signed-off-by: Athira Rajeev Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/1594996707-3727-11-git-send-email-atrajeev@linux.vnet.ibm.com --- arch/powerpc/perf/power10-pmu.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/arch/powerpc/perf/power10-pmu.c b/arch/powerpc/perf/power10-pmu.c index 4b65e95c1e013..f7cff7f36a1cd 100644 --- a/arch/powerpc/perf/power10-pmu.c +++ b/arch/powerpc/perf/power10-pmu.c @@ -83,6 +83,8 @@ /* MMCRA IFM bits - POWER10 */ #define POWER10_MMCRA_IFM1 0x0000000040000000UL +#define POWER10_MMCRA_IFM2 0x0000000080000000UL +#define POWER10_MMCRA_IFM3 0x00000000C0000000UL #define POWER10_MMCRA_BHRB_MASK 0x00000000C0000000UL /* Table of alternatives, sorted by column 0 */ @@ -233,8 +235,15 @@ static u64 power10_bhrb_filter_map(u64 branch_sample_type) if (branch_sample_type & PERF_SAMPLE_BRANCH_ANY_RETURN) return -1; - if (branch_sample_type & PERF_SAMPLE_BRANCH_IND_CALL) - return -1; + if (branch_sample_type & PERF_SAMPLE_BRANCH_IND_CALL) { + pmu_bhrb_filter |= POWER10_MMCRA_IFM2; + return pmu_bhrb_filter; + } + + if (branch_sample_type & PERF_SAMPLE_BRANCH_COND) { + pmu_bhrb_filter |= POWER10_MMCRA_IFM3; + return pmu_bhrb_filter; + } if (branch_sample_type & PERF_SAMPLE_BRANCH_CALL) return -1; -- GitLab From 1cade527f6e9bec6a6412d0641643c359ada8096 Mon Sep 17 00:00:00 2001 From: Athira Rajeev Date: Fri, 17 Jul 2020 10:38:23 -0400 Subject: [PATCH 0765/1476] powerpc/perf: BHRB control to disable BHRB logic when not used PowerISA v3.1 has few updates for the Branch History Rolling Buffer(BHRB). BHRB disable is controlled via Monitor Mode Control Register A (MMCRA) bit, namely "BHRB Recording Disable (BHRBRD)". This field controls whether BHRB entries are written when BHRB recording is enabled by other bits. This patch implements support for this BHRB disable bit. By setting 0b1 to this bit will disable the BHRB and by setting 0b0 to this bit will have BHRB enabled. This addresses backward compatibility (for older OS), since this bit will be cleared and hardware will be writing to BHRB by default. This patch addresses changes to set MMCRA (BHRBRD) at boot for power10 (there by the core will run faster) and enable this feature only on runtime ie, on explicit need from user. Also save/restore MMCRA in the restore path of state-loss idle state to make sure we keep BHRB disabled if it was not enabled on request at runtime. Signed-off-by: Athira Rajeev Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/1594996707-3727-12-git-send-email-atrajeev@linux.vnet.ibm.com --- arch/powerpc/perf/core-book3s.c | 20 ++++++++++++++++---- arch/powerpc/perf/isa207-common.c | 12 ++++++++++++ arch/powerpc/platforms/powernv/idle.c | 22 ++++++++++++++++++++-- 3 files changed, 48 insertions(+), 6 deletions(-) diff --git a/arch/powerpc/perf/core-book3s.c b/arch/powerpc/perf/core-book3s.c index e31629fb6e7eb..e29c846bf35dd 100644 --- a/arch/powerpc/perf/core-book3s.c +++ b/arch/powerpc/perf/core-book3s.c @@ -1219,7 +1219,7 @@ static void write_mmcr0(struct cpu_hw_events *cpuhw, unsigned long mmcr0) static void power_pmu_disable(struct pmu *pmu) { struct cpu_hw_events *cpuhw; - unsigned long flags, mmcr0, val; + unsigned long flags, mmcr0, val, mmcra; if (!ppmu) return; @@ -1252,12 +1252,24 @@ static void power_pmu_disable(struct pmu *pmu) mb(); isync(); + val = mmcra = cpuhw->mmcr.mmcra; + /* * Disable instruction sampling if it was enabled */ - if (cpuhw->mmcr.mmcra & MMCRA_SAMPLE_ENABLE) { - mtspr(SPRN_MMCRA, - cpuhw->mmcr.mmcra & ~MMCRA_SAMPLE_ENABLE); + if (cpuhw->mmcr.mmcra & MMCRA_SAMPLE_ENABLE) + val &= ~MMCRA_SAMPLE_ENABLE; + + /* Disable BHRB via mmcra (BHRBRD) for p10 */ + if (ppmu->flags & PPMU_ARCH_31) + val |= MMCRA_BHRB_DISABLE; + + /* + * Write SPRN_MMCRA if mmcra has either disabled + * instruction sampling or BHRB. + */ + if (val != mmcra) { + mtspr(SPRN_MMCRA, mmcra); mb(); isync(); } diff --git a/arch/powerpc/perf/isa207-common.c b/arch/powerpc/perf/isa207-common.c index 77643f3ae4645..964437adec185 100644 --- a/arch/powerpc/perf/isa207-common.c +++ b/arch/powerpc/perf/isa207-common.c @@ -404,6 +404,13 @@ int isa207_compute_mmcr(u64 event[], int n_ev, mmcra = mmcr1 = mmcr2 = mmcr3 = 0; + /* + * Disable bhrb unless explicitly requested + * by setting MMCRA (BHRBRD) bit. + */ + if (cpu_has_feature(CPU_FTR_ARCH_31)) + mmcra |= MMCRA_BHRB_DISABLE; + /* Second pass: assign PMCs, set all MMCR1 fields */ for (i = 0; i < n_ev; ++i) { pmc = (event[i] >> EVENT_PMC_SHIFT) & EVENT_PMC_MASK; @@ -479,6 +486,11 @@ int isa207_compute_mmcr(u64 event[], int n_ev, mmcra |= val << MMCRA_IFM_SHIFT; } + /* set MMCRA (BHRBRD) to 0 if there is user request for BHRB */ + if (cpu_has_feature(CPU_FTR_ARCH_31) && + (has_branch_stack(pevents[i]) || (event[i] & EVENT_WANTS_BHRB))) + mmcra &= ~MMCRA_BHRB_DISABLE; + if (pevents[i]->attr.exclude_user) mmcr2 |= MMCR2_FCP(pmc); diff --git a/arch/powerpc/platforms/powernv/idle.c b/arch/powerpc/platforms/powernv/idle.c index 2dd467383a884..1c9d0a9d50e02 100644 --- a/arch/powerpc/platforms/powernv/idle.c +++ b/arch/powerpc/platforms/powernv/idle.c @@ -611,6 +611,7 @@ static unsigned long power9_idle_stop(unsigned long psscr, bool mmu_on) unsigned long srr1; unsigned long pls; unsigned long mmcr0 = 0; + unsigned long mmcra = 0; struct p9_sprs sprs = {}; /* avoid false used-uninitialised */ bool sprs_saved = false; @@ -657,6 +658,21 @@ static unsigned long power9_idle_stop(unsigned long psscr, bool mmu_on) */ mmcr0 = mfspr(SPRN_MMCR0); } + + if (cpu_has_feature(CPU_FTR_ARCH_31)) { + /* + * POWER10 uses MMCRA (BHRBRD) as BHRB disable bit. + * If the user hasn't asked for the BHRB to be + * written, the value of MMCRA[BHRBRD] is 1. + * On wakeup from stop, MMCRA[BHRBD] will be 0, + * since it is previleged resource and will be lost. + * Thus, if we do not save and restore the MMCRA[BHRBD], + * hardware will be needlessly writing to the BHRB + * in problem mode. + */ + mmcra = mfspr(SPRN_MMCRA); + } + if ((psscr & PSSCR_RL_MASK) >= pnv_first_spr_loss_level) { sprs.lpcr = mfspr(SPRN_LPCR); sprs.hfscr = mfspr(SPRN_HFSCR); @@ -700,8 +716,6 @@ static unsigned long power9_idle_stop(unsigned long psscr, bool mmu_on) WARN_ON_ONCE(mfmsr() & (MSR_IR|MSR_DR)); if ((srr1 & SRR1_WAKESTATE) != SRR1_WS_NOLOSS) { - unsigned long mmcra; - /* * We don't need an isync after the mtsprs here because the * upcoming mtmsrd is execution synchronizing. @@ -721,6 +735,10 @@ static unsigned long power9_idle_stop(unsigned long psscr, bool mmu_on) mtspr(SPRN_MMCR0, mmcr0); } + /* Reload MMCRA to restore BHRB disable bit for POWER10 */ + if (cpu_has_feature(CPU_FTR_ARCH_31)) + mtspr(SPRN_MMCRA, mmcra); + /* * DD2.2 and earlier need to set then clear bit 60 in MMCRA * to ensure the PMU starts running. -- GitLab From b2dc2977cba48990df45e0a96150663d4f342700 Mon Sep 17 00:00:00 2001 From: Nicholas Piggin Date: Thu, 11 Jun 2020 18:12:02 +1000 Subject: [PATCH 0766/1476] powerpc/64s/exception: treat NIA below __end_interrupts as soft-masked The scv instruction causes an interrupt which can enter the kernel with MSR[EE]=1, thus allowing interrupts to hit at any time. These must not be taken as normal interrupts, because they come from MSR[PR]=0 context, and yet the kernel stack is not yet set up and r13 is not set to the PACA). Treat this as a soft-masked interrupt regardless of the soft masked state. This does not affect behaviour yet, because currently all interrupts are taken with MSR[EE]=0. Signed-off-by: Nicholas Piggin Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20200611081203.995112-2-npiggin@gmail.com --- arch/powerpc/kernel/exceptions-64s.S | 27 ++++++++++++++++++++++++--- 1 file changed, 24 insertions(+), 3 deletions(-) diff --git a/arch/powerpc/kernel/exceptions-64s.S b/arch/powerpc/kernel/exceptions-64s.S index fa080694e581e..f15307c50bc1e 100644 --- a/arch/powerpc/kernel/exceptions-64s.S +++ b/arch/powerpc/kernel/exceptions-64s.S @@ -508,8 +508,24 @@ DEFINE_FIXED_SYMBOL(\name\()_common_real) .macro __GEN_COMMON_BODY name .if IMASK + .if ! ISTACK + .error "No support for masked interrupt to use custom stack" + .endif + + /* If coming from user, skip soft-mask tests. */ + andi. r10,r12,MSR_PR + bne 2f + + /* Kernel code running below __end_interrupts is implicitly + * soft-masked */ + LOAD_HANDLER(r10, __end_interrupts) + cmpld r11,r10 + li r10,IMASK + blt- 1f + + /* Test the soft mask state against our interrupt's bit */ lbz r10,PACAIRQSOFTMASK(r13) - andi. r10,r10,IMASK +1: andi. r10,r10,IMASK /* Associate vector numbers with bits in paca->irq_happened */ .if IVEC == 0x500 || IVEC == 0xea0 li r10,PACA_IRQ_EE @@ -540,7 +556,7 @@ DEFINE_FIXED_SYMBOL(\name\()_common_real) .if ISTACK andi. r10,r12,MSR_PR /* See if coming from user */ - mr r10,r1 /* Save r1 */ +2: mr r10,r1 /* Save r1 */ subi r1,r1,INT_FRAME_SIZE /* alloc frame on kernel stack */ beq- 100f ld r1,PACAKSAVE(r13) /* kernel stack to use */ @@ -2838,7 +2854,8 @@ masked_interrupt: ld r10,PACA_EXGEN+EX_R10(r13) ld r11,PACA_EXGEN+EX_R11(r13) ld r12,PACA_EXGEN+EX_R12(r13) - /* returns to kernel where r13 must be set up, so don't restore it */ + ld r13,PACA_EXGEN+EX_R13(r13) + /* May return to masked low address where r13 is not set up */ .if \hsrr HRFI_TO_KERNEL .else @@ -2997,6 +3014,10 @@ EXC_COMMON_BEGIN(ppc64_runlatch_on_trampoline) USE_FIXED_SECTION(virt_trampolines) /* + * All code below __end_interrupts is treated as soft-masked. If + * any code runs here with MSR[EE]=1, it must then cope with pending + * soft interrupt being raised (i.e., by ensuring it is replayed). + * * The __end_interrupts marker must be past the out-of-line (OOL) * handlers, so that they are copied to real address 0x100 when running * a relocatable kernel. This ensures they can be reached from the short -- GitLab From 7fa95f9adaee7e5cbb195d3359741120829e488b Mon Sep 17 00:00:00 2001 From: Nicholas Piggin Date: Thu, 11 Jun 2020 18:12:03 +1000 Subject: [PATCH 0767/1476] powerpc/64s: system call support for scv/rfscv instructions Add support for the scv instruction on POWER9 and later CPUs. For now this implements the zeroth scv vector 'scv 0', as identical to 'sc' system calls, with the exception that LR is not preserved, nor are volatile CR registers, and error is not indicated with CR0[SO], but by returning a negative errno. rfscv is implemented to return from scv type system calls. It can not be used to return from sc system calls because those are defined to preserve LR. getpid syscall throughput on POWER9 is improved by 26% (428 to 318 cycles), largely due to reducing mtmsr and mtspr. Signed-off-by: Nicholas Piggin [mpe: Fix ppc64e build] Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20200611081203.995112-3-npiggin@gmail.com --- Documentation/powerpc/syscall64-abi.rst | 42 ++++-- arch/powerpc/include/asm/asm-prototypes.h | 2 +- arch/powerpc/include/asm/exception-64s.h | 6 + arch/powerpc/include/asm/head-64.h | 2 +- arch/powerpc/include/asm/ppc_asm.h | 2 + arch/powerpc/include/asm/ptrace.h | 7 +- arch/powerpc/include/asm/setup.h | 4 +- arch/powerpc/include/asm/sstep.h | 1 + arch/powerpc/kernel/cpu_setup_power.S | 10 +- arch/powerpc/kernel/cputable.c | 3 +- arch/powerpc/kernel/dt_cpu_ftrs.c | 1 + arch/powerpc/kernel/entry_64.S | 171 +++++++++++++++++++++- arch/powerpc/kernel/exceptions-64s.S | 123 +++++++++++++++- arch/powerpc/kernel/process.c | 10 +- arch/powerpc/kernel/setup_64.c | 5 +- arch/powerpc/kernel/signal.c | 19 ++- arch/powerpc/kernel/syscall_64.c | 32 +++- arch/powerpc/lib/sstep.c | 16 ++ arch/powerpc/platforms/pseries/setup.c | 8 +- arch/powerpc/xmon/xmon.c | 1 + 20 files changed, 421 insertions(+), 44 deletions(-) diff --git a/Documentation/powerpc/syscall64-abi.rst b/Documentation/powerpc/syscall64-abi.rst index e49f69f941b99..46caaadbb0290 100644 --- a/Documentation/powerpc/syscall64-abi.rst +++ b/Documentation/powerpc/syscall64-abi.rst @@ -5,6 +5,15 @@ Power Architecture 64-bit Linux system call ABI syscall ======= +Invocation +---------- +The syscall is made with the sc instruction, and returns with execution +continuing at the instruction following the sc instruction. + +If PPC_FEATURE2_SCV appears in the AT_HWCAP2 ELF auxiliary vector, the +scv 0 instruction is an alternative that may provide better performance, +with some differences to calling sequence. + syscall calling sequence\ [1]_ matches the Power Architecture 64-bit ELF ABI specification C function calling sequence, including register preservation rules, with the following differences. @@ -12,16 +21,23 @@ rules, with the following differences. .. [1] Some syscalls (typically low-level management functions) may have different calling sequences (e.g., rt_sigreturn). -Parameters and return value ---------------------------- +Parameters +---------- The system call number is specified in r0. There is a maximum of 6 integer parameters to a syscall, passed in r3-r8. -Both a return value and a return error code are returned. cr0.SO is the return -error code, and r3 is the return value or error code. When cr0.SO is clear, -the syscall succeeded and r3 is the return value. When cr0.SO is set, the -syscall failed and r3 is the error code that generally corresponds to errno. +Return value +------------ +- For the sc instruction, both a value and an error condition are returned. + cr0.SO is the error condition, and r3 is the return value. When cr0.SO is + clear, the syscall succeeded and r3 is the return value. When cr0.SO is set, + the syscall failed and r3 is the error value (that normally corresponds to + errno). + +- For the scv 0 instruction, the return value indicates failure if it is + -4095..-1 (i.e., it is >= -MAX_ERRNO (-4095) as an unsigned comparison), + in which case the error value is the negated return value. Stack ----- @@ -34,22 +50,23 @@ Register preservation rules match the ELF ABI calling sequence with the following differences: =========== ============= ======================================== +--- For the sc instruction, differences with the ELF ABI --- r0 Volatile (System call number.) r3 Volatile (Parameter 1, and return value.) r4-r8 Volatile (Parameters 2-6.) -cr0 Volatile (cr0.SO is the return error condition) +cr0 Volatile (cr0.SO is the return error condition.) cr1, cr5-7 Nonvolatile lr Nonvolatile + +--- For the scv 0 instruction, differences with the ELF ABI --- +r0 Volatile (System call number.) +r3 Volatile (Parameter 1, and return value.) +r4-r8 Volatile (Parameters 2-6.) =========== ============= ======================================== All floating point and vector data registers as well as control and status registers are nonvolatile. -Invocation ----------- -The syscall is performed with the sc instruction, and returns with execution -continuing at the instruction following the sc instruction. - Transactional Memory -------------------- Syscall behavior can change if the processor is in transactional or suspended @@ -75,6 +92,7 @@ auxiliary vector. returning to the caller. This case is not well defined or supported, so this behavior should not be relied upon. +scv 0 syscalls will always behave as PPC_FEATURE2_HTM_NOSC. vsyscall ======== diff --git a/arch/powerpc/include/asm/asm-prototypes.h b/arch/powerpc/include/asm/asm-prototypes.h index 7d81e86a1e5dd..fb47bf5818c89 100644 --- a/arch/powerpc/include/asm/asm-prototypes.h +++ b/arch/powerpc/include/asm/asm-prototypes.h @@ -98,7 +98,7 @@ unsigned long __init early_init(unsigned long dt_ptr); void __init machine_init(u64 dt_ptr); #endif long system_call_exception(long r3, long r4, long r5, long r6, long r7, long r8, unsigned long r0, struct pt_regs *regs); -notrace unsigned long syscall_exit_prepare(unsigned long r3, struct pt_regs *regs); +notrace unsigned long syscall_exit_prepare(unsigned long r3, struct pt_regs *regs, long scv); notrace unsigned long interrupt_exit_user_prepare(struct pt_regs *regs, unsigned long msr); notrace unsigned long interrupt_exit_kernel_prepare(struct pt_regs *regs, unsigned long msr); diff --git a/arch/powerpc/include/asm/exception-64s.h b/arch/powerpc/include/asm/exception-64s.h index 47bd4ea0837d4..0c2fe7f042d11 100644 --- a/arch/powerpc/include/asm/exception-64s.h +++ b/arch/powerpc/include/asm/exception-64s.h @@ -123,6 +123,12 @@ hrfid; \ b hrfi_flush_fallback +#define RFSCV_TO_USER \ + STF_EXIT_BARRIER_SLOT; \ + RFI_FLUSH_SLOT; \ + RFSCV; \ + b rfscv_flush_fallback + #endif /* __ASSEMBLY__ */ #endif /* _ASM_POWERPC_EXCEPTION_H */ diff --git a/arch/powerpc/include/asm/head-64.h b/arch/powerpc/include/asm/head-64.h index 2dabcf6682929..4cb9efa2eb218 100644 --- a/arch/powerpc/include/asm/head-64.h +++ b/arch/powerpc/include/asm/head-64.h @@ -128,7 +128,7 @@ name: .if ((start) % (size) != 0); \ .error "Fixed section exception vector misalignment"; \ .endif; \ - .if ((size) != 0x20) && ((size) != 0x80) && ((size) != 0x100); \ + .if ((size) != 0x20) && ((size) != 0x80) && ((size) != 0x100) && ((size) != 0x1000); \ .error "Fixed section exception vector bad size"; \ .endif; \ .if (start) < sname##_start; \ diff --git a/arch/powerpc/include/asm/ppc_asm.h b/arch/powerpc/include/asm/ppc_asm.h index 6b03dff61a05e..160f3bb77ea41 100644 --- a/arch/powerpc/include/asm/ppc_asm.h +++ b/arch/powerpc/include/asm/ppc_asm.h @@ -755,6 +755,8 @@ END_FTR_SECTION_NESTED(CPU_FTR_CELL_TB_BUG, CPU_FTR_CELL_TB_BUG, 96) #define N_SLINE 68 #define N_SO 100 +#define RFSCV .long 0x4c0000a4 + /* * Create an endian fixup trampoline * diff --git a/arch/powerpc/include/asm/ptrace.h b/arch/powerpc/include/asm/ptrace.h index ac3970fff0d5d..f194339cef3ba 100644 --- a/arch/powerpc/include/asm/ptrace.h +++ b/arch/powerpc/include/asm/ptrace.h @@ -222,9 +222,14 @@ static inline void set_trap(struct pt_regs *regs, unsigned long val) regs->trap = (regs->trap & TRAP_FLAGS_MASK) | (val & ~TRAP_FLAGS_MASK); } +static inline bool trap_is_scv(struct pt_regs *regs) +{ + return (IS_ENABLED(CONFIG_PPC_BOOK3S_64) && TRAP(regs) == 0x3000); +} + static inline bool trap_is_syscall(struct pt_regs *regs) { - return TRAP(regs) == 0xc00; + return (trap_is_scv(regs) || TRAP(regs) == 0xc00); } static inline bool trap_norestart(struct pt_regs *regs) diff --git a/arch/powerpc/include/asm/setup.h b/arch/powerpc/include/asm/setup.h index 65676e2325b85..9efbddee2bca9 100644 --- a/arch/powerpc/include/asm/setup.h +++ b/arch/powerpc/include/asm/setup.h @@ -30,12 +30,12 @@ void setup_panic(void); #define ARCH_PANIC_TIMEOUT 180 #ifdef CONFIG_PPC_PSERIES -extern void pseries_enable_reloc_on_exc(void); +extern bool pseries_enable_reloc_on_exc(void); extern void pseries_disable_reloc_on_exc(void); extern void pseries_big_endian_exceptions(void); extern void pseries_little_endian_exceptions(void); #else -static inline void pseries_enable_reloc_on_exc(void) {} +static inline bool pseries_enable_reloc_on_exc(void) { return false; } static inline void pseries_disable_reloc_on_exc(void) {} static inline void pseries_big_endian_exceptions(void) {} static inline void pseries_little_endian_exceptions(void) {} diff --git a/arch/powerpc/include/asm/sstep.h b/arch/powerpc/include/asm/sstep.h index 3b01c69a44aa4..eaa4fb6c89607 100644 --- a/arch/powerpc/include/asm/sstep.h +++ b/arch/powerpc/include/asm/sstep.h @@ -40,6 +40,7 @@ enum instruction_type { CACHEOP, BARRIER, SYSCALL, + SYSCALL_VECTORED_0, MFMSR, MTMSR, RFI, diff --git a/arch/powerpc/kernel/cpu_setup_power.S b/arch/powerpc/kernel/cpu_setup_power.S index efdcfa714106d..86527d19348cb 100644 --- a/arch/powerpc/kernel/cpu_setup_power.S +++ b/arch/powerpc/kernel/cpu_setup_power.S @@ -98,7 +98,7 @@ _GLOBAL(__setup_cpu_power10) _GLOBAL(__setup_cpu_power9) mflr r11 - bl __init_FSCR + bl __init_FSCR_power9 1: bl __init_PMU bl __init_hvmode_206 mtlr r11 @@ -128,7 +128,7 @@ _GLOBAL(__restore_cpu_power10) _GLOBAL(__restore_cpu_power9) mflr r11 - bl __init_FSCR + bl __init_FSCR_power9 1: bl __init_PMU mfmsr r3 rldicl. r0,r3,4,63 @@ -198,6 +198,12 @@ __init_FSCR_power10: mtspr SPRN_FSCR, r3 // fall through +__init_FSCR_power9: + mfspr r3, SPRN_FSCR + ori r3, r3, FSCR_SCV + mtspr SPRN_FSCR, r3 + // fall through + __init_FSCR: mfspr r3,SPRN_FSCR ori r3,r3,FSCR_TAR|FSCR_EBB diff --git a/arch/powerpc/kernel/cputable.c b/arch/powerpc/kernel/cputable.c index b4066354f0730..3d406a9626e86 100644 --- a/arch/powerpc/kernel/cputable.c +++ b/arch/powerpc/kernel/cputable.c @@ -120,7 +120,8 @@ extern void __restore_cpu_e6500(void); #define COMMON_USER2_POWER9 (COMMON_USER2_POWER8 | \ PPC_FEATURE2_ARCH_3_00 | \ PPC_FEATURE2_HAS_IEEE128 | \ - PPC_FEATURE2_DARN ) + PPC_FEATURE2_DARN | \ + PPC_FEATURE2_SCV) #define COMMON_USER_POWER10 COMMON_USER_POWER9 #define COMMON_USER2_POWER10 (COMMON_USER2_POWER9 | \ PPC_FEATURE2_ARCH_3_1 | \ diff --git a/arch/powerpc/kernel/dt_cpu_ftrs.c b/arch/powerpc/kernel/dt_cpu_ftrs.c index 3a409517c031d..50b2d544361e0 100644 --- a/arch/powerpc/kernel/dt_cpu_ftrs.c +++ b/arch/powerpc/kernel/dt_cpu_ftrs.c @@ -587,6 +587,7 @@ static struct dt_cpu_feature_match __initdata {"little-endian", feat_enable_le, CPU_FTR_REAL_LE}, {"smt", feat_enable_smt, 0}, {"interrupt-facilities", feat_enable, 0}, + {"system-call-vectored", feat_enable, 0}, {"timer-facilities", feat_enable, 0}, {"timer-facilities-v3", feat_enable, 0}, {"debug-facilities", feat_enable, 0}, diff --git a/arch/powerpc/kernel/entry_64.S b/arch/powerpc/kernel/entry_64.S index 9d49338e0c859..223c4f008e633 100644 --- a/arch/powerpc/kernel/entry_64.S +++ b/arch/powerpc/kernel/entry_64.S @@ -64,15 +64,173 @@ exception_marker: .section ".text" .align 7 +#ifdef CONFIG_PPC_BOOK3S +.macro system_call_vectored name trapnr + .globl system_call_vectored_\name +system_call_vectored_\name: +_ASM_NOKPROBE_SYMBOL(system_call_vectored_\name) +#ifdef CONFIG_PPC_TRANSACTIONAL_MEM +BEGIN_FTR_SECTION + extrdi. r10, r12, 1, (63-MSR_TS_T_LG) /* transaction active? */ + bne .Ltabort_syscall +END_FTR_SECTION_IFSET(CPU_FTR_TM) +#endif + INTERRUPT_TO_KERNEL + mr r10,r1 + ld r1,PACAKSAVE(r13) + std r10,0(r1) + std r11,_NIP(r1) + std r12,_MSR(r1) + std r0,GPR0(r1) + std r10,GPR1(r1) + std r2,GPR2(r1) + ld r2,PACATOC(r13) + mfcr r12 + li r11,0 + /* Can we avoid saving r3-r8 in common case? */ + std r3,GPR3(r1) + std r4,GPR4(r1) + std r5,GPR5(r1) + std r6,GPR6(r1) + std r7,GPR7(r1) + std r8,GPR8(r1) + /* Zero r9-r12, this should only be required when restoring all GPRs */ + std r11,GPR9(r1) + std r11,GPR10(r1) + std r11,GPR11(r1) + std r11,GPR12(r1) + std r9,GPR13(r1) + SAVE_NVGPRS(r1) + std r11,_XER(r1) + std r11,_LINK(r1) + std r11,_CTR(r1) + + li r11,\trapnr + std r11,_TRAP(r1) + std r12,_CCR(r1) + std r3,ORIG_GPR3(r1) + addi r10,r1,STACK_FRAME_OVERHEAD + ld r11,exception_marker@toc(r2) + std r11,-16(r10) /* "regshere" marker */ + + /* + * RECONCILE_IRQ_STATE without calling trace_hardirqs_off(), which + * would clobber syscall parameters. Also we always enter with IRQs + * enabled and nothing pending. system_call_exception() will call + * trace_hardirqs_off(). + * + * scv enters with MSR[EE]=1, so don't set PACA_IRQ_HARD_DIS. The + * entry vector already sets PACAIRQSOFTMASK to IRQS_ALL_DISABLED. + */ + + /* Calling convention has r9 = orig r0, r10 = regs */ + mr r9,r0 + bl system_call_exception + +.Lsyscall_vectored_\name\()_exit: + addi r4,r1,STACK_FRAME_OVERHEAD + li r5,1 /* scv */ + bl syscall_exit_prepare + + ld r2,_CCR(r1) + ld r4,_NIP(r1) + ld r5,_MSR(r1) + +BEGIN_FTR_SECTION + stdcx. r0,0,r1 /* to clear the reservation */ +END_FTR_SECTION_IFCLR(CPU_FTR_STCX_CHECKS_ADDRESS) + +BEGIN_FTR_SECTION + HMT_MEDIUM_LOW +END_FTR_SECTION_IFSET(CPU_FTR_HAS_PPR) + + cmpdi r3,0 + bne .Lsyscall_vectored_\name\()_restore_regs + + /* rfscv returns with LR->NIA and CTR->MSR */ + mtlr r4 + mtctr r5 + + /* Could zero these as per ABI, but we may consider a stricter ABI + * which preserves these if libc implementations can benefit, so + * restore them for now until further measurement is done. */ + ld r0,GPR0(r1) + ld r4,GPR4(r1) + ld r5,GPR5(r1) + ld r6,GPR6(r1) + ld r7,GPR7(r1) + ld r8,GPR8(r1) + /* Zero volatile regs that may contain sensitive kernel data */ + li r9,0 + li r10,0 + li r11,0 + li r12,0 + mtspr SPRN_XER,r0 + + /* + * We don't need to restore AMR on the way back to userspace for KUAP. + * The value of AMR only matters while we're in the kernel. + */ + mtcr r2 + ld r2,GPR2(r1) + ld r3,GPR3(r1) + ld r13,GPR13(r1) + ld r1,GPR1(r1) + RFSCV_TO_USER + b . /* prevent speculative execution */ + +.Lsyscall_vectored_\name\()_restore_regs: + li r3,0 + mtmsrd r3,1 + mtspr SPRN_SRR0,r4 + mtspr SPRN_SRR1,r5 + + ld r3,_CTR(r1) + ld r4,_LINK(r1) + ld r5,_XER(r1) + + REST_NVGPRS(r1) + ld r0,GPR0(r1) + mtcr r2 + mtctr r3 + mtlr r4 + mtspr SPRN_XER,r5 + REST_10GPRS(2, r1) + REST_2GPRS(12, r1) + ld r1,GPR1(r1) + RFI_TO_USER +.endm + +system_call_vectored common 0x3000 +/* + * We instantiate another entry copy for the SIGILL variant, with TRAP=0x7ff0 + * which is tested by system_call_exception when r0 is -1 (as set by vector + * entry code). + */ +system_call_vectored sigill 0x7ff0 + + +/* + * Entered via kernel return set up by kernel/sstep.c, must match entry regs + */ + .globl system_call_vectored_emulate +system_call_vectored_emulate: +_ASM_NOKPROBE_SYMBOL(system_call_vectored_emulate) + li r10,IRQS_ALL_DISABLED + stb r10,PACAIRQSOFTMASK(r13) + b system_call_vectored_common +#endif + + .balign IFETCH_ALIGN_BYTES .globl system_call_common system_call_common: +_ASM_NOKPROBE_SYMBOL(system_call_common) #ifdef CONFIG_PPC_TRANSACTIONAL_MEM BEGIN_FTR_SECTION extrdi. r10, r12, 1, (63-MSR_TS_T_LG) /* transaction active? */ bne .Ltabort_syscall END_FTR_SECTION_IFSET(CPU_FTR_TM) #endif -_ASM_NOKPROBE_SYMBOL(system_call_common) mr r10,r1 ld r1,PACAKSAVE(r13) std r10,0(r1) @@ -138,6 +296,7 @@ END_BTB_FLUSH_SECTION .Lsyscall_exit: addi r4,r1,STACK_FRAME_OVERHEAD + li r5,0 /* !scv */ bl syscall_exit_prepare ld r2,_CCR(r1) @@ -224,10 +383,18 @@ END_FTR_SECTION_IFSET(CPU_FTR_HAS_PPR) b . /* prevent speculative execution */ #endif +#ifdef CONFIG_PPC_BOOK3S +_GLOBAL(ret_from_fork_scv) + bl schedule_tail + REST_NVGPRS(r1) + li r3,0 /* fork() return value */ + b .Lsyscall_vectored_common_exit +#endif + _GLOBAL(ret_from_fork) bl schedule_tail REST_NVGPRS(r1) - li r3,0 + li r3,0 /* fork() return value */ b .Lsyscall_exit _GLOBAL(ret_from_kernel_thread) diff --git a/arch/powerpc/kernel/exceptions-64s.S b/arch/powerpc/kernel/exceptions-64s.S index f15307c50bc1e..8afc0e03d7d4a 100644 --- a/arch/powerpc/kernel/exceptions-64s.S +++ b/arch/powerpc/kernel/exceptions-64s.S @@ -756,6 +756,9 @@ END_FTR_SECTION_IFSET(CPU_FTR_CAN_NAP) * guarantee they will be delivered virtually. Some conditions (see the ISA) * cause exceptions to be delivered in real mode. * + * The scv instructions are a special case. They get a 0x3000 offset applied. + * scv exceptions have unique reentrancy properties, see below. + * * It's impossible to receive interrupts below 0x300 via AIL. * * KVM: None of the virtual exceptions are from the guest. Anything that @@ -765,8 +768,8 @@ END_FTR_SECTION_IFSET(CPU_FTR_CAN_NAP) * We layout physical memory as follows: * 0x0000 - 0x00ff : Secondary processor spin code * 0x0100 - 0x18ff : Real mode pSeries interrupt vectors - * 0x1900 - 0x3fff : Real mode trampolines - * 0x4000 - 0x58ff : Relon (IR=1,DR=1) mode pSeries interrupt vectors + * 0x1900 - 0x2fff : Real mode trampolines + * 0x3000 - 0x58ff : Relon (IR=1,DR=1) mode pSeries interrupt vectors * 0x5900 - 0x6fff : Relon mode trampolines * 0x7000 - 0x7fff : FWNMI data area * 0x8000 - .... : Common interrupt handlers, remaining early @@ -777,8 +780,8 @@ END_FTR_SECTION_IFSET(CPU_FTR_CAN_NAP) * vectors there. */ OPEN_FIXED_SECTION(real_vectors, 0x0100, 0x1900) -OPEN_FIXED_SECTION(real_trampolines, 0x1900, 0x4000) -OPEN_FIXED_SECTION(virt_vectors, 0x4000, 0x5900) +OPEN_FIXED_SECTION(real_trampolines, 0x1900, 0x3000) +OPEN_FIXED_SECTION(virt_vectors, 0x3000, 0x5900) OPEN_FIXED_SECTION(virt_trampolines, 0x5900, 0x7000) #ifdef CONFIG_PPC_POWERNV @@ -814,6 +817,77 @@ USE_FIXED_SECTION(real_vectors) .globl __start_interrupts __start_interrupts: +/** + * Interrupt 0x3000 - System Call Vectored Interrupt (syscall). + * This is a synchronous interrupt invoked with the "scv" instruction. The + * system call does not alter the HV bit, so it is directed to the OS. + * + * Handling: + * scv instructions enter the kernel without changing EE, RI, ME, or HV. + * In particular, this means we can take a maskable interrupt at any point + * in the scv handler, which is unlike any other interrupt. This is solved + * by treating the instruction addresses below __end_interrupts as being + * soft-masked. + * + * AIL-0 mode scv exceptions go to 0x17000-0x17fff, but we set AIL-3 and + * ensure scv is never executed with relocation off, which means AIL-0 + * should never happen. + * + * Before leaving the below __end_interrupts text, at least of the following + * must be true: + * - MSR[PR]=1 (i.e., return to userspace) + * - MSR_EE|MSR_RI is set (no reentrant exceptions) + * - Standard kernel environment is set up (stack, paca, etc) + * + * Call convention: + * + * syscall register convention is in Documentation/powerpc/syscall64-abi.rst + */ +EXC_VIRT_BEGIN(system_call_vectored, 0x3000, 0x1000) + /* SCV 0 */ + mr r9,r13 + GET_PACA(r13) + mflr r11 + mfctr r12 + li r10,IRQS_ALL_DISABLED + stb r10,PACAIRQSOFTMASK(r13) +#ifdef CONFIG_RELOCATABLE + b system_call_vectored_tramp +#else + b system_call_vectored_common +#endif + nop + + /* SCV 1 - 127 */ + .rept 127 + mr r9,r13 + GET_PACA(r13) + mflr r11 + mfctr r12 + li r10,IRQS_ALL_DISABLED + stb r10,PACAIRQSOFTMASK(r13) + li r0,-1 /* cause failure */ +#ifdef CONFIG_RELOCATABLE + b system_call_vectored_sigill_tramp +#else + b system_call_vectored_sigill +#endif + .endr +EXC_VIRT_END(system_call_vectored, 0x3000, 0x1000) + +#ifdef CONFIG_RELOCATABLE +TRAMP_VIRT_BEGIN(system_call_vectored_tramp) + __LOAD_HANDLER(r10, system_call_vectored_common) + mtctr r10 + bctr + +TRAMP_VIRT_BEGIN(system_call_vectored_sigill_tramp) + __LOAD_HANDLER(r10, system_call_vectored_sigill) + mtctr r10 + bctr +#endif + + /* No virt vectors corresponding with 0x0..0x100 */ EXC_VIRT_NONE(0x4000, 0x100) @@ -2963,6 +3037,47 @@ TRAMP_REAL_BEGIN(hrfi_flush_fallback) GET_SCRATCH0(r13); hrfid +TRAMP_REAL_BEGIN(rfscv_flush_fallback) + /* system call volatile */ + mr r7,r13 + GET_PACA(r13); + mr r8,r1 + ld r1,PACAKSAVE(r13) + mfctr r9 + ld r10,PACA_RFI_FLUSH_FALLBACK_AREA(r13) + ld r11,PACA_L1D_FLUSH_SIZE(r13) + srdi r11,r11,(7 + 3) /* 128 byte lines, unrolled 8x */ + mtctr r11 + DCBT_BOOK3S_STOP_ALL_STREAM_IDS(r11) /* Stop prefetch streams */ + + /* order ld/st prior to dcbt stop all streams with flushing */ + sync + + /* + * The load adresses are at staggered offsets within cachelines, + * which suits some pipelines better (on others it should not + * hurt). + */ +1: + ld r11,(0x80 + 8)*0(r10) + ld r11,(0x80 + 8)*1(r10) + ld r11,(0x80 + 8)*2(r10) + ld r11,(0x80 + 8)*3(r10) + ld r11,(0x80 + 8)*4(r10) + ld r11,(0x80 + 8)*5(r10) + ld r11,(0x80 + 8)*6(r10) + ld r11,(0x80 + 8)*7(r10) + addi r10,r10,0x80*8 + bdnz 1b + + mtctr r9 + li r9,0 + li r10,0 + li r11,0 + mr r1,r8 + mr r13,r7 + RFSCV + USE_TEXT_SECTION() MASKED_INTERRUPT MASKED_INTERRUPT hsrr=1 diff --git a/arch/powerpc/kernel/process.c b/arch/powerpc/kernel/process.c index 4650b9bb217fb..939935613cf1c 100644 --- a/arch/powerpc/kernel/process.c +++ b/arch/powerpc/kernel/process.c @@ -1599,6 +1599,7 @@ int copy_thread_tls(unsigned long clone_flags, unsigned long usp, { struct pt_regs *childregs, *kregs; extern void ret_from_fork(void); + extern void ret_from_fork_scv(void); extern void ret_from_kernel_thread(void); void (*f)(void); unsigned long sp = (unsigned long)task_stack_page(p) + THREAD_SIZE; @@ -1635,7 +1636,9 @@ int copy_thread_tls(unsigned long clone_flags, unsigned long usp, if (usp) childregs->gpr[1] = usp; p->thread.regs = childregs; - childregs->gpr[3] = 0; /* Result from fork() */ + /* 64s sets this in ret_from_fork */ + if (!IS_ENABLED(CONFIG_PPC_BOOK3S_64)) + childregs->gpr[3] = 0; /* Result from fork() */ if (clone_flags & CLONE_SETTLS) { if (!is_32bit_task()) childregs->gpr[13] = tls; @@ -1643,7 +1646,10 @@ int copy_thread_tls(unsigned long clone_flags, unsigned long usp, childregs->gpr[2] = tls; } - f = ret_from_fork; + if (trap_is_scv(regs)) + f = ret_from_fork_scv; + else + f = ret_from_fork; } childregs->msr &= ~(MSR_FP|MSR_VEC|MSR_VSX); sp -= STACK_FRAME_OVERHEAD; diff --git a/arch/powerpc/kernel/setup_64.c b/arch/powerpc/kernel/setup_64.c index 0ba1ed77dc686..6be430107c6f2 100644 --- a/arch/powerpc/kernel/setup_64.c +++ b/arch/powerpc/kernel/setup_64.c @@ -196,7 +196,10 @@ static void __init configure_exceptions(void) /* Under a PAPR hypervisor, we need hypercalls */ if (firmware_has_feature(FW_FEATURE_SET_MODE)) { /* Enable AIL if possible */ - pseries_enable_reloc_on_exc(); + if (!pseries_enable_reloc_on_exc()) { + init_task.thread.fscr &= ~FSCR_SCV; + cur_cpu_spec->cpu_user_features2 &= ~PPC_FEATURE2_SCV; + } /* * Tell the hypervisor that we want our exceptions to diff --git a/arch/powerpc/kernel/signal.c b/arch/powerpc/kernel/signal.c index b4143b6ff0936..d15a98c758b8b 100644 --- a/arch/powerpc/kernel/signal.c +++ b/arch/powerpc/kernel/signal.c @@ -205,8 +205,14 @@ static void check_syscall_restart(struct pt_regs *regs, struct k_sigaction *ka, return; /* error signalled ? */ - if (!(regs->ccr & 0x10000000)) + if (trap_is_scv(regs)) { + /* 32-bit compat mode sign extend? */ + if (!IS_ERR_VALUE(ret)) + return; + ret = -ret; + } else if (!(regs->ccr & 0x10000000)) { return; + } switch (ret) { case ERESTART_RESTARTBLOCK: @@ -239,9 +245,14 @@ static void check_syscall_restart(struct pt_regs *regs, struct k_sigaction *ka, regs->nip -= 4; regs->result = 0; } else { - regs->result = -EINTR; - regs->gpr[3] = EINTR; - regs->ccr |= 0x10000000; + if (trap_is_scv(regs)) { + regs->result = -EINTR; + regs->gpr[3] = -EINTR; + } else { + regs->result = -EINTR; + regs->gpr[3] = EINTR; + regs->ccr |= 0x10000000; + } } } diff --git a/arch/powerpc/kernel/syscall_64.c b/arch/powerpc/kernel/syscall_64.c index 79edba3ab3124..a783fd324cb03 100644 --- a/arch/powerpc/kernel/syscall_64.c +++ b/arch/powerpc/kernel/syscall_64.c @@ -60,6 +60,11 @@ notrace long system_call_exception(long r3, long r4, long r5, local_irq_enable(); if (unlikely(current_thread_info()->flags & _TIF_SYSCALL_DOTRACE)) { + if (unlikely(regs->trap == 0x7ff0)) { + /* Unsupported scv vector */ + _exception(SIGILL, regs, ILL_ILLOPC, regs->nip); + return regs->gpr[3]; + } /* * We use the return value of do_syscall_trace_enter() as the * syscall number. If the syscall was rejected for any reason @@ -78,6 +83,11 @@ notrace long system_call_exception(long r3, long r4, long r5, r8 = regs->gpr[8]; } else if (unlikely(r0 >= NR_syscalls)) { + if (unlikely(regs->trap == 0x7ff0)) { + /* Unsupported scv vector */ + _exception(SIGILL, regs, ILL_ILLOPC, regs->nip); + return regs->gpr[3]; + } return -ENOSYS; } @@ -105,16 +115,20 @@ notrace long system_call_exception(long r3, long r4, long r5, * local irqs must be disabled. Returns false if the caller must re-enable * them, check for new work, and try again. */ -static notrace inline bool prep_irq_for_enabled_exit(void) +static notrace inline bool prep_irq_for_enabled_exit(bool clear_ri) { /* This must be done with RI=1 because tracing may touch vmaps */ trace_hardirqs_on(); /* This pattern matches prep_irq_for_idle */ - __hard_EE_RI_disable(); + if (clear_ri) + __hard_EE_RI_disable(); + else + __hard_irq_disable(); if (unlikely(lazy_irq_pending_nocheck())) { /* Took an interrupt, may have more exit work to do. */ - __hard_RI_enable(); + if (clear_ri) + __hard_RI_enable(); trace_hardirqs_off(); local_paca->irq_happened |= PACA_IRQ_HARD_DIS; @@ -136,7 +150,8 @@ static notrace inline bool prep_irq_for_enabled_exit(void) * because RI=0 and soft mask state is "unreconciled", so it is marked notrace. */ notrace unsigned long syscall_exit_prepare(unsigned long r3, - struct pt_regs *regs) + struct pt_regs *regs, + long scv) { unsigned long *ti_flagsp = ¤t_thread_info()->flags; unsigned long ti_flags; @@ -151,7 +166,7 @@ notrace unsigned long syscall_exit_prepare(unsigned long r3, ti_flags = *ti_flagsp; - if (unlikely(r3 >= (unsigned long)-MAX_ERRNO)) { + if (unlikely(r3 >= (unsigned long)-MAX_ERRNO) && !scv) { if (likely(!(ti_flags & (_TIF_NOERROR | _TIF_RESTOREALL)))) { r3 = -r3; regs->ccr |= 0x10000000; /* Set SO bit in CR */ @@ -211,7 +226,8 @@ again: } } - if (unlikely(!prep_irq_for_enabled_exit())) { + /* scv need not set RI=0 because SRRs are not used */ + if (unlikely(!prep_irq_for_enabled_exit(!scv))) { local_irq_enable(); goto again; } @@ -282,7 +298,7 @@ again: } } - if (unlikely(!prep_irq_for_enabled_exit())) { + if (unlikely(!prep_irq_for_enabled_exit(true))) { local_irq_enable(); local_irq_disable(); goto again; @@ -345,7 +361,7 @@ again: } } - if (unlikely(!prep_irq_for_enabled_exit())) { + if (unlikely(!prep_irq_for_enabled_exit(true))) { /* * Can't local_irq_restore to replay if we were in * interrupt context. Must replay directly. diff --git a/arch/powerpc/lib/sstep.c b/arch/powerpc/lib/sstep.c index 5abe98216dc22..ab6a137c38629 100644 --- a/arch/powerpc/lib/sstep.c +++ b/arch/powerpc/lib/sstep.c @@ -16,6 +16,7 @@ #include extern char system_call_common[]; +extern char system_call_vectored_emulate[]; #ifdef CONFIG_PPC64 /* Bits in SRR1 that are copied from MSR */ @@ -1236,6 +1237,9 @@ int analyse_instr(struct instruction_op *op, const struct pt_regs *regs, case 17: /* sc */ if ((word & 0xfe2) == 2) op->type = SYSCALL; + else if (IS_ENABLED(CONFIG_PPC_BOOK3S_64) && + (word & 0xfe3) == 1) + op->type = SYSCALL_VECTORED_0; else op->type = UNKNOWN; return 0; @@ -3378,6 +3382,18 @@ int emulate_step(struct pt_regs *regs, struct ppc_inst instr) regs->msr = MSR_KERNEL; return 1; +#ifdef CONFIG_PPC64_BOOK3S + case SYSCALL_VECTORED_0: /* scv 0 */ + regs->gpr[9] = regs->gpr[13]; + regs->gpr[10] = MSR_KERNEL; + regs->gpr[11] = regs->nip + 4; + regs->gpr[12] = regs->msr & MSR_MASK; + regs->gpr[13] = (unsigned long) get_paca(); + regs->nip = (unsigned long) &system_call_vectored_emulate; + regs->msr = MSR_KERNEL; + return 1; +#endif + case RFI: return -1; #endif diff --git a/arch/powerpc/platforms/pseries/setup.c b/arch/powerpc/platforms/pseries/setup.c index 2db8469e475f3..8c85466e0dd8e 100644 --- a/arch/powerpc/platforms/pseries/setup.c +++ b/arch/powerpc/platforms/pseries/setup.c @@ -358,7 +358,7 @@ static void pseries_lpar_idle(void) * to ever be a problem in practice we can move this into a kernel thread to * finish off the process later in boot. */ -void pseries_enable_reloc_on_exc(void) +bool pseries_enable_reloc_on_exc(void) { long rc; unsigned int delay, total_delay = 0; @@ -369,11 +369,13 @@ void pseries_enable_reloc_on_exc(void) if (rc == H_P2) { pr_info("Relocation on exceptions not" " supported\n"); + return false; } else if (rc != H_SUCCESS) { pr_warn("Unable to enable relocation" " on exceptions: %ld\n", rc); + return false; } - break; + return true; } delay = get_longbusy_msecs(rc); @@ -382,7 +384,7 @@ void pseries_enable_reloc_on_exc(void) pr_warn("Warning: Giving up waiting to enable " "relocation on exceptions (%u msec)!\n", total_delay); - return; + return false; } mdelay(delay); diff --git a/arch/powerpc/xmon/xmon.c b/arch/powerpc/xmon/xmon.c index 7efe4bc3ccf63..3203c3606737f 100644 --- a/arch/powerpc/xmon/xmon.c +++ b/arch/powerpc/xmon/xmon.c @@ -1593,6 +1593,7 @@ const char *getvecname(unsigned long vec) case 0x1300: ret = "(Instruction Breakpoint)"; break; case 0x1500: ret = "(Denormalisation)"; break; case 0x1700: ret = "(Altivec Assist)"; break; + case 0x3000: ret = "(System Call Vectored)"; break; default: ret = ""; } return ret; -- GitLab From 1c1df9087108253caa9e5a26809f0128ccb77ee0 Mon Sep 17 00:00:00 2001 From: Vinod Koul Date: Sat, 18 Jul 2020 19:21:59 +0530 Subject: [PATCH 0768/1476] dmaengine: xilinx: dpdma: remove comparison of unsigned expression MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit xilinx_dpdma_config() channel id is unsigned int and compares with ZYNQMP_DPDMA_VIDEO0 which is zero, so remove this comparison drivers/dma/xilinx/xilinx_dpdma.c:1073:15: warning: comparison of unsigned expression in ‘>= 0’ is always true [-Wtype-limits] if (chan->id >= ZYNQMP_DPDMA_VIDEO0 && chan->id <= ZYNQMP_DPDMA_VIDEO2) Link: https://lore.kernel.org/r/20200718135201.191881-1-vkoul@kernel.org Reviewed-by: Laurent Pinchart Signed-off-by: Vinod Koul --- drivers/dma/xilinx/xilinx_dpdma.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/dma/xilinx/xilinx_dpdma.c b/drivers/dma/xilinx/xilinx_dpdma.c index af88a6762ef46..8e602378f2dca 100644 --- a/drivers/dma/xilinx/xilinx_dpdma.c +++ b/drivers/dma/xilinx/xilinx_dpdma.c @@ -1070,7 +1070,7 @@ static int xilinx_dpdma_config(struct dma_chan *dchan, * Abuse the slave_id to indicate that the channel is part of a video * group. */ - if (chan->id >= ZYNQMP_DPDMA_VIDEO0 && chan->id <= ZYNQMP_DPDMA_VIDEO2) + if (chan->id <= ZYNQMP_DPDMA_VIDEO2) chan->video_group = config->slave_id != 0; spin_unlock_irqrestore(&chan->lock, flags); -- GitLab From ea55b6a349cf0403f54e18e87e3d1cd167a3f16c Mon Sep 17 00:00:00 2001 From: Vinod Koul Date: Sat, 18 Jul 2020 19:22:00 +0530 Subject: [PATCH 0769/1476] dmaengine: xilinx: dpdma: add missing kernel doc xilinx_dpdma_sw_desc_set_dma_addrs() documentation is missing describing 'xdev', so add it drivers/dma/xilinx/xilinx_dpdma.c:313: warning: Function parameter or member 'xdev' not described in 'xilinx_dpdma_sw_desc_set_dma_addrs' Link: https://lore.kernel.org/r/20200718135201.191881-2-vkoul@kernel.org Reviewed-by: Laurent Pinchart Signed-off-by: Vinod Koul --- drivers/dma/xilinx/xilinx_dpdma.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/dma/xilinx/xilinx_dpdma.c b/drivers/dma/xilinx/xilinx_dpdma.c index 8e602378f2dca..430f3714f6a32 100644 --- a/drivers/dma/xilinx/xilinx_dpdma.c +++ b/drivers/dma/xilinx/xilinx_dpdma.c @@ -295,6 +295,7 @@ static inline void dpdma_set(void __iomem *base, u32 offset, u32 set) /** * xilinx_dpdma_sw_desc_set_dma_addrs - Set DMA addresses in the descriptor + * @xdev: DPDMA device * @sw_desc: The software descriptor in which to set DMA addresses * @prev: The previous descriptor * @dma_addr: array of dma addresses -- GitLab From bc227385eb71f2cb453efeddc053265bbd2525f9 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Wed, 22 Jul 2020 19:17:47 +0300 Subject: [PATCH 0770/1476] dmaengine: xilinx: dpdma: Fix kerneldoc warning Document the struct xilinx_dpdma_chan desc field to fix a kerneldoc undocumented member warning (which can be reproduced by compiling with W=1). Signed-off-by: Laurent Pinchart Link: https://lore.kernel.org/r/20200722161747.30048-1-laurent.pinchart@ideasonboard.com Signed-off-by: Vinod Koul --- drivers/dma/xilinx/xilinx_dpdma.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/dma/xilinx/xilinx_dpdma.c b/drivers/dma/xilinx/xilinx_dpdma.c index 430f3714f6a32..b37197c772aae 100644 --- a/drivers/dma/xilinx/xilinx_dpdma.c +++ b/drivers/dma/xilinx/xilinx_dpdma.c @@ -214,6 +214,7 @@ struct xilinx_dpdma_tx_desc { * @lock: lock to access struct xilinx_dpdma_chan * @desc_pool: descriptor allocation pool * @err_task: error IRQ bottom half handler + * @desc: References to descriptors being processed * @desc.pending: Descriptor schedule to the hardware, pending execution * @desc.active: Descriptor being executed by the hardware * @xdev: DPDMA device -- GitLab From 600a5b4fc8e8f6dad098cd50e0e727cb2a16be46 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Thu, 16 Jul 2020 17:34:30 -0500 Subject: [PATCH 0771/1476] PCI/ERR: Rename pci_aer_clear_device_status() to pcie_clear_device_status() pci_aer_clear_device_status() clears the error bits in the PCIe Device Status Register (PCI_EXP_DEVSTA). Every PCIe device has this register, regardless of whether it supports AER. Rename pci_aer_clear_device_status() to pcie_clear_device_status() to make clear that it is PCIe-specific but not AER-specific. Move it to drivers/pci/pci.c, again since it's not AER-specific. No functional change intended. Link: https://lore.kernel.org/r/20200717195619.766662-1-helgaas@kernel.org Signed-off-by: Bjorn Helgaas --- drivers/pci/pci.c | 8 ++++++++ drivers/pci/pci.h | 3 +-- drivers/pci/pcie/aer.c | 10 +--------- drivers/pci/pcie/err.c | 2 +- 4 files changed, 11 insertions(+), 12 deletions(-) diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index ce096272f52b1..bc37013c7185e 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -2046,6 +2046,14 @@ int pci_set_pcie_reset_state(struct pci_dev *dev, enum pcie_reset_state state) } EXPORT_SYMBOL_GPL(pci_set_pcie_reset_state); +void pcie_clear_device_status(struct pci_dev *dev) +{ + u16 sta; + + pcie_capability_read_word(dev, PCI_EXP_DEVSTA, &sta); + pcie_capability_write_word(dev, PCI_EXP_DEVSTA, sta); +} + /** * pcie_clear_root_pme_status - Clear root port PME interrupt status. * @dev: PCIe root port or event collector. diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index c6c0c455f59fc..b1d1ad6fca746 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h @@ -92,6 +92,7 @@ void pci_refresh_power_state(struct pci_dev *dev); int pci_power_up(struct pci_dev *dev); void pci_disable_enabled_device(struct pci_dev *dev); int pci_finish_runtime_suspend(struct pci_dev *dev); +void pcie_clear_device_status(struct pci_dev *dev); void pcie_clear_root_pme_status(struct pci_dev *dev); bool pci_check_pme_status(struct pci_dev *dev); void pci_pme_wakeup_bus(struct pci_bus *bus); @@ -658,7 +659,6 @@ void pci_aer_init(struct pci_dev *dev); void pci_aer_exit(struct pci_dev *dev); extern const struct attribute_group aer_stats_attr_group; void pci_aer_clear_fatal_status(struct pci_dev *dev); -void pci_aer_clear_device_status(struct pci_dev *dev); int pci_aer_clear_status(struct pci_dev *dev); int pci_aer_raw_clear_status(struct pci_dev *dev); #else @@ -666,7 +666,6 @@ static inline void pci_no_aer(void) { } static inline void pci_aer_init(struct pci_dev *d) { } static inline void pci_aer_exit(struct pci_dev *d) { } static inline void pci_aer_clear_fatal_status(struct pci_dev *dev) { } -static inline void pci_aer_clear_device_status(struct pci_dev *dev) { } static inline int pci_aer_clear_status(struct pci_dev *dev) { return -EINVAL; } static inline int pci_aer_raw_clear_status(struct pci_dev *dev) { return -EINVAL; } #endif diff --git a/drivers/pci/pcie/aer.c b/drivers/pci/pcie/aer.c index ca886bf91fd9f..f6d7783085582 100644 --- a/drivers/pci/pcie/aer.c +++ b/drivers/pci/pcie/aer.c @@ -241,14 +241,6 @@ int pci_disable_pcie_error_reporting(struct pci_dev *dev) } EXPORT_SYMBOL_GPL(pci_disable_pcie_error_reporting); -void pci_aer_clear_device_status(struct pci_dev *dev) -{ - u16 sta; - - pcie_capability_read_word(dev, PCI_EXP_DEVSTA, &sta); - pcie_capability_write_word(dev, PCI_EXP_DEVSTA, sta); -} - int pci_aer_clear_nonfatal_status(struct pci_dev *dev) { int aer = dev->aer_cap; @@ -947,7 +939,7 @@ static void handle_error_source(struct pci_dev *dev, struct aer_err_info *info) if (aer) pci_write_config_dword(dev, aer + PCI_ERR_COR_STATUS, info->status); - pci_aer_clear_device_status(dev); + pcie_clear_device_status(dev); } else if (info->severity == AER_NONFATAL) pcie_do_recovery(dev, pci_channel_io_normal, aer_root_reset); else if (info->severity == AER_FATAL) diff --git a/drivers/pci/pcie/err.c b/drivers/pci/pcie/err.c index 467686ee2d8be..55755bc493f1a 100644 --- a/drivers/pci/pcie/err.c +++ b/drivers/pci/pcie/err.c @@ -197,7 +197,7 @@ pci_ers_result_t pcie_do_recovery(struct pci_dev *dev, pci_dbg(dev, "broadcast resume message\n"); pci_walk_bus(bus, report_resume, &status); - pci_aer_clear_device_status(dev); + pcie_clear_device_status(dev); pci_aer_clear_nonfatal_status(dev); pci_info(dev, "device recovery successful\n"); return status; -- GitLab From 068c29a248b6ddbfdf7bb150b547569759620d36 Mon Sep 17 00:00:00 2001 From: Jonathan Cameron Date: Mon, 22 Jun 2020 19:35:23 +0800 Subject: [PATCH 0772/1476] PCI/ERR: Clear PCIe Device Status errors only if OS owns AER pcie_clear_device_status() resets the error bits in the PCIe Device Status Register (PCI_EXP_DEVSTA). Previously we did this unconditionally, but on ACPI systems, the _OSC AER bit negotiates control of the AER capability. Per sec 4.5.1 of the System Firmware Intermediary _OSC and DPC Updates ECN [1], this bit also covers other error enable/status bits including the following: Correctable Error Reporting Enable Non-Fatal Error Reporting Enable Fatal Error Reporting Enable Unsupported Request Reporting Enable These bits are all in the PCIe Device Control register (the ECN omitted "Reporting", but I think that's a typo), so by implication the _OSC AER bit also applies to the error status bits in the PCIe Device Status register: Correctable Error Detected Non-Fatal Error Detected Fatal Error Detected Unsupported Request Detected Clear the PCIe Device Status error bits only when the OS controls the AER capability and related error enable/status bits. If platform firmware controls the AER capability, firmware is responsible for clearing these bits. One call path leading here is: ghes_do_proc ghes_handle_aer aer_recover_queue schedule_work(&aer_recover_work) ... aer_recover_work_func pcie_do_recovery pcie_clear_device_status [1] System Firmware Intermediary (SFI) _OSC and DPC Updates ECN, Feb 24, 2020, affecting PCI Firmware Specification, Rev. 3.2 https://members.pcisig.com/wg/PCI-SIG/document/14076 [bhelgaas: commit log, move test from pcie_clear_device_status() to callers] Link: https://lore.kernel.org/r/20200622113523.891666-1-Jonathan.Cameron@huawei.com Signed-off-by: Jonathan Cameron Signed-off-by: Bjorn Helgaas --- drivers/pci/pcie/aer.c | 3 ++- drivers/pci/pcie/err.c | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/pci/pcie/aer.c b/drivers/pci/pcie/aer.c index f6d7783085582..cccd6747e5f69 100644 --- a/drivers/pci/pcie/aer.c +++ b/drivers/pci/pcie/aer.c @@ -939,7 +939,8 @@ static void handle_error_source(struct pci_dev *dev, struct aer_err_info *info) if (aer) pci_write_config_dword(dev, aer + PCI_ERR_COR_STATUS, info->status); - pcie_clear_device_status(dev); + if (pcie_aer_is_native(dev)) + pcie_clear_device_status(dev); } else if (info->severity == AER_NONFATAL) pcie_do_recovery(dev, pci_channel_io_normal, aer_root_reset); else if (info->severity == AER_FATAL) diff --git a/drivers/pci/pcie/err.c b/drivers/pci/pcie/err.c index 55755bc493f1a..c543f419d8f9e 100644 --- a/drivers/pci/pcie/err.c +++ b/drivers/pci/pcie/err.c @@ -197,7 +197,8 @@ pci_ers_result_t pcie_do_recovery(struct pci_dev *dev, pci_dbg(dev, "broadcast resume message\n"); pci_walk_bus(bus, report_resume, &status); - pcie_clear_device_status(dev); + if (pcie_aer_is_native(dev)) + pcie_clear_device_status(dev); pci_aer_clear_nonfatal_status(dev); pci_info(dev, "device recovery successful\n"); return status; -- GitLab From b6b54b42722a2393056c891c0d05cd8cc40eb776 Mon Sep 17 00:00:00 2001 From: Jordan Niethe Date: Mon, 25 May 2020 12:59:19 +1000 Subject: [PATCH 0773/1476] powerpc/sstep: Add tests for prefixed integer load/stores Add tests for the prefixed versions of the integer load/stores that are currently tested. This includes the following instructions: * Prefixed Load Doubleword (pld) * Prefixed Load Word and Zero (plwz) * Prefixed Store Doubleword (pstd) Skip the new tests if ISA v3.1 is unsupported. Signed-off-by: Jordan Niethe [mpe: Fix conflicts with ppc-opcode.h changes] Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20200525025923.19843-1-jniethe5@gmail.com --- arch/powerpc/include/asm/ppc-opcode.h | 9 +++ arch/powerpc/lib/test_emulate_step.c | 85 +++++++++++++++++++++++++++ 2 files changed, 94 insertions(+) diff --git a/arch/powerpc/include/asm/ppc-opcode.h b/arch/powerpc/include/asm/ppc-opcode.h index 6b7a3f0f2d62f..90ae33a74d783 100644 --- a/arch/powerpc/include/asm/ppc-opcode.h +++ b/arch/powerpc/include/asm/ppc-opcode.h @@ -265,6 +265,14 @@ #define PPC_INST_BRANCH 0x48000000 #define PPC_INST_BRANCH_COND 0x40800000 +/* Prefixes */ +#define PPC_PREFIX_MLS 0x06000000 +#define PPC_PREFIX_8LS 0x04000000 + +/* Prefixed instructions */ +#define PPC_INST_PLD 0xe4000000 +#define PPC_INST_PSTD 0xf4000000 + /* macros to insert fields into opcodes */ #define ___PPC_RA(a) (((a) & 0x1f) << 16) #define ___PPC_RB(b) (((b) & 0x1f) << 11) @@ -296,6 +304,7 @@ #define __PPC_CT(t) (((t) & 0x0f) << 21) #define __PPC_SPR(r) ((((r) & 0x1f) << 16) | ((((r) >> 5) & 0x1f) << 11)) #define __PPC_RC21 (0x1 << 10) +#define __PPC_PRFX_R(r) (((r) & 0x1) << 20) /* * Both low and high 16 bits are added as SIGNED additions, so if low 16 bits diff --git a/arch/powerpc/lib/test_emulate_step.c b/arch/powerpc/lib/test_emulate_step.c index 988c734e7370a..34f1ea0e67424 100644 --- a/arch/powerpc/lib/test_emulate_step.c +++ b/arch/powerpc/lib/test_emulate_step.c @@ -19,6 +19,18 @@ #define IGNORE_XER (0x1UL << 32) #define IGNORE_CCR (0x1UL << 33) +#define TEST_PLD(r, base, i, pr) \ + ppc_inst_prefix(PPC_PREFIX_8LS | __PPC_PRFX_R(pr) | IMM_H(i), \ + PPC_INST_PLD | ___PPC_RT(r) | ___PPC_RA(base) | IMM_L(i)) + +#define TEST_PLWZ(r, base, i, pr) \ + ppc_inst_prefix(PPC_PREFIX_MLS | __PPC_PRFX_R(pr) | IMM_H(i), \ + PPC_RAW_LWZ(r, base, i)) + +#define TEST_PSTD(r, base, i, pr) \ + ppc_inst_prefix(PPC_PREFIX_8LS | __PPC_PRFX_R(pr) | IMM_H(i), \ + PPC_INST_PSTD | ___PPC_RT(r) | ___PPC_RA(base) | IMM_L(i)) + static void __init init_pt_regs(struct pt_regs *regs) { static unsigned long msr; @@ -70,6 +82,29 @@ static void __init test_ld(void) show_result("ld", "FAIL"); } +static void __init test_pld(void) +{ + struct pt_regs regs; + unsigned long a = 0x23; + int stepped = -1; + + if (!cpu_has_feature(CPU_FTR_ARCH_31)) { + show_result("pld", "SKIP (!CPU_FTR_ARCH_31)"); + return; + } + + init_pt_regs(®s); + regs.gpr[3] = (unsigned long)&a; + + /* pld r5, 0(r3), 0 */ + stepped = emulate_step(®s, TEST_PLD(5, 3, 0, 0)); + + if (stepped == 1 && regs.gpr[5] == a) + show_result("pld", "PASS"); + else + show_result("pld", "FAIL"); +} + static void __init test_lwz(void) { struct pt_regs regs; @@ -88,6 +123,30 @@ static void __init test_lwz(void) show_result("lwz", "FAIL"); } +static void __init test_plwz(void) +{ + struct pt_regs regs; + unsigned int a = 0x4545; + int stepped = -1; + + if (!cpu_has_feature(CPU_FTR_ARCH_31)) { + show_result("plwz", "SKIP (!CPU_FTR_ARCH_31)"); + return; + } + + init_pt_regs(®s); + regs.gpr[3] = (unsigned long)&a; + + /* plwz r5, 0(r3), 0 */ + + stepped = emulate_step(®s, TEST_PLWZ(5, 3, 0, 0)); + + if (stepped == 1 && regs.gpr[5] == a) + show_result("plwz", "PASS"); + else + show_result("plwz", "FAIL"); +} + static void __init test_lwzx(void) { struct pt_regs regs; @@ -125,6 +184,29 @@ static void __init test_std(void) show_result("std", "FAIL"); } +static void __init test_pstd(void) +{ + struct pt_regs regs; + unsigned long a = 0x1234; + int stepped = -1; + + if (!cpu_has_feature(CPU_FTR_ARCH_31)) { + show_result("pstd", "SKIP (!CPU_FTR_ARCH_31)"); + return; + } + + init_pt_regs(®s); + regs.gpr[3] = (unsigned long)&a; + regs.gpr[5] = 0x5678; + + /* pstd r5, 0(r3), 0 */ + stepped = emulate_step(®s, TEST_PSTD(5, 3, 0, 0)); + if (stepped == 1 || regs.gpr[5] == a) + show_result("pstd", "PASS"); + else + show_result("pstd", "FAIL"); +} + static void __init test_ldarx_stdcx(void) { struct pt_regs regs; @@ -404,9 +486,12 @@ static void __init test_lxvd2x_stxvd2x(void) static void __init run_tests_load_store(void) { test_ld(); + test_pld(); test_lwz(); + test_plwz(); test_lwzx(); test_std(); + test_pstd(); test_ldarx_stdcx(); test_lfsx_stfsx(); test_lfdx_stfdx(); -- GitLab From 0396de6d8561c721b03fce386eb9682b37a26013 Mon Sep 17 00:00:00 2001 From: Jordan Niethe Date: Mon, 25 May 2020 12:59:20 +1000 Subject: [PATCH 0774/1476] powerpc/sstep: Add tests for prefixed floating-point load/stores Add tests for the prefixed versions of the floating-point load/stores that are currently tested. This includes the following instructions: * Prefixed Load Floating-Point Single (plfs) * Prefixed Load Floating-Point Double (plfd) * Prefixed Store Floating-Point Single (pstfs) * Prefixed Store Floating-Point Double (pstfd) Skip the new tests if ISA v3.10 is unsupported. Signed-off-by: Jordan Niethe [mpe: Fix conflicts with ppc-opcode.h changes] Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20200525025923.19843-2-jniethe5@gmail.com --- arch/powerpc/include/asm/ppc-opcode.h | 4 + arch/powerpc/lib/test_emulate_step.c | 124 ++++++++++++++++++++++++++ 2 files changed, 128 insertions(+) diff --git a/arch/powerpc/include/asm/ppc-opcode.h b/arch/powerpc/include/asm/ppc-opcode.h index 90ae33a74d783..4c0bdafb6a7b9 100644 --- a/arch/powerpc/include/asm/ppc-opcode.h +++ b/arch/powerpc/include/asm/ppc-opcode.h @@ -266,6 +266,10 @@ #define PPC_INST_BRANCH_COND 0x40800000 /* Prefixes */ +#define PPC_INST_LFS 0xc0000000 +#define PPC_INST_STFS 0xd0000000 +#define PPC_INST_LFD 0xc8000000 +#define PPC_INST_STFD 0xd8000000 #define PPC_PREFIX_MLS 0x06000000 #define PPC_PREFIX_8LS 0x04000000 diff --git a/arch/powerpc/lib/test_emulate_step.c b/arch/powerpc/lib/test_emulate_step.c index 34f1ea0e67424..0059290f0fed2 100644 --- a/arch/powerpc/lib/test_emulate_step.c +++ b/arch/powerpc/lib/test_emulate_step.c @@ -31,6 +31,22 @@ ppc_inst_prefix(PPC_PREFIX_8LS | __PPC_PRFX_R(pr) | IMM_H(i), \ PPC_INST_PSTD | ___PPC_RT(r) | ___PPC_RA(base) | IMM_L(i)) +#define TEST_PLFS(r, base, i, pr) \ + ppc_inst_prefix(PPC_PREFIX_MLS | __PPC_PRFX_R(pr) | IMM_H(i), \ + PPC_INST_LFS | ___PPC_RT(r) | ___PPC_RA(base) | IMM_L(i)) + +#define TEST_PSTFS(r, base, i, pr) \ + ppc_inst_prefix(PPC_PREFIX_MLS | __PPC_PRFX_R(pr) | IMM_H(i), \ + PPC_INST_STFS | ___PPC_RT(r) | ___PPC_RA(base) | IMM_L(i)) + +#define TEST_PLFD(r, base, i, pr) \ + ppc_inst_prefix(PPC_PREFIX_MLS | __PPC_PRFX_R(pr) | IMM_H(i), \ + PPC_INST_LFD | ___PPC_RT(r) | ___PPC_RA(base) | IMM_L(i)) + +#define TEST_PSTFD(r, base, i, pr) \ + ppc_inst_prefix(PPC_PREFIX_MLS | __PPC_PRFX_R(pr) | IMM_H(i), \ + PPC_INST_STFD | ___PPC_RT(r) | ___PPC_RA(base) | IMM_L(i)) + static void __init init_pt_regs(struct pt_regs *regs) { static unsigned long msr; @@ -304,6 +320,53 @@ static void __init test_lfsx_stfsx(void) show_result("stfsx", "FAIL"); } +static void __init test_plfs_pstfs(void) +{ + struct pt_regs regs; + union { + float a; + int b; + } c; + int cached_b; + int stepped = -1; + + if (!cpu_has_feature(CPU_FTR_ARCH_31)) { + show_result("pld", "SKIP (!CPU_FTR_ARCH_31)"); + return; + } + + init_pt_regs(®s); + + + /*** plfs ***/ + + c.a = 123.45; + cached_b = c.b; + + regs.gpr[3] = (unsigned long)&c.a; + + /* plfs frt10, 0(r3), 0 */ + stepped = emulate_step(®s, TEST_PLFS(10, 3, 0, 0)); + + if (stepped == 1) + show_result("plfs", "PASS"); + else + show_result("plfs", "FAIL"); + + + /*** pstfs ***/ + + c.a = 678.91; + + /* pstfs frs10, 0(r3), 0 */ + stepped = emulate_step(®s, TEST_PSTFS(10, 3, 0, 0)); + + if (stepped == 1 && c.b == cached_b) + show_result("pstfs", "PASS"); + else + show_result("pstfs", "FAIL"); +} + static void __init test_lfdx_stfdx(void) { struct pt_regs regs; @@ -346,6 +409,53 @@ static void __init test_lfdx_stfdx(void) else show_result("stfdx", "FAIL"); } + +static void __init test_plfd_pstfd(void) +{ + struct pt_regs regs; + union { + double a; + long b; + } c; + long cached_b; + int stepped = -1; + + if (!cpu_has_feature(CPU_FTR_ARCH_31)) { + show_result("pld", "SKIP (!CPU_FTR_ARCH_31)"); + return; + } + + init_pt_regs(®s); + + + /*** plfd ***/ + + c.a = 123456.78; + cached_b = c.b; + + regs.gpr[3] = (unsigned long)&c.a; + + /* plfd frt10, 0(r3), 0 */ + stepped = emulate_step(®s, TEST_PLFD(10, 3, 0, 0)); + + if (stepped == 1) + show_result("plfd", "PASS"); + else + show_result("plfd", "FAIL"); + + + /*** pstfd ***/ + + c.a = 987654.32; + + /* pstfd frs10, 0(r3), 0 */ + stepped = emulate_step(®s, TEST_PSTFD(10, 3, 0, 0)); + + if (stepped == 1 && c.b == cached_b) + show_result("pstfd", "PASS"); + else + show_result("pstfd", "FAIL"); +} #else static void __init test_lfsx_stfsx(void) { @@ -353,11 +463,23 @@ static void __init test_lfsx_stfsx(void) show_result("stfsx", "SKIP (CONFIG_PPC_FPU is not set)"); } +static void __init test_plfs_pstfs(void) +{ + show_result("plfs", "SKIP (CONFIG_PPC_FPU is not set)"); + show_result("pstfs", "SKIP (CONFIG_PPC_FPU is not set)"); +} + static void __init test_lfdx_stfdx(void) { show_result("lfdx", "SKIP (CONFIG_PPC_FPU is not set)"); show_result("stfdx", "SKIP (CONFIG_PPC_FPU is not set)"); } + +static void __init test_plfd_pstfd(void) +{ + show_result("plfd", "SKIP (CONFIG_PPC_FPU is not set)"); + show_result("pstfd", "SKIP (CONFIG_PPC_FPU is not set)"); +} #endif /* CONFIG_PPC_FPU */ #ifdef CONFIG_ALTIVEC @@ -494,7 +616,9 @@ static void __init run_tests_load_store(void) test_pstd(); test_ldarx_stdcx(); test_lfsx_stfsx(); + test_plfs_pstfs(); test_lfdx_stfdx(); + test_plfd_pstfd(); test_lvx_stvx(); test_lxvd2x_stxvd2x(); } -- GitLab From 1c89cf7fbed36f078b20fd47d308b4fc6dbff5f6 Mon Sep 17 00:00:00 2001 From: Jordan Niethe Date: Mon, 25 May 2020 12:59:21 +1000 Subject: [PATCH 0775/1476] powerpc/sstep: Set NIP in instruction emulation tests The tests for emulation of compute instructions execute and emulate an instruction and then compare the results to verify the emulation. In ISA v3.1 there are instructions that operate relative to the NIP. Therefore set the NIP in the regs used for the emulated instruction to the location of the executed instruction so they will give the same result. This is a rework of a patch by Balamuruhan S. Signed-off-by: Jordan Niethe Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20200525025923.19843-3-jniethe5@gmail.com --- arch/powerpc/lib/test_emulate_step.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/arch/powerpc/lib/test_emulate_step.c b/arch/powerpc/lib/test_emulate_step.c index 0059290f0fed2..857b95ef57131 100644 --- a/arch/powerpc/lib/test_emulate_step.c +++ b/arch/powerpc/lib/test_emulate_step.c @@ -1011,11 +1011,14 @@ static struct compute_test compute_tests[] = { static int __init emulate_compute_instr(struct pt_regs *regs, struct ppc_inst instr) { + extern s32 patch__exec_instr; struct instruction_op op; if (!regs || !ppc_inst_val(instr)) return -EINVAL; + regs->nip = patch_site_addr(&patch__exec_instr); + if (analyse_instr(&op, regs, instr) != 1 || GETTYPE(op.type) != COMPUTE) { pr_info("emulation failed, instruction = 0x%08x\n", ppc_inst_val(instr)); -- GitLab From 301ebf7d69f6709575d137a41a0291f69f343aed Mon Sep 17 00:00:00 2001 From: Jordan Niethe Date: Mon, 25 May 2020 12:59:22 +1000 Subject: [PATCH 0776/1476] powerpc/sstep: Let compute tests specify a required cpu feature An a array of struct compute_test's are used to declare tests for compute instructions. Add a cpu_feature field to struct compute_test as an optional way to specify a cpu feature that must be present. If not present then skip the test. Signed-off-by: Jordan Niethe Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20200525025923.19843-4-jniethe5@gmail.com --- arch/powerpc/lib/test_emulate_step.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/arch/powerpc/lib/test_emulate_step.c b/arch/powerpc/lib/test_emulate_step.c index 857b95ef57131..f8cd3ca356c65 100644 --- a/arch/powerpc/lib/test_emulate_step.c +++ b/arch/powerpc/lib/test_emulate_step.c @@ -625,6 +625,7 @@ static void __init run_tests_load_store(void) struct compute_test { char *mnemonic; + unsigned long cpu_feature; struct { char *descr; unsigned long flags; @@ -1068,6 +1069,11 @@ static void __init run_tests_compute(void) for (i = 0; i < ARRAY_SIZE(compute_tests); i++) { test = &compute_tests[i]; + if (test->cpu_feature && !early_cpu_has_feature(test->cpu_feature)) { + show_result(test->mnemonic, "SKIP (!CPU_FTR)"); + continue; + } + for (j = 0; j < MAX_SUBTESTS && test->subtests[j].descr; j++) { instr = test->subtests[j].instr; flags = test->subtests[j].flags; -- GitLab From 4f825900786e1c24e4c48622e12eb493a6cd27b6 Mon Sep 17 00:00:00 2001 From: Jordan Niethe Date: Mon, 25 May 2020 12:59:23 +1000 Subject: [PATCH 0777/1476] powerpc/sstep: Add tests for Prefixed Add Immediate Use the existing support for testing compute type instructions to test Prefixed Add Immediate (paddi). The R bit of the paddi instruction controls whether current instruction address is used. Add test cases for when R=1 and for R=0. paddi has a 34 bit immediate field formed by concatenating si0 and si1. Add tests for the extreme values of this field. Skip the paddi tests if ISA v3.1 is unsupported. Some of these test cases were added by Balamuruhan S. Signed-off-by: Jordan Niethe [mpe: Fix conflicts with ppc-opcode.h changes, squash in .balign] Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20200525025923.19843-5-jniethe5@gmail.com --- arch/powerpc/lib/test_emulate_step.c | 125 ++++++++++++++++++ .../lib/test_emulate_step_exec_instr.S | 2 + 2 files changed, 127 insertions(+) diff --git a/arch/powerpc/lib/test_emulate_step.c b/arch/powerpc/lib/test_emulate_step.c index f8cd3ca356c65..c9a1a343123d1 100644 --- a/arch/powerpc/lib/test_emulate_step.c +++ b/arch/powerpc/lib/test_emulate_step.c @@ -47,6 +47,11 @@ ppc_inst_prefix(PPC_PREFIX_MLS | __PPC_PRFX_R(pr) | IMM_H(i), \ PPC_INST_STFD | ___PPC_RT(r) | ___PPC_RA(base) | IMM_L(i)) +#define TEST_PADDI(t, a, i, pr) \ + ppc_inst_prefix(PPC_PREFIX_MLS | __PPC_PRFX_R(pr) | IMM_H(i), \ + PPC_RAW_ADDI(t, a, i)) + + static void __init init_pt_regs(struct pt_regs *regs) { static unsigned long msr; @@ -634,6 +639,11 @@ struct compute_test { } subtests[MAX_SUBTESTS + 1]; }; +/* Extreme values for si0||si1 (the MLS:D-form 34 bit immediate field) */ +#define SI_MIN BIT(33) +#define SI_MAX (BIT(33) - 1) +#define SI_UMAX (BIT(34) - 1) + static struct compute_test compute_tests[] = { { .mnemonic = "nop", @@ -1006,6 +1016,121 @@ static struct compute_test compute_tests[] = { } } } + }, + { + .mnemonic = "paddi", + .cpu_feature = CPU_FTR_ARCH_31, + .subtests = { + { + .descr = "RA = LONG_MIN, SI = SI_MIN, R = 0", + .instr = TEST_PADDI(21, 22, SI_MIN, 0), + .regs = { + .gpr[21] = 0, + .gpr[22] = LONG_MIN, + } + }, + { + .descr = "RA = LONG_MIN, SI = SI_MAX, R = 0", + .instr = TEST_PADDI(21, 22, SI_MAX, 0), + .regs = { + .gpr[21] = 0, + .gpr[22] = LONG_MIN, + } + }, + { + .descr = "RA = LONG_MAX, SI = SI_MAX, R = 0", + .instr = TEST_PADDI(21, 22, SI_MAX, 0), + .regs = { + .gpr[21] = 0, + .gpr[22] = LONG_MAX, + } + }, + { + .descr = "RA = ULONG_MAX, SI = SI_UMAX, R = 0", + .instr = TEST_PADDI(21, 22, SI_UMAX, 0), + .regs = { + .gpr[21] = 0, + .gpr[22] = ULONG_MAX, + } + }, + { + .descr = "RA = ULONG_MAX, SI = 0x1, R = 0", + .instr = TEST_PADDI(21, 22, 0x1, 0), + .regs = { + .gpr[21] = 0, + .gpr[22] = ULONG_MAX, + } + }, + { + .descr = "RA = INT_MIN, SI = SI_MIN, R = 0", + .instr = TEST_PADDI(21, 22, SI_MIN, 0), + .regs = { + .gpr[21] = 0, + .gpr[22] = INT_MIN, + } + }, + { + .descr = "RA = INT_MIN, SI = SI_MAX, R = 0", + .instr = TEST_PADDI(21, 22, SI_MAX, 0), + .regs = { + .gpr[21] = 0, + .gpr[22] = INT_MIN, + } + }, + { + .descr = "RA = INT_MAX, SI = SI_MAX, R = 0", + .instr = TEST_PADDI(21, 22, SI_MAX, 0), + .regs = { + .gpr[21] = 0, + .gpr[22] = INT_MAX, + } + }, + { + .descr = "RA = UINT_MAX, SI = 0x1, R = 0", + .instr = TEST_PADDI(21, 22, 0x1, 0), + .regs = { + .gpr[21] = 0, + .gpr[22] = UINT_MAX, + } + }, + { + .descr = "RA = UINT_MAX, SI = SI_MAX, R = 0", + .instr = TEST_PADDI(21, 22, SI_MAX, 0), + .regs = { + .gpr[21] = 0, + .gpr[22] = UINT_MAX, + } + }, + { + .descr = "RA is r0, SI = SI_MIN, R = 0", + .instr = TEST_PADDI(21, 0, SI_MIN, 0), + .regs = { + .gpr[21] = 0x0, + } + }, + { + .descr = "RA = 0, SI = SI_MIN, R = 0", + .instr = TEST_PADDI(21, 22, SI_MIN, 0), + .regs = { + .gpr[21] = 0x0, + .gpr[22] = 0x0, + } + }, + { + .descr = "RA is r0, SI = 0, R = 1", + .instr = TEST_PADDI(21, 0, 0, 1), + .regs = { + .gpr[21] = 0, + } + }, + { + .descr = "RA is r0, SI = SI_MIN, R = 1", + .instr = TEST_PADDI(21, 0, SI_MIN, 1), + .regs = { + .gpr[21] = 0, + } + } + } } }; diff --git a/arch/powerpc/lib/test_emulate_step_exec_instr.S b/arch/powerpc/lib/test_emulate_step_exec_instr.S index 1580f34f4f4fa..9ef941d958d80 100644 --- a/arch/powerpc/lib/test_emulate_step_exec_instr.S +++ b/arch/powerpc/lib/test_emulate_step_exec_instr.S @@ -80,7 +80,9 @@ _GLOBAL(exec_instr) REST_NVGPRS(r31) /* Placeholder for the test instruction */ + .balign 64 1: nop + nop patch_site 1b patch__exec_instr /* -- GitLab From 50428fdc53ba48f6936b10dfdc0d644972403908 Mon Sep 17 00:00:00 2001 From: Jordan Niethe Date: Tue, 2 Jun 2020 15:27:25 +1000 Subject: [PATCH 0778/1476] powerpc: Add a ppc_inst_as_str() helper There are quite a few places where instructions are printed, this is done using a '%x' format specifier. With the introduction of prefixed instructions, this does not work well. Currently in these places, ppc_inst_val() is used for the value for %x so only the first word of prefixed instructions are printed. When the instructions are word instructions, only a single word should be printed. For prefixed instructions both the prefix and suffix should be printed. To accommodate both of these situations, instead of a '%x' specifier use '%s' and introduce a helper, __ppc_inst_as_str() which returns a char *. The char * __ppc_inst_as_str() returns is buffer that is passed to it by the caller. It is cumbersome to require every caller of __ppc_inst_as_str() to now declare a buffer. To make it more convenient to use __ppc_inst_as_str(), wrap it in a macro that uses a compound statement to allocate a buffer on the caller's stack before calling it. Signed-off-by: Jordan Niethe Reviewed-by: Joel Stanley Acked-by: Segher Boessenkool [mpe: Drop 0x prefix to match most existings uses, especially xmon] Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20200602052728.18227-1-jniethe5@gmail.com --- arch/powerpc/include/asm/inst.h | 19 +++++++++++++++++++ arch/powerpc/kernel/kprobes.c | 2 +- arch/powerpc/kernel/trace/ftrace.c | 26 +++++++++++++------------- arch/powerpc/lib/test_emulate_step.c | 4 ++-- arch/powerpc/xmon/xmon.c | 2 +- 5 files changed, 36 insertions(+), 17 deletions(-) diff --git a/arch/powerpc/include/asm/inst.h b/arch/powerpc/include/asm/inst.h index 45f3ec868258f..cc73c12675721 100644 --- a/arch/powerpc/include/asm/inst.h +++ b/arch/powerpc/include/asm/inst.h @@ -122,6 +122,25 @@ static inline u64 ppc_inst_as_u64(struct ppc_inst x) #endif } +#define PPC_INST_STR_LEN sizeof("00000000 00000000") + +static inline char *__ppc_inst_as_str(char str[PPC_INST_STR_LEN], struct ppc_inst x) +{ + if (ppc_inst_prefixed(x)) + sprintf(str, "%08x %08x", ppc_inst_val(x), ppc_inst_suffix(x)); + else + sprintf(str, "%08x", ppc_inst_val(x)); + + return str; +} + +#define ppc_inst_as_str(x) \ +({ \ + char __str[PPC_INST_STR_LEN]; \ + __ppc_inst_as_str(__str, x); \ + __str; \ +}) + int probe_user_read_inst(struct ppc_inst *inst, struct ppc_inst __user *nip); diff --git a/arch/powerpc/kernel/kprobes.c b/arch/powerpc/kernel/kprobes.c index 9cc792a3a6a91..6ab9b4d037c30 100644 --- a/arch/powerpc/kernel/kprobes.c +++ b/arch/powerpc/kernel/kprobes.c @@ -244,7 +244,7 @@ static int try_to_emulate(struct kprobe *p, struct pt_regs *regs) * So, we should never get here... but, its still * good to catch them, just in case... */ - printk("Can't step on instruction %x\n", ppc_inst_val(insn)); + printk("Can't step on instruction %s\n", ppc_inst_as_str(insn)); BUG(); } else { /* diff --git a/arch/powerpc/kernel/trace/ftrace.c b/arch/powerpc/kernel/trace/ftrace.c index c1fede6ec9349..42761ebec9f75 100644 --- a/arch/powerpc/kernel/trace/ftrace.c +++ b/arch/powerpc/kernel/trace/ftrace.c @@ -73,8 +73,8 @@ ftrace_modify_code(unsigned long ip, struct ppc_inst old, struct ppc_inst new) /* Make sure it is what we expect it to be */ if (!ppc_inst_equal(replaced, old)) { - pr_err("%p: replaced (%#x) != old (%#x)", - (void *)ip, ppc_inst_val(replaced), ppc_inst_val(old)); + pr_err("%p: replaced (%s) != old (%s)", + (void *)ip, ppc_inst_as_str(replaced), ppc_inst_as_str(old)); return -EINVAL; } @@ -137,7 +137,7 @@ __ftrace_make_nop(struct module *mod, /* Make sure that that this is still a 24bit jump */ if (!is_bl_op(op)) { - pr_err("Not expected bl: opcode is %x\n", ppc_inst_val(op)); + pr_err("Not expected bl: opcode is %s\n", ppc_inst_as_str(op)); return -EINVAL; } @@ -172,8 +172,8 @@ __ftrace_make_nop(struct module *mod, /* We expect either a mflr r0, or a std r0, LRSAVE(r1) */ if (!ppc_inst_equal(op, ppc_inst(PPC_INST_MFLR)) && !ppc_inst_equal(op, ppc_inst(PPC_INST_STD_LR))) { - pr_err("Unexpected instruction %08x around bl _mcount\n", - ppc_inst_val(op)); + pr_err("Unexpected instruction %s around bl _mcount\n", + ppc_inst_as_str(op)); return -EINVAL; } #else @@ -203,7 +203,7 @@ __ftrace_make_nop(struct module *mod, } if (!ppc_inst_equal(op, ppc_inst(PPC_INST_LD_TOC))) { - pr_err("Expected %08x found %08x\n", PPC_INST_LD_TOC, ppc_inst_val(op)); + pr_err("Expected %08x found %s\n", PPC_INST_LD_TOC, ppc_inst_as_str(op)); return -EINVAL; } #endif /* CONFIG_MPROFILE_KERNEL */ @@ -231,7 +231,7 @@ __ftrace_make_nop(struct module *mod, /* Make sure that that this is still a 24bit jump */ if (!is_bl_op(op)) { - pr_err("Not expected bl: opcode is %x\n", ppc_inst_val(op)); + pr_err("Not expected bl: opcode is %s\n", ppc_inst_as_str(op)); return -EINVAL; } @@ -406,7 +406,7 @@ static int __ftrace_make_nop_kernel(struct dyn_ftrace *rec, unsigned long addr) /* Make sure that that this is still a 24bit jump */ if (!is_bl_op(op)) { - pr_err("Not expected bl: opcode is %x\n", ppc_inst_val(op)); + pr_err("Not expected bl: opcode is %s\n", ppc_inst_as_str(op)); return -EINVAL; } @@ -533,8 +533,8 @@ __ftrace_make_call(struct dyn_ftrace *rec, unsigned long addr) return -EFAULT; if (!expected_nop_sequence(ip, op[0], op[1])) { - pr_err("Unexpected call sequence at %p: %x %x\n", - ip, ppc_inst_val(op[0]), ppc_inst_val(op[1])); + pr_err("Unexpected call sequence at %p: %s %s\n", + ip, ppc_inst_as_str(op[0]), ppc_inst_as_str(op[1])); return -EINVAL; } @@ -597,7 +597,7 @@ __ftrace_make_call(struct dyn_ftrace *rec, unsigned long addr) /* It should be pointing to a nop */ if (!ppc_inst_equal(op, ppc_inst(PPC_INST_NOP))) { - pr_err("Expected NOP but have %x\n", ppc_inst_val(op)); + pr_err("Expected NOP but have %s\n", ppc_inst_as_str(op)); return -EINVAL; } @@ -654,7 +654,7 @@ static int __ftrace_make_call_kernel(struct dyn_ftrace *rec, unsigned long addr) } if (!ppc_inst_equal(op, ppc_inst(PPC_INST_NOP))) { - pr_err("Unexpected call sequence at %p: %x\n", ip, ppc_inst_val(op)); + pr_err("Unexpected call sequence at %p: %s\n", ip, ppc_inst_as_str(op)); return -EINVAL; } @@ -733,7 +733,7 @@ __ftrace_modify_call(struct dyn_ftrace *rec, unsigned long old_addr, /* Make sure that that this is still a 24bit jump */ if (!is_bl_op(op)) { - pr_err("Not expected bl: opcode is %x\n", ppc_inst_val(op)); + pr_err("Not expected bl: opcode is %s\n", ppc_inst_as_str(op)); return -EINVAL; } diff --git a/arch/powerpc/lib/test_emulate_step.c b/arch/powerpc/lib/test_emulate_step.c index c9a1a343123d1..e25e92750416d 100644 --- a/arch/powerpc/lib/test_emulate_step.c +++ b/arch/powerpc/lib/test_emulate_step.c @@ -1147,7 +1147,7 @@ static int __init emulate_compute_instr(struct pt_regs *regs, if (analyse_instr(&op, regs, instr) != 1 || GETTYPE(op.type) != COMPUTE) { - pr_info("emulation failed, instruction = 0x%08x\n", ppc_inst_val(instr)); + pr_info("execution failed, instruction = %s\n", ppc_inst_as_str(instr)); return -EFAULT; } @@ -1167,7 +1167,7 @@ static int __init execute_compute_instr(struct pt_regs *regs, /* Patch the NOP with the actual instruction */ patch_instruction_site(&patch__exec_instr, instr); if (exec_instr(regs)) { - pr_info("execution failed, instruction = 0x%08x\n", ppc_inst_val(instr)); + pr_info("execution failed, instruction = %s\n", ppc_inst_as_str(instr)); return -EFAULT; } diff --git a/arch/powerpc/xmon/xmon.c b/arch/powerpc/xmon/xmon.c index f08b2ef1995a1..8df83a744528d 100644 --- a/arch/powerpc/xmon/xmon.c +++ b/arch/powerpc/xmon/xmon.c @@ -2977,7 +2977,7 @@ generic_inst_dump(unsigned long adr, long count, int praddr, dotted = 0; last_inst = inst; if (praddr) - printf(REG" %.8x", adr, ppc_inst_val(inst)); + printf(REG" %s", adr, ppc_inst_as_str(inst)); printf("\t"); dump_func(ppc_inst_val(inst), adr); printf("\n"); -- GitLab From 8b98afc117aaf825c66d7ddd59f1849e559b42cd Mon Sep 17 00:00:00 2001 From: Jordan Niethe Date: Tue, 2 Jun 2020 15:27:26 +1000 Subject: [PATCH 0779/1476] powerpc/xmon: Improve dumping prefixed instructions Currently prefixed instructions are dumped as two separate word instructions. Use mread_instr() so that prefixed instructions are read as such and update the incrementor in the loop to take this into account. 'dump_func' is print_insn_powerpc() which comes from ppc-dis.c which is taken from binutils. When this is updated prefixed instructions will be disassembled. Currently dumping prefixed instructions looks like this: 0:mon> di c000000000094168 c000000000094168 0x06000000 .long 0x6000000 c00000000009416c 0x392a0003 addi r9,r10,3 c000000000094170 0x913f0028 stw r9,40(r31) c000000000094174 0xe93f002a lwa r9,40(r31) c000000000094178 0x7d234b78 mr r3,r9 c00000000009417c 0x383f0040 addi r1,r31,64 c000000000094180 0xebe1fff8 ld r31,-8(r1) c000000000094184 0x4e800020 blr c000000000094188 0x60000000 nop ... c000000000094190 0x3c4c0121 addis r2,r12,289 c000000000094194 0x38429670 addi r2,r2,-27024 c000000000094198 0x7c0802a6 mflr r0 c00000000009419c 0x60000000 nop c0000000000941a0 0xe9240100 ld r9,256(r4) c0000000000941a4 0x39400001 li r10,1 After this it looks like: 0:mon> di c000000000094168 c000000000094168 0x06000000 0x392a0003 .long 0x392a000306000000 c000000000094170 0x913f0028 stw r9,40(r31) c000000000094174 0xe93f002a lwa r9,40(r31) c000000000094178 0x7d234b78 mr r3,r9 c00000000009417c 0x383f0040 addi r1,r31,64 c000000000094180 0xebe1fff8 ld r31,-8(r1) c000000000094184 0x4e800020 blr c000000000094188 0x60000000 nop ... c000000000094190 0x3c4c0121 addis r2,r12,289 c000000000094194 0x38429570 addi r2,r2,-27280 c000000000094198 0x7c0802a6 mflr r0 c00000000009419c 0x60000000 nop c0000000000941a0 0xe9240100 ld r9,256(r4) c0000000000941a4 0x39400001 li r10,1 c0000000000941a8 0x3d02000b addis r8,r2,11 Signed-off-by: Jordan Niethe Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20200602052728.18227-2-jniethe5@gmail.com --- arch/powerpc/xmon/xmon.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/arch/powerpc/xmon/xmon.c b/arch/powerpc/xmon/xmon.c index 8df83a744528d..1a240b4b76f97 100644 --- a/arch/powerpc/xmon/xmon.c +++ b/arch/powerpc/xmon/xmon.c @@ -2954,11 +2954,10 @@ generic_inst_dump(unsigned long adr, long count, int praddr, int nr, dotted; unsigned long first_adr; struct ppc_inst inst, last_inst = ppc_inst(0); - unsigned char val[4]; dotted = 0; - for (first_adr = adr; count > 0; --count, adr += 4) { - nr = mread(adr, val, 4); + for (first_adr = adr; count > 0; --count, adr += ppc_inst_len(inst)) { + nr = mread_instr(adr, &inst); if (nr == 0) { if (praddr) { const char *x = fault_chars[fault_type]; @@ -2966,7 +2965,6 @@ generic_inst_dump(unsigned long adr, long count, int praddr, } break; } - inst = ppc_inst(GETWORD(val)); if (adr > first_adr && ppc_inst_equal(inst, last_inst)) { if (!dotted) { printf(" ...\n"); @@ -2979,7 +2977,10 @@ generic_inst_dump(unsigned long adr, long count, int praddr, if (praddr) printf(REG" %s", adr, ppc_inst_as_str(inst)); printf("\t"); - dump_func(ppc_inst_val(inst), adr); + if (!ppc_inst_prefixed(inst)) + dump_func(ppc_inst_val(inst), adr); + else + dump_func(ppc_inst_as_u64(inst), adr); printf("\n"); } return adr - first_adr; -- GitLab From 93c3a0ba2a0863a5c82a518d64044434f82a57f5 Mon Sep 17 00:00:00 2001 From: Balamuruhan S Date: Fri, 26 Jun 2020 15:21:55 +0530 Subject: [PATCH 0780/1476] powerpc/test_emulate_step: Enhancement to test negative scenarios add provision to declare test is a negative scenario, verify whether emulation fails and avoid executing it. Signed-off-by: Balamuruhan S Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20200626095158.1031507-2-bala24@linux.ibm.com --- arch/powerpc/lib/test_emulate_step.c | 30 +++++++++++++++++++--------- 1 file changed, 21 insertions(+), 9 deletions(-) diff --git a/arch/powerpc/lib/test_emulate_step.c b/arch/powerpc/lib/test_emulate_step.c index e25e92750416d..31065e986ff0c 100644 --- a/arch/powerpc/lib/test_emulate_step.c +++ b/arch/powerpc/lib/test_emulate_step.c @@ -18,6 +18,7 @@ #define IGNORE_GPR(n) (0x1UL << (n)) #define IGNORE_XER (0x1UL << 32) #define IGNORE_CCR (0x1UL << 33) +#define NEGATIVE_TEST (0x1UL << 63) #define TEST_PLD(r, base, i, pr) \ ppc_inst_prefix(PPC_PREFIX_8LS | __PPC_PRFX_R(pr) | IMM_H(i), \ @@ -1135,8 +1136,10 @@ static struct compute_test compute_tests[] = { }; static int __init emulate_compute_instr(struct pt_regs *regs, - struct ppc_inst instr) + struct ppc_inst instr, + bool negative) { + int analysed; extern s32 patch__exec_instr; struct instruction_op op; @@ -1145,13 +1148,17 @@ static int __init emulate_compute_instr(struct pt_regs *regs, regs->nip = patch_site_addr(&patch__exec_instr); - if (analyse_instr(&op, regs, instr) != 1 || - GETTYPE(op.type) != COMPUTE) { - pr_info("execution failed, instruction = %s\n", ppc_inst_as_str(instr)); + analysed = analyse_instr(&op, regs, instr); + if (analysed != 1 || GETTYPE(op.type) != COMPUTE) { + if (negative) + return -EFAULT; + pr_info("emulation failed, instruction = %s\n", ppc_inst_as_str(instr)); return -EFAULT; } - - emulate_update_regs(regs, &op); + if (analysed == 1 && negative) + pr_info("negative test failed, instruction = %s\n", ppc_inst_as_str(instr)); + if (!negative) + emulate_update_regs(regs, &op); return 0; } @@ -1189,7 +1196,7 @@ static void __init run_tests_compute(void) struct pt_regs *regs, exp, got; unsigned int i, j, k; struct ppc_inst instr; - bool ignore_gpr, ignore_xer, ignore_ccr, passed; + bool ignore_gpr, ignore_xer, ignore_ccr, passed, rc, negative; for (i = 0; i < ARRAY_SIZE(compute_tests); i++) { test = &compute_tests[i]; @@ -1203,6 +1210,7 @@ static void __init run_tests_compute(void) instr = test->subtests[j].instr; flags = test->subtests[j].flags; regs = &test->subtests[j].regs; + negative = flags & NEGATIVE_TEST; ignore_xer = flags & IGNORE_XER; ignore_ccr = flags & IGNORE_CCR; passed = true; @@ -1217,8 +1225,12 @@ static void __init run_tests_compute(void) exp.msr = MSR_KERNEL; got.msr = MSR_KERNEL; - if (emulate_compute_instr(&got, instr) || - execute_compute_instr(&exp, instr)) { + rc = emulate_compute_instr(&got, instr, negative) != 0; + if (negative) { + /* skip executing instruction */ + passed = rc; + goto print; + } else if (rc || execute_compute_instr(&exp, instr)) { passed = false; goto print; } -- GitLab From 7e67c73b939b25d4ad18a536e52282aa35d8ee56 Mon Sep 17 00:00:00 2001 From: Balamuruhan S Date: Fri, 26 Jun 2020 15:21:56 +0530 Subject: [PATCH 0781/1476] powerpc/test_emulate_step: Add negative tests for prefixed addi testcases for `paddi` instruction to cover the negative case, if R is equal to 1 and RA is not equal to 0, the instruction form is invalid. Signed-off-by: Balamuruhan S Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20200626095158.1031507-3-bala24@linux.ibm.com --- arch/powerpc/lib/test_emulate_step.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/arch/powerpc/lib/test_emulate_step.c b/arch/powerpc/lib/test_emulate_step.c index 31065e986ff0c..7d11ab5ef86ca 100644 --- a/arch/powerpc/lib/test_emulate_step.c +++ b/arch/powerpc/lib/test_emulate_step.c @@ -1130,6 +1130,16 @@ static struct compute_test compute_tests[] = { .regs = { .gpr[21] = 0, } + }, + /* Invalid instruction form with R = 1 and RA != 0 */ + { + .descr = "RA = R22(0), SI = 0, R = 1", + .instr = TEST_PADDI(21, 22, 0, 1), + .flags = NEGATIVE_TEST, + .regs = { + .gpr[21] = 0, + .gpr[22] = 0, + } } } } -- GitLab From 68a180a44c29d7e918ae7d3c18a01b0751d1c22f Mon Sep 17 00:00:00 2001 From: Balamuruhan S Date: Fri, 26 Jun 2020 15:21:57 +0530 Subject: [PATCH 0782/1476] powerpc/sstep: Introduce macros to retrieve Prefix instruction operands retrieve prefix instruction operands RA and pc relative bit R values using macros and adopt it in sstep.c and test_emulate_step.c. Signed-off-by: Balamuruhan S Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20200626095158.1031507-4-bala24@linux.ibm.com --- arch/powerpc/include/asm/sstep.h | 4 ++++ arch/powerpc/lib/sstep.c | 12 ++++++------ 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/arch/powerpc/include/asm/sstep.h b/arch/powerpc/include/asm/sstep.h index 3b01c69a44aa4..325975b4ef307 100644 --- a/arch/powerpc/include/asm/sstep.h +++ b/arch/powerpc/include/asm/sstep.h @@ -104,6 +104,10 @@ enum instruction_type { #define MKOP(t, f, s) ((t) | (f) | SIZE(s)) +/* Prefix instruction operands */ +#define GET_PREFIX_RA(i) (((i) >> 16) & 0x1f) +#define GET_PREFIX_R(i) ((i) & (1ul << 20)) + struct instruction_op { int type; int reg; diff --git a/arch/powerpc/lib/sstep.c b/arch/powerpc/lib/sstep.c index 5abe98216dc22..fb4c5767663d1 100644 --- a/arch/powerpc/lib/sstep.c +++ b/arch/powerpc/lib/sstep.c @@ -200,8 +200,8 @@ static nokprobe_inline unsigned long mlsd_8lsd_ea(unsigned int instr, unsigned int dd; unsigned long ea, d0, d1, d; - prefix_r = instr & (1ul << 20); - ra = (suffix >> 16) & 0x1f; + prefix_r = GET_PREFIX_R(instr); + ra = GET_PREFIX_RA(suffix); d0 = instr & 0x3ffff; d1 = suffix & 0xffff; @@ -1339,8 +1339,8 @@ int analyse_instr(struct instruction_op *op, const struct pt_regs *regs, switch (opcode) { #ifdef __powerpc64__ case 1: - prefix_r = word & (1ul << 20); - ra = (suffix >> 16) & 0x1f; + prefix_r = GET_PREFIX_R(word); + ra = GET_PREFIX_RA(suffix); rd = (suffix >> 21) & 0x1f; op->reg = rd; op->val = regs->gpr[rd]; @@ -2715,8 +2715,8 @@ int analyse_instr(struct instruction_op *op, const struct pt_regs *regs, } break; case 1: /* Prefixed instructions */ - prefix_r = word & (1ul << 20); - ra = (suffix >> 16) & 0x1f; + prefix_r = GET_PREFIX_R(word); + ra = GET_PREFIX_RA(suffix); op->update_reg = ra; rd = (suffix >> 21) & 0x1f; op->reg = rd; -- GitLab From e93ad65e3611b06288efdf0cfd76c012df3feec1 Mon Sep 17 00:00:00 2001 From: Balamuruhan S Date: Fri, 26 Jun 2020 15:21:58 +0530 Subject: [PATCH 0783/1476] powerpc/test_emulate_step: Move extern declaration to sstep.h fix checkpatch.pl warnings by moving extern declaration from source file to headerfile. Signed-off-by: Balamuruhan S Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20200626095158.1031507-5-bala24@linux.ibm.com --- arch/powerpc/include/asm/sstep.h | 2 ++ arch/powerpc/lib/test_emulate_step.c | 2 -- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/powerpc/include/asm/sstep.h b/arch/powerpc/include/asm/sstep.h index 325975b4ef307..c8e37ef060c14 100644 --- a/arch/powerpc/include/asm/sstep.h +++ b/arch/powerpc/include/asm/sstep.h @@ -108,6 +108,8 @@ enum instruction_type { #define GET_PREFIX_RA(i) (((i) >> 16) & 0x1f) #define GET_PREFIX_R(i) ((i) & (1ul << 20)) +extern s32 patch__exec_instr; + struct instruction_op { int type; int reg; diff --git a/arch/powerpc/lib/test_emulate_step.c b/arch/powerpc/lib/test_emulate_step.c index 7d11ab5ef86ca..081b05480c477 100644 --- a/arch/powerpc/lib/test_emulate_step.c +++ b/arch/powerpc/lib/test_emulate_step.c @@ -1150,7 +1150,6 @@ static int __init emulate_compute_instr(struct pt_regs *regs, bool negative) { int analysed; - extern s32 patch__exec_instr; struct instruction_op op; if (!regs || !ppc_inst_val(instr)) @@ -1176,7 +1175,6 @@ static int __init execute_compute_instr(struct pt_regs *regs, struct ppc_inst instr) { extern int exec_instr(struct pt_regs *regs); - extern s32 patch__exec_instr; if (!regs || !ppc_inst_val(instr)) return -EINVAL; -- GitLab From 147c13413c04bc6a2bd76f2503402905e5e98cff Mon Sep 17 00:00:00 2001 From: Palmer Dabbelt Date: Thu, 16 Jul 2020 12:38:20 -0700 Subject: [PATCH 0784/1476] powerpc/64: Fix an out of date comment about MMIO ordering This primitive has been renamed, but because it was spelled incorrectly in the first place it must have escaped the fixup patch. As far as I can tell this logic is still correct: smp_mb__after_spinlock() uses the default smp_mb() implementation, which is "sync" rather than "hwsync" but those are the same (though I'm not that familiar with PowerPC). Signed-off-by: Palmer Dabbelt Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20200716193820.1141936-1-palmer@dabbelt.com --- arch/powerpc/kernel/entry_64.S | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/powerpc/kernel/entry_64.S b/arch/powerpc/kernel/entry_64.S index da85c2511e57d..2547c5dac07ad 100644 --- a/arch/powerpc/kernel/entry_64.S +++ b/arch/powerpc/kernel/entry_64.S @@ -354,7 +354,7 @@ _GLOBAL(_switch) * kernel/sched/core.c). * * Uncacheable stores in the case of involuntary preemption must - * be taken care of. The smp_mb__before_spin_lock() in __schedule() + * be taken care of. The smp_mb__after_spinlock() in __schedule() * is implemented as hwsync on powerpc, which orders MMIO too. So * long as there is an hwsync in the context switch path, it will * be executed on the source CPU after the task has performed -- GitLab From 2384b36f9156c3b815a5ce5f694edc5054ab7625 Mon Sep 17 00:00:00 2001 From: Nicholas Piggin Date: Thu, 16 Jul 2020 11:35:22 +1000 Subject: [PATCH 0785/1476] powerpc: Select ARCH_HAS_MEMBARRIER_SYNC_CORE powerpc return from interrupt and return from system call sequences are context synchronising. Signed-off-by: Nicholas Piggin Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20200716013522.338318-1-npiggin@gmail.com --- .../features/sched/membarrier-sync-core/arch-support.txt | 4 ++-- arch/powerpc/Kconfig | 1 + arch/powerpc/include/asm/exception-64e.h | 6 +++++- arch/powerpc/include/asm/exception-64s.h | 8 ++++++++ arch/powerpc/kernel/entry_32.S | 6 ++++++ 5 files changed, 22 insertions(+), 3 deletions(-) diff --git a/Documentation/features/sched/membarrier-sync-core/arch-support.txt b/Documentation/features/sched/membarrier-sync-core/arch-support.txt index 8a521a622966a..52ad74a25f54a 100644 --- a/Documentation/features/sched/membarrier-sync-core/arch-support.txt +++ b/Documentation/features/sched/membarrier-sync-core/arch-support.txt @@ -5,7 +5,7 @@ # # Architecture requirements # -# * arm/arm64 +# * arm/arm64/powerpc # # Rely on implicit context synchronization as a result of exception return # when returning from IPI handler, and when returning to user-space. @@ -45,7 +45,7 @@ | nios2: | TODO | | openrisc: | TODO | | parisc: | TODO | - | powerpc: | TODO | + | powerpc: | ok | | riscv: | TODO | | s390: | TODO | | sh: | TODO | diff --git a/arch/powerpc/Kconfig b/arch/powerpc/Kconfig index 70adf69f8b1a5..81c0dee1cbffe 100644 --- a/arch/powerpc/Kconfig +++ b/arch/powerpc/Kconfig @@ -131,6 +131,7 @@ config PPC select ARCH_HAS_PTE_DEVMAP if PPC_BOOK3S_64 select ARCH_HAS_PTE_SPECIAL select ARCH_HAS_MEMBARRIER_CALLBACKS + select ARCH_HAS_MEMBARRIER_SYNC_CORE select ARCH_HAS_SCALED_CPUTIME if VIRT_CPU_ACCOUNTING_NATIVE && PPC_BOOK3S_64 select ARCH_HAS_STRICT_KERNEL_RWX if (PPC32 && !HIBERNATION) select ARCH_HAS_TICK_BROADCAST if GENERIC_CLOCKEVENTS_BROADCAST diff --git a/arch/powerpc/include/asm/exception-64e.h b/arch/powerpc/include/asm/exception-64e.h index 54a98ef7d7fe8..72b6657acd2de 100644 --- a/arch/powerpc/include/asm/exception-64e.h +++ b/arch/powerpc/include/asm/exception-64e.h @@ -204,7 +204,11 @@ exc_##label##_book3e: LOAD_REG_ADDR(r3,interrupt_base_book3e);\ ori r3,r3,vector_offset@l; \ mtspr SPRN_IVOR##vector_number,r3; - +/* + * powerpc relies on return from interrupt/syscall being context synchronising + * (which rfi is) to support ARCH_HAS_MEMBARRIER_SYNC_CORE without additional + * synchronisation instructions. + */ #define RFI_TO_KERNEL \ rfi diff --git a/arch/powerpc/include/asm/exception-64s.h b/arch/powerpc/include/asm/exception-64s.h index 47bd4ea0837d4..d7a1a427a6904 100644 --- a/arch/powerpc/include/asm/exception-64s.h +++ b/arch/powerpc/include/asm/exception-64s.h @@ -68,6 +68,14 @@ * * The nop instructions allow us to insert one or more instructions to flush the * L1-D cache when returning to userspace or a guest. + * + * powerpc relies on return from interrupt/syscall being context synchronising + * (which hrfid, rfid, and rfscv are) to support ARCH_HAS_MEMBARRIER_SYNC_CORE + * without additional synchronisation instructions. + * + * soft-masked interrupt replay does not include a context-synchronising rfid, + * but those always return to kernel, the sync is only required when returning + * to user. */ #define RFI_FLUSH_SLOT \ RFI_FLUSH_FIXUP_SECTION; \ diff --git a/arch/powerpc/kernel/entry_32.S b/arch/powerpc/kernel/entry_32.S index 217ebdf5b00b3..f4d0af8e1136a 100644 --- a/arch/powerpc/kernel/entry_32.S +++ b/arch/powerpc/kernel/entry_32.S @@ -35,6 +35,12 @@ #include "head_32.h" +/* + * powerpc relies on return from interrupt/syscall being context synchronising + * (which rfi is) to support ARCH_HAS_MEMBARRIER_SYNC_CORE without additional + * synchronisation instructions. + */ + /* * Align to 4k in order to ensure that all functions modyfing srr0/srr1 * fit into one page in order to not encounter a TLB miss between the -- GitLab From 5a090f7c363fdc09b99222eae679506a58e7cc68 Mon Sep 17 00:00:00 2001 From: Wen Xiong Date: Mon, 13 Jul 2020 09:39:33 -0500 Subject: [PATCH 0786/1476] powerpc/pseries: PCIE PHB reset Several device drivers hit EEH(Extended Error handling) when triggering kdump on Pseries PowerVM. This patch implemented a reset of the PHBs in pci general code when triggering kdump. PHB reset stop all PCI transactions from normal kernel. We have tested the patch in several enviroments: - direct slot adapters - adapters under the switch - a VF adapter in PowerVM - a VF adapter/adapter in KVM guest. Signed-off-by: Wen Xiong [mpe: Fix broken whitespace, subject & SOB formatting] Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/1594651173-32166-1-git-send-email-wenxiong@linux.vnet.ibm.com --- arch/powerpc/platforms/pseries/eeh_pseries.c | 232 ++++++++++++++----- 1 file changed, 169 insertions(+), 63 deletions(-) diff --git a/arch/powerpc/platforms/pseries/eeh_pseries.c b/arch/powerpc/platforms/pseries/eeh_pseries.c index ace117f99d940..47760fb515cc6 100644 --- a/arch/powerpc/platforms/pseries/eeh_pseries.c +++ b/arch/powerpc/platforms/pseries/eeh_pseries.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #include @@ -80,6 +81,152 @@ void pseries_pcibios_bus_add_device(struct pci_dev *pdev) eeh_probe_device(pdev); } + +/** + * pseries_eeh_get_config_addr - Retrieve config address + * + * Retrieve the assocated config address. Actually, there're 2 RTAS + * function calls dedicated for the purpose. We need implement + * it through the new function and then the old one. Besides, + * you should make sure the config address is figured out from + * FDT node before calling the function. + * + * It's notable that zero'ed return value means invalid PE config + * address. + */ +static int pseries_eeh_get_config_addr(struct pci_controller *phb, int config_addr) +{ + int ret = 0; + int rets[3]; + + if (ibm_get_config_addr_info2 != RTAS_UNKNOWN_SERVICE) { + /* + * First of all, we need to make sure there has one PE + * associated with the device. Otherwise, PE address is + * meaningless. + */ + ret = rtas_call(ibm_get_config_addr_info2, 4, 2, rets, + config_addr, BUID_HI(phb->buid), + BUID_LO(phb->buid), 1); + if (ret || (rets[0] == 0)) + return 0; + + /* Retrieve the associated PE config address */ + ret = rtas_call(ibm_get_config_addr_info2, 4, 2, rets, + config_addr, BUID_HI(phb->buid), + BUID_LO(phb->buid), 0); + if (ret) { + pr_warn("%s: Failed to get address for PHB#%x-PE#%x\n", + __func__, phb->global_number, config_addr); + return 0; + } + + return rets[0]; + } + + if (ibm_get_config_addr_info != RTAS_UNKNOWN_SERVICE) { + ret = rtas_call(ibm_get_config_addr_info, 4, 2, rets, + config_addr, BUID_HI(phb->buid), + BUID_LO(phb->buid), 0); + if (ret) { + pr_warn("%s: Failed to get address for PHB#%x-PE#%x\n", + __func__, phb->global_number, config_addr); + return 0; + } + + return rets[0]; + } + + return ret; +} + +/** + * pseries_eeh_phb_reset - Reset the specified PHB + * @phb: PCI controller + * @config_adddr: the associated config address + * @option: reset option + * + * Reset the specified PHB/PE + */ +static int pseries_eeh_phb_reset(struct pci_controller *phb, int config_addr, int option) +{ + int ret; + + /* Reset PE through RTAS call */ + ret = rtas_call(ibm_set_slot_reset, 4, 1, NULL, + config_addr, BUID_HI(phb->buid), + BUID_LO(phb->buid), option); + + /* If fundamental-reset not supported, try hot-reset */ + if (option == EEH_RESET_FUNDAMENTAL && + ret == -8) { + option = EEH_RESET_HOT; + ret = rtas_call(ibm_set_slot_reset, 4, 1, NULL, + config_addr, BUID_HI(phb->buid), + BUID_LO(phb->buid), option); + } + + /* We need reset hold or settlement delay */ + if (option == EEH_RESET_FUNDAMENTAL || + option == EEH_RESET_HOT) + msleep(EEH_PE_RST_HOLD_TIME); + else + msleep(EEH_PE_RST_SETTLE_TIME); + + return ret; +} + +/** + * pseries_eeh_phb_configure_bridge - Configure PCI bridges in the indicated PE + * @phb: PCI controller + * @config_adddr: the associated config address + * + * The function will be called to reconfigure the bridges included + * in the specified PE so that the mulfunctional PE would be recovered + * again. + */ +static int pseries_eeh_phb_configure_bridge(struct pci_controller *phb, int config_addr) +{ + int ret; + /* Waiting 0.2s maximum before skipping configuration */ + int max_wait = 200; + + while (max_wait > 0) { + ret = rtas_call(ibm_configure_pe, 3, 1, NULL, + config_addr, BUID_HI(phb->buid), + BUID_LO(phb->buid)); + + if (!ret) + return ret; + if (ret < 0) + break; + + /* + * If RTAS returns a delay value that's above 100ms, cut it + * down to 100ms in case firmware made a mistake. For more + * on how these delay values work see rtas_busy_delay_time + */ + if (ret > RTAS_EXTENDED_DELAY_MIN+2 && + ret <= RTAS_EXTENDED_DELAY_MAX) + ret = RTAS_EXTENDED_DELAY_MIN+2; + + max_wait -= rtas_busy_delay_time(ret); + + if (max_wait < 0) + break; + + rtas_busy_delay(ret); + } + + pr_warn("%s: Unable to configure bridge PHB#%x-PE#%x (%d)\n", + __func__, phb->global_number, config_addr, ret); + /* PAPR defines -3 as "Parameter Error" for this function: */ + if (ret == -3) + return -EINVAL; + else + return -EIO; +} + /* * Buffer for reporting slot-error-detail rtas calls. Its here * in BSS, and not dynamically alloced, so that it ends up in @@ -96,6 +243,10 @@ static int eeh_error_buf_size; */ static int pseries_eeh_init(void) { + struct pci_controller *phb; + struct pci_dn *pdn; + int addr, config_addr; + /* figure out EEH RTAS function call tokens */ ibm_set_eeh_option = rtas_token("ibm,set-eeh-option"); ibm_set_slot_reset = rtas_token("ibm,set-slot-reset"); @@ -148,6 +299,22 @@ static int pseries_eeh_init(void) /* Set EEH machine dependent code */ ppc_md.pcibios_bus_add_device = pseries_pcibios_bus_add_device; + if (is_kdump_kernel() || reset_devices) { + pr_info("Issue PHB reset ...\n"); + list_for_each_entry(phb, &hose_list, list_node) { + pdn = list_first_entry(&PCI_DN(phb->dn)->child_list, struct pci_dn, list); + addr = (pdn->busno << 16) | (pdn->devfn << 8); + config_addr = pseries_eeh_get_config_addr(phb, addr); + /* invalid PE config addr */ + if (config_addr == 0) + continue; + + pseries_eeh_phb_reset(phb, config_addr, EEH_RESET_FUNDAMENTAL); + pseries_eeh_phb_reset(phb, config_addr, EEH_RESET_DEACTIVATE); + pseries_eeh_phb_configure_bridge(phb, config_addr); + } + } + return 0; } @@ -569,35 +736,13 @@ static int pseries_eeh_get_state(struct eeh_pe *pe, int *delay) static int pseries_eeh_reset(struct eeh_pe *pe, int option) { int config_addr; - int ret; /* Figure out PE address */ config_addr = pe->config_addr; if (pe->addr) config_addr = pe->addr; - /* Reset PE through RTAS call */ - ret = rtas_call(ibm_set_slot_reset, 4, 1, NULL, - config_addr, BUID_HI(pe->phb->buid), - BUID_LO(pe->phb->buid), option); - - /* If fundamental-reset not supported, try hot-reset */ - if (option == EEH_RESET_FUNDAMENTAL && - ret == -8) { - option = EEH_RESET_HOT; - ret = rtas_call(ibm_set_slot_reset, 4, 1, NULL, - config_addr, BUID_HI(pe->phb->buid), - BUID_LO(pe->phb->buid), option); - } - - /* We need reset hold or settlement delay */ - if (option == EEH_RESET_FUNDAMENTAL || - option == EEH_RESET_HOT) - msleep(EEH_PE_RST_HOLD_TIME); - else - msleep(EEH_PE_RST_SETTLE_TIME); - - return ret; + return pseries_eeh_phb_reset(pe->phb, config_addr, option); } /** @@ -641,56 +786,17 @@ static int pseries_eeh_get_log(struct eeh_pe *pe, int severity, char *drv_log, u * pseries_eeh_configure_bridge - Configure PCI bridges in the indicated PE * @pe: EEH PE * - * The function will be called to reconfigure the bridges included - * in the specified PE so that the mulfunctional PE would be recovered - * again. */ static int pseries_eeh_configure_bridge(struct eeh_pe *pe) { int config_addr; - int ret; - /* Waiting 0.2s maximum before skipping configuration */ - int max_wait = 200; /* Figure out the PE address */ config_addr = pe->config_addr; if (pe->addr) config_addr = pe->addr; - while (max_wait > 0) { - ret = rtas_call(ibm_configure_pe, 3, 1, NULL, - config_addr, BUID_HI(pe->phb->buid), - BUID_LO(pe->phb->buid)); - - if (!ret) - return ret; - if (ret < 0) - break; - - /* - * If RTAS returns a delay value that's above 100ms, cut it - * down to 100ms in case firmware made a mistake. For more - * on how these delay values work see rtas_busy_delay_time - */ - if (ret > RTAS_EXTENDED_DELAY_MIN+2 && - ret <= RTAS_EXTENDED_DELAY_MAX) - ret = RTAS_EXTENDED_DELAY_MIN+2; - - max_wait -= rtas_busy_delay_time(ret); - - if (max_wait < 0) - break; - - rtas_busy_delay(ret); - } - - pr_warn("%s: Unable to configure bridge PHB#%x-PE#%x (%d)\n", - __func__, pe->phb->global_number, pe->addr, ret); - /* PAPR defines -3 as "Parameter Error" for this function: */ - if (ret == -3) - return -EINVAL; - else - return -EIO; + return pseries_eeh_phb_configure_bridge(pe->phb, config_addr); } /** -- GitLab From 201220bb0e8cbc163ec7f550b3b7b3da46eb5877 Mon Sep 17 00:00:00 2001 From: Nicholas Piggin Date: Fri, 3 Jul 2020 09:33:43 +1000 Subject: [PATCH 0787/1476] powerpc/powernv: Machine check handler for POWER10 Signed-off-by: Nicholas Piggin Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20200702233343.1128026-1-npiggin@gmail.com --- arch/powerpc/include/asm/mce.h | 1 + arch/powerpc/kernel/dt_cpu_ftrs.c | 10 ++++ arch/powerpc/kernel/mce.c | 1 + arch/powerpc/kernel/mce_power.c | 84 +++++++++++++++++++++++++++++++ 4 files changed, 96 insertions(+) diff --git a/arch/powerpc/include/asm/mce.h b/arch/powerpc/include/asm/mce.h index 7bdd0cd4f2de0..adf2cda67f9a4 100644 --- a/arch/powerpc/include/asm/mce.h +++ b/arch/powerpc/include/asm/mce.h @@ -86,6 +86,7 @@ enum MCE_TlbErrorType { enum MCE_UserErrorType { MCE_USER_ERROR_INDETERMINATE = 0, MCE_USER_ERROR_TLBIE = 1, + MCE_USER_ERROR_SCV = 2, }; enum MCE_RaErrorType { diff --git a/arch/powerpc/kernel/dt_cpu_ftrs.c b/arch/powerpc/kernel/dt_cpu_ftrs.c index 0d65d95977780..0a16bb6fa07d0 100644 --- a/arch/powerpc/kernel/dt_cpu_ftrs.c +++ b/arch/powerpc/kernel/dt_cpu_ftrs.c @@ -66,6 +66,7 @@ struct dt_cpu_feature { extern long __machine_check_early_realmode_p8(struct pt_regs *regs); extern long __machine_check_early_realmode_p9(struct pt_regs *regs); +extern long __machine_check_early_realmode_p10(struct pt_regs *regs); static int hv_mode; @@ -475,6 +476,14 @@ static int __init feat_enable_pmu_power10(struct dt_cpu_feature *f) return 1; } +static int __init feat_enable_mce_power10(struct dt_cpu_feature *f) +{ + cur_cpu_spec->platform = "power10"; + cur_cpu_spec->machine_check_early = __machine_check_early_realmode_p10; + + return 1; +} + static int __init feat_enable_tm(struct dt_cpu_feature *f) { #ifdef CONFIG_PPC_TRANSACTIONAL_MEM @@ -663,6 +672,7 @@ static struct dt_cpu_feature_match __initdata {"group-start-register", feat_enable, 0}, {"pc-relative-addressing", feat_enable, 0}, {"machine-check-power9", feat_enable_mce_power9, 0}, + {"machine-check-power10", feat_enable_mce_power10, 0}, {"performance-monitor-power9", feat_enable_pmu_power9, 0}, {"performance-monitor-power10", feat_enable_pmu_power10, 0}, {"event-based-branch-v3", feat_enable, 0}, diff --git a/arch/powerpc/kernel/mce.c b/arch/powerpc/kernel/mce.c index b7b3ed4e61937..c2fa8ee63720e 100644 --- a/arch/powerpc/kernel/mce.c +++ b/arch/powerpc/kernel/mce.c @@ -385,6 +385,7 @@ void machine_check_print_event_info(struct machine_check_event *evt, static const char *mc_user_types[] = { "Indeterminate", "tlbie(l) invalid", + "scv invalid", }; static const char *mc_ra_types[] = { "Indeterminate", diff --git a/arch/powerpc/kernel/mce_power.c b/arch/powerpc/kernel/mce_power.c index c3b522bff9b4b..b7e173754a2e9 100644 --- a/arch/powerpc/kernel/mce_power.c +++ b/arch/powerpc/kernel/mce_power.c @@ -243,6 +243,45 @@ static const struct mce_ierror_table mce_p9_ierror_table[] = { MCE_INITIATOR_CPU, MCE_SEV_SEVERE, true }, { 0, 0, 0, 0, 0, 0, 0 } }; +static const struct mce_ierror_table mce_p10_ierror_table[] = { +{ 0x00000000081c0000, 0x0000000000040000, true, + MCE_ERROR_TYPE_UE, MCE_UE_ERROR_IFETCH, MCE_ECLASS_HARDWARE, + MCE_INITIATOR_CPU, MCE_SEV_SEVERE, true }, +{ 0x00000000081c0000, 0x0000000000080000, true, + MCE_ERROR_TYPE_SLB, MCE_SLB_ERROR_PARITY, MCE_ECLASS_HARD_INDETERMINATE, + MCE_INITIATOR_CPU, MCE_SEV_SEVERE, true }, +{ 0x00000000081c0000, 0x00000000000c0000, true, + MCE_ERROR_TYPE_SLB, MCE_SLB_ERROR_MULTIHIT, MCE_ECLASS_SOFT_INDETERMINATE, + MCE_INITIATOR_CPU, MCE_SEV_WARNING, true }, +{ 0x00000000081c0000, 0x0000000000100000, true, + MCE_ERROR_TYPE_ERAT, MCE_ERAT_ERROR_MULTIHIT, MCE_ECLASS_SOFT_INDETERMINATE, + MCE_INITIATOR_CPU, MCE_SEV_WARNING, true }, +{ 0x00000000081c0000, 0x0000000000140000, true, + MCE_ERROR_TYPE_TLB, MCE_TLB_ERROR_MULTIHIT, MCE_ECLASS_SOFT_INDETERMINATE, + MCE_INITIATOR_CPU, MCE_SEV_WARNING, true }, +{ 0x00000000081c0000, 0x0000000000180000, true, + MCE_ERROR_TYPE_UE, MCE_UE_ERROR_PAGE_TABLE_WALK_IFETCH, MCE_ECLASS_HARDWARE, + MCE_INITIATOR_CPU, MCE_SEV_SEVERE, true }, +{ 0x00000000081c0000, 0x00000000001c0000, true, + MCE_ERROR_TYPE_RA, MCE_RA_ERROR_IFETCH_FOREIGN, MCE_ECLASS_SOFTWARE, + MCE_INITIATOR_CPU, MCE_SEV_SEVERE, true }, +{ 0x00000000081c0000, 0x0000000008080000, true, + MCE_ERROR_TYPE_USER,MCE_USER_ERROR_SCV, MCE_ECLASS_SOFTWARE, + MCE_INITIATOR_CPU, MCE_SEV_WARNING, true }, +{ 0x00000000081c0000, 0x00000000080c0000, true, + MCE_ERROR_TYPE_RA, MCE_RA_ERROR_IFETCH, MCE_ECLASS_SOFTWARE, + MCE_INITIATOR_CPU, MCE_SEV_SEVERE, true }, +{ 0x00000000081c0000, 0x0000000008100000, true, + MCE_ERROR_TYPE_RA, MCE_RA_ERROR_PAGE_TABLE_WALK_IFETCH, MCE_ECLASS_SOFTWARE, + MCE_INITIATOR_CPU, MCE_SEV_SEVERE, true }, +{ 0x00000000081c0000, 0x0000000008140000, false, + MCE_ERROR_TYPE_RA, MCE_RA_ERROR_STORE, MCE_ECLASS_HARDWARE, + MCE_INITIATOR_CPU, MCE_SEV_FATAL, false }, /* ASYNC is fatal */ +{ 0x00000000081c0000, 0x00000000081c0000, true, MCE_ECLASS_HARDWARE, + MCE_ERROR_TYPE_RA, MCE_RA_ERROR_PAGE_TABLE_WALK_IFETCH_FOREIGN, + MCE_INITIATOR_CPU, MCE_SEV_SEVERE, true }, +{ 0, 0, 0, 0, 0, 0, 0 } }; + struct mce_derror_table { unsigned long dsisr_value; bool dar_valid; /* dar is a valid indicator of faulting address */ @@ -361,6 +400,46 @@ static const struct mce_derror_table mce_p9_derror_table[] = { MCE_INITIATOR_CPU, MCE_SEV_SEVERE, true }, { 0, false, 0, 0, 0, 0, 0 } }; +static const struct mce_derror_table mce_p10_derror_table[] = { +{ 0x00008000, false, + MCE_ERROR_TYPE_UE, MCE_UE_ERROR_LOAD_STORE, MCE_ECLASS_HARDWARE, + MCE_INITIATOR_CPU, MCE_SEV_SEVERE, true }, +{ 0x00004000, true, + MCE_ERROR_TYPE_UE, MCE_UE_ERROR_PAGE_TABLE_WALK_LOAD_STORE, + MCE_ECLASS_HARDWARE, + MCE_INITIATOR_CPU, MCE_SEV_SEVERE, true }, +{ 0x00000800, true, + MCE_ERROR_TYPE_ERAT, MCE_ERAT_ERROR_MULTIHIT, MCE_ECLASS_SOFT_INDETERMINATE, + MCE_INITIATOR_CPU, MCE_SEV_WARNING, true }, +{ 0x00000400, true, + MCE_ERROR_TYPE_TLB, MCE_TLB_ERROR_MULTIHIT, MCE_ECLASS_SOFT_INDETERMINATE, + MCE_INITIATOR_CPU, MCE_SEV_WARNING, true }, +{ 0x00000200, false, + MCE_ERROR_TYPE_USER, MCE_USER_ERROR_TLBIE, MCE_ECLASS_SOFTWARE, + MCE_INITIATOR_CPU, MCE_SEV_WARNING, true }, +{ 0x00000080, true, + MCE_ERROR_TYPE_SLB, MCE_SLB_ERROR_MULTIHIT, /* Before PARITY */ + MCE_ECLASS_SOFT_INDETERMINATE, + MCE_INITIATOR_CPU, MCE_SEV_WARNING, true }, +{ 0x00000100, true, + MCE_ERROR_TYPE_SLB, MCE_SLB_ERROR_PARITY, MCE_ECLASS_HARD_INDETERMINATE, + MCE_INITIATOR_CPU, MCE_SEV_SEVERE, true }, +{ 0x00000040, true, + MCE_ERROR_TYPE_RA, MCE_RA_ERROR_LOAD, MCE_ECLASS_HARDWARE, + MCE_INITIATOR_CPU, MCE_SEV_SEVERE, true }, +{ 0x00000020, false, + MCE_ERROR_TYPE_RA, MCE_RA_ERROR_PAGE_TABLE_WALK_LOAD_STORE, + MCE_ECLASS_HARDWARE, + MCE_INITIATOR_CPU, MCE_SEV_SEVERE, true }, +{ 0x00000010, false, + MCE_ERROR_TYPE_RA, MCE_RA_ERROR_PAGE_TABLE_WALK_LOAD_STORE_FOREIGN, + MCE_ECLASS_HARDWARE, + MCE_INITIATOR_CPU, MCE_SEV_SEVERE, true }, +{ 0x00000008, false, + MCE_ERROR_TYPE_RA, MCE_RA_ERROR_LOAD_STORE_FOREIGN, MCE_ECLASS_HARDWARE, + MCE_INITIATOR_CPU, MCE_SEV_SEVERE, true }, +{ 0, false, 0, 0, 0, 0, 0 } }; + static int mce_find_instr_ea_and_phys(struct pt_regs *regs, uint64_t *addr, uint64_t *phys_addr) { @@ -657,3 +736,8 @@ long __machine_check_early_realmode_p9(struct pt_regs *regs) return mce_handle_error(regs, mce_p9_derror_table, mce_p9_ierror_table); } + +long __machine_check_early_realmode_p10(struct pt_regs *regs) +{ + return mce_handle_error(regs, mce_p10_derror_table, mce_p10_ierror_table); +} -- GitLab From 7c7ff885c7bce40a487e41c68f1dac14dd2c8033 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Wed, 10 Jun 2020 10:55:54 +0200 Subject: [PATCH 0788/1476] powerpc/spufs: Fix the type of ret in spufs_arch_write_note Both the ->dump method and snprintf return an int. So switch to an int and properly handle errors from ->dump. Fixes: 5456ffdee666 ("powerpc/spufs: simplify spufs core dumping") Reported-by: kbuild test robot Signed-off-by: Christoph Hellwig Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20200610085554.5647-1-hch@lst.de --- arch/powerpc/platforms/cell/spufs/coredump.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/powerpc/platforms/cell/spufs/coredump.c b/arch/powerpc/platforms/cell/spufs/coredump.c index 40dd7b483f06b..026c181a98c5d 100644 --- a/arch/powerpc/platforms/cell/spufs/coredump.c +++ b/arch/powerpc/platforms/cell/spufs/coredump.c @@ -118,7 +118,7 @@ static int spufs_arch_write_note(struct spu_context *ctx, int i, size_t sz = spufs_coredump_read[i].size; char fullname[80]; struct elf_note en; - size_t ret; + int ret; sprintf(fullname, "SPU/%d/%s", dfd, spufs_coredump_read[i].name); en.n_namesz = strlen(fullname) + 1; -- GitLab From 0f10228c6ff6af36cbb31af35b01f76cdb0b3fc1 Mon Sep 17 00:00:00 2001 From: Leonardo Bras Date: Mon, 6 Jul 2020 21:48:12 -0300 Subject: [PATCH 0789/1476] KVM: PPC: Fix typo on H_DISABLE_AND_GET hcall On PAPR+ the hcall() on 0x1B0 is called H_DISABLE_AND_GET, but got defined as H_DISABLE_AND_GETC instead. This define was introduced with a typo in commit ("[PATCH] powerpc: Extends HCALL interface for InfiniBand usage"), and was later used without having the typo noticed. Signed-off-by: Leonardo Bras Acked-by: Paul Mackerras Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20200707004812.190765-1-leobras.c@gmail.com --- arch/powerpc/include/asm/hvcall.h | 2 +- arch/powerpc/kvm/trace_hv.h | 2 +- tools/perf/arch/powerpc/util/book3s_hcalls.h | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/arch/powerpc/include/asm/hvcall.h b/arch/powerpc/include/asm/hvcall.h index 43486e773bd6b..8d7ee3b533c41 100644 --- a/arch/powerpc/include/asm/hvcall.h +++ b/arch/powerpc/include/asm/hvcall.h @@ -237,7 +237,7 @@ #define H_CREATE_RPT 0x1A4 #define H_REMOVE_RPT 0x1A8 #define H_REGISTER_RPAGES 0x1AC -#define H_DISABLE_AND_GETC 0x1B0 +#define H_DISABLE_AND_GET 0x1B0 #define H_ERROR_DATA 0x1B4 #define H_GET_HCA_INFO 0x1B8 #define H_GET_PERF_COUNT 0x1BC diff --git a/arch/powerpc/kvm/trace_hv.h b/arch/powerpc/kvm/trace_hv.h index 4a61a971c34e1..830a126e095d8 100644 --- a/arch/powerpc/kvm/trace_hv.h +++ b/arch/powerpc/kvm/trace_hv.h @@ -89,7 +89,7 @@ {H_CREATE_RPT, "H_CREATE_RPT"}, \ {H_REMOVE_RPT, "H_REMOVE_RPT"}, \ {H_REGISTER_RPAGES, "H_REGISTER_RPAGES"}, \ - {H_DISABLE_AND_GETC, "H_DISABLE_AND_GETC"}, \ + {H_DISABLE_AND_GET, "H_DISABLE_AND_GET"}, \ {H_ERROR_DATA, "H_ERROR_DATA"}, \ {H_GET_HCA_INFO, "H_GET_HCA_INFO"}, \ {H_GET_PERF_COUNT, "H_GET_PERF_COUNT"}, \ diff --git a/tools/perf/arch/powerpc/util/book3s_hcalls.h b/tools/perf/arch/powerpc/util/book3s_hcalls.h index 54cfa0530e86d..488f4339b83c7 100644 --- a/tools/perf/arch/powerpc/util/book3s_hcalls.h +++ b/tools/perf/arch/powerpc/util/book3s_hcalls.h @@ -84,7 +84,7 @@ {0x1a4, "H_CREATE_RPT"}, \ {0x1a8, "H_REMOVE_RPT"}, \ {0x1ac, "H_REGISTER_RPAGES"}, \ - {0x1b0, "H_DISABLE_AND_GETC"}, \ + {0x1b0, "H_DISABLE_AND_GET"}, \ {0x1b4, "H_ERROR_DATA"}, \ {0x1b8, "H_GET_HCA_INFO"}, \ {0x1bc, "H_GET_PERF_COUNT"}, \ -- GitLab From 69507b984ddce803df81215cc7813825189adafa Mon Sep 17 00:00:00 2001 From: Santosh Sivaraj Date: Tue, 21 Jul 2020 14:49:15 +0530 Subject: [PATCH 0790/1476] powerpc/mm/hash64: Remove comment that is no longer valid hash_low_64.S was removed in commit a43c0eb8364c ("powerpc/mm: Convert 4k insert from asm to C") and flush_hash_page() is no longer called from any assembly routine. Signed-off-by: Santosh Sivaraj [mpe: Tweak comment wording] Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20200721091915.205006-1-santosh@fossix.org --- arch/powerpc/mm/book3s64/hash_utils.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/arch/powerpc/mm/book3s64/hash_utils.c b/arch/powerpc/mm/book3s64/hash_utils.c index 6f9f346a5f65a..9fdabea049907 100644 --- a/arch/powerpc/mm/book3s64/hash_utils.c +++ b/arch/powerpc/mm/book3s64/hash_utils.c @@ -1707,10 +1707,6 @@ unsigned long pte_get_hash_gslot(unsigned long vpn, unsigned long shift, return gslot; } -/* - * WARNING: This is called from hash_low_64.S, if you change this prototype, - * do not forget to update the assembly call site ! - */ void flush_hash_page(unsigned long vpn, real_pte_t pte, int psize, int ssize, unsigned long flags) { -- GitLab From 8747bf36f312356f8a295a0c39ff092d65ce75ae Mon Sep 17 00:00:00 2001 From: Pratik Rajesh Sampat Date: Tue, 21 Jul 2020 21:07:06 +0530 Subject: [PATCH 0791/1476] powerpc/powernv/idle: Replace CPU feature check with PVR check The POWER9 idle driver contains implementation-specific details that means it is not suitable to run on any processor that implements ISA v3.0 (e.g., POWER10), so only init the driver when running on a POWER9. Signed-off-by: Pratik Rajesh Sampat [mpe: Use updated change log from Nick] Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20200721153708.89057-2-psampat@linux.ibm.com --- arch/powerpc/platforms/powernv/idle.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/powerpc/platforms/powernv/idle.c b/arch/powerpc/platforms/powernv/idle.c index 1c9d0a9d50e02..3a604d7ac91db 100644 --- a/arch/powerpc/platforms/powernv/idle.c +++ b/arch/powerpc/platforms/powernv/idle.c @@ -1223,7 +1223,7 @@ static void __init pnv_probe_idle_states(void) return; } - if (cpu_has_feature(CPU_FTR_ARCH_300)) + if (pvr_version_is(PVR_POWER9)) pnv_power9_idle_init(); for (i = 0; i < nr_pnv_idle_states; i++) -- GitLab From dcbbfa6b05daca94ebcdbce80a7cf05c717d2942 Mon Sep 17 00:00:00 2001 From: Pratik Rajesh Sampat Date: Tue, 21 Jul 2020 21:07:07 +0530 Subject: [PATCH 0792/1476] powerpc/powernv/idle: Rename pnv_first_spr_loss_level variable Replace the variable name from using "pnv_first_spr_loss_level" to "deep_spr_loss_state". pnv_first_spr_loss_level is supposed to be the earliest state that has OPAL_PM_LOSE_FULL_CONTEXT set, in other places the kernel uses the "deep" states as terminology. Hence renaming the variable to be coherent to its semantics. Signed-off-by: Pratik Rajesh Sampat Acked-by: Gautham R. Shenoy Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20200721153708.89057-3-psampat@linux.ibm.com --- arch/powerpc/platforms/powernv/idle.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/arch/powerpc/platforms/powernv/idle.c b/arch/powerpc/platforms/powernv/idle.c index 3a604d7ac91db..ed09f1fe16fe0 100644 --- a/arch/powerpc/platforms/powernv/idle.c +++ b/arch/powerpc/platforms/powernv/idle.c @@ -48,7 +48,7 @@ static bool default_stop_found; * First stop state levels when SPR and TB loss can occur. */ static u64 pnv_first_tb_loss_level = MAX_STOP_STATE + 1; -static u64 pnv_first_spr_loss_level = MAX_STOP_STATE + 1; +static u64 deep_spr_loss_state = MAX_STOP_STATE + 1; /* * psscr value and mask of the deepest stop idle state. @@ -673,7 +673,7 @@ static unsigned long power9_idle_stop(unsigned long psscr, bool mmu_on) mmcra = mfspr(SPRN_MMCRA); } - if ((psscr & PSSCR_RL_MASK) >= pnv_first_spr_loss_level) { + if ((psscr & PSSCR_RL_MASK) >= deep_spr_loss_state) { sprs.lpcr = mfspr(SPRN_LPCR); sprs.hfscr = mfspr(SPRN_HFSCR); sprs.fscr = mfspr(SPRN_FSCR); @@ -759,7 +759,7 @@ static unsigned long power9_idle_stop(unsigned long psscr, bool mmu_on) * just always test PSSCR for SPR/TB state loss. */ pls = (psscr & PSSCR_PLS) >> PSSCR_PLS_SHIFT; - if (likely(pls < pnv_first_spr_loss_level)) { + if (likely(pls < deep_spr_loss_state)) { if (sprs_saved) atomic_stop_thread_idle(); goto out; @@ -1106,7 +1106,7 @@ static void __init pnv_power9_idle_init(void) * the deepest loss-less (OPAL_PM_STOP_INST_FAST) stop state. */ pnv_first_tb_loss_level = MAX_STOP_STATE + 1; - pnv_first_spr_loss_level = MAX_STOP_STATE + 1; + deep_spr_loss_state = MAX_STOP_STATE + 1; for (i = 0; i < nr_pnv_idle_states; i++) { int err; struct pnv_idle_states_t *state = &pnv_idle_states[i]; @@ -1117,8 +1117,8 @@ static void __init pnv_power9_idle_init(void) pnv_first_tb_loss_level = psscr_rl; if ((state->flags & OPAL_PM_LOSE_FULL_CONTEXT) && - (pnv_first_spr_loss_level > psscr_rl)) - pnv_first_spr_loss_level = psscr_rl; + (deep_spr_loss_state > psscr_rl)) + deep_spr_loss_state = psscr_rl; /* * The idle code does not deal with TB loss occurring @@ -1129,8 +1129,8 @@ static void __init pnv_power9_idle_init(void) * compatibility. */ if ((state->flags & OPAL_PM_TIMEBASE_STOP) && - (pnv_first_spr_loss_level > psscr_rl)) - pnv_first_spr_loss_level = psscr_rl; + (deep_spr_loss_state > psscr_rl)) + deep_spr_loss_state = psscr_rl; err = validate_psscr_val_mask(&state->psscr_val, &state->psscr_mask, @@ -1176,7 +1176,7 @@ static void __init pnv_power9_idle_init(void) } pr_info("cpuidle-powernv: First stop level that may lose SPRs = 0x%llx\n", - pnv_first_spr_loss_level); + deep_spr_loss_state); pr_info("cpuidle-powernv: First stop level that may lose timebase = 0x%llx\n", pnv_first_tb_loss_level); -- GitLab From 5c92fb1b46102e1efe0eed69e743f711bc1c7d2e Mon Sep 17 00:00:00 2001 From: Pratik Rajesh Sampat Date: Tue, 21 Jul 2020 21:07:08 +0530 Subject: [PATCH 0793/1476] powerpc/powernv/idle: Exclude mfspr on HID1, 4, 5 on P9 and above POWER9 onwards the support for the registers HID1, HID4, HID5 has been receded. Although mfspr on the above registers worked in Power9, In Power10 simulator is unrecognized. Moving their assignment under the check for machines lower than Power9 Signed-off-by: Pratik Rajesh Sampat Reviewed-by: Gautham R. Shenoy Reviewed-by: Nicholas Piggin Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20200721153708.89057-4-psampat@linux.ibm.com --- arch/powerpc/platforms/powernv/idle.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/arch/powerpc/platforms/powernv/idle.c b/arch/powerpc/platforms/powernv/idle.c index ed09f1fe16fe0..77513a80cef90 100644 --- a/arch/powerpc/platforms/powernv/idle.c +++ b/arch/powerpc/platforms/powernv/idle.c @@ -73,9 +73,6 @@ static int pnv_save_sprs_for_deep_states(void) */ uint64_t lpcr_val = mfspr(SPRN_LPCR); uint64_t hid0_val = mfspr(SPRN_HID0); - uint64_t hid1_val = mfspr(SPRN_HID1); - uint64_t hid4_val = mfspr(SPRN_HID4); - uint64_t hid5_val = mfspr(SPRN_HID5); uint64_t hmeer_val = mfspr(SPRN_HMEER); uint64_t msr_val = MSR_IDLE; uint64_t psscr_val = pnv_deepest_stop_psscr_val; @@ -117,6 +114,9 @@ static int pnv_save_sprs_for_deep_states(void) /* Only p8 needs to set extra HID regiters */ if (!cpu_has_feature(CPU_FTR_ARCH_300)) { + uint64_t hid1_val = mfspr(SPRN_HID1); + uint64_t hid4_val = mfspr(SPRN_HID4); + uint64_t hid5_val = mfspr(SPRN_HID5); rc = opal_slw_set_reg(pir, SPRN_HID1, hid1_val); if (rc != 0) -- GitLab From 8ac9b9d61f0eceba6ce571e7527798465ae9a7c5 Mon Sep 17 00:00:00 2001 From: Michael Ellerman Date: Wed, 22 Jul 2020 15:53:15 +1000 Subject: [PATCH 0794/1476] selftests/powerpc: Add test of memcmp at end of page Update our memcmp selftest, to test the case where we're comparing up to the end of a page and the subsequent page is not mapped. We have to make sure we don't read off the end of the page and cause a fault. We had a bug there in the past, fixed in commit d9470757398a ("powerpc/64: Fix memcmp reading past the end of src/dest"). Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20200722055315.962391-1-mpe@ellerman.id.au --- .../selftests/powerpc/stringloops/memcmp.c | 40 ++++++++++--------- 1 file changed, 22 insertions(+), 18 deletions(-) diff --git a/tools/testing/selftests/powerpc/stringloops/memcmp.c b/tools/testing/selftests/powerpc/stringloops/memcmp.c index b1fa7546957f7..e4605ca850dc1 100644 --- a/tools/testing/selftests/powerpc/stringloops/memcmp.c +++ b/tools/testing/selftests/powerpc/stringloops/memcmp.c @@ -2,6 +2,7 @@ #include #include #include +#include #include #include "utils.h" @@ -13,6 +14,9 @@ #define LARGE_MAX_OFFSET 32 #define LARGE_SIZE_START 4096 +/* This is big enough to fit LARGE_SIZE and works on 4K & 64K kernels */ +#define MAP_SIZE (64 * 1024) + #define MAX_OFFSET_DIFF_S1_S2 48 int vmx_count; @@ -68,25 +72,25 @@ static void test_one(char *s1, char *s2, unsigned long max_offset, static int testcase(bool islarge) { - char *s1; - char *s2; - unsigned long i; - - unsigned long comp_size = (islarge ? LARGE_SIZE : SIZE); - unsigned long alloc_size = comp_size + MAX_OFFSET_DIFF_S1_S2; - int iterations = islarge ? LARGE_ITERATIONS : ITERATIONS; - - s1 = memalign(128, alloc_size); - if (!s1) { - perror("memalign"); - exit(1); - } + unsigned long i, comp_size, alloc_size; + char *p, *s1, *s2; + int iterations; - s2 = memalign(128, alloc_size); - if (!s2) { - perror("memalign"); - exit(1); - } + comp_size = (islarge ? LARGE_SIZE : SIZE); + alloc_size = comp_size + MAX_OFFSET_DIFF_S1_S2; + iterations = islarge ? LARGE_ITERATIONS : ITERATIONS; + + p = mmap(NULL, 4 * MAP_SIZE, PROT_READ | PROT_WRITE, + MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); + FAIL_IF(p == MAP_FAILED); + + /* Put s1/s2 at the end of a page */ + s1 = p + MAP_SIZE - alloc_size; + s2 = p + 3 * MAP_SIZE - alloc_size; + + /* And unmap the subsequent page to force a fault if we overread */ + munmap(p + MAP_SIZE, MAP_SIZE); + munmap(p + 3 * MAP_SIZE, MAP_SIZE); srandom(time(0)); -- GitLab From a68e06e729b1b06c50ee52917d6b825b43e7d269 Mon Sep 17 00:00:00 2001 From: Dinghao Liu Date: Thu, 9 Jul 2020 14:43:56 +0800 Subject: [PATCH 0795/1476] PCI: rcar: Fix runtime PM imbalance on error pm_runtime_get_sync() increments the runtime PM usage counter even when the call returns an error code. Thus a corresponding decrement is needed on the error handling path to keep the counter balanced. Link: https://lore.kernel.org/r/20200709064356.8800-1-dinghao.liu@zju.edu.cn Fixes: 0df6150e7ceb ("PCI: rcar: Use runtime PM to control controller clock") Signed-off-by: Dinghao Liu Signed-off-by: Lorenzo Pieralisi Reviewed-by: Yoshihiro Shimoda --- drivers/pci/controller/pcie-rcar-host.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/pci/controller/pcie-rcar-host.c b/drivers/pci/controller/pcie-rcar-host.c index d210a36561be2..060c24f5221e2 100644 --- a/drivers/pci/controller/pcie-rcar-host.c +++ b/drivers/pci/controller/pcie-rcar-host.c @@ -986,7 +986,7 @@ static int rcar_pcie_probe(struct platform_device *pdev) err = pm_runtime_get_sync(pcie->dev); if (err < 0) { dev_err(pcie->dev, "pm_runtime_get_sync failed\n"); - goto err_pm_disable; + goto err_pm_put; } err = rcar_pcie_get_resources(host); @@ -1057,8 +1057,6 @@ err_unmap_msi_irqs: err_pm_put: pm_runtime_put(dev); - -err_pm_disable: pm_runtime_disable(dev); pci_free_resource_list(&host->resources); -- GitLab From da30e8bc9accaf74505bff4b16b792a01d6d1c0b Mon Sep 17 00:00:00 2001 From: Dejin Zheng Date: Tue, 26 May 2020 23:09:54 +0800 Subject: [PATCH 0796/1476] PCI: controller: Remove duplicate error message devm_pci_remap_cfg_resource() will print an error message by itself when goes wrong, so remove the duplicate error message. Link: https://lore.kernel.org/r/20200526150954.4729-1-zhengdejin5@gmail.com Signed-off-by: Dejin Zheng Signed-off-by: Lorenzo Pieralisi Reviewed-by: Rob Herring --- drivers/pci/controller/cadence/pcie-cadence-host.c | 4 +--- drivers/pci/controller/dwc/pcie-al.c | 13 +++---------- drivers/pci/controller/dwc/pcie-armada8k.c | 1 - drivers/pci/controller/dwc/pcie-spear13xx.c | 1 - 4 files changed, 4 insertions(+), 15 deletions(-) diff --git a/drivers/pci/controller/cadence/pcie-cadence-host.c b/drivers/pci/controller/cadence/pcie-cadence-host.c index 59b9b3df46404..8809beb51d040 100644 --- a/drivers/pci/controller/cadence/pcie-cadence-host.c +++ b/drivers/pci/controller/cadence/pcie-cadence-host.c @@ -225,10 +225,8 @@ int cdns_pcie_host_setup(struct cdns_pcie_rc *rc) res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "cfg"); rc->cfg_base = devm_pci_remap_cfg_resource(dev, res); - if (IS_ERR(rc->cfg_base)) { - dev_err(dev, "missing \"cfg\"\n"); + if (IS_ERR(rc->cfg_base)) return PTR_ERR(rc->cfg_base); - } rc->cfg_res = res; res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "mem"); diff --git a/drivers/pci/controller/dwc/pcie-al.c b/drivers/pci/controller/dwc/pcie-al.c index 270868f3859a2..d57d4ee158480 100644 --- a/drivers/pci/controller/dwc/pcie-al.c +++ b/drivers/pci/controller/dwc/pcie-al.c @@ -67,13 +67,8 @@ static int al_pcie_init(struct pci_config_window *cfg) dev_dbg(dev, "Root port dbi res: %pR\n", res); al_pcie->dbi_base = devm_pci_remap_cfg_resource(dev, res); - if (IS_ERR(al_pcie->dbi_base)) { - long err = PTR_ERR(al_pcie->dbi_base); - - dev_err(dev, "couldn't remap dbi base %pR (err:%ld)\n", - res, err); - return err; - } + if (IS_ERR(al_pcie->dbi_base)) + return PTR_ERR(al_pcie->dbi_base); cfg->priv = al_pcie; @@ -408,10 +403,8 @@ static int al_pcie_probe(struct platform_device *pdev) dbi_res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dbi"); pci->dbi_base = devm_pci_remap_cfg_resource(dev, dbi_res); - if (IS_ERR(pci->dbi_base)) { - dev_err(dev, "couldn't remap dbi base %pR\n", dbi_res); + if (IS_ERR(pci->dbi_base)) return PTR_ERR(pci->dbi_base); - } ecam_res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "config"); if (!ecam_res) { diff --git a/drivers/pci/controller/dwc/pcie-armada8k.c b/drivers/pci/controller/dwc/pcie-armada8k.c index 49596547e8c29..896b95d6917cf 100644 --- a/drivers/pci/controller/dwc/pcie-armada8k.c +++ b/drivers/pci/controller/dwc/pcie-armada8k.c @@ -317,7 +317,6 @@ static int armada8k_pcie_probe(struct platform_device *pdev) base = platform_get_resource_byname(pdev, IORESOURCE_MEM, "ctrl"); pci->dbi_base = devm_pci_remap_cfg_resource(dev, base); if (IS_ERR(pci->dbi_base)) { - dev_err(dev, "couldn't remap regs base %p\n", base); ret = PTR_ERR(pci->dbi_base); goto fail_clkreg; } diff --git a/drivers/pci/controller/dwc/pcie-spear13xx.c b/drivers/pci/controller/dwc/pcie-spear13xx.c index 7d0cdfd8138b9..cdfde1bd7d8e5 100644 --- a/drivers/pci/controller/dwc/pcie-spear13xx.c +++ b/drivers/pci/controller/dwc/pcie-spear13xx.c @@ -273,7 +273,6 @@ static int spear13xx_pcie_probe(struct platform_device *pdev) dbi_base = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dbi"); pci->dbi_base = devm_pci_remap_cfg_resource(dev, dbi_base); if (IS_ERR(pci->dbi_base)) { - dev_err(dev, "couldn't remap dbi base %p\n", dbi_base); ret = PTR_ERR(pci->dbi_base); goto fail_clk; } -- GitLab From 79cbde56f98bec1c50f4c4ce15f5251a6a4348dd Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Tue, 21 Jul 2020 20:24:56 -0600 Subject: [PATCH 0797/1476] PCI: versatile: Drop flag PCI_ENABLE_PROC_DOMAINS PCI_ENABLE_PROC_DOMAINS is only used on powerpc and doesn't do anything for the Versatile host driver, so let's drop it. I'm not sure how or why I had this to begin with. PCI_ENABLE_PROC_DOMAINS was never used on ARM. Link: https://lore.kernel.org/r/20200722022514.1283916-2-robh@kernel.org Signed-off-by: Rob Herring Signed-off-by: Lorenzo Pieralisi Acked-by: Bjorn Helgaas Cc: Lorenzo Pieralisi Cc: Bjorn Helgaas --- drivers/pci/controller/pci-versatile.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/pci/controller/pci-versatile.c b/drivers/pci/controller/pci-versatile.c index a8d361f6c5d9b..39ed7a171fac8 100644 --- a/drivers/pci/controller/pci-versatile.c +++ b/drivers/pci/controller/pci-versatile.c @@ -151,7 +151,6 @@ static int versatile_pci_probe(struct platform_device *pdev) */ writel(0, versatile_cfg_base[0] + PCI_INTERRUPT_LINE); - pci_add_flags(PCI_ENABLE_PROC_DOMAINS); pci_add_flags(PCI_REASSIGN_ALL_BUS); bridge->dev.parent = dev; -- GitLab From 6a589900d05091dfec6d9d1f35245a6578cccee6 Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Tue, 21 Jul 2020 20:24:57 -0600 Subject: [PATCH 0798/1476] PCI: Set default bridge parent device The host bridge's parent device is always the platform device. As we already have a pointer to it in the devres functions, let's initialize the parent device. Drivers can still override the parent if desired. Link: https://lore.kernel.org/r/20200722022514.1283916-3-robh@kernel.org Signed-off-by: Rob Herring Signed-off-by: Lorenzo Pieralisi Acked-by: Bjorn Helgaas Cc: Lorenzo Pieralisi Cc: Bjorn Helgaas --- drivers/pci/controller/cadence/pcie-cadence-host.c | 1 - drivers/pci/controller/dwc/pcie-designware-host.c | 1 - drivers/pci/controller/mobiveil/pcie-mobiveil-host.c | 1 - drivers/pci/controller/pci-aardvark.c | 1 - drivers/pci/controller/pci-ftpci100.c | 1 - drivers/pci/controller/pci-host-common.c | 1 - drivers/pci/controller/pci-loongson.c | 1 - drivers/pci/controller/pci-mvebu.c | 1 - drivers/pci/controller/pci-tegra.c | 1 - drivers/pci/controller/pci-v3-semi.c | 1 - drivers/pci/controller/pci-versatile.c | 1 - drivers/pci/controller/pci-xgene.c | 1 - drivers/pci/controller/pcie-altera.c | 1 - drivers/pci/controller/pcie-brcmstb.c | 1 - drivers/pci/controller/pcie-iproc.c | 1 - drivers/pci/controller/pcie-mediatek.c | 1 - drivers/pci/controller/pcie-rcar-host.c | 2 -- drivers/pci/controller/pcie-rockchip-host.c | 1 - drivers/pci/controller/pcie-xilinx-nwl.c | 1 - drivers/pci/controller/pcie-xilinx.c | 1 - drivers/pci/probe.c | 2 ++ 21 files changed, 2 insertions(+), 21 deletions(-) diff --git a/drivers/pci/controller/cadence/pcie-cadence-host.c b/drivers/pci/controller/cadence/pcie-cadence-host.c index 8809beb51d040..ec41c6d6e4fac 100644 --- a/drivers/pci/controller/cadence/pcie-cadence-host.c +++ b/drivers/pci/controller/cadence/pcie-cadence-host.c @@ -241,7 +241,6 @@ int cdns_pcie_host_setup(struct cdns_pcie_rc *rc) if (ret) goto err_init; - bridge->dev.parent = dev; bridge->busnr = pcie->bus; bridge->ops = &cdns_pcie_host_ops; bridge->map_irq = of_irq_parse_and_map_pci; diff --git a/drivers/pci/controller/dwc/pcie-designware-host.c b/drivers/pci/controller/dwc/pcie-designware-host.c index 0a4a5aa6fe469..4a16306cec257 100644 --- a/drivers/pci/controller/dwc/pcie-designware-host.c +++ b/drivers/pci/controller/dwc/pcie-designware-host.c @@ -475,7 +475,6 @@ int dw_pcie_host_init(struct pcie_port *pp) pp->root_bus_nr = pp->busn->start; - bridge->dev.parent = dev; bridge->sysdata = pp; bridge->busnr = pp->root_bus_nr; bridge->ops = &dw_pcie_ops; diff --git a/drivers/pci/controller/mobiveil/pcie-mobiveil-host.c b/drivers/pci/controller/mobiveil/pcie-mobiveil-host.c index 5974619811ec2..705542b4bd210 100644 --- a/drivers/pci/controller/mobiveil/pcie-mobiveil-host.c +++ b/drivers/pci/controller/mobiveil/pcie-mobiveil-host.c @@ -605,7 +605,6 @@ int mobiveil_pcie_host_probe(struct mobiveil_pcie *pcie) } /* Initialize bridge */ - bridge->dev.parent = dev; bridge->sysdata = pcie; bridge->busnr = rp->root_bus_nr; bridge->ops = &mobiveil_pcie_ops; diff --git a/drivers/pci/controller/pci-aardvark.c b/drivers/pci/controller/pci-aardvark.c index 0d98f9b04daac..6aaa6ed0438cb 100644 --- a/drivers/pci/controller/pci-aardvark.c +++ b/drivers/pci/controller/pci-aardvark.c @@ -1183,7 +1183,6 @@ static int advk_pcie_probe(struct platform_device *pdev) return ret; } - bridge->dev.parent = dev; bridge->sysdata = pcie; bridge->busnr = 0; bridge->ops = &advk_pcie_ops; diff --git a/drivers/pci/controller/pci-ftpci100.c b/drivers/pci/controller/pci-ftpci100.c index 221dfc9dc81b8..db887cbbec093 100644 --- a/drivers/pci/controller/pci-ftpci100.c +++ b/drivers/pci/controller/pci-ftpci100.c @@ -436,7 +436,6 @@ static int faraday_pci_probe(struct platform_device *pdev) if (!host) return -ENOMEM; - host->dev.parent = dev; host->ops = &faraday_pci_ops; host->busnr = 0; host->msi = NULL; diff --git a/drivers/pci/controller/pci-host-common.c b/drivers/pci/controller/pci-host-common.c index b76e55f495e47..ad395d7feddc5 100644 --- a/drivers/pci/controller/pci-host-common.c +++ b/drivers/pci/controller/pci-host-common.c @@ -76,7 +76,6 @@ int pci_host_common_probe(struct platform_device *pdev) if (!pci_has_flag(PCI_PROBE_ONLY)) pci_add_flags(PCI_REASSIGN_ALL_BUS); - bridge->dev.parent = dev; bridge->sysdata = cfg; bridge->busnr = cfg->busr.start; bridge->ops = (struct pci_ops *)&ops->pci_ops; diff --git a/drivers/pci/controller/pci-loongson.c b/drivers/pci/controller/pci-loongson.c index 459009c8a4a02..0198c15ed97c5 100644 --- a/drivers/pci/controller/pci-loongson.c +++ b/drivers/pci/controller/pci-loongson.c @@ -225,7 +225,6 @@ static int loongson_pci_probe(struct platform_device *pdev) return err; } - bridge->dev.parent = dev; bridge->sysdata = priv; bridge->ops = &loongson_pci_ops; bridge->map_irq = loongson_map_irq; diff --git a/drivers/pci/controller/pci-mvebu.c b/drivers/pci/controller/pci-mvebu.c index 801044523a3d1..7be6db851614a 100644 --- a/drivers/pci/controller/pci-mvebu.c +++ b/drivers/pci/controller/pci-mvebu.c @@ -1116,7 +1116,6 @@ static int mvebu_pcie_probe(struct platform_device *pdev) pcie->nports = i; - bridge->dev.parent = dev; bridge->sysdata = pcie; bridge->busnr = 0; bridge->ops = &mvebu_pcie_ops; diff --git a/drivers/pci/controller/pci-tegra.c b/drivers/pci/controller/pci-tegra.c index 71bf4b2684ac2..41232bd07c3d1 100644 --- a/drivers/pci/controller/pci-tegra.c +++ b/drivers/pci/controller/pci-tegra.c @@ -2713,7 +2713,6 @@ static int tegra_pcie_probe(struct platform_device *pdev) } host->busnr = bus->start; - host->dev.parent = &pdev->dev; host->ops = &tegra_pcie_ops; host->map_irq = tegra_pcie_map_irq; host->swizzle_irq = pci_common_swizzle; diff --git a/drivers/pci/controller/pci-v3-semi.c b/drivers/pci/controller/pci-v3-semi.c index 198cf2c6ed924..e8b5e55803b2a 100644 --- a/drivers/pci/controller/pci-v3-semi.c +++ b/drivers/pci/controller/pci-v3-semi.c @@ -723,7 +723,6 @@ static int v3_pci_probe(struct platform_device *pdev) if (!host) return -ENOMEM; - host->dev.parent = dev; host->ops = &v3_pci_ops; host->busnr = 0; host->msi = NULL; diff --git a/drivers/pci/controller/pci-versatile.c b/drivers/pci/controller/pci-versatile.c index 39ed7a171fac8..ea1889cad7227 100644 --- a/drivers/pci/controller/pci-versatile.c +++ b/drivers/pci/controller/pci-versatile.c @@ -153,7 +153,6 @@ static int versatile_pci_probe(struct platform_device *pdev) pci_add_flags(PCI_REASSIGN_ALL_BUS); - bridge->dev.parent = dev; bridge->sysdata = NULL; bridge->busnr = 0; bridge->ops = &pci_versatile_ops; diff --git a/drivers/pci/controller/pci-xgene.c b/drivers/pci/controller/pci-xgene.c index 4c783cfddb3a6..f02cbd65b8b3f 100644 --- a/drivers/pci/controller/pci-xgene.c +++ b/drivers/pci/controller/pci-xgene.c @@ -623,7 +623,6 @@ static int xgene_pcie_probe(struct platform_device *pdev) if (ret) return ret; - bridge->dev.parent = dev; bridge->sysdata = port; bridge->busnr = 0; bridge->ops = &xgene_pcie_ops; diff --git a/drivers/pci/controller/pcie-altera.c b/drivers/pci/controller/pcie-altera.c index 4ffe382f220f3..4424430dacb07 100644 --- a/drivers/pci/controller/pcie-altera.c +++ b/drivers/pci/controller/pcie-altera.c @@ -813,7 +813,6 @@ static int altera_pcie_probe(struct platform_device *pdev) cra_writel(pcie, P2A_INT_ENA_ALL, P2A_INT_ENABLE); altera_pcie_host_init(pcie); - bridge->dev.parent = dev; bridge->sysdata = pcie; bridge->busnr = pcie->root_bus_nr; bridge->ops = &altera_pcie_ops; diff --git a/drivers/pci/controller/pcie-brcmstb.c b/drivers/pci/controller/pcie-brcmstb.c index 91a4b7f3ee45f..87e9695e2afa3 100644 --- a/drivers/pci/controller/pcie-brcmstb.c +++ b/drivers/pci/controller/pcie-brcmstb.c @@ -994,7 +994,6 @@ static int brcm_pcie_probe(struct platform_device *pdev) } } - bridge->dev.parent = &pdev->dev; bridge->busnr = 0; bridge->ops = &brcm_pcie_ops; bridge->sysdata = pcie; diff --git a/drivers/pci/controller/pcie-iproc.c b/drivers/pci/controller/pcie-iproc.c index 232fca0754e15..cf1cb717c7df8 100644 --- a/drivers/pci/controller/pcie-iproc.c +++ b/drivers/pci/controller/pcie-iproc.c @@ -1524,7 +1524,6 @@ int iproc_pcie_setup(struct iproc_pcie *pcie, struct list_head *res) dev_info(dev, "not using iProc MSI\n"); host->busnr = 0; - host->dev.parent = dev; host->ops = &iproc_pcie_ops; host->sysdata = pcie; host->map_irq = pcie->map_irq; diff --git a/drivers/pci/controller/pcie-mediatek.c b/drivers/pci/controller/pcie-mediatek.c index d8e38276dbe34..a9399b8d2e8b4 100644 --- a/drivers/pci/controller/pcie-mediatek.c +++ b/drivers/pci/controller/pcie-mediatek.c @@ -1095,7 +1095,6 @@ static int mtk_pcie_probe(struct platform_device *pdev) return err; host->busnr = pcie->busnr; - host->dev.parent = pcie->dev; host->ops = pcie->soc->ops; host->map_irq = of_irq_parse_and_map_pci; host->swizzle_irq = pci_common_swizzle; diff --git a/drivers/pci/controller/pcie-rcar-host.c b/drivers/pci/controller/pcie-rcar-host.c index 9069ad96fe95c..3a8e749b4904b 100644 --- a/drivers/pci/controller/pcie-rcar-host.c +++ b/drivers/pci/controller/pcie-rcar-host.c @@ -329,7 +329,6 @@ static int rcar_pcie_enable(struct rcar_pcie_host *host) { struct pci_host_bridge *bridge = pci_host_bridge_from_priv(host); struct rcar_pcie *pcie = &host->pcie; - struct device *dev = pcie->dev; /* Try setting 5 GT/s link speed */ rcar_pcie_force_speedup(pcie); @@ -338,7 +337,6 @@ static int rcar_pcie_enable(struct rcar_pcie_host *host) pci_add_flags(PCI_REASSIGN_ALL_BUS); - bridge->dev.parent = dev; bridge->sysdata = host; bridge->busnr = host->root_bus_nr; bridge->ops = &rcar_pcie_ops; diff --git a/drivers/pci/controller/pcie-rockchip-host.c b/drivers/pci/controller/pcie-rockchip-host.c index 6a3c8442258b9..4eb79c60d627f 100644 --- a/drivers/pci/controller/pcie-rockchip-host.c +++ b/drivers/pci/controller/pcie-rockchip-host.c @@ -1007,7 +1007,6 @@ static int rockchip_pcie_probe(struct platform_device *pdev) goto err_remove_irq_domain; } - bridge->dev.parent = dev; bridge->sysdata = rockchip; bridge->busnr = 0; bridge->ops = &rockchip_pcie_ops; diff --git a/drivers/pci/controller/pcie-xilinx-nwl.c b/drivers/pci/controller/pcie-xilinx-nwl.c index 32a0b08d6da5f..3c747aa4b6d19 100644 --- a/drivers/pci/controller/pcie-xilinx-nwl.c +++ b/drivers/pci/controller/pcie-xilinx-nwl.c @@ -854,7 +854,6 @@ static int nwl_pcie_probe(struct platform_device *pdev) return err; } - bridge->dev.parent = dev; bridge->sysdata = pcie; bridge->busnr = pcie->root_busno; bridge->ops = &nwl_pcie_ops; diff --git a/drivers/pci/controller/pcie-xilinx.c b/drivers/pci/controller/pcie-xilinx.c index 05547497f391d..7c0f3268fc5c1 100644 --- a/drivers/pci/controller/pcie-xilinx.c +++ b/drivers/pci/controller/pcie-xilinx.c @@ -651,7 +651,6 @@ static int xilinx_pcie_probe(struct platform_device *pdev) return err; } - bridge->dev.parent = dev; bridge->sysdata = port; bridge->busnr = 0; bridge->ops = &xilinx_pcie_ops; diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index 2f66988cea257..5583037dbdfab 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -628,6 +628,8 @@ struct pci_host_bridge *devm_pci_alloc_host_bridge(struct device *dev, if (!bridge) return NULL; + bridge->dev.parent = dev; + ret = devm_add_action_or_reset(dev, devm_pci_alloc_host_bridge_release, bridge); if (ret) -- GitLab From d3bb94d06aae0f47d492f2999d024d58ce1baea9 Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Tue, 21 Jul 2020 20:24:58 -0600 Subject: [PATCH 0799/1476] PCI: Drop unnecessary zeroing of bridge fields The struct pci_host_bridge is 0 initialized when allocated, so there's no need to explicitly set fields to 0. Link: https://lore.kernel.org/r/20200722022514.1283916-4-robh@kernel.org Signed-off-by: Rob Herring Signed-off-by: Lorenzo Pieralisi Acked-by: Bjorn Helgaas Cc: Lorenzo Pieralisi Cc: Bjorn Helgaas --- drivers/pci/controller/pci-aardvark.c | 1 - drivers/pci/controller/pci-ftpci100.c | 2 -- drivers/pci/controller/pci-mvebu.c | 1 - drivers/pci/controller/pci-v3-semi.c | 2 -- drivers/pci/controller/pci-versatile.c | 2 -- drivers/pci/controller/pci-xgene.c | 1 - drivers/pci/controller/pcie-brcmstb.c | 1 - drivers/pci/controller/pcie-iproc.c | 1 - drivers/pci/controller/pcie-rockchip-host.c | 1 - drivers/pci/controller/pcie-xilinx.c | 1 - 10 files changed, 13 deletions(-) diff --git a/drivers/pci/controller/pci-aardvark.c b/drivers/pci/controller/pci-aardvark.c index 6aaa6ed0438cb..bfd29d8b3333e 100644 --- a/drivers/pci/controller/pci-aardvark.c +++ b/drivers/pci/controller/pci-aardvark.c @@ -1184,7 +1184,6 @@ static int advk_pcie_probe(struct platform_device *pdev) } bridge->sysdata = pcie; - bridge->busnr = 0; bridge->ops = &advk_pcie_ops; bridge->map_irq = of_irq_parse_and_map_pci; bridge->swizzle_irq = pci_common_swizzle; diff --git a/drivers/pci/controller/pci-ftpci100.c b/drivers/pci/controller/pci-ftpci100.c index db887cbbec093..5441a5f927399 100644 --- a/drivers/pci/controller/pci-ftpci100.c +++ b/drivers/pci/controller/pci-ftpci100.c @@ -437,8 +437,6 @@ static int faraday_pci_probe(struct platform_device *pdev) return -ENOMEM; host->ops = &faraday_pci_ops; - host->busnr = 0; - host->msi = NULL; host->map_irq = of_irq_parse_and_map_pci; host->swizzle_irq = pci_common_swizzle; p = pci_host_bridge_priv(host); diff --git a/drivers/pci/controller/pci-mvebu.c b/drivers/pci/controller/pci-mvebu.c index 7be6db851614a..db93823a2dcb1 100644 --- a/drivers/pci/controller/pci-mvebu.c +++ b/drivers/pci/controller/pci-mvebu.c @@ -1117,7 +1117,6 @@ static int mvebu_pcie_probe(struct platform_device *pdev) pcie->nports = i; bridge->sysdata = pcie; - bridge->busnr = 0; bridge->ops = &mvebu_pcie_ops; bridge->map_irq = of_irq_parse_and_map_pci; bridge->swizzle_irq = pci_common_swizzle; diff --git a/drivers/pci/controller/pci-v3-semi.c b/drivers/pci/controller/pci-v3-semi.c index e8b5e55803b2a..d79af39e6e606 100644 --- a/drivers/pci/controller/pci-v3-semi.c +++ b/drivers/pci/controller/pci-v3-semi.c @@ -724,8 +724,6 @@ static int v3_pci_probe(struct platform_device *pdev) return -ENOMEM; host->ops = &v3_pci_ops; - host->busnr = 0; - host->msi = NULL; host->map_irq = of_irq_parse_and_map_pci; host->swizzle_irq = pci_common_swizzle; v3 = pci_host_bridge_priv(host); diff --git a/drivers/pci/controller/pci-versatile.c b/drivers/pci/controller/pci-versatile.c index ea1889cad7227..66f5c61b8eb8e 100644 --- a/drivers/pci/controller/pci-versatile.c +++ b/drivers/pci/controller/pci-versatile.c @@ -153,8 +153,6 @@ static int versatile_pci_probe(struct platform_device *pdev) pci_add_flags(PCI_REASSIGN_ALL_BUS); - bridge->sysdata = NULL; - bridge->busnr = 0; bridge->ops = &pci_versatile_ops; bridge->map_irq = of_irq_parse_and_map_pci; bridge->swizzle_irq = pci_common_swizzle; diff --git a/drivers/pci/controller/pci-xgene.c b/drivers/pci/controller/pci-xgene.c index f02cbd65b8b3f..d3aa28e1a4829 100644 --- a/drivers/pci/controller/pci-xgene.c +++ b/drivers/pci/controller/pci-xgene.c @@ -624,7 +624,6 @@ static int xgene_pcie_probe(struct platform_device *pdev) return ret; bridge->sysdata = port; - bridge->busnr = 0; bridge->ops = &xgene_pcie_ops; bridge->map_irq = of_irq_parse_and_map_pci; bridge->swizzle_irq = pci_common_swizzle; diff --git a/drivers/pci/controller/pcie-brcmstb.c b/drivers/pci/controller/pcie-brcmstb.c index 87e9695e2afa3..20e1aa23edc58 100644 --- a/drivers/pci/controller/pcie-brcmstb.c +++ b/drivers/pci/controller/pcie-brcmstb.c @@ -994,7 +994,6 @@ static int brcm_pcie_probe(struct platform_device *pdev) } } - bridge->busnr = 0; bridge->ops = &brcm_pcie_ops; bridge->sysdata = pcie; bridge->map_irq = of_irq_parse_and_map_pci; diff --git a/drivers/pci/controller/pcie-iproc.c b/drivers/pci/controller/pcie-iproc.c index cf1cb717c7df8..e98dafd0fff4a 100644 --- a/drivers/pci/controller/pcie-iproc.c +++ b/drivers/pci/controller/pcie-iproc.c @@ -1523,7 +1523,6 @@ int iproc_pcie_setup(struct iproc_pcie *pcie, struct list_head *res) if (iproc_pcie_msi_enable(pcie)) dev_info(dev, "not using iProc MSI\n"); - host->busnr = 0; host->ops = &iproc_pcie_ops; host->sysdata = pcie; host->map_irq = pcie->map_irq; diff --git a/drivers/pci/controller/pcie-rockchip-host.c b/drivers/pci/controller/pcie-rockchip-host.c index 4eb79c60d627f..fb88030161f2c 100644 --- a/drivers/pci/controller/pcie-rockchip-host.c +++ b/drivers/pci/controller/pcie-rockchip-host.c @@ -1008,7 +1008,6 @@ static int rockchip_pcie_probe(struct platform_device *pdev) } bridge->sysdata = rockchip; - bridge->busnr = 0; bridge->ops = &rockchip_pcie_ops; bridge->map_irq = of_irq_parse_and_map_pci; bridge->swizzle_irq = pci_common_swizzle; diff --git a/drivers/pci/controller/pcie-xilinx.c b/drivers/pci/controller/pcie-xilinx.c index 7c0f3268fc5c1..cce67cb8fb2b0 100644 --- a/drivers/pci/controller/pcie-xilinx.c +++ b/drivers/pci/controller/pcie-xilinx.c @@ -652,7 +652,6 @@ static int xilinx_pcie_probe(struct platform_device *pdev) } bridge->sysdata = port; - bridge->busnr = 0; bridge->ops = &xilinx_pcie_ops; bridge->map_irq = of_irq_parse_and_map_pci; bridge->swizzle_irq = pci_common_swizzle; -- GitLab From 11e97973607fab2131be2a378c7da90a9729cc21 Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Tue, 21 Jul 2020 20:24:59 -0600 Subject: [PATCH 0800/1476] PCI: aardvark: Use pci_is_root_bus() to check if bus is root bus Use pci_is_root_bus() rather than tracking the root bus number to determine if the bus is the root bus or not. This removes storing duplicated data as well as the need for the host bridge driver to have to care about the bus numbers in most cases. Link: https://lore.kernel.org/r/20200722022514.1283916-5-robh@kernel.org Signed-off-by: Rob Herring Signed-off-by: Lorenzo Pieralisi Acked-by: Bjorn Helgaas Cc: Thomas Petazzoni Cc: Lorenzo Pieralisi Cc: Bjorn Helgaas --- drivers/pci/controller/pci-aardvark.c | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/drivers/pci/controller/pci-aardvark.c b/drivers/pci/controller/pci-aardvark.c index bfd29d8b3333e..e08ed861368fc 100644 --- a/drivers/pci/controller/pci-aardvark.c +++ b/drivers/pci/controller/pci-aardvark.c @@ -195,7 +195,6 @@ struct advk_pcie { DECLARE_BITMAP(msi_used, MSI_IRQ_NUM); struct mutex msi_used_lock; u16 msi_msg; - int root_bus_nr; int link_gen; struct pci_bridge_emul bridge; struct gpio_desc *reset_gpio; @@ -641,7 +640,7 @@ static void advk_sw_pci_bridge_init(struct advk_pcie *pcie) static bool advk_pcie_valid_device(struct advk_pcie *pcie, struct pci_bus *bus, int devfn) { - if ((bus->number == pcie->root_bus_nr) && PCI_SLOT(devfn) != 0) + if (pci_is_root_bus(bus) && PCI_SLOT(devfn) != 0) return false; return true; @@ -659,7 +658,7 @@ static int advk_pcie_rd_conf(struct pci_bus *bus, u32 devfn, return PCIBIOS_DEVICE_NOT_FOUND; } - if (bus->number == pcie->root_bus_nr) + if (pci_is_root_bus(bus)) return pci_bridge_emul_conf_read(&pcie->bridge, where, size, val); @@ -670,7 +669,7 @@ static int advk_pcie_rd_conf(struct pci_bus *bus, u32 devfn, /* Program the control register */ reg = advk_readl(pcie, PIO_CTRL); reg &= ~PIO_CTRL_TYPE_MASK; - if (bus->primary == pcie->root_bus_nr) + if (pci_is_root_bus(bus->parent)) reg |= PCIE_CONFIG_RD_TYPE0; else reg |= PCIE_CONFIG_RD_TYPE1; @@ -715,7 +714,7 @@ static int advk_pcie_wr_conf(struct pci_bus *bus, u32 devfn, if (!advk_pcie_valid_device(pcie, bus, devfn)) return PCIBIOS_DEVICE_NOT_FOUND; - if (bus->number == pcie->root_bus_nr) + if (pci_is_root_bus(bus)) return pci_bridge_emul_conf_write(&pcie->bridge, where, size, val); @@ -729,7 +728,7 @@ static int advk_pcie_wr_conf(struct pci_bus *bus, u32 devfn, /* Program the control register */ reg = advk_readl(pcie, PIO_CTRL); reg &= ~PIO_CTRL_TYPE_MASK; - if (bus->primary == pcie->root_bus_nr) + if (pci_is_root_bus(bus->parent)) reg |= PCIE_CONFIG_WR_TYPE0; else reg |= PCIE_CONFIG_WR_TYPE1; @@ -1138,7 +1137,7 @@ static int advk_pcie_probe(struct platform_device *pdev) dev_err(dev, "Failed to parse resources\n"); return ret; } - pcie->root_bus_nr = bus->start; + bridge->busnr = bus->start; pcie->reset_gpio = devm_gpiod_get_from_of_node(dev, dev->of_node, "reset-gpios", 0, -- GitLab From 552549386624583bfdbd1da7c5eeba951fd94336 Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Tue, 21 Jul 2020 20:25:00 -0600 Subject: [PATCH 0801/1476] PCI: designware: Use pci_is_root_bus() to check if bus is root bus Use pci_is_root_bus() rather than tracking the root bus number to determine if the bus is the root bus or not. This removes storing duplicated data as well as the need for the host bridge driver to have to care about the bus numbers in most cases. Link: https://lore.kernel.org/r/20200722022514.1283916-6-robh@kernel.org Signed-off-by: Rob Herring Signed-off-by: Lorenzo Pieralisi Acked-by: Bjorn Helgaas Cc: Richard Zhu Cc: Lucas Stach Cc: Lorenzo Pieralisi Cc: Bjorn Helgaas Cc: Shawn Guo Cc: Sascha Hauer Cc: Pengutronix Kernel Team Cc: Fabio Estevam Cc: NXP Linux Team Cc: Murali Karicheri Cc: Jingoo Han Cc: Gustavo Pimentel --- drivers/pci/controller/dwc/pci-imx6.c | 2 +- drivers/pci/controller/dwc/pci-keystone.c | 4 ++-- .../pci/controller/dwc/pcie-designware-host.c | 18 +++++++----------- drivers/pci/controller/dwc/pcie-designware.h | 1 - 4 files changed, 10 insertions(+), 15 deletions(-) diff --git a/drivers/pci/controller/dwc/pci-imx6.c b/drivers/pci/controller/dwc/pci-imx6.c index 8f08ae53f53e4..9f1e4d9c008b3 100644 --- a/drivers/pci/controller/dwc/pci-imx6.c +++ b/drivers/pci/controller/dwc/pci-imx6.c @@ -1269,7 +1269,7 @@ static void imx6_pcie_quirk(struct pci_dev *dev) if (bus->dev.parent->parent->driver != &imx6_pcie_driver.driver) return; - if (bus->number == pp->root_bus_nr) { + if (pci_is_root_bus(bus)) { struct dw_pcie *pci = to_dw_pcie_from_pp(pp); struct imx6_pcie *imx6_pcie = to_imx6_pcie(pci); diff --git a/drivers/pci/controller/dwc/pci-keystone.c b/drivers/pci/controller/dwc/pci-keystone.c index 790679fdfa488..fcc3586c345b9 100644 --- a/drivers/pci/controller/dwc/pci-keystone.c +++ b/drivers/pci/controller/dwc/pci-keystone.c @@ -440,7 +440,7 @@ static int ks_pcie_rd_other_conf(struct pcie_port *pp, struct pci_bus *bus, reg = CFG_BUS(bus->number) | CFG_DEVICE(PCI_SLOT(devfn)) | CFG_FUNC(PCI_FUNC(devfn)); - if (bus->parent->number != pp->root_bus_nr) + if (!pci_is_root_bus(bus->parent)) reg |= CFG_TYPE1; ks_pcie_app_writel(ks_pcie, CFG_SETUP, reg); @@ -457,7 +457,7 @@ static int ks_pcie_wr_other_conf(struct pcie_port *pp, struct pci_bus *bus, reg = CFG_BUS(bus->number) | CFG_DEVICE(PCI_SLOT(devfn)) | CFG_FUNC(PCI_FUNC(devfn)); - if (bus->parent->number != pp->root_bus_nr) + if (!pci_is_root_bus(bus->parent)) reg |= CFG_TYPE1; ks_pcie_app_writel(ks_pcie, CFG_SETUP, reg); diff --git a/drivers/pci/controller/dwc/pcie-designware-host.c b/drivers/pci/controller/dwc/pcie-designware-host.c index 4a16306cec257..9e8a9cfc6d3a7 100644 --- a/drivers/pci/controller/dwc/pcie-designware-host.c +++ b/drivers/pci/controller/dwc/pcie-designware-host.c @@ -473,10 +473,8 @@ int dw_pcie_host_init(struct pcie_port *pp) goto err_free_msi; } - pp->root_bus_nr = pp->busn->start; - bridge->sysdata = pp; - bridge->busnr = pp->root_bus_nr; + bridge->busnr = pp->busn->start; bridge->ops = &dw_pcie_ops; bridge->map_irq = of_irq_parse_and_map_pci; bridge->swizzle_irq = pci_common_swizzle; @@ -528,7 +526,7 @@ static int dw_pcie_access_other_conf(struct pcie_port *pp, struct pci_bus *bus, busdev = PCIE_ATU_BUS(bus->number) | PCIE_ATU_DEV(PCI_SLOT(devfn)) | PCIE_ATU_FUNC(PCI_FUNC(devfn)); - if (bus->parent->number == pp->root_bus_nr) { + if (pci_is_root_bus(bus->parent)) { type = PCIE_ATU_TYPE_CFG0; cpu_addr = pp->cfg0_base; cfg_size = pp->cfg0_size; @@ -584,13 +582,11 @@ static int dw_pcie_valid_device(struct pcie_port *pp, struct pci_bus *bus, struct dw_pcie *pci = to_dw_pcie_from_pp(pp); /* If there is no link, then there is no device */ - if (bus->number != pp->root_bus_nr) { + if (!pci_is_root_bus(bus)) { if (!dw_pcie_link_up(pci)) return 0; - } - - /* Access only one slot on each root port */ - if (bus->number == pp->root_bus_nr && dev > 0) + } else if (dev > 0) + /* Access only one slot on each root port */ return 0; return 1; @@ -606,7 +602,7 @@ static int dw_pcie_rd_conf(struct pci_bus *bus, u32 devfn, int where, return PCIBIOS_DEVICE_NOT_FOUND; } - if (bus->number == pp->root_bus_nr) + if (pci_is_root_bus(bus)) return dw_pcie_rd_own_conf(pp, where, size, val); return dw_pcie_rd_other_conf(pp, bus, devfn, where, size, val); @@ -620,7 +616,7 @@ static int dw_pcie_wr_conf(struct pci_bus *bus, u32 devfn, if (!dw_pcie_valid_device(pp, bus, PCI_SLOT(devfn))) return PCIBIOS_DEVICE_NOT_FOUND; - if (bus->number == pp->root_bus_nr) + if (pci_is_root_bus(bus)) return dw_pcie_wr_own_conf(pp, where, size, val); return dw_pcie_wr_other_conf(pp, bus, devfn, where, size, val); diff --git a/drivers/pci/controller/dwc/pcie-designware.h b/drivers/pci/controller/dwc/pcie-designware.h index 656e00f8fbeb4..fd2146298b586 100644 --- a/drivers/pci/controller/dwc/pcie-designware.h +++ b/drivers/pci/controller/dwc/pcie-designware.h @@ -173,7 +173,6 @@ struct dw_pcie_host_ops { }; struct pcie_port { - u8 root_bus_nr; u64 cfg0_base; void __iomem *va_cfg0_base; u32 cfg0_size; -- GitLab From 388637d972db7286f56f076596443971dd193292 Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Tue, 21 Jul 2020 20:25:01 -0600 Subject: [PATCH 0802/1476] PCI: mobiveil: Use pci_is_root_bus() to check if bus is root bus Use pci_is_root_bus() rather than tracking the root bus number to determine if the bus is the root bus or not. This removes storing duplicated data as well as the need for the host bridge driver to have to care about the bus numbers in most cases. Link: https://lore.kernel.org/r/20200722022514.1283916-7-robh@kernel.org Signed-off-by: Rob Herring Signed-off-by: Lorenzo Pieralisi Acked-by: Bjorn Helgaas Cc: Karthikeyan Mitran Cc: Hou Zhiqiang Cc: Lorenzo Pieralisi Cc: Bjorn Helgaas --- drivers/pci/controller/mobiveil/pcie-mobiveil-host.c | 10 +++------- drivers/pci/controller/mobiveil/pcie-mobiveil.h | 1 - 2 files changed, 3 insertions(+), 8 deletions(-) diff --git a/drivers/pci/controller/mobiveil/pcie-mobiveil-host.c b/drivers/pci/controller/mobiveil/pcie-mobiveil-host.c index 705542b4bd210..7250b84a7efee 100644 --- a/drivers/pci/controller/mobiveil/pcie-mobiveil-host.c +++ b/drivers/pci/controller/mobiveil/pcie-mobiveil-host.c @@ -29,18 +29,15 @@ static bool mobiveil_pcie_valid_device(struct pci_bus *bus, unsigned int devfn) { - struct mobiveil_pcie *pcie = bus->sysdata; - struct mobiveil_root_port *rp = &pcie->rp; - /* Only one device down on each root port */ - if ((bus->number == rp->root_bus_nr) && (devfn > 0)) + if (pci_is_root_bus(bus) && (devfn > 0)) return false; /* * Do not read more than one device on the bus directly * attached to RC */ - if ((bus->primary == rp->root_bus_nr) && (PCI_SLOT(devfn) > 0)) + if ((bus->primary == to_pci_host_bridge(bus->bridge)->busnr) && (PCI_SLOT(devfn) > 0)) return false; return true; @@ -61,7 +58,7 @@ static void __iomem *mobiveil_pcie_map_bus(struct pci_bus *bus, return NULL; /* RC config access */ - if (bus->number == rp->root_bus_nr) + if (pci_is_root_bus(bus)) return pcie->csr_axi_slave_base + where; /* @@ -606,7 +603,6 @@ int mobiveil_pcie_host_probe(struct mobiveil_pcie *pcie) /* Initialize bridge */ bridge->sysdata = pcie; - bridge->busnr = rp->root_bus_nr; bridge->ops = &mobiveil_pcie_ops; bridge->map_irq = of_irq_parse_and_map_pci; bridge->swizzle_irq = pci_common_swizzle; diff --git a/drivers/pci/controller/mobiveil/pcie-mobiveil.h b/drivers/pci/controller/mobiveil/pcie-mobiveil.h index 767e36a8522da..6082b8afbc31e 100644 --- a/drivers/pci/controller/mobiveil/pcie-mobiveil.h +++ b/drivers/pci/controller/mobiveil/pcie-mobiveil.h @@ -149,7 +149,6 @@ struct mobiveil_rp_ops { }; struct mobiveil_root_port { - char root_bus_nr; void __iomem *config_axi_slave_base; /* endpoint config base */ struct resource *ob_io_res; struct mobiveil_rp_ops *ops; -- GitLab From 77fc9aa9694323b20bd9bde8627dd129d02dbdaa Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Tue, 21 Jul 2020 20:25:02 -0600 Subject: [PATCH 0803/1476] PCI: xilinx-nwl: Use pci_is_root_bus() to check if bus is root bus Use pci_is_root_bus() rather than tracking the root bus number to determine if the bus is the root bus or not. This removes storing duplicated data as well as the need for the host bridge driver to have to care about the bus numbers in most cases. There was also a bug that the pci_host_bridge.busnr is set from root_busno, but root_busno is never set which means the root bus number is always 0 even if the DT said something else. Link: https://lore.kernel.org/r/20200722022514.1283916-8-robh@kernel.org Signed-off-by: Rob Herring Signed-off-by: Lorenzo Pieralisi Acked-by: Bjorn Helgaas Cc: Lorenzo Pieralisi Cc: Bjorn Helgaas Cc: Michal Simek --- drivers/pci/controller/pcie-xilinx-nwl.c | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/drivers/pci/controller/pcie-xilinx-nwl.c b/drivers/pci/controller/pcie-xilinx-nwl.c index 3c747aa4b6d19..566165c18fad1 100644 --- a/drivers/pci/controller/pcie-xilinx-nwl.c +++ b/drivers/pci/controller/pcie-xilinx-nwl.c @@ -166,7 +166,6 @@ struct nwl_pcie { int irq_misc; u32 ecam_value; u8 last_busno; - u8 root_busno; struct nwl_msi msi; struct irq_domain *legacy_irq_domain; raw_spinlock_t leg_mask_lock; @@ -217,13 +216,11 @@ static bool nwl_pcie_valid_device(struct pci_bus *bus, unsigned int devfn) struct nwl_pcie *pcie = bus->sysdata; /* Check link before accessing downstream ports */ - if (bus->number != pcie->root_busno) { + if (!pci_is_root_bus(bus)) { if (!nwl_pcie_link_up(pcie)) return false; - } - - /* Only one device down on each root port */ - if (bus->number == pcie->root_busno && devfn > 0) + } else if (devfn > 0) + /* Only one device down on each root port */ return false; return true; @@ -855,7 +852,6 @@ static int nwl_pcie_probe(struct platform_device *pdev) } bridge->sysdata = pcie; - bridge->busnr = pcie->root_busno; bridge->ops = &nwl_pcie_ops; bridge->map_irq = of_irq_parse_and_map_pci; bridge->swizzle_irq = pci_common_swizzle; -- GitLab From 49169c255c9906ce8fc9570045a4f5519ecd99b8 Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Tue, 21 Jul 2020 20:25:03 -0600 Subject: [PATCH 0804/1476] PCI: xilinx: Use pci_is_root_bus() to check if bus is root bus Use pci_is_root_bus() rather than tracking the root bus number to determine if the bus is the root bus or not. This removes storing duplicated data as well as the need for the host bridge driver to have to care about the bus numbers in most cases. There was also a bug that the root_busno is never set which means the root bus number is always 0 even if the DT said something else. Link: https://lore.kernel.org/r/20200722022514.1283916-9-robh@kernel.org Signed-off-by: Rob Herring Signed-off-by: Lorenzo Pieralisi Acked-by: Bjorn Helgaas Cc: Lorenzo Pieralisi Cc: Bjorn Helgaas Cc: Michal Simek Cc: linux-pci@vger.kernel.org Cc: linux-arm-kernel@lists.infradead.org --- drivers/pci/controller/pcie-xilinx.c | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/drivers/pci/controller/pcie-xilinx.c b/drivers/pci/controller/pcie-xilinx.c index cce67cb8fb2b0..7bf80f68efa97 100644 --- a/drivers/pci/controller/pcie-xilinx.c +++ b/drivers/pci/controller/pcie-xilinx.c @@ -98,7 +98,6 @@ * @reg_base: IO Mapped Register Base * @irq: Interrupt number * @msi_pages: MSI pages - * @root_busno: Root Bus number * @dev: Device pointer * @msi_domain: MSI IRQ domain pointer * @leg_domain: Legacy IRQ domain pointer @@ -108,7 +107,6 @@ struct xilinx_pcie_port { void __iomem *reg_base; u32 irq; unsigned long msi_pages; - u8 root_busno; struct device *dev; struct irq_domain *msi_domain; struct irq_domain *leg_domain; @@ -162,14 +160,13 @@ static bool xilinx_pcie_valid_device(struct pci_bus *bus, unsigned int devfn) struct xilinx_pcie_port *port = bus->sysdata; /* Check if link is up when trying to access downstream ports */ - if (bus->number != port->root_busno) + if (!pci_is_root_bus(bus)) { if (!xilinx_pcie_link_up(port)) return false; - - /* Only one device down on each root port */ - if (bus->number == port->root_busno && devfn > 0) + } else if (devfn > 0) { + /* Only one device down on each root port */ return false; - + } return true; } -- GitLab From d84c572de1a360501d2e439ac632126f5facf59d Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Tue, 21 Jul 2020 20:25:04 -0600 Subject: [PATCH 0805/1476] PCI: rockchip: Use pci_is_root_bus() to check if bus is root bus Use pci_is_root_bus() rather than tracking the root bus number to determine if the bus is the root bus or not. This removes storing duplicated data as well as the need for the host bridge driver to have to care about the bus numbers in most cases. Also, bridge->busnr is never set so effectively the root bus must be 0. This will be fixed by a subsequent commit. Link: https://lore.kernel.org/r/20200722022514.1283916-10-robh@kernel.org Signed-off-by: Rob Herring Signed-off-by: Lorenzo Pieralisi Acked-by: Bjorn Helgaas Cc: Shawn Lin Cc: Lorenzo Pieralisi Cc: Bjorn Helgaas Cc: Heiko Stuebner Cc: linux-rockchip@lists.infradead.org --- drivers/pci/controller/pcie-rockchip-host.c | 17 +++++++---------- drivers/pci/controller/pcie-rockchip.h | 1 - 2 files changed, 7 insertions(+), 11 deletions(-) diff --git a/drivers/pci/controller/pcie-rockchip-host.c b/drivers/pci/controller/pcie-rockchip-host.c index fb88030161f2c..9a30d08976d89 100644 --- a/drivers/pci/controller/pcie-rockchip-host.c +++ b/drivers/pci/controller/pcie-rockchip-host.c @@ -72,14 +72,14 @@ static int rockchip_pcie_valid_device(struct rockchip_pcie *rockchip, struct pci_bus *bus, int dev) { /* access only one slot on each root port */ - if (bus->number == rockchip->root_bus_nr && dev > 0) + if (pci_is_root_bus(bus) && dev > 0) return 0; /* * do not read more than one device on the bus directly attached * to RC's downstream side. */ - if (bus->primary == rockchip->root_bus_nr && dev > 0) + if (pci_is_root_bus(bus->parent) && dev > 0) return 0; return 1; @@ -170,7 +170,7 @@ static int rockchip_pcie_rd_other_conf(struct rockchip_pcie *rockchip, return PCIBIOS_BAD_REGISTER_NUMBER; } - if (bus->parent->number == rockchip->root_bus_nr) + if (pci_is_root_bus(bus->parent)) rockchip_pcie_cfg_configuration_accesses(rockchip, AXI_WRAPPER_TYPE0_CFG); else @@ -201,7 +201,7 @@ static int rockchip_pcie_wr_other_conf(struct rockchip_pcie *rockchip, if (!IS_ALIGNED(busdev, size)) return PCIBIOS_BAD_REGISTER_NUMBER; - if (bus->parent->number == rockchip->root_bus_nr) + if (pci_is_root_bus(bus->parent)) rockchip_pcie_cfg_configuration_accesses(rockchip, AXI_WRAPPER_TYPE0_CFG); else @@ -230,7 +230,7 @@ static int rockchip_pcie_rd_conf(struct pci_bus *bus, u32 devfn, int where, return PCIBIOS_DEVICE_NOT_FOUND; } - if (bus->number == rockchip->root_bus_nr) + if (pci_is_root_bus(bus)) return rockchip_pcie_rd_own_conf(rockchip, where, size, val); return rockchip_pcie_rd_other_conf(rockchip, bus, devfn, where, size, @@ -245,7 +245,7 @@ static int rockchip_pcie_wr_conf(struct pci_bus *bus, u32 devfn, if (!rockchip_pcie_valid_device(rockchip, bus, PCI_SLOT(devfn))) return PCIBIOS_DEVICE_NOT_FOUND; - if (bus->number == rockchip->root_bus_nr) + if (pci_is_root_bus(bus)) return rockchip_pcie_wr_own_conf(rockchip, where, size, val); return rockchip_pcie_wr_other_conf(rockchip, bus, devfn, where, size, @@ -950,7 +950,6 @@ static int rockchip_pcie_probe(struct platform_device *pdev) struct rockchip_pcie *rockchip; struct device *dev = &pdev->dev; struct pci_host_bridge *bridge; - struct resource *bus_res; int err; if (!dev->of_node) @@ -991,12 +990,10 @@ static int rockchip_pcie_probe(struct platform_device *pdev) goto err_deinit_port; err = pci_parse_request_of_pci_ranges(dev, &bridge->windows, - &bridge->dma_ranges, &bus_res); + &bridge->dma_ranges, NULL); if (err) goto err_remove_irq_domain; - rockchip->root_bus_nr = bus_res->start; - err = rockchip_pcie_cfg_atu(rockchip); if (err) goto err_remove_irq_domain; diff --git a/drivers/pci/controller/pcie-rockchip.h b/drivers/pci/controller/pcie-rockchip.h index 4012543bafbee..c7d0178fc8c23 100644 --- a/drivers/pci/controller/pcie-rockchip.h +++ b/drivers/pci/controller/pcie-rockchip.h @@ -298,7 +298,6 @@ struct rockchip_pcie { struct gpio_desc *ep_gpio; u32 lanes; u8 lanes_map; - u8 root_bus_nr; int link_gen; struct device *dev; struct irq_domain *irq_domain; -- GitLab From 6176a5f32751b9531980a2f23dca73d597d182e6 Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Tue, 21 Jul 2020 20:25:05 -0600 Subject: [PATCH 0806/1476] PCI: rcar: Use pci_is_root_bus() to check if bus is root bus Use pci_is_root_bus() rather than tracking the root bus number to determine if the bus is the root bus or not. This removes storing duplicated data as well as the need for the host bridge driver to have to care about the bus numbers in most cases. Link: https://lore.kernel.org/r/20200722022514.1283916-11-robh@kernel.org Signed-off-by: Rob Herring Signed-off-by: Lorenzo Pieralisi Acked-by: Bjorn Helgaas Cc: Marek Vasut Cc: Yoshihiro Shimoda Cc: Lorenzo Pieralisi Cc: Bjorn Helgaas Cc: linux-renesas-soc@vger.kernel.org --- drivers/pci/controller/pcie-rcar-host.c | 19 +++++-------------- 1 file changed, 5 insertions(+), 14 deletions(-) diff --git a/drivers/pci/controller/pcie-rcar-host.c b/drivers/pci/controller/pcie-rcar-host.c index 3a8e749b4904b..bf10f1cf04f43 100644 --- a/drivers/pci/controller/pcie-rcar-host.c +++ b/drivers/pci/controller/pcie-rcar-host.c @@ -54,7 +54,6 @@ struct rcar_pcie_host { struct phy *phy; void __iomem *base; struct list_head resources; - int root_bus_nr; struct clk *bus_clk; struct rcar_msi msi; int (*phy_init_fn)(struct rcar_pcie_host *host); @@ -100,22 +99,14 @@ static int rcar_pcie_config_access(struct rcar_pcie_host *host, if (dev != 0) return PCIBIOS_DEVICE_NOT_FOUND; - if (access_type == RCAR_PCI_ACCESS_READ) { + if (access_type == RCAR_PCI_ACCESS_READ) *data = rcar_pci_read_reg(pcie, PCICONF(index)); - } else { - /* Keep an eye out for changes to the root bus number */ - if (pci_is_root_bus(bus) && (reg == PCI_PRIMARY_BUS)) - host->root_bus_nr = *data & 0xff; - + else rcar_pci_write_reg(pcie, *data, PCICONF(index)); - } return PCIBIOS_SUCCESSFUL; } - if (host->root_bus_nr < 0) - return PCIBIOS_DEVICE_NOT_FOUND; - /* Clear errors */ rcar_pci_write_reg(pcie, rcar_pci_read_reg(pcie, PCIEERRFR), PCIEERRFR); @@ -124,7 +115,7 @@ static int rcar_pcie_config_access(struct rcar_pcie_host *host, PCIE_CONF_DEV(dev) | PCIE_CONF_FUNC(func) | reg, PCIECAR); /* Enable the configuration access */ - if (bus->parent->number == host->root_bus_nr) + if (pci_is_root_bus(bus->parent)) rcar_pci_write_reg(pcie, CONFIG_SEND_ENABLE | TYPE0, PCIECCTLR); else rcar_pci_write_reg(pcie, CONFIG_SEND_ENABLE | TYPE1, PCIECCTLR); @@ -215,6 +206,7 @@ static struct pci_ops rcar_pcie_ops = { static int rcar_pcie_setup(struct list_head *resource, struct rcar_pcie_host *host) { + struct pci_host_bridge *bridge = pci_host_bridge_from_priv(host); struct resource_entry *win; int i = 0; @@ -232,7 +224,7 @@ static int rcar_pcie_setup(struct list_head *resource, i++; break; case IORESOURCE_BUS: - host->root_bus_nr = res->start; + bridge->busnr = res->start; break; default: continue; @@ -338,7 +330,6 @@ static int rcar_pcie_enable(struct rcar_pcie_host *host) pci_add_flags(PCI_REASSIGN_ALL_BUS); bridge->sysdata = host; - bridge->busnr = host->root_bus_nr; bridge->ops = &rcar_pcie_ops; bridge->map_irq = of_irq_parse_and_map_pci; bridge->swizzle_irq = pci_common_swizzle; -- GitLab From 4f5c883d7815fc4b5ab10dac74561c3f01defdad Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Tue, 21 Jul 2020 20:25:06 -0600 Subject: [PATCH 0807/1476] PCI: Move setting pci_host_bridge.busnr out of host drivers Most host drivers only parse the DT bus range to set the root bus number in pci_host_bridge.busnr. The ones that don't set busnr are buggy in that they ignore what's in DT. Let's set busnr in pci_scan_root_bus_bridge() where we already check for the bus resource and remove setting it in host drivers. Link: https://lore.kernel.org/r/20200722022514.1283916-12-robh@kernel.org Signed-off-by: Rob Herring Signed-off-by: Lorenzo Pieralisi Acked-by: Bjorn Helgaas Cc: Jingoo Han Cc: Gustavo Pimentel Cc: Lorenzo Pieralisi Cc: Bjorn Helgaas Cc: Thomas Petazzoni Cc: Will Deacon Cc: Thierry Reding Cc: Jonathan Hunter Cc: Linus Walleij Cc: Ryder Lee Cc: Marek Vasut Cc: Yoshihiro Shimoda Cc: linux-tegra@vger.kernel.org Cc: linux-mediatek@lists.infradead.org Cc: linux-renesas-soc@vger.kernel.org --- drivers/pci/controller/dwc/pcie-designware-host.c | 1 - drivers/pci/controller/pci-aardvark.c | 4 +--- drivers/pci/controller/pci-host-common.c | 1 - drivers/pci/controller/pci-tegra.c | 4 +--- drivers/pci/controller/pci-v3-semi.c | 2 -- drivers/pci/controller/pcie-mediatek.c | 8 +------- drivers/pci/controller/pcie-rcar-host.c | 1 - drivers/pci/probe.c | 1 + 8 files changed, 4 insertions(+), 18 deletions(-) diff --git a/drivers/pci/controller/dwc/pcie-designware-host.c b/drivers/pci/controller/dwc/pcie-designware-host.c index 9e8a9cfc6d3a7..9775558acdc88 100644 --- a/drivers/pci/controller/dwc/pcie-designware-host.c +++ b/drivers/pci/controller/dwc/pcie-designware-host.c @@ -474,7 +474,6 @@ int dw_pcie_host_init(struct pcie_port *pp) } bridge->sysdata = pp; - bridge->busnr = pp->busn->start; bridge->ops = &dw_pcie_ops; bridge->map_irq = of_irq_parse_and_map_pci; bridge->swizzle_irq = pci_common_swizzle; diff --git a/drivers/pci/controller/pci-aardvark.c b/drivers/pci/controller/pci-aardvark.c index e08ed861368fc..36dc2412c66fe 100644 --- a/drivers/pci/controller/pci-aardvark.c +++ b/drivers/pci/controller/pci-aardvark.c @@ -1104,7 +1104,6 @@ static int advk_pcie_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct advk_pcie *pcie; - struct resource *bus; struct pci_host_bridge *bridge; int ret, irq; @@ -1132,12 +1131,11 @@ static int advk_pcie_probe(struct platform_device *pdev) } ret = pci_parse_request_of_pci_ranges(dev, &bridge->windows, - &bridge->dma_ranges, &bus); + &bridge->dma_ranges, NULL); if (ret) { dev_err(dev, "Failed to parse resources\n"); return ret; } - bridge->busnr = bus->start; pcie->reset_gpio = devm_gpiod_get_from_of_node(dev, dev->of_node, "reset-gpios", 0, diff --git a/drivers/pci/controller/pci-host-common.c b/drivers/pci/controller/pci-host-common.c index ad395d7feddc5..e662910fe0328 100644 --- a/drivers/pci/controller/pci-host-common.c +++ b/drivers/pci/controller/pci-host-common.c @@ -77,7 +77,6 @@ int pci_host_common_probe(struct platform_device *pdev) pci_add_flags(PCI_REASSIGN_ALL_BUS); bridge->sysdata = cfg; - bridge->busnr = cfg->busr.start; bridge->ops = (struct pci_ops *)&ops->pci_ops; bridge->map_irq = of_irq_parse_and_map_pci; bridge->swizzle_irq = pci_common_swizzle; diff --git a/drivers/pci/controller/pci-tegra.c b/drivers/pci/controller/pci-tegra.c index 41232bd07c3d1..8f37cbe7657ef 100644 --- a/drivers/pci/controller/pci-tegra.c +++ b/drivers/pci/controller/pci-tegra.c @@ -2668,7 +2668,6 @@ static int tegra_pcie_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; struct pci_host_bridge *host; struct tegra_pcie *pcie; - struct resource *bus; int err; host = devm_pci_alloc_host_bridge(dev, sizeof(*pcie)); @@ -2683,7 +2682,7 @@ static int tegra_pcie_probe(struct platform_device *pdev) INIT_LIST_HEAD(&pcie->ports); pcie->dev = dev; - err = pci_parse_request_of_pci_ranges(dev, &host->windows, NULL, &bus); + err = pci_parse_request_of_pci_ranges(dev, &host->windows, NULL, NULL); if (err) { dev_err(dev, "Getting bridge resources failed\n"); return err; @@ -2712,7 +2711,6 @@ static int tegra_pcie_probe(struct platform_device *pdev) goto pm_runtime_put; } - host->busnr = bus->start; host->ops = &tegra_pcie_ops; host->map_irq = tegra_pcie_map_irq; host->swizzle_irq = pci_common_swizzle; diff --git a/drivers/pci/controller/pci-v3-semi.c b/drivers/pci/controller/pci-v3-semi.c index d79af39e6e606..d2619f583bfbe 100644 --- a/drivers/pci/controller/pci-v3-semi.c +++ b/drivers/pci/controller/pci-v3-semi.c @@ -584,8 +584,6 @@ static int v3_pci_setup_resource(struct v3_pci *v3, } break; case IORESOURCE_BUS: - dev_dbg(dev, "BUS %pR\n", win->res); - host->busnr = win->res->start; break; default: dev_info(dev, "Unknown resource type %lu\n", diff --git a/drivers/pci/controller/pcie-mediatek.c b/drivers/pci/controller/pcie-mediatek.c index a9399b8d2e8b4..ac6dfa0872471 100644 --- a/drivers/pci/controller/pcie-mediatek.c +++ b/drivers/pci/controller/pcie-mediatek.c @@ -209,7 +209,6 @@ struct mtk_pcie_port { * @mem: non-prefetchable memory resource * @ports: pointer to PCIe port information * @soc: pointer to SoC-dependent operations - * @busnr: root bus number */ struct mtk_pcie { struct device *dev; @@ -218,7 +217,6 @@ struct mtk_pcie { struct list_head ports; const struct mtk_pcie_soc *soc; - unsigned int busnr; }; static void mtk_pcie_subsys_powerdown(struct mtk_pcie *pcie) @@ -1031,16 +1029,13 @@ static int mtk_pcie_setup(struct mtk_pcie *pcie) struct mtk_pcie_port *port, *tmp; struct pci_host_bridge *host = pci_host_bridge_from_priv(pcie); struct list_head *windows = &host->windows; - struct resource *bus; int err; err = pci_parse_request_of_pci_ranges(dev, windows, - &host->dma_ranges, &bus); + &host->dma_ranges, NULL); if (err) return err; - pcie->busnr = bus->start; - for_each_available_child_of_node(node, child) { int slot; @@ -1094,7 +1089,6 @@ static int mtk_pcie_probe(struct platform_device *pdev) if (err) return err; - host->busnr = pcie->busnr; host->ops = pcie->soc->ops; host->map_irq = of_irq_parse_and_map_pci; host->swizzle_irq = pci_common_swizzle; diff --git a/drivers/pci/controller/pcie-rcar-host.c b/drivers/pci/controller/pcie-rcar-host.c index bf10f1cf04f43..58f4d339eb0c8 100644 --- a/drivers/pci/controller/pcie-rcar-host.c +++ b/drivers/pci/controller/pcie-rcar-host.c @@ -224,7 +224,6 @@ static int rcar_pcie_setup(struct list_head *resource, i++; break; case IORESOURCE_BUS: - bridge->busnr = res->start; break; default: continue; diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index 5583037dbdfab..f850782efc35f 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -3088,6 +3088,7 @@ int pci_scan_root_bus_bridge(struct pci_host_bridge *bridge) resource_list_for_each_entry(window, &bridge->windows) if (window->res->flags & IORESOURCE_BUS) { + bridge->busnr = window->res->start; found = true; break; } -- GitLab From 06ff98fcc4a7588c7e45833f2092b3a14e66b575 Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Tue, 21 Jul 2020 20:25:07 -0600 Subject: [PATCH 0808/1476] PCI: cadence: Use bridge resources for outbound window setup Instead of parsing 'ranges' from DT again, use the bridge window resources. Link: https://lore.kernel.org/r/20200722022514.1283916-13-robh@kernel.org Signed-off-by: Rob Herring Signed-off-by: Lorenzo Pieralisi Acked-by: Bjorn Helgaas Cc: Tom Joseph Cc: Lorenzo Pieralisi Cc: Bjorn Helgaas --- .../controller/cadence/pcie-cadence-host.c | 37 ++++++++----------- 1 file changed, 16 insertions(+), 21 deletions(-) diff --git a/drivers/pci/controller/cadence/pcie-cadence-host.c b/drivers/pci/controller/cadence/pcie-cadence-host.c index ec41c6d6e4fac..d1788872abb81 100644 --- a/drivers/pci/controller/cadence/pcie-cadence-host.c +++ b/drivers/pci/controller/cadence/pcie-cadence-host.c @@ -104,16 +104,14 @@ static int cdns_pcie_host_init_root_port(struct cdns_pcie_rc *rc) static int cdns_pcie_host_init_address_translation(struct cdns_pcie_rc *rc) { struct cdns_pcie *pcie = &rc->pcie; + struct pci_host_bridge *bridge = pci_host_bridge_from_priv(rc); struct resource *mem_res = pcie->mem_res; struct resource *bus_range = rc->bus_range; struct resource *cfg_res = rc->cfg_res; - struct device *dev = pcie->dev; - struct device_node *np = dev->of_node; - struct of_pci_range_parser parser; - struct of_pci_range range; + struct resource_entry *entry; u32 addr0, addr1, desc1; u64 cpu_addr; - int r, err; + int r; /* * Reserve region 0 for PCI configure space accesses: @@ -132,25 +130,22 @@ static int cdns_pcie_host_init_address_translation(struct cdns_pcie_rc *rc) cdns_pcie_writel(pcie, CDNS_PCIE_AT_OB_REGION_CPU_ADDR0(0), addr0); cdns_pcie_writel(pcie, CDNS_PCIE_AT_OB_REGION_CPU_ADDR1(0), addr1); - err = of_pci_range_parser_init(&parser, np); - if (err) - return err; - r = 1; - for_each_of_pci_range(&parser, &range) { - bool is_io; - - if ((range.flags & IORESOURCE_TYPE_BITS) == IORESOURCE_MEM) - is_io = false; - else if ((range.flags & IORESOURCE_TYPE_BITS) == IORESOURCE_IO) - is_io = true; + resource_list_for_each_entry(entry, &bridge->windows) { + struct resource *res = entry->res; + u64 pci_addr = res->start - entry->offset; + + if (resource_type(res) == IORESOURCE_IO) + cdns_pcie_set_outbound_region(pcie, 0, r, true, + pci_pio_to_address(res->start), + pci_addr, + resource_size(res)); else - continue; + cdns_pcie_set_outbound_region(pcie, 0, r, false, + res->start, + pci_addr, + resource_size(res)); - cdns_pcie_set_outbound_region(pcie, 0, r, is_io, - range.cpu_addr, - range.pci_addr, - range.size); r++; } -- GitLab From ec64e2795988322bf33aaf0f0935e0f1b4da0ed2 Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Tue, 21 Jul 2020 20:25:08 -0600 Subject: [PATCH 0809/1476] PCI: cadence: Remove private bus number and range storage There's no need to store the bus number or range resource as the driver only needs the bus number which is already in the pci_host_bridge. For endpoint mode, the bus number is always 0. Link: https://lore.kernel.org/r/20200722022514.1283916-14-robh@kernel.org Signed-off-by: Rob Herring Signed-off-by: Lorenzo Pieralisi Acked-by: Bjorn Helgaas Cc: Tom Joseph Cc: Lorenzo Pieralisi Cc: Bjorn Helgaas --- .../pci/controller/cadence/pcie-cadence-ep.c | 6 ++--- .../controller/cadence/pcie-cadence-host.c | 27 +++++++++---------- drivers/pci/controller/cadence/pcie-cadence.c | 9 ++++--- drivers/pci/controller/cadence/pcie-cadence.h | 8 +++--- 4 files changed, 24 insertions(+), 26 deletions(-) diff --git a/drivers/pci/controller/cadence/pcie-cadence-ep.c b/drivers/pci/controller/cadence/pcie-cadence-ep.c index 74ffa03fde5f5..e208fa5904bc7 100644 --- a/drivers/pci/controller/cadence/pcie-cadence-ep.c +++ b/drivers/pci/controller/cadence/pcie-cadence-ep.c @@ -156,7 +156,7 @@ static int cdns_pcie_ep_map_addr(struct pci_epc *epc, u8 fn, phys_addr_t addr, return -EINVAL; } - cdns_pcie_set_outbound_region(pcie, fn, r, false, addr, pci_addr, size); + cdns_pcie_set_outbound_region(pcie, 0, fn, r, false, addr, pci_addr, size); set_bit(r, &ep->ob_region_map); ep->ob_addr[r] = addr; @@ -239,7 +239,7 @@ static void cdns_pcie_ep_assert_intx(struct cdns_pcie_ep *ep, u8 fn, if (unlikely(ep->irq_pci_addr != CDNS_PCIE_EP_IRQ_PCI_ADDR_LEGACY || ep->irq_pci_fn != fn)) { /* First region was reserved for IRQ writes. */ - cdns_pcie_set_outbound_region_for_normal_msg(pcie, fn, 0, + cdns_pcie_set_outbound_region_for_normal_msg(pcie, 0, fn, 0, ep->irq_phys_addr); ep->irq_pci_addr = CDNS_PCIE_EP_IRQ_PCI_ADDR_LEGACY; ep->irq_pci_fn = fn; @@ -318,7 +318,7 @@ static int cdns_pcie_ep_send_msi_irq(struct cdns_pcie_ep *ep, u8 fn, if (unlikely(ep->irq_pci_addr != (pci_addr & ~pci_addr_mask) || ep->irq_pci_fn != fn)) { /* First region was reserved for IRQ writes. */ - cdns_pcie_set_outbound_region(pcie, fn, 0, + cdns_pcie_set_outbound_region(pcie, 0, fn, 0, false, ep->irq_phys_addr, pci_addr & ~pci_addr_mask, diff --git a/drivers/pci/controller/cadence/pcie-cadence-host.c b/drivers/pci/controller/cadence/pcie-cadence-host.c index d1788872abb81..39a8a7a3051e7 100644 --- a/drivers/pci/controller/cadence/pcie-cadence-host.c +++ b/drivers/pci/controller/cadence/pcie-cadence-host.c @@ -20,7 +20,7 @@ static void __iomem *cdns_pci_map_bus(struct pci_bus *bus, unsigned int devfn, unsigned int busn = bus->number; u32 addr0, desc0; - if (busn == rc->bus_range->start) { + if (pci_is_root_bus(bus)) { /* * Only the root port (devfn == 0) is connected to this bus. * All other PCI devices are behind some bridge hence on another @@ -50,7 +50,7 @@ static void __iomem *cdns_pci_map_bus(struct pci_bus *bus, unsigned int devfn, * The bus number was already set once for all in desc1 by * cdns_pcie_host_init_address_translation(). */ - if (busn == rc->bus_range->start + 1) + if (busn == bridge->busnr + 1) desc0 |= CDNS_PCIE_AT_OB_REGION_DESC0_TYPE_CONF_TYPE0; else desc0 |= CDNS_PCIE_AT_OB_REGION_DESC0_TYPE_CONF_TYPE1; @@ -106,12 +106,15 @@ static int cdns_pcie_host_init_address_translation(struct cdns_pcie_rc *rc) struct cdns_pcie *pcie = &rc->pcie; struct pci_host_bridge *bridge = pci_host_bridge_from_priv(rc); struct resource *mem_res = pcie->mem_res; - struct resource *bus_range = rc->bus_range; struct resource *cfg_res = rc->cfg_res; struct resource_entry *entry; u32 addr0, addr1, desc1; u64 cpu_addr; - int r; + int r, busnr = 0; + + entry = resource_list_first_type(&bridge->windows, IORESOURCE_BUS); + if (entry) + busnr = entry->res->start; /* * Reserve region 0 for PCI configure space accesses: @@ -119,7 +122,7 @@ static int cdns_pcie_host_init_address_translation(struct cdns_pcie_rc *rc) * cdns_pci_map_bus(), other region registers are set here once for all. */ addr1 = 0; /* Should be programmed to zero. */ - desc1 = CDNS_PCIE_AT_OB_REGION_DESC1_BUS(bus_range->start); + desc1 = CDNS_PCIE_AT_OB_REGION_DESC1_BUS(busnr); cdns_pcie_writel(pcie, CDNS_PCIE_AT_OB_REGION_PCI_ADDR1(0), addr1); cdns_pcie_writel(pcie, CDNS_PCIE_AT_OB_REGION_DESC1(0), desc1); @@ -136,12 +139,14 @@ static int cdns_pcie_host_init_address_translation(struct cdns_pcie_rc *rc) u64 pci_addr = res->start - entry->offset; if (resource_type(res) == IORESOURCE_IO) - cdns_pcie_set_outbound_region(pcie, 0, r, true, + cdns_pcie_set_outbound_region(pcie, busnr, 0, r, + true, pci_pio_to_address(res->start), pci_addr, resource_size(res)); else - cdns_pcie_set_outbound_region(pcie, 0, r, false, + cdns_pcie_set_outbound_region(pcie, busnr, 0, r, + false, res->start, pci_addr, resource_size(res)); @@ -167,18 +172,13 @@ static int cdns_pcie_host_init(struct device *dev, struct cdns_pcie_rc *rc) { struct pci_host_bridge *bridge = pci_host_bridge_from_priv(rc); - struct resource *bus_range = NULL; int err; /* Parse our PCI ranges and request their resources */ - err = pci_parse_request_of_pci_ranges(dev, &bridge->windows, NULL, - &bus_range); + err = pci_parse_request_of_pci_ranges(dev, &bridge->windows, NULL, NULL); if (err) return err; - rc->bus_range = bus_range; - rc->pcie.bus = bus_range->start; - err = cdns_pcie_host_init_root_port(rc); if (err) return err; @@ -236,7 +236,6 @@ int cdns_pcie_host_setup(struct cdns_pcie_rc *rc) if (ret) goto err_init; - bridge->busnr = pcie->bus; bridge->ops = &cdns_pcie_host_ops; bridge->map_irq = of_irq_parse_and_map_pci; bridge->swizzle_irq = pci_common_swizzle; diff --git a/drivers/pci/controller/cadence/pcie-cadence.c b/drivers/pci/controller/cadence/pcie-cadence.c index cd795f6fc1e23..fdd13765ee759 100644 --- a/drivers/pci/controller/cadence/pcie-cadence.c +++ b/drivers/pci/controller/cadence/pcie-cadence.c @@ -7,7 +7,7 @@ #include "pcie-cadence.h" -void cdns_pcie_set_outbound_region(struct cdns_pcie *pcie, u8 fn, +void cdns_pcie_set_outbound_region(struct cdns_pcie *pcie, u8 busnr, u8 fn, u32 r, bool is_io, u64 cpu_addr, u64 pci_addr, size_t size) { @@ -60,7 +60,7 @@ void cdns_pcie_set_outbound_region(struct cdns_pcie *pcie, u8 fn, /* The device and function numbers are always 0. */ desc0 |= CDNS_PCIE_AT_OB_REGION_DESC0_HARDCODED_RID | CDNS_PCIE_AT_OB_REGION_DESC0_DEVFN(0); - desc1 |= CDNS_PCIE_AT_OB_REGION_DESC1_BUS(pcie->bus); + desc1 |= CDNS_PCIE_AT_OB_REGION_DESC1_BUS(busnr); } else { /* * Use captured values for bus and device numbers but still @@ -82,7 +82,8 @@ void cdns_pcie_set_outbound_region(struct cdns_pcie *pcie, u8 fn, cdns_pcie_writel(pcie, CDNS_PCIE_AT_OB_REGION_CPU_ADDR1(r), addr1); } -void cdns_pcie_set_outbound_region_for_normal_msg(struct cdns_pcie *pcie, u8 fn, +void cdns_pcie_set_outbound_region_for_normal_msg(struct cdns_pcie *pcie, + u8 busnr, u8 fn, u32 r, u64 cpu_addr) { u32 addr0, addr1, desc0, desc1; @@ -94,7 +95,7 @@ void cdns_pcie_set_outbound_region_for_normal_msg(struct cdns_pcie *pcie, u8 fn, if (pcie->is_rc) { desc0 |= CDNS_PCIE_AT_OB_REGION_DESC0_HARDCODED_RID | CDNS_PCIE_AT_OB_REGION_DESC0_DEVFN(0); - desc1 |= CDNS_PCIE_AT_OB_REGION_DESC1_BUS(pcie->bus); + desc1 |= CDNS_PCIE_AT_OB_REGION_DESC1_BUS(busnr); } else { desc0 |= CDNS_PCIE_AT_OB_REGION_DESC0_DEVFN(fn); } diff --git a/drivers/pci/controller/cadence/pcie-cadence.h b/drivers/pci/controller/cadence/pcie-cadence.h index df14ad002fe92..02803f262f0ca 100644 --- a/drivers/pci/controller/cadence/pcie-cadence.h +++ b/drivers/pci/controller/cadence/pcie-cadence.h @@ -235,7 +235,6 @@ struct cdns_pcie { struct resource *mem_res; struct device *dev; bool is_rc; - u8 bus; int phy_count; struct phy **phy; struct device_link **link; @@ -248,7 +247,6 @@ struct cdns_pcie { * @dev: pointer to PCIe device * @cfg_res: start/end offsets in the physical system memory to map PCI * configuration space accesses - * @bus_range: first/last buses behind the PCIe host controller * @cfg_base: IO mapped window to access the PCI configuration space of a * single function at a time * @no_bar_nbits: Number of bits to keep for inbound (PCIe -> CPU) address @@ -259,7 +257,6 @@ struct cdns_pcie { struct cdns_pcie_rc { struct cdns_pcie pcie; struct resource *cfg_res; - struct resource *bus_range; void __iomem *cfg_base; u32 no_bar_nbits; u32 vendor_id; @@ -381,11 +378,12 @@ static inline int cdns_pcie_ep_setup(struct cdns_pcie_ep *ep) return 0; } #endif -void cdns_pcie_set_outbound_region(struct cdns_pcie *pcie, u8 fn, +void cdns_pcie_set_outbound_region(struct cdns_pcie *pcie, u8 busnr, u8 fn, u32 r, bool is_io, u64 cpu_addr, u64 pci_addr, size_t size); -void cdns_pcie_set_outbound_region_for_normal_msg(struct cdns_pcie *pcie, u8 fn, +void cdns_pcie_set_outbound_region_for_normal_msg(struct cdns_pcie *pcie, + u8 busnr, u8 fn, u32 r, u64 cpu_addr); void cdns_pcie_reset_outbound_region(struct cdns_pcie *pcie, u32 r); -- GitLab From 61f11f8250e2dc7acde8d9e09ddae3a70cbe1a6f Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Tue, 21 Jul 2020 20:25:09 -0600 Subject: [PATCH 0810/1476] PCI: rcar: Use devm_pci_alloc_host_bridge() Move to the resource managed devm_pci_alloc_host_bridge() and simplify the error path. Link: https://lore.kernel.org/r/20200722022514.1283916-15-robh@kernel.org Signed-off-by: Rob Herring Signed-off-by: Lorenzo Pieralisi Acked-by: Bjorn Helgaas Cc: Marek Vasut Cc: Yoshihiro Shimoda Cc: Lorenzo Pieralisi Cc: Bjorn Helgaas Cc: linux-renesas-soc@vger.kernel.org --- drivers/pci/controller/pcie-rcar-host.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/drivers/pci/controller/pcie-rcar-host.c b/drivers/pci/controller/pcie-rcar-host.c index 58f4d339eb0c8..c470bff5af0f2 100644 --- a/drivers/pci/controller/pcie-rcar-host.c +++ b/drivers/pci/controller/pcie-rcar-host.c @@ -940,7 +940,7 @@ static int rcar_pcie_probe(struct platform_device *pdev) int err; struct pci_host_bridge *bridge; - bridge = pci_alloc_host_bridge(sizeof(*host)); + bridge = devm_pci_alloc_host_bridge(dev, sizeof(*host)); if (!bridge) return -ENOMEM; @@ -952,7 +952,7 @@ static int rcar_pcie_probe(struct platform_device *pdev) err = pci_parse_request_of_pci_ranges(dev, &host->resources, &bridge->dma_ranges, NULL); if (err) - goto err_free_bridge; + return err; pm_runtime_enable(pcie->dev); err = pm_runtime_get_sync(pcie->dev); @@ -1034,9 +1034,6 @@ err_pm_disable: pm_runtime_disable(dev); pci_free_resource_list(&host->resources); -err_free_bridge: - pci_free_host_bridge(bridge); - return err; } -- GitLab From b411b2e1adb9aa81d75799272dd484c8045f0b88 Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Tue, 21 Jul 2020 20:25:10 -0600 Subject: [PATCH 0811/1476] PCI: rcar: Use struct pci_host_bridge.windows list directly There's no need to create a temporary resource list and then splice it to struct pci_host_bridge.windows list. Just use pci_host_bridge.windows directly. The necessary clean-up is already handled by the PCI core. Link: https://lore.kernel.org/r/20200722022514.1283916-16-robh@kernel.org Signed-off-by: Rob Herring Signed-off-by: Lorenzo Pieralisi Acked-by: Bjorn Helgaas Cc: Marek Vasut Cc: Yoshihiro Shimoda Cc: Lorenzo Pieralisi Cc: Bjorn Helgaas Cc: linux-pci@vger.kernel.org Cc: linux-renesas-soc@vger.kernel.org --- drivers/pci/controller/pcie-rcar-host.c | 45 +++---------------------- 1 file changed, 4 insertions(+), 41 deletions(-) diff --git a/drivers/pci/controller/pcie-rcar-host.c b/drivers/pci/controller/pcie-rcar-host.c index c470bff5af0f2..fa7b893789047 100644 --- a/drivers/pci/controller/pcie-rcar-host.c +++ b/drivers/pci/controller/pcie-rcar-host.c @@ -53,7 +53,6 @@ struct rcar_pcie_host { struct device *dev; struct phy *phy; void __iomem *base; - struct list_head resources; struct clk *bus_clk; struct rcar_msi msi; int (*phy_init_fn)(struct rcar_pcie_host *host); @@ -203,38 +202,6 @@ static struct pci_ops rcar_pcie_ops = { .write = rcar_pcie_write_conf, }; -static int rcar_pcie_setup(struct list_head *resource, - struct rcar_pcie_host *host) -{ - struct pci_host_bridge *bridge = pci_host_bridge_from_priv(host); - struct resource_entry *win; - int i = 0; - - /* Setup PCI resources */ - resource_list_for_each_entry(win, &host->resources) { - struct resource *res = win->res; - - if (!res->flags) - continue; - - switch (resource_type(res)) { - case IORESOURCE_IO: - case IORESOURCE_MEM: - rcar_pcie_set_outbound(&host->pcie, i, win); - i++; - break; - case IORESOURCE_BUS: - break; - default: - continue; - } - - pci_add_resource(resource, res); - } - - return 1; -} - static void rcar_pcie_force_speedup(struct rcar_pcie *pcie) { struct device *dev = pcie->dev; @@ -292,6 +259,7 @@ done: static void rcar_pcie_hw_enable(struct rcar_pcie_host *host) { struct rcar_pcie *pcie = &host->pcie; + struct pci_host_bridge *bridge = pci_host_bridge_from_priv(host); struct resource_entry *win; LIST_HEAD(res); int i = 0; @@ -300,7 +268,7 @@ static void rcar_pcie_hw_enable(struct rcar_pcie_host *host) rcar_pcie_force_speedup(pcie); /* Setup PCI resources */ - resource_list_for_each_entry(win, &host->resources) { + resource_list_for_each_entry(win, &bridge->windows) { struct resource *res = win->res; if (!res->flags) @@ -319,12 +287,8 @@ static void rcar_pcie_hw_enable(struct rcar_pcie_host *host) static int rcar_pcie_enable(struct rcar_pcie_host *host) { struct pci_host_bridge *bridge = pci_host_bridge_from_priv(host); - struct rcar_pcie *pcie = &host->pcie; - /* Try setting 5 GT/s link speed */ - rcar_pcie_force_speedup(pcie); - - rcar_pcie_setup(&bridge->windows, host); + rcar_pcie_hw_enable(host); pci_add_flags(PCI_REASSIGN_ALL_BUS); @@ -949,7 +913,7 @@ static int rcar_pcie_probe(struct platform_device *pdev) pcie->dev = dev; platform_set_drvdata(pdev, host); - err = pci_parse_request_of_pci_ranges(dev, &host->resources, + err = pci_parse_request_of_pci_ranges(dev, &bridge->windows, &bridge->dma_ranges, NULL); if (err) return err; @@ -1032,7 +996,6 @@ err_pm_put: err_pm_disable: pm_runtime_disable(dev); - pci_free_resource_list(&host->resources); return err; } -- GitLab From 4cb18d13b01142d54a5fab66a2e9f562c08bc23b Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Tue, 21 Jul 2020 20:25:11 -0600 Subject: [PATCH 0812/1476] PCI: of: Reduce missing non-prefetchable memory region to a warning The pci-rcar-gen2 controller requires only a prefetchable memory region, and the error prevents using pci_parse_request_of_pci_ranges() for it. Let's reduce this to just a warning message so this function can be used for pci-rcar-gen2. Link: https://lore.kernel.org/r/20200722022514.1283916-17-robh@kernel.org Signed-off-by: Rob Herring Signed-off-by: Lorenzo Pieralisi Acked-by: Bjorn Helgaas Cc: Bjorn Helgaas Cc: Lorenzo Pieralisi --- drivers/pci/of.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/drivers/pci/of.c b/drivers/pci/of.c index 27839cd2459f6..cfb940c8b3995 100644 --- a/drivers/pci/of.c +++ b/drivers/pci/of.c @@ -564,11 +564,10 @@ int pci_parse_request_of_pci_ranges(struct device *dev, } } - if (res_valid) - return 0; + if (!res_valid) + dev_warn(dev, "non-prefetchable memory resource required\n"); - dev_err(dev, "non-prefetchable memory resource required\n"); - err = -EINVAL; + return 0; out_release_res: pci_free_resource_list(resources); -- GitLab From 70cc062c47e7851335ff4c44ba9b362174baf7d4 Mon Sep 17 00:00:00 2001 From: Michael Ellerman Date: Fri, 24 Jul 2020 09:02:26 +1000 Subject: [PATCH 0813/1476] powerpc/test_emulate_sstep: Fix build error ppc64_book3e_allmodconfig fails with: arch/powerpc/lib/test_emulate_step.c: In function 'test_pld': arch/powerpc/lib/test_emulate_step.c:113:7: error: implicit declaration of function 'cpu_has_feature' 113 | if (!cpu_has_feature(CPU_FTR_ARCH_31)) { | ^~~~~~~~~~~~~~~ Add an include of cpu_has_feature.h to fix it. Fixes: b6b54b42722a ("powerpc/sstep: Add tests for prefixed integer load/stores") Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20200724004109.1461709-1-mpe@ellerman.id.au --- arch/powerpc/lib/test_emulate_step.c | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/powerpc/lib/test_emulate_step.c b/arch/powerpc/lib/test_emulate_step.c index 081b05480c477..d242e9f72e0c7 100644 --- a/arch/powerpc/lib/test_emulate_step.c +++ b/arch/powerpc/lib/test_emulate_step.c @@ -8,6 +8,7 @@ #define pr_fmt(fmt) "emulate_step_test: " fmt #include +#include #include #include #include -- GitLab From 826b07b190c8ca69ce674f13b4dc9be2bc536fcd Mon Sep 17 00:00:00 2001 From: Michael Ellerman Date: Fri, 24 Jul 2020 20:42:54 +1000 Subject: [PATCH 0814/1476] powerpc/sstep: Fix incorrect CONFIG symbol in scv handling When I "fixed" the ppc64e build in Nick's recent patch, I typoed the CONFIG symbol, resulting in one that doesn't exist. Fix it to use the correct symbol. Reported-by: Christophe Leroy Fixes: 7fa95f9adaee ("powerpc/64s: system call support for scv/rfscv instructions") Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20200724131609.1640533-1-mpe@ellerman.id.au --- arch/powerpc/lib/sstep.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/powerpc/lib/sstep.c b/arch/powerpc/lib/sstep.c index 4194119eff82e..c58ea9e787cb6 100644 --- a/arch/powerpc/lib/sstep.c +++ b/arch/powerpc/lib/sstep.c @@ -3382,7 +3382,7 @@ int emulate_step(struct pt_regs *regs, struct ppc_inst instr) regs->msr = MSR_KERNEL; return 1; -#ifdef CONFIG_PPC64_BOOK3S +#ifdef CONFIG_PPC_BOOK3S_64 case SYSCALL_VECTORED_0: /* scv 0 */ regs->gpr[9] = regs->gpr[13]; regs->gpr[10] = MSR_KERNEL; -- GitLab From 1c026a18d40bdd904d3ae1f7737cd08b376286d6 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Thu, 11 Jun 2020 13:31:34 +0100 Subject: [PATCH 0815/1476] xen: Remove redundant initialization of irq The variable irq is being initialized with a value that is never read and it is being updated later with a new value. The initialization is redundant and can be removed. Addresses-Coverity: ("Unused value") Link: https://lore.kernel.org/r/20200611123134.922395-1-colin.king@canonical.com Signed-off-by: Colin Ian King Signed-off-by: Bjorn Helgaas --- arch/x86/pci/xen.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/x86/pci/xen.c b/arch/x86/pci/xen.c index e3f1ca3160684..9f9aad42ccff5 100644 --- a/arch/x86/pci/xen.c +++ b/arch/x86/pci/xen.c @@ -62,7 +62,7 @@ static int xen_pcifront_enable_irq(struct pci_dev *dev) #ifdef CONFIG_ACPI static int xen_register_pirq(u32 gsi, int triggering, bool set_pirq) { - int rc, pirq = -1, irq = -1; + int rc, pirq = -1, irq; struct physdev_map_pirq map_irq; int shareable = 0; char *name; -- GitLab From 3f9a7a13fe4cb6e119e4e4745fbf975d30bfac9b Mon Sep 17 00:00:00 2001 From: Ashok Raj Date: Thu, 23 Jul 2020 15:37:29 -0700 Subject: [PATCH 0816/1476] PCI/ATS: Add pci_pri_supported() to check device or associated PF For SR-IOV, the PF PRI is shared between the PF and any associated VFs, and the PRI Capability is allowed for PFs but not for VFs. Searching for the PRI Capability on a VF always fails, even if its associated PF supports PRI. Add pci_pri_supported() to check whether device or its associated PF supports PRI. [bhelgaas: commit log, avoid "!!"] Fixes: b16d0cb9e2fc ("iommu/vt-d: Always enable PASID/PRI PCI capabilities before ATS") Link: https://lore.kernel.org/r/1595543849-19692-1-git-send-email-ashok.raj@intel.com Signed-off-by: Ashok Raj Signed-off-by: Bjorn Helgaas Reviewed-by: Lu Baolu Acked-by: Joerg Roedel Cc: stable@vger.kernel.org # v4.4+ --- drivers/iommu/intel/iommu.c | 2 +- drivers/pci/ats.c | 15 +++++++++++++++ include/linux/pci-ats.h | 4 ++++ 3 files changed, 20 insertions(+), 1 deletion(-) diff --git a/drivers/iommu/intel/iommu.c b/drivers/iommu/intel/iommu.c index 9129663a7406b..5552e7d5d2b11 100644 --- a/drivers/iommu/intel/iommu.c +++ b/drivers/iommu/intel/iommu.c @@ -2554,7 +2554,7 @@ static struct dmar_domain *dmar_insert_one_dev_info(struct intel_iommu *iommu, } if (info->ats_supported && ecap_prs(iommu->ecap) && - pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_PRI)) + pci_pri_supported(pdev)) info->pri_supported = 1; } } diff --git a/drivers/pci/ats.c b/drivers/pci/ats.c index b761c1f72f672..647e097530a89 100644 --- a/drivers/pci/ats.c +++ b/drivers/pci/ats.c @@ -325,6 +325,21 @@ int pci_prg_resp_pasid_required(struct pci_dev *pdev) return pdev->pasid_required; } + +/** + * pci_pri_supported - Check if PRI is supported. + * @pdev: PCI device structure + * + * Returns true if PRI capability is present, false otherwise. + */ +bool pci_pri_supported(struct pci_dev *pdev) +{ + /* VFs share the PF PRI */ + if (pci_physfn(pdev)->pri_cap) + return true; + return false; +} +EXPORT_SYMBOL_GPL(pci_pri_supported); #endif /* CONFIG_PCI_PRI */ #ifdef CONFIG_PCI_PASID diff --git a/include/linux/pci-ats.h b/include/linux/pci-ats.h index f75c307f346de..df54cd5b15db0 100644 --- a/include/linux/pci-ats.h +++ b/include/linux/pci-ats.h @@ -28,6 +28,10 @@ int pci_enable_pri(struct pci_dev *pdev, u32 reqs); void pci_disable_pri(struct pci_dev *pdev); int pci_reset_pri(struct pci_dev *pdev); int pci_prg_resp_pasid_required(struct pci_dev *pdev); +bool pci_pri_supported(struct pci_dev *pdev); +#else +static inline bool pci_pri_supported(struct pci_dev *pdev) +{ return false; } #endif /* CONFIG_PCI_PRI */ #ifdef CONFIG_PCI_PASID -- GitLab From 269e829f48a0d0d27667abe25ca5c9e5b6ab08e2 Mon Sep 17 00:00:00 2001 From: "Aneesh Kumar K.V" Date: Sun, 26 Jul 2020 18:55:17 +0530 Subject: [PATCH 0817/1476] powerpc/book3s64/pkey: Disable pkey on POWER6 and before POWER6 only supports AMR update via privileged mode (MSR[PR] = 0, SPRN_AMR=29) The PR=1 (userspace) alias for that SPR (SPRN_AMR=13) was only supported from POWER7. Since we don't allow userspace modifying of AMR value we should disable pkey support on P6 and before. The hypervisor will still report pkey support via "ibm,processor-storage-keys". Hence also check for P7 CPU_FTR bit to decide on pkey support. Fixes: f491fe3fb41e ("powerpc/book3s64/pkeys: Simplify the key initialization") Reported-by: Michael Ellerman Signed-off-by: Aneesh Kumar K.V Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20200726132517.399076-1-aneesh.kumar@linux.ibm.com --- arch/powerpc/mm/book3s64/pkeys.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/arch/powerpc/mm/book3s64/pkeys.c b/arch/powerpc/mm/book3s64/pkeys.c index 792b36aa9619e..69a6b87f2bb4e 100644 --- a/arch/powerpc/mm/book3s64/pkeys.c +++ b/arch/powerpc/mm/book3s64/pkeys.c @@ -73,6 +73,12 @@ static int scan_pkey_feature(void) if (early_radix_enabled()) return 0; + /* + * Only P7 and above supports SPRN_AMR update with MSR[PR] = 1 + */ + if (!early_cpu_has_feature(CPU_FTR_ARCH_206)) + return 0; + ret = of_scan_flat_dt(dt_scan_storage_keys, &pkeys_total); if (ret == 0) { /* -- GitLab From 3190ecbfeeb2ab17778887ce3fa964615d6460fd Mon Sep 17 00:00:00 2001 From: Ravi Bangoria Date: Thu, 23 Jul 2020 14:38:04 +0530 Subject: [PATCH 0818/1476] powerpc/watchpoint: Fix 512 byte boundary limit Milton Miller reported that we are aligning start and end address to wrong size SZ_512M. It should be SZ_512. Fix that. While doing this change I also found a case where ALIGN() comparison fails. Within a given aligned range, ALIGN() of two addresses does not match when start address is pointing to the first byte and end address is pointing to any other byte except the first one. But that's not true for ALIGN_DOWN(). ALIGN_DOWN() of any two addresses within that range will always point to the first byte. So use ALIGN_DOWN() instead of ALIGN(). Fixes: e68ef121c1f4 ("powerpc/watchpoint: Use builtin ALIGN*() macros") Reported-by: Milton Miller Signed-off-by: Ravi Bangoria Tested-by: Jordan Niethe Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20200723090813.303838-2-ravi.bangoria@linux.ibm.com --- arch/powerpc/kernel/hw_breakpoint.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/powerpc/kernel/hw_breakpoint.c b/arch/powerpc/kernel/hw_breakpoint.c index 0000daf0e1dae..031e6defc08e6 100644 --- a/arch/powerpc/kernel/hw_breakpoint.c +++ b/arch/powerpc/kernel/hw_breakpoint.c @@ -419,7 +419,7 @@ static int hw_breakpoint_validate_len(struct arch_hw_breakpoint *hw) if (dawr_enabled()) { max_len = DAWR_MAX_LEN; /* DAWR region can't cross 512 bytes boundary */ - if (ALIGN(start_addr, SZ_512M) != ALIGN(end_addr - 1, SZ_512M)) + if (ALIGN_DOWN(start_addr, SZ_512) != ALIGN_DOWN(end_addr - 1, SZ_512)) return -EINVAL; } else if (IS_ENABLED(CONFIG_PPC_8xx)) { /* 8xx can setup a range without limitation */ -- GitLab From f6780ce619f8daa285760302d56e95892087bd1f Mon Sep 17 00:00:00 2001 From: Ravi Bangoria Date: Thu, 23 Jul 2020 14:38:05 +0530 Subject: [PATCH 0819/1476] powerpc/watchpoint: Fix DAWR exception constraint Pedro Miraglia Franco de Carvalho noticed that on p8/p9, DAR value is inconsistent with different type of load/store. Like for byte,word etc. load/stores, DAR is set to the address of the first byte of overlap between watch range and real access. But for quadword load/ store it's sometime set to the address of the first byte of real access whereas sometime set to the address of the first byte of overlap. This issue has been fixed in p10. In p10(ISA 3.1), DAR is always set to the address of the first byte of overlap. Commit 27985b2a640e ("powerpc/watchpoint: Don't ignore extraneous exceptions blindly") wrongly assumes that DAR is set to the address of the first byte of overlap for all load/stores on p8/p9 as well. Fix that. With the fix, we now rely on 'ea' provided by analyse_instr(). If analyse_instr() fails, generate event unconditionally on p8/p9, and on p10 generate event only if DAR is within a DAWR range. Note: 8xx is not affected. Fixes: 27985b2a640e ("powerpc/watchpoint: Don't ignore extraneous exceptions blindly") Fixes: 74c6881019b7 ("powerpc/watchpoint: Prepare handler to handle more than one watchpoint") Reported-by: Pedro Miraglia Franco de Carvalho Signed-off-by: Ravi Bangoria Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20200723090813.303838-3-ravi.bangoria@linux.ibm.com --- arch/powerpc/kernel/hw_breakpoint.c | 72 ++++++++++++++++------------- 1 file changed, 41 insertions(+), 31 deletions(-) diff --git a/arch/powerpc/kernel/hw_breakpoint.c b/arch/powerpc/kernel/hw_breakpoint.c index 031e6defc08e6..a971e22aea819 100644 --- a/arch/powerpc/kernel/hw_breakpoint.c +++ b/arch/powerpc/kernel/hw_breakpoint.c @@ -498,11 +498,11 @@ static bool dar_in_user_range(unsigned long dar, struct arch_hw_breakpoint *info return ((info->address <= dar) && (dar - info->address < info->len)); } -static bool dar_user_range_overlaps(unsigned long dar, int size, - struct arch_hw_breakpoint *info) +static bool ea_user_range_overlaps(unsigned long ea, int size, + struct arch_hw_breakpoint *info) { - return ((dar < info->address + info->len) && - (dar + size > info->address)); + return ((ea < info->address + info->len) && + (ea + size > info->address)); } static bool dar_in_hw_range(unsigned long dar, struct arch_hw_breakpoint *info) @@ -515,20 +515,22 @@ static bool dar_in_hw_range(unsigned long dar, struct arch_hw_breakpoint *info) return ((hw_start_addr <= dar) && (hw_end_addr > dar)); } -static bool dar_hw_range_overlaps(unsigned long dar, int size, - struct arch_hw_breakpoint *info) +static bool ea_hw_range_overlaps(unsigned long ea, int size, + struct arch_hw_breakpoint *info) { unsigned long hw_start_addr, hw_end_addr; hw_start_addr = ALIGN_DOWN(info->address, HW_BREAKPOINT_SIZE); hw_end_addr = ALIGN(info->address + info->len, HW_BREAKPOINT_SIZE); - return ((dar < hw_end_addr) && (dar + size > hw_start_addr)); + return ((ea < hw_end_addr) && (ea + size > hw_start_addr)); } /* * If hw has multiple DAWR registers, we also need to check all * dawrx constraint bits to confirm this is _really_ a valid event. + * If type is UNKNOWN, but privilege level matches, consider it as + * a positive match. */ static bool check_dawrx_constraints(struct pt_regs *regs, int type, struct arch_hw_breakpoint *info) @@ -553,7 +555,8 @@ static bool check_dawrx_constraints(struct pt_regs *regs, int type, * including extraneous exception. Otherwise return false. */ static bool check_constraints(struct pt_regs *regs, struct ppc_inst instr, - int type, int size, struct arch_hw_breakpoint *info) + unsigned long ea, int type, int size, + struct arch_hw_breakpoint *info) { bool in_user_range = dar_in_user_range(regs->dar, info); bool dawrx_constraints; @@ -569,22 +572,27 @@ static bool check_constraints(struct pt_regs *regs, struct ppc_inst instr, } if (unlikely(ppc_inst_equal(instr, ppc_inst(0)))) { - if (in_user_range) - return true; + if (cpu_has_feature(CPU_FTR_ARCH_31) && + !dar_in_hw_range(regs->dar, info)) + return false; - if (dar_in_hw_range(regs->dar, info)) { - info->type |= HW_BRK_TYPE_EXTRANEOUS_IRQ; - return true; - } - return false; + return true; } dawrx_constraints = check_dawrx_constraints(regs, type, info); - if (dar_user_range_overlaps(regs->dar, size, info)) + if (type == UNKNOWN) { + if (cpu_has_feature(CPU_FTR_ARCH_31) && + !dar_in_hw_range(regs->dar, info)) + return false; + return dawrx_constraints; + } - if (dar_hw_range_overlaps(regs->dar, size, info)) { + if (ea_user_range_overlaps(ea, size, info)) + return dawrx_constraints; + + if (ea_hw_range_overlaps(ea, size, info)) { if (dawrx_constraints) { info->type |= HW_BRK_TYPE_EXTRANEOUS_IRQ; return true; @@ -594,7 +602,7 @@ static bool check_constraints(struct pt_regs *regs, struct ppc_inst instr, } static void get_instr_detail(struct pt_regs *regs, struct ppc_inst *instr, - int *type, int *size, bool *larx_stcx) + int *type, int *size, unsigned long *ea) { struct instruction_op op; @@ -602,16 +610,18 @@ static void get_instr_detail(struct pt_regs *regs, struct ppc_inst *instr, return; analyse_instr(&op, regs, *instr); - - /* - * Set size = 8 if analyse_instr() fails. If it's a userspace - * watchpoint(valid or extraneous), we can notify user about it. - * If it's a kernel watchpoint, instruction emulation will fail - * in stepping_handler() and watchpoint will be disabled. - */ *type = GETTYPE(op.type); - *size = !(*type == UNKNOWN) ? GETSIZE(op.type) : 8; - *larx_stcx = (*type == LARX || *type == STCX); + *ea = op.ea; +#ifdef __powerpc64__ + if (!(regs->msr & MSR_64BIT)) + *ea &= 0xffffffffUL; +#endif + *size = GETSIZE(op.type); +} + +static bool is_larx_stcx_instr(int type) +{ + return type == LARX || type == STCX; } /* @@ -678,7 +688,7 @@ int hw_breakpoint_handler(struct die_args *args) struct ppc_inst instr = ppc_inst(0); int type = 0; int size = 0; - bool larx_stcx = false; + unsigned long ea; /* Disable breakpoints during exception handling */ hw_breakpoint_disable(); @@ -692,7 +702,7 @@ int hw_breakpoint_handler(struct die_args *args) rcu_read_lock(); if (!IS_ENABLED(CONFIG_PPC_8xx)) - get_instr_detail(regs, &instr, &type, &size, &larx_stcx); + get_instr_detail(regs, &instr, &type, &size, &ea); for (i = 0; i < nr_wp_slots(); i++) { bp[i] = __this_cpu_read(bp_per_reg[i]); @@ -702,7 +712,7 @@ int hw_breakpoint_handler(struct die_args *args) info[i] = counter_arch_bp(bp[i]); info[i]->type &= ~HW_BRK_TYPE_EXTRANEOUS_IRQ; - if (check_constraints(regs, instr, type, size, info[i])) { + if (check_constraints(regs, instr, ea, type, size, info[i])) { if (!IS_ENABLED(CONFIG_PPC_8xx) && ppc_inst_equal(instr, ppc_inst(0))) { handler_error(bp[i], info[i]); @@ -744,7 +754,7 @@ int hw_breakpoint_handler(struct die_args *args) } if (!IS_ENABLED(CONFIG_PPC_8xx)) { - if (larx_stcx) { + if (is_larx_stcx_instr(type)) { for (i = 0; i < nr_wp_slots(); i++) { if (!hit[i]) continue; -- GitLab From f3c832f1350bcf1e6906113ee3168066f4235dbe Mon Sep 17 00:00:00 2001 From: Ravi Bangoria Date: Thu, 23 Jul 2020 14:38:06 +0530 Subject: [PATCH 0820/1476] powerpc/watchpoint: Fix DAWR exception for CACHEOP 'ea' returned by analyse_instr() needs to be aligned down to cache block size for CACHEOP instructions. analyse_instr() does not set size for CACHEOP, thus size also needs to be calculated manually. Fixes: 27985b2a640e ("powerpc/watchpoint: Don't ignore extraneous exceptions blindly") Fixes: 74c6881019b7 ("powerpc/watchpoint: Prepare handler to handle more than one watchpoint") Signed-off-by: Ravi Bangoria Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20200723090813.303838-4-ravi.bangoria@linux.ibm.com --- arch/powerpc/kernel/hw_breakpoint.c | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/arch/powerpc/kernel/hw_breakpoint.c b/arch/powerpc/kernel/hw_breakpoint.c index a971e22aea819..c55e67bab2710 100644 --- a/arch/powerpc/kernel/hw_breakpoint.c +++ b/arch/powerpc/kernel/hw_breakpoint.c @@ -538,7 +538,12 @@ static bool check_dawrx_constraints(struct pt_regs *regs, int type, if (OP_IS_LOAD(type) && !(info->type & HW_BRK_TYPE_READ)) return false; - if (OP_IS_STORE(type) && !(info->type & HW_BRK_TYPE_WRITE)) + /* + * The Cache Management instructions other than dcbz never + * cause a match. i.e. if type is CACHEOP, the instruction + * is dcbz, and dcbz is treated as Store. + */ + if ((OP_IS_STORE(type) || type == CACHEOP) && !(info->type & HW_BRK_TYPE_WRITE)) return false; if (is_kernel_addr(regs->nip) && !(info->type & HW_BRK_TYPE_KERNEL)) @@ -601,6 +606,15 @@ static bool check_constraints(struct pt_regs *regs, struct ppc_inst instr, return false; } +static int cache_op_size(void) +{ +#ifdef __powerpc64__ + return ppc64_caches.l1d.block_size; +#else + return L1_CACHE_BYTES; +#endif +} + static void get_instr_detail(struct pt_regs *regs, struct ppc_inst *instr, int *type, int *size, unsigned long *ea) { @@ -616,7 +630,12 @@ static void get_instr_detail(struct pt_regs *regs, struct ppc_inst *instr, if (!(regs->msr & MSR_64BIT)) *ea &= 0xffffffffUL; #endif + *size = GETSIZE(op.type); + if (*type == CACHEOP) { + *size = cache_op_size(); + *ea &= ~(*size - 1); + } } static bool is_larx_stcx_instr(int type) -- GitLab From 8f460a8175e6d85537d581734e9fa7ef97036b1a Mon Sep 17 00:00:00 2001 From: Ravi Bangoria Date: Thu, 23 Jul 2020 14:38:07 +0530 Subject: [PATCH 0821/1476] powerpc/watchpoint: Enable watchpoint functionality on power10 guest CPU_FTR_DAWR is by default enabled for host via CPU_FTRS_DT_CPU_BASE (controlled by CONFIG_PPC_DT_CPU_FTRS). But cpu-features device-tree node is not PAPR compatible and thus not yet used by kvm or pHyp guests. Enable watchpoint functionality on power10 guest (both kvm and powervm) by adding CPU_FTR_DAWR to CPU_FTRS_POWER10. Note that this change does not enable 2nd DAWR support. Signed-off-by: Ravi Bangoria Tested-by: Jordan Niethe Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20200723090813.303838-5-ravi.bangoria@linux.ibm.com --- arch/powerpc/include/asm/cputable.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/arch/powerpc/include/asm/cputable.h b/arch/powerpc/include/asm/cputable.h index a461c33008047..a0453fca2dff7 100644 --- a/arch/powerpc/include/asm/cputable.h +++ b/arch/powerpc/include/asm/cputable.h @@ -477,7 +477,8 @@ static inline void cpu_feature_keys_init(void) { } CPU_FTR_STCX_CHECKS_ADDRESS | CPU_FTR_POPCNTB | CPU_FTR_POPCNTD | \ CPU_FTR_CFAR | CPU_FTR_HVMODE | CPU_FTR_VMX_COPY | \ CPU_FTR_DBELL | CPU_FTR_HAS_PPR | CPU_FTR_ARCH_207S | \ - CPU_FTR_TM_COMP | CPU_FTR_ARCH_300 | CPU_FTR_ARCH_31) + CPU_FTR_TM_COMP | CPU_FTR_ARCH_300 | CPU_FTR_ARCH_31 | \ + CPU_FTR_DAWR) #define CPU_FTRS_CELL (CPU_FTR_LWSYNC | \ CPU_FTR_PPCAS_ARCH_V2 | CPU_FTR_CTRL | \ CPU_FTR_ALTIVEC_COMP | CPU_FTR_MMCRA | CPU_FTR_SMT | \ -- GitLab From dc1cedca54704d336c333b5398daaf13b23e391b Mon Sep 17 00:00:00 2001 From: Ravi Bangoria Date: Thu, 23 Jul 2020 14:38:08 +0530 Subject: [PATCH 0822/1476] powerpc/dt_cpu_ftrs: Add feature for 2nd DAWR Add new device-tree feature for 2nd DAWR. If this feature is present, 2nd DAWR is supported, otherwise not. Signed-off-by: Ravi Bangoria Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20200723090813.303838-6-ravi.bangoria@linux.ibm.com --- arch/powerpc/include/asm/cputable.h | 3 ++- arch/powerpc/kernel/dt_cpu_ftrs.c | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/arch/powerpc/include/asm/cputable.h b/arch/powerpc/include/asm/cputable.h index a0453fca2dff7..d36db35e3aac6 100644 --- a/arch/powerpc/include/asm/cputable.h +++ b/arch/powerpc/include/asm/cputable.h @@ -214,6 +214,7 @@ static inline void cpu_feature_keys_init(void) { } #define CPU_FTR_P9_TLBIE_ERAT_BUG LONG_ASM_CONST(0x0001000000000000) #define CPU_FTR_P9_RADIX_PREFETCH_BUG LONG_ASM_CONST(0x0002000000000000) #define CPU_FTR_ARCH_31 LONG_ASM_CONST(0x0004000000000000) +#define CPU_FTR_DAWR1 LONG_ASM_CONST(0x0008000000000000) #ifndef __ASSEMBLY__ @@ -478,7 +479,7 @@ static inline void cpu_feature_keys_init(void) { } CPU_FTR_CFAR | CPU_FTR_HVMODE | CPU_FTR_VMX_COPY | \ CPU_FTR_DBELL | CPU_FTR_HAS_PPR | CPU_FTR_ARCH_207S | \ CPU_FTR_TM_COMP | CPU_FTR_ARCH_300 | CPU_FTR_ARCH_31 | \ - CPU_FTR_DAWR) + CPU_FTR_DAWR | CPU_FTR_DAWR1) #define CPU_FTRS_CELL (CPU_FTR_LWSYNC | \ CPU_FTR_PPCAS_ARCH_V2 | CPU_FTR_CTRL | \ CPU_FTR_ALTIVEC_COMP | CPU_FTR_MMCRA | CPU_FTR_SMT | \ diff --git a/arch/powerpc/kernel/dt_cpu_ftrs.c b/arch/powerpc/kernel/dt_cpu_ftrs.c index 22ba7fba11476..6f8c0c6b937a1 100644 --- a/arch/powerpc/kernel/dt_cpu_ftrs.c +++ b/arch/powerpc/kernel/dt_cpu_ftrs.c @@ -686,6 +686,7 @@ static struct dt_cpu_feature_match __initdata {"wait-v3", feat_enable, 0}, {"prefix-instructions", feat_enable, 0}, {"matrix-multiply-assist", feat_enable_mma, 0}, + {"debug-facilities-v31", feat_enable, CPU_FTR_DAWR1}, }; static bool __initdata using_dt_cpu_ftrs; -- GitLab From 8f45ca3f8b87c4810674fbfe65de6d041ee0baee Mon Sep 17 00:00:00 2001 From: Ravi Bangoria Date: Thu, 23 Jul 2020 14:38:09 +0530 Subject: [PATCH 0823/1476] powerpc/watchpoint: Set CPU_FTR_DAWR1 based on pa-features bit As per the PAPR, bit 0 of byte 64 in pa-features property indicates availability of 2nd DAWR registers. i.e. If this bit is set, 2nd DAWR is present, otherwise not. Host generally uses "cpu-features", which masks "pa-features". But "cpu-features" are still not used for guests and thus this change is mostly applicable for guests only. Signed-off-by: Ravi Bangoria Tested-by: Jordan Niethe Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20200723090813.303838-7-ravi.bangoria@linux.ibm.com --- arch/powerpc/kernel/prom.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/arch/powerpc/kernel/prom.c b/arch/powerpc/kernel/prom.c index dfe0103ad1317..5a05c5e993fd0 100644 --- a/arch/powerpc/kernel/prom.c +++ b/arch/powerpc/kernel/prom.c @@ -175,6 +175,8 @@ static struct ibm_pa_feature { */ { .pabyte = 22, .pabit = 0, .cpu_features = CPU_FTR_TM_COMP, .cpu_user_ftrs2 = PPC_FEATURE2_HTM_COMP | PPC_FEATURE2_HTM_NOSC_COMP }, + + { .pabyte = 64, .pabit = 0, .cpu_features = CPU_FTR_DAWR1 }, }; static void __init scan_features(unsigned long node, const unsigned char *ftrs, -- GitLab From 6f3fe297f95134e9b2386dae0067bf530e1ddca0 Mon Sep 17 00:00:00 2001 From: Ravi Bangoria Date: Thu, 23 Jul 2020 14:38:10 +0530 Subject: [PATCH 0824/1476] powerpc/watchpoint: Rename current H_SET_MODE DAWR macro Current H_SET_MODE hcall macro name for setting/resetting DAWR0 is H_SET_MODE_RESOURCE_SET_DAWR. Add suffix 0 to macro name as well. Signed-off-by: Ravi Bangoria Reviewed-by: Jordan Niethe Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20200723090813.303838-8-ravi.bangoria@linux.ibm.com --- arch/powerpc/include/asm/hvcall.h | 2 +- arch/powerpc/include/asm/plpar_wrappers.h | 2 +- arch/powerpc/kvm/book3s_hv.c | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/arch/powerpc/include/asm/hvcall.h b/arch/powerpc/include/asm/hvcall.h index 8d7ee3b533c41..9ab367030f4f9 100644 --- a/arch/powerpc/include/asm/hvcall.h +++ b/arch/powerpc/include/asm/hvcall.h @@ -355,7 +355,7 @@ /* Values for 2nd argument to H_SET_MODE */ #define H_SET_MODE_RESOURCE_SET_CIABR 1 -#define H_SET_MODE_RESOURCE_SET_DAWR 2 +#define H_SET_MODE_RESOURCE_SET_DAWR0 2 #define H_SET_MODE_RESOURCE_ADDR_TRANS_MODE 3 #define H_SET_MODE_RESOURCE_LE 4 diff --git a/arch/powerpc/include/asm/plpar_wrappers.h b/arch/powerpc/include/asm/plpar_wrappers.h index 4293c5d2ddf4a..d12c3680d9469 100644 --- a/arch/powerpc/include/asm/plpar_wrappers.h +++ b/arch/powerpc/include/asm/plpar_wrappers.h @@ -312,7 +312,7 @@ static inline long plpar_set_ciabr(unsigned long ciabr) static inline long plpar_set_watchpoint0(unsigned long dawr0, unsigned long dawrx0) { - return plpar_set_mode(0, H_SET_MODE_RESOURCE_SET_DAWR, dawr0, dawrx0); + return plpar_set_mode(0, H_SET_MODE_RESOURCE_SET_DAWR0, dawr0, dawrx0); } static inline long plpar_signal_sys_reset(long cpu) diff --git a/arch/powerpc/kvm/book3s_hv.c b/arch/powerpc/kvm/book3s_hv.c index 0a0df31987f75..a22ac01fa660c 100644 --- a/arch/powerpc/kvm/book3s_hv.c +++ b/arch/powerpc/kvm/book3s_hv.c @@ -764,7 +764,7 @@ static int kvmppc_h_set_mode(struct kvm_vcpu *vcpu, unsigned long mflags, return H_P3; vcpu->arch.ciabr = value1; return H_SUCCESS; - case H_SET_MODE_RESOURCE_SET_DAWR: + case H_SET_MODE_RESOURCE_SET_DAWR0: if (!kvmppc_power8_compatible(vcpu)) return H_P2; if (!ppc_breakpoint_available()) -- GitLab From 03f3e54abd95061ea11bdb4eedbe3cab6553704f Mon Sep 17 00:00:00 2001 From: Ravi Bangoria Date: Thu, 23 Jul 2020 14:38:11 +0530 Subject: [PATCH 0825/1476] powerpc/watchpoint: Guest support for 2nd DAWR hcall 2nd DAWR can be set/unset using H_SET_MODE hcall with resource value 5. Enable powervm guest support with that. This has no effect on kvm guest because kvm will return error if guest does hcall with resource value 5. Signed-off-by: Ravi Bangoria Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20200723090813.303838-9-ravi.bangoria@linux.ibm.com --- arch/powerpc/include/asm/hvcall.h | 1 + arch/powerpc/include/asm/machdep.h | 2 +- arch/powerpc/include/asm/plpar_wrappers.h | 5 +++++ arch/powerpc/kernel/dawr.c | 2 +- arch/powerpc/platforms/pseries/setup.c | 7 +++++-- 5 files changed, 13 insertions(+), 4 deletions(-) diff --git a/arch/powerpc/include/asm/hvcall.h b/arch/powerpc/include/asm/hvcall.h index 9ab367030f4f9..fbb3770554718 100644 --- a/arch/powerpc/include/asm/hvcall.h +++ b/arch/powerpc/include/asm/hvcall.h @@ -358,6 +358,7 @@ #define H_SET_MODE_RESOURCE_SET_DAWR0 2 #define H_SET_MODE_RESOURCE_ADDR_TRANS_MODE 3 #define H_SET_MODE_RESOURCE_LE 4 +#define H_SET_MODE_RESOURCE_SET_DAWR1 5 /* Values for argument to H_SIGNAL_SYS_RESET */ #define H_SIGNAL_SYS_RESET_ALL -1 diff --git a/arch/powerpc/include/asm/machdep.h b/arch/powerpc/include/asm/machdep.h index 7bcb64444a394..a90b892f0bfe3 100644 --- a/arch/powerpc/include/asm/machdep.h +++ b/arch/powerpc/include/asm/machdep.h @@ -131,7 +131,7 @@ struct machdep_calls { unsigned long dabrx); /* Set DAWR for this platform, leave empty for default implementation */ - int (*set_dawr)(unsigned long dawr, + int (*set_dawr)(int nr, unsigned long dawr, unsigned long dawrx); #ifdef CONFIG_PPC32 /* XXX for now */ diff --git a/arch/powerpc/include/asm/plpar_wrappers.h b/arch/powerpc/include/asm/plpar_wrappers.h index d12c3680d9469..ece84a430701f 100644 --- a/arch/powerpc/include/asm/plpar_wrappers.h +++ b/arch/powerpc/include/asm/plpar_wrappers.h @@ -315,6 +315,11 @@ static inline long plpar_set_watchpoint0(unsigned long dawr0, unsigned long dawr return plpar_set_mode(0, H_SET_MODE_RESOURCE_SET_DAWR0, dawr0, dawrx0); } +static inline long plpar_set_watchpoint1(unsigned long dawr1, unsigned long dawrx1) +{ + return plpar_set_mode(0, H_SET_MODE_RESOURCE_SET_DAWR1, dawr1, dawrx1); +} + static inline long plpar_signal_sys_reset(long cpu) { return plpar_hcall_norets(H_SIGNAL_SYS_RESET, cpu); diff --git a/arch/powerpc/kernel/dawr.c b/arch/powerpc/kernel/dawr.c index 500f52fa47114..cdc2dccb987dc 100644 --- a/arch/powerpc/kernel/dawr.c +++ b/arch/powerpc/kernel/dawr.c @@ -37,7 +37,7 @@ int set_dawr(int nr, struct arch_hw_breakpoint *brk) dawrx |= (mrd & 0x3f) << (63 - 53); if (ppc_md.set_dawr) - return ppc_md.set_dawr(dawr, dawrx); + return ppc_md.set_dawr(nr, dawr, dawrx); if (nr == 0) { mtspr(SPRN_DAWR0, dawr); diff --git a/arch/powerpc/platforms/pseries/setup.c b/arch/powerpc/platforms/pseries/setup.c index 8c85466e0dd8e..ba34eb23e8f53 100644 --- a/arch/powerpc/platforms/pseries/setup.c +++ b/arch/powerpc/platforms/pseries/setup.c @@ -833,12 +833,15 @@ static int pseries_set_xdabr(unsigned long dabr, unsigned long dabrx) return plpar_hcall_norets(H_SET_XDABR, dabr, dabrx); } -static int pseries_set_dawr(unsigned long dawr, unsigned long dawrx) +static int pseries_set_dawr(int nr, unsigned long dawr, unsigned long dawrx) { /* PAPR says we can't set HYP */ dawrx &= ~DAWRX_HYP; - return plpar_set_watchpoint0(dawr, dawrx); + if (nr == 0) + return plpar_set_watchpoint0(dawr, dawrx); + else + return plpar_set_watchpoint1(dawr, dawrx); } #define CMO_CHARACTERISTICS_TOKEN 44 -- GitLab From deb2bd9bcc8428d4b65b6ba640ba8b57c1b20b17 Mon Sep 17 00:00:00 2001 From: Ravi Bangoria Date: Thu, 23 Jul 2020 14:38:12 +0530 Subject: [PATCH 0826/1476] powerpc/watchpoint: Return available watchpoints dynamically So far Book3S Powerpc supported only one watchpoint. Power10 is introducing 2nd DAWR. Enable 2nd DAWR support for Power10. Availability of 2nd DAWR will depend on CPU_FTR_DAWR1. Signed-off-by: Ravi Bangoria Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20200723090813.303838-10-ravi.bangoria@linux.ibm.com --- arch/powerpc/include/asm/cputable.h | 5 +++-- arch/powerpc/include/asm/hw_breakpoint.h | 4 +++- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/arch/powerpc/include/asm/cputable.h b/arch/powerpc/include/asm/cputable.h index d36db35e3aac6..fdddb822d564b 100644 --- a/arch/powerpc/include/asm/cputable.h +++ b/arch/powerpc/include/asm/cputable.h @@ -629,9 +629,10 @@ enum { /* * Maximum number of hw breakpoint supported on powerpc. Number of - * breakpoints supported by actual hw might be less than this. + * breakpoints supported by actual hw might be less than this, which + * is decided at run time in nr_wp_slots(). */ -#define HBP_NUM_MAX 1 +#define HBP_NUM_MAX 2 #endif /* !__ASSEMBLY__ */ diff --git a/arch/powerpc/include/asm/hw_breakpoint.h b/arch/powerpc/include/asm/hw_breakpoint.h index cb424799da0dc..c89250b6ac34b 100644 --- a/arch/powerpc/include/asm/hw_breakpoint.h +++ b/arch/powerpc/include/asm/hw_breakpoint.h @@ -9,6 +9,8 @@ #ifndef _PPC_BOOK3S_64_HW_BREAKPOINT_H #define _PPC_BOOK3S_64_HW_BREAKPOINT_H +#include + #ifdef __KERNEL__ struct arch_hw_breakpoint { unsigned long address; @@ -46,7 +48,7 @@ struct arch_hw_breakpoint { static inline int nr_wp_slots(void) { - return HBP_NUM_MAX; + return cpu_has_feature(CPU_FTR_DAWR1) ? 2 : 1; } #ifdef CONFIG_HAVE_HW_BREAKPOINT -- GitLab From 3f31e49dc4588d396023028791e36c23235e1334 Mon Sep 17 00:00:00 2001 From: Ravi Bangoria Date: Thu, 23 Jul 2020 14:38:13 +0530 Subject: [PATCH 0827/1476] powerpc/watchpoint: Remove 512 byte boundary Power10 has removed 512 bytes boundary from match criteria i.e. the watch range can cross 512 bytes boundary. Note: ISA 3.1 Book III 9.4 match criteria includes 512 byte limit but that is a documentation mistake and hopefully will be fixed in the next version of ISA. Though, ISA 3.1 change log mentions about removal of 512B boundary: Multiple DEAW: Added a second Data Address Watchpoint. [H]DAR is set to the first byte of overlap. 512B boundary is removed. Signed-off-by: Ravi Bangoria Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20200723090813.303838-11-ravi.bangoria@linux.ibm.com --- arch/powerpc/kernel/hw_breakpoint.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/arch/powerpc/kernel/hw_breakpoint.c b/arch/powerpc/kernel/hw_breakpoint.c index c55e67bab2710..1f4a1efa00744 100644 --- a/arch/powerpc/kernel/hw_breakpoint.c +++ b/arch/powerpc/kernel/hw_breakpoint.c @@ -418,8 +418,9 @@ static int hw_breakpoint_validate_len(struct arch_hw_breakpoint *hw) if (dawr_enabled()) { max_len = DAWR_MAX_LEN; - /* DAWR region can't cross 512 bytes boundary */ - if (ALIGN_DOWN(start_addr, SZ_512) != ALIGN_DOWN(end_addr - 1, SZ_512)) + /* DAWR region can't cross 512 bytes boundary on p10 predecessors */ + if (!cpu_has_feature(CPU_FTR_ARCH_31) && + (ALIGN_DOWN(start_addr, SZ_512) != ALIGN_DOWN(end_addr - 1, SZ_512))) return -EINVAL; } else if (IS_ENABLED(CONFIG_PPC_8xx)) { /* 8xx can setup a range without limitation */ -- GitLab From 475028efc708880e16e61cc4cbbc00af784cb39b Mon Sep 17 00:00:00 2001 From: Oliver O'Halloran Date: Sat, 25 Jul 2020 18:12:18 +1000 Subject: [PATCH 0828/1476] powerpc/eeh: Remove eeh_dev_phb_init_dynamic() This function is a one line wrapper around eeh_phb_pe_create() and despite the name it doesn't create any eeh_dev structures. Replace it with direct calls to eeh_phb_pe_create() since that does what it says on the tin and removes a layer of indirection. Signed-off-by: Oliver O'Halloran Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20200725081231.39076-1-oohall@gmail.com --- arch/powerpc/include/asm/eeh.h | 2 +- arch/powerpc/kernel/eeh.c | 2 +- arch/powerpc/kernel/eeh_dev.c | 13 ------------- arch/powerpc/kernel/of_platform.c | 4 ++-- arch/powerpc/platforms/pseries/pci_dlpar.c | 2 +- 5 files changed, 5 insertions(+), 18 deletions(-) diff --git a/arch/powerpc/include/asm/eeh.h b/arch/powerpc/include/asm/eeh.h index 964a54292b365..1a19b1bb74c01 100644 --- a/arch/powerpc/include/asm/eeh.h +++ b/arch/powerpc/include/asm/eeh.h @@ -294,7 +294,6 @@ const char *eeh_pe_loc_get(struct eeh_pe *pe); struct pci_bus *eeh_pe_bus_get(struct eeh_pe *pe); struct eeh_dev *eeh_dev_init(struct pci_dn *pdn); -void eeh_dev_phb_init_dynamic(struct pci_controller *phb); void eeh_show_enabled(void); int __init eeh_ops_register(struct eeh_ops *ops); int __exit eeh_ops_unregister(const char *name); @@ -362,6 +361,7 @@ static inline void eeh_remove_device(struct pci_dev *dev) { } #define EEH_POSSIBLE_ERROR(val, type) (0) #define EEH_IO_ERROR_VALUE(size) (-1UL) +static inline int eeh_phb_pe_create(struct pci_controller *phb) { return 0; } #endif /* CONFIG_EEH */ #if defined(CONFIG_PPC_PSERIES) && defined(CONFIG_EEH) diff --git a/arch/powerpc/kernel/eeh.c b/arch/powerpc/kernel/eeh.c index d407981dec76b..859f760202564 100644 --- a/arch/powerpc/kernel/eeh.c +++ b/arch/powerpc/kernel/eeh.c @@ -1096,7 +1096,7 @@ static int eeh_init(void) /* Initialize PHB PEs */ list_for_each_entry_safe(hose, tmp, &hose_list, list_node) - eeh_dev_phb_init_dynamic(hose); + eeh_phb_pe_create(hose); eeh_addr_cache_init(); diff --git a/arch/powerpc/kernel/eeh_dev.c b/arch/powerpc/kernel/eeh_dev.c index 7370185c7a051..8e159a12f10c8 100644 --- a/arch/powerpc/kernel/eeh_dev.c +++ b/arch/powerpc/kernel/eeh_dev.c @@ -52,16 +52,3 @@ struct eeh_dev *eeh_dev_init(struct pci_dn *pdn) return edev; } - -/** - * eeh_dev_phb_init_dynamic - Create EEH devices for devices included in PHB - * @phb: PHB - * - * Scan the PHB OF node and its child association, then create the - * EEH devices accordingly - */ -void eeh_dev_phb_init_dynamic(struct pci_controller *phb) -{ - /* EEH PE for PHB */ - eeh_phb_pe_create(phb); -} diff --git a/arch/powerpc/kernel/of_platform.c b/arch/powerpc/kernel/of_platform.c index 71a3f97dc9885..f89376ff633e7 100644 --- a/arch/powerpc/kernel/of_platform.c +++ b/arch/powerpc/kernel/of_platform.c @@ -62,8 +62,8 @@ static int of_pci_phb_probe(struct platform_device *dev) /* Init pci_dn data structures */ pci_devs_phb_init_dynamic(phb); - /* Create EEH PEs for the PHB */ - eeh_dev_phb_init_dynamic(phb); + /* Create EEH PE for the PHB */ + eeh_phb_pe_create(phb); /* Scan the bus */ pcibios_scan_phb(phb); diff --git a/arch/powerpc/platforms/pseries/pci_dlpar.c b/arch/powerpc/platforms/pseries/pci_dlpar.c index b3a38f5a6b688..f9ae17e8a0f46 100644 --- a/arch/powerpc/platforms/pseries/pci_dlpar.c +++ b/arch/powerpc/platforms/pseries/pci_dlpar.c @@ -34,7 +34,7 @@ struct pci_controller *init_phb_dynamic(struct device_node *dn) pci_devs_phb_init_dynamic(phb); /* Create EEH devices for the PHB */ - eeh_dev_phb_init_dynamic(phb); + eeh_phb_pe_create(phb); if (dn->child) pseries_eeh_init_edev_recursive(PCI_DN(dn)); -- GitLab From d74ee8e9d12e2071014ecec96a1ce2744f77639d Mon Sep 17 00:00:00 2001 From: Oliver O'Halloran Date: Sat, 25 Jul 2020 18:12:19 +1000 Subject: [PATCH 0829/1476] powerpc/eeh: Remove eeh_dev.c The only thing in this file is eeh_dev_init() which is allocates and initialises an eeh_dev based on a pci_dn. This is only ever called from pci_dn.c so move it into there and remove the file. Signed-off-by: Oliver O'Halloran Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20200725081231.39076-2-oohall@gmail.com --- arch/powerpc/include/asm/eeh.h | 6 ---- arch/powerpc/kernel/Makefile | 2 +- arch/powerpc/kernel/eeh_dev.c | 54 ---------------------------------- arch/powerpc/kernel/pci_dn.c | 20 +++++++++++++ 4 files changed, 21 insertions(+), 61 deletions(-) delete mode 100644 arch/powerpc/kernel/eeh_dev.c diff --git a/arch/powerpc/include/asm/eeh.h b/arch/powerpc/include/asm/eeh.h index 1a19b1bb74c01..dd7dd55db7dc7 100644 --- a/arch/powerpc/include/asm/eeh.h +++ b/arch/powerpc/include/asm/eeh.h @@ -293,7 +293,6 @@ void eeh_pe_restore_bars(struct eeh_pe *pe); const char *eeh_pe_loc_get(struct eeh_pe *pe); struct pci_bus *eeh_pe_bus_get(struct eeh_pe *pe); -struct eeh_dev *eeh_dev_init(struct pci_dn *pdn); void eeh_show_enabled(void); int __init eeh_ops_register(struct eeh_ops *ops); int __exit eeh_ops_unregister(const char *name); @@ -339,11 +338,6 @@ static inline bool eeh_enabled(void) static inline void eeh_show_enabled(void) { } -static inline void *eeh_dev_init(struct pci_dn *pdn, void *data) -{ - return NULL; -} - static inline void eeh_dev_phb_init_dynamic(struct pci_controller *phb) { } static inline int eeh_check_failure(const volatile void __iomem *token) diff --git a/arch/powerpc/kernel/Makefile b/arch/powerpc/kernel/Makefile index 244542ae2a915..c5211bdcf1b62 100644 --- a/arch/powerpc/kernel/Makefile +++ b/arch/powerpc/kernel/Makefile @@ -71,7 +71,7 @@ obj-$(CONFIG_PPC_RTAS_DAEMON) += rtasd.o obj-$(CONFIG_RTAS_FLASH) += rtas_flash.o obj-$(CONFIG_RTAS_PROC) += rtas-proc.o obj-$(CONFIG_PPC_DT_CPU_FTRS) += dt_cpu_ftrs.o -obj-$(CONFIG_EEH) += eeh.o eeh_pe.o eeh_dev.o eeh_cache.o \ +obj-$(CONFIG_EEH) += eeh.o eeh_pe.o eeh_cache.o \ eeh_driver.o eeh_event.o eeh_sysfs.o obj-$(CONFIG_GENERIC_TBSYNC) += smp-tbsync.o obj-$(CONFIG_CRASH_DUMP) += crash_dump.o diff --git a/arch/powerpc/kernel/eeh_dev.c b/arch/powerpc/kernel/eeh_dev.c deleted file mode 100644 index 8e159a12f10c8..0000000000000 --- a/arch/powerpc/kernel/eeh_dev.c +++ /dev/null @@ -1,54 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * The file intends to implement dynamic creation of EEH device, which will - * be bound with OF node and PCI device simutaneously. The EEH devices would - * be foundamental information for EEH core components to work proerly. Besides, - * We have to support multiple situations where dynamic creation of EEH device - * is required: - * - * 1) Before PCI emunation starts, we need create EEH devices according to the - * PCI sensitive OF nodes. - * 2) When PCI emunation is done, we need do the binding between PCI device and - * the associated EEH device. - * 3) DR (Dynamic Reconfiguration) would create PCI sensitive OF node. EEH device - * will be created while PCI sensitive OF node is detected from DR. - * 4) PCI hotplug needs redoing the binding between PCI device and EEH device. If - * PHB is newly inserted, we also need create EEH devices accordingly. - * - * Copyright Benjamin Herrenschmidt & Gavin Shan, IBM Corporation 2012. - */ - -#include -#include -#include -#include -#include -#include - -#include -#include - -/** - * eeh_dev_init - Create EEH device according to OF node - * @pdn: PCI device node - * - * It will create EEH device according to the given OF node. The function - * might be called by PCI emunation, DR, PHB hotplug. - */ -struct eeh_dev *eeh_dev_init(struct pci_dn *pdn) -{ - struct eeh_dev *edev; - - /* Allocate EEH device */ - edev = kzalloc(sizeof(*edev), GFP_KERNEL); - if (!edev) - return NULL; - - /* Associate EEH device with OF node */ - pdn->edev = edev; - edev->pdn = pdn; - edev->bdfn = (pdn->busno << 8) | pdn->devfn; - edev->controller = pdn->phb; - - return edev; -} diff --git a/arch/powerpc/kernel/pci_dn.c b/arch/powerpc/kernel/pci_dn.c index 4e654df55969e..f790a8d06f50e 100644 --- a/arch/powerpc/kernel/pci_dn.c +++ b/arch/powerpc/kernel/pci_dn.c @@ -124,6 +124,26 @@ struct pci_dn *pci_get_pdn(struct pci_dev *pdev) return NULL; } +#ifdef CONFIG_EEH +static struct eeh_dev *eeh_dev_init(struct pci_dn *pdn) +{ + struct eeh_dev *edev; + + /* Allocate EEH device */ + edev = kzalloc(sizeof(*edev), GFP_KERNEL); + if (!edev) + return NULL; + + /* Associate EEH device with OF node */ + pdn->edev = edev; + edev->pdn = pdn; + edev->bdfn = (pdn->busno << 8) | pdn->devfn; + edev->controller = pdn->phb; + + return edev; +} +#endif /* CONFIG_EEH */ + #ifdef CONFIG_PCI_IOV static struct pci_dn *add_one_sriov_vf_pdn(struct pci_dn *parent, int vf_index, -- GitLab From dffa91539e80355402c0716a91af17fc8ddd1abf Mon Sep 17 00:00:00 2001 From: Oliver O'Halloran Date: Sat, 25 Jul 2020 18:12:20 +1000 Subject: [PATCH 0830/1476] powerpc/eeh: Move vf_index out of pci_dn and into eeh_dev Drivers that do not support the PCI error handling callbacks are handled by tearing down the device and re-probing them. If the device being removed is a virtual function then we need to know the VF index so it can be removed using the pci_iov_{add|remove}_virtfn() API. Currently this is handled by looking up the pci_dn, and using the vf_index that was stashed there when the pci_dn for the VF was created in pcibios_sriov_enable(). We would like to eliminate the use of pci_dn outside of pseries though so we need to provide the generic EEH code with some other way to find the vf_index. The easiest thing to do here is move the vf_index field out of pci_dn and into eeh_dev. Currently pci_dn and eeh_dev are allocated and initialized together so this is a fairly minimal change in preparation for splitting pci_dn and eeh_dev in the future. Signed-off-by: Oliver O'Halloran Reviewed-by: Alexey Kardashevskiy Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20200725081231.39076-3-oohall@gmail.com --- arch/powerpc/include/asm/eeh.h | 3 +++ arch/powerpc/include/asm/pci-bridge.h | 1 - arch/powerpc/kernel/eeh_driver.c | 6 ++---- arch/powerpc/kernel/pci_dn.c | 7 ++++--- 4 files changed, 9 insertions(+), 8 deletions(-) diff --git a/arch/powerpc/include/asm/eeh.h b/arch/powerpc/include/asm/eeh.h index dd7dd55db7dc7..2a935db72198e 100644 --- a/arch/powerpc/include/asm/eeh.h +++ b/arch/powerpc/include/asm/eeh.h @@ -148,7 +148,10 @@ struct eeh_dev { struct pci_dn *pdn; /* Associated PCI device node */ struct pci_dev *pdev; /* Associated PCI device */ bool in_error; /* Error flag for edev */ + + /* VF specific properties */ struct pci_dev *physfn; /* Associated SRIOV PF */ + int vf_index; /* Index of this VF */ }; /* "fmt" must be a simple literal string */ diff --git a/arch/powerpc/include/asm/pci-bridge.h b/arch/powerpc/include/asm/pci-bridge.h index b92e81b256e58..d2a2a14e56f91 100644 --- a/arch/powerpc/include/asm/pci-bridge.h +++ b/arch/powerpc/include/asm/pci-bridge.h @@ -202,7 +202,6 @@ struct pci_dn { #define IODA_INVALID_PE 0xFFFFFFFF unsigned int pe_number; #ifdef CONFIG_PCI_IOV - int vf_index; /* VF index in the PF */ u16 vfs_expanded; /* number of VFs IOV BAR expanded */ u16 num_vfs; /* number of VFs enabled*/ unsigned int *pe_num_map; /* PE# for the first VF PE or array */ diff --git a/arch/powerpc/kernel/eeh_driver.c b/arch/powerpc/kernel/eeh_driver.c index 7b048cee767c7..b70b9273f45a5 100644 --- a/arch/powerpc/kernel/eeh_driver.c +++ b/arch/powerpc/kernel/eeh_driver.c @@ -477,7 +477,7 @@ static void *eeh_add_virt_device(struct eeh_dev *edev) } #ifdef CONFIG_PCI_IOV - pci_iov_add_virtfn(edev->physfn, eeh_dev_to_pdn(edev)->vf_index); + pci_iov_add_virtfn(edev->physfn, edev->vf_index); #endif return NULL; } @@ -521,9 +521,7 @@ static void eeh_rmv_device(struct eeh_dev *edev, void *userdata) if (edev->physfn) { #ifdef CONFIG_PCI_IOV - struct pci_dn *pdn = eeh_dev_to_pdn(edev); - - pci_iov_remove_virtfn(edev->physfn, pdn->vf_index); + pci_iov_remove_virtfn(edev->physfn, edev->vf_index); edev->pdev = NULL; #endif if (rmv_data) diff --git a/arch/powerpc/kernel/pci_dn.c b/arch/powerpc/kernel/pci_dn.c index f790a8d06f50e..bf11ac8427acd 100644 --- a/arch/powerpc/kernel/pci_dn.c +++ b/arch/powerpc/kernel/pci_dn.c @@ -146,7 +146,6 @@ static struct eeh_dev *eeh_dev_init(struct pci_dn *pdn) #ifdef CONFIG_PCI_IOV static struct pci_dn *add_one_sriov_vf_pdn(struct pci_dn *parent, - int vf_index, int busno, int devfn) { struct pci_dn *pdn; @@ -163,7 +162,6 @@ static struct pci_dn *add_one_sriov_vf_pdn(struct pci_dn *parent, pdn->parent = parent; pdn->busno = busno; pdn->devfn = devfn; - pdn->vf_index = vf_index; pdn->pe_number = IODA_INVALID_PE; INIT_LIST_HEAD(&pdn->child_list); INIT_LIST_HEAD(&pdn->list); @@ -194,7 +192,7 @@ struct pci_dn *add_sriov_vf_pdns(struct pci_dev *pdev) for (i = 0; i < pci_sriov_get_totalvfs(pdev); i++) { struct eeh_dev *edev __maybe_unused; - pdn = add_one_sriov_vf_pdn(parent, i, + pdn = add_one_sriov_vf_pdn(parent, pci_iov_virtfn_bus(pdev, i), pci_iov_virtfn_devfn(pdev, i)); if (!pdn) { @@ -207,7 +205,10 @@ struct pci_dn *add_sriov_vf_pdns(struct pci_dev *pdev) /* Create the EEH device for the VF */ edev = eeh_dev_init(pdn); BUG_ON(!edev); + + /* FIXME: these should probably be populated by the EEH probe */ edev->physfn = pdev; + edev->vf_index = i; #endif /* CONFIG_EEH */ } return pci_get_pdn(pdev); -- GitLab From c408ce9075b8e1533f30fd3a113b75fb745f722f Mon Sep 17 00:00:00 2001 From: Oliver O'Halloran Date: Sat, 25 Jul 2020 18:12:21 +1000 Subject: [PATCH 0831/1476] powerpc/pseries: Stop using pdn->pe_number The pci_dn->pe_number field is mainly used to track the IODA PE number of a device on PowerNV. At some point it grew a user in the pseries SR-IOV support which muddies the waters a bit, so remove it. Signed-off-by: Oliver O'Halloran Reviewed-by: Alexey Kardashevskiy Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20200725081231.39076-4-oohall@gmail.com --- arch/powerpc/platforms/pseries/eeh_pseries.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/arch/powerpc/platforms/pseries/eeh_pseries.c b/arch/powerpc/platforms/pseries/eeh_pseries.c index 47760fb515cc6..a82057b581443 100644 --- a/arch/powerpc/platforms/pseries/eeh_pseries.c +++ b/arch/powerpc/platforms/pseries/eeh_pseries.c @@ -53,8 +53,6 @@ void pseries_pcibios_bus_add_device(struct pci_dev *pdev) dev_dbg(&pdev->dev, "EEH: Setting up device\n"); #ifdef CONFIG_PCI_IOV if (pdev->is_virtfn) { - struct pci_dn *physfn_pdn; - pdn->device_id = pdev->device; pdn->vendor_id = pdev->vendor; pdn->class_code = pdev->class; @@ -64,8 +62,6 @@ void pseries_pcibios_bus_add_device(struct pci_dev *pdev) * completion from platform. */ pdn->last_allow_rc = 0; - physfn_pdn = pci_get_pdn(pdev->physfn); - pdn->pe_number = physfn_pdn->pe_num_map[pdn->vf_index]; } #endif pseries_eeh_init_edev(pdn); @@ -878,8 +874,8 @@ int pseries_send_allow_unfreeze(struct pci_dn *pdn, static int pseries_call_allow_unfreeze(struct eeh_dev *edev) { + int cur_vfs = 0, rc = 0, vf_index, bus, devfn, vf_pe_num; struct pci_dn *pdn, *tmp, *parent, *physfn_pdn; - int cur_vfs = 0, rc = 0, vf_index, bus, devfn; u16 *vf_pe_array; vf_pe_array = kzalloc(RTAS_DATA_BUF_SIZE, GFP_KERNEL); @@ -912,8 +908,10 @@ static int pseries_call_allow_unfreeze(struct eeh_dev *edev) } } else { pdn = pci_get_pdn(edev->pdev); - vf_pe_array[0] = cpu_to_be16(pdn->pe_number); physfn_pdn = pci_get_pdn(edev->physfn); + + vf_pe_num = physfn_pdn->pe_num_map[edev->vf_index]; + vf_pe_array[0] = cpu_to_be16(vf_pe_num); rc = pseries_send_allow_unfreeze(physfn_pdn, vf_pe_array, 1); pdn->last_allow_rc = rc; -- GitLab From a40db934312cb2a4bef16b3edc962bc8c7f6462f Mon Sep 17 00:00:00 2001 From: Oliver O'Halloran Date: Sat, 25 Jul 2020 18:12:22 +1000 Subject: [PATCH 0832/1476] powerpc/eeh: Kill off eeh_ops->get_pe_addr() This is used in precisely one place which is in pseries specific platform code. There's no need to have the callback in eeh_ops since the platform chooses the EEH PE addresses anyway. The PowerNV implementation has always been a stub too so remove it. Signed-off-by: Oliver O'Halloran Reviewed-by: Alexey Kardashevskiy Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20200725081231.39076-5-oohall@gmail.com --- arch/powerpc/include/asm/eeh.h | 1 - arch/powerpc/platforms/powernv/eeh-powernv.c | 13 ------------ arch/powerpc/platforms/pseries/eeh_pseries.c | 22 ++++++++++---------- 3 files changed, 11 insertions(+), 25 deletions(-) diff --git a/arch/powerpc/include/asm/eeh.h b/arch/powerpc/include/asm/eeh.h index 2a935db72198e..676d499bda42d 100644 --- a/arch/powerpc/include/asm/eeh.h +++ b/arch/powerpc/include/asm/eeh.h @@ -220,7 +220,6 @@ struct eeh_ops { int (*init)(void); struct eeh_dev *(*probe)(struct pci_dev *pdev); int (*set_option)(struct eeh_pe *pe, int option); - int (*get_pe_addr)(struct eeh_pe *pe); int (*get_state)(struct eeh_pe *pe, int *delay); int (*reset)(struct eeh_pe *pe, int option); int (*get_log)(struct eeh_pe *pe, int severity, char *drv_log, unsigned long len); diff --git a/arch/powerpc/platforms/powernv/eeh-powernv.c b/arch/powerpc/platforms/powernv/eeh-powernv.c index 79409e005fcde..bcd0515d8f791 100644 --- a/arch/powerpc/platforms/powernv/eeh-powernv.c +++ b/arch/powerpc/platforms/powernv/eeh-powernv.c @@ -535,18 +535,6 @@ static int pnv_eeh_set_option(struct eeh_pe *pe, int option) return 0; } -/** - * pnv_eeh_get_pe_addr - Retrieve PE address - * @pe: EEH PE - * - * Retrieve the PE address according to the given tranditional - * PCI BDF (Bus/Device/Function) address. - */ -static int pnv_eeh_get_pe_addr(struct eeh_pe *pe) -{ - return pe->addr; -} - static void pnv_eeh_get_phb_diag(struct eeh_pe *pe) { struct pnv_phb *phb = pe->phb->private_data; @@ -1670,7 +1658,6 @@ static struct eeh_ops pnv_eeh_ops = { .init = pnv_eeh_init, .probe = pnv_eeh_probe, .set_option = pnv_eeh_set_option, - .get_pe_addr = pnv_eeh_get_pe_addr, .get_state = pnv_eeh_get_state, .reset = pnv_eeh_reset, .get_log = pnv_eeh_get_log, diff --git a/arch/powerpc/platforms/pseries/eeh_pseries.c b/arch/powerpc/platforms/pseries/eeh_pseries.c index a82057b581443..4a8a851e64d1f 100644 --- a/arch/powerpc/platforms/pseries/eeh_pseries.c +++ b/arch/powerpc/platforms/pseries/eeh_pseries.c @@ -33,6 +33,8 @@ #include #include +static int pseries_eeh_get_pe_addr(struct pci_dn *pdn); + /* RTAS tokens */ static int ibm_set_eeh_option; static int ibm_set_slot_reset; @@ -468,7 +470,7 @@ void pseries_eeh_init_edev(struct pci_dn *pdn) eeh_edev_dbg(edev, "EEH failed to enable on device (code %d)\n", ret); } else { /* Retrieve PE address */ - edev->pe_config_addr = eeh_ops->get_pe_addr(&pe); + edev->pe_config_addr = pseries_eeh_get_pe_addr(pdn); pe.addr = edev->pe_config_addr; /* Some older systems (Power4) allow the ibm,set-eeh-option @@ -598,8 +600,10 @@ static int pseries_eeh_set_option(struct eeh_pe *pe, int option) * It's notable that zero'ed return value means invalid PE config * address. */ -static int pseries_eeh_get_pe_addr(struct eeh_pe *pe) +static int pseries_eeh_get_pe_addr(struct pci_dn *pdn) { + int config_addr = rtas_config_addr(pdn->busno, pdn->devfn, 0); + unsigned long buid = pdn->phb->buid; int ret = 0; int rets[3]; @@ -610,18 +614,16 @@ static int pseries_eeh_get_pe_addr(struct eeh_pe *pe) * meaningless. */ ret = rtas_call(ibm_get_config_addr_info2, 4, 2, rets, - pe->config_addr, BUID_HI(pe->phb->buid), - BUID_LO(pe->phb->buid), 1); + config_addr, BUID_HI(buid), BUID_LO(buid), 1); if (ret || (rets[0] == 0)) return 0; /* Retrieve the associated PE config address */ ret = rtas_call(ibm_get_config_addr_info2, 4, 2, rets, - pe->config_addr, BUID_HI(pe->phb->buid), - BUID_LO(pe->phb->buid), 0); + config_addr, BUID_HI(buid), BUID_LO(buid), 0); if (ret) { pr_warn("%s: Failed to get address for PHB#%x-PE#%x\n", - __func__, pe->phb->global_number, pe->config_addr); + __func__, pdn->phb->global_number, config_addr); return 0; } @@ -630,11 +632,10 @@ static int pseries_eeh_get_pe_addr(struct eeh_pe *pe) if (ibm_get_config_addr_info != RTAS_UNKNOWN_SERVICE) { ret = rtas_call(ibm_get_config_addr_info, 4, 2, rets, - pe->config_addr, BUID_HI(pe->phb->buid), - BUID_LO(pe->phb->buid), 0); + config_addr, BUID_HI(buid), BUID_LO(buid), 0); if (ret) { pr_warn("%s: Failed to get address for PHB#%x-PE#%x\n", - __func__, pe->phb->global_number, pe->config_addr); + __func__, pdn->phb->global_number, config_addr); return 0; } @@ -945,7 +946,6 @@ static struct eeh_ops pseries_eeh_ops = { .init = pseries_eeh_init, .probe = pseries_eeh_probe, .set_option = pseries_eeh_set_option, - .get_pe_addr = pseries_eeh_get_pe_addr, .get_state = pseries_eeh_get_state, .reset = pseries_eeh_reset, .get_log = pseries_eeh_get_log, -- GitLab From 21b43bd59c7838825b94eea288333affb53dd399 Mon Sep 17 00:00:00 2001 From: Oliver O'Halloran Date: Sat, 25 Jul 2020 18:12:23 +1000 Subject: [PATCH 0833/1476] powerpc/eeh: Remove VF config space restoration There's a bunch of strange things about this code. First up is that none of the fields being written to are functional for a VF. The SR-IOV specification lists then as "Reserved, but OS should preserve" so writing new values to them doesn't do anything and is clearly wrong from a correctness perspective. However, since VFs are designed to be managed by the OS there is an argument to be made that we should be saving and restoring some parts of config space. We already sort of do that by saving the first 64 bytes of config space in the eeh_dev (see eeh_dev->config_space[]). This is inadequate since it doesn't even consider saving and restoring the PCI capability structures. However, this is a problem with EEH in general and that needs to be fixed for non-VF devices too. There's no real reason to keep around this around so delete it. Signed-off-by: Oliver O'Halloran Reviewed-by: Alexey Kardashevskiy Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20200725081231.39076-6-oohall@gmail.com --- arch/powerpc/include/asm/eeh.h | 1 - arch/powerpc/kernel/eeh.c | 59 -------------------- arch/powerpc/platforms/powernv/eeh-powernv.c | 20 ++----- arch/powerpc/platforms/pseries/eeh_pseries.c | 26 +-------- 4 files changed, 7 insertions(+), 99 deletions(-) diff --git a/arch/powerpc/include/asm/eeh.h b/arch/powerpc/include/asm/eeh.h index 676d499bda42d..091d1ed287988 100644 --- a/arch/powerpc/include/asm/eeh.h +++ b/arch/powerpc/include/asm/eeh.h @@ -314,7 +314,6 @@ int eeh_pe_reset(struct eeh_pe *pe, int option, bool include_passed); int eeh_pe_configure(struct eeh_pe *pe); int eeh_pe_inject_err(struct eeh_pe *pe, int type, int func, unsigned long addr, unsigned long mask); -int eeh_restore_vf_config(struct pci_dn *pdn); /** * EEH_POSSIBLE_ERROR() -- test for possible MMIO failure. diff --git a/arch/powerpc/kernel/eeh.c b/arch/powerpc/kernel/eeh.c index 859f760202564..a4df6f6de0bdd 100644 --- a/arch/powerpc/kernel/eeh.c +++ b/arch/powerpc/kernel/eeh.c @@ -742,65 +742,6 @@ static void eeh_restore_dev_state(struct eeh_dev *edev, void *userdata) pci_restore_state(pdev); } -int eeh_restore_vf_config(struct pci_dn *pdn) -{ - struct eeh_dev *edev = pdn_to_eeh_dev(pdn); - u32 devctl, cmd, cap2, aer_capctl; - int old_mps; - - if (edev->pcie_cap) { - /* Restore MPS */ - old_mps = (ffs(pdn->mps) - 8) << 5; - eeh_ops->read_config(pdn, edev->pcie_cap + PCI_EXP_DEVCTL, - 2, &devctl); - devctl &= ~PCI_EXP_DEVCTL_PAYLOAD; - devctl |= old_mps; - eeh_ops->write_config(pdn, edev->pcie_cap + PCI_EXP_DEVCTL, - 2, devctl); - - /* Disable Completion Timeout if possible */ - eeh_ops->read_config(pdn, edev->pcie_cap + PCI_EXP_DEVCAP2, - 4, &cap2); - if (cap2 & PCI_EXP_DEVCAP2_COMP_TMOUT_DIS) { - eeh_ops->read_config(pdn, - edev->pcie_cap + PCI_EXP_DEVCTL2, - 4, &cap2); - cap2 |= PCI_EXP_DEVCTL2_COMP_TMOUT_DIS; - eeh_ops->write_config(pdn, - edev->pcie_cap + PCI_EXP_DEVCTL2, - 4, cap2); - } - } - - /* Enable SERR and parity checking */ - eeh_ops->read_config(pdn, PCI_COMMAND, 2, &cmd); - cmd |= (PCI_COMMAND_PARITY | PCI_COMMAND_SERR); - eeh_ops->write_config(pdn, PCI_COMMAND, 2, cmd); - - /* Enable report various errors */ - if (edev->pcie_cap) { - eeh_ops->read_config(pdn, edev->pcie_cap + PCI_EXP_DEVCTL, - 2, &devctl); - devctl &= ~PCI_EXP_DEVCTL_CERE; - devctl |= (PCI_EXP_DEVCTL_NFERE | - PCI_EXP_DEVCTL_FERE | - PCI_EXP_DEVCTL_URRE); - eeh_ops->write_config(pdn, edev->pcie_cap + PCI_EXP_DEVCTL, - 2, devctl); - } - - /* Enable ECRC generation and check */ - if (edev->pcie_cap && edev->aer_cap) { - eeh_ops->read_config(pdn, edev->aer_cap + PCI_ERR_CAP, - 4, &aer_capctl); - aer_capctl |= (PCI_ERR_CAP_ECRC_GENE | PCI_ERR_CAP_ECRC_CHKE); - eeh_ops->write_config(pdn, edev->aer_cap + PCI_ERR_CAP, - 4, aer_capctl); - } - - return 0; -} - /** * pcibios_set_pcie_reset_state - Set PCI-E reset state * @dev: pci device struct diff --git a/arch/powerpc/platforms/powernv/eeh-powernv.c b/arch/powerpc/platforms/powernv/eeh-powernv.c index bcd0515d8f791..8f3a7611efc10 100644 --- a/arch/powerpc/platforms/powernv/eeh-powernv.c +++ b/arch/powerpc/platforms/powernv/eeh-powernv.c @@ -1629,20 +1629,12 @@ static int pnv_eeh_restore_config(struct pci_dn *pdn) if (!edev) return -EEXIST; - /* - * We have to restore the PCI config space after reset since the - * firmware can't see SRIOV VFs. - * - * FIXME: The MPS, error routing rules, timeout setting are worthy - * to be exported by firmware in extendible way. - */ - if (edev->physfn) { - ret = eeh_restore_vf_config(pdn); - } else { - phb = pdn->phb->private_data; - ret = opal_pci_reinit(phb->opal_id, - OPAL_REINIT_PCI_DEV, config_addr); - } + if (edev->physfn) + return 0; + + phb = edev->controller->private_data; + ret = opal_pci_reinit(phb->opal_id, + OPAL_REINIT_PCI_DEV, edev->bdfn); if (ret) { pr_warn("%s: Can't reinit PCI dev 0x%x (%lld)\n", diff --git a/arch/powerpc/platforms/pseries/eeh_pseries.c b/arch/powerpc/platforms/pseries/eeh_pseries.c index 4a8a851e64d1f..e32b49ead3137 100644 --- a/arch/powerpc/platforms/pseries/eeh_pseries.c +++ b/arch/powerpc/platforms/pseries/eeh_pseries.c @@ -824,30 +824,6 @@ static int pseries_eeh_write_config(struct pci_dn *pdn, int where, int size, u32 return rtas_write_config(pdn, where, size, val); } -static int pseries_eeh_restore_config(struct pci_dn *pdn) -{ - struct eeh_dev *edev = pdn_to_eeh_dev(pdn); - s64 ret = 0; - - if (!edev) - return -EEXIST; - - /* - * FIXME: The MPS, error routing rules, timeout setting are worthy - * to be exported by firmware in extendible way. - */ - if (edev->physfn) - ret = eeh_restore_vf_config(pdn); - - if (ret) { - pr_warn("%s: Can't reinit PCI dev 0x%x (%lld)\n", - __func__, edev->pe_config_addr, ret); - return -EIO; - } - - return ret; -} - #ifdef CONFIG_PCI_IOV int pseries_send_allow_unfreeze(struct pci_dn *pdn, u16 *vf_pe_array, int cur_vfs) @@ -954,7 +930,7 @@ static struct eeh_ops pseries_eeh_ops = { .read_config = pseries_eeh_read_config, .write_config = pseries_eeh_write_config, .next_error = NULL, - .restore_config = pseries_eeh_restore_config, + .restore_config = NULL, /* NB: configure_bridge() does this */ #ifdef CONFIG_PCI_IOV .notify_resume = pseries_notify_resume #endif -- GitLab From 0c2c76523c04ac184c7d7bbb8756f603375b7fc4 Mon Sep 17 00:00:00 2001 From: Oliver O'Halloran Date: Sat, 25 Jul 2020 18:12:24 +1000 Subject: [PATCH 0834/1476] powerpc/eeh: Pass eeh_dev to eeh_ops->restore_config() Mechanical conversion of the eeh_ops interfaces to use eeh_dev to reference a specific device rather than pci_dn. No functional changes. Signed-off-by: Oliver O'Halloran Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20200725081231.39076-7-oohall@gmail.com --- arch/powerpc/include/asm/eeh.h | 2 +- arch/powerpc/kernel/eeh.c | 5 ++--- arch/powerpc/kernel/eeh_pe.c | 6 ++---- arch/powerpc/platforms/powernv/eeh-powernv.c | 6 ++---- 4 files changed, 7 insertions(+), 12 deletions(-) diff --git a/arch/powerpc/include/asm/eeh.h b/arch/powerpc/include/asm/eeh.h index 091d1ed287988..c23c8fd5229db 100644 --- a/arch/powerpc/include/asm/eeh.h +++ b/arch/powerpc/include/asm/eeh.h @@ -229,7 +229,7 @@ struct eeh_ops { int (*read_config)(struct pci_dn *pdn, int where, int size, u32 *val); int (*write_config)(struct pci_dn *pdn, int where, int size, u32 val); int (*next_error)(struct eeh_pe **pe); - int (*restore_config)(struct pci_dn *pdn); + int (*restore_config)(struct eeh_dev *edev); int (*notify_resume)(struct pci_dn *pdn); }; diff --git a/arch/powerpc/kernel/eeh.c b/arch/powerpc/kernel/eeh.c index a4df6f6de0bdd..1cef0f4bb2d55 100644 --- a/arch/powerpc/kernel/eeh.c +++ b/arch/powerpc/kernel/eeh.c @@ -726,7 +726,6 @@ static void eeh_disable_and_save_dev_state(struct eeh_dev *edev, static void eeh_restore_dev_state(struct eeh_dev *edev, void *userdata) { - struct pci_dn *pdn = eeh_dev_to_pdn(edev); struct pci_dev *pdev = eeh_dev_to_pci_dev(edev); struct pci_dev *dev = userdata; @@ -734,8 +733,8 @@ static void eeh_restore_dev_state(struct eeh_dev *edev, void *userdata) return; /* Apply customization from firmware */ - if (pdn && eeh_ops->restore_config) - eeh_ops->restore_config(pdn); + if (eeh_ops->restore_config) + eeh_ops->restore_config(edev); /* The caller should restore state for the specified device */ if (pdev != dev) diff --git a/arch/powerpc/kernel/eeh_pe.c b/arch/powerpc/kernel/eeh_pe.c index 177852e39a253..d71493f669177 100644 --- a/arch/powerpc/kernel/eeh_pe.c +++ b/arch/powerpc/kernel/eeh_pe.c @@ -843,16 +843,14 @@ static void eeh_restore_device_bars(struct eeh_dev *edev) */ static void eeh_restore_one_device_bars(struct eeh_dev *edev, void *flag) { - struct pci_dn *pdn = eeh_dev_to_pdn(edev); - /* Do special restore for bridges */ if (edev->mode & EEH_DEV_BRIDGE) eeh_restore_bridge_bars(edev); else eeh_restore_device_bars(edev); - if (eeh_ops->restore_config && pdn) - eeh_ops->restore_config(pdn); + if (eeh_ops->restore_config) + eeh_ops->restore_config(edev); } /** diff --git a/arch/powerpc/platforms/powernv/eeh-powernv.c b/arch/powerpc/platforms/powernv/eeh-powernv.c index 8f3a7611efc10..a41e67f674e62 100644 --- a/arch/powerpc/platforms/powernv/eeh-powernv.c +++ b/arch/powerpc/platforms/powernv/eeh-powernv.c @@ -1619,12 +1619,10 @@ static int pnv_eeh_next_error(struct eeh_pe **pe) return ret; } -static int pnv_eeh_restore_config(struct pci_dn *pdn) +static int pnv_eeh_restore_config(struct eeh_dev *edev) { - struct eeh_dev *edev = pdn_to_eeh_dev(pdn); struct pnv_phb *phb; s64 ret = 0; - int config_addr = (pdn->busno << 8) | (pdn->devfn); if (!edev) return -EEXIST; @@ -1638,7 +1636,7 @@ static int pnv_eeh_restore_config(struct pci_dn *pdn) if (ret) { pr_warn("%s: Can't reinit PCI dev 0x%x (%lld)\n", - __func__, config_addr, ret); + __func__, edev->bdfn, ret); return -EIO; } -- GitLab From 8225d543dc0170e5b61af8559af07ec4f26f0bd6 Mon Sep 17 00:00:00 2001 From: Oliver O'Halloran Date: Sat, 25 Jul 2020 18:12:25 +1000 Subject: [PATCH 0835/1476] powerpc/eeh: Pass eeh_dev to eeh_ops->resume_notify() Mechanical conversion of the eeh_ops interfaces to use eeh_dev to reference a specific device rather than pci_dn. No functional changes. Signed-off-by: Oliver O'Halloran Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20200725081231.39076-8-oohall@gmail.com --- arch/powerpc/include/asm/eeh.h | 2 +- arch/powerpc/kernel/eeh_driver.c | 4 ++-- arch/powerpc/kernel/eeh_sysfs.c | 2 +- arch/powerpc/platforms/pseries/eeh_pseries.c | 4 +--- 4 files changed, 5 insertions(+), 7 deletions(-) diff --git a/arch/powerpc/include/asm/eeh.h b/arch/powerpc/include/asm/eeh.h index c23c8fd5229db..73e6a73a36847 100644 --- a/arch/powerpc/include/asm/eeh.h +++ b/arch/powerpc/include/asm/eeh.h @@ -230,7 +230,7 @@ struct eeh_ops { int (*write_config)(struct pci_dn *pdn, int where, int size, u32 val); int (*next_error)(struct eeh_pe **pe); int (*restore_config)(struct eeh_dev *edev); - int (*notify_resume)(struct pci_dn *pdn); + int (*notify_resume)(struct eeh_dev *edev); }; extern int eeh_subsystem_flags; diff --git a/arch/powerpc/kernel/eeh_driver.c b/arch/powerpc/kernel/eeh_driver.c index b70b9273f45a5..b84d3cb2532e2 100644 --- a/arch/powerpc/kernel/eeh_driver.c +++ b/arch/powerpc/kernel/eeh_driver.c @@ -425,8 +425,8 @@ static enum pci_ers_result eeh_report_resume(struct eeh_dev *edev, pci_uevent_ers(edev->pdev, PCI_ERS_RESULT_RECOVERED); #ifdef CONFIG_PCI_IOV - if (eeh_ops->notify_resume && eeh_dev_to_pdn(edev)) - eeh_ops->notify_resume(eeh_dev_to_pdn(edev)); + if (eeh_ops->notify_resume) + eeh_ops->notify_resume(edev); #endif return PCI_ERS_RESULT_NONE; } diff --git a/arch/powerpc/kernel/eeh_sysfs.c b/arch/powerpc/kernel/eeh_sysfs.c index 4fb0f1e1017a9..429620da73baa 100644 --- a/arch/powerpc/kernel/eeh_sysfs.c +++ b/arch/powerpc/kernel/eeh_sysfs.c @@ -99,7 +99,7 @@ static ssize_t eeh_notify_resume_store(struct device *dev, if (!edev || !edev->pe || !eeh_ops->notify_resume) return -ENODEV; - if (eeh_ops->notify_resume(pci_get_pdn(pdev))) + if (eeh_ops->notify_resume(edev)) return -EIO; return count; diff --git a/arch/powerpc/platforms/pseries/eeh_pseries.c b/arch/powerpc/platforms/pseries/eeh_pseries.c index e32b49ead3137..57de8acad5f5a 100644 --- a/arch/powerpc/platforms/pseries/eeh_pseries.c +++ b/arch/powerpc/platforms/pseries/eeh_pseries.c @@ -899,10 +899,8 @@ static int pseries_call_allow_unfreeze(struct eeh_dev *edev) return rc; } -static int pseries_notify_resume(struct pci_dn *pdn) +static int pseries_notify_resume(struct eeh_dev *edev) { - struct eeh_dev *edev = pdn_to_eeh_dev(pdn); - if (!edev) return -EEXIST; -- GitLab From 17d2a4870467bc8e8966304c08980571da943558 Mon Sep 17 00:00:00 2001 From: Oliver O'Halloran Date: Sat, 25 Jul 2020 18:12:26 +1000 Subject: [PATCH 0836/1476] powerpc/eeh: Pass eeh_dev to eeh_ops->{read|write}_config() Mechanical conversion of the eeh_ops interfaces to use eeh_dev to reference a specific device rather than pci_dn. No functional changes. Signed-off-by: Oliver O'Halloran Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20200725081231.39076-9-oohall@gmail.com --- arch/powerpc/include/asm/eeh.h | 4 +- arch/powerpc/kernel/eeh.c | 22 +++++---- arch/powerpc/kernel/eeh_pe.c | 47 +++++++++----------- arch/powerpc/platforms/powernv/eeh-powernv.c | 43 ++++++++++-------- arch/powerpc/platforms/pseries/eeh_pseries.c | 16 ++++--- 5 files changed, 68 insertions(+), 64 deletions(-) diff --git a/arch/powerpc/include/asm/eeh.h b/arch/powerpc/include/asm/eeh.h index 73e6a73a36847..f8ef27f75c375 100644 --- a/arch/powerpc/include/asm/eeh.h +++ b/arch/powerpc/include/asm/eeh.h @@ -226,8 +226,8 @@ struct eeh_ops { int (*configure_bridge)(struct eeh_pe *pe); int (*err_inject)(struct eeh_pe *pe, int type, int func, unsigned long addr, unsigned long mask); - int (*read_config)(struct pci_dn *pdn, int where, int size, u32 *val); - int (*write_config)(struct pci_dn *pdn, int where, int size, u32 val); + int (*read_config)(struct eeh_dev *edev, int where, int size, u32 *val); + int (*write_config)(struct eeh_dev *edev, int where, int size, u32 val); int (*next_error)(struct eeh_pe **pe); int (*restore_config)(struct eeh_dev *edev); int (*notify_resume)(struct eeh_dev *edev); diff --git a/arch/powerpc/kernel/eeh.c b/arch/powerpc/kernel/eeh.c index 1cef0f4bb2d55..1a12c8bdf61ee 100644 --- a/arch/powerpc/kernel/eeh.c +++ b/arch/powerpc/kernel/eeh.c @@ -185,21 +185,21 @@ static size_t eeh_dump_dev_log(struct eeh_dev *edev, char *buf, size_t len) pdn->phb->global_number, pdn->busno, PCI_SLOT(pdn->devfn), PCI_FUNC(pdn->devfn)); - eeh_ops->read_config(pdn, PCI_VENDOR_ID, 4, &cfg); + eeh_ops->read_config(edev, PCI_VENDOR_ID, 4, &cfg); n += scnprintf(buf+n, len-n, "dev/vend:%08x\n", cfg); pr_warn("EEH: PCI device/vendor: %08x\n", cfg); - eeh_ops->read_config(pdn, PCI_COMMAND, 4, &cfg); + eeh_ops->read_config(edev, PCI_COMMAND, 4, &cfg); n += scnprintf(buf+n, len-n, "cmd/stat:%x\n", cfg); pr_warn("EEH: PCI cmd/status register: %08x\n", cfg); /* Gather bridge-specific registers */ if (edev->mode & EEH_DEV_BRIDGE) { - eeh_ops->read_config(pdn, PCI_SEC_STATUS, 2, &cfg); + eeh_ops->read_config(edev, PCI_SEC_STATUS, 2, &cfg); n += scnprintf(buf+n, len-n, "sec stat:%x\n", cfg); pr_warn("EEH: Bridge secondary status: %04x\n", cfg); - eeh_ops->read_config(pdn, PCI_BRIDGE_CONTROL, 2, &cfg); + eeh_ops->read_config(edev, PCI_BRIDGE_CONTROL, 2, &cfg); n += scnprintf(buf+n, len-n, "brdg ctl:%x\n", cfg); pr_warn("EEH: Bridge control: %04x\n", cfg); } @@ -207,11 +207,11 @@ static size_t eeh_dump_dev_log(struct eeh_dev *edev, char *buf, size_t len) /* Dump out the PCI-X command and status regs */ cap = edev->pcix_cap; if (cap) { - eeh_ops->read_config(pdn, cap, 4, &cfg); + eeh_ops->read_config(edev, cap, 4, &cfg); n += scnprintf(buf+n, len-n, "pcix-cmd:%x\n", cfg); pr_warn("EEH: PCI-X cmd: %08x\n", cfg); - eeh_ops->read_config(pdn, cap+4, 4, &cfg); + eeh_ops->read_config(edev, cap+4, 4, &cfg); n += scnprintf(buf+n, len-n, "pcix-stat:%x\n", cfg); pr_warn("EEH: PCI-X status: %08x\n", cfg); } @@ -223,7 +223,7 @@ static size_t eeh_dump_dev_log(struct eeh_dev *edev, char *buf, size_t len) pr_warn("EEH: PCI-E capabilities and status follow:\n"); for (i=0; i<=8; i++) { - eeh_ops->read_config(pdn, cap+4*i, 4, &cfg); + eeh_ops->read_config(edev, cap+4*i, 4, &cfg); n += scnprintf(buf+n, len-n, "%02x:%x\n", 4*i, cfg); if ((i % 4) == 0) { @@ -250,7 +250,7 @@ static size_t eeh_dump_dev_log(struct eeh_dev *edev, char *buf, size_t len) pr_warn("EEH: PCI-E AER capability register set follows:\n"); for (i=0; i<=13; i++) { - eeh_ops->read_config(pdn, cap+4*i, 4, &cfg); + eeh_ops->read_config(edev, cap+4*i, 4, &cfg); n += scnprintf(buf+n, len-n, "%02x:%x\n", 4*i, cfg); if ((i % 4) == 0) { @@ -917,15 +917,13 @@ int eeh_pe_reset_full(struct eeh_pe *pe, bool include_passed) */ void eeh_save_bars(struct eeh_dev *edev) { - struct pci_dn *pdn; int i; - pdn = eeh_dev_to_pdn(edev); - if (!pdn) + if (!edev) return; for (i = 0; i < 16; i++) - eeh_ops->read_config(pdn, i * 4, 4, &edev->config_space[i]); + eeh_ops->read_config(edev, i * 4, 4, &edev->config_space[i]); /* * For PCI bridges including root port, we need enable bus diff --git a/arch/powerpc/kernel/eeh_pe.c b/arch/powerpc/kernel/eeh_pe.c index d71493f669177..f20fb0ee6aec5 100644 --- a/arch/powerpc/kernel/eeh_pe.c +++ b/arch/powerpc/kernel/eeh_pe.c @@ -698,7 +698,6 @@ void eeh_pe_state_clear(struct eeh_pe *root, int state, bool include_passed) */ static void eeh_bridge_check_link(struct eeh_dev *edev) { - struct pci_dn *pdn = eeh_dev_to_pdn(edev); int cap; uint32_t val; int timeout = 0; @@ -714,32 +713,32 @@ static void eeh_bridge_check_link(struct eeh_dev *edev) /* Check slot status */ cap = edev->pcie_cap; - eeh_ops->read_config(pdn, cap + PCI_EXP_SLTSTA, 2, &val); + eeh_ops->read_config(edev, cap + PCI_EXP_SLTSTA, 2, &val); if (!(val & PCI_EXP_SLTSTA_PDS)) { eeh_edev_dbg(edev, "No card in the slot (0x%04x) !\n", val); return; } /* Check power status if we have the capability */ - eeh_ops->read_config(pdn, cap + PCI_EXP_SLTCAP, 2, &val); + eeh_ops->read_config(edev, cap + PCI_EXP_SLTCAP, 2, &val); if (val & PCI_EXP_SLTCAP_PCP) { - eeh_ops->read_config(pdn, cap + PCI_EXP_SLTCTL, 2, &val); + eeh_ops->read_config(edev, cap + PCI_EXP_SLTCTL, 2, &val); if (val & PCI_EXP_SLTCTL_PCC) { eeh_edev_dbg(edev, "In power-off state, power it on ...\n"); val &= ~(PCI_EXP_SLTCTL_PCC | PCI_EXP_SLTCTL_PIC); val |= (0x0100 & PCI_EXP_SLTCTL_PIC); - eeh_ops->write_config(pdn, cap + PCI_EXP_SLTCTL, 2, val); + eeh_ops->write_config(edev, cap + PCI_EXP_SLTCTL, 2, val); msleep(2 * 1000); } } /* Enable link */ - eeh_ops->read_config(pdn, cap + PCI_EXP_LNKCTL, 2, &val); + eeh_ops->read_config(edev, cap + PCI_EXP_LNKCTL, 2, &val); val &= ~PCI_EXP_LNKCTL_LD; - eeh_ops->write_config(pdn, cap + PCI_EXP_LNKCTL, 2, val); + eeh_ops->write_config(edev, cap + PCI_EXP_LNKCTL, 2, val); /* Check link */ - eeh_ops->read_config(pdn, cap + PCI_EXP_LNKCAP, 4, &val); + eeh_ops->read_config(edev, cap + PCI_EXP_LNKCAP, 4, &val); if (!(val & PCI_EXP_LNKCAP_DLLLARC)) { eeh_edev_dbg(edev, "No link reporting capability (0x%08x) \n", val); msleep(1000); @@ -752,7 +751,7 @@ static void eeh_bridge_check_link(struct eeh_dev *edev) msleep(20); timeout += 20; - eeh_ops->read_config(pdn, cap + PCI_EXP_LNKSTA, 2, &val); + eeh_ops->read_config(edev, cap + PCI_EXP_LNKSTA, 2, &val); if (val & PCI_EXP_LNKSTA_DLLLA) break; } @@ -769,7 +768,6 @@ static void eeh_bridge_check_link(struct eeh_dev *edev) static void eeh_restore_bridge_bars(struct eeh_dev *edev) { - struct pci_dn *pdn = eeh_dev_to_pdn(edev); int i; /* @@ -777,20 +775,20 @@ static void eeh_restore_bridge_bars(struct eeh_dev *edev) * Bus numbers and windows: 0x18 - 0x30 */ for (i = 4; i < 13; i++) - eeh_ops->write_config(pdn, i*4, 4, edev->config_space[i]); + eeh_ops->write_config(edev, i*4, 4, edev->config_space[i]); /* Rom: 0x38 */ - eeh_ops->write_config(pdn, 14*4, 4, edev->config_space[14]); + eeh_ops->write_config(edev, 14*4, 4, edev->config_space[14]); /* Cache line & Latency timer: 0xC 0xD */ - eeh_ops->write_config(pdn, PCI_CACHE_LINE_SIZE, 1, + eeh_ops->write_config(edev, PCI_CACHE_LINE_SIZE, 1, SAVED_BYTE(PCI_CACHE_LINE_SIZE)); - eeh_ops->write_config(pdn, PCI_LATENCY_TIMER, 1, - SAVED_BYTE(PCI_LATENCY_TIMER)); + eeh_ops->write_config(edev, PCI_LATENCY_TIMER, 1, + SAVED_BYTE(PCI_LATENCY_TIMER)); /* Max latency, min grant, interrupt ping and line: 0x3C */ - eeh_ops->write_config(pdn, 15*4, 4, edev->config_space[15]); + eeh_ops->write_config(edev, 15*4, 4, edev->config_space[15]); /* PCI Command: 0x4 */ - eeh_ops->write_config(pdn, PCI_COMMAND, 4, edev->config_space[1] | + eeh_ops->write_config(edev, PCI_COMMAND, 4, edev->config_space[1] | PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER); /* Check the PCIe link is ready */ @@ -799,28 +797,27 @@ static void eeh_restore_bridge_bars(struct eeh_dev *edev) static void eeh_restore_device_bars(struct eeh_dev *edev) { - struct pci_dn *pdn = eeh_dev_to_pdn(edev); int i; u32 cmd; for (i = 4; i < 10; i++) - eeh_ops->write_config(pdn, i*4, 4, edev->config_space[i]); + eeh_ops->write_config(edev, i*4, 4, edev->config_space[i]); /* 12 == Expansion ROM Address */ - eeh_ops->write_config(pdn, 12*4, 4, edev->config_space[12]); + eeh_ops->write_config(edev, 12*4, 4, edev->config_space[12]); - eeh_ops->write_config(pdn, PCI_CACHE_LINE_SIZE, 1, + eeh_ops->write_config(edev, PCI_CACHE_LINE_SIZE, 1, SAVED_BYTE(PCI_CACHE_LINE_SIZE)); - eeh_ops->write_config(pdn, PCI_LATENCY_TIMER, 1, + eeh_ops->write_config(edev, PCI_LATENCY_TIMER, 1, SAVED_BYTE(PCI_LATENCY_TIMER)); /* max latency, min grant, interrupt pin and line */ - eeh_ops->write_config(pdn, 15*4, 4, edev->config_space[15]); + eeh_ops->write_config(edev, 15*4, 4, edev->config_space[15]); /* * Restore PERR & SERR bits, some devices require it, * don't touch the other command bits */ - eeh_ops->read_config(pdn, PCI_COMMAND, 4, &cmd); + eeh_ops->read_config(edev, PCI_COMMAND, 4, &cmd); if (edev->config_space[1] & PCI_COMMAND_PARITY) cmd |= PCI_COMMAND_PARITY; else @@ -829,7 +826,7 @@ static void eeh_restore_device_bars(struct eeh_dev *edev) cmd |= PCI_COMMAND_SERR; else cmd &= ~PCI_COMMAND_SERR; - eeh_ops->write_config(pdn, PCI_COMMAND, 4, cmd); + eeh_ops->write_config(edev, PCI_COMMAND, 4, cmd); } /** diff --git a/arch/powerpc/platforms/powernv/eeh-powernv.c b/arch/powerpc/platforms/powernv/eeh-powernv.c index a41e67f674e62..c9f2f454d0530 100644 --- a/arch/powerpc/platforms/powernv/eeh-powernv.c +++ b/arch/powerpc/platforms/powernv/eeh-powernv.c @@ -838,32 +838,32 @@ static int __pnv_eeh_bridge_reset(struct pci_dev *dev, int option) case EEH_RESET_HOT: /* Don't report linkDown event */ if (aer) { - eeh_ops->read_config(pdn, aer + PCI_ERR_UNCOR_MASK, + eeh_ops->read_config(edev, aer + PCI_ERR_UNCOR_MASK, 4, &ctrl); ctrl |= PCI_ERR_UNC_SURPDN; - eeh_ops->write_config(pdn, aer + PCI_ERR_UNCOR_MASK, + eeh_ops->write_config(edev, aer + PCI_ERR_UNCOR_MASK, 4, ctrl); } - eeh_ops->read_config(pdn, PCI_BRIDGE_CONTROL, 2, &ctrl); + eeh_ops->read_config(edev, PCI_BRIDGE_CONTROL, 2, &ctrl); ctrl |= PCI_BRIDGE_CTL_BUS_RESET; - eeh_ops->write_config(pdn, PCI_BRIDGE_CONTROL, 2, ctrl); + eeh_ops->write_config(edev, PCI_BRIDGE_CONTROL, 2, ctrl); msleep(EEH_PE_RST_HOLD_TIME); break; case EEH_RESET_DEACTIVATE: - eeh_ops->read_config(pdn, PCI_BRIDGE_CONTROL, 2, &ctrl); + eeh_ops->read_config(edev, PCI_BRIDGE_CONTROL, 2, &ctrl); ctrl &= ~PCI_BRIDGE_CTL_BUS_RESET; - eeh_ops->write_config(pdn, PCI_BRIDGE_CONTROL, 2, ctrl); + eeh_ops->write_config(edev, PCI_BRIDGE_CONTROL, 2, ctrl); msleep(EEH_PE_RST_SETTLE_TIME); /* Continue reporting linkDown event */ if (aer) { - eeh_ops->read_config(pdn, aer + PCI_ERR_UNCOR_MASK, + eeh_ops->read_config(edev, aer + PCI_ERR_UNCOR_MASK, 4, &ctrl); ctrl &= ~PCI_ERR_UNC_SURPDN; - eeh_ops->write_config(pdn, aer + PCI_ERR_UNCOR_MASK, + eeh_ops->write_config(edev, aer + PCI_ERR_UNCOR_MASK, 4, ctrl); } @@ -932,11 +932,12 @@ void pnv_pci_reset_secondary_bus(struct pci_dev *dev) static void pnv_eeh_wait_for_pending(struct pci_dn *pdn, const char *type, int pos, u16 mask) { + struct eeh_dev *edev = pdn->edev; int i, status = 0; /* Wait for Transaction Pending bit to be cleared */ for (i = 0; i < 4; i++) { - eeh_ops->read_config(pdn, pos, 2, &status); + eeh_ops->read_config(edev, pos, 2, &status); if (!(status & mask)) return; @@ -957,7 +958,7 @@ static int pnv_eeh_do_flr(struct pci_dn *pdn, int option) if (WARN_ON(!edev->pcie_cap)) return -ENOTTY; - eeh_ops->read_config(pdn, edev->pcie_cap + PCI_EXP_DEVCAP, 4, ®); + eeh_ops->read_config(edev, edev->pcie_cap + PCI_EXP_DEVCAP, 4, ®); if (!(reg & PCI_EXP_DEVCAP_FLR)) return -ENOTTY; @@ -967,18 +968,18 @@ static int pnv_eeh_do_flr(struct pci_dn *pdn, int option) pnv_eeh_wait_for_pending(pdn, "", edev->pcie_cap + PCI_EXP_DEVSTA, PCI_EXP_DEVSTA_TRPND); - eeh_ops->read_config(pdn, edev->pcie_cap + PCI_EXP_DEVCTL, + eeh_ops->read_config(edev, edev->pcie_cap + PCI_EXP_DEVCTL, 4, ®); reg |= PCI_EXP_DEVCTL_BCR_FLR; - eeh_ops->write_config(pdn, edev->pcie_cap + PCI_EXP_DEVCTL, + eeh_ops->write_config(edev, edev->pcie_cap + PCI_EXP_DEVCTL, 4, reg); msleep(EEH_PE_RST_HOLD_TIME); break; case EEH_RESET_DEACTIVATE: - eeh_ops->read_config(pdn, edev->pcie_cap + PCI_EXP_DEVCTL, + eeh_ops->read_config(edev, edev->pcie_cap + PCI_EXP_DEVCTL, 4, ®); reg &= ~PCI_EXP_DEVCTL_BCR_FLR; - eeh_ops->write_config(pdn, edev->pcie_cap + PCI_EXP_DEVCTL, + eeh_ops->write_config(edev, edev->pcie_cap + PCI_EXP_DEVCTL, 4, reg); msleep(EEH_PE_RST_SETTLE_TIME); break; @@ -995,7 +996,7 @@ static int pnv_eeh_do_af_flr(struct pci_dn *pdn, int option) if (WARN_ON(!edev->af_cap)) return -ENOTTY; - eeh_ops->read_config(pdn, edev->af_cap + PCI_AF_CAP, 1, &cap); + eeh_ops->read_config(edev, edev->af_cap + PCI_AF_CAP, 1, &cap); if (!(cap & PCI_AF_CAP_TP) || !(cap & PCI_AF_CAP_FLR)) return -ENOTTY; @@ -1010,12 +1011,12 @@ static int pnv_eeh_do_af_flr(struct pci_dn *pdn, int option) pnv_eeh_wait_for_pending(pdn, "AF", edev->af_cap + PCI_AF_CTRL, PCI_AF_STATUS_TP << 8); - eeh_ops->write_config(pdn, edev->af_cap + PCI_AF_CTRL, + eeh_ops->write_config(edev, edev->af_cap + PCI_AF_CTRL, 1, PCI_AF_CTRL_FLR); msleep(EEH_PE_RST_HOLD_TIME); break; case EEH_RESET_DEACTIVATE: - eeh_ops->write_config(pdn, edev->af_cap + PCI_AF_CTRL, 1, 0); + eeh_ops->write_config(edev, edev->af_cap + PCI_AF_CTRL, 1, 0); msleep(EEH_PE_RST_SETTLE_TIME); break; } @@ -1249,9 +1250,11 @@ static inline bool pnv_eeh_cfg_blocked(struct pci_dn *pdn) return false; } -static int pnv_eeh_read_config(struct pci_dn *pdn, +static int pnv_eeh_read_config(struct eeh_dev *edev, int where, int size, u32 *val) { + struct pci_dn *pdn = eeh_dev_to_pdn(edev); + if (!pdn) return PCIBIOS_DEVICE_NOT_FOUND; @@ -1263,9 +1266,11 @@ static int pnv_eeh_read_config(struct pci_dn *pdn, return pnv_pci_cfg_read(pdn, where, size, val); } -static int pnv_eeh_write_config(struct pci_dn *pdn, +static int pnv_eeh_write_config(struct eeh_dev *edev, int where, int size, u32 val) { + struct pci_dn *pdn = eeh_dev_to_pdn(edev); + if (!pdn) return PCIBIOS_DEVICE_NOT_FOUND; diff --git a/arch/powerpc/platforms/pseries/eeh_pseries.c b/arch/powerpc/platforms/pseries/eeh_pseries.c index 57de8acad5f5a..53e5381b3d536 100644 --- a/arch/powerpc/platforms/pseries/eeh_pseries.c +++ b/arch/powerpc/platforms/pseries/eeh_pseries.c @@ -798,29 +798,33 @@ static int pseries_eeh_configure_bridge(struct eeh_pe *pe) /** * pseries_eeh_read_config - Read PCI config space - * @pdn: PCI device node - * @where: PCI address + * @edev: EEH device handle + * @where: PCI config space offset * @size: size to read * @val: return value * * Read config space from the speicifed device */ -static int pseries_eeh_read_config(struct pci_dn *pdn, int where, int size, u32 *val) +static int pseries_eeh_read_config(struct eeh_dev *edev, int where, int size, u32 *val) { + struct pci_dn *pdn = eeh_dev_to_pdn(edev); + return rtas_read_config(pdn, where, size, val); } /** * pseries_eeh_write_config - Write PCI config space - * @pdn: PCI device node - * @where: PCI address + * @edev: EEH device handle + * @where: PCI config space offset * @size: size to write * @val: value to be written * * Write config space to the specified device */ -static int pseries_eeh_write_config(struct pci_dn *pdn, int where, int size, u32 val) +static int pseries_eeh_write_config(struct eeh_dev *edev, int where, int size, u32 val) { + struct pci_dn *pdn = eeh_dev_to_pdn(edev); + return rtas_write_config(pdn, where, size, val); } -- GitLab From 1a303d8844d082ef58ff5fc3005b99621a3263ba Mon Sep 17 00:00:00 2001 From: Oliver O'Halloran Date: Sat, 25 Jul 2020 18:12:27 +1000 Subject: [PATCH 0837/1476] powerpc/eeh: Remove spurious use of pci_dn in eeh_dump_dev_log Retrieve the domain, bus, device, and function numbers from the edev. Signed-off-by: Oliver O'Halloran Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20200725081231.39076-10-oohall@gmail.com --- arch/powerpc/kernel/eeh.c | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/arch/powerpc/kernel/eeh.c b/arch/powerpc/kernel/eeh.c index 1a12c8bdf61ee..f203ffc5c57d2 100644 --- a/arch/powerpc/kernel/eeh.c +++ b/arch/powerpc/kernel/eeh.c @@ -167,23 +167,17 @@ void eeh_show_enabled(void) */ static size_t eeh_dump_dev_log(struct eeh_dev *edev, char *buf, size_t len) { - struct pci_dn *pdn = eeh_dev_to_pdn(edev); u32 cfg; int cap, i; int n = 0, l = 0; char buffer[128]; - if (!pdn) { - pr_warn("EEH: Note: No error log for absent device.\n"); - return 0; - } - n += scnprintf(buf+n, len-n, "%04x:%02x:%02x.%01x\n", - pdn->phb->global_number, pdn->busno, - PCI_SLOT(pdn->devfn), PCI_FUNC(pdn->devfn)); + edev->pe->phb->global_number, edev->bdfn >> 8, + PCI_SLOT(edev->bdfn), PCI_FUNC(edev->bdfn)); pr_warn("EEH: of node=%04x:%02x:%02x.%01x\n", - pdn->phb->global_number, pdn->busno, - PCI_SLOT(pdn->devfn), PCI_FUNC(pdn->devfn)); + edev->pe->phb->global_number, edev->bdfn >> 8, + PCI_SLOT(edev->bdfn), PCI_FUNC(edev->bdfn)); eeh_ops->read_config(edev, PCI_VENDOR_ID, 4, &cfg); n += scnprintf(buf+n, len-n, "dev/vend:%08x\n", cfg); -- GitLab From 768a42845b9ecdb28ba1991e17088b7eeb23a3eb Mon Sep 17 00:00:00 2001 From: Oliver O'Halloran Date: Sat, 25 Jul 2020 18:12:28 +1000 Subject: [PATCH 0838/1476] powerpc/eeh: Remove class code field from edev The edev->class_code field is never referenced anywhere except for the platform specific probe functions. The same information is available in the pci_dev for PowerNV and in the pci_dn on pseries so we can remove the field. Signed-off-by: Oliver O'Halloran Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20200725081231.39076-11-oohall@gmail.com --- arch/powerpc/include/asm/eeh.h | 1 - arch/powerpc/platforms/powernv/eeh-powernv.c | 5 ++--- arch/powerpc/platforms/pseries/eeh_pseries.c | 3 +-- 3 files changed, 3 insertions(+), 6 deletions(-) diff --git a/arch/powerpc/include/asm/eeh.h b/arch/powerpc/include/asm/eeh.h index f8ef27f75c375..79de8624809a7 100644 --- a/arch/powerpc/include/asm/eeh.h +++ b/arch/powerpc/include/asm/eeh.h @@ -133,7 +133,6 @@ static inline bool eeh_pe_passed(struct eeh_pe *pe) struct eeh_dev { int mode; /* EEH mode */ - int class_code; /* Class code of the device */ int bdfn; /* bdfn of device (for cfg ops) */ struct pci_controller *controller; int pe_config_addr; /* PE config address */ diff --git a/arch/powerpc/platforms/powernv/eeh-powernv.c b/arch/powerpc/platforms/powernv/eeh-powernv.c index c9f2f454d0530..7cbb03a97a61c 100644 --- a/arch/powerpc/platforms/powernv/eeh-powernv.c +++ b/arch/powerpc/platforms/powernv/eeh-powernv.c @@ -372,19 +372,18 @@ static struct eeh_dev *pnv_eeh_probe(struct pci_dev *pdev) } /* Skip for PCI-ISA bridge */ - if ((pdn->class_code >> 8) == PCI_CLASS_BRIDGE_ISA) + if ((pdev->class >> 8) == PCI_CLASS_BRIDGE_ISA) return NULL; eeh_edev_dbg(edev, "Probing device\n"); /* Initialize eeh device */ - edev->class_code = pdn->class_code; edev->mode &= 0xFFFFFF00; edev->pcix_cap = pnv_eeh_find_cap(pdn, PCI_CAP_ID_PCIX); edev->pcie_cap = pnv_eeh_find_cap(pdn, PCI_CAP_ID_EXP); edev->af_cap = pnv_eeh_find_cap(pdn, PCI_CAP_ID_AF); edev->aer_cap = pnv_eeh_find_ecap(pdn, PCI_EXT_CAP_ID_ERR); - if ((edev->class_code >> 8) == PCI_CLASS_BRIDGE_PCI) { + if ((pdev->class >> 8) == PCI_CLASS_BRIDGE_PCI) { edev->mode |= EEH_DEV_BRIDGE; if (edev->pcie_cap) { pnv_pci_cfg_read(pdn, edev->pcie_cap + PCI_EXP_FLAGS, diff --git a/arch/powerpc/platforms/pseries/eeh_pseries.c b/arch/powerpc/platforms/pseries/eeh_pseries.c index 53e5381b3d536..902b929ef307e 100644 --- a/arch/powerpc/platforms/pseries/eeh_pseries.c +++ b/arch/powerpc/platforms/pseries/eeh_pseries.c @@ -440,12 +440,11 @@ void pseries_eeh_init_edev(struct pci_dn *pdn) * correctly reflects that current device is root port * or PCIe switch downstream port. */ - edev->class_code = pdn->class_code; edev->pcix_cap = pseries_eeh_find_cap(pdn, PCI_CAP_ID_PCIX); edev->pcie_cap = pseries_eeh_find_cap(pdn, PCI_CAP_ID_EXP); edev->aer_cap = pseries_eeh_find_ecap(pdn, PCI_EXT_CAP_ID_ERR); edev->mode &= 0xFFFFFF00; - if ((edev->class_code >> 8) == PCI_CLASS_BRIDGE_PCI) { + if ((pdn->class_code >> 8) == PCI_CLASS_BRIDGE_PCI) { edev->mode |= EEH_DEV_BRIDGE; if (edev->pcie_cap) { rtas_read_config(pdn, edev->pcie_cap + PCI_EXP_FLAGS, -- GitLab From d923ab7a96fcc2b46aac9b2fc38ffdca72436fd1 Mon Sep 17 00:00:00 2001 From: Oliver O'Halloran Date: Sat, 25 Jul 2020 18:12:29 +1000 Subject: [PATCH 0839/1476] powerpc/eeh: Rename eeh_{add_to|remove_from}_parent_pe() The naming of eeh_{add_to|remove_from}_parent_pe() doesn't really reflect what they actually do. If the PE referred to be edev->pe_config_addr already exists under that PHB then the edev is added to that PE. However, if the PE doesn't exist the a new one is created for the edev. The bulk of the implementation of eeh_add_to_parent_pe() covers that second case. Similarly, most of eeh_remove_from_parent_pe() is determining when it's safe to delete a PE. Signed-off-by: Oliver O'Halloran Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20200725081231.39076-12-oohall@gmail.com --- arch/powerpc/include/asm/eeh.h | 4 ++-- arch/powerpc/kernel/eeh.c | 4 ++-- arch/powerpc/kernel/eeh_driver.c | 2 +- arch/powerpc/kernel/eeh_pe.c | 8 ++++---- arch/powerpc/kernel/pci_dn.c | 2 +- arch/powerpc/platforms/powernv/eeh-powernv.c | 2 +- arch/powerpc/platforms/pseries/eeh_pseries.c | 8 ++++---- 7 files changed, 15 insertions(+), 15 deletions(-) diff --git a/arch/powerpc/include/asm/eeh.h b/arch/powerpc/include/asm/eeh.h index 79de8624809a7..ee5f354bc8b96 100644 --- a/arch/powerpc/include/asm/eeh.h +++ b/arch/powerpc/include/asm/eeh.h @@ -283,8 +283,8 @@ struct eeh_pe *eeh_phb_pe_get(struct pci_controller *phb); struct eeh_pe *eeh_pe_next(struct eeh_pe *pe, struct eeh_pe *root); struct eeh_pe *eeh_pe_get(struct pci_controller *phb, int pe_no, int config_addr); -int eeh_add_to_parent_pe(struct eeh_dev *edev); -int eeh_rmv_from_parent_pe(struct eeh_dev *edev); +int eeh_pe_tree_insert(struct eeh_dev *edev); +int eeh_pe_tree_remove(struct eeh_dev *edev); void eeh_pe_update_time_stamp(struct eeh_pe *pe); void *eeh_pe_traverse(struct eeh_pe *root, eeh_pe_traverse_func fn, void *flag); diff --git a/arch/powerpc/kernel/eeh.c b/arch/powerpc/kernel/eeh.c index f203ffc5c57d2..94682382fc8c7 100644 --- a/arch/powerpc/kernel/eeh.c +++ b/arch/powerpc/kernel/eeh.c @@ -1107,7 +1107,7 @@ void eeh_probe_device(struct pci_dev *dev) * FIXME: HEY MA, LOOK AT ME, NO LOCKING! */ if (edev->pdev && edev->pdev != dev) { - eeh_rmv_from_parent_pe(edev); + eeh_pe_tree_remove(edev); eeh_addr_cache_rmv_dev(edev->pdev); eeh_sysfs_remove_device(edev->pdev); @@ -1186,7 +1186,7 @@ void eeh_remove_device(struct pci_dev *dev) edev->in_error = false; dev->dev.archdata.edev = NULL; if (!(edev->pe->state & EEH_PE_KEEP)) - eeh_rmv_from_parent_pe(edev); + eeh_pe_tree_remove(edev); else edev->mode |= EEH_DEV_DISCONNECTED; } diff --git a/arch/powerpc/kernel/eeh_driver.c b/arch/powerpc/kernel/eeh_driver.c index b84d3cb2532e2..4197e4559f659 100644 --- a/arch/powerpc/kernel/eeh_driver.c +++ b/arch/powerpc/kernel/eeh_driver.c @@ -542,7 +542,7 @@ static void *eeh_pe_detach_dev(struct eeh_pe *pe, void *userdata) continue; edev->mode &= ~(EEH_DEV_DISCONNECTED | EEH_DEV_IRQ_DISABLED); - eeh_rmv_from_parent_pe(edev); + eeh_pe_tree_remove(edev); } return NULL; diff --git a/arch/powerpc/kernel/eeh_pe.c b/arch/powerpc/kernel/eeh_pe.c index f20fb0ee6aec5..97bf09db2ecda 100644 --- a/arch/powerpc/kernel/eeh_pe.c +++ b/arch/powerpc/kernel/eeh_pe.c @@ -356,7 +356,7 @@ static struct eeh_pe *eeh_pe_get_parent(struct eeh_dev *edev) } /** - * eeh_add_to_parent_pe - Add EEH device to parent PE + * eeh_pe_tree_insert - Add EEH device to parent PE * @edev: EEH device * * Add EEH device to the parent PE. If the parent PE already @@ -364,7 +364,7 @@ static struct eeh_pe *eeh_pe_get_parent(struct eeh_dev *edev) * we have to create new PE to hold the EEH device and the new * PE will be linked to its parent PE as well. */ -int eeh_add_to_parent_pe(struct eeh_dev *edev) +int eeh_pe_tree_insert(struct eeh_dev *edev) { struct eeh_pe *pe, *parent; struct pci_dn *pdn = eeh_dev_to_pdn(edev); @@ -459,7 +459,7 @@ int eeh_add_to_parent_pe(struct eeh_dev *edev) } /** - * eeh_rmv_from_parent_pe - Remove one EEH device from the associated PE + * eeh_pe_tree_remove - Remove one EEH device from the associated PE * @edev: EEH device * * The PE hierarchy tree might be changed when doing PCI hotplug. @@ -467,7 +467,7 @@ int eeh_add_to_parent_pe(struct eeh_dev *edev) * during EEH recovery. So we have to call the function remove the * corresponding PE accordingly if necessary. */ -int eeh_rmv_from_parent_pe(struct eeh_dev *edev) +int eeh_pe_tree_remove(struct eeh_dev *edev) { struct eeh_pe *pe, *parent, *child; bool keep, recover; diff --git a/arch/powerpc/kernel/pci_dn.c b/arch/powerpc/kernel/pci_dn.c index bf11ac8427acd..e99b7c547d7e9 100644 --- a/arch/powerpc/kernel/pci_dn.c +++ b/arch/powerpc/kernel/pci_dn.c @@ -263,7 +263,7 @@ void remove_sriov_vf_pdns(struct pci_dev *pdev) * have a configured PE. */ if (edev->pe) - eeh_rmv_from_parent_pe(edev); + eeh_pe_tree_remove(edev); pdn->edev = NULL; kfree(edev); diff --git a/arch/powerpc/platforms/powernv/eeh-powernv.c b/arch/powerpc/platforms/powernv/eeh-powernv.c index 7cbb03a97a61c..8c9fca7736929 100644 --- a/arch/powerpc/platforms/powernv/eeh-powernv.c +++ b/arch/powerpc/platforms/powernv/eeh-powernv.c @@ -399,7 +399,7 @@ static struct eeh_dev *pnv_eeh_probe(struct pci_dev *pdev) edev->pe_config_addr = phb->ioda.pe_rmap[config_addr]; /* Create PE */ - ret = eeh_add_to_parent_pe(edev); + ret = eeh_pe_tree_insert(edev); if (ret) { eeh_edev_warn(edev, "Failed to add device to PE (code %d)\n", ret); return NULL; diff --git a/arch/powerpc/platforms/pseries/eeh_pseries.c b/arch/powerpc/platforms/pseries/eeh_pseries.c index 902b929ef307e..69650d8dc7058 100644 --- a/arch/powerpc/platforms/pseries/eeh_pseries.c +++ b/arch/powerpc/platforms/pseries/eeh_pseries.c @@ -72,8 +72,8 @@ void pseries_pcibios_bus_add_device(struct pci_dev *pdev) struct eeh_dev *edev = pdn_to_eeh_dev(pdn); edev->pe_config_addr = (pdn->busno << 16) | (pdn->devfn << 8); - eeh_rmv_from_parent_pe(edev); /* Remove as it is adding to bus pe */ - eeh_add_to_parent_pe(edev); /* Add as VF PE type */ + eeh_pe_tree_remove(edev); /* Remove as it is adding to bus pe */ + eeh_pe_tree_insert(edev); /* Add as VF PE type */ } #endif eeh_probe_device(pdev); @@ -482,14 +482,14 @@ void pseries_eeh_init_edev(struct pci_dn *pdn) if (enable) { eeh_add_flag(EEH_ENABLED); - eeh_add_to_parent_pe(edev); + eeh_pe_tree_insert(edev); } else if (pdn->parent && pdn_to_eeh_dev(pdn->parent) && (pdn_to_eeh_dev(pdn->parent))->pe) { /* This device doesn't support EEH, but it may have an * EEH parent, in which case we mark it as supported. */ edev->pe_config_addr = pdn_to_eeh_dev(pdn->parent)->pe_config_addr; - eeh_add_to_parent_pe(edev); + eeh_pe_tree_insert(edev); } eeh_edev_dbg(edev, "EEH is %s on device (code %d)\n", (enable ? "enabled" : "unsupported"), ret); -- GitLab From 31595ae5aece519be5faa2e2013278ce45894d26 Mon Sep 17 00:00:00 2001 From: Oliver O'Halloran Date: Sat, 25 Jul 2020 18:12:30 +1000 Subject: [PATCH 0840/1476] powerpc/eeh: Drop pdn use in eeh_pe_tree_insert() This is mostly just to make the subsequent diffs less noisy. No functional changes. One thing that needs calling out is the removal of the "config_addr" variable and replacing it with edev->bdfn. The contents of edev->bdfn are the same, however it's worth pointing out that what RTAS calls a "config_addr" isn't the same as the bdfn. The config_addr is supposed to be: with each field being an 8 bit number. Various parts of the EEH code use BDFN and "config_addr" as interchangeable quantities even though they aren't really. Signed-off-by: Oliver O'Halloran Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20200725081231.39076-13-oohall@gmail.com --- arch/powerpc/kernel/eeh_pe.c | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/arch/powerpc/kernel/eeh_pe.c b/arch/powerpc/kernel/eeh_pe.c index 97bf09db2ecda..898205829a8fa 100644 --- a/arch/powerpc/kernel/eeh_pe.c +++ b/arch/powerpc/kernel/eeh_pe.c @@ -366,9 +366,8 @@ static struct eeh_pe *eeh_pe_get_parent(struct eeh_dev *edev) */ int eeh_pe_tree_insert(struct eeh_dev *edev) { + struct pci_controller *hose = edev->controller; struct eeh_pe *pe, *parent; - struct pci_dn *pdn = eeh_dev_to_pdn(edev); - int config_addr = (pdn->busno << 8) | (pdn->devfn); /* Check if the PE number is valid */ if (!eeh_has_flag(EEH_VALID_PE_ZERO) && !edev->pe_config_addr) { @@ -382,7 +381,7 @@ int eeh_pe_tree_insert(struct eeh_dev *edev) * PE should be composed of PCI bus and its subordinate * components. */ - pe = eeh_pe_get(pdn->phb, edev->pe_config_addr, config_addr); + pe = eeh_pe_get(hose, edev->pe_config_addr, edev->bdfn); if (pe) { if (pe->type & EEH_PE_INVALID) { list_add_tail(&edev->entry, &pe->edevs); @@ -416,15 +415,15 @@ int eeh_pe_tree_insert(struct eeh_dev *edev) /* Create a new EEH PE */ if (edev->physfn) - pe = eeh_pe_alloc(pdn->phb, EEH_PE_VF); + pe = eeh_pe_alloc(hose, EEH_PE_VF); else - pe = eeh_pe_alloc(pdn->phb, EEH_PE_DEVICE); + pe = eeh_pe_alloc(hose, EEH_PE_DEVICE); if (!pe) { pr_err("%s: out of memory!\n", __func__); return -ENOMEM; } pe->addr = edev->pe_config_addr; - pe->config_addr = config_addr; + pe->config_addr = edev->bdfn; /* * Put the new EEH PE into hierarchy tree. If the parent @@ -434,10 +433,10 @@ int eeh_pe_tree_insert(struct eeh_dev *edev) */ parent = eeh_pe_get_parent(edev); if (!parent) { - parent = eeh_phb_pe_get(pdn->phb); + parent = eeh_phb_pe_get(hose); if (!parent) { pr_err("%s: No PHB PE is found (PHB Domain=%d)\n", - __func__, pdn->phb->global_number); + __func__, hose->global_number); edev->pe = NULL; kfree(pe); return -EEXIST; -- GitLab From a131bfc69bc868083a6c7f9b5dad1331902a3534 Mon Sep 17 00:00:00 2001 From: Oliver O'Halloran Date: Sat, 25 Jul 2020 18:12:31 +1000 Subject: [PATCH 0841/1476] powerpc/eeh: Move PE tree setup into the platform The EEH core has a concept of a "PE tree" to support PowerNV. The PE tree follows the PCI bus structures because a reset asserted on an upstream bridge will be propagated to the downstream bridges. On pseries there's a 1-1 correspondence between what the guest sees are a PHB and a PE so the "tree" is really just a single node. Current the EEH core is reponsible for setting up this PE tree which it does by traversing the pci_dn tree. The structure of the pci_dn tree matches the bus tree on PowerNV which leads to the PE tree being "correct" this setup method doesn't make a whole lot of sense and it's actively confusing for the pseries case where it doesn't really do anything. We want to remove the dependence on pci_dn anyway so this patch move choosing where to insert a new PE into the platform code rather than being part of the generic EEH code. For PowerNV this simplifies the tree building logic and removes the use of pci_dn. For pseries we keep the existing logic. I'm not really convinced it does anything due to the 1-1 PE-to-PHB correspondence so every device under that PHB should be in the same PE, but I'd rather not remove it entirely until we've had a chance to look at it more deeply. Signed-off-by: Oliver O'Halloran Reviewed-by: Alexey Kardashevskiy Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20200725081231.39076-14-oohall@gmail.com --- arch/powerpc/include/asm/eeh.h | 2 +- arch/powerpc/kernel/eeh_pe.c | 71 ++++++-------------- arch/powerpc/platforms/powernv/eeh-powernv.c | 27 +++++++- arch/powerpc/platforms/pseries/eeh_pseries.c | 67 +++++++++++++++--- 4 files changed, 103 insertions(+), 64 deletions(-) diff --git a/arch/powerpc/include/asm/eeh.h b/arch/powerpc/include/asm/eeh.h index ee5f354bc8b96..d5f369bcd130c 100644 --- a/arch/powerpc/include/asm/eeh.h +++ b/arch/powerpc/include/asm/eeh.h @@ -283,7 +283,7 @@ struct eeh_pe *eeh_phb_pe_get(struct pci_controller *phb); struct eeh_pe *eeh_pe_next(struct eeh_pe *pe, struct eeh_pe *root); struct eeh_pe *eeh_pe_get(struct pci_controller *phb, int pe_no, int config_addr); -int eeh_pe_tree_insert(struct eeh_dev *edev); +int eeh_pe_tree_insert(struct eeh_dev *edev, struct eeh_pe *new_pe_parent); int eeh_pe_tree_remove(struct eeh_dev *edev); void eeh_pe_update_time_stamp(struct eeh_pe *pe); void *eeh_pe_traverse(struct eeh_pe *root, diff --git a/arch/powerpc/kernel/eeh_pe.c b/arch/powerpc/kernel/eeh_pe.c index 898205829a8fa..d2aaaa73fdd5e 100644 --- a/arch/powerpc/kernel/eeh_pe.c +++ b/arch/powerpc/kernel/eeh_pe.c @@ -318,53 +318,20 @@ struct eeh_pe *eeh_pe_get(struct pci_controller *phb, return pe; } -/** - * eeh_pe_get_parent - Retrieve the parent PE - * @edev: EEH device - * - * The whole PEs existing in the system are organized as hierarchy - * tree. The function is used to retrieve the parent PE according - * to the parent EEH device. - */ -static struct eeh_pe *eeh_pe_get_parent(struct eeh_dev *edev) -{ - struct eeh_dev *parent; - struct pci_dn *pdn = eeh_dev_to_pdn(edev); - - /* - * It might have the case for the indirect parent - * EEH device already having associated PE, but - * the direct parent EEH device doesn't have yet. - */ - if (edev->physfn) - pdn = pci_get_pdn(edev->physfn); - else - pdn = pdn ? pdn->parent : NULL; - while (pdn) { - /* We're poking out of PCI territory */ - parent = pdn_to_eeh_dev(pdn); - if (!parent) - return NULL; - - if (parent->pe) - return parent->pe; - - pdn = pdn->parent; - } - - return NULL; -} - /** * eeh_pe_tree_insert - Add EEH device to parent PE * @edev: EEH device + * @new_pe_parent: PE to create additional PEs under * - * Add EEH device to the parent PE. If the parent PE already - * exists, the PE type will be changed to EEH_PE_BUS. Otherwise, - * we have to create new PE to hold the EEH device and the new - * PE will be linked to its parent PE as well. + * Add EEH device to the PE in edev->pe_config_addr. If a PE already + * exists with that address then @edev is added to that PE. Otherwise + * a new PE is created and inserted into the PE tree as a child of + * @new_pe_parent. + * + * If @new_pe_parent is NULL then the new PE will be inserted under + * directly under the the PHB. */ -int eeh_pe_tree_insert(struct eeh_dev *edev) +int eeh_pe_tree_insert(struct eeh_dev *edev, struct eeh_pe *new_pe_parent) { struct pci_controller *hose = edev->controller; struct eeh_pe *pe, *parent; @@ -398,8 +365,7 @@ int eeh_pe_tree_insert(struct eeh_dev *edev) parent = parent->parent; } - eeh_edev_dbg(edev, - "Added to device PE (parent: PE#%x)\n", + eeh_edev_dbg(edev, "Added to existing PE (parent: PE#%x)\n", pe->parent->addr); } else { /* Mark the PE as type of PCI bus */ @@ -431,10 +397,9 @@ int eeh_pe_tree_insert(struct eeh_dev *edev) * to PHB directly. Otherwise, we have to associate the * PE with its parent. */ - parent = eeh_pe_get_parent(edev); - if (!parent) { - parent = eeh_phb_pe_get(hose); - if (!parent) { + if (!new_pe_parent) { + new_pe_parent = eeh_phb_pe_get(hose); + if (!new_pe_parent) { pr_err("%s: No PHB PE is found (PHB Domain=%d)\n", __func__, hose->global_number); edev->pe = NULL; @@ -442,17 +407,19 @@ int eeh_pe_tree_insert(struct eeh_dev *edev) return -EEXIST; } } - pe->parent = parent; + + /* link new PE into the tree */ + pe->parent = new_pe_parent; + list_add_tail(&pe->child, &new_pe_parent->child_list); /* * Put the newly created PE into the child list and * link the EEH device accordingly. */ - list_add_tail(&pe->child, &parent->child_list); list_add_tail(&edev->entry, &pe->edevs); edev->pe = pe; - eeh_edev_dbg(edev, "Added to device PE (parent: PE#%x)\n", - pe->parent->addr); + eeh_edev_dbg(edev, "Added to new (parent: PE#%x)\n", + new_pe_parent->addr); return 0; } diff --git a/arch/powerpc/platforms/powernv/eeh-powernv.c b/arch/powerpc/platforms/powernv/eeh-powernv.c index 8c9fca7736929..9af8c3b988535 100644 --- a/arch/powerpc/platforms/powernv/eeh-powernv.c +++ b/arch/powerpc/platforms/powernv/eeh-powernv.c @@ -338,6 +338,28 @@ static int pnv_eeh_find_ecap(struct pci_dn *pdn, int cap) return 0; } +static struct eeh_pe *pnv_eeh_get_upstream_pe(struct pci_dev *pdev) +{ + struct pci_controller *hose = pdev->bus->sysdata; + struct pnv_phb *phb = hose->private_data; + struct pci_dev *parent = pdev->bus->self; + +#ifdef CONFIG_PCI_IOV + /* for VFs we use the PF's PE as the upstream PE */ + if (pdev->is_virtfn) + parent = pdev->physfn; +#endif + + /* otherwise use the PE of our parent bridge */ + if (parent) { + struct pnv_ioda_pe *ioda_pe = pnv_ioda_get_pe(parent); + + return eeh_pe_get(phb->hose, ioda_pe->pe_number, 0); + } + + return NULL; +} + /** * pnv_eeh_probe - Do probe on PCI device * @pdev: pci_dev to probe @@ -350,6 +372,7 @@ static struct eeh_dev *pnv_eeh_probe(struct pci_dev *pdev) struct pci_controller *hose = pdn->phb; struct pnv_phb *phb = hose->private_data; struct eeh_dev *edev = pdn_to_eeh_dev(pdn); + struct eeh_pe *upstream_pe; uint32_t pcie_flags; int ret; int config_addr = (pdn->busno << 8) | (pdn->devfn); @@ -398,8 +421,10 @@ static struct eeh_dev *pnv_eeh_probe(struct pci_dev *pdev) edev->pe_config_addr = phb->ioda.pe_rmap[config_addr]; + upstream_pe = pnv_eeh_get_upstream_pe(pdev); + /* Create PE */ - ret = eeh_pe_tree_insert(edev); + ret = eeh_pe_tree_insert(edev, upstream_pe); if (ret) { eeh_edev_warn(edev, "Failed to add device to PE (code %d)\n", ret); return NULL; diff --git a/arch/powerpc/platforms/pseries/eeh_pseries.c b/arch/powerpc/platforms/pseries/eeh_pseries.c index 69650d8dc7058..cb2d9a970b7b0 100644 --- a/arch/powerpc/platforms/pseries/eeh_pseries.c +++ b/arch/powerpc/platforms/pseries/eeh_pseries.c @@ -69,11 +69,16 @@ void pseries_pcibios_bus_add_device(struct pci_dev *pdev) pseries_eeh_init_edev(pdn); #ifdef CONFIG_PCI_IOV if (pdev->is_virtfn) { + /* + * FIXME: This really should be handled by choosing the right + * parent PE in in pseries_eeh_init_edev(). + */ + struct eeh_pe *physfn_pe = pci_dev_to_eeh_dev(pdev->physfn)->pe; struct eeh_dev *edev = pdn_to_eeh_dev(pdn); edev->pe_config_addr = (pdn->busno << 16) | (pdn->devfn << 8); eeh_pe_tree_remove(edev); /* Remove as it is adding to bus pe */ - eeh_pe_tree_insert(edev); /* Add as VF PE type */ + eeh_pe_tree_insert(edev, physfn_pe); /* Add as VF PE type */ } #endif eeh_probe_device(pdev); @@ -385,6 +390,43 @@ static int pseries_eeh_find_ecap(struct pci_dn *pdn, int cap) return 0; } +/** + * pseries_eeh_pe_get_parent - Retrieve the parent PE + * @edev: EEH device + * + * The whole PEs existing in the system are organized as hierarchy + * tree. The function is used to retrieve the parent PE according + * to the parent EEH device. + */ +static struct eeh_pe *pseries_eeh_pe_get_parent(struct eeh_dev *edev) +{ + struct eeh_dev *parent; + struct pci_dn *pdn = eeh_dev_to_pdn(edev); + + /* + * It might have the case for the indirect parent + * EEH device already having associated PE, but + * the direct parent EEH device doesn't have yet. + */ + if (edev->physfn) + pdn = pci_get_pdn(edev->physfn); + else + pdn = pdn ? pdn->parent : NULL; + while (pdn) { + /* We're poking out of PCI territory */ + parent = pdn_to_eeh_dev(pdn); + if (!parent) + return NULL; + + if (parent->pe) + return parent->pe; + + pdn = pdn->parent; + } + + return NULL; +} + /** * pseries_eeh_init_edev - initialise the eeh_dev and eeh_pe for a pci_dn * @@ -468,6 +510,8 @@ void pseries_eeh_init_edev(struct pci_dn *pdn) if (ret) { eeh_edev_dbg(edev, "EEH failed to enable on device (code %d)\n", ret); } else { + struct eeh_pe *parent; + /* Retrieve PE address */ edev->pe_config_addr = pseries_eeh_get_pe_addr(pdn); pe.addr = edev->pe_config_addr; @@ -480,16 +524,19 @@ void pseries_eeh_init_edev(struct pci_dn *pdn) if (ret > 0 && ret != EEH_STATE_NOT_SUPPORT) enable = 1; - if (enable) { + /* + * This device doesn't support EEH, but it may have an + * EEH parent. In this case any error on the device will + * freeze the PE of it's upstream bridge, so added it to + * the upstream PE. + */ + parent = pseries_eeh_pe_get_parent(edev); + if (parent && !enable) + edev->pe_config_addr = parent->addr; + + if (enable || parent) { eeh_add_flag(EEH_ENABLED); - eeh_pe_tree_insert(edev); - } else if (pdn->parent && pdn_to_eeh_dev(pdn->parent) && - (pdn_to_eeh_dev(pdn->parent))->pe) { - /* This device doesn't support EEH, but it may have an - * EEH parent, in which case we mark it as supported. - */ - edev->pe_config_addr = pdn_to_eeh_dev(pdn->parent)->pe_config_addr; - eeh_pe_tree_insert(edev); + eeh_pe_tree_insert(edev, parent); } eeh_edev_dbg(edev, "EEH is %s on device (code %d)\n", (enable ? "enabled" : "unsupported"), ret); -- GitLab From 5609ffddd19dd52019d78b197e86b0331aeef8ae Mon Sep 17 00:00:00 2001 From: Oliver O'Halloran Date: Wed, 22 Jul 2020 16:57:00 +1000 Subject: [PATCH 0842/1476] powerpc/powernv/pci: Add pci_bus_to_pnvhb() helper Add a helper to go from a pci_bus structure to the pnv_phb that hosts that bus. There's a lot of instances of the following pattern: struct pci_controller *hose = pci_bus_to_host(pdev->bus); struct pnv_phb *phb = hose->private_data; Without any other uses of the pci_controller inside the function. This is hard to read since it requires you to memorise the contents of the private data fields and kind of error prone since it involves blindly assigning a void pointer. Add a helper to make it more concise and explicit. Signed-off-by: Oliver O'Halloran Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20200722065715.1432738-1-oohall@gmail.com --- arch/powerpc/platforms/powernv/pci-ioda.c | 88 +++++++---------------- arch/powerpc/platforms/powernv/pci.c | 14 ++-- arch/powerpc/platforms/powernv/pci.h | 10 +++ 3 files changed, 38 insertions(+), 74 deletions(-) diff --git a/arch/powerpc/platforms/powernv/pci-ioda.c b/arch/powerpc/platforms/powernv/pci-ioda.c index 31c3e6d58c410..687919db03474 100644 --- a/arch/powerpc/platforms/powernv/pci-ioda.c +++ b/arch/powerpc/platforms/powernv/pci-ioda.c @@ -252,8 +252,7 @@ fail: static void pnv_ioda_reserve_dev_m64_pe(struct pci_dev *pdev, unsigned long *pe_bitmap) { - struct pci_controller *hose = pci_bus_to_host(pdev->bus); - struct pnv_phb *phb = hose->private_data; + struct pnv_phb *phb = pci_bus_to_pnvhb(pdev->bus); struct resource *r; resource_size_t base, sgsz, start, end; int segno, i; @@ -351,8 +350,7 @@ static void pnv_ioda_reserve_m64_pe(struct pci_bus *bus, static struct pnv_ioda_pe *pnv_ioda_pick_m64_pe(struct pci_bus *bus, bool all) { - struct pci_controller *hose = pci_bus_to_host(bus); - struct pnv_phb *phb = hose->private_data; + struct pnv_phb *phb = pci_bus_to_pnvhb(bus); struct pnv_ioda_pe *master_pe, *pe; unsigned long size, *pe_alloc; int i; @@ -673,8 +671,7 @@ struct pnv_ioda_pe *pnv_pci_bdfn_to_pe(struct pnv_phb *phb, u16 bdfn) struct pnv_ioda_pe *pnv_ioda_get_pe(struct pci_dev *dev) { - struct pci_controller *hose = pci_bus_to_host(dev->bus); - struct pnv_phb *phb = hose->private_data; + struct pnv_phb *phb = pci_bus_to_pnvhb(dev->bus); struct pci_dn *pdn = pci_get_pdn(dev); if (!pdn) @@ -1069,8 +1066,7 @@ static int pnv_pci_vf_resource_shift(struct pci_dev *dev, int offset) static struct pnv_ioda_pe *pnv_ioda_setup_dev_PE(struct pci_dev *dev) { - struct pci_controller *hose = pci_bus_to_host(dev->bus); - struct pnv_phb *phb = hose->private_data; + struct pnv_phb *phb = pci_bus_to_pnvhb(dev->bus); struct pci_dn *pdn = pci_get_pdn(dev); struct pnv_ioda_pe *pe; @@ -1129,8 +1125,7 @@ static struct pnv_ioda_pe *pnv_ioda_setup_dev_PE(struct pci_dev *dev) */ static struct pnv_ioda_pe *pnv_ioda_setup_bus_PE(struct pci_bus *bus, bool all) { - struct pci_controller *hose = pci_bus_to_host(bus); - struct pnv_phb *phb = hose->private_data; + struct pnv_phb *phb = pci_bus_to_pnvhb(bus); struct pnv_ioda_pe *pe = NULL; unsigned int pe_num; @@ -1196,8 +1191,7 @@ static struct pnv_ioda_pe *pnv_ioda_setup_npu_PE(struct pci_dev *npu_pdev) struct pnv_ioda_pe *pe; struct pci_dev *gpu_pdev; struct pci_dn *npu_pdn; - struct pci_controller *hose = pci_bus_to_host(npu_pdev->bus); - struct pnv_phb *phb = hose->private_data; + struct pnv_phb *phb = pci_bus_to_pnvhb(npu_pdev->bus); /* * Intentionally leak a reference on the npu device (for @@ -1300,16 +1294,12 @@ static void pnv_pci_ioda_setup_nvlink(void) #ifdef CONFIG_PCI_IOV static int pnv_pci_vf_release_m64(struct pci_dev *pdev, u16 num_vfs) { - struct pci_bus *bus; - struct pci_controller *hose; struct pnv_phb *phb; struct pci_dn *pdn; int i, j; int m64_bars; - bus = pdev->bus; - hose = pci_bus_to_host(bus); - phb = hose->private_data; + phb = pci_bus_to_pnvhb(pdev->bus); pdn = pci_get_pdn(pdev); if (pdn->m64_single_mode) @@ -1333,8 +1323,6 @@ static int pnv_pci_vf_release_m64(struct pci_dev *pdev, u16 num_vfs) static int pnv_pci_vf_assign_m64(struct pci_dev *pdev, u16 num_vfs) { - struct pci_bus *bus; - struct pci_controller *hose; struct pnv_phb *phb; struct pci_dn *pdn; unsigned int win; @@ -1346,9 +1334,7 @@ static int pnv_pci_vf_assign_m64(struct pci_dev *pdev, u16 num_vfs) int pe_num; int m64_bars; - bus = pdev->bus; - hose = pci_bus_to_host(bus); - phb = hose->private_data; + phb = pci_bus_to_pnvhb(pdev->bus); pdn = pci_get_pdn(pdev); total_vfs = pci_sriov_get_totalvfs(pdev); @@ -1459,15 +1445,11 @@ static void pnv_pci_ioda2_release_dma_pe(struct pci_dev *dev, struct pnv_ioda_pe static void pnv_ioda_release_vf_PE(struct pci_dev *pdev) { - struct pci_bus *bus; - struct pci_controller *hose; struct pnv_phb *phb; struct pnv_ioda_pe *pe, *pe_n; struct pci_dn *pdn; - bus = pdev->bus; - hose = pci_bus_to_host(bus); - phb = hose->private_data; + phb = pci_bus_to_pnvhb(pdev->bus); pdn = pci_get_pdn(pdev); if (!pdev->is_physfn) @@ -1492,16 +1474,12 @@ static void pnv_ioda_release_vf_PE(struct pci_dev *pdev) static void pnv_pci_sriov_disable(struct pci_dev *pdev) { - struct pci_bus *bus; - struct pci_controller *hose; struct pnv_phb *phb; struct pnv_ioda_pe *pe; struct pci_dn *pdn; u16 num_vfs, i; - bus = pdev->bus; - hose = pci_bus_to_host(bus); - phb = hose->private_data; + phb = pci_bus_to_pnvhb(pdev->bus); pdn = pci_get_pdn(pdev); num_vfs = pdn->num_vfs; @@ -1535,17 +1513,13 @@ static void pnv_pci_ioda2_setup_dma_pe(struct pnv_phb *phb, struct pnv_ioda_pe *pe); static void pnv_ioda_setup_vf_PE(struct pci_dev *pdev, u16 num_vfs) { - struct pci_bus *bus; - struct pci_controller *hose; struct pnv_phb *phb; struct pnv_ioda_pe *pe; int pe_num; u16 vf_index; struct pci_dn *pdn; - bus = pdev->bus; - hose = pci_bus_to_host(bus); - phb = hose->private_data; + phb = pci_bus_to_pnvhb(pdev->bus); pdn = pci_get_pdn(pdev); if (!pdev->is_physfn) @@ -1572,7 +1546,7 @@ static void pnv_ioda_setup_vf_PE(struct pci_dev *pdev, u16 num_vfs) pe->rid = (vf_bus << 8) | vf_devfn; pe_info(pe, "VF %04d:%02d:%02d.%d associated with PE#%x\n", - hose->global_number, pdev->bus->number, + pci_domain_nr(pdev->bus), pdev->bus->number, PCI_SLOT(vf_devfn), PCI_FUNC(vf_devfn), pe_num); if (pnv_ioda_configure_pe(phb, pe)) { @@ -1602,17 +1576,13 @@ static void pnv_ioda_setup_vf_PE(struct pci_dev *pdev, u16 num_vfs) static int pnv_pci_sriov_enable(struct pci_dev *pdev, u16 num_vfs) { - struct pci_bus *bus; - struct pci_controller *hose; struct pnv_phb *phb; struct pnv_ioda_pe *pe; struct pci_dn *pdn; int ret; u16 i; - bus = pdev->bus; - hose = pci_bus_to_host(bus); - phb = hose->private_data; + phb = pci_bus_to_pnvhb(pdev->bus); pdn = pci_get_pdn(pdev); if (phb->type == PNV_PHB_IODA2) { @@ -1735,8 +1705,7 @@ static int pnv_pcibios_sriov_enable(struct pci_dev *pdev, u16 num_vfs) static void pnv_pci_ioda_dma_dev_setup(struct pci_dev *pdev) { - struct pci_controller *hose = pci_bus_to_host(pdev->bus); - struct pnv_phb *phb = hose->private_data; + struct pnv_phb *phb = pci_bus_to_pnvhb(pdev->bus); struct pci_dn *pdn = pci_get_pdn(pdev); struct pnv_ioda_pe *pe; @@ -1847,8 +1816,7 @@ err: static bool pnv_pci_ioda_iommu_bypass_supported(struct pci_dev *pdev, u64 dma_mask) { - struct pci_controller *hose = pci_bus_to_host(pdev->bus); - struct pnv_phb *phb = hose->private_data; + struct pnv_phb *phb = pci_bus_to_pnvhb(pdev->bus); struct pci_dn *pdn = pci_get_pdn(pdev); struct pnv_ioda_pe *pe; @@ -2766,8 +2734,7 @@ static void pnv_pci_init_ioda_msis(struct pnv_phb *phb) #ifdef CONFIG_PCI_IOV static void pnv_pci_ioda_fixup_iov_resources(struct pci_dev *pdev) { - struct pci_controller *hose = pci_bus_to_host(pdev->bus); - struct pnv_phb *phb = hose->private_data; + struct pnv_phb *phb = pci_bus_to_pnvhb(pdev->bus); const resource_size_t gate = phb->ioda.m64_segsize >> 2; struct resource *res; int i; @@ -3101,10 +3068,9 @@ static void pnv_pci_ioda_fixup(void) static resource_size_t pnv_pci_window_alignment(struct pci_bus *bus, unsigned long type) { - struct pci_dev *bridge; - struct pci_controller *hose = pci_bus_to_host(bus); - struct pnv_phb *phb = hose->private_data; + struct pnv_phb *phb = pci_bus_to_pnvhb(bus); int num_pci_bridges = 0; + struct pci_dev *bridge; bridge = bus->self; while (bridge) { @@ -3190,8 +3156,7 @@ static void pnv_pci_fixup_bridge_resources(struct pci_bus *bus, static void pnv_pci_configure_bus(struct pci_bus *bus) { - struct pci_controller *hose = pci_bus_to_host(bus); - struct pnv_phb *phb = hose->private_data; + struct pnv_phb *phb = pci_bus_to_pnvhb(bus); struct pci_dev *bridge = bus->self; struct pnv_ioda_pe *pe; bool all = (bridge && pci_pcie_type(bridge) == PCI_EXP_TYPE_PCI_BRIDGE); @@ -3237,8 +3202,7 @@ static resource_size_t pnv_pci_default_alignment(void) static resource_size_t pnv_pci_iov_resource_alignment(struct pci_dev *pdev, int resno) { - struct pci_controller *hose = pci_bus_to_host(pdev->bus); - struct pnv_phb *phb = hose->private_data; + struct pnv_phb *phb = pci_bus_to_pnvhb(pdev->bus); struct pci_dn *pdn = pci_get_pdn(pdev); resource_size_t align; @@ -3274,8 +3238,7 @@ static resource_size_t pnv_pci_iov_resource_alignment(struct pci_dev *pdev, */ static bool pnv_pci_enable_device_hook(struct pci_dev *dev) { - struct pci_controller *hose = pci_bus_to_host(dev->bus); - struct pnv_phb *phb = hose->private_data; + struct pnv_phb *phb = pci_bus_to_pnvhb(dev->bus); struct pci_dn *pdn; /* The function is probably called while the PEs have @@ -3488,8 +3451,7 @@ static void pnv_ioda_release_pe(struct pnv_ioda_pe *pe) static void pnv_pci_release_device(struct pci_dev *pdev) { - struct pci_controller *hose = pci_bus_to_host(pdev->bus); - struct pnv_phb *phb = hose->private_data; + struct pnv_phb *phb = pci_bus_to_pnvhb(pdev->bus); struct pci_dn *pdn = pci_get_pdn(pdev); struct pnv_ioda_pe *pe; @@ -3534,8 +3496,7 @@ static void pnv_pci_ioda_shutdown(struct pci_controller *hose) static void pnv_pci_ioda_dma_bus_setup(struct pci_bus *bus) { - struct pci_controller *hose = bus->sysdata; - struct pnv_phb *phb = hose->private_data; + struct pnv_phb *phb = pci_bus_to_pnvhb(bus); struct pnv_ioda_pe *pe; list_for_each_entry(pe, &phb->ioda.pe_list, list) { @@ -3873,8 +3834,7 @@ void __init pnv_pci_init_npu2_opencapi_phb(struct device_node *np) static void pnv_npu2_opencapi_cfg_size_fixup(struct pci_dev *dev) { - struct pci_controller *hose = pci_bus_to_host(dev->bus); - struct pnv_phb *phb = hose->private_data; + struct pnv_phb *phb = pci_bus_to_pnvhb(dev->bus); if (!machine_is(powernv)) return; diff --git a/arch/powerpc/platforms/powernv/pci.c b/arch/powerpc/platforms/powernv/pci.c index 091fe1cf386b4..9b9bca169275a 100644 --- a/arch/powerpc/platforms/powernv/pci.c +++ b/arch/powerpc/platforms/powernv/pci.c @@ -162,8 +162,7 @@ EXPORT_SYMBOL_GPL(pnv_pci_set_power_state); int pnv_setup_msi_irqs(struct pci_dev *pdev, int nvec, int type) { - struct pci_controller *hose = pci_bus_to_host(pdev->bus); - struct pnv_phb *phb = hose->private_data; + struct pnv_phb *phb = pci_bus_to_pnvhb(pdev->bus); struct msi_desc *entry; struct msi_msg msg; int hwirq; @@ -211,8 +210,7 @@ int pnv_setup_msi_irqs(struct pci_dev *pdev, int nvec, int type) void pnv_teardown_msi_irqs(struct pci_dev *pdev) { - struct pci_controller *hose = pci_bus_to_host(pdev->bus); - struct pnv_phb *phb = hose->private_data; + struct pnv_phb *phb = pci_bus_to_pnvhb(pdev->bus); struct msi_desc *entry; irq_hw_number_t hwirq; @@ -824,10 +822,9 @@ EXPORT_SYMBOL(pnv_pci_get_phb_node); int pnv_pci_set_tunnel_bar(struct pci_dev *dev, u64 addr, int enable) { - __be64 val; - struct pci_controller *hose; - struct pnv_phb *phb; + struct pnv_phb *phb = pci_bus_to_pnvhb(dev->bus); u64 tunnel_bar; + __be64 val; int rc; if (!opal_check_token(OPAL_PCI_GET_PBCQ_TUNNEL_BAR)) @@ -835,9 +832,6 @@ int pnv_pci_set_tunnel_bar(struct pci_dev *dev, u64 addr, int enable) if (!opal_check_token(OPAL_PCI_SET_PBCQ_TUNNEL_BAR)) return -ENXIO; - hose = pci_bus_to_host(dev->bus); - phb = hose->private_data; - mutex_lock(&tunnel_mutex); rc = opal_pci_get_pbcq_tunnel_bar(phb->opal_id, &val); if (rc != OPAL_SUCCESS) { diff --git a/arch/powerpc/platforms/powernv/pci.h b/arch/powerpc/platforms/powernv/pci.h index 51c254f2f3cbc..0727dec9a0d13 100644 --- a/arch/powerpc/platforms/powernv/pci.h +++ b/arch/powerpc/platforms/powernv/pci.h @@ -260,4 +260,14 @@ extern void pnv_pci_setup_iommu_table(struct iommu_table *tbl, extern unsigned long pnv_ioda_parse_tce_sizes(struct pnv_phb *phb); +static inline struct pnv_phb *pci_bus_to_pnvhb(struct pci_bus *bus) +{ + struct pci_controller *hose = bus->sysdata; + + if (hose) + return hose->private_data; + + return NULL; +} + #endif /* __POWERNV_PCI_H */ -- GitLab From 7a52ffabe867c0d93e47af113e5107340974047a Mon Sep 17 00:00:00 2001 From: Oliver O'Halloran Date: Wed, 22 Jul 2020 16:57:01 +1000 Subject: [PATCH 0843/1476] powerpc/powernv/pci: Always tear down DMA windows on PE release Currently we have these two functions: pnv_pci_ioda2_release_dma_pe(), and pnv_pci_ioda2_release_pe_dma() The first is used when tearing down VF PEs and the other is used for normal devices. There's very little difference between the two though. The latter (non-VF) will skip a call to pnv_pci_ioda2_unset_window() unless CONFIG_IOMMU_API=y is set. There's no real point in doing this so fold the two together. Signed-off-by: Oliver O'Halloran Reviewed-by: Alexey Kardashevskiy Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20200722065715.1432738-2-oohall@gmail.com --- arch/powerpc/platforms/powernv/pci-ioda.c | 30 +++-------------------- 1 file changed, 3 insertions(+), 27 deletions(-) diff --git a/arch/powerpc/platforms/powernv/pci-ioda.c b/arch/powerpc/platforms/powernv/pci-ioda.c index 687919db03474..bfb40607aa0ee 100644 --- a/arch/powerpc/platforms/powernv/pci-ioda.c +++ b/arch/powerpc/platforms/powernv/pci-ioda.c @@ -1422,26 +1422,7 @@ m64_failed: return -EBUSY; } -static long pnv_pci_ioda2_unset_window(struct iommu_table_group *table_group, - int num); - -static void pnv_pci_ioda2_release_dma_pe(struct pci_dev *dev, struct pnv_ioda_pe *pe) -{ - struct iommu_table *tbl; - int64_t rc; - - tbl = pe->table_group.tables[0]; - rc = pnv_pci_ioda2_unset_window(&pe->table_group, 0); - if (rc) - pe_warn(pe, "OPAL error %lld release DMA window\n", rc); - - pnv_pci_ioda2_set_bypass(pe, false); - if (pe->table_group.group) { - iommu_group_put(pe->table_group.group); - BUG_ON(pe->table_group.group); - } - iommu_tce_table_put(tbl); -} +static void pnv_pci_ioda2_release_pe_dma(struct pnv_ioda_pe *pe); static void pnv_ioda_release_vf_PE(struct pci_dev *pdev) { @@ -1455,11 +1436,12 @@ static void pnv_ioda_release_vf_PE(struct pci_dev *pdev) if (!pdev->is_physfn) return; + /* FIXME: Use pnv_ioda_release_pe()? */ list_for_each_entry_safe(pe, pe_n, &phb->ioda.pe_list, list) { if (pe->parent_dev != pdev) continue; - pnv_pci_ioda2_release_dma_pe(pdev, pe); + pnv_pci_ioda2_release_pe_dma(pe); /* Remove from list */ mutex_lock(&phb->ioda.pe_list_mutex); @@ -2429,7 +2411,6 @@ static long pnv_pci_ioda2_setup_default_config(struct pnv_ioda_pe *pe) return 0; } -#if defined(CONFIG_IOMMU_API) || defined(CONFIG_PCI_IOV) static long pnv_pci_ioda2_unset_window(struct iommu_table_group *table_group, int num) { @@ -2453,7 +2434,6 @@ static long pnv_pci_ioda2_unset_window(struct iommu_table_group *table_group, return ret; } -#endif #ifdef CONFIG_IOMMU_API unsigned long pnv_pci_ioda2_get_table_size(__u32 page_shift, @@ -3334,18 +3314,14 @@ static void pnv_pci_ioda2_release_pe_dma(struct pnv_ioda_pe *pe) { struct iommu_table *tbl = pe->table_group.tables[0]; unsigned int weight = pnv_pci_ioda_pe_dma_weight(pe); -#ifdef CONFIG_IOMMU_API int64_t rc; -#endif if (!weight) return; -#ifdef CONFIG_IOMMU_API rc = pnv_pci_ioda2_unset_window(&pe->table_group, 0); if (rc) pe_warn(pe, "OPAL error %lld release DMA window\n", rc); -#endif pnv_pci_ioda2_set_bypass(pe, false); if (pe->table_group.group) { -- GitLab From 01e12629af4e0e4864ed4d83e07783d7cb5b06be Mon Sep 17 00:00:00 2001 From: Oliver O'Halloran Date: Wed, 22 Jul 2020 16:57:02 +1000 Subject: [PATCH 0844/1476] powerpc/powernv/pci: Add explicit tracking of the DMA setup state There's an optimisation in the PE setup which skips performing DMA setup for a PE if we only have bridges in a PE. The assumption being that only "real" devices will DMA to system memory, which is probably fair. However, if we start off with only bridge devices in a PE then add a non-bridge device the new device won't be able to use DMA because we never configured it. Fix this (admittedly pretty weird) edge case by tracking whether we've done the DMA setup for the PE or not. If a non-bridge device is added to the PE (via rescan or hotplug, or whatever) we can set up DMA on demand. This also means the only remaining user of the old "DMA Weight" code is the IODA1 DMA setup code that it was originally added for, which is good. Signed-off-by: Oliver O'Halloran Reviewed-by: Alexey Kardashevskiy Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20200722065715.1432738-3-oohall@gmail.com --- arch/powerpc/platforms/powernv/pci-ioda.c | 48 ++++++++++++++--------- arch/powerpc/platforms/powernv/pci.h | 7 ++++ 2 files changed, 36 insertions(+), 19 deletions(-) diff --git a/arch/powerpc/platforms/powernv/pci-ioda.c b/arch/powerpc/platforms/powernv/pci-ioda.c index bfb40607aa0ee..bb9c1cc60c336 100644 --- a/arch/powerpc/platforms/powernv/pci-ioda.c +++ b/arch/powerpc/platforms/powernv/pci-ioda.c @@ -141,6 +141,7 @@ static struct pnv_ioda_pe *pnv_ioda_init_pe(struct pnv_phb *phb, int pe_no) phb->ioda.pe_array[pe_no].phb = phb; phb->ioda.pe_array[pe_no].pe_number = pe_no; + phb->ioda.pe_array[pe_no].dma_setup_done = false; /* * Clear the PE frozen state as it might be put into frozen state @@ -1685,6 +1686,12 @@ static int pnv_pcibios_sriov_enable(struct pci_dev *pdev, u16 num_vfs) } #endif /* CONFIG_PCI_IOV */ +static void pnv_pci_ioda1_setup_dma_pe(struct pnv_phb *phb, + struct pnv_ioda_pe *pe); + +static void pnv_pci_ioda2_setup_dma_pe(struct pnv_phb *phb, + struct pnv_ioda_pe *pe); + static void pnv_pci_ioda_dma_dev_setup(struct pci_dev *pdev) { struct pnv_phb *phb = pci_bus_to_pnvhb(pdev->bus); @@ -1713,6 +1720,24 @@ static void pnv_pci_ioda_dma_dev_setup(struct pci_dev *pdev) pci_info(pdev, "Added to existing PE#%x\n", pe->pe_number); } + /* + * We assume that bridges *probably* don't need to do any DMA so we can + * skip allocating a TCE table, etc unless we get a non-bridge device. + */ + if (!pe->dma_setup_done && !pci_is_bridge(pdev)) { + switch (phb->type) { + case PNV_PHB_IODA1: + pnv_pci_ioda1_setup_dma_pe(phb, pe); + break; + case PNV_PHB_IODA2: + pnv_pci_ioda2_setup_dma_pe(phb, pe); + break; + default: + pr_warn("%s: No DMA for PHB#%x (type %d)\n", + __func__, phb->hose->global_number, phb->type); + } + } + if (pdn) pdn->pe_number = pe->pe_number; pe->device_count++; @@ -2222,6 +2247,7 @@ found: pe->table_group.tce32_size = tbl->it_size << tbl->it_page_shift; iommu_init_table(tbl, phb->hose->node, 0, 0); + pe->dma_setup_done = true; return; fail: /* XXX Failure: Try to fallback to 64-bit only ? */ @@ -2536,9 +2562,6 @@ static void pnv_pci_ioda2_setup_dma_pe(struct pnv_phb *phb, { int64_t rc; - if (!pnv_pci_ioda_pe_dma_weight(pe)) - return; - /* TVE #1 is selected by PCI address bit 59 */ pe->tce_bypass_base = 1ull << 59; @@ -2563,6 +2586,7 @@ static void pnv_pci_ioda2_setup_dma_pe(struct pnv_phb *phb, iommu_register_group(&pe->table_group, phb->hose->global_number, pe->pe_number); #endif + pe->dma_setup_done = true; } int64_t pnv_opal_pci_msi_eoi(struct irq_chip *chip, unsigned int hw_irq) @@ -3136,7 +3160,6 @@ static void pnv_pci_fixup_bridge_resources(struct pci_bus *bus, static void pnv_pci_configure_bus(struct pci_bus *bus) { - struct pnv_phb *phb = pci_bus_to_pnvhb(bus); struct pci_dev *bridge = bus->self; struct pnv_ioda_pe *pe; bool all = (bridge && pci_pcie_type(bridge) == PCI_EXP_TYPE_PCI_BRIDGE); @@ -3160,17 +3183,6 @@ static void pnv_pci_configure_bus(struct pci_bus *bus) return; pnv_ioda_setup_pe_seg(pe); - switch (phb->type) { - case PNV_PHB_IODA1: - pnv_pci_ioda1_setup_dma_pe(phb, pe); - break; - case PNV_PHB_IODA2: - pnv_pci_ioda2_setup_dma_pe(phb, pe); - break; - default: - pr_warn("%s: No DMA for PHB#%x (type %d)\n", - __func__, phb->hose->global_number, phb->type); - } } static resource_size_t pnv_pci_default_alignment(void) @@ -3289,11 +3301,10 @@ static long pnv_pci_ioda1_unset_window(struct iommu_table_group *table_group, static void pnv_pci_ioda1_release_pe_dma(struct pnv_ioda_pe *pe) { - unsigned int weight = pnv_pci_ioda_pe_dma_weight(pe); struct iommu_table *tbl = pe->table_group.tables[0]; int64_t rc; - if (!weight) + if (!pe->dma_setup_done) return; rc = pnv_pci_ioda1_unset_window(&pe->table_group, 0); @@ -3313,10 +3324,9 @@ static void pnv_pci_ioda1_release_pe_dma(struct pnv_ioda_pe *pe) static void pnv_pci_ioda2_release_pe_dma(struct pnv_ioda_pe *pe) { struct iommu_table *tbl = pe->table_group.tables[0]; - unsigned int weight = pnv_pci_ioda_pe_dma_weight(pe); int64_t rc; - if (!weight) + if (pe->dma_setup_done) return; rc = pnv_pci_ioda2_unset_window(&pe->table_group, 0); diff --git a/arch/powerpc/platforms/powernv/pci.h b/arch/powerpc/platforms/powernv/pci.h index 0727dec9a0d13..6aa6aefb637d2 100644 --- a/arch/powerpc/platforms/powernv/pci.h +++ b/arch/powerpc/platforms/powernv/pci.h @@ -87,6 +87,13 @@ struct pnv_ioda_pe { bool tce_bypass_enabled; uint64_t tce_bypass_base; + /* + * Used to track whether we've done DMA setup for this PE or not. We + * want to defer allocating TCE tables, etc until we've added a + * non-bridge device to the PE. + */ + bool dma_setup_done; + /* MSIs. MVE index is identical for for 32 and 64 bit MSI * and -1 if not supported. (It's actually identical to the * PE number) -- GitLab From 369633654fcb9639cd4cd0e1a448ffde3533d776 Mon Sep 17 00:00:00 2001 From: Oliver O'Halloran Date: Wed, 22 Jul 2020 16:57:03 +1000 Subject: [PATCH 0845/1476] powerpc/powernv/pci: Initialise M64 for IODA1 as a 1-1 window We pre-configure the m64 window for IODA1 as a 1-1 segment-PE mapping, similar to PHB3. Currently the actual mapping of segments occurs in pnv_ioda_pick_m64_pe(), but we can move it into pnv_ioda1_init_m64() and drop the IODA1 specific code paths in the PE setup / teardown. Signed-off-by: Oliver O'Halloran Reviewed-by: Alexey Kardashevskiy Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20200722065715.1432738-4-oohall@gmail.com --- arch/powerpc/platforms/powernv/pci-ioda.c | 55 +++++++++++------------ 1 file changed, 25 insertions(+), 30 deletions(-) diff --git a/arch/powerpc/platforms/powernv/pci-ioda.c b/arch/powerpc/platforms/powernv/pci-ioda.c index bb9c1cc60c336..8fb17676d914c 100644 --- a/arch/powerpc/platforms/powernv/pci-ioda.c +++ b/arch/powerpc/platforms/powernv/pci-ioda.c @@ -311,6 +311,28 @@ static int pnv_ioda1_init_m64(struct pnv_phb *phb) } } + for (index = 0; index < phb->ioda.total_pe_num; index++) { + int64_t rc; + + /* + * P7IOC supports M64DT, which helps mapping M64 segment + * to one particular PE#. However, PHB3 has fixed mapping + * between M64 segment and PE#. In order to have same logic + * for P7IOC and PHB3, we enforce fixed mapping between M64 + * segment and PE# on P7IOC. + */ + rc = opal_pci_map_pe_mmio_window(phb->opal_id, + index, OPAL_M64_WINDOW_TYPE, + index / PNV_IODA1_M64_SEGS, + index % PNV_IODA1_M64_SEGS); + if (rc != OPAL_SUCCESS) { + pr_warn("%s: Error %lld mapping M64 for PHB#%x-PE#%x\n", + __func__, rc, phb->hose->global_number, + index); + goto fail; + } + } + /* * Exclude the segments for reserved and root bus PE, which * are first or last two PEs. @@ -402,26 +424,6 @@ static struct pnv_ioda_pe *pnv_ioda_pick_m64_pe(struct pci_bus *bus, bool all) pe->master = master_pe; list_add_tail(&pe->list, &master_pe->slaves); } - - /* - * P7IOC supports M64DT, which helps mapping M64 segment - * to one particular PE#. However, PHB3 has fixed mapping - * between M64 segment and PE#. In order to have same logic - * for P7IOC and PHB3, we enforce fixed mapping between M64 - * segment and PE# on P7IOC. - */ - if (phb->type == PNV_PHB_IODA1) { - int64_t rc; - - rc = opal_pci_map_pe_mmio_window(phb->opal_id, - pe->pe_number, OPAL_M64_WINDOW_TYPE, - pe->pe_number / PNV_IODA1_M64_SEGS, - pe->pe_number % PNV_IODA1_M64_SEGS); - if (rc != OPAL_SUCCESS) - pr_warn("%s: Error %lld mapping M64 for PHB#%x-PE#%x\n", - __func__, rc, phb->hose->global_number, - pe->pe_number); - } } kfree(pe_alloc); @@ -3354,14 +3356,8 @@ static void pnv_ioda_free_pe_seg(struct pnv_ioda_pe *pe, if (map[idx] != pe->pe_number) continue; - if (win == OPAL_M64_WINDOW_TYPE) - rc = opal_pci_map_pe_mmio_window(phb->opal_id, - phb->ioda.reserved_pe_idx, win, - idx / PNV_IODA1_M64_SEGS, - idx % PNV_IODA1_M64_SEGS); - else - rc = opal_pci_map_pe_mmio_window(phb->opal_id, - phb->ioda.reserved_pe_idx, win, 0, idx); + rc = opal_pci_map_pe_mmio_window(phb->opal_id, + phb->ioda.reserved_pe_idx, win, 0, idx); if (rc != OPAL_SUCCESS) pe_warn(pe, "Error %lld unmapping (%d) segment#%d\n", @@ -3380,8 +3376,7 @@ static void pnv_ioda_release_pe_seg(struct pnv_ioda_pe *pe) phb->ioda.io_segmap); pnv_ioda_free_pe_seg(pe, OPAL_M32_WINDOW_TYPE, phb->ioda.m32_segmap); - pnv_ioda_free_pe_seg(pe, OPAL_M64_WINDOW_TYPE, - phb->ioda.m64_segmap); + /* M64 is pre-configured by pnv_ioda1_init_m64() */ } else if (phb->type == PNV_PHB_IODA2) { pnv_ioda_free_pe_seg(pe, OPAL_M32_WINDOW_TYPE, phb->ioda.m32_segmap); -- GitLab From 37b59ef08c546c6f54cdc52eed749f494619a102 Mon Sep 17 00:00:00 2001 From: Oliver O'Halloran Date: Wed, 22 Jul 2020 16:57:04 +1000 Subject: [PATCH 0846/1476] powerpc/powernv/sriov: Move SR-IOV into a separate file pci-ioda.c is getting a bit unwieldly due to the amount of stuff jammed in there. The SR-IOV support can be extracted easily enough and is mostly standalone, so move it into a separate file. This patch also moves the PowerNV SR-IOV specific fields from pci_dn and moves them into a platform specific structure. I'm not sure how they ended up in there in the first place, but leaking platform specifics into common code has proven to be a terrible idea so far so lets stop doing that. Signed-off-by: Oliver O'Halloran Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20200722065715.1432738-5-oohall@gmail.com --- arch/powerpc/include/asm/device.h | 3 + arch/powerpc/platforms/powernv/Makefile | 1 + arch/powerpc/platforms/powernv/pci-ioda.c | 673 +-------------------- arch/powerpc/platforms/powernv/pci-sriov.c | 640 ++++++++++++++++++++ arch/powerpc/platforms/powernv/pci.h | 74 +++ 5 files changed, 736 insertions(+), 655 deletions(-) create mode 100644 arch/powerpc/platforms/powernv/pci-sriov.c diff --git a/arch/powerpc/include/asm/device.h b/arch/powerpc/include/asm/device.h index 266542769e4bd..4d8934db7ef55 100644 --- a/arch/powerpc/include/asm/device.h +++ b/arch/powerpc/include/asm/device.h @@ -49,6 +49,9 @@ struct dev_archdata { #ifdef CONFIG_CXL_BASE struct cxl_context *cxl_ctx; #endif +#ifdef CONFIG_PCI_IOV + void *iov_data; +#endif }; struct pdev_archdata { diff --git a/arch/powerpc/platforms/powernv/Makefile b/arch/powerpc/platforms/powernv/Makefile index fe3f0fb5aecad..2eb6ae150d1fd 100644 --- a/arch/powerpc/platforms/powernv/Makefile +++ b/arch/powerpc/platforms/powernv/Makefile @@ -11,6 +11,7 @@ obj-$(CONFIG_FA_DUMP) += opal-fadump.o obj-$(CONFIG_PRESERVE_FA_DUMP) += opal-fadump.o obj-$(CONFIG_OPAL_CORE) += opal-core.o obj-$(CONFIG_PCI) += pci.o pci-ioda.o npu-dma.o pci-ioda-tce.o +obj-$(CONFIG_PCI_IOV) += pci-sriov.o obj-$(CONFIG_CXL_BASE) += pci-cxl.o obj-$(CONFIG_EEH) += eeh-powernv.o obj-$(CONFIG_MEMORY_FAILURE) += opal-memory-errors.o diff --git a/arch/powerpc/platforms/powernv/pci-ioda.c b/arch/powerpc/platforms/powernv/pci-ioda.c index 8fb17676d914c..2d36a9ebf0e91 100644 --- a/arch/powerpc/platforms/powernv/pci-ioda.c +++ b/arch/powerpc/platforms/powernv/pci-ioda.c @@ -115,26 +115,6 @@ static int __init pci_reset_phbs_setup(char *str) early_param("ppc_pci_reset_phbs", pci_reset_phbs_setup); -static inline bool pnv_pci_is_m64(struct pnv_phb *phb, struct resource *r) -{ - /* - * WARNING: We cannot rely on the resource flags. The Linux PCI - * allocation code sometimes decides to put a 64-bit prefetchable - * BAR in the 32-bit window, so we have to compare the addresses. - * - * For simplicity we only test resource start. - */ - return (r->start >= phb->ioda.m64_base && - r->start < (phb->ioda.m64_base + phb->ioda.m64_size)); -} - -static inline bool pnv_pci_is_m64_flags(unsigned long resource_flags) -{ - unsigned long flags = (IORESOURCE_MEM_64 | IORESOURCE_PREFETCH); - - return (resource_flags & flags) == flags; -} - static struct pnv_ioda_pe *pnv_ioda_init_pe(struct pnv_phb *phb, int pe_no) { s64 rc; @@ -172,7 +152,7 @@ static void pnv_ioda_reserve_pe(struct pnv_phb *phb, int pe_no) pnv_ioda_init_pe(phb, pe_no); } -static struct pnv_ioda_pe *pnv_ioda_alloc_pe(struct pnv_phb *phb) +struct pnv_ioda_pe *pnv_ioda_alloc_pe(struct pnv_phb *phb) { long pe; @@ -184,7 +164,7 @@ static struct pnv_ioda_pe *pnv_ioda_alloc_pe(struct pnv_phb *phb) return NULL; } -static void pnv_ioda_free_pe(struct pnv_ioda_pe *pe) +void pnv_ioda_free_pe(struct pnv_ioda_pe *pe) { struct pnv_phb *phb = pe->phb; unsigned int pe_num = pe->pe_number; @@ -816,7 +796,7 @@ static void pnv_ioda_unset_peltv(struct pnv_phb *phb, pe_warn(pe, "OPAL error %lld remove self from PELTV\n", rc); } -static int pnv_ioda_deconfigure_pe(struct pnv_phb *phb, struct pnv_ioda_pe *pe) +int pnv_ioda_deconfigure_pe(struct pnv_phb *phb, struct pnv_ioda_pe *pe) { struct pci_dev *parent; uint8_t bcomp, dcomp, fcomp; @@ -887,7 +867,7 @@ static int pnv_ioda_deconfigure_pe(struct pnv_phb *phb, struct pnv_ioda_pe *pe) return 0; } -static int pnv_ioda_configure_pe(struct pnv_phb *phb, struct pnv_ioda_pe *pe) +int pnv_ioda_configure_pe(struct pnv_phb *phb, struct pnv_ioda_pe *pe) { struct pci_dev *parent; uint8_t bcomp, dcomp, fcomp; @@ -982,91 +962,6 @@ out: return 0; } -#ifdef CONFIG_PCI_IOV -static int pnv_pci_vf_resource_shift(struct pci_dev *dev, int offset) -{ - struct pci_dn *pdn = pci_get_pdn(dev); - int i; - struct resource *res, res2; - resource_size_t size; - u16 num_vfs; - - if (!dev->is_physfn) - return -EINVAL; - - /* - * "offset" is in VFs. The M64 windows are sized so that when they - * are segmented, each segment is the same size as the IOV BAR. - * Each segment is in a separate PE, and the high order bits of the - * address are the PE number. Therefore, each VF's BAR is in a - * separate PE, and changing the IOV BAR start address changes the - * range of PEs the VFs are in. - */ - num_vfs = pdn->num_vfs; - for (i = 0; i < PCI_SRIOV_NUM_BARS; i++) { - res = &dev->resource[i + PCI_IOV_RESOURCES]; - if (!res->flags || !res->parent) - continue; - - /* - * The actual IOV BAR range is determined by the start address - * and the actual size for num_vfs VFs BAR. This check is to - * make sure that after shifting, the range will not overlap - * with another device. - */ - size = pci_iov_resource_size(dev, i + PCI_IOV_RESOURCES); - res2.flags = res->flags; - res2.start = res->start + (size * offset); - res2.end = res2.start + (size * num_vfs) - 1; - - if (res2.end > res->end) { - dev_err(&dev->dev, "VF BAR%d: %pR would extend past %pR (trying to enable %d VFs shifted by %d)\n", - i, &res2, res, num_vfs, offset); - return -EBUSY; - } - } - - /* - * Since M64 BAR shares segments among all possible 256 PEs, - * we have to shift the beginning of PF IOV BAR to make it start from - * the segment which belongs to the PE number assigned to the first VF. - * This creates a "hole" in the /proc/iomem which could be used for - * allocating other resources so we reserve this area below and - * release when IOV is released. - */ - for (i = 0; i < PCI_SRIOV_NUM_BARS; i++) { - res = &dev->resource[i + PCI_IOV_RESOURCES]; - if (!res->flags || !res->parent) - continue; - - size = pci_iov_resource_size(dev, i + PCI_IOV_RESOURCES); - res2 = *res; - res->start += size * offset; - - dev_info(&dev->dev, "VF BAR%d: %pR shifted to %pR (%sabling %d VFs shifted by %d)\n", - i, &res2, res, (offset > 0) ? "En" : "Dis", - num_vfs, offset); - - if (offset < 0) { - devm_release_resource(&dev->dev, &pdn->holes[i]); - memset(&pdn->holes[i], 0, sizeof(pdn->holes[i])); - } - - pci_update_resource(dev, i + PCI_IOV_RESOURCES); - - if (offset > 0) { - pdn->holes[i].start = res2.start; - pdn->holes[i].end = res2.start + size * offset - 1; - pdn->holes[i].flags = IORESOURCE_BUS; - pdn->holes[i].name = "pnv_iov_reserved"; - devm_request_resource(&dev->dev, res->parent, - &pdn->holes[i]); - } - } - return 0; -} -#endif /* CONFIG_PCI_IOV */ - static struct pnv_ioda_pe *pnv_ioda_setup_dev_PE(struct pci_dev *dev) { struct pnv_phb *phb = pci_bus_to_pnvhb(dev->bus); @@ -1294,406 +1189,9 @@ static void pnv_pci_ioda_setup_nvlink(void) #endif } -#ifdef CONFIG_PCI_IOV -static int pnv_pci_vf_release_m64(struct pci_dev *pdev, u16 num_vfs) -{ - struct pnv_phb *phb; - struct pci_dn *pdn; - int i, j; - int m64_bars; - - phb = pci_bus_to_pnvhb(pdev->bus); - pdn = pci_get_pdn(pdev); - - if (pdn->m64_single_mode) - m64_bars = num_vfs; - else - m64_bars = 1; - - for (i = 0; i < PCI_SRIOV_NUM_BARS; i++) - for (j = 0; j < m64_bars; j++) { - if (pdn->m64_map[j][i] == IODA_INVALID_M64) - continue; - opal_pci_phb_mmio_enable(phb->opal_id, - OPAL_M64_WINDOW_TYPE, pdn->m64_map[j][i], 0); - clear_bit(pdn->m64_map[j][i], &phb->ioda.m64_bar_alloc); - pdn->m64_map[j][i] = IODA_INVALID_M64; - } - - kfree(pdn->m64_map); - return 0; -} - -static int pnv_pci_vf_assign_m64(struct pci_dev *pdev, u16 num_vfs) -{ - struct pnv_phb *phb; - struct pci_dn *pdn; - unsigned int win; - struct resource *res; - int i, j; - int64_t rc; - int total_vfs; - resource_size_t size, start; - int pe_num; - int m64_bars; - - phb = pci_bus_to_pnvhb(pdev->bus); - pdn = pci_get_pdn(pdev); - total_vfs = pci_sriov_get_totalvfs(pdev); - - if (pdn->m64_single_mode) - m64_bars = num_vfs; - else - m64_bars = 1; - - pdn->m64_map = kmalloc_array(m64_bars, - sizeof(*pdn->m64_map), - GFP_KERNEL); - if (!pdn->m64_map) - return -ENOMEM; - /* Initialize the m64_map to IODA_INVALID_M64 */ - for (i = 0; i < m64_bars ; i++) - for (j = 0; j < PCI_SRIOV_NUM_BARS; j++) - pdn->m64_map[i][j] = IODA_INVALID_M64; - - - for (i = 0; i < PCI_SRIOV_NUM_BARS; i++) { - res = &pdev->resource[i + PCI_IOV_RESOURCES]; - if (!res->flags || !res->parent) - continue; - - for (j = 0; j < m64_bars; j++) { - do { - win = find_next_zero_bit(&phb->ioda.m64_bar_alloc, - phb->ioda.m64_bar_idx + 1, 0); - - if (win >= phb->ioda.m64_bar_idx + 1) - goto m64_failed; - } while (test_and_set_bit(win, &phb->ioda.m64_bar_alloc)); - - pdn->m64_map[j][i] = win; - - if (pdn->m64_single_mode) { - size = pci_iov_resource_size(pdev, - PCI_IOV_RESOURCES + i); - start = res->start + size * j; - } else { - size = resource_size(res); - start = res->start; - } - - /* Map the M64 here */ - if (pdn->m64_single_mode) { - pe_num = pdn->pe_num_map[j]; - rc = opal_pci_map_pe_mmio_window(phb->opal_id, - pe_num, OPAL_M64_WINDOW_TYPE, - pdn->m64_map[j][i], 0); - } - - rc = opal_pci_set_phb_mem_window(phb->opal_id, - OPAL_M64_WINDOW_TYPE, - pdn->m64_map[j][i], - start, - 0, /* unused */ - size); - - - if (rc != OPAL_SUCCESS) { - dev_err(&pdev->dev, "Failed to map M64 window #%d: %lld\n", - win, rc); - goto m64_failed; - } - - if (pdn->m64_single_mode) - rc = opal_pci_phb_mmio_enable(phb->opal_id, - OPAL_M64_WINDOW_TYPE, pdn->m64_map[j][i], 2); - else - rc = opal_pci_phb_mmio_enable(phb->opal_id, - OPAL_M64_WINDOW_TYPE, pdn->m64_map[j][i], 1); - - if (rc != OPAL_SUCCESS) { - dev_err(&pdev->dev, "Failed to enable M64 window #%d: %llx\n", - win, rc); - goto m64_failed; - } - } - } - return 0; - -m64_failed: - pnv_pci_vf_release_m64(pdev, num_vfs); - return -EBUSY; -} - -static void pnv_pci_ioda2_release_pe_dma(struct pnv_ioda_pe *pe); - -static void pnv_ioda_release_vf_PE(struct pci_dev *pdev) -{ - struct pnv_phb *phb; - struct pnv_ioda_pe *pe, *pe_n; - struct pci_dn *pdn; - - phb = pci_bus_to_pnvhb(pdev->bus); - pdn = pci_get_pdn(pdev); - - if (!pdev->is_physfn) - return; - - /* FIXME: Use pnv_ioda_release_pe()? */ - list_for_each_entry_safe(pe, pe_n, &phb->ioda.pe_list, list) { - if (pe->parent_dev != pdev) - continue; - - pnv_pci_ioda2_release_pe_dma(pe); - - /* Remove from list */ - mutex_lock(&phb->ioda.pe_list_mutex); - list_del(&pe->list); - mutex_unlock(&phb->ioda.pe_list_mutex); - - pnv_ioda_deconfigure_pe(phb, pe); - - pnv_ioda_free_pe(pe); - } -} - -static void pnv_pci_sriov_disable(struct pci_dev *pdev) -{ - struct pnv_phb *phb; - struct pnv_ioda_pe *pe; - struct pci_dn *pdn; - u16 num_vfs, i; - - phb = pci_bus_to_pnvhb(pdev->bus); - pdn = pci_get_pdn(pdev); - num_vfs = pdn->num_vfs; - - /* Release VF PEs */ - pnv_ioda_release_vf_PE(pdev); - - if (phb->type == PNV_PHB_IODA2) { - if (!pdn->m64_single_mode) - pnv_pci_vf_resource_shift(pdev, -*pdn->pe_num_map); - - /* Release M64 windows */ - pnv_pci_vf_release_m64(pdev, num_vfs); - - /* Release PE numbers */ - if (pdn->m64_single_mode) { - for (i = 0; i < num_vfs; i++) { - if (pdn->pe_num_map[i] == IODA_INVALID_PE) - continue; - - pe = &phb->ioda.pe_array[pdn->pe_num_map[i]]; - pnv_ioda_free_pe(pe); - } - } else - bitmap_clear(phb->ioda.pe_alloc, *pdn->pe_num_map, num_vfs); - /* Releasing pe_num_map */ - kfree(pdn->pe_num_map); - } -} - -static void pnv_pci_ioda2_setup_dma_pe(struct pnv_phb *phb, - struct pnv_ioda_pe *pe); -static void pnv_ioda_setup_vf_PE(struct pci_dev *pdev, u16 num_vfs) -{ - struct pnv_phb *phb; - struct pnv_ioda_pe *pe; - int pe_num; - u16 vf_index; - struct pci_dn *pdn; - - phb = pci_bus_to_pnvhb(pdev->bus); - pdn = pci_get_pdn(pdev); - - if (!pdev->is_physfn) - return; - - /* Reserve PE for each VF */ - for (vf_index = 0; vf_index < num_vfs; vf_index++) { - int vf_devfn = pci_iov_virtfn_devfn(pdev, vf_index); - int vf_bus = pci_iov_virtfn_bus(pdev, vf_index); - struct pci_dn *vf_pdn; - - if (pdn->m64_single_mode) - pe_num = pdn->pe_num_map[vf_index]; - else - pe_num = *pdn->pe_num_map + vf_index; - - pe = &phb->ioda.pe_array[pe_num]; - pe->pe_number = pe_num; - pe->phb = phb; - pe->flags = PNV_IODA_PE_VF; - pe->pbus = NULL; - pe->parent_dev = pdev; - pe->mve_number = -1; - pe->rid = (vf_bus << 8) | vf_devfn; - - pe_info(pe, "VF %04d:%02d:%02d.%d associated with PE#%x\n", - pci_domain_nr(pdev->bus), pdev->bus->number, - PCI_SLOT(vf_devfn), PCI_FUNC(vf_devfn), pe_num); - - if (pnv_ioda_configure_pe(phb, pe)) { - /* XXX What do we do here ? */ - pnv_ioda_free_pe(pe); - pe->pdev = NULL; - continue; - } - - /* Put PE to the list */ - mutex_lock(&phb->ioda.pe_list_mutex); - list_add_tail(&pe->list, &phb->ioda.pe_list); - mutex_unlock(&phb->ioda.pe_list_mutex); - - /* associate this pe to it's pdn */ - list_for_each_entry(vf_pdn, &pdn->parent->child_list, list) { - if (vf_pdn->busno == vf_bus && - vf_pdn->devfn == vf_devfn) { - vf_pdn->pe_number = pe_num; - break; - } - } - - pnv_pci_ioda2_setup_dma_pe(phb, pe); - } -} - -static int pnv_pci_sriov_enable(struct pci_dev *pdev, u16 num_vfs) -{ - struct pnv_phb *phb; - struct pnv_ioda_pe *pe; - struct pci_dn *pdn; - int ret; - u16 i; - - phb = pci_bus_to_pnvhb(pdev->bus); - pdn = pci_get_pdn(pdev); - - if (phb->type == PNV_PHB_IODA2) { - if (!pdn->vfs_expanded) { - dev_info(&pdev->dev, "don't support this SRIOV device" - " with non 64bit-prefetchable IOV BAR\n"); - return -ENOSPC; - } - - /* - * When M64 BARs functions in Single PE mode, the number of VFs - * could be enabled must be less than the number of M64 BARs. - */ - if (pdn->m64_single_mode && num_vfs > phb->ioda.m64_bar_idx) { - dev_info(&pdev->dev, "Not enough M64 BAR for VFs\n"); - return -EBUSY; - } - - /* Allocating pe_num_map */ - if (pdn->m64_single_mode) - pdn->pe_num_map = kmalloc_array(num_vfs, - sizeof(*pdn->pe_num_map), - GFP_KERNEL); - else - pdn->pe_num_map = kmalloc(sizeof(*pdn->pe_num_map), GFP_KERNEL); - - if (!pdn->pe_num_map) - return -ENOMEM; - - if (pdn->m64_single_mode) - for (i = 0; i < num_vfs; i++) - pdn->pe_num_map[i] = IODA_INVALID_PE; - - /* Calculate available PE for required VFs */ - if (pdn->m64_single_mode) { - for (i = 0; i < num_vfs; i++) { - pe = pnv_ioda_alloc_pe(phb); - if (!pe) { - ret = -EBUSY; - goto m64_failed; - } - - pdn->pe_num_map[i] = pe->pe_number; - } - } else { - mutex_lock(&phb->ioda.pe_alloc_mutex); - *pdn->pe_num_map = bitmap_find_next_zero_area( - phb->ioda.pe_alloc, phb->ioda.total_pe_num, - 0, num_vfs, 0); - if (*pdn->pe_num_map >= phb->ioda.total_pe_num) { - mutex_unlock(&phb->ioda.pe_alloc_mutex); - dev_info(&pdev->dev, "Failed to enable VF%d\n", num_vfs); - kfree(pdn->pe_num_map); - return -EBUSY; - } - bitmap_set(phb->ioda.pe_alloc, *pdn->pe_num_map, num_vfs); - mutex_unlock(&phb->ioda.pe_alloc_mutex); - } - pdn->num_vfs = num_vfs; - - /* Assign M64 window accordingly */ - ret = pnv_pci_vf_assign_m64(pdev, num_vfs); - if (ret) { - dev_info(&pdev->dev, "Not enough M64 window resources\n"); - goto m64_failed; - } - - /* - * When using one M64 BAR to map one IOV BAR, we need to shift - * the IOV BAR according to the PE# allocated to the VFs. - * Otherwise, the PE# for the VF will conflict with others. - */ - if (!pdn->m64_single_mode) { - ret = pnv_pci_vf_resource_shift(pdev, *pdn->pe_num_map); - if (ret) - goto m64_failed; - } - } - - /* Setup VF PEs */ - pnv_ioda_setup_vf_PE(pdev, num_vfs); - - return 0; - -m64_failed: - if (pdn->m64_single_mode) { - for (i = 0; i < num_vfs; i++) { - if (pdn->pe_num_map[i] == IODA_INVALID_PE) - continue; - - pe = &phb->ioda.pe_array[pdn->pe_num_map[i]]; - pnv_ioda_free_pe(pe); - } - } else - bitmap_clear(phb->ioda.pe_alloc, *pdn->pe_num_map, num_vfs); - - /* Releasing pe_num_map */ - kfree(pdn->pe_num_map); - - return ret; -} - -static int pnv_pcibios_sriov_disable(struct pci_dev *pdev) -{ - pnv_pci_sriov_disable(pdev); - - /* Release PCI data */ - remove_sriov_vf_pdns(pdev); - return 0; -} - -static int pnv_pcibios_sriov_enable(struct pci_dev *pdev, u16 num_vfs) -{ - /* Allocate PCI data */ - add_sriov_vf_pdns(pdev); - - return pnv_pci_sriov_enable(pdev, num_vfs); -} -#endif /* CONFIG_PCI_IOV */ - static void pnv_pci_ioda1_setup_dma_pe(struct pnv_phb *phb, struct pnv_ioda_pe *pe); -static void pnv_pci_ioda2_setup_dma_pe(struct pnv_phb *phb, - struct pnv_ioda_pe *pe); - static void pnv_pci_ioda_dma_dev_setup(struct pci_dev *pdev) { struct pnv_phb *phb = pci_bus_to_pnvhb(pdev->bus); @@ -2559,8 +2057,8 @@ static struct iommu_table_group_ops pnv_pci_ioda2_ops = { }; #endif -static void pnv_pci_ioda2_setup_dma_pe(struct pnv_phb *phb, - struct pnv_ioda_pe *pe) +void pnv_pci_ioda2_setup_dma_pe(struct pnv_phb *phb, + struct pnv_ioda_pe *pe) { int64_t rc; @@ -2737,117 +2235,6 @@ static void pnv_pci_init_ioda_msis(struct pnv_phb *phb) count, phb->msi_base); } -#ifdef CONFIG_PCI_IOV -static void pnv_pci_ioda_fixup_iov_resources(struct pci_dev *pdev) -{ - struct pnv_phb *phb = pci_bus_to_pnvhb(pdev->bus); - const resource_size_t gate = phb->ioda.m64_segsize >> 2; - struct resource *res; - int i; - resource_size_t size, total_vf_bar_sz; - struct pci_dn *pdn; - int mul, total_vfs; - - pdn = pci_get_pdn(pdev); - pdn->vfs_expanded = 0; - pdn->m64_single_mode = false; - - total_vfs = pci_sriov_get_totalvfs(pdev); - mul = phb->ioda.total_pe_num; - total_vf_bar_sz = 0; - - for (i = 0; i < PCI_SRIOV_NUM_BARS; i++) { - res = &pdev->resource[i + PCI_IOV_RESOURCES]; - if (!res->flags || res->parent) - continue; - if (!pnv_pci_is_m64_flags(res->flags)) { - dev_warn(&pdev->dev, "Don't support SR-IOV with" - " non M64 VF BAR%d: %pR. \n", - i, res); - goto truncate_iov; - } - - total_vf_bar_sz += pci_iov_resource_size(pdev, - i + PCI_IOV_RESOURCES); - - /* - * If bigger than quarter of M64 segment size, just round up - * power of two. - * - * Generally, one M64 BAR maps one IOV BAR. To avoid conflict - * with other devices, IOV BAR size is expanded to be - * (total_pe * VF_BAR_size). When VF_BAR_size is half of M64 - * segment size , the expanded size would equal to half of the - * whole M64 space size, which will exhaust the M64 Space and - * limit the system flexibility. This is a design decision to - * set the boundary to quarter of the M64 segment size. - */ - if (total_vf_bar_sz > gate) { - mul = roundup_pow_of_two(total_vfs); - dev_info(&pdev->dev, - "VF BAR Total IOV size %llx > %llx, roundup to %d VFs\n", - total_vf_bar_sz, gate, mul); - pdn->m64_single_mode = true; - break; - } - } - - for (i = 0; i < PCI_SRIOV_NUM_BARS; i++) { - res = &pdev->resource[i + PCI_IOV_RESOURCES]; - if (!res->flags || res->parent) - continue; - - size = pci_iov_resource_size(pdev, i + PCI_IOV_RESOURCES); - /* - * On PHB3, the minimum size alignment of M64 BAR in single - * mode is 32MB. - */ - if (pdn->m64_single_mode && (size < SZ_32M)) - goto truncate_iov; - dev_dbg(&pdev->dev, " Fixing VF BAR%d: %pR to\n", i, res); - res->end = res->start + size * mul - 1; - dev_dbg(&pdev->dev, " %pR\n", res); - dev_info(&pdev->dev, "VF BAR%d: %pR (expanded to %d VFs for PE alignment)", - i, res, mul); - } - pdn->vfs_expanded = mul; - - return; - -truncate_iov: - /* To save MMIO space, IOV BAR is truncated. */ - for (i = 0; i < PCI_SRIOV_NUM_BARS; i++) { - res = &pdev->resource[i + PCI_IOV_RESOURCES]; - res->flags = 0; - res->end = res->start - 1; - } -} - -static void pnv_pci_ioda_fixup_iov(struct pci_dev *pdev) -{ - if (WARN_ON(pci_dev_is_added(pdev))) - return; - - if (pdev->is_virtfn) { - struct pnv_ioda_pe *pe = pnv_ioda_get_pe(pdev); - - /* - * VF PEs are single-device PEs so their pdev pointer needs to - * be set. The pdev doesn't exist when the PE is allocated (in - * (pcibios_sriov_enable()) so we fix it up here. - */ - pe->pdev = pdev; - WARN_ON(!(pe->flags & PNV_IODA_PE_VF)); - } else if (pdev->is_physfn) { - /* - * For PFs adjust their allocated IOV resources to match what - * the PHB can support using it's M64 BAR table. - */ - pnv_pci_ioda_fixup_iov_resources(pdev); - } -} -#endif /* CONFIG_PCI_IOV */ - static void pnv_ioda_setup_pe_res(struct pnv_ioda_pe *pe, struct resource *res) { @@ -3192,41 +2579,6 @@ static resource_size_t pnv_pci_default_alignment(void) return PAGE_SIZE; } -#ifdef CONFIG_PCI_IOV -static resource_size_t pnv_pci_iov_resource_alignment(struct pci_dev *pdev, - int resno) -{ - struct pnv_phb *phb = pci_bus_to_pnvhb(pdev->bus); - struct pci_dn *pdn = pci_get_pdn(pdev); - resource_size_t align; - - /* - * On PowerNV platform, IOV BAR is mapped by M64 BAR to enable the - * SR-IOV. While from hardware perspective, the range mapped by M64 - * BAR should be size aligned. - * - * When IOV BAR is mapped with M64 BAR in Single PE mode, the extra - * powernv-specific hardware restriction is gone. But if just use the - * VF BAR size as the alignment, PF BAR / VF BAR may be allocated with - * in one segment of M64 #15, which introduces the PE conflict between - * PF and VF. Based on this, the minimum alignment of an IOV BAR is - * m64_segsize. - * - * This function returns the total IOV BAR size if M64 BAR is in - * Shared PE mode or just VF BAR size if not. - * If the M64 BAR is in Single PE mode, return the VF BAR size or - * M64 segment size if IOV BAR size is less. - */ - align = pci_iov_resource_size(pdev, resno); - if (!pdn->vfs_expanded) - return align; - if (pdn->m64_single_mode) - return max(align, (resource_size_t)phb->ioda.m64_segsize); - - return pdn->vfs_expanded * align; -} -#endif /* CONFIG_PCI_IOV */ - /* Prevent enabling devices for which we couldn't properly * assign a PE */ @@ -3323,7 +2675,7 @@ static void pnv_pci_ioda1_release_pe_dma(struct pnv_ioda_pe *pe) iommu_tce_table_put(tbl); } -static void pnv_pci_ioda2_release_pe_dma(struct pnv_ioda_pe *pe) +void pnv_pci_ioda2_release_pe_dma(struct pnv_ioda_pe *pe) { struct iommu_table *tbl = pe->table_group.tables[0]; int64_t rc; @@ -3436,12 +2788,23 @@ static void pnv_pci_release_device(struct pci_dev *pdev) struct pci_dn *pdn = pci_get_pdn(pdev); struct pnv_ioda_pe *pe; + /* The VF PE state is torn down when sriov_disable() is called */ if (pdev->is_virtfn) return; if (!pdn || pdn->pe_number == IODA_INVALID_PE) return; +#ifdef CONFIG_PCI_IOV + /* + * FIXME: Try move this to sriov_disable(). It's here since we allocate + * the iov state at probe time since we need to fiddle with the IOV + * resources. + */ + if (pdev->is_physfn) + kfree(pdev->dev.archdata.iov_data); +#endif + /* * PCI hotplug can happen as part of EEH error recovery. The @pdn * isn't removed and added afterwards in this scenario. We should diff --git a/arch/powerpc/platforms/powernv/pci-sriov.c b/arch/powerpc/platforms/powernv/pci-sriov.c new file mode 100644 index 0000000000000..c5b7e20286c67 --- /dev/null +++ b/arch/powerpc/platforms/powernv/pci-sriov.c @@ -0,0 +1,640 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include +#include +#include +#include + +#include + +#include "pci.h" + +/* for pci_dev_is_added() */ +#include "../../../../drivers/pci/pci.h" + + +static void pnv_pci_ioda_fixup_iov_resources(struct pci_dev *pdev) +{ + struct pnv_phb *phb = pci_bus_to_pnvhb(pdev->bus); + const resource_size_t gate = phb->ioda.m64_segsize >> 2; + struct resource *res; + int i; + resource_size_t size, total_vf_bar_sz; + struct pnv_iov_data *iov; + int mul, total_vfs; + + iov = kzalloc(sizeof(*iov), GFP_KERNEL); + if (!iov) + goto truncate_iov; + pdev->dev.archdata.iov_data = iov; + + total_vfs = pci_sriov_get_totalvfs(pdev); + mul = phb->ioda.total_pe_num; + total_vf_bar_sz = 0; + + for (i = 0; i < PCI_SRIOV_NUM_BARS; i++) { + res = &pdev->resource[i + PCI_IOV_RESOURCES]; + if (!res->flags || res->parent) + continue; + if (!pnv_pci_is_m64_flags(res->flags)) { + dev_warn(&pdev->dev, "Don't support SR-IOV with non M64 VF BAR%d: %pR. \n", + i, res); + goto truncate_iov; + } + + total_vf_bar_sz += pci_iov_resource_size(pdev, + i + PCI_IOV_RESOURCES); + + /* + * If bigger than quarter of M64 segment size, just round up + * power of two. + * + * Generally, one M64 BAR maps one IOV BAR. To avoid conflict + * with other devices, IOV BAR size is expanded to be + * (total_pe * VF_BAR_size). When VF_BAR_size is half of M64 + * segment size , the expanded size would equal to half of the + * whole M64 space size, which will exhaust the M64 Space and + * limit the system flexibility. This is a design decision to + * set the boundary to quarter of the M64 segment size. + */ + if (total_vf_bar_sz > gate) { + mul = roundup_pow_of_two(total_vfs); + dev_info(&pdev->dev, + "VF BAR Total IOV size %llx > %llx, roundup to %d VFs\n", + total_vf_bar_sz, gate, mul); + iov->m64_single_mode = true; + break; + } + } + + for (i = 0; i < PCI_SRIOV_NUM_BARS; i++) { + res = &pdev->resource[i + PCI_IOV_RESOURCES]; + if (!res->flags || res->parent) + continue; + + size = pci_iov_resource_size(pdev, i + PCI_IOV_RESOURCES); + /* + * On PHB3, the minimum size alignment of M64 BAR in single + * mode is 32MB. + */ + if (iov->m64_single_mode && (size < SZ_32M)) + goto truncate_iov; + dev_dbg(&pdev->dev, " Fixing VF BAR%d: %pR to\n", i, res); + res->end = res->start + size * mul - 1; + dev_dbg(&pdev->dev, " %pR\n", res); + dev_info(&pdev->dev, "VF BAR%d: %pR (expanded to %d VFs for PE alignment)", + i, res, mul); + } + iov->vfs_expanded = mul; + + return; + +truncate_iov: + /* To save MMIO space, IOV BAR is truncated. */ + for (i = 0; i < PCI_SRIOV_NUM_BARS; i++) { + res = &pdev->resource[i + PCI_IOV_RESOURCES]; + res->flags = 0; + res->end = res->start - 1; + } + + pdev->dev.archdata.iov_data = NULL; + kfree(iov); +} + +void pnv_pci_ioda_fixup_iov(struct pci_dev *pdev) +{ + if (WARN_ON(pci_dev_is_added(pdev))) + return; + + if (pdev->is_virtfn) { + struct pnv_ioda_pe *pe = pnv_ioda_get_pe(pdev); + + /* + * VF PEs are single-device PEs so their pdev pointer needs to + * be set. The pdev doesn't exist when the PE is allocated (in + * (pcibios_sriov_enable()) so we fix it up here. + */ + pe->pdev = pdev; + WARN_ON(!(pe->flags & PNV_IODA_PE_VF)); + } else if (pdev->is_physfn) { + /* + * For PFs adjust their allocated IOV resources to match what + * the PHB can support using it's M64 BAR table. + */ + pnv_pci_ioda_fixup_iov_resources(pdev); + } +} + +resource_size_t pnv_pci_iov_resource_alignment(struct pci_dev *pdev, + int resno) +{ + struct pnv_phb *phb = pci_bus_to_pnvhb(pdev->bus); + struct pnv_iov_data *iov = pnv_iov_get(pdev); + resource_size_t align; + + /* + * On PowerNV platform, IOV BAR is mapped by M64 BAR to enable the + * SR-IOV. While from hardware perspective, the range mapped by M64 + * BAR should be size aligned. + * + * When IOV BAR is mapped with M64 BAR in Single PE mode, the extra + * powernv-specific hardware restriction is gone. But if just use the + * VF BAR size as the alignment, PF BAR / VF BAR may be allocated with + * in one segment of M64 #15, which introduces the PE conflict between + * PF and VF. Based on this, the minimum alignment of an IOV BAR is + * m64_segsize. + * + * This function returns the total IOV BAR size if M64 BAR is in + * Shared PE mode or just VF BAR size if not. + * If the M64 BAR is in Single PE mode, return the VF BAR size or + * M64 segment size if IOV BAR size is less. + */ + align = pci_iov_resource_size(pdev, resno); + + /* + * iov can be null if we have an SR-IOV device with IOV BAR that can't + * be placed in the m64 space (i.e. The BAR is 32bit or non-prefetch). + * In that case we don't allow VFs to be enabled so just return the + * default alignment. + */ + if (!iov) + return align; + if (!iov->vfs_expanded) + return align; + if (iov->m64_single_mode) + return max(align, (resource_size_t)phb->ioda.m64_segsize); + + return iov->vfs_expanded * align; +} + +static int pnv_pci_vf_release_m64(struct pci_dev *pdev, u16 num_vfs) +{ + struct pnv_iov_data *iov; + struct pnv_phb *phb; + int i, j; + int m64_bars; + + phb = pci_bus_to_pnvhb(pdev->bus); + iov = pnv_iov_get(pdev); + + if (iov->m64_single_mode) + m64_bars = num_vfs; + else + m64_bars = 1; + + for (i = 0; i < PCI_SRIOV_NUM_BARS; i++) + for (j = 0; j < m64_bars; j++) { + if (iov->m64_map[j][i] == IODA_INVALID_M64) + continue; + opal_pci_phb_mmio_enable(phb->opal_id, + OPAL_M64_WINDOW_TYPE, iov->m64_map[j][i], 0); + clear_bit(iov->m64_map[j][i], &phb->ioda.m64_bar_alloc); + iov->m64_map[j][i] = IODA_INVALID_M64; + } + + kfree(iov->m64_map); + return 0; +} + +static int pnv_pci_vf_assign_m64(struct pci_dev *pdev, u16 num_vfs) +{ + struct pnv_iov_data *iov; + struct pnv_phb *phb; + unsigned int win; + struct resource *res; + int i, j; + int64_t rc; + int total_vfs; + resource_size_t size, start; + int pe_num; + int m64_bars; + + phb = pci_bus_to_pnvhb(pdev->bus); + iov = pnv_iov_get(pdev); + total_vfs = pci_sriov_get_totalvfs(pdev); + + if (iov->m64_single_mode) + m64_bars = num_vfs; + else + m64_bars = 1; + + iov->m64_map = kmalloc_array(m64_bars, + sizeof(*iov->m64_map), + GFP_KERNEL); + if (!iov->m64_map) + return -ENOMEM; + /* Initialize the m64_map to IODA_INVALID_M64 */ + for (i = 0; i < m64_bars ; i++) + for (j = 0; j < PCI_SRIOV_NUM_BARS; j++) + iov->m64_map[i][j] = IODA_INVALID_M64; + + + for (i = 0; i < PCI_SRIOV_NUM_BARS; i++) { + res = &pdev->resource[i + PCI_IOV_RESOURCES]; + if (!res->flags || !res->parent) + continue; + + for (j = 0; j < m64_bars; j++) { + do { + win = find_next_zero_bit(&phb->ioda.m64_bar_alloc, + phb->ioda.m64_bar_idx + 1, 0); + + if (win >= phb->ioda.m64_bar_idx + 1) + goto m64_failed; + } while (test_and_set_bit(win, &phb->ioda.m64_bar_alloc)); + + iov->m64_map[j][i] = win; + + if (iov->m64_single_mode) { + size = pci_iov_resource_size(pdev, + PCI_IOV_RESOURCES + i); + start = res->start + size * j; + } else { + size = resource_size(res); + start = res->start; + } + + /* Map the M64 here */ + if (iov->m64_single_mode) { + pe_num = iov->pe_num_map[j]; + rc = opal_pci_map_pe_mmio_window(phb->opal_id, + pe_num, OPAL_M64_WINDOW_TYPE, + iov->m64_map[j][i], 0); + } + + rc = opal_pci_set_phb_mem_window(phb->opal_id, + OPAL_M64_WINDOW_TYPE, + iov->m64_map[j][i], + start, + 0, /* unused */ + size); + + + if (rc != OPAL_SUCCESS) { + dev_err(&pdev->dev, "Failed to map M64 window #%d: %lld\n", + win, rc); + goto m64_failed; + } + + if (iov->m64_single_mode) + rc = opal_pci_phb_mmio_enable(phb->opal_id, + OPAL_M64_WINDOW_TYPE, iov->m64_map[j][i], 2); + else + rc = opal_pci_phb_mmio_enable(phb->opal_id, + OPAL_M64_WINDOW_TYPE, iov->m64_map[j][i], 1); + + if (rc != OPAL_SUCCESS) { + dev_err(&pdev->dev, "Failed to enable M64 window #%d: %llx\n", + win, rc); + goto m64_failed; + } + } + } + return 0; + +m64_failed: + pnv_pci_vf_release_m64(pdev, num_vfs); + return -EBUSY; +} + +static void pnv_ioda_release_vf_PE(struct pci_dev *pdev) +{ + struct pnv_phb *phb; + struct pnv_ioda_pe *pe, *pe_n; + + phb = pci_bus_to_pnvhb(pdev->bus); + + if (!pdev->is_physfn) + return; + + /* FIXME: Use pnv_ioda_release_pe()? */ + list_for_each_entry_safe(pe, pe_n, &phb->ioda.pe_list, list) { + if (pe->parent_dev != pdev) + continue; + + pnv_pci_ioda2_release_pe_dma(pe); + + /* Remove from list */ + mutex_lock(&phb->ioda.pe_list_mutex); + list_del(&pe->list); + mutex_unlock(&phb->ioda.pe_list_mutex); + + pnv_ioda_deconfigure_pe(phb, pe); + + pnv_ioda_free_pe(pe); + } +} + +static int pnv_pci_vf_resource_shift(struct pci_dev *dev, int offset) +{ + struct resource *res, res2; + struct pnv_iov_data *iov; + resource_size_t size; + u16 num_vfs; + int i; + + if (!dev->is_physfn) + return -EINVAL; + iov = pnv_iov_get(dev); + + /* + * "offset" is in VFs. The M64 windows are sized so that when they + * are segmented, each segment is the same size as the IOV BAR. + * Each segment is in a separate PE, and the high order bits of the + * address are the PE number. Therefore, each VF's BAR is in a + * separate PE, and changing the IOV BAR start address changes the + * range of PEs the VFs are in. + */ + num_vfs = iov->num_vfs; + for (i = 0; i < PCI_SRIOV_NUM_BARS; i++) { + res = &dev->resource[i + PCI_IOV_RESOURCES]; + if (!res->flags || !res->parent) + continue; + + /* + * The actual IOV BAR range is determined by the start address + * and the actual size for num_vfs VFs BAR. This check is to + * make sure that after shifting, the range will not overlap + * with another device. + */ + size = pci_iov_resource_size(dev, i + PCI_IOV_RESOURCES); + res2.flags = res->flags; + res2.start = res->start + (size * offset); + res2.end = res2.start + (size * num_vfs) - 1; + + if (res2.end > res->end) { + dev_err(&dev->dev, "VF BAR%d: %pR would extend past %pR (trying to enable %d VFs shifted by %d)\n", + i, &res2, res, num_vfs, offset); + return -EBUSY; + } + } + + /* + * Since M64 BAR shares segments among all possible 256 PEs, + * we have to shift the beginning of PF IOV BAR to make it start from + * the segment which belongs to the PE number assigned to the first VF. + * This creates a "hole" in the /proc/iomem which could be used for + * allocating other resources so we reserve this area below and + * release when IOV is released. + */ + for (i = 0; i < PCI_SRIOV_NUM_BARS; i++) { + res = &dev->resource[i + PCI_IOV_RESOURCES]; + if (!res->flags || !res->parent) + continue; + + size = pci_iov_resource_size(dev, i + PCI_IOV_RESOURCES); + res2 = *res; + res->start += size * offset; + + dev_info(&dev->dev, "VF BAR%d: %pR shifted to %pR (%sabling %d VFs shifted by %d)\n", + i, &res2, res, (offset > 0) ? "En" : "Dis", + num_vfs, offset); + + if (offset < 0) { + devm_release_resource(&dev->dev, &iov->holes[i]); + memset(&iov->holes[i], 0, sizeof(iov->holes[i])); + } + + pci_update_resource(dev, i + PCI_IOV_RESOURCES); + + if (offset > 0) { + iov->holes[i].start = res2.start; + iov->holes[i].end = res2.start + size * offset - 1; + iov->holes[i].flags = IORESOURCE_BUS; + iov->holes[i].name = "pnv_iov_reserved"; + devm_request_resource(&dev->dev, res->parent, + &iov->holes[i]); + } + } + return 0; +} + +static void pnv_pci_sriov_disable(struct pci_dev *pdev) +{ + struct pnv_phb *phb; + struct pnv_ioda_pe *pe; + struct pnv_iov_data *iov; + u16 num_vfs, i; + + phb = pci_bus_to_pnvhb(pdev->bus); + iov = pnv_iov_get(pdev); + num_vfs = iov->num_vfs; + + /* Release VF PEs */ + pnv_ioda_release_vf_PE(pdev); + + if (phb->type == PNV_PHB_IODA2) { + if (!iov->m64_single_mode) + pnv_pci_vf_resource_shift(pdev, -*iov->pe_num_map); + + /* Release M64 windows */ + pnv_pci_vf_release_m64(pdev, num_vfs); + + /* Release PE numbers */ + if (iov->m64_single_mode) { + for (i = 0; i < num_vfs; i++) { + if (iov->pe_num_map[i] == IODA_INVALID_PE) + continue; + + pe = &phb->ioda.pe_array[iov->pe_num_map[i]]; + pnv_ioda_free_pe(pe); + } + } else + bitmap_clear(phb->ioda.pe_alloc, *iov->pe_num_map, num_vfs); + /* Releasing pe_num_map */ + kfree(iov->pe_num_map); + } +} + +static void pnv_ioda_setup_vf_PE(struct pci_dev *pdev, u16 num_vfs) +{ + struct pnv_phb *phb; + struct pnv_ioda_pe *pe; + int pe_num; + u16 vf_index; + struct pnv_iov_data *iov; + struct pci_dn *pdn; + + if (!pdev->is_physfn) + return; + + phb = pci_bus_to_pnvhb(pdev->bus); + pdn = pci_get_pdn(pdev); + iov = pnv_iov_get(pdev); + + /* Reserve PE for each VF */ + for (vf_index = 0; vf_index < num_vfs; vf_index++) { + int vf_devfn = pci_iov_virtfn_devfn(pdev, vf_index); + int vf_bus = pci_iov_virtfn_bus(pdev, vf_index); + struct pci_dn *vf_pdn; + + if (iov->m64_single_mode) + pe_num = iov->pe_num_map[vf_index]; + else + pe_num = *iov->pe_num_map + vf_index; + + pe = &phb->ioda.pe_array[pe_num]; + pe->pe_number = pe_num; + pe->phb = phb; + pe->flags = PNV_IODA_PE_VF; + pe->pbus = NULL; + pe->parent_dev = pdev; + pe->mve_number = -1; + pe->rid = (vf_bus << 8) | vf_devfn; + + pe_info(pe, "VF %04d:%02d:%02d.%d associated with PE#%x\n", + pci_domain_nr(pdev->bus), pdev->bus->number, + PCI_SLOT(vf_devfn), PCI_FUNC(vf_devfn), pe_num); + + if (pnv_ioda_configure_pe(phb, pe)) { + /* XXX What do we do here ? */ + pnv_ioda_free_pe(pe); + pe->pdev = NULL; + continue; + } + + /* Put PE to the list */ + mutex_lock(&phb->ioda.pe_list_mutex); + list_add_tail(&pe->list, &phb->ioda.pe_list); + mutex_unlock(&phb->ioda.pe_list_mutex); + + /* associate this pe to it's pdn */ + list_for_each_entry(vf_pdn, &pdn->parent->child_list, list) { + if (vf_pdn->busno == vf_bus && + vf_pdn->devfn == vf_devfn) { + vf_pdn->pe_number = pe_num; + break; + } + } + + pnv_pci_ioda2_setup_dma_pe(phb, pe); + } +} + +static int pnv_pci_sriov_enable(struct pci_dev *pdev, u16 num_vfs) +{ + struct pnv_iov_data *iov; + struct pnv_phb *phb; + struct pnv_ioda_pe *pe; + int ret; + u16 i; + + phb = pci_bus_to_pnvhb(pdev->bus); + iov = pnv_iov_get(pdev); + + if (phb->type == PNV_PHB_IODA2) { + if (!iov->vfs_expanded) { + dev_info(&pdev->dev, + "don't support this SRIOV device with non 64bit-prefetchable IOV BAR\n"); + return -ENOSPC; + } + + /* + * When M64 BARs functions in Single PE mode, the number of VFs + * could be enabled must be less than the number of M64 BARs. + */ + if (iov->m64_single_mode && num_vfs > phb->ioda.m64_bar_idx) { + dev_info(&pdev->dev, "Not enough M64 BAR for VFs\n"); + return -EBUSY; + } + + /* Allocating pe_num_map */ + if (iov->m64_single_mode) + iov->pe_num_map = kmalloc_array(num_vfs, + sizeof(*iov->pe_num_map), + GFP_KERNEL); + else + iov->pe_num_map = kmalloc(sizeof(*iov->pe_num_map), GFP_KERNEL); + + if (!iov->pe_num_map) + return -ENOMEM; + + if (iov->m64_single_mode) + for (i = 0; i < num_vfs; i++) + iov->pe_num_map[i] = IODA_INVALID_PE; + + /* Calculate available PE for required VFs */ + if (iov->m64_single_mode) { + for (i = 0; i < num_vfs; i++) { + pe = pnv_ioda_alloc_pe(phb); + if (!pe) { + ret = -EBUSY; + goto m64_failed; + } + + iov->pe_num_map[i] = pe->pe_number; + } + } else { + mutex_lock(&phb->ioda.pe_alloc_mutex); + *iov->pe_num_map = bitmap_find_next_zero_area( + phb->ioda.pe_alloc, phb->ioda.total_pe_num, + 0, num_vfs, 0); + if (*iov->pe_num_map >= phb->ioda.total_pe_num) { + mutex_unlock(&phb->ioda.pe_alloc_mutex); + dev_info(&pdev->dev, "Failed to enable VF%d\n", num_vfs); + kfree(iov->pe_num_map); + return -EBUSY; + } + bitmap_set(phb->ioda.pe_alloc, *iov->pe_num_map, num_vfs); + mutex_unlock(&phb->ioda.pe_alloc_mutex); + } + iov->num_vfs = num_vfs; + + /* Assign M64 window accordingly */ + ret = pnv_pci_vf_assign_m64(pdev, num_vfs); + if (ret) { + dev_info(&pdev->dev, "Not enough M64 window resources\n"); + goto m64_failed; + } + + /* + * When using one M64 BAR to map one IOV BAR, we need to shift + * the IOV BAR according to the PE# allocated to the VFs. + * Otherwise, the PE# for the VF will conflict with others. + */ + if (!iov->m64_single_mode) { + ret = pnv_pci_vf_resource_shift(pdev, *iov->pe_num_map); + if (ret) + goto m64_failed; + } + } + + /* Setup VF PEs */ + pnv_ioda_setup_vf_PE(pdev, num_vfs); + + return 0; + +m64_failed: + if (iov->m64_single_mode) { + for (i = 0; i < num_vfs; i++) { + if (iov->pe_num_map[i] == IODA_INVALID_PE) + continue; + + pe = &phb->ioda.pe_array[iov->pe_num_map[i]]; + pnv_ioda_free_pe(pe); + } + } else + bitmap_clear(phb->ioda.pe_alloc, *iov->pe_num_map, num_vfs); + + /* Releasing pe_num_map */ + kfree(iov->pe_num_map); + + return ret; +} + +int pnv_pcibios_sriov_disable(struct pci_dev *pdev) +{ + pnv_pci_sriov_disable(pdev); + + /* Release PCI data */ + remove_sriov_vf_pdns(pdev); + return 0; +} + +int pnv_pcibios_sriov_enable(struct pci_dev *pdev, u16 num_vfs) +{ + /* Allocate PCI data */ + add_sriov_vf_pdns(pdev); + + return pnv_pci_sriov_enable(pdev, num_vfs); +} diff --git a/arch/powerpc/platforms/powernv/pci.h b/arch/powerpc/platforms/powernv/pci.h index 6aa6aefb637d2..0156d7d17f7d2 100644 --- a/arch/powerpc/platforms/powernv/pci.h +++ b/arch/powerpc/platforms/powernv/pci.h @@ -194,6 +194,80 @@ struct pnv_phb { u8 *diag_data; }; + +/* IODA PE management */ + +static inline bool pnv_pci_is_m64(struct pnv_phb *phb, struct resource *r) +{ + /* + * WARNING: We cannot rely on the resource flags. The Linux PCI + * allocation code sometimes decides to put a 64-bit prefetchable + * BAR in the 32-bit window, so we have to compare the addresses. + * + * For simplicity we only test resource start. + */ + return (r->start >= phb->ioda.m64_base && + r->start < (phb->ioda.m64_base + phb->ioda.m64_size)); +} + +static inline bool pnv_pci_is_m64_flags(unsigned long resource_flags) +{ + unsigned long flags = (IORESOURCE_MEM_64 | IORESOURCE_PREFETCH); + + return (resource_flags & flags) == flags; +} + +int pnv_ioda_configure_pe(struct pnv_phb *phb, struct pnv_ioda_pe *pe); +int pnv_ioda_deconfigure_pe(struct pnv_phb *phb, struct pnv_ioda_pe *pe); + +void pnv_pci_ioda2_setup_dma_pe(struct pnv_phb *phb, struct pnv_ioda_pe *pe); +void pnv_pci_ioda2_release_pe_dma(struct pnv_ioda_pe *pe); + +struct pnv_ioda_pe *pnv_ioda_alloc_pe(struct pnv_phb *phb); +void pnv_ioda_free_pe(struct pnv_ioda_pe *pe); + +#ifdef CONFIG_PCI_IOV +/* + * For SR-IOV we want to put each VF's MMIO resource in to a separate PE. + * This requires a bit of acrobatics with the MMIO -> PE configuration + * and this structure is used to keep track of it all. + */ +struct pnv_iov_data { + /* number of VFs IOV BAR expanded. FIXME: rename this to something less bad */ + u16 vfs_expanded; + + /* number of VFs enabled */ + u16 num_vfs; + unsigned int *pe_num_map; /* PE# for the first VF PE or array */ + + /* Did we map the VF BARs with single-PE IODA BARs? */ + bool m64_single_mode; + + int (*m64_map)[PCI_SRIOV_NUM_BARS]; +#define IODA_INVALID_M64 (-1) + + /* + * If we map the SR-IOV BARs with a segmented window then + * parts of that window will be "claimed" by other PEs. + * + * "holes" here is used to reserve the leading portion + * of the window that is used by other (non VF) PEs. + */ + struct resource holes[PCI_SRIOV_NUM_BARS]; +}; + +static inline struct pnv_iov_data *pnv_iov_get(struct pci_dev *pdev) +{ + return pdev->dev.archdata.iov_data; +} + +void pnv_pci_ioda_fixup_iov(struct pci_dev *pdev); +resource_size_t pnv_pci_iov_resource_alignment(struct pci_dev *pdev, int resno); + +int pnv_pcibios_sriov_enable(struct pci_dev *pdev, u16 num_vfs); +int pnv_pcibios_sriov_disable(struct pci_dev *pdev); +#endif /* CONFIG_PCI_IOV */ + extern struct pci_ops pnv_pci_ops; void pnv_pci_dump_phb_diag_data(struct pci_controller *hose, -- GitLab From ff79e11af0979b25ecb38e4c843779d4a759a4e2 Mon Sep 17 00:00:00 2001 From: Oliver O'Halloran Date: Wed, 22 Jul 2020 16:57:05 +1000 Subject: [PATCH 0847/1476] powerpc/powernv/sriov: Explain how SR-IOV works on PowerNV SR-IOV support on PowerNV is a byzantine maze of hooks. I have no idea how anyone is supposed to know how it works except through a lot of stuffering. Write up some docs about the overall story to help out the next sucker^Wperson who needs to tinker with it. Signed-off-by: Oliver O'Halloran Reviewed-by: Alexey Kardashevskiy Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20200722065715.1432738-6-oohall@gmail.com --- arch/powerpc/platforms/powernv/pci-sriov.c | 130 +++++++++++++++++++++ 1 file changed, 130 insertions(+) diff --git a/arch/powerpc/platforms/powernv/pci-sriov.c b/arch/powerpc/platforms/powernv/pci-sriov.c index c5b7e20286c67..8f5f0f50281e1 100644 --- a/arch/powerpc/platforms/powernv/pci-sriov.c +++ b/arch/powerpc/platforms/powernv/pci-sriov.c @@ -12,6 +12,136 @@ /* for pci_dev_is_added() */ #include "../../../../drivers/pci/pci.h" +/* + * The majority of the complexity in supporting SR-IOV on PowerNV comes from + * the need to put the MMIO space for each VF into a separate PE. Internally + * the PHB maps MMIO addresses to a specific PE using the "Memory BAR Table". + * The MBT historically only applied to the 64bit MMIO window of the PHB + * so it's common to see it referred to as the "M64BT". + * + * An MBT entry stores the mapped range as an , pair. This forces + * the address range that we want to map to be power-of-two sized and aligned. + * For conventional PCI devices this isn't really an issue since PCI device BARs + * have the same requirement. + * + * For a SR-IOV BAR things are a little more awkward since size and alignment + * are not coupled. The alignment is set based on the the per-VF BAR size, but + * the total BAR area is: number-of-vfs * per-vf-size. The number of VFs + * isn't necessarily a power of two, so neither is the total size. To fix that + * we need to finesse (read: hack) the Linux BAR allocator so that it will + * allocate the SR-IOV BARs in a way that lets us map them using the MBT. + * + * The changes to size and alignment that we need to do depend on the "mode" + * of MBT entry that we use. We only support SR-IOV on PHB3 (IODA2) and above, + * so as a baseline we can assume that we have the following BAR modes + * available: + * + * NB: $PE_COUNT is the number of PEs that the PHB supports. + * + * a) A segmented BAR that splits the mapped range into $PE_COUNT equally sized + * segments. The n'th segment is mapped to the n'th PE. + * b) An un-segmented BAR that maps the whole address range to a specific PE. + * + * + * We prefer to use mode a) since it only requires one MBT entry per SR-IOV BAR + * For comparison b) requires one entry per-VF per-BAR, or: + * (num-vfs * num-sriov-bars) in total. To use a) we need the size of each segment + * to equal the size of the per-VF BAR area. So: + * + * new_size = per-vf-size * number-of-PEs + * + * The alignment for the SR-IOV BAR also needs to be changed from per-vf-size + * to "new_size", calculated above. Implementing this is a convoluted process + * which requires several hooks in the PCI core: + * + * 1. In pcibios_add_device() we call pnv_pci_ioda_fixup_iov(). + * + * At this point the device has been probed and the device's BARs are sized, + * but no resource allocations have been done. The SR-IOV BARs are sized + * based on the maximum number of VFs supported by the device and we need + * to increase that to new_size. + * + * 2. Later, when Linux actually assigns resources it tries to make the resource + * allocations for each PCI bus as compact as possible. As a part of that it + * sorts the BARs on a bus by their required alignment, which is calculated + * using pci_resource_alignment(). + * + * For IOV resources this goes: + * pci_resource_alignment() + * pci_sriov_resource_alignment() + * pcibios_sriov_resource_alignment() + * pnv_pci_iov_resource_alignment() + * + * Our hook overrides the default alignment, equal to the per-vf-size, with + * new_size computed above. + * + * 3. When userspace enables VFs for a device: + * + * sriov_enable() + * pcibios_sriov_enable() + * pnv_pcibios_sriov_enable() + * + * This is where we actually allocate PE numbers for each VF and setup the + * MBT mapping for each SR-IOV BAR. In steps 1) and 2) we setup an "arena" + * where each MBT segment is equal in size to the VF BAR so we can shift + * around the actual SR-IOV BAR location within this arena. We need this + * ability because the PE space is shared by all devices on the same PHB. + * When using mode a) described above segment 0 in maps to PE#0 which might + * be already being used by another device on the PHB. + * + * As a result we need allocate a contigious range of PE numbers, then shift + * the address programmed into the SR-IOV BAR of the PF so that the address + * of VF0 matches up with the segment corresponding to the first allocated + * PE number. This is handled in pnv_pci_vf_resource_shift(). + * + * Once all that is done we return to the PCI core which then enables VFs, + * scans them and creates pci_devs for each. The init process for a VF is + * largely the same as a normal device, but the VF is inserted into the IODA + * PE that we allocated for it rather than the PE associated with the bus. + * + * 4. When userspace disables VFs we unwind the above in + * pnv_pcibios_sriov_disable(). Fortunately this is relatively simple since + * we don't need to validate anything, just tear down the mappings and + * move SR-IOV resource back to its "proper" location. + * + * That's how mode a) works. In theory mode b) (single PE mapping) is less work + * since we can map each individual VF with a separate BAR. However, there's a + * few limitations: + * + * 1) For IODA2 mode b) has a minimum alignment requirement of 32MB. This makes + * it only usable for devices with very large per-VF BARs. Such devices are + * similar to Big Foot. They definitely exist, but I've never seen one. + * + * 2) The number of MBT entries that we have is limited. PHB3 and PHB4 only + * 16 total and some are needed for. Most SR-IOV capable network cards can support + * more than 16 VFs on each port. + * + * We use b) when using a) would use more than 1/4 of the entire 64 bit MMIO + * window of the PHB. + * + * + * + * PHB4 (IODA3) added a few new features that would be useful for SR-IOV. It + * allowed the MBT to map 32bit MMIO space in addition to 64bit which allows + * us to support SR-IOV BARs in the 32bit MMIO window. This is useful since + * the Linux BAR allocation will place any BAR marked as non-prefetchable into + * the non-prefetchable bridge window, which is 32bit only. It also added two + * new modes: + * + * c) A segmented BAR similar to a), but each segment can be individually + * mapped to any PE. This is matches how the 32bit MMIO window worked on + * IODA1&2. + * + * d) A segmented BAR with 8, 64, or 128 segments. This works similarly to a), + * but with fewer segments and configurable base PE. + * + * i.e. The n'th segment maps to the (n + base)'th PE. + * + * The base PE is also required to be a multiple of the window size. + * + * Unfortunately, the OPAL API doesn't currently (as of skiboot v6.6) allow us + * to exploit any of the IODA3 features. + */ static void pnv_pci_ioda_fixup_iov_resources(struct pci_dev *pdev) { -- GitLab From fac248f8119170e3f8f54900985498ff6ee560bf Mon Sep 17 00:00:00 2001 From: Oliver O'Halloran Date: Wed, 22 Jul 2020 16:57:06 +1000 Subject: [PATCH 0848/1476] powerpc/powernv/sriov: Rename truncate_iov This prevents SR-IOV being used by making the SR-IOV BAR resources unallocatable. Rename it to reflect what it actually does. Signed-off-by: Oliver O'Halloran Reviewed-by: Alexey Kardashevskiy Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20200722065715.1432738-7-oohall@gmail.com --- arch/powerpc/platforms/powernv/pci-sriov.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/arch/powerpc/platforms/powernv/pci-sriov.c b/arch/powerpc/platforms/powernv/pci-sriov.c index 8f5f0f50281e1..10ebdf05aeb8f 100644 --- a/arch/powerpc/platforms/powernv/pci-sriov.c +++ b/arch/powerpc/platforms/powernv/pci-sriov.c @@ -155,7 +155,7 @@ static void pnv_pci_ioda_fixup_iov_resources(struct pci_dev *pdev) iov = kzalloc(sizeof(*iov), GFP_KERNEL); if (!iov) - goto truncate_iov; + goto disable_iov; pdev->dev.archdata.iov_data = iov; total_vfs = pci_sriov_get_totalvfs(pdev); @@ -169,7 +169,7 @@ static void pnv_pci_ioda_fixup_iov_resources(struct pci_dev *pdev) if (!pnv_pci_is_m64_flags(res->flags)) { dev_warn(&pdev->dev, "Don't support SR-IOV with non M64 VF BAR%d: %pR. \n", i, res); - goto truncate_iov; + goto disable_iov; } total_vf_bar_sz += pci_iov_resource_size(pdev, @@ -208,7 +208,8 @@ static void pnv_pci_ioda_fixup_iov_resources(struct pci_dev *pdev) * mode is 32MB. */ if (iov->m64_single_mode && (size < SZ_32M)) - goto truncate_iov; + goto disable_iov; + dev_dbg(&pdev->dev, " Fixing VF BAR%d: %pR to\n", i, res); res->end = res->start + size * mul - 1; dev_dbg(&pdev->dev, " %pR\n", res); @@ -219,8 +220,8 @@ static void pnv_pci_ioda_fixup_iov_resources(struct pci_dev *pdev) return; -truncate_iov: - /* To save MMIO space, IOV BAR is truncated. */ +disable_iov: + /* Save ourselves some MMIO space by disabling the unusable BARs */ for (i = 0; i < PCI_SRIOV_NUM_BARS; i++) { res = &pdev->resource[i + PCI_IOV_RESOURCES]; res->flags = 0; -- GitLab From ad9add529d99d195195c27abf99e42d4965d35e2 Mon Sep 17 00:00:00 2001 From: Oliver O'Halloran Date: Wed, 22 Jul 2020 16:57:07 +1000 Subject: [PATCH 0849/1476] powerpc/powernv/sriov: Simplify used window tracking No need for the multi-dimensional arrays, just use a bitmap. Signed-off-by: Oliver O'Halloran Reviewed-by: Alexey Kardashevskiy Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20200722065715.1432738-8-oohall@gmail.com --- arch/powerpc/platforms/powernv/pci-sriov.c | 50 +++++++--------------- arch/powerpc/platforms/powernv/pci.h | 8 +++- 2 files changed, 22 insertions(+), 36 deletions(-) diff --git a/arch/powerpc/platforms/powernv/pci-sriov.c b/arch/powerpc/platforms/powernv/pci-sriov.c index 10ebdf05aeb8f..261df2307175d 100644 --- a/arch/powerpc/platforms/powernv/pci-sriov.c +++ b/arch/powerpc/platforms/powernv/pci-sriov.c @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0 +// SPDX-License-Identifier: GPL-2.0-or-later #include #include @@ -302,28 +302,20 @@ static int pnv_pci_vf_release_m64(struct pci_dev *pdev, u16 num_vfs) { struct pnv_iov_data *iov; struct pnv_phb *phb; - int i, j; - int m64_bars; + int window_id; phb = pci_bus_to_pnvhb(pdev->bus); iov = pnv_iov_get(pdev); - if (iov->m64_single_mode) - m64_bars = num_vfs; - else - m64_bars = 1; + for_each_set_bit(window_id, iov->used_m64_bar_mask, MAX_M64_BARS) { + opal_pci_phb_mmio_enable(phb->opal_id, + OPAL_M64_WINDOW_TYPE, + window_id, + 0); - for (i = 0; i < PCI_SRIOV_NUM_BARS; i++) - for (j = 0; j < m64_bars; j++) { - if (iov->m64_map[j][i] == IODA_INVALID_M64) - continue; - opal_pci_phb_mmio_enable(phb->opal_id, - OPAL_M64_WINDOW_TYPE, iov->m64_map[j][i], 0); - clear_bit(iov->m64_map[j][i], &phb->ioda.m64_bar_alloc); - iov->m64_map[j][i] = IODA_INVALID_M64; - } + clear_bit(window_id, &phb->ioda.m64_bar_alloc); + } - kfree(iov->m64_map); return 0; } @@ -349,23 +341,14 @@ static int pnv_pci_vf_assign_m64(struct pci_dev *pdev, u16 num_vfs) else m64_bars = 1; - iov->m64_map = kmalloc_array(m64_bars, - sizeof(*iov->m64_map), - GFP_KERNEL); - if (!iov->m64_map) - return -ENOMEM; - /* Initialize the m64_map to IODA_INVALID_M64 */ - for (i = 0; i < m64_bars ; i++) - for (j = 0; j < PCI_SRIOV_NUM_BARS; j++) - iov->m64_map[i][j] = IODA_INVALID_M64; - - for (i = 0; i < PCI_SRIOV_NUM_BARS; i++) { res = &pdev->resource[i + PCI_IOV_RESOURCES]; if (!res->flags || !res->parent) continue; for (j = 0; j < m64_bars; j++) { + + /* allocate a window ID for this BAR */ do { win = find_next_zero_bit(&phb->ioda.m64_bar_alloc, phb->ioda.m64_bar_idx + 1, 0); @@ -373,8 +356,7 @@ static int pnv_pci_vf_assign_m64(struct pci_dev *pdev, u16 num_vfs) if (win >= phb->ioda.m64_bar_idx + 1) goto m64_failed; } while (test_and_set_bit(win, &phb->ioda.m64_bar_alloc)); - - iov->m64_map[j][i] = win; + set_bit(win, iov->used_m64_bar_mask); if (iov->m64_single_mode) { size = pci_iov_resource_size(pdev, @@ -390,12 +372,12 @@ static int pnv_pci_vf_assign_m64(struct pci_dev *pdev, u16 num_vfs) pe_num = iov->pe_num_map[j]; rc = opal_pci_map_pe_mmio_window(phb->opal_id, pe_num, OPAL_M64_WINDOW_TYPE, - iov->m64_map[j][i], 0); + win, 0); } rc = opal_pci_set_phb_mem_window(phb->opal_id, OPAL_M64_WINDOW_TYPE, - iov->m64_map[j][i], + win, start, 0, /* unused */ size); @@ -409,10 +391,10 @@ static int pnv_pci_vf_assign_m64(struct pci_dev *pdev, u16 num_vfs) if (iov->m64_single_mode) rc = opal_pci_phb_mmio_enable(phb->opal_id, - OPAL_M64_WINDOW_TYPE, iov->m64_map[j][i], 2); + OPAL_M64_WINDOW_TYPE, win, 2); else rc = opal_pci_phb_mmio_enable(phb->opal_id, - OPAL_M64_WINDOW_TYPE, iov->m64_map[j][i], 1); + OPAL_M64_WINDOW_TYPE, win, 1); if (rc != OPAL_SUCCESS) { dev_err(&pdev->dev, "Failed to enable M64 window #%d: %llx\n", diff --git a/arch/powerpc/platforms/powernv/pci.h b/arch/powerpc/platforms/powernv/pci.h index 0156d7d17f7d2..23fc5e391c7fe 100644 --- a/arch/powerpc/platforms/powernv/pci.h +++ b/arch/powerpc/platforms/powernv/pci.h @@ -154,6 +154,7 @@ struct pnv_phb { unsigned long m64_size; unsigned long m64_segsize; unsigned long m64_base; +#define MAX_M64_BARS 64 unsigned long m64_bar_alloc; /* IO ports */ @@ -243,8 +244,11 @@ struct pnv_iov_data { /* Did we map the VF BARs with single-PE IODA BARs? */ bool m64_single_mode; - int (*m64_map)[PCI_SRIOV_NUM_BARS]; -#define IODA_INVALID_M64 (-1) + /* + * Bit mask used to track which m64 windows are used to map the + * SR-IOV BARs for this device. + */ + DECLARE_BITMAP(used_m64_bar_mask, MAX_M64_BARS); /* * If we map the SR-IOV BARs with a segmented window then -- GitLab From a610d35cc8780e781321ea8d002d5fef8484bf59 Mon Sep 17 00:00:00 2001 From: Oliver O'Halloran Date: Wed, 22 Jul 2020 16:57:08 +1000 Subject: [PATCH 0850/1476] powerpc/powernv/sriov: Factor out M64 BAR setup The sequence required to use the single PE BAR mode is kinda janky and requires a little explanation. The API was designed with P7-IOC style windows where the setup process is something like: 1. Configure the window start / end address 2. Enable the window 3. Map the segments of each window to the PE For Single PE BARs the process is: 1. Set the PE for segment zero on a disabled window 2. Set the range 3. Enable the window Move the OPAL calls into their own helper functions where the quirks can be contained. Signed-off-by: Oliver O'Halloran Reviewed-by: Alexey Kardashevskiy Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20200722065715.1432738-9-oohall@gmail.com --- arch/powerpc/platforms/powernv/pci-sriov.c | 129 ++++++++++++++++----- 1 file changed, 100 insertions(+), 29 deletions(-) diff --git a/arch/powerpc/platforms/powernv/pci-sriov.c b/arch/powerpc/platforms/powernv/pci-sriov.c index 261df2307175d..5d2f5d5ff1836 100644 --- a/arch/powerpc/platforms/powernv/pci-sriov.c +++ b/arch/powerpc/platforms/powernv/pci-sriov.c @@ -319,6 +319,99 @@ static int pnv_pci_vf_release_m64(struct pci_dev *pdev, u16 num_vfs) return 0; } + +/* + * PHB3 and beyond support segmented windows. The window's address range + * is subdivided into phb->ioda.total_pe_num segments and there's a 1-1 + * mapping between PEs and segments. + */ +static int64_t pnv_ioda_map_m64_segmented(struct pnv_phb *phb, + int window_id, + resource_size_t start, + resource_size_t size) +{ + int64_t rc; + + rc = opal_pci_set_phb_mem_window(phb->opal_id, + OPAL_M64_WINDOW_TYPE, + window_id, + start, + 0, /* unused */ + size); + if (rc) + goto out; + + rc = opal_pci_phb_mmio_enable(phb->opal_id, + OPAL_M64_WINDOW_TYPE, + window_id, + OPAL_ENABLE_M64_SPLIT); +out: + if (rc) + pr_err("Failed to map M64 window #%d: %lld\n", window_id, rc); + + return rc; +} + +static int64_t pnv_ioda_map_m64_single(struct pnv_phb *phb, + int pe_num, + int window_id, + resource_size_t start, + resource_size_t size) +{ + int64_t rc; + + /* + * The API for setting up m64 mmio windows seems to have been designed + * with P7-IOC in mind. For that chip each M64 BAR (window) had a fixed + * split of 8 equally sized segments each of which could individually + * assigned to a PE. + * + * The problem with this is that the API doesn't have any way to + * communicate the number of segments we want on a BAR. This wasn't + * a problem for p7-ioc since you didn't have a choice, but the + * single PE windows added in PHB3 don't map cleanly to this API. + * + * As a result we've got this slightly awkward process where we + * call opal_pci_map_pe_mmio_window() to put the single in single + * PE mode, and set the PE for the window before setting the address + * bounds. We need to do it this way because the single PE windows + * for PHB3 have different alignment requirements on PHB3. + */ + rc = opal_pci_map_pe_mmio_window(phb->opal_id, + pe_num, + OPAL_M64_WINDOW_TYPE, + window_id, + 0); + if (rc) + goto out; + + /* + * NB: In single PE mode the window needs to be aligned to 32MB + */ + rc = opal_pci_set_phb_mem_window(phb->opal_id, + OPAL_M64_WINDOW_TYPE, + window_id, + start, + 0, /* ignored by FW, m64 is 1-1 */ + size); + if (rc) + goto out; + + /* + * Now actually enable it. We specified the BAR should be in "non-split" + * mode so FW will validate that the BAR is in single PE mode. + */ + rc = opal_pci_phb_mmio_enable(phb->opal_id, + OPAL_M64_WINDOW_TYPE, + window_id, + OPAL_ENABLE_M64_NON_SPLIT); +out: + if (rc) + pr_err("Error mapping single PE BAR\n"); + + return rc; +} + static int pnv_pci_vf_assign_m64(struct pci_dev *pdev, u16 num_vfs) { struct pnv_iov_data *iov; @@ -329,7 +422,6 @@ static int pnv_pci_vf_assign_m64(struct pci_dev *pdev, u16 num_vfs) int64_t rc; int total_vfs; resource_size_t size, start; - int pe_num; int m64_bars; phb = pci_bus_to_pnvhb(pdev->bus); @@ -358,49 +450,28 @@ static int pnv_pci_vf_assign_m64(struct pci_dev *pdev, u16 num_vfs) } while (test_and_set_bit(win, &phb->ioda.m64_bar_alloc)); set_bit(win, iov->used_m64_bar_mask); + if (iov->m64_single_mode) { size = pci_iov_resource_size(pdev, PCI_IOV_RESOURCES + i); start = res->start + size * j; + rc = pnv_ioda_map_m64_single(phb, win, + iov->pe_num_map[j], + start, + size); } else { size = resource_size(res); start = res->start; - } - /* Map the M64 here */ - if (iov->m64_single_mode) { - pe_num = iov->pe_num_map[j]; - rc = opal_pci_map_pe_mmio_window(phb->opal_id, - pe_num, OPAL_M64_WINDOW_TYPE, - win, 0); + rc = pnv_ioda_map_m64_segmented(phb, win, start, + size); } - rc = opal_pci_set_phb_mem_window(phb->opal_id, - OPAL_M64_WINDOW_TYPE, - win, - start, - 0, /* unused */ - size); - - if (rc != OPAL_SUCCESS) { dev_err(&pdev->dev, "Failed to map M64 window #%d: %lld\n", win, rc); goto m64_failed; } - - if (iov->m64_single_mode) - rc = opal_pci_phb_mmio_enable(phb->opal_id, - OPAL_M64_WINDOW_TYPE, win, 2); - else - rc = opal_pci_phb_mmio_enable(phb->opal_id, - OPAL_M64_WINDOW_TYPE, win, 1); - - if (rc != OPAL_SUCCESS) { - dev_err(&pdev->dev, "Failed to enable M64 window #%d: %llx\n", - win, rc); - goto m64_failed; - } } } return 0; -- GitLab From a4bc676ed5c3f53781cc342b73097eb7e8d43fa5 Mon Sep 17 00:00:00 2001 From: Oliver O'Halloran Date: Wed, 22 Jul 2020 16:57:09 +1000 Subject: [PATCH 0851/1476] powerpc/powernv/pci: Refactor pnv_ioda_alloc_pe() Rework the PE allocation logic to allow allocating blocks of PEs rather than individually. We'll use this to allocate contigious blocks of PEs for the SR-IOVs. This patch also adds code to pnv_ioda_alloc_pe() and pnv_ioda_reserve_pe() to use the existing, but unused, phb->pe_alloc_mutex. Currently these functions use atomic bit ops to release a currently allocated PE number. However, the pnv_ioda_alloc_pe() wants to have exclusive access to the bit map while scanning for hole large enough to accomodate the allocation size. Signed-off-by: Oliver O'Halloran Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20200722065715.1432738-10-oohall@gmail.com --- arch/powerpc/platforms/powernv/pci-ioda.c | 41 ++++++++++++++++++----- arch/powerpc/platforms/powernv/pci.h | 2 +- 2 files changed, 34 insertions(+), 9 deletions(-) diff --git a/arch/powerpc/platforms/powernv/pci-ioda.c b/arch/powerpc/platforms/powernv/pci-ioda.c index 2d36a9ebf0e91..c9c25fb0783c4 100644 --- a/arch/powerpc/platforms/powernv/pci-ioda.c +++ b/arch/powerpc/platforms/powernv/pci-ioda.c @@ -145,23 +145,45 @@ static void pnv_ioda_reserve_pe(struct pnv_phb *phb, int pe_no) return; } + mutex_lock(&phb->ioda.pe_alloc_mutex); if (test_and_set_bit(pe_no, phb->ioda.pe_alloc)) pr_debug("%s: PE %x was reserved on PHB#%x\n", __func__, pe_no, phb->hose->global_number); + mutex_unlock(&phb->ioda.pe_alloc_mutex); pnv_ioda_init_pe(phb, pe_no); } -struct pnv_ioda_pe *pnv_ioda_alloc_pe(struct pnv_phb *phb) +struct pnv_ioda_pe *pnv_ioda_alloc_pe(struct pnv_phb *phb, int count) { - long pe; + struct pnv_ioda_pe *ret = NULL; + int run = 0, pe, i; + mutex_lock(&phb->ioda.pe_alloc_mutex); + + /* scan backwards for a run of @count cleared bits */ for (pe = phb->ioda.total_pe_num - 1; pe >= 0; pe--) { - if (!test_and_set_bit(pe, phb->ioda.pe_alloc)) - return pnv_ioda_init_pe(phb, pe); + if (test_bit(pe, phb->ioda.pe_alloc)) { + run = 0; + continue; + } + + run++; + if (run == count) + break; } + if (run != count) + goto out; - return NULL; + for (i = pe; i < pe + count; i++) { + set_bit(i, phb->ioda.pe_alloc); + pnv_ioda_init_pe(phb, i); + } + ret = &phb->ioda.pe_array[pe]; + +out: + mutex_unlock(&phb->ioda.pe_alloc_mutex); + return ret; } void pnv_ioda_free_pe(struct pnv_ioda_pe *pe) @@ -173,7 +195,10 @@ void pnv_ioda_free_pe(struct pnv_ioda_pe *pe) WARN_ON(pe->npucomp); /* NPUs for nvlink are not supposed to be freed */ kfree(pe->npucomp); memset(pe, 0, sizeof(struct pnv_ioda_pe)); + + mutex_lock(&phb->ioda.pe_alloc_mutex); clear_bit(pe_num, phb->ioda.pe_alloc); + mutex_unlock(&phb->ioda.pe_alloc_mutex); } /* The default M64 BAR is shared by all PEs */ @@ -976,7 +1001,7 @@ static struct pnv_ioda_pe *pnv_ioda_setup_dev_PE(struct pci_dev *dev) if (pdn->pe_number != IODA_INVALID_PE) return NULL; - pe = pnv_ioda_alloc_pe(phb); + pe = pnv_ioda_alloc_pe(phb, 1); if (!pe) { pr_warn("%s: Not enough PE# available, disabling device\n", pci_name(dev)); @@ -1047,7 +1072,7 @@ static struct pnv_ioda_pe *pnv_ioda_setup_bus_PE(struct pci_bus *bus, bool all) /* The PE number isn't pinned by M64 */ if (!pe) - pe = pnv_ioda_alloc_pe(phb); + pe = pnv_ioda_alloc_pe(phb, 1); if (!pe) { pr_warn("%s: Not enough PE# available for PCI bus %04x:%02x\n", @@ -3065,7 +3090,7 @@ static void __init pnv_pci_init_ioda_phb(struct device_node *np, pnv_ioda_reserve_pe(phb, phb->ioda.root_pe_idx); } else { /* otherwise just allocate one */ - root_pe = pnv_ioda_alloc_pe(phb); + root_pe = pnv_ioda_alloc_pe(phb, 1); phb->ioda.root_pe_idx = root_pe->pe_number; } diff --git a/arch/powerpc/platforms/powernv/pci.h b/arch/powerpc/platforms/powernv/pci.h index 23fc5e391c7fe..06431a4521301 100644 --- a/arch/powerpc/platforms/powernv/pci.h +++ b/arch/powerpc/platforms/powernv/pci.h @@ -224,7 +224,7 @@ int pnv_ioda_deconfigure_pe(struct pnv_phb *phb, struct pnv_ioda_pe *pe); void pnv_pci_ioda2_setup_dma_pe(struct pnv_phb *phb, struct pnv_ioda_pe *pe); void pnv_pci_ioda2_release_pe_dma(struct pnv_ioda_pe *pe); -struct pnv_ioda_pe *pnv_ioda_alloc_pe(struct pnv_phb *phb); +struct pnv_ioda_pe *pnv_ioda_alloc_pe(struct pnv_phb *phb, int count); void pnv_ioda_free_pe(struct pnv_ioda_pe *pe); #ifdef CONFIG_PCI_IOV -- GitLab From d29a2488d2c020032fdb1fe052347a6021e3591d Mon Sep 17 00:00:00 2001 From: Oliver O'Halloran Date: Wed, 22 Jul 2020 16:57:10 +1000 Subject: [PATCH 0852/1476] powerpc/powernv/sriov: Drop iov->pe_num_map[] Currently the iov->pe_num_map[] does one of two things depending on whether single PE mode is being used or not. When it is, this contains an array which maps a vf_index to the corresponding PE number. When single PE mode is not being used this contains a scalar which is the base PE for the set of enabled VFs (for for VFn is base + n). The array was necessary because when calling pnv_ioda_alloc_pe() there is no guarantee that the allocated PEs would be contigious. We can now allocate contigious blocks of PEs so this is no longer an issue. This allows us to drop the if (single_mode) {} .. else {} block scattered through the SR-IOV code which is a nice clean up. This also fixes a bug in pnv_pci_sriov_disable() which is the non-atomic bitmap_clear() to manipulate the PE allocation map. Other users of the map assume it will be accessed with atomic ops. Signed-off-by: Oliver O'Halloran Reviewed-by: Alexey Kardashevskiy Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20200722065715.1432738-11-oohall@gmail.com --- arch/powerpc/platforms/powernv/pci-sriov.c | 109 +++++---------------- arch/powerpc/platforms/powernv/pci.h | 7 +- 2 files changed, 28 insertions(+), 88 deletions(-) diff --git a/arch/powerpc/platforms/powernv/pci-sriov.c b/arch/powerpc/platforms/powernv/pci-sriov.c index 5d2f5d5ff1836..4c02a1d90afe5 100644 --- a/arch/powerpc/platforms/powernv/pci-sriov.c +++ b/arch/powerpc/platforms/powernv/pci-sriov.c @@ -452,11 +452,13 @@ static int pnv_pci_vf_assign_m64(struct pci_dev *pdev, u16 num_vfs) if (iov->m64_single_mode) { + int pe_num = iov->vf_pe_arr[j].pe_number; + size = pci_iov_resource_size(pdev, PCI_IOV_RESOURCES + i); start = res->start + size * j; rc = pnv_ioda_map_m64_single(phb, win, - iov->pe_num_map[j], + pe_num, start, size); } else { @@ -595,38 +597,24 @@ static int pnv_pci_vf_resource_shift(struct pci_dev *dev, int offset) static void pnv_pci_sriov_disable(struct pci_dev *pdev) { + u16 num_vfs, base_pe; struct pnv_phb *phb; - struct pnv_ioda_pe *pe; struct pnv_iov_data *iov; - u16 num_vfs, i; phb = pci_bus_to_pnvhb(pdev->bus); iov = pnv_iov_get(pdev); num_vfs = iov->num_vfs; + base_pe = iov->vf_pe_arr[0].pe_number; /* Release VF PEs */ pnv_ioda_release_vf_PE(pdev); if (phb->type == PNV_PHB_IODA2) { if (!iov->m64_single_mode) - pnv_pci_vf_resource_shift(pdev, -*iov->pe_num_map); + pnv_pci_vf_resource_shift(pdev, -base_pe); /* Release M64 windows */ pnv_pci_vf_release_m64(pdev, num_vfs); - - /* Release PE numbers */ - if (iov->m64_single_mode) { - for (i = 0; i < num_vfs; i++) { - if (iov->pe_num_map[i] == IODA_INVALID_PE) - continue; - - pe = &phb->ioda.pe_array[iov->pe_num_map[i]]; - pnv_ioda_free_pe(pe); - } - } else - bitmap_clear(phb->ioda.pe_alloc, *iov->pe_num_map, num_vfs); - /* Releasing pe_num_map */ - kfree(iov->pe_num_map); } } @@ -652,13 +640,7 @@ static void pnv_ioda_setup_vf_PE(struct pci_dev *pdev, u16 num_vfs) int vf_bus = pci_iov_virtfn_bus(pdev, vf_index); struct pci_dn *vf_pdn; - if (iov->m64_single_mode) - pe_num = iov->pe_num_map[vf_index]; - else - pe_num = *iov->pe_num_map + vf_index; - - pe = &phb->ioda.pe_array[pe_num]; - pe->pe_number = pe_num; + pe = &iov->vf_pe_arr[vf_index]; pe->phb = phb; pe->flags = PNV_IODA_PE_VF; pe->pbus = NULL; @@ -666,6 +648,7 @@ static void pnv_ioda_setup_vf_PE(struct pci_dev *pdev, u16 num_vfs) pe->mve_number = -1; pe->rid = (vf_bus << 8) | vf_devfn; + pe_num = pe->pe_number; pe_info(pe, "VF %04d:%02d:%02d.%d associated with PE#%x\n", pci_domain_nr(pdev->bus), pdev->bus->number, PCI_SLOT(vf_devfn), PCI_FUNC(vf_devfn), pe_num); @@ -697,9 +680,9 @@ static void pnv_ioda_setup_vf_PE(struct pci_dev *pdev, u16 num_vfs) static int pnv_pci_sriov_enable(struct pci_dev *pdev, u16 num_vfs) { + struct pnv_ioda_pe *base_pe; struct pnv_iov_data *iov; struct pnv_phb *phb; - struct pnv_ioda_pe *pe; int ret; u16 i; @@ -713,55 +696,14 @@ static int pnv_pci_sriov_enable(struct pci_dev *pdev, u16 num_vfs) return -ENOSPC; } - /* - * When M64 BARs functions in Single PE mode, the number of VFs - * could be enabled must be less than the number of M64 BARs. - */ - if (iov->m64_single_mode && num_vfs > phb->ioda.m64_bar_idx) { - dev_info(&pdev->dev, "Not enough M64 BAR for VFs\n"); + /* allocate a contigious block of PEs for our VFs */ + base_pe = pnv_ioda_alloc_pe(phb, num_vfs); + if (!base_pe) { + pci_err(pdev, "Unable to allocate PEs for %d VFs\n", num_vfs); return -EBUSY; } - /* Allocating pe_num_map */ - if (iov->m64_single_mode) - iov->pe_num_map = kmalloc_array(num_vfs, - sizeof(*iov->pe_num_map), - GFP_KERNEL); - else - iov->pe_num_map = kmalloc(sizeof(*iov->pe_num_map), GFP_KERNEL); - - if (!iov->pe_num_map) - return -ENOMEM; - - if (iov->m64_single_mode) - for (i = 0; i < num_vfs; i++) - iov->pe_num_map[i] = IODA_INVALID_PE; - - /* Calculate available PE for required VFs */ - if (iov->m64_single_mode) { - for (i = 0; i < num_vfs; i++) { - pe = pnv_ioda_alloc_pe(phb); - if (!pe) { - ret = -EBUSY; - goto m64_failed; - } - - iov->pe_num_map[i] = pe->pe_number; - } - } else { - mutex_lock(&phb->ioda.pe_alloc_mutex); - *iov->pe_num_map = bitmap_find_next_zero_area( - phb->ioda.pe_alloc, phb->ioda.total_pe_num, - 0, num_vfs, 0); - if (*iov->pe_num_map >= phb->ioda.total_pe_num) { - mutex_unlock(&phb->ioda.pe_alloc_mutex); - dev_info(&pdev->dev, "Failed to enable VF%d\n", num_vfs); - kfree(iov->pe_num_map); - return -EBUSY; - } - bitmap_set(phb->ioda.pe_alloc, *iov->pe_num_map, num_vfs); - mutex_unlock(&phb->ioda.pe_alloc_mutex); - } + iov->vf_pe_arr = base_pe; iov->num_vfs = num_vfs; /* Assign M64 window accordingly */ @@ -777,9 +719,10 @@ static int pnv_pci_sriov_enable(struct pci_dev *pdev, u16 num_vfs) * Otherwise, the PE# for the VF will conflict with others. */ if (!iov->m64_single_mode) { - ret = pnv_pci_vf_resource_shift(pdev, *iov->pe_num_map); + ret = pnv_pci_vf_resource_shift(pdev, + base_pe->pe_number); if (ret) - goto m64_failed; + goto shift_failed; } } @@ -788,20 +731,12 @@ static int pnv_pci_sriov_enable(struct pci_dev *pdev, u16 num_vfs) return 0; -m64_failed: - if (iov->m64_single_mode) { - for (i = 0; i < num_vfs; i++) { - if (iov->pe_num_map[i] == IODA_INVALID_PE) - continue; - - pe = &phb->ioda.pe_array[iov->pe_num_map[i]]; - pnv_ioda_free_pe(pe); - } - } else - bitmap_clear(phb->ioda.pe_alloc, *iov->pe_num_map, num_vfs); +shift_failed: + pnv_pci_vf_release_m64(pdev, num_vfs); - /* Releasing pe_num_map */ - kfree(iov->pe_num_map); +m64_failed: + for (i = 0; i < num_vfs; i++) + pnv_ioda_free_pe(&iov->vf_pe_arr[i]); return ret; } diff --git a/arch/powerpc/platforms/powernv/pci.h b/arch/powerpc/platforms/powernv/pci.h index 06431a4521301..f76923f44f665 100644 --- a/arch/powerpc/platforms/powernv/pci.h +++ b/arch/powerpc/platforms/powernv/pci.h @@ -239,7 +239,12 @@ struct pnv_iov_data { /* number of VFs enabled */ u16 num_vfs; - unsigned int *pe_num_map; /* PE# for the first VF PE or array */ + + /* + * Pointer to the IODA PE state of each VF. Note that this is a pointer + * into the PHB's PE array (phb->ioda.pe_array). + */ + struct pnv_ioda_pe *vf_pe_arr; /* Did we map the VF BARs with single-PE IODA BARs? */ bool m64_single_mode; -- GitLab From 052da31d45fc71238ea8bed7e9a84648a1ee0bf3 Mon Sep 17 00:00:00 2001 From: Oliver O'Halloran Date: Wed, 22 Jul 2020 16:57:11 +1000 Subject: [PATCH 0853/1476] powerpc/powernv/sriov: De-indent setup and teardown Remove the IODA2 PHB checks. We already assume IODA2 in several places so there's not much point in wrapping most of the setup and teardown process in an if block. Signed-off-by: Oliver O'Halloran Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20200722065715.1432738-12-oohall@gmail.com --- arch/powerpc/platforms/powernv/pci-sriov.c | 85 ++++++++++++---------- arch/powerpc/platforms/powernv/pci.h | 5 +- 2 files changed, 49 insertions(+), 41 deletions(-) diff --git a/arch/powerpc/platforms/powernv/pci-sriov.c b/arch/powerpc/platforms/powernv/pci-sriov.c index 4c02a1d90afe5..4bd617b19b6d9 100644 --- a/arch/powerpc/platforms/powernv/pci-sriov.c +++ b/arch/powerpc/platforms/powernv/pci-sriov.c @@ -606,16 +606,18 @@ static void pnv_pci_sriov_disable(struct pci_dev *pdev) num_vfs = iov->num_vfs; base_pe = iov->vf_pe_arr[0].pe_number; + if (WARN_ON(!iov)) + return; + /* Release VF PEs */ pnv_ioda_release_vf_PE(pdev); - if (phb->type == PNV_PHB_IODA2) { - if (!iov->m64_single_mode) - pnv_pci_vf_resource_shift(pdev, -base_pe); + /* Un-shift the IOV BAR resources */ + if (!iov->m64_single_mode) + pnv_pci_vf_resource_shift(pdev, -base_pe); - /* Release M64 windows */ - pnv_pci_vf_release_m64(pdev, num_vfs); - } + /* Release M64 windows */ + pnv_pci_vf_release_m64(pdev, num_vfs); } static void pnv_ioda_setup_vf_PE(struct pci_dev *pdev, u16 num_vfs) @@ -689,41 +691,50 @@ static int pnv_pci_sriov_enable(struct pci_dev *pdev, u16 num_vfs) phb = pci_bus_to_pnvhb(pdev->bus); iov = pnv_iov_get(pdev); - if (phb->type == PNV_PHB_IODA2) { - if (!iov->vfs_expanded) { - dev_info(&pdev->dev, - "don't support this SRIOV device with non 64bit-prefetchable IOV BAR\n"); - return -ENOSPC; - } + /* + * There's a calls to IODA2 PE setup code littered throughout. We could + * probably fix that, but we'd still have problems due to the + * restriction inherent on IODA1 PHBs. + * + * NB: We class IODA3 as IODA2 since they're very similar. + */ + if (phb->type != PNV_PHB_IODA2) { + pci_err(pdev, "SR-IOV is not supported on this PHB\n"); + return -ENXIO; + } - /* allocate a contigious block of PEs for our VFs */ - base_pe = pnv_ioda_alloc_pe(phb, num_vfs); - if (!base_pe) { - pci_err(pdev, "Unable to allocate PEs for %d VFs\n", num_vfs); - return -EBUSY; - } + if (!iov->vfs_expanded) { + dev_info(&pdev->dev, "don't support this SRIOV device with non 64bit-prefetchable IOV BAR\n"); + return -ENOSPC; + } - iov->vf_pe_arr = base_pe; - iov->num_vfs = num_vfs; + /* allocate a contigious block of PEs for our VFs */ + base_pe = pnv_ioda_alloc_pe(phb, num_vfs); + if (!base_pe) { + pci_err(pdev, "Unable to allocate PEs for %d VFs\n", num_vfs); + return -EBUSY; + } - /* Assign M64 window accordingly */ - ret = pnv_pci_vf_assign_m64(pdev, num_vfs); - if (ret) { - dev_info(&pdev->dev, "Not enough M64 window resources\n"); - goto m64_failed; - } + iov->vf_pe_arr = base_pe; + iov->num_vfs = num_vfs; - /* - * When using one M64 BAR to map one IOV BAR, we need to shift - * the IOV BAR according to the PE# allocated to the VFs. - * Otherwise, the PE# for the VF will conflict with others. - */ - if (!iov->m64_single_mode) { - ret = pnv_pci_vf_resource_shift(pdev, - base_pe->pe_number); - if (ret) - goto shift_failed; - } + /* Assign M64 window accordingly */ + ret = pnv_pci_vf_assign_m64(pdev, num_vfs); + if (ret) { + dev_info(&pdev->dev, "Not enough M64 window resources\n"); + goto m64_failed; + } + + /* + * When using one M64 BAR to map one IOV BAR, we need to shift + * the IOV BAR according to the PE# allocated to the VFs. + * Otherwise, the PE# for the VF will conflict with others. + */ + if (!iov->m64_single_mode) { + ret = pnv_pci_vf_resource_shift(pdev, + base_pe->pe_number); + if (ret) + goto shift_failed; } /* Setup VF PEs */ diff --git a/arch/powerpc/platforms/powernv/pci.h b/arch/powerpc/platforms/powernv/pci.h index f76923f44f665..41a6f4e938e4e 100644 --- a/arch/powerpc/platforms/powernv/pci.h +++ b/arch/powerpc/platforms/powernv/pci.h @@ -240,10 +240,7 @@ struct pnv_iov_data { /* number of VFs enabled */ u16 num_vfs; - /* - * Pointer to the IODA PE state of each VF. Note that this is a pointer - * into the PHB's PE array (phb->ioda.pe_array). - */ + /* pointer to the array of VF PEs. num_vfs long*/ struct pnv_ioda_pe *vf_pe_arr; /* Did we map the VF BARs with single-PE IODA BARs? */ -- GitLab From 39efc03e3ee8f41909b7542be70b4061b38ca277 Mon Sep 17 00:00:00 2001 From: Oliver O'Halloran Date: Wed, 22 Jul 2020 16:57:12 +1000 Subject: [PATCH 0854/1476] powerpc/powernv/sriov: Move M64 BAR allocation into a helper I want to refactor the loop this code is currently inside of. Hoist it on out. Signed-off-by: Oliver O'Halloran Reviewed-by: Alexey Kardashevskiy Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20200722065715.1432738-13-oohall@gmail.com --- arch/powerpc/platforms/powernv/pci-sriov.c | 31 ++++++++++++++-------- 1 file changed, 20 insertions(+), 11 deletions(-) diff --git a/arch/powerpc/platforms/powernv/pci-sriov.c b/arch/powerpc/platforms/powernv/pci-sriov.c index 4bd617b19b6d9..8eee59c210c89 100644 --- a/arch/powerpc/platforms/powernv/pci-sriov.c +++ b/arch/powerpc/platforms/powernv/pci-sriov.c @@ -412,6 +412,23 @@ out: return rc; } +static int pnv_pci_alloc_m64_bar(struct pnv_phb *phb, struct pnv_iov_data *iov) +{ + int win; + + do { + win = find_next_zero_bit(&phb->ioda.m64_bar_alloc, + phb->ioda.m64_bar_idx + 1, 0); + + if (win >= phb->ioda.m64_bar_idx + 1) + return -1; + } while (test_and_set_bit(win, &phb->ioda.m64_bar_alloc)); + + set_bit(win, iov->used_m64_bar_mask); + + return win; +} + static int pnv_pci_vf_assign_m64(struct pci_dev *pdev, u16 num_vfs) { struct pnv_iov_data *iov; @@ -439,17 +456,9 @@ static int pnv_pci_vf_assign_m64(struct pci_dev *pdev, u16 num_vfs) continue; for (j = 0; j < m64_bars; j++) { - - /* allocate a window ID for this BAR */ - do { - win = find_next_zero_bit(&phb->ioda.m64_bar_alloc, - phb->ioda.m64_bar_idx + 1, 0); - - if (win >= phb->ioda.m64_bar_idx + 1) - goto m64_failed; - } while (test_and_set_bit(win, &phb->ioda.m64_bar_alloc)); - set_bit(win, iov->used_m64_bar_mask); - + win = pnv_pci_alloc_m64_bar(phb, iov); + if (win < 0) + goto m64_failed; if (iov->m64_single_mode) { int pe_num = iov->vf_pe_arr[j].pe_number; -- GitLab From a0be516f8160fdb4836237cba037229e88a1de7d Mon Sep 17 00:00:00 2001 From: Oliver O'Halloran Date: Wed, 22 Jul 2020 16:57:13 +1000 Subject: [PATCH 0855/1476] powerpc/powernv/sriov: Refactor M64 BAR setup Split up the logic so that we have one branch that handles setting up a segmented window and another that handles setting up single PE windows for each VF. Signed-off-by: Oliver O'Halloran Reviewed-by: Alexey Kardashevskiy Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20200722065715.1432738-14-oohall@gmail.com --- arch/powerpc/platforms/powernv/pci-sriov.c | 57 ++++++++++------------ 1 file changed, 27 insertions(+), 30 deletions(-) diff --git a/arch/powerpc/platforms/powernv/pci-sriov.c b/arch/powerpc/platforms/powernv/pci-sriov.c index 8eee59c210c89..31adc36c6bf17 100644 --- a/arch/powerpc/platforms/powernv/pci-sriov.c +++ b/arch/powerpc/platforms/powernv/pci-sriov.c @@ -437,52 +437,49 @@ static int pnv_pci_vf_assign_m64(struct pci_dev *pdev, u16 num_vfs) struct resource *res; int i, j; int64_t rc; - int total_vfs; resource_size_t size, start; - int m64_bars; + int base_pe_num; phb = pci_bus_to_pnvhb(pdev->bus); iov = pnv_iov_get(pdev); - total_vfs = pci_sriov_get_totalvfs(pdev); - - if (iov->m64_single_mode) - m64_bars = num_vfs; - else - m64_bars = 1; for (i = 0; i < PCI_SRIOV_NUM_BARS; i++) { res = &pdev->resource[i + PCI_IOV_RESOURCES]; if (!res->flags || !res->parent) continue; - for (j = 0; j < m64_bars; j++) { + /* don't need single mode? map everything in one go! */ + if (!iov->m64_single_mode) { win = pnv_pci_alloc_m64_bar(phb, iov); if (win < 0) goto m64_failed; - if (iov->m64_single_mode) { - int pe_num = iov->vf_pe_arr[j].pe_number; - - size = pci_iov_resource_size(pdev, - PCI_IOV_RESOURCES + i); - start = res->start + size * j; - rc = pnv_ioda_map_m64_single(phb, win, - pe_num, - start, - size); - } else { - size = resource_size(res); - start = res->start; - - rc = pnv_ioda_map_m64_segmented(phb, win, start, - size); - } + size = resource_size(res); + start = res->start; - if (rc != OPAL_SUCCESS) { - dev_err(&pdev->dev, "Failed to map M64 window #%d: %lld\n", - win, rc); + rc = pnv_ioda_map_m64_segmented(phb, win, start, size); + if (rc) + goto m64_failed; + + continue; + } + + /* otherwise map each VF with single PE BARs */ + size = pci_iov_resource_size(pdev, PCI_IOV_RESOURCES + i); + base_pe_num = iov->vf_pe_arr[0].pe_number; + + for (j = 0; j < num_vfs; j++) { + win = pnv_pci_alloc_m64_bar(phb, iov); + if (win < 0) + goto m64_failed; + + start = res->start + size * j; + rc = pnv_ioda_map_m64_single(phb, win, + base_pe_num + j, + start, + size); + if (rc) goto m64_failed; - } } } return 0; -- GitLab From 4c51f3e1e8702cbd0e53159fc3d1f54c20c70574 Mon Sep 17 00:00:00 2001 From: Oliver O'Halloran Date: Wed, 22 Jul 2020 16:57:14 +1000 Subject: [PATCH 0856/1476] powerpc/powernv/sriov: Make single PE mode a per-BAR setting Using single PE BARs to map an SR-IOV BAR is really a choice about what strategy to use when mapping a BAR. It doesn't make much sense for this to be a global setting since a device might have one large BAR which needs to be mapped with single PE windows and another smaller BAR that can be mapped with a regular segmented window. Make the segmented vs single decision a per-BAR setting and clean up the logic that decides which mode to use. Signed-off-by: Oliver O'Halloran Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20200722065715.1432738-15-oohall@gmail.com --- arch/powerpc/platforms/powernv/pci-sriov.c | 131 ++++++++++----------- arch/powerpc/platforms/powernv/pci.h | 11 +- 2 files changed, 73 insertions(+), 69 deletions(-) diff --git a/arch/powerpc/platforms/powernv/pci-sriov.c b/arch/powerpc/platforms/powernv/pci-sriov.c index 31adc36c6bf17..d73684869c497 100644 --- a/arch/powerpc/platforms/powernv/pci-sriov.c +++ b/arch/powerpc/platforms/powernv/pci-sriov.c @@ -146,21 +146,17 @@ static void pnv_pci_ioda_fixup_iov_resources(struct pci_dev *pdev) { struct pnv_phb *phb = pci_bus_to_pnvhb(pdev->bus); - const resource_size_t gate = phb->ioda.m64_segsize >> 2; struct resource *res; int i; - resource_size_t size, total_vf_bar_sz; + resource_size_t vf_bar_sz; struct pnv_iov_data *iov; - int mul, total_vfs; + int mul; iov = kzalloc(sizeof(*iov), GFP_KERNEL); if (!iov) goto disable_iov; pdev->dev.archdata.iov_data = iov; - - total_vfs = pci_sriov_get_totalvfs(pdev); mul = phb->ioda.total_pe_num; - total_vf_bar_sz = 0; for (i = 0; i < PCI_SRIOV_NUM_BARS; i++) { res = &pdev->resource[i + PCI_IOV_RESOURCES]; @@ -172,50 +168,50 @@ static void pnv_pci_ioda_fixup_iov_resources(struct pci_dev *pdev) goto disable_iov; } - total_vf_bar_sz += pci_iov_resource_size(pdev, - i + PCI_IOV_RESOURCES); + vf_bar_sz = pci_iov_resource_size(pdev, i + PCI_IOV_RESOURCES); /* - * If bigger than quarter of M64 segment size, just round up - * power of two. + * Generally, one segmented M64 BAR maps one IOV BAR. However, + * if a VF BAR is too large we end up wasting a lot of space. + * If each VF needs more than 1/4 of the default m64 segment + * then each VF BAR should be mapped in single-PE mode to reduce + * the amount of space required. This does however limit the + * number of VFs we can support. * - * Generally, one M64 BAR maps one IOV BAR. To avoid conflict - * with other devices, IOV BAR size is expanded to be - * (total_pe * VF_BAR_size). When VF_BAR_size is half of M64 - * segment size , the expanded size would equal to half of the - * whole M64 space size, which will exhaust the M64 Space and - * limit the system flexibility. This is a design decision to - * set the boundary to quarter of the M64 segment size. + * The 1/4 limit is arbitrary and can be tweaked. */ - if (total_vf_bar_sz > gate) { - mul = roundup_pow_of_two(total_vfs); - dev_info(&pdev->dev, - "VF BAR Total IOV size %llx > %llx, roundup to %d VFs\n", - total_vf_bar_sz, gate, mul); - iov->m64_single_mode = true; - break; - } - } + if (vf_bar_sz > (phb->ioda.m64_segsize >> 2)) { + /* + * On PHB3, the minimum size alignment of M64 BAR in + * single mode is 32MB. If this VF BAR is smaller than + * 32MB, but still too large for a segmented window + * then we can't map it and need to disable SR-IOV for + * this device. + */ + if (vf_bar_sz < SZ_32M) { + pci_err(pdev, "VF BAR%d: %pR can't be mapped in single PE mode\n", + i, res); + goto disable_iov; + } - for (i = 0; i < PCI_SRIOV_NUM_BARS; i++) { - res = &pdev->resource[i + PCI_IOV_RESOURCES]; - if (!res->flags || res->parent) + iov->m64_single_mode[i] = true; continue; + } - size = pci_iov_resource_size(pdev, i + PCI_IOV_RESOURCES); /* - * On PHB3, the minimum size alignment of M64 BAR in single - * mode is 32MB. + * This BAR can be mapped with one segmented window, so adjust + * te resource size to accommodate. */ - if (iov->m64_single_mode && (size < SZ_32M)) - goto disable_iov; + pci_dbg(pdev, " Fixing VF BAR%d: %pR to\n", i, res); + res->end = res->start + vf_bar_sz * mul - 1; + pci_dbg(pdev, " %pR\n", res); - dev_dbg(&pdev->dev, " Fixing VF BAR%d: %pR to\n", i, res); - res->end = res->start + size * mul - 1; - dev_dbg(&pdev->dev, " %pR\n", res); - dev_info(&pdev->dev, "VF BAR%d: %pR (expanded to %d VFs for PE alignment)", + pci_info(pdev, "VF BAR%d: %pR (expanded to %d VFs for PE alignment)", i, res, mul); + + iov->need_shift = true; } + iov->vfs_expanded = mul; return; @@ -259,42 +255,40 @@ void pnv_pci_ioda_fixup_iov(struct pci_dev *pdev) resource_size_t pnv_pci_iov_resource_alignment(struct pci_dev *pdev, int resno) { - struct pnv_phb *phb = pci_bus_to_pnvhb(pdev->bus); struct pnv_iov_data *iov = pnv_iov_get(pdev); resource_size_t align; + /* + * iov can be null if we have an SR-IOV device with IOV BAR that can't + * be placed in the m64 space (i.e. The BAR is 32bit or non-prefetch). + * In that case we don't allow VFs to be enabled since one of their + * BARs would not be placed in the correct PE. + */ + if (!iov) + return align; + if (!iov->vfs_expanded) + return align; + + align = pci_iov_resource_size(pdev, resno); + + /* + * If we're using single mode then we can just use the native VF BAR + * alignment. We validated that it's possible to use a single PE + * window above when we did the fixup. + */ + if (iov->m64_single_mode[resno - PCI_IOV_RESOURCES]) + return align; + /* * On PowerNV platform, IOV BAR is mapped by M64 BAR to enable the * SR-IOV. While from hardware perspective, the range mapped by M64 * BAR should be size aligned. * - * When IOV BAR is mapped with M64 BAR in Single PE mode, the extra - * powernv-specific hardware restriction is gone. But if just use the - * VF BAR size as the alignment, PF BAR / VF BAR may be allocated with - * in one segment of M64 #15, which introduces the PE conflict between - * PF and VF. Based on this, the minimum alignment of an IOV BAR is - * m64_segsize. - * * This function returns the total IOV BAR size if M64 BAR is in * Shared PE mode or just VF BAR size if not. * If the M64 BAR is in Single PE mode, return the VF BAR size or * M64 segment size if IOV BAR size is less. */ - align = pci_iov_resource_size(pdev, resno); - - /* - * iov can be null if we have an SR-IOV device with IOV BAR that can't - * be placed in the m64 space (i.e. The BAR is 32bit or non-prefetch). - * In that case we don't allow VFs to be enabled so just return the - * default alignment. - */ - if (!iov) - return align; - if (!iov->vfs_expanded) - return align; - if (iov->m64_single_mode) - return max(align, (resource_size_t)phb->ioda.m64_segsize); - return iov->vfs_expanded * align; } @@ -449,7 +443,7 @@ static int pnv_pci_vf_assign_m64(struct pci_dev *pdev, u16 num_vfs) continue; /* don't need single mode? map everything in one go! */ - if (!iov->m64_single_mode) { + if (!iov->m64_single_mode[i]) { win = pnv_pci_alloc_m64_bar(phb, iov); if (win < 0) goto m64_failed; @@ -542,6 +536,8 @@ static int pnv_pci_vf_resource_shift(struct pci_dev *dev, int offset) res = &dev->resource[i + PCI_IOV_RESOURCES]; if (!res->flags || !res->parent) continue; + if (iov->m64_single_mode[i]) + continue; /* * The actual IOV BAR range is determined by the start address @@ -573,6 +569,8 @@ static int pnv_pci_vf_resource_shift(struct pci_dev *dev, int offset) res = &dev->resource[i + PCI_IOV_RESOURCES]; if (!res->flags || !res->parent) continue; + if (iov->m64_single_mode[i]) + continue; size = pci_iov_resource_size(dev, i + PCI_IOV_RESOURCES); res2 = *res; @@ -618,8 +616,8 @@ static void pnv_pci_sriov_disable(struct pci_dev *pdev) /* Release VF PEs */ pnv_ioda_release_vf_PE(pdev); - /* Un-shift the IOV BAR resources */ - if (!iov->m64_single_mode) + /* Un-shift the IOV BARs if we need to */ + if (iov->need_shift) pnv_pci_vf_resource_shift(pdev, -base_pe); /* Release M64 windows */ @@ -736,9 +734,8 @@ static int pnv_pci_sriov_enable(struct pci_dev *pdev, u16 num_vfs) * the IOV BAR according to the PE# allocated to the VFs. * Otherwise, the PE# for the VF will conflict with others. */ - if (!iov->m64_single_mode) { - ret = pnv_pci_vf_resource_shift(pdev, - base_pe->pe_number); + if (iov->need_shift) { + ret = pnv_pci_vf_resource_shift(pdev, base_pe->pe_number); if (ret) goto shift_failed; } diff --git a/arch/powerpc/platforms/powernv/pci.h b/arch/powerpc/platforms/powernv/pci.h index 41a6f4e938e4e..902e928c7c220 100644 --- a/arch/powerpc/platforms/powernv/pci.h +++ b/arch/powerpc/platforms/powernv/pci.h @@ -243,8 +243,15 @@ struct pnv_iov_data { /* pointer to the array of VF PEs. num_vfs long*/ struct pnv_ioda_pe *vf_pe_arr; - /* Did we map the VF BARs with single-PE IODA BARs? */ - bool m64_single_mode; + /* Did we map the VF BAR with single-PE IODA BARs? */ + bool m64_single_mode[PCI_SRIOV_NUM_BARS]; + + /* + * True if we're using any segmented windows. In that case we need + * shift the start of the IOV resource the segment corresponding to + * the allocated PE. + */ + bool need_shift; /* * Bit mask used to track which m64 windows are used to map the -- GitLab From 84d8505ed1dafb2e62d49fca5e7aa7d96cfcec49 Mon Sep 17 00:00:00 2001 From: Oliver O'Halloran Date: Wed, 22 Jul 2020 16:57:15 +1000 Subject: [PATCH 0857/1476] powerpc/powernv/sriov: Remove vfs_expanded Previously iov->vfs_expanded was used for two purposes. 1) To work out how much we need to multiple the per-VF BAR size to figure out the total space required for the IOV BAR. 2) To indicate that IOV is not usable with this device (vfs_expanded == 0). We don't really need the field for either since the multiple in 1) is always the number PEs supported by the PHB. Similarly, we don't really need it in 2) either since the IOV data field will be NULL if we can't use IOV with the device. Signed-off-by: Oliver O'Halloran Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20200722065715.1432738-16-oohall@gmail.com --- arch/powerpc/platforms/powernv/pci-sriov.c | 9 +++------ arch/powerpc/platforms/powernv/pci.h | 3 --- 2 files changed, 3 insertions(+), 9 deletions(-) diff --git a/arch/powerpc/platforms/powernv/pci-sriov.c b/arch/powerpc/platforms/powernv/pci-sriov.c index d73684869c497..8404d8c3901de 100644 --- a/arch/powerpc/platforms/powernv/pci-sriov.c +++ b/arch/powerpc/platforms/powernv/pci-sriov.c @@ -212,8 +212,6 @@ static void pnv_pci_ioda_fixup_iov_resources(struct pci_dev *pdev) iov->need_shift = true; } - iov->vfs_expanded = mul; - return; disable_iov: @@ -255,6 +253,7 @@ void pnv_pci_ioda_fixup_iov(struct pci_dev *pdev) resource_size_t pnv_pci_iov_resource_alignment(struct pci_dev *pdev, int resno) { + struct pnv_phb *phb = pci_bus_to_pnvhb(pdev->bus); struct pnv_iov_data *iov = pnv_iov_get(pdev); resource_size_t align; @@ -266,8 +265,6 @@ resource_size_t pnv_pci_iov_resource_alignment(struct pci_dev *pdev, */ if (!iov) return align; - if (!iov->vfs_expanded) - return align; align = pci_iov_resource_size(pdev, resno); @@ -289,7 +286,7 @@ resource_size_t pnv_pci_iov_resource_alignment(struct pci_dev *pdev, * If the M64 BAR is in Single PE mode, return the VF BAR size or * M64 segment size if IOV BAR size is less. */ - return iov->vfs_expanded * align; + return phb->ioda.total_pe_num * align; } static int pnv_pci_vf_release_m64(struct pci_dev *pdev, u16 num_vfs) @@ -707,7 +704,7 @@ static int pnv_pci_sriov_enable(struct pci_dev *pdev, u16 num_vfs) return -ENXIO; } - if (!iov->vfs_expanded) { + if (!iov) { dev_info(&pdev->dev, "don't support this SRIOV device with non 64bit-prefetchable IOV BAR\n"); return -ENOSPC; } diff --git a/arch/powerpc/platforms/powernv/pci.h b/arch/powerpc/platforms/powernv/pci.h index 902e928c7c220..c8cc152bdf525 100644 --- a/arch/powerpc/platforms/powernv/pci.h +++ b/arch/powerpc/platforms/powernv/pci.h @@ -234,9 +234,6 @@ void pnv_ioda_free_pe(struct pnv_ioda_pe *pe); * and this structure is used to keep track of it all. */ struct pnv_iov_data { - /* number of VFs IOV BAR expanded. FIXME: rename this to something less bad */ - u16 vfs_expanded; - /* number of VFs enabled */ u16 num_vfs; -- GitLab From 65156f2b1d9d5bf3fd0eac54b0a7fd515c92773c Mon Sep 17 00:00:00 2001 From: Athira Rajeev Date: Thu, 23 Jul 2020 03:32:37 -0400 Subject: [PATCH 0858/1476] powerpc/perf: Initialize power10 PMU registers in cpu setup routine Initialize Monitor Mode Control Register 3 (MMCR3) SPR which is new in power10. For PowerISA v3.1, BHRB disable is controlled via Monitor Mode Control Register A (MMCRA) bit, namely "BHRB Recording Disable (BHRBRD)". This patch also initializes MMCRA BHRBRD to disable BHRB feature at boot for power10. Reported-by: kernel test robot Signed-off-by: Athira Rajeev Reviewed-by: Jordan Niethe Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/1595489557-2047-1-git-send-email-atrajeev@linux.vnet.ibm.com --- arch/powerpc/kernel/cpu_setup_power.S | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/arch/powerpc/kernel/cpu_setup_power.S b/arch/powerpc/kernel/cpu_setup_power.S index 86527d19348cb..704e8b9501eee 100644 --- a/arch/powerpc/kernel/cpu_setup_power.S +++ b/arch/powerpc/kernel/cpu_setup_power.S @@ -94,13 +94,15 @@ _GLOBAL(__restore_cpu_power8) _GLOBAL(__setup_cpu_power10) mflr r11 bl __init_FSCR_power10 + bl __init_PMU + bl __init_PMU_ISA31 b 1f _GLOBAL(__setup_cpu_power9) mflr r11 bl __init_FSCR_power9 -1: bl __init_PMU - bl __init_hvmode_206 + bl __init_PMU +1: bl __init_hvmode_206 mtlr r11 beqlr li r0,0 @@ -124,13 +126,15 @@ _GLOBAL(__setup_cpu_power9) _GLOBAL(__restore_cpu_power10) mflr r11 bl __init_FSCR_power10 + bl __init_PMU + bl __init_PMU_ISA31 b 1f _GLOBAL(__restore_cpu_power9) mflr r11 bl __init_FSCR_power9 -1: bl __init_PMU - mfmsr r3 + bl __init_PMU +1: mfmsr r3 rldicl. r0,r3,4,63 mtlr r11 beqlr @@ -239,3 +243,10 @@ __init_PMU_ISA207: li r5,0 mtspr SPRN_MMCRS,r5 blr + +__init_PMU_ISA31: + li r5,0 + mtspr SPRN_MMCR3,r5 + LOAD_REG_IMMEDIATE(r5, MMCRA_BHRB_DISABLE) + mtspr SPRN_MMCRA,r5 + blr -- GitLab From 8384c82ab0860cd7db2ce4ec403e574f4ee54b6e Mon Sep 17 00:00:00 2001 From: Finn Thain Date: Sun, 31 May 2020 09:17:03 +1000 Subject: [PATCH 0859/1476] macintosh/adb-iop: Remove dead and redundant code Signed-off-by: Finn Thain Tested-by: Stan Johnson Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/7720ffb559c334504e16b24d9c2f3b8973d2d674.1590880623.git.fthain@telegraphics.com.au --- drivers/macintosh/adb-iop.c | 29 ----------------------------- 1 file changed, 29 deletions(-) diff --git a/drivers/macintosh/adb-iop.c b/drivers/macintosh/adb-iop.c index fca31640e3efd..ce28ff40fb9c5 100644 --- a/drivers/macintosh/adb-iop.c +++ b/drivers/macintosh/adb-iop.c @@ -18,24 +18,16 @@ #include #include #include -#include #include #include #include -#include #include #include -/*#define DEBUG_ADB_IOP*/ - static struct adb_request *current_req; static struct adb_request *last_req; -#if 0 -static unsigned char reply_buff[16]; -static unsigned char *reply_ptr; -#endif static enum adb_iop_state { idle, @@ -104,22 +96,11 @@ static void adb_iop_listen(struct iop_msg *msg) struct adb_iopmsg *amsg = (struct adb_iopmsg *)msg->message; struct adb_request *req; unsigned long flags; -#ifdef DEBUG_ADB_IOP - int i; -#endif local_irq_save(flags); req = current_req; -#ifdef DEBUG_ADB_IOP - printk("adb_iop_listen %p: rcvd packet, %d bytes: %02X %02X", req, - (uint)amsg->count + 2, (uint)amsg->flags, (uint)amsg->cmd); - for (i = 0; i < amsg->count; i++) - printk(" %02X", (uint)amsg->data[i]); - printk("\n"); -#endif - /* Handle a timeout. Timeout packets seem to occur even after */ /* we've gotten a valid reply to a TALK, so I'm assuming that */ /* a "timeout" is actually more like an "end-of-data" signal. */ @@ -163,9 +144,6 @@ static void adb_iop_start(void) unsigned long flags; struct adb_request *req; struct adb_iopmsg amsg; -#ifdef DEBUG_ADB_IOP - int i; -#endif /* get the packet to send */ req = current_req; @@ -174,13 +152,6 @@ static void adb_iop_start(void) local_irq_save(flags); -#ifdef DEBUG_ADB_IOP - printk("adb_iop_start %p: sending packet, %d bytes:", req, req->nbytes); - for (i = 0; i < req->nbytes; i++) - printk(" %02X", (uint)req->data[i]); - printk("\n"); -#endif - /* The IOP takes MacII-style packets, so */ /* strip the initial ADB_PACKET byte. */ -- GitLab From ff785e179faf4bb06a2f73b8dcde6dabb66a83d2 Mon Sep 17 00:00:00 2001 From: Finn Thain Date: Sun, 31 May 2020 09:17:03 +1000 Subject: [PATCH 0860/1476] macintosh/adb-iop: Correct comment text This patch improves comment style and corrects some misunderstandings in the text. Signed-off-by: Finn Thain Tested-by: Stan Johnson Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/996f835d2f3d90baaaf9ee954e252d06e8886c6f.1590880623.git.fthain@telegraphics.com.au --- drivers/macintosh/adb-iop.c | 29 +++++++++++++---------------- 1 file changed, 13 insertions(+), 16 deletions(-) diff --git a/drivers/macintosh/adb-iop.c b/drivers/macintosh/adb-iop.c index ce28ff40fb9c5..ca3b411b07421 100644 --- a/drivers/macintosh/adb-iop.c +++ b/drivers/macintosh/adb-iop.c @@ -101,11 +101,10 @@ static void adb_iop_listen(struct iop_msg *msg) req = current_req; - /* Handle a timeout. Timeout packets seem to occur even after */ - /* we've gotten a valid reply to a TALK, so I'm assuming that */ - /* a "timeout" is actually more like an "end-of-data" signal. */ - /* We need to send back a timeout packet to the IOP to shut */ - /* it up, plus complete the current request, if any. */ + /* Handle a timeout. Timeout packets seem to occur even after + * we've gotten a valid reply to a TALK, presumably because of + * autopolling. + */ if (amsg->flags & ADB_IOP_TIMEOUT) { msg->reply[0] = ADB_IOP_TIMEOUT | ADB_IOP_AUTOPOLL; @@ -115,9 +114,6 @@ static void adb_iop_listen(struct iop_msg *msg) adb_iop_end_req(req, idle); } } else { - /* TODO: is it possible for more than one chunk of data */ - /* to arrive before the timeout? If so we need to */ - /* use reply_ptr here like the other drivers do. */ if ((adb_iop_state == awaiting_reply) && (amsg->flags & ADB_IOP_EXPLICIT)) { req->reply_len = amsg->count + 1; @@ -152,23 +148,24 @@ static void adb_iop_start(void) local_irq_save(flags); - /* The IOP takes MacII-style packets, so */ - /* strip the initial ADB_PACKET byte. */ - + /* The IOP takes MacII-style packets, so strip the initial + * ADB_PACKET byte. + */ amsg.flags = ADB_IOP_EXPLICIT; amsg.count = req->nbytes - 2; - /* amsg.data immediately follows amsg.cmd, effectively making */ - /* amsg.cmd a pointer to the beginning of a full ADB packet. */ + /* amsg.data immediately follows amsg.cmd, effectively making + * &amsg.cmd a pointer to the beginning of a full ADB packet. + */ memcpy(&amsg.cmd, req->data + 1, req->nbytes - 1); req->sent = 1; adb_iop_state = sending; local_irq_restore(flags); - /* Now send it. The IOP manager will call adb_iop_complete */ - /* when the packet has been sent. */ - + /* Now send it. The IOP manager will call adb_iop_complete + * when the message has been sent. + */ iop_send_message(ADB_IOP, ADB_CHAN, req, sizeof(amsg), (__u8 *)&amsg, adb_iop_complete); } -- GitLab From 303511edb859b1fbf48b3c1d1d53b33a6ebd2a2b Mon Sep 17 00:00:00 2001 From: Finn Thain Date: Sun, 31 May 2020 09:17:03 +1000 Subject: [PATCH 0861/1476] macintosh/adb-iop: Adopt bus reset algorithm from via-macii driver This algorithm is slightly shorter and avoids the surprising adb_iop_start() call in adb_iop_poll(). Signed-off-by: Finn Thain Tested-by: Stan Johnson Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/b63d56ecb6e75f11a0bf02231f3b2db656a528a3.1590880623.git.fthain@telegraphics.com.au --- drivers/macintosh/adb-iop.c | 21 ++++++++------------- 1 file changed, 8 insertions(+), 13 deletions(-) diff --git a/drivers/macintosh/adb-iop.c b/drivers/macintosh/adb-iop.c index ca3b411b07421..c3089dacf2e2c 100644 --- a/drivers/macintosh/adb-iop.c +++ b/drivers/macintosh/adb-iop.c @@ -238,24 +238,19 @@ int adb_iop_autopoll(int devs) void adb_iop_poll(void) { - if (adb_iop_state == idle) - adb_iop_start(); iop_ism_irq_poll(ADB_IOP); } int adb_iop_reset_bus(void) { - struct adb_request req = { - .reply_expected = 0, - .nbytes = 2, - .data = { ADB_PACKET, 0 }, - }; - - adb_iop_write(&req); - while (!req.complete) { - adb_iop_poll(); - schedule(); - } + struct adb_request req; + + /* Command = 0, Address = ignored */ + adb_request(&req, NULL, ADBREQ_NOSEND, 1, ADB_BUSRESET); + adb_iop_send_request(&req, 1); + + /* Don't want any more requests during the Global Reset low time. */ + mdelay(3); return 0; } -- GitLab From aac840eca8fec02d594560647130d4e4447e10d9 Mon Sep 17 00:00:00 2001 From: Finn Thain Date: Sun, 31 May 2020 09:17:03 +1000 Subject: [PATCH 0862/1476] macintosh/adb-iop: Access current_req and adb_iop_state when inside lock Drop the redundant local_irq_save/restore() from adb_iop_start() because the caller has to do it anyway. This is the pattern used in via-macii. Signed-off-by: Finn Thain Tested-by: Stan Johnson Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/bbe32b087c7e04d68e2425f6a2df4a414d167c32.1590880623.git.fthain@telegraphics.com.au --- drivers/macintosh/adb-iop.c | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/drivers/macintosh/adb-iop.c b/drivers/macintosh/adb-iop.c index c3089dacf2e2c..7ecc41bc7358a 100644 --- a/drivers/macintosh/adb-iop.c +++ b/drivers/macintosh/adb-iop.c @@ -137,7 +137,6 @@ static void adb_iop_listen(struct iop_msg *msg) static void adb_iop_start(void) { - unsigned long flags; struct adb_request *req; struct adb_iopmsg amsg; @@ -146,8 +145,6 @@ static void adb_iop_start(void) if (!req) return; - local_irq_save(flags); - /* The IOP takes MacII-style packets, so strip the initial * ADB_PACKET byte. */ @@ -161,7 +158,6 @@ static void adb_iop_start(void) req->sent = 1; adb_iop_state = sending; - local_irq_restore(flags); /* Now send it. The IOP manager will call adb_iop_complete * when the message has been sent. @@ -208,13 +204,13 @@ static int adb_iop_write(struct adb_request *req) return -EINVAL; } - local_irq_save(flags); - req->next = NULL; req->sent = 0; req->complete = 0; req->reply_len = 0; + local_irq_save(flags); + if (current_req != 0) { last_req->next = req; last_req = req; @@ -223,10 +219,11 @@ static int adb_iop_write(struct adb_request *req) last_req = req; } - local_irq_restore(flags); - if (adb_iop_state == idle) adb_iop_start(); + + local_irq_restore(flags); + return 0; } -- GitLab From 56b732edda96b1942fff974fd298ea2a2c543b94 Mon Sep 17 00:00:00 2001 From: Finn Thain Date: Sun, 31 May 2020 09:17:03 +1000 Subject: [PATCH 0863/1476] macintosh/adb-iop: Resolve static checker warnings drivers/macintosh/adb-iop.c:215:28: warning: Using plain integer as NULL pointer drivers/macintosh/adb-iop.c:170:5: warning: symbol 'adb_iop_probe' was not declared. Should it be static? drivers/macintosh/adb-iop.c:177:5: warning: symbol 'adb_iop_init' was not declared. Should it be static? drivers/macintosh/adb-iop.c:184:5: warning: symbol 'adb_iop_send_request' was not declared. Should it be static? drivers/macintosh/adb-iop.c:230:5: warning: symbol 'adb_iop_autopoll' was not declared. Should it be static? drivers/macintosh/adb-iop.c:236:6: warning: symbol 'adb_iop_poll' was not declared. Should it be static? drivers/macintosh/adb-iop.c:241:5: warning: symbol 'adb_iop_reset_bus' was not declared. Should it be static? Signed-off-by: Finn Thain Tested-by: Stan Johnson Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/25edf4450abd20e002b166ba3a11189dc1efa906.1590880623.git.fthain@telegraphics.com.au --- drivers/macintosh/adb-iop.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/drivers/macintosh/adb-iop.c b/drivers/macintosh/adb-iop.c index 7ecc41bc7358a..a2b28e09f2ce6 100644 --- a/drivers/macintosh/adb-iop.c +++ b/drivers/macintosh/adb-iop.c @@ -166,21 +166,21 @@ static void adb_iop_start(void) adb_iop_complete); } -int adb_iop_probe(void) +static int adb_iop_probe(void) { if (!iop_ism_present) return -ENODEV; return 0; } -int adb_iop_init(void) +static int adb_iop_init(void) { pr_info("adb: IOP ISM driver v0.4 for Unified ADB\n"); iop_listen(ADB_IOP, ADB_CHAN, adb_iop_listen, "ADB"); return 0; } -int adb_iop_send_request(struct adb_request *req, int sync) +static int adb_iop_send_request(struct adb_request *req, int sync) { int err; @@ -211,7 +211,7 @@ static int adb_iop_write(struct adb_request *req) local_irq_save(flags); - if (current_req != 0) { + if (current_req) { last_req->next = req; last_req = req; } else { @@ -227,18 +227,18 @@ static int adb_iop_write(struct adb_request *req) return 0; } -int adb_iop_autopoll(int devs) +static int adb_iop_autopoll(int devs) { /* TODO: how do we enable/disable autopoll? */ return 0; } -void adb_iop_poll(void) +static void adb_iop_poll(void) { iop_ism_irq_poll(ADB_IOP); } -int adb_iop_reset_bus(void) +static int adb_iop_reset_bus(void) { struct adb_request req; -- GitLab From 32226e81704398317e1cc5a82d24c0ef3cc25e5e Mon Sep 17 00:00:00 2001 From: Finn Thain Date: Sun, 31 May 2020 09:17:03 +1000 Subject: [PATCH 0864/1476] macintosh/adb-iop: Implement idle -> sending state transition In the present algorithm, the 'idle' state transition does not take place until there's a bus timeout. Once idle, the driver does not automatically proceed with the next request. Change the algorithm so that queued ADB requests will be sent as soon as the driver becomes idle. This is to take place after the current IOP message is completed. Signed-off-by: Finn Thain Tested-by: Stan Johnson Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/dedcdfc62f43e85cc4c2a8d211a7e2fec7bc7c1a.1590880623.git.fthain@telegraphics.com.au --- drivers/macintosh/adb-iop.c | 43 +++++++++++++++++++++---------------- 1 file changed, 24 insertions(+), 19 deletions(-) diff --git a/drivers/macintosh/adb-iop.c b/drivers/macintosh/adb-iop.c index a2b28e09f2ce6..f22792c95d4fa 100644 --- a/drivers/macintosh/adb-iop.c +++ b/drivers/macintosh/adb-iop.c @@ -54,13 +54,19 @@ struct adb_driver adb_iop_driver = { .reset_bus = adb_iop_reset_bus }; -static void adb_iop_end_req(struct adb_request *req, int state) +static void adb_iop_done(void) { + struct adb_request *req = current_req; + + adb_iop_state = idle; + req->complete = 1; current_req = req->next; if (req->done) (*req->done)(req); - adb_iop_state = state; + + if (adb_iop_state == idle) + adb_iop_start(); } /* @@ -94,37 +100,36 @@ static void adb_iop_complete(struct iop_msg *msg) static void adb_iop_listen(struct iop_msg *msg) { struct adb_iopmsg *amsg = (struct adb_iopmsg *)msg->message; - struct adb_request *req; unsigned long flags; + bool req_done = false; local_irq_save(flags); - req = current_req; - /* Handle a timeout. Timeout packets seem to occur even after * we've gotten a valid reply to a TALK, presumably because of * autopolling. */ - if (amsg->flags & ADB_IOP_TIMEOUT) { - msg->reply[0] = ADB_IOP_TIMEOUT | ADB_IOP_AUTOPOLL; - msg->reply[1] = 0; - msg->reply[2] = 0; - if (req && (adb_iop_state != idle)) { - adb_iop_end_req(req, idle); - } - } else { - if ((adb_iop_state == awaiting_reply) && - (amsg->flags & ADB_IOP_EXPLICIT)) { + if (amsg->flags & ADB_IOP_EXPLICIT) { + if (adb_iop_state == awaiting_reply) { + struct adb_request *req = current_req; + req->reply_len = amsg->count + 1; memcpy(req->reply, &amsg->cmd, req->reply_len); - } else { - adb_input(&amsg->cmd, amsg->count + 1, - amsg->flags & ADB_IOP_AUTOPOLL); + + req_done = true; } - memcpy(msg->reply, msg->message, IOP_MSG_LEN); + } else if (!(amsg->flags & ADB_IOP_TIMEOUT)) { + adb_input(&amsg->cmd, amsg->count + 1, + amsg->flags & ADB_IOP_AUTOPOLL); } + + msg->reply[0] = ADB_IOP_AUTOPOLL; iop_complete_message(msg); + + if (req_done) + adb_iop_done(); + local_irq_restore(flags); } -- GitLab From e2954e5f727fad126258e83259b513988973c166 Mon Sep 17 00:00:00 2001 From: Finn Thain Date: Sun, 31 May 2020 09:17:03 +1000 Subject: [PATCH 0865/1476] macintosh/adb-iop: Implement sending -> idle state transition On leaving the 'sending' state, proceed to the 'idle' state if no reply is expected. Drop redundant test for adb_iop_state == sending && current_req. Signed-off-by: Finn Thain Tested-by: Stan Johnson Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/6991996dd4aaf0b52cfd650172bf0f6fbe37a452.1590880623.git.fthain@telegraphics.com.au --- drivers/macintosh/adb-iop.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/drivers/macintosh/adb-iop.c b/drivers/macintosh/adb-iop.c index f22792c95d4fa..8594e4f9a8309 100644 --- a/drivers/macintosh/adb-iop.c +++ b/drivers/macintosh/adb-iop.c @@ -77,15 +77,14 @@ static void adb_iop_done(void) static void adb_iop_complete(struct iop_msg *msg) { - struct adb_request *req; unsigned long flags; local_irq_save(flags); - req = current_req; - if ((adb_iop_state == sending) && req && req->reply_expected) { + if (current_req->reply_expected) adb_iop_state = awaiting_reply; - } + else + adb_iop_done(); local_irq_restore(flags); } -- GitLab From c66da95a39ec2bb95544c3def974d96e8c178f57 Mon Sep 17 00:00:00 2001 From: Finn Thain Date: Sun, 31 May 2020 09:17:03 +1000 Subject: [PATCH 0866/1476] macintosh/adb-iop: Implement SRQ autopolling The adb_driver.autopoll method is needed during ADB bus scan and device address assignment. Implement this method so that the IOP's list of device addresses can be updated. When the list is empty, disable SRQ autopolling. Signed-off-by: Finn Thain Tested-by: Stan Johnson Acked-by: Geert Uytterhoeven Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/0fb7fdcd99d7820bb27faf1f27f7f6f1923914ef.1590880623.git.fthain@telegraphics.com.au --- arch/m68k/include/asm/adb_iop.h | 1 + drivers/macintosh/adb-iop.c | 32 ++++++++++++++++++++++++++------ 2 files changed, 27 insertions(+), 6 deletions(-) diff --git a/arch/m68k/include/asm/adb_iop.h b/arch/m68k/include/asm/adb_iop.h index 195d7fb1268c9..6aecd020e2fc9 100644 --- a/arch/m68k/include/asm/adb_iop.h +++ b/arch/m68k/include/asm/adb_iop.h @@ -29,6 +29,7 @@ #define ADB_IOP_EXPLICIT 0x80 /* nonzero if explicit command */ #define ADB_IOP_AUTOPOLL 0x40 /* auto/SRQ polling enabled */ +#define ADB_IOP_SET_AUTOPOLL 0x20 /* set autopoll device list */ #define ADB_IOP_SRQ 0x04 /* SRQ detected */ #define ADB_IOP_TIMEOUT 0x02 /* nonzero if timeout */ diff --git a/drivers/macintosh/adb-iop.c b/drivers/macintosh/adb-iop.c index 8594e4f9a8309..f3d1a460fbce1 100644 --- a/drivers/macintosh/adb-iop.c +++ b/drivers/macintosh/adb-iop.c @@ -7,10 +7,6 @@ * 1999-07-01 (jmt) - First implementation for new driver architecture. * * 1999-07-31 (jmt) - First working version. - * - * TODO: - * - * o Implement SRQ handling. */ #include @@ -28,6 +24,7 @@ static struct adb_request *current_req; static struct adb_request *last_req; +static unsigned int autopoll_devs; static enum adb_iop_state { idle, @@ -123,7 +120,7 @@ static void adb_iop_listen(struct iop_msg *msg) amsg->flags & ADB_IOP_AUTOPOLL); } - msg->reply[0] = ADB_IOP_AUTOPOLL; + msg->reply[0] = autopoll_devs ? ADB_IOP_AUTOPOLL : 0; iop_complete_message(msg); if (req_done) @@ -231,9 +228,32 @@ static int adb_iop_write(struct adb_request *req) return 0; } +static void adb_iop_set_ap_complete(struct iop_msg *msg) +{ + struct adb_iopmsg *amsg = (struct adb_iopmsg *)msg->message; + + autopoll_devs = (amsg->data[1] << 8) | amsg->data[0]; +} + static int adb_iop_autopoll(int devs) { - /* TODO: how do we enable/disable autopoll? */ + struct adb_iopmsg amsg; + unsigned long flags; + unsigned int mask = (unsigned int)devs & 0xFFFE; + + local_irq_save(flags); + + amsg.flags = ADB_IOP_SET_AUTOPOLL | (mask ? ADB_IOP_AUTOPOLL : 0); + amsg.count = 2; + amsg.cmd = 0; + amsg.data[0] = mask & 0xFF; + amsg.data[1] = (mask >> 8) & 0xFF; + + iop_send_message(ADB_IOP, ADB_CHAN, NULL, sizeof(amsg), (__u8 *)&amsg, + adb_iop_set_ap_complete); + + local_irq_restore(flags); + return 0; } -- GitLab From 59ea38f6b3af5636edf541768a1ed721eeaca99e Mon Sep 17 00:00:00 2001 From: Finn Thain Date: Sun, 28 Jun 2020 14:23:12 +1000 Subject: [PATCH 0867/1476] macintosh/via-macii: Access autopoll_devs when inside lock The interrupt handler should be excluded when accessing the autopoll_devs variable. Fixes: d95fd5fce88f0 ("m68k: Mac II ADB fixes") # v5.0+ Signed-off-by: Finn Thain Tested-by: Stan Johnson Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/5952dd8a9bc9de90f1acc4790c51dd42b4c98065.1593318192.git.fthain@telegraphics.com.au --- drivers/macintosh/via-macii.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/drivers/macintosh/via-macii.c b/drivers/macintosh/via-macii.c index ac824d7b2dcfc..6aa903529570d 100644 --- a/drivers/macintosh/via-macii.c +++ b/drivers/macintosh/via-macii.c @@ -270,15 +270,12 @@ static int macii_autopoll(int devs) unsigned long flags; int err = 0; + local_irq_save(flags); + /* bit 1 == device 1, and so on. */ autopoll_devs = devs & 0xFFFE; - if (!autopoll_devs) - return 0; - - local_irq_save(flags); - - if (current_req == NULL) { + if (autopoll_devs && !current_req) { /* Send a Talk Reg 0. The controller will repeatedly transmit * this as long as it is idle. */ -- GitLab From f93bfeb55255bddaa16597e187a99ae6131b964a Mon Sep 17 00:00:00 2001 From: Finn Thain Date: Sun, 28 Jun 2020 14:23:12 +1000 Subject: [PATCH 0868/1476] macintosh/via-macii: Poll the device most likely to respond Poll the most recently polled device by default, rather than the lowest device address that happens to be enabled in autopoll_devs. This improves input latency. Re-use macii_queue_poll() rather than duplicate that logic. This eliminates a static struct and function. Fixes: d95fd5fce88f0 ("m68k: Mac II ADB fixes") # v5.0+ Signed-off-by: Finn Thain Tested-by: Stan Johnson Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/5836f80886ebcfbe5be5fb7e0dc49feed6469712.1593318192.git.fthain@telegraphics.com.au --- drivers/macintosh/via-macii.c | 99 +++++++++++++++++++---------------- 1 file changed, 53 insertions(+), 46 deletions(-) diff --git a/drivers/macintosh/via-macii.c b/drivers/macintosh/via-macii.c index 6aa903529570d..d4f1a65c5f1fd 100644 --- a/drivers/macintosh/via-macii.c +++ b/drivers/macintosh/via-macii.c @@ -77,6 +77,10 @@ static volatile unsigned char *via; #define ST_ODD 0x20 /* ADB state: odd data byte */ #define ST_IDLE 0x30 /* ADB state: idle, nothing to send */ +/* ADB command byte structure */ +#define ADDR_MASK 0xF0 +#define CMD_MASK 0x0F + static int macii_init_via(void); static void macii_start(void); static irqreturn_t macii_interrupt(int irq, void *arg); @@ -117,7 +121,8 @@ static int reply_len; /* number of bytes received in reply_buf or req->reply */ static int status; /* VIA's ADB status bits captured upon interrupt */ static int last_status; /* status bits as at previous interrupt */ static int srq_asserted; /* have to poll for the device that asserted it */ -static int command_byte; /* the most recent command byte transmitted */ +static u8 last_cmd; /* the most recent command byte transmitted */ +static u8 last_poll_cmd; /* the most recent Talk R0 command byte transmitted */ static int autopoll_devs; /* bits set are device addresses to be polled */ /* Check for MacII style ADB */ @@ -179,35 +184,49 @@ static int macii_init_via(void) /* Send an ADB poll (Talk Register 0 command prepended to the request queue) */ static void macii_queue_poll(void) { - /* No point polling the active device as it will never assert SRQ, so - * poll the next device in the autopoll list. This could leave us - * stuck in a polling loop if an unprobed device is asserting SRQ. - * In theory, that could only happen if a device was plugged in after - * probing started. Unplugging it again will break the cycle. - * (Simply polling the next higher device often ends up polling almost - * every device (after wrapping around), which takes too long.) - */ - int device_mask; - int next_device; static struct adb_request req; + unsigned char poll_command; + unsigned int poll_addr; + /* This only polls devices in the autopoll list, which assumes that + * unprobed devices never assert SRQ. That could happen if a device was + * plugged in after the adb bus scan. Unplugging it again will resolve + * the problem. This behaviour is similar to MacOS. + */ if (!autopoll_devs) return; - device_mask = (1 << (((command_byte & 0xF0) >> 4) + 1)) - 1; - if (autopoll_devs & ~device_mask) - next_device = ffs(autopoll_devs & ~device_mask) - 1; - else - next_device = ffs(autopoll_devs) - 1; + /* The device most recently polled may not be the best device to poll + * right now. Some other device(s) may have signalled SRQ (the active + * device won't do that). Or the autopoll list may have been changed. + * Try polling the next higher address. + */ + poll_addr = (last_poll_cmd & ADDR_MASK) >> 4; + if ((srq_asserted && last_cmd == last_poll_cmd) || + !(autopoll_devs & (1 << poll_addr))) { + unsigned int higher_devs; + + higher_devs = autopoll_devs & -(1 << (poll_addr + 1)); + poll_addr = ffs(higher_devs ? higher_devs : autopoll_devs) - 1; + } - adb_request(&req, NULL, ADBREQ_NOSEND, 1, ADB_READREG(next_device, 0)); + /* Send a Talk Register 0 command */ + poll_command = ADB_READREG(poll_addr, 0); + + /* No need to repeat this Talk command. The transceiver will do that + * as long as it is idle. + */ + if (poll_command == last_cmd) + return; + + adb_request(&req, NULL, ADBREQ_NOSEND, 1, poll_command); req.sent = 0; req.complete = 0; req.reply_len = 0; req.next = current_req; - if (current_req != NULL) { + if (WARN_ON(current_req)) { current_req = &req; } else { current_req = &req; @@ -266,37 +285,22 @@ static int macii_write(struct adb_request *req) /* Start auto-polling */ static int macii_autopoll(int devs) { - static struct adb_request req; unsigned long flags; - int err = 0; local_irq_save(flags); /* bit 1 == device 1, and so on. */ autopoll_devs = devs & 0xFFFE; - if (autopoll_devs && !current_req) { - /* Send a Talk Reg 0. The controller will repeatedly transmit - * this as long as it is idle. - */ - adb_request(&req, NULL, ADBREQ_NOSEND, 1, - ADB_READREG(ffs(autopoll_devs) - 1, 0)); - err = macii_write(&req); + if (!current_req) { + macii_queue_poll(); + if (current_req && macii_state == idle) + macii_start(); } local_irq_restore(flags); - return err; -} -static inline int need_autopoll(void) -{ - /* Was the last command Talk Reg 0 - * and is the target on the autopoll list? - */ - if ((command_byte & 0x0F) == 0x0C && - ((1 << ((command_byte & 0xF0) >> 4)) & autopoll_devs)) - return 0; - return 1; + return 0; } /* Prod the chip without interrupts */ @@ -333,7 +337,12 @@ static void macii_start(void) */ /* store command byte */ - command_byte = req->data[1]; + last_cmd = req->data[1]; + + /* If this is a Talk Register 0 command, store the command byte */ + if ((last_cmd & CMD_MASK) == ADB_READREG(0, 0)) + last_poll_cmd = last_cmd; + /* Output mode */ via[ACR] |= SR_OUT; /* Load data */ @@ -424,10 +433,11 @@ static irqreturn_t macii_interrupt(int irq, void *arg) if (req->done) (*req->done)(req); - if (current_req) + if (!current_req) + macii_queue_poll(); + + if (current_req && macii_state == idle) macii_start(); - else if (need_autopoll()) - macii_autopoll(autopoll_devs); } if (macii_state == idle) { @@ -507,14 +517,11 @@ static irqreturn_t macii_interrupt(int irq, void *arg) macii_state = idle; - /* SRQ seen before, initiate poll now */ - if (srq_asserted) + if (!current_req) macii_queue_poll(); if (current_req) macii_start(); - else if (need_autopoll()) - macii_autopoll(autopoll_devs); if (macii_state == idle) via[B] = (via[B] & ~ST_MASK) | ST_IDLE; -- GitLab From b4d76c28eca369b8105fe3a0a9f396e3fbcd0dd5 Mon Sep 17 00:00:00 2001 From: Finn Thain Date: Sun, 28 Jun 2020 14:23:12 +1000 Subject: [PATCH 0869/1476] macintosh/via-macii: Handle /CTLR_IRQ signal correctly I'm told that the /CTLR_IRQ signal from the ADB transceiver gets interpreted by MacOS to mean SRQ, bus timeout or end-of-packet depending on the circumstances, and that Linux's via-macii driver does not correctly interpret this signal. Instead, the via-macii driver interprets certain received byte values (0x00 and 0xFF) as signalling end of packet and bus timeout (respectively). Problem is, those values can also appear under other circumstances. This patch changes the bus timeout, end of packet and SRQ detection logic to bring it closer to the logic that MacOS reportedly uses. Fixes: 1da177e4c3f41 ("Linux-2.6.12-rc2") # v5.0+ Reported-by: Mark Cave-Ayland Signed-off-by: Finn Thain Tested-by: Stan Johnson Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/6541fda1d8db3ae87c3abe17d189a10dc96e2382.1593318192.git.fthain@telegraphics.com.au --- drivers/macintosh/via-macii.c | 166 ++++++++++++++++++++-------------- 1 file changed, 97 insertions(+), 69 deletions(-) diff --git a/drivers/macintosh/via-macii.c b/drivers/macintosh/via-macii.c index d4f1a65c5f1fd..6a5cd7de05baf 100644 --- a/drivers/macintosh/via-macii.c +++ b/drivers/macintosh/via-macii.c @@ -80,6 +80,8 @@ static volatile unsigned char *via; /* ADB command byte structure */ #define ADDR_MASK 0xF0 #define CMD_MASK 0x0F +#define OP_MASK 0x0C +#define TALK 0x0C static int macii_init_via(void); static void macii_start(void); @@ -119,9 +121,10 @@ static int reading_reply; /* store reply in reply_buf else req->reply */ static int data_index; /* index of the next byte to send from req->data */ static int reply_len; /* number of bytes received in reply_buf or req->reply */ static int status; /* VIA's ADB status bits captured upon interrupt */ -static int last_status; /* status bits as at previous interrupt */ -static int srq_asserted; /* have to poll for the device that asserted it */ +static bool bus_timeout; /* no data was sent by the device */ +static bool srq_asserted; /* have to poll for the device that asserted it */ static u8 last_cmd; /* the most recent command byte transmitted */ +static u8 last_talk_cmd; /* the most recent Talk command byte transmitted */ static u8 last_poll_cmd; /* the most recent Talk R0 command byte transmitted */ static int autopoll_devs; /* bits set are device addresses to be polled */ @@ -170,7 +173,6 @@ static int macii_init_via(void) /* Set up state: idle */ via[B] |= ST_IDLE; - last_status = via[B] & (ST_MASK | CTLR_IRQ); /* Shift register on input */ via[ACR] = (via[ACR] & ~SR_CTRL) | SR_EXT; @@ -336,13 +338,6 @@ static void macii_start(void) * And req->nbytes is the number of bytes of real data plus one. */ - /* store command byte */ - last_cmd = req->data[1]; - - /* If this is a Talk Register 0 command, store the command byte */ - if ((last_cmd & CMD_MASK) == ADB_READREG(0, 0)) - last_poll_cmd = last_cmd; - /* Output mode */ via[ACR] |= SR_OUT; /* Load data */ @@ -352,6 +347,9 @@ static void macii_start(void) macii_state = sending; data_index = 2; + + bus_timeout = false; + srq_asserted = false; } /* @@ -360,15 +358,17 @@ static void macii_start(void) * generating shift register interrupts (SR_INT) for us. This means there has * to be activity on the ADB bus. The chip will poll to achieve this. * - * The basic ADB state machine was left unchanged from the original MacII code - * by Alan Cox, which was based on the CUDA driver for PowerMac. - * The syntax of the ADB status lines is totally different on MacII, - * though. MacII uses the states Command -> Even -> Odd -> Even ->...-> Idle - * for sending and Idle -> Even -> Odd -> Even ->...-> Idle for receiving. - * Start and end of a receive packet are signalled by asserting /IRQ on the - * interrupt line (/IRQ means the CTLR_IRQ bit in port B; not to be confused - * with the VIA shift register interrupt. /IRQ never actually interrupts the - * processor, it's just an ordinary input.) + * The VIA Port B output signalling works as follows. After the ADB transceiver + * sees a transition on the PB4 and PB5 lines it will crank over the VIA shift + * register which eventually raises the SR_INT interrupt. The PB4/PB5 outputs + * are toggled with each byte as the ADB transaction progresses. + * + * Request with no reply expected (and empty transceiver buffer): + * CMD -> IDLE + * Request with expected reply packet (or with buffered autopoll packet): + * CMD -> EVEN -> ODD -> EVEN -> ... -> IDLE + * Unsolicited packet: + * IDLE -> EVEN -> ODD -> EVEN -> ... -> IDLE */ static irqreturn_t macii_interrupt(int irq, void *arg) { @@ -388,31 +388,31 @@ static irqreturn_t macii_interrupt(int irq, void *arg) } } - last_status = status; status = via[B] & (ST_MASK | CTLR_IRQ); switch (macii_state) { case idle: - if (reading_reply) { - reply_ptr = current_req->reply; - } else { - WARN_ON(current_req); - reply_ptr = reply_buf; - } + WARN_ON((status & ST_MASK) != ST_IDLE); + + reply_ptr = reply_buf; + reading_reply = 0; + + bus_timeout = false; + srq_asserted = false; x = via[SR]; - if ((status & CTLR_IRQ) && (x == 0xFF)) { - /* Bus timeout without SRQ sequence: - * data is "FF" while CTLR_IRQ is "H" + if (!(status & CTLR_IRQ)) { + /* /CTLR_IRQ asserted in idle state means we must + * read an autopoll reply from the transceiver buffer. */ - reply_len = 0; - srq_asserted = 0; - macii_state = read_done; - } else { macii_state = reading; *reply_ptr = x; reply_len = 1; + } else { + /* bus timeout */ + macii_state = read_done; + reply_len = 0; } /* set ADB state = even for first data byte */ @@ -421,13 +421,52 @@ static irqreturn_t macii_interrupt(int irq, void *arg) case sending: req = current_req; - if (data_index >= req->nbytes) { + + if (status == (ST_CMD | CTLR_IRQ)) { + /* /CTLR_IRQ de-asserted after the command byte means + * the host can continue with the transaction. + */ + + /* Store command byte */ + last_cmd = req->data[1]; + if ((last_cmd & OP_MASK) == TALK) { + last_talk_cmd = last_cmd; + if ((last_cmd & CMD_MASK) == ADB_READREG(0, 0)) + last_poll_cmd = last_cmd; + } + } + + if (status == ST_CMD) { + /* /CTLR_IRQ asserted after the command byte means we + * must read an autopoll reply. The first byte was + * lost because the shift register was an output. + */ + macii_state = reading; + + reading_reply = 0; + reply_ptr = reply_buf; + *reply_ptr = last_talk_cmd; + reply_len = 1; + + /* reset to shift in */ + via[ACR] &= ~SR_OUT; + x = via[SR]; + } else if (data_index >= req->nbytes) { req->sent = 1; - macii_state = idle; if (req->reply_expected) { + macii_state = reading; + reading_reply = 1; + reply_ptr = req->reply; + *reply_ptr = req->data[1]; + reply_len = 1; + + via[ACR] &= ~SR_OUT; + x = via[SR]; } else { + macii_state = idle; + req->complete = 1; current_req = req->next; if (req->done) @@ -438,25 +477,26 @@ static irqreturn_t macii_interrupt(int irq, void *arg) if (current_req && macii_state == idle) macii_start(); - } - if (macii_state == idle) { - /* reset to shift in */ - via[ACR] &= ~SR_OUT; - x = via[SR]; - /* set ADB state idle - might get SRQ */ - via[B] = (via[B] & ~ST_MASK) | ST_IDLE; + if (macii_state == idle) { + /* reset to shift in */ + via[ACR] &= ~SR_OUT; + x = via[SR]; + /* set ADB state idle - might get SRQ */ + via[B] = (via[B] & ~ST_MASK) | ST_IDLE; + } + break; } } else { via[SR] = req->data[data_index++]; + } - if ((via[B] & ST_MASK) == ST_CMD) { - /* just sent the command byte, set to EVEN */ - via[B] = (via[B] & ~ST_MASK) | ST_EVEN; - } else { - /* invert state bits, toggle ODD/EVEN */ - via[B] ^= ST_MASK; - } + if ((via[B] & ST_MASK) == ST_CMD) { + /* just sent the command byte, set to EVEN */ + via[B] = (via[B] & ~ST_MASK) | ST_EVEN; + } else { + /* invert state bits, toggle ODD/EVEN */ + via[B] ^= ST_MASK; } break; @@ -465,28 +505,13 @@ static irqreturn_t macii_interrupt(int irq, void *arg) WARN_ON((status & ST_MASK) == ST_CMD || (status & ST_MASK) == ST_IDLE); - /* Bus timeout with SRQ sequence: - * data is "XX FF" while CTLR_IRQ is "L L" - * End of packet without SRQ sequence: - * data is "XX...YY 00" while CTLR_IRQ is "L...H L" - * End of packet SRQ sequence: - * data is "XX...YY 00" while CTLR_IRQ is "L...L L" - * (where XX is the first response byte and - * YY is the last byte of valid response data.) - */ - - srq_asserted = 0; if (!(status & CTLR_IRQ)) { - if (x == 0xFF) { - if (!(last_status & CTLR_IRQ)) { - macii_state = read_done; - reply_len = 0; - srq_asserted = 1; - } - } else if (x == 0x00) { + if (status == ST_EVEN && reply_len == 1) { + bus_timeout = true; + } else if (status == ST_ODD && reply_len == 2) { + srq_asserted = true; + } else { macii_state = read_done; - if (!(last_status & CTLR_IRQ)) - srq_asserted = 1; } } @@ -504,6 +529,9 @@ static irqreturn_t macii_interrupt(int irq, void *arg) case read_done: x = via[SR]; + if (bus_timeout) + reply_len = 0; + if (reading_reply) { reading_reply = 0; req = current_req; -- GitLab From b16b67689baa01a5616b651356df7ad3e47a8763 Mon Sep 17 00:00:00 2001 From: Finn Thain Date: Sun, 28 Jun 2020 14:23:12 +1000 Subject: [PATCH 0870/1476] macintosh/via-macii: Remove read_done state The driver state machine may enter the 'read_done' state when leaving the 'idle' or 'reading' state. This transition is pointless, as is the extra interrupt it requires. The interrupt is produced by the transceiver (even when it has no data to send) because an extra EVEN/ODD toggle was signalled by the driver. Drop the extra state to simplify the code. Fixes: 1da177e4c3f41 ("Linux-2.6.12-rc2") # v5.0+ Signed-off-by: Finn Thain Tested-by: Stan Johnson Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/0253194363af4426f9788796811a6a29fb87c713.1593318192.git.fthain@telegraphics.com.au --- drivers/macintosh/via-macii.c | 70 ++++++++++++++--------------------- 1 file changed, 28 insertions(+), 42 deletions(-) diff --git a/drivers/macintosh/via-macii.c b/drivers/macintosh/via-macii.c index 6a5cd7de05baf..d29c87943ca46 100644 --- a/drivers/macintosh/via-macii.c +++ b/drivers/macintosh/via-macii.c @@ -110,7 +110,6 @@ static enum macii_state { idle, sending, reading, - read_done, } macii_state; static struct adb_request *current_req; /* first request struct in the queue */ @@ -411,8 +410,8 @@ static irqreturn_t macii_interrupt(int irq, void *arg) reply_len = 1; } else { /* bus timeout */ - macii_state = read_done; reply_len = 0; + break; } /* set ADB state = even for first data byte */ @@ -471,20 +470,6 @@ static irqreturn_t macii_interrupt(int irq, void *arg) current_req = req->next; if (req->done) (*req->done)(req); - - if (!current_req) - macii_queue_poll(); - - if (current_req && macii_state == idle) - macii_start(); - - if (macii_state == idle) { - /* reset to shift in */ - via[ACR] &= ~SR_OUT; - x = via[SR]; - /* set ADB state idle - might get SRQ */ - via[B] = (via[B] & ~ST_MASK) | ST_IDLE; - } break; } } else { @@ -511,12 +496,28 @@ static irqreturn_t macii_interrupt(int irq, void *arg) } else if (status == ST_ODD && reply_len == 2) { srq_asserted = true; } else { - macii_state = read_done; + macii_state = idle; + + if (bus_timeout) + reply_len = 0; + + if (reading_reply) { + struct adb_request *req = current_req; + + req->reply_len = reply_len; + + req->complete = 1; + current_req = req->next; + if (req->done) + (*req->done)(req); + } else if (reply_len && autopoll_devs) { + adb_input(reply_buf, reply_len, 0); + } + break; } } - if (macii_state == reading && - reply_len < ARRAY_SIZE(reply_buf)) { + if (reply_len < ARRAY_SIZE(reply_buf)) { reply_ptr++; *reply_ptr = x; reply_len++; @@ -526,37 +527,22 @@ static irqreturn_t macii_interrupt(int irq, void *arg) via[B] ^= ST_MASK; break; - case read_done: - x = via[SR]; - - if (bus_timeout) - reply_len = 0; - - if (reading_reply) { - reading_reply = 0; - req = current_req; - req->reply_len = reply_len; - req->complete = 1; - current_req = req->next; - if (req->done) - (*req->done)(req); - } else if (reply_len && autopoll_devs) - adb_input(reply_buf, reply_len, 0); - - macii_state = idle; + default: + break; + } + if (macii_state == idle) { if (!current_req) macii_queue_poll(); if (current_req) macii_start(); - if (macii_state == idle) + if (macii_state == idle) { + via[ACR] &= ~SR_OUT; + x = via[SR]; via[B] = (via[B] & ~ST_MASK) | ST_IDLE; - break; - - default: - break; + } } local_irq_restore(flags); -- GitLab From 624cf5b538b507293ec761797bd8ce0702fefe64 Mon Sep 17 00:00:00 2001 From: Finn Thain Date: Sun, 28 Jun 2020 14:23:12 +1000 Subject: [PATCH 0871/1476] macintosh/via-macii: Handle poll replies correctly Userspace applications may use /dev/adb to send Talk requests. Such requests always have req->reply_expected == 1. The same is true of Talk requests sent by the kernel, except for poll requests queued internally by the via-macii driver. Those requests have req->reply_expected == 0. Consequently, poll reply packets get treated like autopoll reply packets. (It doesn't make sense to try to distinguish them.) Always enter 'reading' state after a poll request, so that the reply gets collected and passed to adb_input(), and none go missing. All Talk replies passed to adb_input() come from polling or autopolling, so call adb_input() with the autopoll parameter set to 1. Fixes: d95fd5fce88f0 ("m68k: Mac II ADB fixes") # v5.0+ Signed-off-by: Finn Thain Tested-by: Stan Johnson Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/754cddfa045e5bfa53e5da199831de02e7d2f27f.1593318192.git.fthain@telegraphics.com.au --- drivers/macintosh/via-macii.c | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/drivers/macintosh/via-macii.c b/drivers/macintosh/via-macii.c index d29c87943ca46..8d5ef77b4a435 100644 --- a/drivers/macintosh/via-macii.c +++ b/drivers/macintosh/via-macii.c @@ -463,6 +463,21 @@ static irqreturn_t macii_interrupt(int irq, void *arg) via[ACR] &= ~SR_OUT; x = via[SR]; + } else if ((req->data[1] & OP_MASK) == TALK) { + macii_state = reading; + + reading_reply = 0; + reply_ptr = reply_buf; + *reply_ptr = req->data[1]; + reply_len = 1; + + via[ACR] &= ~SR_OUT; + x = via[SR]; + + req->complete = 1; + current_req = req->next; + if (req->done) + (*req->done)(req); } else { macii_state = idle; @@ -510,8 +525,9 @@ static irqreturn_t macii_interrupt(int irq, void *arg) current_req = req->next; if (req->done) (*req->done)(req); - } else if (reply_len && autopoll_devs) { - adb_input(reply_buf, reply_len, 0); + } else if (reply_len && autopoll_devs && + reply_buf[0] == last_poll_cmd) { + adb_input(reply_buf, reply_len, 1); } break; } -- GitLab From f87a162572c9f7c839a207c7de6c73ffe54a777c Mon Sep 17 00:00:00 2001 From: Finn Thain Date: Sun, 28 Jun 2020 14:23:12 +1000 Subject: [PATCH 0872/1476] macintosh/via-macii: Use bool type for reading_reply variable Signed-off-by: Finn Thain Tested-by: Stan Johnson Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/779551219a11b19e574dfcd87e4ef60af08c4fc3.1593318192.git.fthain@telegraphics.com.au --- drivers/macintosh/via-macii.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/macintosh/via-macii.c b/drivers/macintosh/via-macii.c index 8d5ef77b4a435..e143ddb81de34 100644 --- a/drivers/macintosh/via-macii.c +++ b/drivers/macintosh/via-macii.c @@ -116,7 +116,7 @@ static struct adb_request *current_req; /* first request struct in the queue */ static struct adb_request *last_req; /* last request struct in the queue */ static unsigned char reply_buf[16]; /* storage for autopolled replies */ static unsigned char *reply_ptr; /* next byte in reply_buf or req->reply */ -static int reading_reply; /* store reply in reply_buf else req->reply */ +static bool reading_reply; /* store reply in reply_buf else req->reply */ static int data_index; /* index of the next byte to send from req->data */ static int reply_len; /* number of bytes received in reply_buf or req->reply */ static int status; /* VIA's ADB status bits captured upon interrupt */ @@ -394,7 +394,7 @@ static irqreturn_t macii_interrupt(int irq, void *arg) WARN_ON((status & ST_MASK) != ST_IDLE); reply_ptr = reply_buf; - reading_reply = 0; + reading_reply = false; bus_timeout = false; srq_asserted = false; @@ -442,7 +442,7 @@ static irqreturn_t macii_interrupt(int irq, void *arg) */ macii_state = reading; - reading_reply = 0; + reading_reply = false; reply_ptr = reply_buf; *reply_ptr = last_talk_cmd; reply_len = 1; @@ -456,7 +456,7 @@ static irqreturn_t macii_interrupt(int irq, void *arg) if (req->reply_expected) { macii_state = reading; - reading_reply = 1; + reading_reply = true; reply_ptr = req->reply; *reply_ptr = req->data[1]; reply_len = 1; @@ -466,7 +466,7 @@ static irqreturn_t macii_interrupt(int irq, void *arg) } else if ((req->data[1] & OP_MASK) == TALK) { macii_state = reading; - reading_reply = 0; + reading_reply = false; reply_ptr = reply_buf; *reply_ptr = req->data[1]; reply_len = 1; -- GitLab From 5c0c15a1953a7de2878d7e6f5711fd3322b11faa Mon Sep 17 00:00:00 2001 From: Finn Thain Date: Sun, 28 Jun 2020 14:23:12 +1000 Subject: [PATCH 0873/1476] macintosh/via-macii: Use unsigned type for autopoll_devs variable Signed-off-by: Finn Thain Tested-by: Stan Johnson Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/ca5be30ba745c08c2b7a1f0618f99c61b303e983.1593318192.git.fthain@telegraphics.com.au --- drivers/macintosh/via-macii.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/macintosh/via-macii.c b/drivers/macintosh/via-macii.c index e143ddb81de34..447273967e1e8 100644 --- a/drivers/macintosh/via-macii.c +++ b/drivers/macintosh/via-macii.c @@ -125,7 +125,7 @@ static bool srq_asserted; /* have to poll for the device that asserted it */ static u8 last_cmd; /* the most recent command byte transmitted */ static u8 last_talk_cmd; /* the most recent Talk command byte transmitted */ static u8 last_poll_cmd; /* the most recent Talk R0 command byte transmitted */ -static int autopoll_devs; /* bits set are device addresses to be polled */ +static unsigned int autopoll_devs; /* bits set are device addresses to poll */ /* Check for MacII style ADB */ static int macii_probe(void) @@ -291,7 +291,7 @@ static int macii_autopoll(int devs) local_irq_save(flags); /* bit 1 == device 1, and so on. */ - autopoll_devs = devs & 0xFFFE; + autopoll_devs = (unsigned int)devs & 0xFFFE; if (!current_req) { macii_queue_poll(); -- GitLab From 046ace8256489f32740da07de55a913ca09ce5cf Mon Sep 17 00:00:00 2001 From: Finn Thain Date: Sun, 28 Jun 2020 14:23:12 +1000 Subject: [PATCH 0874/1476] macintosh/via-macii: Use the stack for reset request storage The adb_request struct can be stored on the stack because the request is synchronous and is completed before the function returns. Signed-off-by: Finn Thain Tested-by: Stan Johnson Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/a40f80dde90991757007b6962c386a208c970586.1593318192.git.fthain@telegraphics.com.au --- drivers/macintosh/via-macii.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/macintosh/via-macii.c b/drivers/macintosh/via-macii.c index 447273967e1e8..2f9be4ec7d345 100644 --- a/drivers/macintosh/via-macii.c +++ b/drivers/macintosh/via-macii.c @@ -313,7 +313,7 @@ static void macii_poll(void) /* Reset the bus */ static int macii_reset_bus(void) { - static struct adb_request req; + struct adb_request req; /* Command = 0, Address = ignored */ adb_request(&req, NULL, ADBREQ_NOSEND, 1, ADB_BUSRESET); -- GitLab From 3327e58a04501e06aa531cdb4044aab214a6254a Mon Sep 17 00:00:00 2001 From: Finn Thain Date: Sun, 28 Jun 2020 14:23:12 +1000 Subject: [PATCH 0875/1476] macintosh/via-macii: Clarify definition of macii_init() The function prototype correctly specifies the 'static' storage class. Let the function definition match the declaration for better readability. Signed-off-by: Finn Thain Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/c025aed0b1506399b73ff1d1bfa40ed641fcb3e3.1593318192.git.fthain@telegraphics.com.au --- drivers/macintosh/via-macii.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/macintosh/via-macii.c b/drivers/macintosh/via-macii.c index 2f9be4ec7d345..060e03f2264bc 100644 --- a/drivers/macintosh/via-macii.c +++ b/drivers/macintosh/via-macii.c @@ -140,7 +140,7 @@ static int macii_probe(void) } /* Initialize the driver */ -int macii_init(void) +static int macii_init(void) { unsigned long flags; int err; -- GitLab From dbce456280857f329af9069af5e48a9b6ebad146 Mon Sep 17 00:00:00 2001 From: Srikar Dronamraju Date: Fri, 24 Jul 2020 16:28:09 +0530 Subject: [PATCH 0876/1476] powerpc/numa: Limit possible nodes to within num_possible_nodes MAX_NUMNODES is a theoretical maximum number of nodes thats is supported by the kernel. Device tree properties exposes the number of possible nodes on the current platform. The kernel would detected this and would use it for most of its resource allocations. If the platform now increases the nodes to over what was already exposed, then it may lead to inconsistencies. Hence limit it to the already exposed nodes. Suggested-by: Nathan Lynch Signed-off-by: Srikar Dronamraju Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20200724105809.24733-1-srikar@linux.vnet.ibm.com --- arch/powerpc/mm/numa.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/arch/powerpc/mm/numa.c b/arch/powerpc/mm/numa.c index e437a9ac49564..92938393fec6d 100644 --- a/arch/powerpc/mm/numa.c +++ b/arch/powerpc/mm/numa.c @@ -221,7 +221,8 @@ static void initialize_distance_lookup_table(int nid, } } -/* Returns nid in the range [0..MAX_NUMNODES-1], or -1 if no useful numa +/* + * Returns nid in the range [0..nr_node_ids], or -1 if no useful NUMA * info is found. */ static int associativity_to_nid(const __be32 *associativity) @@ -235,7 +236,7 @@ static int associativity_to_nid(const __be32 *associativity) nid = of_read_number(&associativity[min_common_depth], 1); /* POWER4 LPAR uses 0xffff as invalid node */ - if (nid == 0xffff || nid >= MAX_NUMNODES) + if (nid == 0xffff || nid >= nr_node_ids) nid = NUMA_NO_NODE; if (nid > 0 && @@ -448,7 +449,7 @@ static int of_drconf_to_nid_single(struct drmem_lmb *lmb) index = lmb->aa_index * aa.array_sz + min_common_depth - 1; nid = of_read_number(&aa.arrays[index], 1); - if (nid == 0xffff || nid >= MAX_NUMNODES) + if (nid == 0xffff || nid >= nr_node_ids) nid = default_nid; if (nid > 0) { -- GitLab From 20d444d06f97504d165b08558678b4737dcefb02 Mon Sep 17 00:00:00 2001 From: Nicholas Piggin Date: Fri, 24 Jul 2020 23:14:18 +1000 Subject: [PATCH 0877/1476] powerpc/pseries: Move some PAPR paravirt functions to their own file These functions will be used by the queued spinlock implementation, and may be useful elsewhere too, so move them out of spinlock.h. Signed-off-by: Nicholas Piggin Acked-by: Waiman Long Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20200724131423.1362108-2-npiggin@gmail.com --- arch/powerpc/include/asm/paravirt.h | 59 +++++++++++++++++++++++++++++ arch/powerpc/include/asm/spinlock.h | 24 +----------- arch/powerpc/lib/locks.c | 12 +++--- 3 files changed, 66 insertions(+), 29 deletions(-) create mode 100644 arch/powerpc/include/asm/paravirt.h diff --git a/arch/powerpc/include/asm/paravirt.h b/arch/powerpc/include/asm/paravirt.h new file mode 100644 index 0000000000000..339e8533464b1 --- /dev/null +++ b/arch/powerpc/include/asm/paravirt.h @@ -0,0 +1,59 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +#ifndef _ASM_POWERPC_PARAVIRT_H +#define _ASM_POWERPC_PARAVIRT_H + +#include +#include +#ifdef CONFIG_PPC64 +#include +#include +#endif + +#ifdef CONFIG_PPC_SPLPAR +DECLARE_STATIC_KEY_FALSE(shared_processor); + +static inline bool is_shared_processor(void) +{ + return static_branch_unlikely(&shared_processor); +} + +/* If bit 0 is set, the cpu has been preempted */ +static inline u32 yield_count_of(int cpu) +{ + __be32 yield_count = READ_ONCE(lppaca_of(cpu).yield_count); + return be32_to_cpu(yield_count); +} + +static inline void yield_to_preempted(int cpu, u32 yield_count) +{ + plpar_hcall_norets(H_CONFER, get_hard_smp_processor_id(cpu), yield_count); +} +#else +static inline bool is_shared_processor(void) +{ + return false; +} + +static inline u32 yield_count_of(int cpu) +{ + return 0; +} + +extern void ___bad_yield_to_preempted(void); +static inline void yield_to_preempted(int cpu, u32 yield_count) +{ + ___bad_yield_to_preempted(); /* This would be a bug */ +} +#endif + +#define vcpu_is_preempted vcpu_is_preempted +static inline bool vcpu_is_preempted(int cpu) +{ + if (!is_shared_processor()) + return false; + if (yield_count_of(cpu) & 1) + return true; + return false; +} + +#endif /* _ASM_POWERPC_PARAVIRT_H */ diff --git a/arch/powerpc/include/asm/spinlock.h b/arch/powerpc/include/asm/spinlock.h index 2d620896cdaee..79be9bb10bbb0 100644 --- a/arch/powerpc/include/asm/spinlock.h +++ b/arch/powerpc/include/asm/spinlock.h @@ -15,11 +15,10 @@ * * (the type definitions are in asm/spinlock_types.h) */ -#include #include +#include #ifdef CONFIG_PPC64 #include -#include #endif #include #include @@ -35,18 +34,6 @@ #define LOCK_TOKEN 1 #endif -#ifdef CONFIG_PPC_PSERIES -DECLARE_STATIC_KEY_FALSE(shared_processor); - -#define vcpu_is_preempted vcpu_is_preempted -static inline bool vcpu_is_preempted(int cpu) -{ - if (!static_branch_unlikely(&shared_processor)) - return false; - return !!(be32_to_cpu(lppaca_of(cpu).yield_count) & 1); -} -#endif - static __always_inline int arch_spin_value_unlocked(arch_spinlock_t lock) { return lock.slock == 0; @@ -110,15 +97,6 @@ static inline void splpar_spin_yield(arch_spinlock_t *lock) {}; static inline void splpar_rw_yield(arch_rwlock_t *lock) {}; #endif -static inline bool is_shared_processor(void) -{ -#ifdef CONFIG_PPC_SPLPAR - return static_branch_unlikely(&shared_processor); -#else - return false; -#endif -} - static inline void spin_yield(arch_spinlock_t *lock) { if (is_shared_processor()) diff --git a/arch/powerpc/lib/locks.c b/arch/powerpc/lib/locks.c index 6440d5943c003..04165b7a163fa 100644 --- a/arch/powerpc/lib/locks.c +++ b/arch/powerpc/lib/locks.c @@ -27,14 +27,14 @@ void splpar_spin_yield(arch_spinlock_t *lock) return; holder_cpu = lock_value & 0xffff; BUG_ON(holder_cpu >= NR_CPUS); - yield_count = be32_to_cpu(lppaca_of(holder_cpu).yield_count); + + yield_count = yield_count_of(holder_cpu); if ((yield_count & 1) == 0) return; /* virtual cpu is currently running */ rmb(); if (lock->slock != lock_value) return; /* something has changed */ - plpar_hcall_norets(H_CONFER, - get_hard_smp_processor_id(holder_cpu), yield_count); + yield_to_preempted(holder_cpu, yield_count); } EXPORT_SYMBOL_GPL(splpar_spin_yield); @@ -53,13 +53,13 @@ void splpar_rw_yield(arch_rwlock_t *rw) return; /* no write lock at present */ holder_cpu = lock_value & 0xffff; BUG_ON(holder_cpu >= NR_CPUS); - yield_count = be32_to_cpu(lppaca_of(holder_cpu).yield_count); + + yield_count = yield_count_of(holder_cpu); if ((yield_count & 1) == 0) return; /* virtual cpu is currently running */ rmb(); if (rw->lock != lock_value) return; /* something has changed */ - plpar_hcall_norets(H_CONFER, - get_hard_smp_processor_id(holder_cpu), yield_count); + yield_to_preempted(holder_cpu, yield_count); } #endif -- GitLab From 12d0b9d6c843e7dbe739ebefcf16c7e4a45e4e78 Mon Sep 17 00:00:00 2001 From: Nicholas Piggin Date: Fri, 24 Jul 2020 23:14:19 +1000 Subject: [PATCH 0878/1476] powerpc: Move spinlock implementation to simple_spinlock To prepare for queued spinlocks. This is a simple rename except to update preprocessor guard name and a file reference. Signed-off-by: Nicholas Piggin Acked-by: Waiman Long Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20200724131423.1362108-3-npiggin@gmail.com --- arch/powerpc/include/asm/simple_spinlock.h | 288 ++++++++++++++++++ .../include/asm/simple_spinlock_types.h | 21 ++ arch/powerpc/include/asm/spinlock.h | 285 +---------------- arch/powerpc/include/asm/spinlock_types.h | 12 +- 4 files changed, 311 insertions(+), 295 deletions(-) create mode 100644 arch/powerpc/include/asm/simple_spinlock.h create mode 100644 arch/powerpc/include/asm/simple_spinlock_types.h diff --git a/arch/powerpc/include/asm/simple_spinlock.h b/arch/powerpc/include/asm/simple_spinlock.h new file mode 100644 index 0000000000000..9c3c305343331 --- /dev/null +++ b/arch/powerpc/include/asm/simple_spinlock.h @@ -0,0 +1,288 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +#ifndef _ASM_POWERPC_SIMPLE_SPINLOCK_H +#define _ASM_POWERPC_SIMPLE_SPINLOCK_H + +/* + * Simple spin lock operations. + * + * Copyright (C) 2001-2004 Paul Mackerras , IBM + * Copyright (C) 2001 Anton Blanchard , IBM + * Copyright (C) 2002 Dave Engebretsen , IBM + * Rework to support virtual processors + * + * Type of int is used as a full 64b word is not necessary. + * + * (the type definitions are in asm/simple_spinlock_types.h) + */ +#include +#include +#include +#include +#include + +#ifdef CONFIG_PPC64 +/* use 0x800000yy when locked, where yy == CPU number */ +#ifdef __BIG_ENDIAN__ +#define LOCK_TOKEN (*(u32 *)(&get_paca()->lock_token)) +#else +#define LOCK_TOKEN (*(u32 *)(&get_paca()->paca_index)) +#endif +#else +#define LOCK_TOKEN 1 +#endif + +static __always_inline int arch_spin_value_unlocked(arch_spinlock_t lock) +{ + return lock.slock == 0; +} + +static inline int arch_spin_is_locked(arch_spinlock_t *lock) +{ + smp_mb(); + return !arch_spin_value_unlocked(*lock); +} + +/* + * This returns the old value in the lock, so we succeeded + * in getting the lock if the return value is 0. + */ +static inline unsigned long __arch_spin_trylock(arch_spinlock_t *lock) +{ + unsigned long tmp, token; + + token = LOCK_TOKEN; + __asm__ __volatile__( +"1: " PPC_LWARX(%0,0,%2,1) "\n\ + cmpwi 0,%0,0\n\ + bne- 2f\n\ + stwcx. %1,0,%2\n\ + bne- 1b\n" + PPC_ACQUIRE_BARRIER +"2:" + : "=&r" (tmp) + : "r" (token), "r" (&lock->slock) + : "cr0", "memory"); + + return tmp; +} + +static inline int arch_spin_trylock(arch_spinlock_t *lock) +{ + return __arch_spin_trylock(lock) == 0; +} + +/* + * On a system with shared processors (that is, where a physical + * processor is multiplexed between several virtual processors), + * there is no point spinning on a lock if the holder of the lock + * isn't currently scheduled on a physical processor. Instead + * we detect this situation and ask the hypervisor to give the + * rest of our timeslice to the lock holder. + * + * So that we can tell which virtual processor is holding a lock, + * we put 0x80000000 | smp_processor_id() in the lock when it is + * held. Conveniently, we have a word in the paca that holds this + * value. + */ + +#if defined(CONFIG_PPC_SPLPAR) +/* We only yield to the hypervisor if we are in shared processor mode */ +void splpar_spin_yield(arch_spinlock_t *lock); +void splpar_rw_yield(arch_rwlock_t *lock); +#else /* SPLPAR */ +static inline void splpar_spin_yield(arch_spinlock_t *lock) {}; +static inline void splpar_rw_yield(arch_rwlock_t *lock) {}; +#endif + +static inline void spin_yield(arch_spinlock_t *lock) +{ + if (is_shared_processor()) + splpar_spin_yield(lock); + else + barrier(); +} + +static inline void rw_yield(arch_rwlock_t *lock) +{ + if (is_shared_processor()) + splpar_rw_yield(lock); + else + barrier(); +} + +static inline void arch_spin_lock(arch_spinlock_t *lock) +{ + while (1) { + if (likely(__arch_spin_trylock(lock) == 0)) + break; + do { + HMT_low(); + if (is_shared_processor()) + splpar_spin_yield(lock); + } while (unlikely(lock->slock != 0)); + HMT_medium(); + } +} + +static inline +void arch_spin_lock_flags(arch_spinlock_t *lock, unsigned long flags) +{ + unsigned long flags_dis; + + while (1) { + if (likely(__arch_spin_trylock(lock) == 0)) + break; + local_save_flags(flags_dis); + local_irq_restore(flags); + do { + HMT_low(); + if (is_shared_processor()) + splpar_spin_yield(lock); + } while (unlikely(lock->slock != 0)); + HMT_medium(); + local_irq_restore(flags_dis); + } +} +#define arch_spin_lock_flags arch_spin_lock_flags + +static inline void arch_spin_unlock(arch_spinlock_t *lock) +{ + __asm__ __volatile__("# arch_spin_unlock\n\t" + PPC_RELEASE_BARRIER: : :"memory"); + lock->slock = 0; +} + +/* + * Read-write spinlocks, allowing multiple readers + * but only one writer. + * + * NOTE! it is quite common to have readers in interrupts + * but no interrupt writers. For those circumstances we + * can "mix" irq-safe locks - any writer needs to get a + * irq-safe write-lock, but readers can get non-irqsafe + * read-locks. + */ + +#ifdef CONFIG_PPC64 +#define __DO_SIGN_EXTEND "extsw %0,%0\n" +#define WRLOCK_TOKEN LOCK_TOKEN /* it's negative */ +#else +#define __DO_SIGN_EXTEND +#define WRLOCK_TOKEN (-1) +#endif + +/* + * This returns the old value in the lock + 1, + * so we got a read lock if the return value is > 0. + */ +static inline long __arch_read_trylock(arch_rwlock_t *rw) +{ + long tmp; + + __asm__ __volatile__( +"1: " PPC_LWARX(%0,0,%1,1) "\n" + __DO_SIGN_EXTEND +" addic. %0,%0,1\n\ + ble- 2f\n" +" stwcx. %0,0,%1\n\ + bne- 1b\n" + PPC_ACQUIRE_BARRIER +"2:" : "=&r" (tmp) + : "r" (&rw->lock) + : "cr0", "xer", "memory"); + + return tmp; +} + +/* + * This returns the old value in the lock, + * so we got the write lock if the return value is 0. + */ +static inline long __arch_write_trylock(arch_rwlock_t *rw) +{ + long tmp, token; + + token = WRLOCK_TOKEN; + __asm__ __volatile__( +"1: " PPC_LWARX(%0,0,%2,1) "\n\ + cmpwi 0,%0,0\n\ + bne- 2f\n" +" stwcx. %1,0,%2\n\ + bne- 1b\n" + PPC_ACQUIRE_BARRIER +"2:" : "=&r" (tmp) + : "r" (token), "r" (&rw->lock) + : "cr0", "memory"); + + return tmp; +} + +static inline void arch_read_lock(arch_rwlock_t *rw) +{ + while (1) { + if (likely(__arch_read_trylock(rw) > 0)) + break; + do { + HMT_low(); + if (is_shared_processor()) + splpar_rw_yield(rw); + } while (unlikely(rw->lock < 0)); + HMT_medium(); + } +} + +static inline void arch_write_lock(arch_rwlock_t *rw) +{ + while (1) { + if (likely(__arch_write_trylock(rw) == 0)) + break; + do { + HMT_low(); + if (is_shared_processor()) + splpar_rw_yield(rw); + } while (unlikely(rw->lock != 0)); + HMT_medium(); + } +} + +static inline int arch_read_trylock(arch_rwlock_t *rw) +{ + return __arch_read_trylock(rw) > 0; +} + +static inline int arch_write_trylock(arch_rwlock_t *rw) +{ + return __arch_write_trylock(rw) == 0; +} + +static inline void arch_read_unlock(arch_rwlock_t *rw) +{ + long tmp; + + __asm__ __volatile__( + "# read_unlock\n\t" + PPC_RELEASE_BARRIER +"1: lwarx %0,0,%1\n\ + addic %0,%0,-1\n" +" stwcx. %0,0,%1\n\ + bne- 1b" + : "=&r"(tmp) + : "r"(&rw->lock) + : "cr0", "xer", "memory"); +} + +static inline void arch_write_unlock(arch_rwlock_t *rw) +{ + __asm__ __volatile__("# write_unlock\n\t" + PPC_RELEASE_BARRIER: : :"memory"); + rw->lock = 0; +} + +#define arch_spin_relax(lock) spin_yield(lock) +#define arch_read_relax(lock) rw_yield(lock) +#define arch_write_relax(lock) rw_yield(lock) + +/* See include/linux/spinlock.h */ +#define smp_mb__after_spinlock() smp_mb() + +#endif /* _ASM_POWERPC_SIMPLE_SPINLOCK_H */ diff --git a/arch/powerpc/include/asm/simple_spinlock_types.h b/arch/powerpc/include/asm/simple_spinlock_types.h new file mode 100644 index 0000000000000..0f3cdd8faa959 --- /dev/null +++ b/arch/powerpc/include/asm/simple_spinlock_types.h @@ -0,0 +1,21 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _ASM_POWERPC_SIMPLE_SPINLOCK_TYPES_H +#define _ASM_POWERPC_SIMPLE_SPINLOCK_TYPES_H + +#ifndef __LINUX_SPINLOCK_TYPES_H +# error "please don't include this file directly" +#endif + +typedef struct { + volatile unsigned int slock; +} arch_spinlock_t; + +#define __ARCH_SPIN_LOCK_UNLOCKED { 0 } + +typedef struct { + volatile signed int lock; +} arch_rwlock_t; + +#define __ARCH_RW_LOCK_UNLOCKED { 0 } + +#endif /* _ASM_POWERPC_SIMPLE_SPINLOCK_TYPES_H */ diff --git a/arch/powerpc/include/asm/spinlock.h b/arch/powerpc/include/asm/spinlock.h index 79be9bb10bbb0..21357fe05fe00 100644 --- a/arch/powerpc/include/asm/spinlock.h +++ b/arch/powerpc/include/asm/spinlock.h @@ -3,290 +3,7 @@ #define __ASM_SPINLOCK_H #ifdef __KERNEL__ -/* - * Simple spin lock operations. - * - * Copyright (C) 2001-2004 Paul Mackerras , IBM - * Copyright (C) 2001 Anton Blanchard , IBM - * Copyright (C) 2002 Dave Engebretsen , IBM - * Rework to support virtual processors - * - * Type of int is used as a full 64b word is not necessary. - * - * (the type definitions are in asm/spinlock_types.h) - */ -#include -#include -#ifdef CONFIG_PPC64 -#include -#endif -#include -#include - -#ifdef CONFIG_PPC64 -/* use 0x800000yy when locked, where yy == CPU number */ -#ifdef __BIG_ENDIAN__ -#define LOCK_TOKEN (*(u32 *)(&get_paca()->lock_token)) -#else -#define LOCK_TOKEN (*(u32 *)(&get_paca()->paca_index)) -#endif -#else -#define LOCK_TOKEN 1 -#endif - -static __always_inline int arch_spin_value_unlocked(arch_spinlock_t lock) -{ - return lock.slock == 0; -} - -static inline int arch_spin_is_locked(arch_spinlock_t *lock) -{ - smp_mb(); - return !arch_spin_value_unlocked(*lock); -} - -/* - * This returns the old value in the lock, so we succeeded - * in getting the lock if the return value is 0. - */ -static inline unsigned long __arch_spin_trylock(arch_spinlock_t *lock) -{ - unsigned long tmp, token; - - token = LOCK_TOKEN; - __asm__ __volatile__( -"1: " PPC_LWARX(%0,0,%2,1) "\n\ - cmpwi 0,%0,0\n\ - bne- 2f\n\ - stwcx. %1,0,%2\n\ - bne- 1b\n" - PPC_ACQUIRE_BARRIER -"2:" - : "=&r" (tmp) - : "r" (token), "r" (&lock->slock) - : "cr0", "memory"); - - return tmp; -} - -static inline int arch_spin_trylock(arch_spinlock_t *lock) -{ - return __arch_spin_trylock(lock) == 0; -} - -/* - * On a system with shared processors (that is, where a physical - * processor is multiplexed between several virtual processors), - * there is no point spinning on a lock if the holder of the lock - * isn't currently scheduled on a physical processor. Instead - * we detect this situation and ask the hypervisor to give the - * rest of our timeslice to the lock holder. - * - * So that we can tell which virtual processor is holding a lock, - * we put 0x80000000 | smp_processor_id() in the lock when it is - * held. Conveniently, we have a word in the paca that holds this - * value. - */ - -#if defined(CONFIG_PPC_SPLPAR) -/* We only yield to the hypervisor if we are in shared processor mode */ -void splpar_spin_yield(arch_spinlock_t *lock); -void splpar_rw_yield(arch_rwlock_t *lock); -#else /* SPLPAR */ -static inline void splpar_spin_yield(arch_spinlock_t *lock) {}; -static inline void splpar_rw_yield(arch_rwlock_t *lock) {}; -#endif - -static inline void spin_yield(arch_spinlock_t *lock) -{ - if (is_shared_processor()) - splpar_spin_yield(lock); - else - barrier(); -} - -static inline void rw_yield(arch_rwlock_t *lock) -{ - if (is_shared_processor()) - splpar_rw_yield(lock); - else - barrier(); -} - -static inline void arch_spin_lock(arch_spinlock_t *lock) -{ - while (1) { - if (likely(__arch_spin_trylock(lock) == 0)) - break; - do { - HMT_low(); - if (is_shared_processor()) - splpar_spin_yield(lock); - } while (unlikely(lock->slock != 0)); - HMT_medium(); - } -} - -static inline -void arch_spin_lock_flags(arch_spinlock_t *lock, unsigned long flags) -{ - unsigned long flags_dis; - - while (1) { - if (likely(__arch_spin_trylock(lock) == 0)) - break; - local_save_flags(flags_dis); - local_irq_restore(flags); - do { - HMT_low(); - if (is_shared_processor()) - splpar_spin_yield(lock); - } while (unlikely(lock->slock != 0)); - HMT_medium(); - local_irq_restore(flags_dis); - } -} -#define arch_spin_lock_flags arch_spin_lock_flags - -static inline void arch_spin_unlock(arch_spinlock_t *lock) -{ - __asm__ __volatile__("# arch_spin_unlock\n\t" - PPC_RELEASE_BARRIER: : :"memory"); - lock->slock = 0; -} - -/* - * Read-write spinlocks, allowing multiple readers - * but only one writer. - * - * NOTE! it is quite common to have readers in interrupts - * but no interrupt writers. For those circumstances we - * can "mix" irq-safe locks - any writer needs to get a - * irq-safe write-lock, but readers can get non-irqsafe - * read-locks. - */ - -#ifdef CONFIG_PPC64 -#define __DO_SIGN_EXTEND "extsw %0,%0\n" -#define WRLOCK_TOKEN LOCK_TOKEN /* it's negative */ -#else -#define __DO_SIGN_EXTEND -#define WRLOCK_TOKEN (-1) -#endif - -/* - * This returns the old value in the lock + 1, - * so we got a read lock if the return value is > 0. - */ -static inline long __arch_read_trylock(arch_rwlock_t *rw) -{ - long tmp; - - __asm__ __volatile__( -"1: " PPC_LWARX(%0,0,%1,1) "\n" - __DO_SIGN_EXTEND -" addic. %0,%0,1\n\ - ble- 2f\n" -" stwcx. %0,0,%1\n\ - bne- 1b\n" - PPC_ACQUIRE_BARRIER -"2:" : "=&r" (tmp) - : "r" (&rw->lock) - : "cr0", "xer", "memory"); - - return tmp; -} - -/* - * This returns the old value in the lock, - * so we got the write lock if the return value is 0. - */ -static inline long __arch_write_trylock(arch_rwlock_t *rw) -{ - long tmp, token; - - token = WRLOCK_TOKEN; - __asm__ __volatile__( -"1: " PPC_LWARX(%0,0,%2,1) "\n\ - cmpwi 0,%0,0\n\ - bne- 2f\n" -" stwcx. %1,0,%2\n\ - bne- 1b\n" - PPC_ACQUIRE_BARRIER -"2:" : "=&r" (tmp) - : "r" (token), "r" (&rw->lock) - : "cr0", "memory"); - - return tmp; -} - -static inline void arch_read_lock(arch_rwlock_t *rw) -{ - while (1) { - if (likely(__arch_read_trylock(rw) > 0)) - break; - do { - HMT_low(); - if (is_shared_processor()) - splpar_rw_yield(rw); - } while (unlikely(rw->lock < 0)); - HMT_medium(); - } -} - -static inline void arch_write_lock(arch_rwlock_t *rw) -{ - while (1) { - if (likely(__arch_write_trylock(rw) == 0)) - break; - do { - HMT_low(); - if (is_shared_processor()) - splpar_rw_yield(rw); - } while (unlikely(rw->lock != 0)); - HMT_medium(); - } -} - -static inline int arch_read_trylock(arch_rwlock_t *rw) -{ - return __arch_read_trylock(rw) > 0; -} - -static inline int arch_write_trylock(arch_rwlock_t *rw) -{ - return __arch_write_trylock(rw) == 0; -} - -static inline void arch_read_unlock(arch_rwlock_t *rw) -{ - long tmp; - - __asm__ __volatile__( - "# read_unlock\n\t" - PPC_RELEASE_BARRIER -"1: lwarx %0,0,%1\n\ - addic %0,%0,-1\n" -" stwcx. %0,0,%1\n\ - bne- 1b" - : "=&r"(tmp) - : "r"(&rw->lock) - : "cr0", "xer", "memory"); -} - -static inline void arch_write_unlock(arch_rwlock_t *rw) -{ - __asm__ __volatile__("# write_unlock\n\t" - PPC_RELEASE_BARRIER: : :"memory"); - rw->lock = 0; -} - -#define arch_spin_relax(lock) spin_yield(lock) -#define arch_read_relax(lock) rw_yield(lock) -#define arch_write_relax(lock) rw_yield(lock) - -/* See include/linux/spinlock.h */ -#define smp_mb__after_spinlock() smp_mb() +#include #endif /* __KERNEL__ */ #endif /* __ASM_SPINLOCK_H */ diff --git a/arch/powerpc/include/asm/spinlock_types.h b/arch/powerpc/include/asm/spinlock_types.h index 87adaf13b7e84..3906f52dae657 100644 --- a/arch/powerpc/include/asm/spinlock_types.h +++ b/arch/powerpc/include/asm/spinlock_types.h @@ -6,16 +6,6 @@ # error "please don't include this file directly" #endif -typedef struct { - volatile unsigned int slock; -} arch_spinlock_t; - -#define __ARCH_SPIN_LOCK_UNLOCKED { 0 } - -typedef struct { - volatile signed int lock; -} arch_rwlock_t; - -#define __ARCH_RW_LOCK_UNLOCKED { 0 } +#include #endif -- GitLab From aa65ff6b18e0366db1790609956a4ac7308c5668 Mon Sep 17 00:00:00 2001 From: Nicholas Piggin Date: Fri, 24 Jul 2020 23:14:20 +1000 Subject: [PATCH 0879/1476] powerpc/64s: Implement queued spinlocks and rwlocks These have shown significantly improved performance and fairness when spinlock contention is moderate to high on very large systems. With this series including subsequent patches, on a 16 socket 1536 thread POWER9, a stress test such as same-file open/close from all CPUs gets big speedups, 11620op/s aggregate with simple spinlocks vs 384158op/s (33x faster), where the difference in throughput between the fastest and slowest thread goes from 7x to 1.4x. Thanks to the fast path being identical in terms of atomics and barriers (after a subsequent optimisation patch), single threaded performance is not changed (no measurable difference). On smaller systems, performance and fairness seems to be generally improved. Using dbench on tmpfs as a test (that starts to run into kernel spinlock contention), a 2-socket OpenPOWER POWER9 system was tested with bare metal and KVM guest configurations. Results can be found here: https://github.com/linuxppc/issues/issues/305#issuecomment-663487453 Observations are: - Queued spinlocks are equal when contention is insignificant, as expected and as measured with microbenchmarks. - When there is contention, on bare metal queued spinlocks have better throughput and max latency at all points. - When virtualised, queued spinlocks are slightly worse approaching peak throughput, but significantly better throughput and max latency at all points beyond peak, until queued spinlock maximum latency rises when clients are 2x vCPUs. The regressions haven't been analysed very well yet, there are a lot of things that can be tuned, particularly the paravirtualised locking, but the numbers already look like a good net win even on relatively small systems. Signed-off-by: Nicholas Piggin Acked-by: Peter Zijlstra (Intel) Acked-by: Waiman Long Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20200724131423.1362108-4-npiggin@gmail.com --- arch/powerpc/Kconfig | 15 ++++++++++++++ arch/powerpc/include/asm/Kbuild | 1 + arch/powerpc/include/asm/qspinlock.h | 25 +++++++++++++++++++++++ arch/powerpc/include/asm/spinlock.h | 5 +++++ arch/powerpc/include/asm/spinlock_types.h | 5 +++++ arch/powerpc/lib/Makefile | 3 +++ include/asm-generic/qspinlock.h | 2 ++ 7 files changed, 56 insertions(+) create mode 100644 arch/powerpc/include/asm/qspinlock.h diff --git a/arch/powerpc/Kconfig b/arch/powerpc/Kconfig index 81c0dee1cbffe..a751edacf4bc0 100644 --- a/arch/powerpc/Kconfig +++ b/arch/powerpc/Kconfig @@ -146,6 +146,8 @@ config PPC select ARCH_SUPPORTS_ATOMIC_RMW select ARCH_USE_BUILTIN_BSWAP select ARCH_USE_CMPXCHG_LOCKREF if PPC64 + select ARCH_USE_QUEUED_RWLOCKS if PPC_QUEUED_SPINLOCKS + select ARCH_USE_QUEUED_SPINLOCKS if PPC_QUEUED_SPINLOCKS select ARCH_WANT_IPC_PARSE_VERSION select ARCH_WEAK_RELEASE_ACQUIRE select BINFMT_ELF @@ -491,6 +493,19 @@ config HOTPLUG_CPU Say N if you are unsure. +config PPC_QUEUED_SPINLOCKS + bool "Queued spinlocks" + depends on SMP + help + Say Y here to use queued spinlocks which give better scalability and + fairness on large SMP and NUMA systems without harming single threaded + performance. + + This option is currently experimental, the code is more complex and + less tested so it defaults to "N" for the moment. + + If unsure, say "N". + config ARCH_CPU_PROBE_RELEASE def_bool y depends on HOTPLUG_CPU diff --git a/arch/powerpc/include/asm/Kbuild b/arch/powerpc/include/asm/Kbuild index dadbcf3a0b1e7..27c2268dfd6c0 100644 --- a/arch/powerpc/include/asm/Kbuild +++ b/arch/powerpc/include/asm/Kbuild @@ -6,5 +6,6 @@ generated-y += syscall_table_spu.h generic-y += export.h generic-y += local64.h generic-y += mcs_spinlock.h +generic-y += qrwlock.h generic-y += vtime.h generic-y += early_ioremap.h diff --git a/arch/powerpc/include/asm/qspinlock.h b/arch/powerpc/include/asm/qspinlock.h new file mode 100644 index 0000000000000..c49e33e24edd4 --- /dev/null +++ b/arch/powerpc/include/asm/qspinlock.h @@ -0,0 +1,25 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _ASM_POWERPC_QSPINLOCK_H +#define _ASM_POWERPC_QSPINLOCK_H + +#include + +#define _Q_PENDING_LOOPS (1 << 9) /* not tuned */ + +#define smp_mb__after_spinlock() smp_mb() + +static __always_inline int queued_spin_is_locked(struct qspinlock *lock) +{ + /* + * This barrier was added to simple spinlocks by commit 51d7d5205d338, + * but it should now be possible to remove it, asm arm64 has done with + * commit c6f5d02b6a0f. + */ + smp_mb(); + return atomic_read(&lock->val); +} +#define queued_spin_is_locked queued_spin_is_locked + +#include + +#endif /* _ASM_POWERPC_QSPINLOCK_H */ diff --git a/arch/powerpc/include/asm/spinlock.h b/arch/powerpc/include/asm/spinlock.h index 21357fe05fe00..434615f1d7610 100644 --- a/arch/powerpc/include/asm/spinlock.h +++ b/arch/powerpc/include/asm/spinlock.h @@ -3,7 +3,12 @@ #define __ASM_SPINLOCK_H #ifdef __KERNEL__ +#ifdef CONFIG_PPC_QUEUED_SPINLOCKS +#include +#include +#else #include +#endif #endif /* __KERNEL__ */ #endif /* __ASM_SPINLOCK_H */ diff --git a/arch/powerpc/include/asm/spinlock_types.h b/arch/powerpc/include/asm/spinlock_types.h index 3906f52dae657..c5d742f18021d 100644 --- a/arch/powerpc/include/asm/spinlock_types.h +++ b/arch/powerpc/include/asm/spinlock_types.h @@ -6,6 +6,11 @@ # error "please don't include this file directly" #endif +#ifdef CONFIG_PPC_QUEUED_SPINLOCKS +#include +#include +#else #include +#endif #endif diff --git a/arch/powerpc/lib/Makefile b/arch/powerpc/lib/Makefile index 5e994cda8e401..d66a645503ebd 100644 --- a/arch/powerpc/lib/Makefile +++ b/arch/powerpc/lib/Makefile @@ -41,7 +41,10 @@ obj-$(CONFIG_PPC_BOOK3S_64) += copyuser_power7.o copypage_power7.o \ obj64-y += copypage_64.o copyuser_64.o mem_64.o hweight_64.o \ memcpy_64.o memcpy_mcsafe_64.o +ifndef CONFIG_PPC_QUEUED_SPINLOCKS obj64-$(CONFIG_SMP) += locks.o +endif + obj64-$(CONFIG_ALTIVEC) += vmx-helper.o obj64-$(CONFIG_KPROBES_SANITY_TEST) += test_emulate_step.o \ test_emulate_step_exec_instr.o diff --git a/include/asm-generic/qspinlock.h b/include/asm-generic/qspinlock.h index fde943d180e03..fb0a814d4395a 100644 --- a/include/asm-generic/qspinlock.h +++ b/include/asm-generic/qspinlock.h @@ -12,6 +12,7 @@ #include +#ifndef queued_spin_is_locked /** * queued_spin_is_locked - is the spinlock locked? * @lock: Pointer to queued spinlock structure @@ -25,6 +26,7 @@ static __always_inline int queued_spin_is_locked(struct qspinlock *lock) */ return atomic_read(&lock->val); } +#endif /** * queued_spin_value_unlocked - is the spinlock structure unlocked? -- GitLab From 20c0e8269e9d515e677670902c7e1cc0209d6ad9 Mon Sep 17 00:00:00 2001 From: Nicholas Piggin Date: Fri, 24 Jul 2020 23:14:21 +1000 Subject: [PATCH 0880/1476] powerpc/pseries: Implement paravirt qspinlocks for SPLPAR This implements the generic paravirt qspinlocks using H_PROD and H_CONFER to kick and wait. This uses an un-directed yield to any CPU rather than the directed yield to a pre-empted lock holder that paravirtualised simple spinlocks use, that requires no kick hcall. This is something that could be investigated and improved in future. Performance results can be found in the commit which added queued spinlocks. Signed-off-by: Nicholas Piggin Acked-by: Peter Zijlstra (Intel) Acked-by: Waiman Long Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20200724131423.1362108-5-npiggin@gmail.com --- arch/powerpc/include/asm/paravirt.h | 28 ++++++++ arch/powerpc/include/asm/qspinlock.h | 66 +++++++++++++++++++ arch/powerpc/include/asm/qspinlock_paravirt.h | 7 ++ arch/powerpc/include/asm/spinlock.h | 4 ++ arch/powerpc/platforms/pseries/Kconfig | 9 ++- arch/powerpc/platforms/pseries/setup.c | 4 +- include/asm-generic/qspinlock.h | 2 + 7 files changed, 118 insertions(+), 2 deletions(-) create mode 100644 arch/powerpc/include/asm/qspinlock_paravirt.h diff --git a/arch/powerpc/include/asm/paravirt.h b/arch/powerpc/include/asm/paravirt.h index 339e8533464b1..9362c94fe3aa0 100644 --- a/arch/powerpc/include/asm/paravirt.h +++ b/arch/powerpc/include/asm/paravirt.h @@ -28,6 +28,16 @@ static inline void yield_to_preempted(int cpu, u32 yield_count) { plpar_hcall_norets(H_CONFER, get_hard_smp_processor_id(cpu), yield_count); } + +static inline void prod_cpu(int cpu) +{ + plpar_hcall_norets(H_PROD, get_hard_smp_processor_id(cpu)); +} + +static inline void yield_to_any(void) +{ + plpar_hcall_norets(H_CONFER, -1, 0); +} #else static inline bool is_shared_processor(void) { @@ -44,6 +54,19 @@ static inline void yield_to_preempted(int cpu, u32 yield_count) { ___bad_yield_to_preempted(); /* This would be a bug */ } + +extern void ___bad_yield_to_any(void); +static inline void yield_to_any(void) +{ + ___bad_yield_to_any(); /* This would be a bug */ +} + +extern void ___bad_prod_cpu(void); +static inline void prod_cpu(int cpu) +{ + ___bad_prod_cpu(); /* This would be a bug */ +} + #endif #define vcpu_is_preempted vcpu_is_preempted @@ -56,4 +79,9 @@ static inline bool vcpu_is_preempted(int cpu) return false; } +static inline bool pv_is_native_spin_unlock(void) +{ + return !is_shared_processor(); +} + #endif /* _ASM_POWERPC_PARAVIRT_H */ diff --git a/arch/powerpc/include/asm/qspinlock.h b/arch/powerpc/include/asm/qspinlock.h index c49e33e24edd4..f5066f00a08c1 100644 --- a/arch/powerpc/include/asm/qspinlock.h +++ b/arch/powerpc/include/asm/qspinlock.h @@ -3,9 +3,47 @@ #define _ASM_POWERPC_QSPINLOCK_H #include +#include #define _Q_PENDING_LOOPS (1 << 9) /* not tuned */ +#ifdef CONFIG_PARAVIRT_SPINLOCKS +extern void native_queued_spin_lock_slowpath(struct qspinlock *lock, u32 val); +extern void __pv_queued_spin_lock_slowpath(struct qspinlock *lock, u32 val); +extern void __pv_queued_spin_unlock(struct qspinlock *lock); + +static __always_inline void queued_spin_lock_slowpath(struct qspinlock *lock, u32 val) +{ + if (!is_shared_processor()) + native_queued_spin_lock_slowpath(lock, val); + else + __pv_queued_spin_lock_slowpath(lock, val); +} + +#define queued_spin_unlock queued_spin_unlock +static inline void queued_spin_unlock(struct qspinlock *lock) +{ + if (!is_shared_processor()) + smp_store_release(&lock->locked, 0); + else + __pv_queued_spin_unlock(lock); +} + +#else +extern void queued_spin_lock_slowpath(struct qspinlock *lock, u32 val); +#endif + +static __always_inline void queued_spin_lock(struct qspinlock *lock) +{ + u32 val = 0; + + if (likely(atomic_try_cmpxchg_acquire(&lock->val, &val, _Q_LOCKED_VAL))) + return; + + queued_spin_lock_slowpath(lock, val); +} +#define queued_spin_lock queued_spin_lock + #define smp_mb__after_spinlock() smp_mb() static __always_inline int queued_spin_is_locked(struct qspinlock *lock) @@ -20,6 +58,34 @@ static __always_inline int queued_spin_is_locked(struct qspinlock *lock) } #define queued_spin_is_locked queued_spin_is_locked +#ifdef CONFIG_PARAVIRT_SPINLOCKS +#define SPIN_THRESHOLD (1<<15) /* not tuned */ + +static __always_inline void pv_wait(u8 *ptr, u8 val) +{ + if (*ptr != val) + return; + yield_to_any(); + /* + * We could pass in a CPU here if waiting in the queue and yield to + * the previous CPU in the queue. + */ +} + +static __always_inline void pv_kick(int cpu) +{ + prod_cpu(cpu); +} + +extern void __pv_init_lock_hash(void); + +static inline void pv_spinlocks_init(void) +{ + __pv_init_lock_hash(); +} + +#endif + #include #endif /* _ASM_POWERPC_QSPINLOCK_H */ diff --git a/arch/powerpc/include/asm/qspinlock_paravirt.h b/arch/powerpc/include/asm/qspinlock_paravirt.h new file mode 100644 index 0000000000000..6b60e7736a47c --- /dev/null +++ b/arch/powerpc/include/asm/qspinlock_paravirt.h @@ -0,0 +1,7 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +#ifndef _ASM_POWERPC_QSPINLOCK_PARAVIRT_H +#define _ASM_POWERPC_QSPINLOCK_PARAVIRT_H + +EXPORT_SYMBOL(__pv_queued_spin_unlock); + +#endif /* _ASM_POWERPC_QSPINLOCK_PARAVIRT_H */ diff --git a/arch/powerpc/include/asm/spinlock.h b/arch/powerpc/include/asm/spinlock.h index 434615f1d7610..6ec72282888dc 100644 --- a/arch/powerpc/include/asm/spinlock.h +++ b/arch/powerpc/include/asm/spinlock.h @@ -10,5 +10,9 @@ #include #endif +#ifndef CONFIG_PARAVIRT_SPINLOCKS +static inline void pv_spinlocks_init(void) { } +#endif + #endif /* __KERNEL__ */ #endif /* __ASM_SPINLOCK_H */ diff --git a/arch/powerpc/platforms/pseries/Kconfig b/arch/powerpc/platforms/pseries/Kconfig index 24c18362e5eaa..5e037df2a3a17 100644 --- a/arch/powerpc/platforms/pseries/Kconfig +++ b/arch/powerpc/platforms/pseries/Kconfig @@ -25,15 +25,22 @@ config PPC_PSERIES select SWIOTLB default y +config PARAVIRT_SPINLOCKS + bool + config PPC_SPLPAR - depends on PPC_PSERIES bool "Support for shared-processor logical partitions" + depends on PPC_PSERIES + select PARAVIRT_SPINLOCKS if PPC_QUEUED_SPINLOCKS + default y help Enabling this option will make the kernel run more efficiently on logically-partitioned pSeries systems which use shared processors, that is, which share physical processors between two or more partitions. + Say Y if you are unsure. + config DTL bool "Dispatch Trace Log" depends on PPC_SPLPAR && DEBUG_FS diff --git a/arch/powerpc/platforms/pseries/setup.c b/arch/powerpc/platforms/pseries/setup.c index ba34eb23e8f53..e29c9bf0a3b98 100644 --- a/arch/powerpc/platforms/pseries/setup.c +++ b/arch/powerpc/platforms/pseries/setup.c @@ -773,8 +773,10 @@ static void __init pSeries_setup_arch(void) if (firmware_has_feature(FW_FEATURE_LPAR)) { vpa_init(boot_cpuid); - if (lppaca_shared_proc(get_lppaca())) + if (lppaca_shared_proc(get_lppaca())) { static_branch_enable(&shared_processor); + pv_spinlocks_init(); + } ppc_md.power_save = pseries_lpar_idle; ppc_md.enable_pmcs = pseries_lpar_enable_pmcs; diff --git a/include/asm-generic/qspinlock.h b/include/asm-generic/qspinlock.h index fb0a814d4395a..38ca14e79a860 100644 --- a/include/asm-generic/qspinlock.h +++ b/include/asm-generic/qspinlock.h @@ -69,6 +69,7 @@ static __always_inline int queued_spin_trylock(struct qspinlock *lock) extern void queued_spin_lock_slowpath(struct qspinlock *lock, u32 val); +#ifndef queued_spin_lock /** * queued_spin_lock - acquire a queued spinlock * @lock: Pointer to queued spinlock structure @@ -82,6 +83,7 @@ static __always_inline void queued_spin_lock(struct qspinlock *lock) queued_spin_lock_slowpath(lock, val); } +#endif #ifndef queued_spin_unlock /** -- GitLab From 2f6560e652dfdbdb59df28b45a3458bf36d3c580 Mon Sep 17 00:00:00 2001 From: Nicholas Piggin Date: Fri, 24 Jul 2020 23:14:22 +1000 Subject: [PATCH 0881/1476] powerpc/qspinlock: Optimised atomic_try_cmpxchg_lock() that adds the lock hint This brings the behaviour of the uncontended fast path back to roughly equivalent to simple spinlocks -- a single atomic op with lock hint. Signed-off-by: Nicholas Piggin Acked-by: Waiman Long Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20200724131423.1362108-6-npiggin@gmail.com --- arch/powerpc/include/asm/atomic.h | 28 ++++++++++++++++++++++++++++ arch/powerpc/include/asm/qspinlock.h | 2 +- 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/arch/powerpc/include/asm/atomic.h b/arch/powerpc/include/asm/atomic.h index 498785ffc25f3..f6a3d145ffb7a 100644 --- a/arch/powerpc/include/asm/atomic.h +++ b/arch/powerpc/include/asm/atomic.h @@ -193,6 +193,34 @@ static __inline__ int atomic_dec_return_relaxed(atomic_t *v) #define atomic_xchg(v, new) (xchg(&((v)->counter), new)) #define atomic_xchg_relaxed(v, new) xchg_relaxed(&((v)->counter), (new)) +/* + * Don't want to override the generic atomic_try_cmpxchg_acquire, because + * we add a lock hint to the lwarx, which may not be wanted for the + * _acquire case (and is not used by the other _acquire variants so it + * would be a surprise). + */ +static __always_inline bool +atomic_try_cmpxchg_lock(atomic_t *v, int *old, int new) +{ + int r, o = *old; + + __asm__ __volatile__ ( +"1:\t" PPC_LWARX(%0,0,%2,1) " # atomic_try_cmpxchg_acquire \n" +" cmpw 0,%0,%3 \n" +" bne- 2f \n" +" stwcx. %4,0,%2 \n" +" bne- 1b \n" +"\t" PPC_ACQUIRE_BARRIER " \n" +"2: \n" + : "=&r" (r), "+m" (v->counter) + : "r" (&v->counter), "r" (o), "r" (new) + : "cr0", "memory"); + + if (unlikely(r != o)) + *old = r; + return likely(r == o); +} + /** * atomic_fetch_add_unless - add unless the number is a given value * @v: pointer of type atomic_t diff --git a/arch/powerpc/include/asm/qspinlock.h b/arch/powerpc/include/asm/qspinlock.h index f5066f00a08c1..b752d34517b39 100644 --- a/arch/powerpc/include/asm/qspinlock.h +++ b/arch/powerpc/include/asm/qspinlock.h @@ -37,7 +37,7 @@ static __always_inline void queued_spin_lock(struct qspinlock *lock) { u32 val = 0; - if (likely(atomic_try_cmpxchg_acquire(&lock->val, &val, _Q_LOCKED_VAL))) + if (likely(atomic_try_cmpxchg_lock(&lock->val, &val, _Q_LOCKED_VAL))) return; queued_spin_lock_slowpath(lock, val); -- GitLab From 49a7d46a06c30c7beabbf9d1a8ea1de0f9e4fdfe Mon Sep 17 00:00:00 2001 From: Nicholas Piggin Date: Fri, 24 Jul 2020 23:14:23 +1000 Subject: [PATCH 0882/1476] powerpc: Implement smp_cond_load_relaxed() This implements smp_cond_load_relaxed() with the slowpath busy loop using the preferred SMT priority pattern. Signed-off-by: Nicholas Piggin Acked-by: Waiman Long [mpe: Make it 64-bit only to fix build errors on 32-bit] Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20200724131423.1362108-7-npiggin@gmail.com --- arch/powerpc/include/asm/barrier.h | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/arch/powerpc/include/asm/barrier.h b/arch/powerpc/include/asm/barrier.h index 35c1b8f3aa683..f53c423808327 100644 --- a/arch/powerpc/include/asm/barrier.h +++ b/arch/powerpc/include/asm/barrier.h @@ -80,6 +80,22 @@ do { \ ___p1; \ }) +#ifdef CONFIG_PPC64 +#define smp_cond_load_relaxed(ptr, cond_expr) ({ \ + typeof(ptr) __PTR = (ptr); \ + __unqual_scalar_typeof(*ptr) VAL; \ + VAL = READ_ONCE(*__PTR); \ + if (unlikely(!(cond_expr))) { \ + spin_begin(); \ + do { \ + VAL = READ_ONCE(*__PTR); \ + } while (!(cond_expr)); \ + spin_end(); \ + } \ + (typeof(*ptr))VAL; \ +}) +#endif + #ifdef CONFIG_PPC_BOOK3S_64 #define NOSPEC_BARRIER_SLOT nop #elif defined(CONFIG_PPC_FSL_BOOK3E) -- GitLab From faedc380129501bdd7f669bf14e9c7ee3e7a2feb Mon Sep 17 00:00:00 2001 From: Bill Wendling Date: Fri, 24 Jul 2020 15:49:01 -0700 Subject: [PATCH 0883/1476] powerpc/64s: allow for clang's objdump differences Clang's objdump emits slightly different output from GNU's objdump, causing a list of warnings to be emitted during relocatable builds. E.g., clang's objdump emits this: c000000000000004: 2c 00 00 48 b 0xc000000000000030 ... c000000000005c6c: 10 00 82 40 bf 2, 0xc000000000005c7c while GNU objdump emits: c000000000000004: 2c 00 00 48 b c000000000000030 <__start+0x30> ... c000000000005c6c: 10 00 82 40 bne c000000000005c7c Adjust llvm-objdump's output to remove the extraneous '0x' and convert 'bf' and 'bt' to 'bne' and 'beq' resp. to more closely match GNU objdump's output. Note that clang's objdump doesn't yet output the relocation symbols on PPC. Signed-off-by: Bill Wendling Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/191c67db31264b69cf6b566fd69851beb3dd0abb.1595630874.git.morbo@google.com --- arch/powerpc/tools/unrel_branch_check.sh | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/arch/powerpc/tools/unrel_branch_check.sh b/arch/powerpc/tools/unrel_branch_check.sh index 77114755dc6f2..6e6a30aea3ede 100755 --- a/arch/powerpc/tools/unrel_branch_check.sh +++ b/arch/powerpc/tools/unrel_branch_check.sh @@ -31,7 +31,10 @@ grep -e "^c[0-9a-f]*:[[:space:]]*\([0-9a-f][0-9a-f][[:space:]]\)\{4\}[[:space:]] grep -v '\<__start_initialization_multiplatform>' | grep -v -e 'b.\?.\?ctr' | grep -v -e 'b.\?.\?lr' | -sed 's/://' | +sed -e 's/\bbt.\?[[:space:]]*[[:digit:]][[:digit:]]*,/beq/' \ + -e 's/\bbf.\?[[:space:]]*[[:digit:]][[:digit:]]*,/bne/' \ + -e 's/[[:space:]]0x/ /' \ + -e 's/://' | awk '{ print $1 ":" $6 ":0x" $7 ":" $8 " "}' ) -- GitLab From 19a551b254e6c308348a46a65332aa03c01767ed Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Sat, 25 Jul 2020 17:19:49 +0800 Subject: [PATCH 0884/1476] powerpc/papr_scm: Make some symbols static The sparse tool complains as follows: arch/powerpc/platforms/pseries/papr_scm.c:97:1: warning: symbol 'papr_nd_regions' was not declared. Should it be static? arch/powerpc/platforms/pseries/papr_scm.c:98:1: warning: symbol 'papr_ndr_lock' was not declared. Should it be static? Those variables are not used outside of papr_scm.c, so this commit marks them static. Fixes: 85343a8da2d9 ("powerpc/papr/scm: Add bad memory ranges to nvdimm bad ranges") Reported-by: Hulk Robot Signed-off-by: Wei Yongjun Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20200725091949.75234-1-weiyongjun1@huawei.com --- arch/powerpc/platforms/pseries/papr_scm.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/powerpc/platforms/pseries/papr_scm.c b/arch/powerpc/platforms/pseries/papr_scm.c index 8fd441d32487d..3d1235a76ba96 100644 --- a/arch/powerpc/platforms/pseries/papr_scm.c +++ b/arch/powerpc/platforms/pseries/papr_scm.c @@ -94,8 +94,8 @@ struct papr_scm_priv { u64 health_bitmap; }; -LIST_HEAD(papr_nd_regions); -DEFINE_MUTEX(papr_ndr_lock); +static LIST_HEAD(papr_nd_regions); +static DEFINE_MUTEX(papr_ndr_lock); static int drc_pmem_bind(struct papr_scm_priv *p) { -- GitLab From ccc8fcf72a6953fbfd6998999d622295f522b952 Mon Sep 17 00:00:00 2001 From: Christophe Leroy Date: Mon, 29 Jun 2020 11:15:20 +0000 Subject: [PATCH 0885/1476] powerpc/lib: Prepare code-patching for modules allocated outside vmalloc space Use is_vmalloc_or_module_addr() instead of is_vmalloc_addr() Signed-off-by: Christophe Leroy Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/7d884db0e5a6f521331639d8c0f13e520d5a4fef.1593428200.git.christophe.leroy@csgroup.eu --- arch/powerpc/lib/code-patching.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/powerpc/lib/code-patching.c b/arch/powerpc/lib/code-patching.c index 0a051dfeb1773..8c3934ea62209 100644 --- a/arch/powerpc/lib/code-patching.c +++ b/arch/powerpc/lib/code-patching.c @@ -93,7 +93,7 @@ static int map_patch_area(void *addr, unsigned long text_poke_addr) unsigned long pfn; int err; - if (is_vmalloc_addr(addr)) + if (is_vmalloc_or_module_addr(addr)) pfn = vmalloc_to_pfn(addr); else pfn = __pa_symbol(addr) >> PAGE_SHIFT; -- GitLab From 7fbc22ce29931630da200cfc90fe5a454f54a794 Mon Sep 17 00:00:00 2001 From: Christophe Leroy Date: Mon, 29 Jun 2020 11:15:21 +0000 Subject: [PATCH 0886/1476] powerpc: Use MODULES_VADDR if defined In order to allow allocation of modules outside of vmalloc space, use MODULES_VADDR and MODULES_END when MODULES_VADDR is defined. Redefine module_alloc() when MODULES_VADDR defined. Unmap corresponding KASAN shadow memory. Signed-off-by: Christophe Leroy Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/7ecf5fff1eef67d450e73fc412b6ec3818483d75.1593428200.git.christophe.leroy@csgroup.eu --- arch/powerpc/kernel/module.c | 11 +++++++++++ arch/powerpc/mm/kasan/kasan_init_32.c | 6 ++++++ 2 files changed, 17 insertions(+) diff --git a/arch/powerpc/kernel/module.c b/arch/powerpc/kernel/module.c index df649acb56319..a211b0253cdb4 100644 --- a/arch/powerpc/kernel/module.c +++ b/arch/powerpc/kernel/module.c @@ -86,3 +86,14 @@ int module_finalize(const Elf_Ehdr *hdr, return 0; } + +#ifdef MODULES_VADDR +void *module_alloc(unsigned long size) +{ + BUILD_BUG_ON(TASK_SIZE > MODULES_VADDR); + + return __vmalloc_node_range(size, 1, MODULES_VADDR, MODULES_END, GFP_KERNEL, + PAGE_KERNEL_EXEC, VM_FLUSH_RESET_PERMS, NUMA_NO_NODE, + __builtin_return_address(0)); +} +#endif diff --git a/arch/powerpc/mm/kasan/kasan_init_32.c b/arch/powerpc/mm/kasan/kasan_init_32.c index 019b0c0bbbf31..fb294046e00e4 100644 --- a/arch/powerpc/mm/kasan/kasan_init_32.c +++ b/arch/powerpc/mm/kasan/kasan_init_32.c @@ -115,6 +115,12 @@ static void __init kasan_unmap_early_shadow_vmalloc(void) unsigned long k_end = (unsigned long)kasan_mem_to_shadow((void *)VMALLOC_END); kasan_update_early_region(k_start, k_end, __pte(0)); + +#ifdef MODULES_VADDR + k_start = (unsigned long)kasan_mem_to_shadow((void *)MODULES_VADDR); + k_end = (unsigned long)kasan_mem_to_shadow((void *)MODULES_END); + kasan_update_early_region(k_start, k_end, __pte(0)); +#endif } void __init kasan_mmu_init(void) -- GitLab From c496433197154144c310a17939736bc5c155914d Mon Sep 17 00:00:00 2001 From: Christophe Leroy Date: Mon, 29 Jun 2020 11:15:22 +0000 Subject: [PATCH 0887/1476] powerpc/32s: Only leave NX unset on segments used for modules Instead of leaving NX unset on all segments above the start of vmalloc space, only leave NX unset on segments used for modules. Signed-off-by: Christophe Leroy Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/7172c0f5253419315e434a1816ee3d6ed6505bc0.1593428200.git.christophe.leroy@csgroup.eu --- arch/powerpc/mm/book3s32/mmu.c | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/arch/powerpc/mm/book3s32/mmu.c b/arch/powerpc/mm/book3s32/mmu.c index 03b6ba54460e2..c0162911f6cbd 100644 --- a/arch/powerpc/mm/book3s32/mmu.c +++ b/arch/powerpc/mm/book3s32/mmu.c @@ -187,6 +187,17 @@ unsigned long __init mmu_mapin_ram(unsigned long base, unsigned long top) return __mmu_mapin_ram(border, top); } +static bool is_module_segment(unsigned long addr) +{ + if (!IS_ENABLED(CONFIG_MODULES)) + return false; + if (addr < ALIGN_DOWN(VMALLOC_START, SZ_256M)) + return false; + if (addr >= ALIGN(VMALLOC_END, SZ_256M)) + return false; + return true; +} + void mmu_mark_initmem_nx(void) { int nb = mmu_has_feature(MMU_FTR_USE_HIGH_BATS) ? 8 : 4; @@ -223,9 +234,9 @@ void mmu_mark_initmem_nx(void) for (i = TASK_SIZE >> 28; i < 16; i++) { /* Do not set NX on VM space for modules */ - if (IS_ENABLED(CONFIG_MODULES) && - (VMALLOC_START & 0xf0000000) == i << 28) - break; + if (is_module_segment(i << 28)) + continue; + mtsrin(mfsrin(i << 28) | 0x10000000, i << 28); } } -- GitLab From b6be1bb7f7216b9e9f33f57abe6e3290c0e66bd4 Mon Sep 17 00:00:00 2001 From: Christophe Leroy Date: Mon, 29 Jun 2020 11:15:23 +0000 Subject: [PATCH 0888/1476] powerpc/32: Set user/kernel boundary at TASK_SIZE instead of PAGE_OFFSET User space stops at TASK_SIZE. At the moment, kernel space starts at PAGE_OFFSET. In order to use space between TASK_SIZE and PAGE_OFFSET for modules, make TASK_SIZE the limit between user and kernel space. Note that fault.c already considers TASK_SIZE as the boundary between user and kernel space. Signed-off-by: Christophe Leroy Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/b38b52cd8dabbb56fbd6f9219d6f3cdccbb43b44.1593428200.git.christophe.leroy@csgroup.eu --- arch/powerpc/include/asm/page.h | 4 +++- arch/powerpc/mm/ptdump/ptdump.c | 8 ++++++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/arch/powerpc/include/asm/page.h b/arch/powerpc/include/asm/page.h index a63fe6f3a0ff8..254687258f42b 100644 --- a/arch/powerpc/include/asm/page.h +++ b/arch/powerpc/include/asm/page.h @@ -255,8 +255,10 @@ static inline bool pfn_valid(unsigned long pfn) */ #ifdef CONFIG_PPC_BOOK3E_64 #define is_kernel_addr(x) ((x) >= 0x8000000000000000ul) -#else +#elif defined(CONFIG_PPC_BOOK3S_64) #define is_kernel_addr(x) ((x) >= PAGE_OFFSET) +#else +#define is_kernel_addr(x) ((x) >= TASK_SIZE) #endif #ifndef CONFIG_PPC_BOOK3S_64 diff --git a/arch/powerpc/mm/ptdump/ptdump.c b/arch/powerpc/mm/ptdump/ptdump.c index de6e05ef871c1..9d942136c7bee 100644 --- a/arch/powerpc/mm/ptdump/ptdump.c +++ b/arch/powerpc/mm/ptdump/ptdump.c @@ -348,7 +348,11 @@ static void populate_markers(void) { int i = 0; +#ifdef CONFIG_PPC64 address_markers[i++].start_address = PAGE_OFFSET; +#else + address_markers[i++].start_address = TASK_SIZE; +#endif address_markers[i++].start_address = VMALLOC_START; address_markers[i++].start_address = VMALLOC_END; #ifdef CONFIG_PPC64 @@ -385,7 +389,7 @@ static int ptdump_show(struct seq_file *m, void *v) struct pg_state st = { .seq = m, .marker = address_markers, - .start_address = PAGE_OFFSET, + .start_address = IS_ENABLED(CONFIG_PPC64) ? PAGE_OFFSET : TASK_SIZE, }; #ifdef CONFIG_PPC64 @@ -429,7 +433,7 @@ void ptdump_check_wx(void) .seq = NULL, .marker = address_markers, .check_wx = true, - .start_address = PAGE_OFFSET, + .start_address = IS_ENABLED(CONFIG_PPC64) ? PAGE_OFFSET : TASK_SIZE, }; #ifdef CONFIG_PPC64 -- GitLab From f1a1f7a15eb0e13b84791ff2738b84e414501718 Mon Sep 17 00:00:00 2001 From: Christophe Leroy Date: Mon, 29 Jun 2020 11:15:24 +0000 Subject: [PATCH 0889/1476] powerpc/32s: Kernel space starts at TASK_SIZE Kernel space starts at TASK_SIZE. Select kernel page table when address is over TASK_SIZE. Signed-off-by: Christophe Leroy Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/893425e32cd0a003539573b2d115e0ffa98bc26c.1593428200.git.christophe.leroy@csgroup.eu --- arch/powerpc/kernel/head_32.S | 12 ++++++------ arch/powerpc/mm/book3s32/hash_low.S | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/arch/powerpc/kernel/head_32.S b/arch/powerpc/kernel/head_32.S index 705c042309d8c..bbef6ce8322bc 100644 --- a/arch/powerpc/kernel/head_32.S +++ b/arch/powerpc/kernel/head_32.S @@ -474,7 +474,7 @@ InstructionTLBMiss: /* Get PTE (linux-style) and check access */ mfspr r3,SPRN_IMISS #if defined(CONFIG_MODULES) || defined(CONFIG_DEBUG_PAGEALLOC) - lis r1,PAGE_OFFSET@h /* check if kernel address */ + lis r1, TASK_SIZE@h /* check if kernel address */ cmplw 0,r1,r3 #endif mfspr r2, SPRN_SPRG_PGDIR @@ -484,7 +484,7 @@ InstructionTLBMiss: li r1,_PAGE_PRESENT | _PAGE_EXEC #endif #if defined(CONFIG_MODULES) || defined(CONFIG_DEBUG_PAGEALLOC) - bge- 112f + bgt- 112f lis r2, (swapper_pg_dir - PAGE_OFFSET)@ha /* if kernel address, use */ addi r2, r2, (swapper_pg_dir - PAGE_OFFSET)@l /* kernel page table */ #endif @@ -541,7 +541,7 @@ DataLoadTLBMiss: */ /* Get PTE (linux-style) and check access */ mfspr r3,SPRN_DMISS - lis r1,PAGE_OFFSET@h /* check if kernel address */ + lis r1, TASK_SIZE@h /* check if kernel address */ cmplw 0,r1,r3 mfspr r2, SPRN_SPRG_PGDIR #ifdef CONFIG_SWAP @@ -549,7 +549,7 @@ DataLoadTLBMiss: #else li r1, _PAGE_PRESENT #endif - bge- 112f + bgt- 112f lis r2, (swapper_pg_dir - PAGE_OFFSET)@ha /* if kernel address, use */ addi r2, r2, (swapper_pg_dir - PAGE_OFFSET)@l /* kernel page table */ 112: rlwimi r2,r3,12,20,29 /* insert top 10 bits of address */ @@ -621,7 +621,7 @@ DataStoreTLBMiss: */ /* Get PTE (linux-style) and check access */ mfspr r3,SPRN_DMISS - lis r1,PAGE_OFFSET@h /* check if kernel address */ + lis r1, TASK_SIZE@h /* check if kernel address */ cmplw 0,r1,r3 mfspr r2, SPRN_SPRG_PGDIR #ifdef CONFIG_SWAP @@ -629,7 +629,7 @@ DataStoreTLBMiss: #else li r1, _PAGE_RW | _PAGE_DIRTY | _PAGE_PRESENT #endif - bge- 112f + bgt- 112f lis r2, (swapper_pg_dir - PAGE_OFFSET)@ha /* if kernel address, use */ addi r2, r2, (swapper_pg_dir - PAGE_OFFSET)@l /* kernel page table */ 112: rlwimi r2,r3,12,20,29 /* insert top 10 bits of address */ diff --git a/arch/powerpc/mm/book3s32/hash_low.S b/arch/powerpc/mm/book3s32/hash_low.S index 923ad8f374eba..1690d369688b4 100644 --- a/arch/powerpc/mm/book3s32/hash_low.S +++ b/arch/powerpc/mm/book3s32/hash_low.S @@ -62,7 +62,7 @@ _GLOBAL(hash_page) isync #endif /* Get PTE (linux-style) and check access */ - lis r0,KERNELBASE@h /* check if kernel address */ + lis r0, TASK_SIZE@h /* check if kernel address */ cmplw 0,r4,r0 ori r3,r3,_PAGE_USER|_PAGE_PRESENT /* test low addresses as user */ mfspr r5, SPRN_SPRG_PGDIR /* phys page-table root */ -- GitLab From 6ca055322da8fe25ff9ac50db6f3b7b59b6f961c Mon Sep 17 00:00:00 2001 From: Christophe Leroy Date: Mon, 29 Jun 2020 11:15:26 +0000 Subject: [PATCH 0890/1476] powerpc/32s: Use dedicated segment for modules with STRICT_KERNEL_RWX When STRICT_KERNEL_RWX is set, we want to set NX bit on vmalloc segments. But modules require exec. Use a dedicated segment for modules. There is not much space above kernel, and we don't waste vmalloc space to do alignment. Therefore, we take the segment before PAGE_OFFSET for modules. Signed-off-by: Christophe Leroy Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/eb8faba9148b6cf17c696ba776b4e8ee2f6313bf.1593428200.git.christophe.leroy@csgroup.eu --- arch/powerpc/Kconfig | 1 + arch/powerpc/include/asm/book3s/32/pgtable.h | 15 +++++---------- arch/powerpc/mm/ptdump/ptdump.c | 8 ++++++++ 3 files changed, 14 insertions(+), 10 deletions(-) diff --git a/arch/powerpc/Kconfig b/arch/powerpc/Kconfig index a751edacf4bc0..8c7656cc10eb6 100644 --- a/arch/powerpc/Kconfig +++ b/arch/powerpc/Kconfig @@ -1213,6 +1213,7 @@ config TASK_SIZE_BOOL config TASK_SIZE hex "Size of user task space" if TASK_SIZE_BOOL default "0x80000000" if PPC_8xx + default "0xb0000000" if PPC_BOOK3S_32 && STRICT_KERNEL_RWX default "0xc0000000" endmenu diff --git a/arch/powerpc/include/asm/book3s/32/pgtable.h b/arch/powerpc/include/asm/book3s/32/pgtable.h index 224912432821f..36443cda8dcf2 100644 --- a/arch/powerpc/include/asm/book3s/32/pgtable.h +++ b/arch/powerpc/include/asm/book3s/32/pgtable.h @@ -184,17 +184,7 @@ int map_kernel_page(unsigned long va, phys_addr_t pa, pgprot_t prot); */ #define VMALLOC_OFFSET (0x1000000) /* 16M */ -/* - * With CONFIG_STRICT_KERNEL_RWX, kernel segments are set NX. But when modules - * are used, NX cannot be set on VMALLOC space. So vmalloc VM space and linear - * memory shall not share segments. - */ -#if defined(CONFIG_STRICT_KERNEL_RWX) && defined(CONFIG_MODULES) -#define VMALLOC_START ((ALIGN((long)high_memory, 256L << 20) + VMALLOC_OFFSET) & \ - ~(VMALLOC_OFFSET - 1)) -#else #define VMALLOC_START ((((long)high_memory + VMALLOC_OFFSET) & ~(VMALLOC_OFFSET-1))) -#endif #ifdef CONFIG_KASAN_VMALLOC #define VMALLOC_END ALIGN_DOWN(ioremap_bot, PAGE_SIZE << KASAN_SHADOW_SCALE_SHIFT) @@ -202,6 +192,11 @@ int map_kernel_page(unsigned long va, phys_addr_t pa, pgprot_t prot); #define VMALLOC_END ioremap_bot #endif +#ifdef CONFIG_STRICT_KERNEL_RWX +#define MODULES_END ALIGN_DOWN(PAGE_OFFSET, SZ_256M) +#define MODULES_VADDR (MODULES_END - SZ_256M) +#endif + #ifndef __ASSEMBLY__ #include #include diff --git a/arch/powerpc/mm/ptdump/ptdump.c b/arch/powerpc/mm/ptdump/ptdump.c index 9d942136c7bee..b2ed1ca4f2545 100644 --- a/arch/powerpc/mm/ptdump/ptdump.c +++ b/arch/powerpc/mm/ptdump/ptdump.c @@ -74,6 +74,10 @@ struct addr_marker { static struct addr_marker address_markers[] = { { 0, "Start of kernel VM" }, +#ifdef MODULES_VADDR + { 0, "modules start" }, + { 0, "modules end" }, +#endif { 0, "vmalloc() Area" }, { 0, "vmalloc() End" }, #ifdef CONFIG_PPC64 @@ -352,6 +356,10 @@ static void populate_markers(void) address_markers[i++].start_address = PAGE_OFFSET; #else address_markers[i++].start_address = TASK_SIZE; +#endif +#ifdef MODULES_VADDR + address_markers[i++].start_address = MODULES_VADDR; + address_markers[i++].start_address = MODULES_END; #endif address_markers[i++].start_address = VMALLOC_START; address_markers[i++].start_address = VMALLOC_END; -- GitLab From 846feeace51bce13f5c645d5bf162455b89841fd Mon Sep 17 00:00:00 2001 From: Christophe Leroy Date: Mon, 29 Jun 2020 11:17:18 +0000 Subject: [PATCH 0891/1476] powerpc/ptdump: Refactor update of st->last_pa st->last_pa is always updated in note_page() so it can be done outside the if/elseif/else block. Signed-off-by: Christophe Leroy Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/610d6b1a60ad0bedef865a90153c1110cfaa507e.1593429426.git.christophe.leroy@csgroup.eu --- arch/powerpc/mm/ptdump/ptdump.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/arch/powerpc/mm/ptdump/ptdump.c b/arch/powerpc/mm/ptdump/ptdump.c index b2ed1ca4f2545..20a0398679349 100644 --- a/arch/powerpc/mm/ptdump/ptdump.c +++ b/arch/powerpc/mm/ptdump/ptdump.c @@ -211,7 +211,6 @@ static void note_page(struct pg_state *st, unsigned long addr, st->current_flags = flag; st->start_address = addr; st->start_pa = pa; - st->last_pa = pa; st->page_size = page_size; pt_dump_seq_printf(st->seq, "---[ %s ]---\n", st->marker->name); /* @@ -251,13 +250,11 @@ static void note_page(struct pg_state *st, unsigned long addr, } st->start_address = addr; st->start_pa = pa; - st->last_pa = pa; st->page_size = page_size; st->current_flags = flag; st->level = level; - } else { - st->last_pa = pa; } + st->last_pa = pa; } static void walk_pte(struct pg_state *st, pmd_t *pmd, unsigned long start) -- GitLab From e54e30bca40233139290aecfce932dea9b996516 Mon Sep 17 00:00:00 2001 From: Christophe Leroy Date: Mon, 29 Jun 2020 11:17:19 +0000 Subject: [PATCH 0892/1476] powerpc/ptdump: Refactor update of pg_state In note_page(), the pg_state is updated the same way in two places. Add note_page_update_state() to do it. Also include the display of boundary markers there as it is missing "no level" leg, leading to a mismatch when the first two markers are at the same address and the first displayed area uses that address. Signed-off-by: Christophe Leroy Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/a284a809f01c705bbaab303b06fda216f147a99a.1593429426.git.christophe.leroy@csgroup.eu --- arch/powerpc/mm/ptdump/ptdump.c | 34 +++++++++++++++++++-------------- 1 file changed, 20 insertions(+), 14 deletions(-) diff --git a/arch/powerpc/mm/ptdump/ptdump.c b/arch/powerpc/mm/ptdump/ptdump.c index 20a0398679349..c911cd757f7d8 100644 --- a/arch/powerpc/mm/ptdump/ptdump.c +++ b/arch/powerpc/mm/ptdump/ptdump.c @@ -199,6 +199,24 @@ static void note_prot_wx(struct pg_state *st, unsigned long addr) st->wx_pages += (addr - st->start_address) / PAGE_SIZE; } +static void note_page_update_state(struct pg_state *st, unsigned long addr, + unsigned int level, u64 val, unsigned long page_size) +{ + u64 flag = val & pg_level[level].mask; + u64 pa = val & PTE_RPN_MASK; + + st->level = level; + st->current_flags = flag; + st->start_address = addr; + st->start_pa = pa; + st->page_size = page_size; + + while (addr >= st->marker[1].start_address) { + st->marker++; + pt_dump_seq_printf(st->seq, "---[ %s ]---\n", st->marker->name); + } +} + static void note_page(struct pg_state *st, unsigned long addr, unsigned int level, u64 val, unsigned long page_size) { @@ -207,12 +225,8 @@ static void note_page(struct pg_state *st, unsigned long addr, /* At first no level is set */ if (!st->level) { - st->level = level; - st->current_flags = flag; - st->start_address = addr; - st->start_pa = pa; - st->page_size = page_size; pt_dump_seq_printf(st->seq, "---[ %s ]---\n", st->marker->name); + note_page_update_state(st, addr, level, val, page_size); /* * Dump the section of virtual memory when: * - the PTE flags from one entry to the next differs. @@ -244,15 +258,7 @@ static void note_page(struct pg_state *st, unsigned long addr, * Address indicates we have passed the end of the * current section of virtual memory */ - while (addr >= st->marker[1].start_address) { - st->marker++; - pt_dump_seq_printf(st->seq, "---[ %s ]---\n", st->marker->name); - } - st->start_address = addr; - st->start_pa = pa; - st->page_size = page_size; - st->current_flags = flag; - st->level = level; + note_page_update_state(st, addr, level, val, page_size); } st->last_pa = pa; } -- GitLab From e2802618970566277cf5cf5c99df66f21ee83766 Mon Sep 17 00:00:00 2001 From: Li RongQing Date: Fri, 26 Apr 2019 19:36:30 +0800 Subject: [PATCH 0893/1476] powerpc/lib: remove memcpy_flushcache redundant return Align it with other architectures and none of the callers has been interested its return Signed-off-by: Li RongQing Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/1556278590-14727-1-git-send-email-lirongqing@baidu.com --- arch/powerpc/include/asm/string.h | 2 +- arch/powerpc/lib/pmem.c | 4 +--- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/arch/powerpc/include/asm/string.h b/arch/powerpc/include/asm/string.h index b72692702f35f..283552cd0e58e 100644 --- a/arch/powerpc/include/asm/string.h +++ b/arch/powerpc/include/asm/string.h @@ -28,7 +28,7 @@ extern void * memcpy(void *,const void *,__kernel_size_t); extern void * memmove(void *,const void *,__kernel_size_t); extern int memcmp(const void *,const void *,__kernel_size_t); extern void * memchr(const void *,int,__kernel_size_t); -extern void * memcpy_flushcache(void *,const void *,__kernel_size_t); +void memcpy_flushcache(void *dest, const void *src, size_t size); void *__memset(void *s, int c, __kernel_size_t count); void *__memcpy(void *to, const void *from, __kernel_size_t n); diff --git a/arch/powerpc/lib/pmem.c b/arch/powerpc/lib/pmem.c index 21210fa676e50..1550e0d2513a0 100644 --- a/arch/powerpc/lib/pmem.c +++ b/arch/powerpc/lib/pmem.c @@ -76,14 +76,12 @@ long __copy_from_user_flushcache(void *dest, const void __user *src, return copied; } -void *memcpy_flushcache(void *dest, const void *src, size_t size) +void memcpy_flushcache(void *dest, const void *src, size_t size) { unsigned long start = (unsigned long) dest; memcpy(dest, src, size); clean_pmem_range(start, start + size); - - return dest; } EXPORT_SYMBOL(memcpy_flushcache); -- GitLab From 10a4a016d6a882ba7601159b0f719330b102c41b Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Sat, 25 Jul 2020 17:38:01 -0700 Subject: [PATCH 0894/1476] powerpc/book3s/mmu-hash.h: delete duplicated word Drop the repeated word "below". Signed-off-by: Randy Dunlap Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20200726003809.20454-2-rdunlap@infradead.org --- arch/powerpc/include/asm/book3s/64/mmu-hash.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/powerpc/include/asm/book3s/64/mmu-hash.h b/arch/powerpc/include/asm/book3s/64/mmu-hash.h index 58fcc959f9d5a..93d18da5e7eca 100644 --- a/arch/powerpc/include/asm/book3s/64/mmu-hash.h +++ b/arch/powerpc/include/asm/book3s/64/mmu-hash.h @@ -793,7 +793,7 @@ static inline unsigned long get_vsid(unsigned long context, unsigned long ea, } /* - * For kernel space, we use context ids as below + * For kernel space, we use context ids as * below. Range is 512TB per context. * * 0x00001 - [ 0xc000000000000000 - 0xc001ffffffffffff] -- GitLab From 92be1fca08eabe8ab083b1dfccd3e932b4fb6f1a Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Sat, 25 Jul 2020 17:38:02 -0700 Subject: [PATCH 0895/1476] powerpc/book3s/radix-4k.h: delete duplicated word Drop the repeated word "per". Signed-off-by: Randy Dunlap Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20200726003809.20454-3-rdunlap@infradead.org --- arch/powerpc/include/asm/book3s/64/radix-4k.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/powerpc/include/asm/book3s/64/radix-4k.h b/arch/powerpc/include/asm/book3s/64/radix-4k.h index d5f5ab73dc7ff..035ceecd6d674 100644 --- a/arch/powerpc/include/asm/book3s/64/radix-4k.h +++ b/arch/powerpc/include/asm/book3s/64/radix-4k.h @@ -11,7 +11,7 @@ #define RADIX_PGD_INDEX_SIZE 13 // size: 8B << 13 = 64KB, maps 2^13 x 512GB = 4PB /* - * One fragment per per page + * One fragment per page */ #define RADIX_PTE_FRAG_SIZE_SHIFT (RADIX_PTE_INDEX_SIZE + 3) #define RADIX_PTE_FRAG_NR (PAGE_SIZE >> RADIX_PTE_FRAG_SIZE_SHIFT) -- GitLab From dc9bf323d6b8996d22c111add0ac8b0c895dcf52 Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Sat, 25 Jul 2020 17:38:03 -0700 Subject: [PATCH 0896/1476] powerpc/cputime.h: delete duplicated word Drop the repeated word "use". Signed-off-by: Randy Dunlap Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20200726003809.20454-4-rdunlap@infradead.org --- arch/powerpc/include/asm/cputime.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/powerpc/include/asm/cputime.h b/arch/powerpc/include/asm/cputime.h index 9335b93924b43..ed75d1c318e31 100644 --- a/arch/powerpc/include/asm/cputime.h +++ b/arch/powerpc/include/asm/cputime.h @@ -67,7 +67,7 @@ static inline void arch_vtime_task_switch(struct task_struct *prev) /* * account_cpu_user_entry/exit runs "unreconciled", so can't trace, - * can't use use get_paca() + * can't use get_paca() */ static notrace inline void account_cpu_user_entry(void) { -- GitLab From 8965aa4b684f022c4d0bc6429097ddb38a26eaef Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Sat, 25 Jul 2020 17:38:04 -0700 Subject: [PATCH 0897/1476] powerpc/epapr_hcalls.h: delete duplicated words Drop the repeated words "file" and "the". Signed-off-by: Randy Dunlap Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20200726003809.20454-5-rdunlap@infradead.org --- arch/powerpc/include/asm/epapr_hcalls.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/powerpc/include/asm/epapr_hcalls.h b/arch/powerpc/include/asm/epapr_hcalls.h index d3a7e36f14020..c99ba08a408d3 100644 --- a/arch/powerpc/include/asm/epapr_hcalls.h +++ b/arch/powerpc/include/asm/epapr_hcalls.h @@ -37,7 +37,7 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -/* A "hypercall" is an "sc 1" instruction. This header file file provides C +/* A "hypercall" is an "sc 1" instruction. This header file provides C * wrapper functions for the ePAPR hypervisor interface. It is inteded * for use by Linux device drivers and other operating systems. * @@ -246,7 +246,7 @@ static inline unsigned int ev_int_get_mask(unsigned int interrupt, * ev_int_eoi - signal the end of interrupt processing * @interrupt: the interrupt number * - * This function signals the end of processing for the the specified + * This function signals the end of processing for the specified * interrupt, which must be the interrupt currently in service. By * definition, this is also the highest-priority interrupt. * -- GitLab From 028cc22d29959b501add32fc62660e5484c8092d Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Sat, 25 Jul 2020 17:38:05 -0700 Subject: [PATCH 0898/1476] powerpc/hw_breakpoint.h: delete duplicated word Drop the repeated word "the". Signed-off-by: Randy Dunlap Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20200726003809.20454-6-rdunlap@infradead.org --- arch/powerpc/include/asm/hw_breakpoint.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/powerpc/include/asm/hw_breakpoint.h b/arch/powerpc/include/asm/hw_breakpoint.h index c89250b6ac34b..db206a7f38e24 100644 --- a/arch/powerpc/include/asm/hw_breakpoint.h +++ b/arch/powerpc/include/asm/hw_breakpoint.h @@ -19,7 +19,7 @@ struct arch_hw_breakpoint { u16 hw_len; /* length programmed in hw */ }; -/* Note: Don't change the the first 6 bits below as they are in the same order +/* Note: Don't change the first 6 bits below as they are in the same order * as the dabr and dabrx. */ #define HW_BRK_TYPE_READ 0x01 -- GitLab From db10f5500004268b29e3c5bfd1e44ef53a1e25c9 Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Sat, 25 Jul 2020 17:38:06 -0700 Subject: [PATCH 0899/1476] powerpc/ppc_asm.h: delete duplicated word Drop the repeated word "in". Signed-off-by: Randy Dunlap Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20200726003809.20454-7-rdunlap@infradead.org --- arch/powerpc/include/asm/ppc_asm.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/powerpc/include/asm/ppc_asm.h b/arch/powerpc/include/asm/ppc_asm.h index 160f3bb77ea41..b4cc6608131cd 100644 --- a/arch/powerpc/include/asm/ppc_asm.h +++ b/arch/powerpc/include/asm/ppc_asm.h @@ -776,7 +776,7 @@ END_FTR_SECTION_NESTED(CPU_FTR_CELL_TB_BUG, CPU_FTR_CELL_TB_BUG, 96) #define FIXUP_ENDIAN #else /* - * This version may be used in in HV or non-HV context. + * This version may be used in HV or non-HV context. * MSR[EE] must be disabled. */ #define FIXUP_ENDIAN \ -- GitLab From 850659392abc303d41c3f9217d45ab4fa79d201c Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Sat, 25 Jul 2020 17:38:07 -0700 Subject: [PATCH 0900/1476] powerpc/reg.h: delete duplicated word Drop the repeated word "a". Signed-off-by: Randy Dunlap Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20200726003809.20454-8-rdunlap@infradead.org --- arch/powerpc/include/asm/reg.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/powerpc/include/asm/reg.h b/arch/powerpc/include/asm/reg.h index 97efbe2973fab..ae71027d3ca23 100644 --- a/arch/powerpc/include/asm/reg.h +++ b/arch/powerpc/include/asm/reg.h @@ -1479,7 +1479,7 @@ static inline void update_power8_hid0(unsigned long hid0) { /* * The HID0 update on Power8 should at the very least be - * preceded by a a SYNC instruction followed by an ISYNC + * preceded by a SYNC instruction followed by an ISYNC * instruction */ asm volatile("sync; mtspr %0,%1; isync":: "i"(SPRN_HID0), "r"(hid0)); -- GitLab From 3b56ed4b461fd92b66f6ea44d81837e12878031f Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Sat, 25 Jul 2020 17:38:08 -0700 Subject: [PATCH 0901/1476] powerpc/smu.h: delete duplicated word Drop the repeated word "the". Signed-off-by: Randy Dunlap Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20200726003809.20454-9-rdunlap@infradead.org --- arch/powerpc/include/asm/smu.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/powerpc/include/asm/smu.h b/arch/powerpc/include/asm/smu.h index 8dff086c0cabe..4b30a0205c93d 100644 --- a/arch/powerpc/include/asm/smu.h +++ b/arch/powerpc/include/asm/smu.h @@ -108,7 +108,7 @@ /* * i2c commands * - * To issue an i2c command, first is to send a parameter block to the + * To issue an i2c command, first is to send a parameter block to * the SMU. This is a command of type 0x9a with 9 bytes of header * eventually followed by data for a write: * -- GitLab From 86052e407e8e1964c81965de25832258875a0e6d Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Sat, 25 Jul 2020 17:38:09 -0700 Subject: [PATCH 0902/1476] powerpc/powernv/pci.h: delete duplicated word Drop the repeated word "for". Signed-off-by: Randy Dunlap Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20200726003809.20454-10-rdunlap@infradead.org --- arch/powerpc/platforms/powernv/pci.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/powerpc/platforms/powernv/pci.h b/arch/powerpc/platforms/powernv/pci.h index c8cc152bdf525..739a0b3b72e15 100644 --- a/arch/powerpc/platforms/powernv/pci.h +++ b/arch/powerpc/platforms/powernv/pci.h @@ -94,7 +94,7 @@ struct pnv_ioda_pe { */ bool dma_setup_done; - /* MSIs. MVE index is identical for for 32 and 64 bit MSI + /* MSIs. MVE index is identical for 32 and 64 bit MSI * and -1 if not supported. (It's actually identical to the * PE number) */ -- GitLab From f03a3497fbf865d8eeab6d3cfc677f778862b435 Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Sat, 18 Jul 2020 11:43:40 +0200 Subject: [PATCH 0903/1476] power: reset: keystone-reset: Replace HTTP links with HTTPS ones Rationale: Reduces attack surface on kernel devs opening the links for MITM as HTTPS traffic is much harder to manipulate. Deterministic algorithm: For each file: If not .svg: For each line: If doesn't contain `\bxmlns\b`: For each link, `\bhttp://[^# \t\r\n]*(?:\w|/)`: If neither `\bgnu\.org/license`, nor `\bmozilla\.org/MPL\b`: If both the HTTP and HTTPS versions return 200 OK and serve the same content: Replace HTTP with HTTPS. Signed-off-by: Alexander A. Klimov Signed-off-by: Sebastian Reichel --- drivers/power/reset/keystone-reset.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/power/reset/keystone-reset.c b/drivers/power/reset/keystone-reset.c index ad11faae19c5c..211eeef0c81ae 100644 --- a/drivers/power/reset/keystone-reset.c +++ b/drivers/power/reset/keystone-reset.c @@ -2,7 +2,7 @@ /* * TI keystone reboot driver * - * Copyright (C) 2014 Texas Instruments Incorporated. http://www.ti.com/ + * Copyright (C) 2014 Texas Instruments Incorporated. https://www.ti.com/ * * Author: Ivan Khoronzhuk */ -- GitLab From 9d832cd36c606e176f06eaf97f0c54b02359d95a Mon Sep 17 00:00:00 2001 From: Yongqiang Liu Date: Thu, 16 Jul 2020 16:58:49 +0800 Subject: [PATCH 0904/1476] power: Convert to DEFINE_SHOW_ATTRIBUTE Use DEFINE_SHOW_ATTRIBUTE macro to simplify the code. Signed-off-by: Yongqiang Liu Signed-off-by: Sebastian Reichel --- drivers/power/supply/da9030_battery.c | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/drivers/power/supply/da9030_battery.c b/drivers/power/supply/da9030_battery.c index 88582423b87d4..0deba48d22d31 100644 --- a/drivers/power/supply/da9030_battery.c +++ b/drivers/power/supply/da9030_battery.c @@ -172,17 +172,7 @@ static int bat_debug_show(struct seq_file *s, void *data) return 0; } -static int debug_open(struct inode *inode, struct file *file) -{ - return single_open(file, bat_debug_show, inode->i_private); -} - -static const struct file_operations bat_debug_fops = { - .open = debug_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -}; +DEFINE_SHOW_ATTRIBUTE(bat_debug); static struct dentry *da9030_bat_create_debugfs(struct da9030_charger *charger) { -- GitLab From ab92ffd5f6ac3ebd4a7650ef906702ab86127b45 Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Wed, 15 Jul 2020 18:30:01 -0700 Subject: [PATCH 0905/1476] power: fix duplicated words in bq2415x_charger.h MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Drop the doubled word "for". Change "It it" to "If it". Signed-off-by: Randy Dunlap Cc: linux-pm@vger.kernel.org Acked-by: Pali Rohár Signed-off-by: Sebastian Reichel --- include/linux/power/bq2415x_charger.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/linux/power/bq2415x_charger.h b/include/linux/power/bq2415x_charger.h index 4ca08321e2516..f3c267f2a467a 100644 --- a/include/linux/power/bq2415x_charger.h +++ b/include/linux/power/bq2415x_charger.h @@ -14,8 +14,8 @@ * value is -1 then default chip value (specified in datasheet) will be * used. * - * Value resistor_sense is needed for for configuring charge and - * termination current. It it is less or equal to zero, configuring charge + * Value resistor_sense is needed for configuring charge and + * termination current. If it is less or equal to zero, configuring charge * and termination current will not be possible. * * For automode support is needed to provide name of power supply device -- GitLab From cc0fec180d30212095cd0f5a99157c2268df6367 Mon Sep 17 00:00:00 2001 From: Angelo Dureghello Date: Sun, 7 Jun 2020 21:12:15 +0200 Subject: [PATCH 0906/1476] m68k: stmark2: defconfig updates Some defconfig updates for stmark2 board, mainly to enable sdcard and mmu. Signed-off-by: Angelo Dureghello Signed-off-by: Greg Ungerer --- arch/m68k/configs/stmark2_defconfig | 47 +++++++++++++++++------------ 1 file changed, 27 insertions(+), 20 deletions(-) diff --git a/arch/m68k/configs/stmark2_defconfig b/arch/m68k/configs/stmark2_defconfig index 2b746f55f419f..d92306472fcec 100644 --- a/arch/m68k/configs/stmark2_defconfig +++ b/arch/m68k/configs/stmark2_defconfig @@ -1,38 +1,33 @@ CONFIG_LOCALVERSION="stmark2-001" CONFIG_DEFAULT_HOSTNAME="stmark2" CONFIG_SYSVIPC=y -# CONFIG_FHANDLE is not set CONFIG_LOG_BUF_SHIFT=14 CONFIG_NAMESPACES=y CONFIG_BLK_DEV_INITRD=y -# CONFIG_RD_BZIP2 is not set -# CONFIG_RD_LZMA is not set -# CONFIG_RD_XZ is not set -# CONFIG_RD_LZO is not set -# CONFIG_RD_LZ4 is not set CONFIG_CC_OPTIMIZE_FOR_SIZE=y +# CONFIG_FHANDLE is not set # CONFIG_AIO is not set # CONFIG_ADVISE_SYSCALLS is not set # CONFIG_MEMBARRIER is not set CONFIG_EMBEDDED=y # CONFIG_VM_EVENT_COUNTERS is not set # CONFIG_COMPAT_BRK is not set -# CONFIG_BLK_DEV_BSG is not set -CONFIG_BLK_CMDLINE_PARSER=y -# CONFIG_MMU is not set +CONFIG_COLDFIRE=y CONFIG_M5441x=y CONFIG_CLOCK_FREQ=240000000 CONFIG_STMARK2=y +CONFIG_UBOOT=y CONFIG_RAMBASE=0x40000000 CONFIG_RAMSIZE=0x8000000 CONFIG_VECTORBASE=0x40000000 CONFIG_KERNELBASE=0x40001000 +# CONFIG_BLK_DEV_BSG is not set +CONFIG_BLK_CMDLINE_PARSER=y CONFIG_BINFMT_FLAT=y +CONFIG_BINFMT_ZFLAT=y CONFIG_BINFMT_MISC=y -# CONFIG_UEVENT_HELPER is not set CONFIG_DEVTMPFS=y CONFIG_DEVTMPFS_MOUNT=y -CONFIG_FW_LOADER_USER_HELPER_FALLBACK=y # CONFIG_ALLOW_DEV_COREDUMP is not set CONFIG_MTD=y CONFIG_MTD_CMDLINE_PARTS=y @@ -65,24 +60,36 @@ CONFIG_DEBUG_GPIO=y CONFIG_GPIO_SYSFS=y CONFIG_GPIO_GENERIC_PLATFORM=y # CONFIG_HWMON is not set -# CONFIG_RC_CORE is not set # CONFIG_HID is not set # CONFIG_USB_SUPPORT is not set +CONFIG_MMC=y +CONFIG_MMC_DEBUG=y +CONFIG_MMC_SDHCI=y +CONFIG_MMC_SDHCI_PLTFM=y +CONFIG_MMC_SDHCI_ESDHC_MCF=y +CONFIG_DMADEVICES=y +CONFIG_MCF_EDMA=y +CONFIG_EXT2_FS=y +CONFIG_EXT2_FS_XATTR=y +CONFIG_EXT2_FS_POSIX_ACL=y +CONFIG_EXT2_FS_SECURITY=y +CONFIG_EXT3_FS=y +CONFIG_EXT3_FS_POSIX_ACL=y +CONFIG_EXT3_FS_SECURITY=y # CONFIG_FILE_LOCKING is not set # CONFIG_DNOTIFY is not set # CONFIG_INOTIFY_USER is not set +CONFIG_OVERLAY_FS=y CONFIG_FSCACHE=y # CONFIG_PROC_SYSCTL is not set +CONFIG_CRAMFS=y +CONFIG_SQUASHFS=y +CONFIG_ROMFS_FS=y +CONFIG_CRYPTO_ANSI_CPRNG=y +# CONFIG_CRYPTO_HW is not set CONFIG_PRINTK_TIME=y +# CONFIG_DEBUG_BUGVERBOSE is not set # CONFIG_SECTION_MISMATCH_WARN_ONLY is not set CONFIG_SLUB_DEBUG_ON=y CONFIG_PANIC_ON_OOPS=y # CONFIG_SCHED_DEBUG is not set -# CONFIG_DEBUG_BUGVERBOSE is not set -CONFIG_BOOTPARAM=y -CONFIG_BOOTPARAM_STRING="console=ttyS0,115200 root=/dev/ram0 rw rootfstype=ramfs rdinit=/bin/init devtmpfs.mount=1" -CONFIG_CRYPTO=y -# CONFIG_CRYPTO_ECHAINIV is not set -CONFIG_CRYPTO_ANSI_CPRNG=y -# CONFIG_CRYPTO_HW is not set -CONFIG_CRC16=y -- GitLab From 005b73d0dd83c9cb9420a196bea8070cde30ecac Mon Sep 17 00:00:00 2001 From: Greg Ungerer Date: Mon, 15 Jun 2020 08:44:17 +1000 Subject: [PATCH 0907/1476] m68knommu: __force type casts for raw IO access Bring the m68knommu raw IO functions into line with the m68k raw IO access functions and __force casting of the address component. This is primarily to fix sparse warnings on use of these raw macros. Reported-by: kernel test robot CC: Marc Kleine-Budde Reviewed-by: Luc Van Oostenryck Signed-off-by: Greg Ungerer --- arch/m68k/include/asm/io_no.h | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/arch/m68k/include/asm/io_no.h b/arch/m68k/include/asm/io_no.h index 0498192e1d983..1bc739f1f1adb 100644 --- a/arch/m68k/include/asm/io_no.h +++ b/arch/m68k/include/asm/io_no.h @@ -14,15 +14,15 @@ * that behavior here first before we include asm-generic/io.h. */ #define __raw_readb(addr) \ - ({ unsigned char __v = (*(volatile unsigned char *) (addr)); __v; }) + ({ u8 __v = (*(__force volatile u8 *) (addr)); __v; }) #define __raw_readw(addr) \ - ({ unsigned short __v = (*(volatile unsigned short *) (addr)); __v; }) + ({ u16 __v = (*(__force volatile u16 *) (addr)); __v; }) #define __raw_readl(addr) \ - ({ unsigned int __v = (*(volatile unsigned int *) (addr)); __v; }) + ({ u32 __v = (*(__force volatile u32 *) (addr)); __v; }) -#define __raw_writeb(b, addr) (void)((*(volatile unsigned char *) (addr)) = (b)) -#define __raw_writew(b, addr) (void)((*(volatile unsigned short *) (addr)) = (b)) -#define __raw_writel(b, addr) (void)((*(volatile unsigned int *) (addr)) = (b)) +#define __raw_writeb(b, addr) (void)((*(__force volatile u8 *) (addr)) = (b)) +#define __raw_writew(b, addr) (void)((*(__force volatile u16 *) (addr)) = (b)) +#define __raw_writel(b, addr) (void)((*(__force volatile u32 *) (addr)) = (b)) #if defined(CONFIG_COLDFIRE) /* -- GitLab From d4aa8affa1e9e51c237a1ec47a97e96dce76c98c Mon Sep 17 00:00:00 2001 From: Greg Ungerer Date: Mon, 15 Jun 2020 16:13:44 +1000 Subject: [PATCH 0908/1476] m68knommu: fix use of cpu_to_le() on IO access Due to the different data endian requirements of different buses on m68knommu variants we sometimes need to byte swap results for readX() or values to writeX(). Currently the code uses cpu_to_le to do this, resulting in sparse warnings like: arch/m68k/include/asm/io_no.h:78:16: sparse: sparse: cast to restricted __le32 Some casting to force __le32 types would resolve but it looks to be simpler to just switch to using the underlying swab32() to resolve. Similarly handle the 16bit cases in these functions as well. Reported-by: kernel test robot CC: Marc Kleine-Budde Reviewed-by: Luc Van Oostenryck Signed-off-by: Greg Ungerer --- arch/m68k/include/asm/io_no.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/arch/m68k/include/asm/io_no.h b/arch/m68k/include/asm/io_no.h index 1bc739f1f1adb..2c96e8480173b 100644 --- a/arch/m68k/include/asm/io_no.h +++ b/arch/m68k/include/asm/io_no.h @@ -67,7 +67,7 @@ static inline u16 readw(const volatile void __iomem *addr) { if (cf_internalio(addr)) return __raw_readw(addr); - return __le16_to_cpu(__raw_readw(addr)); + return swab16(__raw_readw(addr)); } #define readl readl @@ -75,7 +75,7 @@ static inline u32 readl(const volatile void __iomem *addr) { if (cf_internalio(addr)) return __raw_readl(addr); - return __le32_to_cpu(__raw_readl(addr)); + return swab32(__raw_readl(addr)); } #define writew writew @@ -84,7 +84,7 @@ static inline void writew(u16 value, volatile void __iomem *addr) if (cf_internalio(addr)) __raw_writew(value, addr); else - __raw_writew(__cpu_to_le16(value), addr); + __raw_writew(swab16(value), addr); } #define writel writel @@ -93,7 +93,7 @@ static inline void writel(u32 value, volatile void __iomem *addr) if (cf_internalio(addr)) __raw_writel(value, addr); else - __raw_writel(__cpu_to_le32(value), addr); + __raw_writel(swab32(value), addr); } #else -- GitLab From 416426ab79b713a40c8f8d0428dfbfd2e0f7c080 Mon Sep 17 00:00:00 2001 From: Greg Ungerer Date: Mon, 15 Jun 2020 10:41:08 +1000 Subject: [PATCH 0909/1476] m68k: fix ColdFire mmu init compile warning MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Compiling for MMU enabled ColdFire targets gives a warning: CC arch/m68k/mm/mcfmmu.o arch/m68k/mm/mcfmmu.c: In function ‘paging_init’: arch/m68k/mm/mcfmmu.c:42:17: warning: unused variable ‘zone’ [-Wunused-variable] enum zone_type zone; ^~~~ This was caused by changes in commit fa3354e4ea39 ("mm: free_area_init: use maximal zone PFNs rather than zone sizes") leaving around a now unused variable declaration. Remove the unused variable. Signed-off-by: Greg Ungerer --- arch/m68k/mm/mcfmmu.c | 1 - 1 file changed, 1 deletion(-) diff --git a/arch/m68k/mm/mcfmmu.c b/arch/m68k/mm/mcfmmu.c index 7d04210d34f02..fe1a84891035c 100644 --- a/arch/m68k/mm/mcfmmu.c +++ b/arch/m68k/mm/mcfmmu.c @@ -39,7 +39,6 @@ void __init paging_init(void) unsigned long address, size; unsigned long next_pgtable, bootmem_end; unsigned long max_zone_pfn[MAX_NR_ZONES] = { 0 }; - enum zone_type zone; int i; empty_zero_page = (void *) memblock_alloc(PAGE_SIZE, PAGE_SIZE); -- GitLab From bdee0e793cea10c516ff48bf3ebb4ef1820a116b Mon Sep 17 00:00:00 2001 From: Greg Ungerer Date: Sat, 13 Jun 2020 17:17:52 +1000 Subject: [PATCH 0910/1476] m68knommu: fix overwriting of bits in ColdFire V3 cache control The Cache Control Register (CACR) of the ColdFire V3 has bits that control high level caching functions, and also enable/disable the use of the alternate stack pointer register (the EUSP bit) to provide separate supervisor and user stack pointer registers. The code as it is today will blindly clear the EUSP bit on cache actions like invalidation. So it is broken for this case - and that will result in failed booting (interrupt entry and exit processing will be completely hosed). This only affects ColdFire V3 parts that support the alternate stack register (like the 5329 for example) - generally speaking new parts do, older parts don't. It has no impact on ColdFire V3 parts with the single stack pointer, like the 5307 for example. Fix the cache bit defines used, so they maintain the EUSP bit when carrying out cache actions through the CACR register. Signed-off-by: Greg Ungerer --- arch/m68k/include/asm/m53xxacr.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/arch/m68k/include/asm/m53xxacr.h b/arch/m68k/include/asm/m53xxacr.h index 9138a624c5c81..692f90e7fecc1 100644 --- a/arch/m68k/include/asm/m53xxacr.h +++ b/arch/m68k/include/asm/m53xxacr.h @@ -89,9 +89,9 @@ * coherency though in all cases. And for copyback caches we will need * to push cached data as well. */ -#define CACHE_INIT CACR_CINVA -#define CACHE_INVALIDATE CACR_CINVA -#define CACHE_INVALIDATED CACR_CINVA +#define CACHE_INIT (CACHE_MODE + CACR_CINVA - CACR_EC) +#define CACHE_INVALIDATE (CACHE_MODE + CACR_CINVA) +#define CACHE_INVALIDATED (CACHE_MODE + CACR_CINVA) #define ACR0_MODE ((CONFIG_RAMBASE & 0xff000000) + \ (0x000f0000) + \ -- GitLab From 58f80fa56d8b493f52cbec64a86ba61e6cf384f1 Mon Sep 17 00:00:00 2001 From: Mike Rapoport Date: Thu, 18 Jun 2020 11:17:27 +0300 Subject: [PATCH 0911/1476] m68k: mcfmmu: remove stale part of comment about steal_context The comment about steal_context() came from powerpc and a part of it addresses differences between powerpc variants that are not really relevant to m68k. Remove that part of the comment. Signed-off-by: Mike Rapoport Signed-off-by: Greg Ungerer --- arch/m68k/mm/mcfmmu.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/arch/m68k/mm/mcfmmu.c b/arch/m68k/mm/mcfmmu.c index fe1a84891035c..a3e7e4e70dd5c 100644 --- a/arch/m68k/mm/mcfmmu.c +++ b/arch/m68k/mm/mcfmmu.c @@ -213,11 +213,6 @@ void __init cf_mmu_context_init(void) /* * Steal a context from a task that has one at the moment. - * This is only used on 8xx and 4xx and we presently assume that - * they don't do SMP. If they do then thicfpgalloc.hs will have to check - * whether the MM we steal is in use. - * We also assume that this is only used on systems that don't - * use an MMU hash table - this is true for 8xx and 4xx. * This isn't an LRU system, it just frees up each context in * turn (sort-of pseudo-random replacement :). This would be the * place to implement an LRU scheme if anyone was motivated to do it. -- GitLab From f944814eea028be6cc489d3080ed2901525ded1d Mon Sep 17 00:00:00 2001 From: Greg Ungerer Date: Mon, 15 Jun 2020 14:20:47 +1000 Subject: [PATCH 0912/1476] m68k: use asm-generic cmpxchg_local() Use the asm-generic version of the cmpxchg_local() macro. Although not all target types use asm-generic/cmpxchg.h, for those that do the local cmpxchg_local() is the same as the asm-generic/cmpxchg.h one. So no need to define the local one. Signed-off-by: Greg Ungerer --- arch/m68k/include/asm/cmpxchg.h | 8 -------- 1 file changed, 8 deletions(-) diff --git a/arch/m68k/include/asm/cmpxchg.h b/arch/m68k/include/asm/cmpxchg.h index 38e1d7acc44dc..3a3bdcfcd3754 100644 --- a/arch/m68k/include/asm/cmpxchg.h +++ b/arch/m68k/include/asm/cmpxchg.h @@ -129,14 +129,6 @@ static inline unsigned long __cmpxchg(volatile void *p, unsigned long old, #else -/* - * cmpxchg_local and cmpxchg64_local are atomic wrt current CPU. Always make - * them available. - */ -#define cmpxchg_local(ptr, o, n) \ - ((__typeof__(*(ptr)))__cmpxchg_local_generic((ptr), (unsigned long)(o),\ - (unsigned long)(n), sizeof(*(ptr)))) - #include #endif -- GitLab From fde87ebf1daa8d96e4412aa06536da4b55103e02 Mon Sep 17 00:00:00 2001 From: Angelo Dureghello Date: Fri, 3 Jul 2020 01:53:38 +0200 Subject: [PATCH 0913/1476] m68k: stmark2: enable edma support for dspi Enable edma support for stmark2. Signed-off-by: Angelo Dureghello Signed-off-by: Greg Ungerer --- arch/m68k/coldfire/stmark2.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/arch/m68k/coldfire/stmark2.c b/arch/m68k/coldfire/stmark2.c index a8d2b3d172f95..8b5af9c83244e 100644 --- a/arch/m68k/coldfire/stmark2.c +++ b/arch/m68k/coldfire/stmark2.c @@ -13,6 +13,7 @@ #include #include #include +#include #include /* @@ -78,6 +79,8 @@ static struct resource dspi_spi0_resource[] = { }, }; +static u64 stmark2_dspi_mask = DMA_BIT_MASK(32); + /* SPI controller, id = bus number */ static struct platform_device dspi_spi0_device = { .name = "fsl-dspi", @@ -86,6 +89,8 @@ static struct platform_device dspi_spi0_device = { .resource = dspi_spi0_resource, .dev = { .platform_data = &dspi_spi0_info, + .dma_mask = &stmark2_dspi_mask, + .coherent_dma_mask = DMA_BIT_MASK(32), }, }; -- GitLab From 5f987caec521cbb00d4ba2dc641ac8074626b762 Mon Sep 17 00:00:00 2001 From: Michael Ellerman Date: Mon, 27 Jul 2020 13:44:36 +1000 Subject: [PATCH 0914/1476] powerpc/fadump: Fix build error with CONFIG_PRESERVE_FA_DUMP=y MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit skiroot_defconfig fails: arch/powerpc/kernel/fadump.c:48:17: error: ‘cpus_in_fadump’ defined but not used 48 | static atomic_t cpus_in_fadump; Fix it by moving the definition into the #ifdef where it's used. Fixes: ba608c4fa12c ("powerpc/fadump: fix race between pstore write and fadump crash trigger") Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20200727070341.595634-1-mpe@ellerman.id.au --- arch/powerpc/kernel/fadump.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/arch/powerpc/kernel/fadump.c b/arch/powerpc/kernel/fadump.c index 1858896d68099..10ebb4bf71ad0 100644 --- a/arch/powerpc/kernel/fadump.c +++ b/arch/powerpc/kernel/fadump.c @@ -45,10 +45,12 @@ static struct fw_dump fw_dump; static void __init fadump_reserve_crash_area(u64 base); struct kobject *fadump_kobj; -static atomic_t cpus_in_fadump; #ifndef CONFIG_PRESERVE_FA_DUMP + +static atomic_t cpus_in_fadump; static DEFINE_MUTEX(fadump_mutex); + struct fadump_mrange_info crash_mrange_info = { "crash", NULL, 0, 0, 0, false }; #define RESERVED_RNGS_SZ 16384 /* 16K - 128 entries */ -- GitLab From 44385c4171d03b1aa030f3f21a6fce2f6edcfdab Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Fri, 17 Jul 2020 15:09:02 +0300 Subject: [PATCH 0915/1476] dmaengine: ti: k3-udma: Use defines for capabilities register parsing Add defines for the CAP register fields to make the code self explaining. Signed-off-by: Peter Ujfalusi Link: https://lore.kernel.org/r/20200717120903.8774-2-peter.ujfalusi@ti.com Signed-off-by: Vinod Koul --- drivers/dma/ti/k3-udma.c | 12 ++++++------ drivers/dma/ti/k3-udma.h | 6 ++++++ 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/drivers/dma/ti/k3-udma.c b/drivers/dma/ti/k3-udma.c index 677541434ad18..e1b7cde793e30 100644 --- a/drivers/dma/ti/k3-udma.c +++ b/drivers/dma/ti/k3-udma.c @@ -3209,13 +3209,13 @@ static int udma_setup_resources(struct udma_dev *ud) "ti,sci-rm-range-rchan", "ti,sci-rm-range-rflow" }; - cap2 = udma_read(ud->mmrs[MMR_GCFG], 0x28); - cap3 = udma_read(ud->mmrs[MMR_GCFG], 0x2c); + cap2 = udma_read(ud->mmrs[MMR_GCFG], UDMA_CAP_REG(2)); + cap3 = udma_read(ud->mmrs[MMR_GCFG], UDMA_CAP_REG(3)); - ud->rflow_cnt = cap3 & 0x3fff; - ud->tchan_cnt = cap2 & 0x1ff; - ud->echan_cnt = (cap2 >> 9) & 0x1ff; - ud->rchan_cnt = (cap2 >> 18) & 0x1ff; + ud->rflow_cnt = UDMA_CAP3_RFLOW_CNT(cap3); + ud->tchan_cnt = UDMA_CAP2_TCHAN_CNT(cap2); + ud->echan_cnt = UDMA_CAP2_ECHAN_CNT(cap2); + ud->rchan_cnt = UDMA_CAP2_RCHAN_CNT(cap2); ch_count = ud->tchan_cnt + ud->rchan_cnt; ud->tchan_map = devm_kmalloc_array(dev, BITS_TO_LONGS(ud->tchan_cnt), diff --git a/drivers/dma/ti/k3-udma.h b/drivers/dma/ti/k3-udma.h index a8ea1138e1a5b..9534f0ca29f45 100644 --- a/drivers/dma/ti/k3-udma.h +++ b/drivers/dma/ti/k3-udma.h @@ -37,6 +37,12 @@ #define UDMA_CHAN_RT_BCNT_REG 0x408 #define UDMA_CHAN_RT_SBCNT_REG 0x410 +/* UDMA_CAP Registers */ +#define UDMA_CAP2_TCHAN_CNT(val) ((val) & 0x1ff) +#define UDMA_CAP2_ECHAN_CNT(val) (((val) >> 9) & 0x1ff) +#define UDMA_CAP2_RCHAN_CNT(val) (((val) >> 18) & 0x1ff) +#define UDMA_CAP3_RFLOW_CNT(val) ((val) & 0x3fff) + /* UDMA_CHAN_RT_CTL_REG */ #define UDMA_CHAN_RT_CTL_EN BIT(31) #define UDMA_CHAN_RT_CTL_TDOWN BIT(30) -- GitLab From daf4ad0499aa4f75c6f32493829acdbe48110b32 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Fri, 17 Jul 2020 15:09:03 +0300 Subject: [PATCH 0916/1476] dmaengine: ti: k3-udma: Query throughput level information from hardware The CAP3 register contains information about the number of HCHAN (High Capacity) and UCHAN (Ultra High Capacity) channels in UDMAP. Based on this information the start indexes of the levels can be calculated without a need of a table in the match data. On am654 the CAP3 does not contain information about the number different channels. Set up the tpl information based on the available documentation. This change will allow to use the same compatible for different SoCs where the only difference is the number of channel types. Signed-off-by: Peter Ujfalusi Link: https://lore.kernel.org/r/20200717120903.8774-3-peter.ujfalusi@ti.com Signed-off-by: Vinod Koul --- drivers/dma/ti/k3-udma.c | 57 ++++++++++++++++++++-------------------- drivers/dma/ti/k3-udma.h | 2 ++ 2 files changed, 30 insertions(+), 29 deletions(-) diff --git a/drivers/dma/ti/k3-udma.c b/drivers/dma/ti/k3-udma.c index e1b7cde793e30..8fa26e25ddba7 100644 --- a/drivers/dma/ti/k3-udma.c +++ b/drivers/dma/ti/k3-udma.c @@ -92,9 +92,6 @@ struct udma_match_data { u32 flags; u32 statictr_z_mask; u32 rchan_oes_offset; - - u8 tpl_levels; - u32 level_start_idx[]; }; struct udma_hwdesc { @@ -121,6 +118,9 @@ struct udma_dev { void __iomem *mmrs[MMR_LAST]; const struct udma_match_data *match_data; + u8 tpl_levels; + u32 tpl_start_idx[3]; + size_t desc_align; /* alignment to use for descriptors */ struct udma_tisci_rm tisci_rm; @@ -1210,10 +1210,10 @@ static struct udma_##res *__udma_reserve_##res(struct udma_dev *ud, \ } else { \ int start; \ \ - if (tpl >= ud->match_data->tpl_levels) \ - tpl = ud->match_data->tpl_levels - 1; \ + if (tpl >= ud->tpl_levels) \ + tpl = ud->tpl_levels - 1; \ \ - start = ud->match_data->level_start_idx[tpl]; \ + start = ud->tpl_start_idx[tpl]; \ \ id = find_next_zero_bit(ud->res##_map, ud->res##_cnt, \ start); \ @@ -1262,7 +1262,6 @@ static int udma_get_rchan(struct udma_chan *uc) static int udma_get_chan_pair(struct udma_chan *uc) { struct udma_dev *ud = uc->ud; - const struct udma_match_data *match_data = ud->match_data; int chan_id, end; if ((uc->tchan && uc->rchan) && uc->tchan->id == uc->rchan->id) { @@ -1284,7 +1283,7 @@ static int udma_get_chan_pair(struct udma_chan *uc) /* Can be optimized, but let's have it like this for now */ end = min(ud->tchan_cnt, ud->rchan_cnt); /* Try to use the highest TPL channel pair for MEM_TO_MEM channels */ - chan_id = match_data->level_start_idx[match_data->tpl_levels - 1]; + chan_id = ud->tpl_start_idx[ud->tpl_levels - 1]; for (; chan_id < end; chan_id++) { if (!test_bit(chan_id, ud->tchan_map) && !test_bit(chan_id, ud->rchan_map)) @@ -3117,11 +3116,6 @@ static struct udma_match_data am654_main_data = { .enable_memcpy_support = true, .statictr_z_mask = GENMASK(11, 0), .rchan_oes_offset = 0x2000, - .tpl_levels = 2, - .level_start_idx = { - [0] = 8, /* Normal channels */ - [1] = 0, /* High Throughput channels */ - }, }; static struct udma_match_data am654_mcu_data = { @@ -3129,11 +3123,6 @@ static struct udma_match_data am654_mcu_data = { .enable_memcpy_support = false, .statictr_z_mask = GENMASK(11, 0), .rchan_oes_offset = 0x2000, - .tpl_levels = 2, - .level_start_idx = { - [0] = 2, /* Normal channels */ - [1] = 0, /* High Throughput channels */ - }, }; static struct udma_match_data j721e_main_data = { @@ -3142,12 +3131,6 @@ static struct udma_match_data j721e_main_data = { .flags = UDMA_FLAG_PDMA_ACC32 | UDMA_FLAG_PDMA_BURST, .statictr_z_mask = GENMASK(23, 0), .rchan_oes_offset = 0x400, - .tpl_levels = 3, - .level_start_idx = { - [0] = 16, /* Normal channels */ - [1] = 4, /* High Throughput channels */ - [2] = 0, /* Ultra High Throughput channels */ - }, }; static struct udma_match_data j721e_mcu_data = { @@ -3156,11 +3139,6 @@ static struct udma_match_data j721e_mcu_data = { .flags = UDMA_FLAG_PDMA_ACC32 | UDMA_FLAG_PDMA_BURST, .statictr_z_mask = GENMASK(23, 0), .rchan_oes_offset = 0x400, - .tpl_levels = 2, - .level_start_idx = { - [0] = 2, /* Normal channels */ - [1] = 0, /* High Throughput channels */ - }, }; static const struct of_device_id udma_of_match[] = { @@ -3218,6 +3196,27 @@ static int udma_setup_resources(struct udma_dev *ud) ud->rchan_cnt = UDMA_CAP2_RCHAN_CNT(cap2); ch_count = ud->tchan_cnt + ud->rchan_cnt; + /* Set up the throughput level start indexes */ + if (of_device_is_compatible(dev->of_node, + "ti,am654-navss-main-udmap")) { + ud->tpl_levels = 2; + ud->tpl_start_idx[0] = 8; + } else if (of_device_is_compatible(dev->of_node, + "ti,am654-navss-mcu-udmap")) { + ud->tpl_levels = 2; + ud->tpl_start_idx[0] = 2; + } else if (UDMA_CAP3_UCHAN_CNT(cap3)) { + ud->tpl_levels = 3; + ud->tpl_start_idx[1] = UDMA_CAP3_UCHAN_CNT(cap3); + ud->tpl_start_idx[0] = ud->tpl_start_idx[1] + + UDMA_CAP3_HCHAN_CNT(cap3); + } else if (UDMA_CAP3_HCHAN_CNT(cap3)) { + ud->tpl_levels = 2; + ud->tpl_start_idx[0] = UDMA_CAP3_HCHAN_CNT(cap3); + } else { + ud->tpl_levels = 1; + } + ud->tchan_map = devm_kmalloc_array(dev, BITS_TO_LONGS(ud->tchan_cnt), sizeof(unsigned long), GFP_KERNEL); ud->tchans = devm_kcalloc(dev, ud->tchan_cnt, sizeof(*ud->tchans), diff --git a/drivers/dma/ti/k3-udma.h b/drivers/dma/ti/k3-udma.h index 9534f0ca29f45..09c4529e013d7 100644 --- a/drivers/dma/ti/k3-udma.h +++ b/drivers/dma/ti/k3-udma.h @@ -42,6 +42,8 @@ #define UDMA_CAP2_ECHAN_CNT(val) (((val) >> 9) & 0x1ff) #define UDMA_CAP2_RCHAN_CNT(val) (((val) >> 18) & 0x1ff) #define UDMA_CAP3_RFLOW_CNT(val) ((val) & 0x3fff) +#define UDMA_CAP3_HCHAN_CNT(val) (((val) >> 14) & 0x1ff) +#define UDMA_CAP3_UCHAN_CNT(val) (((val) >> 23) & 0x1ff) /* UDMA_CHAN_RT_CTL_REG */ #define UDMA_CHAN_RT_CTL_EN BIT(31) -- GitLab From be464133cd77c95d13df3369e63255b881d84200 Mon Sep 17 00:00:00 2001 From: Serge Semin Date: Thu, 23 Jul 2020 03:58:39 +0300 Subject: [PATCH 0917/1476] dt-bindings: dma: dw: Convert DW DMAC to DT binding Modern device tree bindings are supposed to be created as YAML-files in accordance with dt-schema. This commit replaces the Synopsis Designware DMA controller legacy bare text bindings with YAML file. The only required prorties are "compatible", "reg", "#dma-cells" and "interrupts", which will be used by the driver to correctly find the controller memory region and handle its events. The rest of the properties are optional, since in case if either "dma-channels" or "dma-masters" isn't specified, the driver will attempt to auto-detect the IP core configuration. Signed-off-by: Serge Semin Reviewed-by: Rob Herring Acked-by: Viresh Kumar Link: https://lore.kernel.org/r/20200723005848.31907-2-Sergey.Semin@baikalelectronics.ru Signed-off-by: Vinod Koul --- .../bindings/dma/snps,dma-spear1340.yaml | 161 ++++++++++++++++++ .../devicetree/bindings/dma/snps-dma.txt | 69 -------- 2 files changed, 161 insertions(+), 69 deletions(-) create mode 100644 Documentation/devicetree/bindings/dma/snps,dma-spear1340.yaml delete mode 100644 Documentation/devicetree/bindings/dma/snps-dma.txt diff --git a/Documentation/devicetree/bindings/dma/snps,dma-spear1340.yaml b/Documentation/devicetree/bindings/dma/snps,dma-spear1340.yaml new file mode 100644 index 0000000000000..e7611840a7cfa --- /dev/null +++ b/Documentation/devicetree/bindings/dma/snps,dma-spear1340.yaml @@ -0,0 +1,161 @@ +# SPDX-License-Identifier: GPL-2.0-only +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/dma/snps,dma-spear1340.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Synopsys Designware DMA Controller + +maintainers: + - Viresh Kumar + - Andy Shevchenko + +allOf: + - $ref: "dma-controller.yaml#" + +properties: + compatible: + const: snps,dma-spear1340 + + "#dma-cells": + const: 3 + description: | + First cell is a phandle pointing to the DMA controller. Second one is + the DMA request line number. Third cell is the memory master identifier + for transfers on dynamically allocated channel. Fourth cell is the + peripheral master identifier for transfers on an allocated channel. + + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + + clocks: + maxItems: 1 + + clock-names: + description: AHB interface reference clock. + const: hclk + + dma-channels: + description: | + Number of DMA channels supported by the controller. In case if + not specified the driver will try to auto-detect this and + the rest of the optional parameters. + minimum: 1 + maximum: 8 + + dma-requests: + minimum: 1 + maximum: 16 + + dma-masters: + $ref: /schemas/types.yaml#definitions/uint32 + description: | + Number of DMA masters supported by the controller. In case if + not specified the driver will try to auto-detect this and + the rest of the optional parameters. + minimum: 1 + maximum: 4 + + chan_allocation_order: + $ref: /schemas/types.yaml#definitions/uint32 + description: | + DMA channels allocation order specifier. Zero means ascending order + (first free allocated), while one - descending (last free allocated). + default: 0 + enum: [0, 1] + + chan_priority: + $ref: /schemas/types.yaml#definitions/uint32 + description: | + DMA channels priority order. Zero means ascending channels priority + so the very first channel has the highest priority. While 1 means + descending priority (the last channel has the highest priority). + default: 0 + enum: [0, 1] + + block_size: + $ref: /schemas/types.yaml#definitions/uint32 + description: Maximum block size supported by the DMA controller. + enum: [3, 7, 15, 31, 63, 127, 255, 511, 1023, 2047, 4095] + + data-width: + $ref: /schemas/types.yaml#/definitions/uint32-array + description: Data bus width per each DMA master in bytes. + items: + maxItems: 4 + items: + enum: [4, 8, 16, 32] + + data_width: + $ref: /schemas/types.yaml#/definitions/uint32-array + deprecated: true + description: | + Data bus width per each DMA master in (2^n * 8) bits. This property is + deprecated. It' usage is discouraged in favor of data-width one. Moreover + the property incorrectly permits to define data-bus width of 8 and 16 + bits, which is impossible in accordance with DW DMAC IP-core data book. + items: + maxItems: 4 + items: + enum: + - 0 # 8 bits + - 1 # 16 bits + - 2 # 32 bits + - 3 # 64 bits + - 4 # 128 bits + - 5 # 256 bits + default: 0 + + multi-block: + $ref: /schemas/types.yaml#/definitions/uint32-array + description: | + LLP-based multi-block transfer supported by hardware per + each DMA channel. + items: + maxItems: 8 + items: + enum: [0, 1] + default: 1 + + snps,dma-protection-control: + $ref: /schemas/types.yaml#definitions/uint32 + description: | + Bits one-to-one passed to the AHB HPROT[3:1] bus. Each bit setting + indicates the following features: bit 0 - privileged mode, + bit 1 - DMA is bufferable, bit 2 - DMA is cacheable. + default: 0 + minimum: 0 + maximum: 7 + +unevaluatedProperties: false + +required: + - compatible + - "#dma-cells" + - reg + - interrupts + +examples: + - | + dma-controller@fc000000 { + compatible = "snps,dma-spear1340"; + reg = <0xfc000000 0x1000>; + interrupt-parent = <&vic1>; + interrupts = <12>; + + dma-channels = <8>; + dma-requests = <16>; + dma-masters = <4>; + #dma-cells = <3>; + + chan_allocation_order = <1>; + chan_priority = <1>; + block_size = <0xfff>; + data-width = <8 8>; + multi-block = <0 0 0 0 0 0 0 0>; + snps,max-burst-len = <16 16 4 4 4 4 4 4>; + }; +... diff --git a/Documentation/devicetree/bindings/dma/snps-dma.txt b/Documentation/devicetree/bindings/dma/snps-dma.txt deleted file mode 100644 index 0bedceed19635..0000000000000 --- a/Documentation/devicetree/bindings/dma/snps-dma.txt +++ /dev/null @@ -1,69 +0,0 @@ -* Synopsys Designware DMA Controller - -Required properties: -- compatible: "snps,dma-spear1340" -- reg: Address range of the DMAC registers -- interrupt: Should contain the DMAC interrupt number -- dma-channels: Number of channels supported by hardware -- dma-requests: Number of DMA request lines supported, up to 16 -- dma-masters: Number of AHB masters supported by the controller -- #dma-cells: must be <3> -- chan_allocation_order: order of allocation of channel, 0 (default): ascending, - 1: descending -- chan_priority: priority of channels. 0 (default): increase from chan 0->n, 1: - increase from chan n->0 -- block_size: Maximum block size supported by the controller -- data-width: Maximum data width supported by hardware per AHB master - (in bytes, power of 2) - - -Deprecated properties: -- data_width: Maximum data width supported by hardware per AHB master - (0 - 8bits, 1 - 16bits, ..., 5 - 256bits) - - -Optional properties: -- multi-block: Multi block transfers supported by hardware. Array property with - one cell per channel. 0: not supported, 1 (default): supported. -- snps,dma-protection-control: AHB HPROT[3:1] protection setting. - The default value is 0 (for non-cacheable, non-buffered, - unprivileged data access). - Refer to include/dt-bindings/dma/dw-dmac.h for possible values. - -Example: - - dmahost: dma@fc000000 { - compatible = "snps,dma-spear1340"; - reg = <0xfc000000 0x1000>; - interrupt-parent = <&vic1>; - interrupts = <12>; - - dma-channels = <8>; - dma-requests = <16>; - dma-masters = <2>; - #dma-cells = <3>; - chan_allocation_order = <1>; - chan_priority = <1>; - block_size = <0xfff>; - data-width = <8 8>; - }; - -DMA clients connected to the Designware DMA controller must use the format -described in the dma.txt file, using a four-cell specifier for each channel. -The four cells in order are: - -1. A phandle pointing to the DMA controller -2. The DMA request line number -3. Memory master for transfers on allocated channel -4. Peripheral master for transfers on allocated channel - -Example: - - serial@e0000000 { - compatible = "arm,pl011", "arm,primecell"; - reg = <0xe0000000 0x1000>; - interrupts = <0 35 0x4>; - dmas = <&dmahost 12 0 1>, - <&dmahost 13 1 0>; - dma-names = "rx", "rx"; - }; -- GitLab From 2e7d74214d03b40e4ca123b834f189b08e40a6b7 Mon Sep 17 00:00:00 2001 From: Serge Semin Date: Thu, 23 Jul 2020 03:58:40 +0300 Subject: [PATCH 0918/1476] dt-bindings: dma: dw: Add max burst transaction length property This array property is used to indicate the maximum burst transaction length supported by each DMA channel. Signed-off-by: Serge Semin Reviewed-by: Rob Herring Acked-by: Viresh Kumar Link: https://lore.kernel.org/r/20200723005848.31907-3-Sergey.Semin@baikalelectronics.ru Signed-off-by: Vinod Koul --- .../bindings/dma/snps,dma-spear1340.yaml | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/Documentation/devicetree/bindings/dma/snps,dma-spear1340.yaml b/Documentation/devicetree/bindings/dma/snps,dma-spear1340.yaml index e7611840a7cfa..20870f5c14dd0 100644 --- a/Documentation/devicetree/bindings/dma/snps,dma-spear1340.yaml +++ b/Documentation/devicetree/bindings/dma/snps,dma-spear1340.yaml @@ -120,6 +120,21 @@ properties: enum: [0, 1] default: 1 + snps,max-burst-len: + $ref: /schemas/types.yaml#/definitions/uint32-array + description: | + Maximum length of the burst transactions supported by the controller. + This property defines the upper limit of the run-time burst setting + (CTLx.SRC_MSIZE/CTLx.DST_MSIZE fields) so the allowed burst length + will be from 1 to max-burst-len words. It's an array property with one + cell per channel in the units determined by the value set in the + CTLx.SRC_TR_WIDTH/CTLx.DST_TR_WIDTH fields (data width). + items: + maxItems: 8 + items: + enum: [4, 8, 16, 32, 64, 128, 256] + default: 256 + snps,dma-protection-control: $ref: /schemas/types.yaml#definitions/uint32 description: | -- GitLab From d97758e048e5fe91c7d8ff9ce5f030ee88d92161 Mon Sep 17 00:00:00 2001 From: Serge Semin Date: Thu, 23 Jul 2020 03:58:41 +0300 Subject: [PATCH 0919/1476] dmaengine: Introduce min burst length capability Some hardware aside from default 0/1 may have greater minimum burst transactions length constraints. Here we introduce the DMA device and slave capability, which if required can be initialized by the DMA engine driver with the device-specific value. Signed-off-by: Serge Semin Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/20200723005848.31907-4-Sergey.Semin@baikalelectronics.ru Signed-off-by: Vinod Koul --- drivers/dma/dmaengine.c | 1 + include/linux/dmaengine.h | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/drivers/dma/dmaengine.c b/drivers/dma/dmaengine.c index 2b06a7a8629d6..2f1a7c0c5446b 100644 --- a/drivers/dma/dmaengine.c +++ b/drivers/dma/dmaengine.c @@ -592,6 +592,7 @@ int dma_get_slave_caps(struct dma_chan *chan, struct dma_slave_caps *caps) caps->src_addr_widths = device->src_addr_widths; caps->dst_addr_widths = device->dst_addr_widths; caps->directions = device->directions; + caps->min_burst = device->min_burst; caps->max_burst = device->max_burst; caps->residue_granularity = device->residue_granularity; caps->descriptor_reuse = device->descriptor_reuse; diff --git a/include/linux/dmaengine.h b/include/linux/dmaengine.h index 883e1e087de5d..7d6e2aa26980b 100644 --- a/include/linux/dmaengine.h +++ b/include/linux/dmaengine.h @@ -467,6 +467,7 @@ enum dma_residue_granularity { * Since the enum dma_transfer_direction is not defined as bit flag for * each type, the dma controller should set BIT() and same * should be checked by controller as well + * @min_burst: min burst capability per-transfer * @max_burst: max burst capability per-transfer * @cmd_pause: true, if pause is supported (i.e. for reading residue or * for resume later) @@ -480,6 +481,7 @@ struct dma_slave_caps { u32 src_addr_widths; u32 dst_addr_widths; u32 directions; + u32 min_burst; u32 max_burst; bool cmd_pause; bool cmd_resume; @@ -771,6 +773,7 @@ struct dma_filter { * Since the enum dma_transfer_direction is not defined as bit flag for * each type, the dma controller should set BIT() and same * should be checked by controller as well + * @min_burst: min burst capability per-transfer * @max_burst: max burst capability per-transfer * @residue_granularity: granularity of the transfer residue reported * by tx_status @@ -841,6 +844,7 @@ struct dma_device { u32 src_addr_widths; u32 dst_addr_widths; u32 directions; + u32 min_burst; u32 max_burst; bool descriptor_reuse; enum dma_residue_granularity residue_granularity; -- GitLab From b1b40b8fe7e8fb26e33bad1766ce322d2c63a6c7 Mon Sep 17 00:00:00 2001 From: Serge Semin Date: Thu, 23 Jul 2020 03:58:42 +0300 Subject: [PATCH 0920/1476] dmaengine: Introduce max SG burst capability Some devices may lack the support of the hardware accelerated SG list entries automatic walking through and execution. In this case a burden of the SG list traversal and DMA engine re-initialization lies on the DMA engine driver (normally implemented by using a DMA transfer completion IRQ to recharge the DMA device with a next SG list entry). But such solution may not be suitable for some DMA consumers. In particular SPI devices need both Tx and Rx DMA channels work synchronously in order to avoid the Rx FIFO overflow. In case if Rx DMA channel is paused for some time while the Tx DMA channel works implicitly pulling data into the Rx FIFO, the later will be eventually overflown, which will cause the data loss. So if SG list entries aren't automatically fetched by the DMA engine, but are one-by-one manually selected for execution in the ISRs/deferred work/etc., such problem will eventually happen due to the non-deterministic latencies of the service execution. In order to let the DMA consumer know about the DMA device capabilities regarding the hardware accelerated SG list traversal we introduce the max_sg_burst capability. It is supposed to be initialized by the DMA engine driver with 0 if there is no limitation of the number of SG entries atomically executed and with non-zero value if there is such constraints, so the upper limit is determined by the number set to the property. Suggested-by: Andy Shevchenko Signed-off-by: Serge Semin Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/20200723005848.31907-5-Sergey.Semin@baikalelectronics.ru Signed-off-by: Vinod Koul --- drivers/dma/dmaengine.c | 1 + include/linux/dmaengine.h | 8 ++++++++ 2 files changed, 9 insertions(+) diff --git a/drivers/dma/dmaengine.c b/drivers/dma/dmaengine.c index 2f1a7c0c5446b..8177f78faedaa 100644 --- a/drivers/dma/dmaengine.c +++ b/drivers/dma/dmaengine.c @@ -594,6 +594,7 @@ int dma_get_slave_caps(struct dma_chan *chan, struct dma_slave_caps *caps) caps->directions = device->directions; caps->min_burst = device->min_burst; caps->max_burst = device->max_burst; + caps->max_sg_burst = device->max_sg_burst; caps->residue_granularity = device->residue_granularity; caps->descriptor_reuse = device->descriptor_reuse; caps->cmd_pause = !!device->device_pause; diff --git a/include/linux/dmaengine.h b/include/linux/dmaengine.h index 7d6e2aa26980b..4cbe09e66db2f 100644 --- a/include/linux/dmaengine.h +++ b/include/linux/dmaengine.h @@ -469,6 +469,9 @@ enum dma_residue_granularity { * should be checked by controller as well * @min_burst: min burst capability per-transfer * @max_burst: max burst capability per-transfer + * @max_sg_burst: max number of SG list entries executed in a single burst + * DMA tansaction with no software intervention for reinitialization. + * Zero value means unlimited number of entries. * @cmd_pause: true, if pause is supported (i.e. for reading residue or * for resume later) * @cmd_resume: true, if resume is supported @@ -483,6 +486,7 @@ struct dma_slave_caps { u32 directions; u32 min_burst; u32 max_burst; + u32 max_sg_burst; bool cmd_pause; bool cmd_resume; bool cmd_terminate; @@ -775,6 +779,9 @@ struct dma_filter { * should be checked by controller as well * @min_burst: min burst capability per-transfer * @max_burst: max burst capability per-transfer + * @max_sg_burst: max number of SG list entries executed in a single burst + * DMA tansaction with no software intervention for reinitialization. + * Zero value means unlimited number of entries. * @residue_granularity: granularity of the transfer residue reported * by tx_status * @device_alloc_chan_resources: allocate resources and return the @@ -846,6 +853,7 @@ struct dma_device { u32 directions; u32 min_burst; u32 max_burst; + u32 max_sg_burst; bool descriptor_reuse; enum dma_residue_granularity residue_granularity; -- GitLab From 3b6d694eb3eebd86ec44a119e730943ac8e03a6b Mon Sep 17 00:00:00 2001 From: Serge Semin Date: Thu, 23 Jul 2020 03:58:43 +0300 Subject: [PATCH 0921/1476] dmaengine: Introduce DMA-device device_caps callback There are DMA devices (like ours version of Synopsys DW DMAC) which have DMA capabilities non-uniformly redistributed between the device channels. In order to provide a way of exposing the channel-specific parameters to the DMA engine consumers, we introduce a new DMA-device callback. In case if provided it gets called from the dma_get_slave_caps() method and is able to override the generic DMA-device capabilities. Signed-off-by: Serge Semin Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/20200723005848.31907-6-Sergey.Semin@baikalelectronics.ru Signed-off-by: Vinod Koul --- drivers/dma/dmaengine.c | 10 ++++++++++ include/linux/dmaengine.h | 4 ++++ 2 files changed, 14 insertions(+) diff --git a/drivers/dma/dmaengine.c b/drivers/dma/dmaengine.c index 8177f78faedaa..a53e71d2bbd4c 100644 --- a/drivers/dma/dmaengine.c +++ b/drivers/dma/dmaengine.c @@ -601,6 +601,16 @@ int dma_get_slave_caps(struct dma_chan *chan, struct dma_slave_caps *caps) caps->cmd_resume = !!device->device_resume; caps->cmd_terminate = !!device->device_terminate_all; + /* + * DMA engine device might be configured with non-uniformly + * distributed slave capabilities per device channels. In this + * case the corresponding driver may provide the device_caps + * callback to override the generic capabilities with + * channel-specific ones. + */ + if (device->device_caps) + device->device_caps(chan, caps); + return 0; } EXPORT_SYMBOL_GPL(dma_get_slave_caps); diff --git a/include/linux/dmaengine.h b/include/linux/dmaengine.h index 4cbe09e66db2f..d718671bfd25b 100644 --- a/include/linux/dmaengine.h +++ b/include/linux/dmaengine.h @@ -801,6 +801,8 @@ struct dma_filter { * be called after period_len bytes have been transferred. * @device_prep_interleaved_dma: Transfer expression in a generic way. * @device_prep_dma_imm_data: DMA's 8 byte immediate data to the dst address + * @device_caps: May be used to override the generic DMA slave capabilities + * with per-channel specific ones * @device_config: Pushes a new configuration to a channel, return 0 or an error * code * @device_pause: Pauses any transfer happening on a channel. Returns @@ -901,6 +903,8 @@ struct dma_device { struct dma_chan *chan, dma_addr_t dst, u64 data, unsigned long flags); + void (*device_caps)(struct dma_chan *chan, + struct dma_slave_caps *caps); int (*device_config)(struct dma_chan *chan, struct dma_slave_config *config); int (*device_pause)(struct dma_chan *chan); -- GitLab From ef3e515a87df2881db66f05b576804ca6f03e1ff Mon Sep 17 00:00:00 2001 From: Serge Semin Date: Thu, 23 Jul 2020 03:58:44 +0300 Subject: [PATCH 0922/1476] dmaengine: dw: Take HC_LLP flag into account for noLLP auto-config Full multi-block transfers functionality is enabled in DW DMA controller only if CHx_MULTI_BLK_EN is set. But LLP-based transfers can be executed only if hardcode channel x LLP register feature isn't enabled, which can be switched on at the IP core synthesis for optimization. If it's enabled then the LLP register is hardcoded to zero, so the blocks chaining based on the LLPs is unsupported. Signed-off-by: Serge Semin Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/20200723005848.31907-7-Sergey.Semin@baikalelectronics.ru Signed-off-by: Vinod Koul --- drivers/dma/dw/core.c | 11 ++++++++++- drivers/dma/dw/regs.h | 1 + 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/drivers/dma/dw/core.c b/drivers/dma/dw/core.c index 21cb2a58dbd29..33e99d95b3d32 100644 --- a/drivers/dma/dw/core.c +++ b/drivers/dma/dw/core.c @@ -1178,8 +1178,17 @@ int do_dma_probe(struct dw_dma_chip *chip) */ dwc->block_size = (4 << ((pdata->block_size >> 4 * i) & 0xf)) - 1; + + /* + * According to the DW DMA databook the true scatter- + * gether LLPs aren't available if either multi-block + * config is disabled (CHx_MULTI_BLK_EN == 0) or the + * LLP register is hard-coded to zeros + * (CHx_HC_LLP == 1). + */ dwc->nollp = - (dwc_params >> DWC_PARAMS_MBLK_EN & 0x1) == 0; + (dwc_params >> DWC_PARAMS_MBLK_EN & 0x1) == 0 || + (dwc_params >> DWC_PARAMS_HC_LLP & 0x1) == 1; } else { dwc->block_size = pdata->block_size; dwc->nollp = !pdata->multi_block[i]; diff --git a/drivers/dma/dw/regs.h b/drivers/dma/dw/regs.h index 3fce66ecee7a5..1ab840b06e798 100644 --- a/drivers/dma/dw/regs.h +++ b/drivers/dma/dw/regs.h @@ -125,6 +125,7 @@ struct dw_dma_regs { /* Bitfields in DWC_PARAMS */ #define DWC_PARAMS_MBLK_EN 11 /* multi block transfer */ +#define DWC_PARAMS_HC_LLP 13 /* set LLP register to zero */ /* bursts size */ enum dw_dma_msize { -- GitLab From e6fe5767961dbdcabbdfcc570993a91eebca4586 Mon Sep 17 00:00:00 2001 From: Serge Semin Date: Thu, 23 Jul 2020 03:58:45 +0300 Subject: [PATCH 0923/1476] dmaengine: dw: Set DMA device max segment size parameter Maximum block size DW DMAC configuration corresponds to the max segment size DMA parameter in the DMA core subsystem notation. Lets set it with a value specific to the probed DW DMA controller. It shall help the DMA clients to create size-optimized SG-list items for the controller. This in turn will cause less dw_desc allocations, less LLP reinitializations, better DMA device performance. Signed-off-by: Serge Semin Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/20200723005848.31907-8-Sergey.Semin@baikalelectronics.ru Signed-off-by: Vinod Koul --- drivers/dma/dw/core.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/dma/dw/core.c b/drivers/dma/dw/core.c index 33e99d95b3d32..fb95920c429e7 100644 --- a/drivers/dma/dw/core.c +++ b/drivers/dma/dw/core.c @@ -1229,6 +1229,13 @@ int do_dma_probe(struct dw_dma_chip *chip) BIT(DMA_MEM_TO_MEM); dw->dma.residue_granularity = DMA_RESIDUE_GRANULARITY_BURST; + /* + * For now there is no hardware with non uniform maximum block size + * across all of the device channels, so we set the maximum segment + * size as the block size found for the very first channel. + */ + dma_set_max_seg_size(dw->dma.dev, dw->chan[0].block_size); + err = dma_async_device_register(&dw->dma); if (err) goto err_dma_register; -- GitLab From 585d35451e94b2e1b0bf59ef55d3b4a1c8ab3d77 Mon Sep 17 00:00:00 2001 From: Serge Semin Date: Thu, 23 Jul 2020 03:58:46 +0300 Subject: [PATCH 0924/1476] dmaengine: dw: Initialize min and max burst DMA device capability According to the DW APB DMAC data book the minimum burst transaction length is 1 and it's true for any version of the controller since isn't parametrised in the coreAssembler so can't be changed at the IP-core synthesis stage. The maximum burst transaction can vary from channel to channel and from controller to controller depending on a IP-core parameter the system engineer activated during the IP-core synthesis. Let's initialise both min_burst and max_burst members of the DMA controller descriptor with extreme values so the DMA clients could use them to properly optimize the DMA requests. The channels and controller-specific max_burst length initialization will be introduced by the follow-up patches. Signed-off-by: Serge Semin Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/20200723005848.31907-9-Sergey.Semin@baikalelectronics.ru Signed-off-by: Vinod Koul --- drivers/dma/dw/core.c | 2 ++ include/linux/platform_data/dma-dw.h | 2 ++ 2 files changed, 4 insertions(+) diff --git a/drivers/dma/dw/core.c b/drivers/dma/dw/core.c index fb95920c429e7..afe5a2e465af6 100644 --- a/drivers/dma/dw/core.c +++ b/drivers/dma/dw/core.c @@ -1223,6 +1223,8 @@ int do_dma_probe(struct dw_dma_chip *chip) dw->dma.device_issue_pending = dwc_issue_pending; /* DMA capabilities */ + dw->dma.min_burst = DW_DMA_MIN_BURST; + dw->dma.max_burst = DW_DMA_MAX_BURST; dw->dma.src_addr_widths = DW_DMA_BUSWIDTHS; dw->dma.dst_addr_widths = DW_DMA_BUSWIDTHS; dw->dma.directions = BIT(DMA_DEV_TO_MEM) | BIT(DMA_MEM_TO_DEV) | diff --git a/include/linux/platform_data/dma-dw.h b/include/linux/platform_data/dma-dw.h index f3eaf9ec00a1b..369e41e9dcc95 100644 --- a/include/linux/platform_data/dma-dw.h +++ b/include/linux/platform_data/dma-dw.h @@ -12,6 +12,8 @@ #define DW_DMA_MAX_NR_MASTERS 4 #define DW_DMA_MAX_NR_CHANNELS 8 +#define DW_DMA_MIN_BURST 1 +#define DW_DMA_MAX_BURST 256 /** * struct dw_dma_slave - Controller-specific information about a slave -- GitLab From ca7f2851712e7072e8f327882dc4bdaaae3a8079 Mon Sep 17 00:00:00 2001 From: Serge Semin Date: Thu, 23 Jul 2020 03:58:47 +0300 Subject: [PATCH 0925/1476] dmaengine: dw: Introduce max burst length hw config IP core of the DW DMA controller may be synthesized with different max burst length of the transfers per each channel. According to Synopsis having the fixed maximum burst transactions length may provide some performance gain. At the same time setting up the source and destination multi size exceeding the max burst length limitation may cause a serious problems. In our case the DMA transaction just hangs up. In order to fix this lets introduce the max burst length platform config of the DW DMA controller device and don't let the DMA channels configuration code exceed the burst length hardware limitation. Note the maximum burst length parameter can be detected either in runtime from the DWC parameter registers or from the dedicated DT property. Depending on the IP core configuration the maximum value can vary from channel to channel so by overriding the channel slave max_burst capability we make sure a DMA consumer will get the channel-specific max burst length. Signed-off-by: Serge Semin Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/20200723005848.31907-10-Sergey.Semin@baikalelectronics.ru Signed-off-by: Vinod Koul --- drivers/dma/dw/core.c | 16 ++++++++++++++++ drivers/dma/dw/of.c | 5 +++++ drivers/dma/dw/regs.h | 2 ++ include/linux/platform_data/dma-dw.h | 3 +++ 4 files changed, 26 insertions(+) diff --git a/drivers/dma/dw/core.c b/drivers/dma/dw/core.c index afe5a2e465af6..588b9bae827c2 100644 --- a/drivers/dma/dw/core.c +++ b/drivers/dma/dw/core.c @@ -791,6 +791,11 @@ static int dwc_config(struct dma_chan *chan, struct dma_slave_config *sconfig) memcpy(&dwc->dma_sconfig, sconfig, sizeof(*sconfig)); + dwc->dma_sconfig.src_maxburst = + clamp(dwc->dma_sconfig.src_maxburst, 0U, dwc->max_burst); + dwc->dma_sconfig.dst_maxburst = + clamp(dwc->dma_sconfig.dst_maxburst, 0U, dwc->max_burst); + dw->encode_maxburst(dwc, &dwc->dma_sconfig.src_maxburst); dw->encode_maxburst(dwc, &dwc->dma_sconfig.dst_maxburst); @@ -1049,6 +1054,13 @@ static void dwc_free_chan_resources(struct dma_chan *chan) dev_vdbg(chan2dev(chan), "%s: done\n", __func__); } +static void dwc_caps(struct dma_chan *chan, struct dma_slave_caps *caps) +{ + struct dw_dma_chan *dwc = to_dw_dma_chan(chan); + + caps->max_burst = dwc->max_burst; +} + int do_dma_probe(struct dw_dma_chip *chip) { struct dw_dma *dw = chip->dw; @@ -1189,9 +1201,12 @@ int do_dma_probe(struct dw_dma_chip *chip) dwc->nollp = (dwc_params >> DWC_PARAMS_MBLK_EN & 0x1) == 0 || (dwc_params >> DWC_PARAMS_HC_LLP & 0x1) == 1; + dwc->max_burst = + (0x4 << (dwc_params >> DWC_PARAMS_MSIZE & 0x7)); } else { dwc->block_size = pdata->block_size; dwc->nollp = !pdata->multi_block[i]; + dwc->max_burst = pdata->max_burst[i] ?: DW_DMA_MAX_BURST; } } @@ -1214,6 +1229,7 @@ int do_dma_probe(struct dw_dma_chip *chip) dw->dma.device_prep_dma_memcpy = dwc_prep_dma_memcpy; dw->dma.device_prep_slave_sg = dwc_prep_slave_sg; + dw->dma.device_caps = dwc_caps; dw->dma.device_config = dwc_config; dw->dma.device_pause = dwc_pause; dw->dma.device_resume = dwc_resume; diff --git a/drivers/dma/dw/of.c b/drivers/dma/dw/of.c index 9e27831dee324..1474b3817ef4f 100644 --- a/drivers/dma/dw/of.c +++ b/drivers/dma/dw/of.c @@ -98,6 +98,11 @@ struct dw_dma_platform_data *dw_dma_parse_dt(struct platform_device *pdev) pdata->multi_block[tmp] = 1; } + if (of_property_read_u32_array(np, "snps,max-burst-len", pdata->max_burst, + nr_channels)) { + memset32(pdata->max_burst, DW_DMA_MAX_BURST, nr_channels); + } + if (!of_property_read_u32(np, "snps,dma-protection-control", &tmp)) { if (tmp > CHAN_PROTCTL_MASK) return NULL; diff --git a/drivers/dma/dw/regs.h b/drivers/dma/dw/regs.h index 1ab840b06e798..76654bd13c1ab 100644 --- a/drivers/dma/dw/regs.h +++ b/drivers/dma/dw/regs.h @@ -126,6 +126,7 @@ struct dw_dma_regs { /* Bitfields in DWC_PARAMS */ #define DWC_PARAMS_MBLK_EN 11 /* multi block transfer */ #define DWC_PARAMS_HC_LLP 13 /* set LLP register to zero */ +#define DWC_PARAMS_MSIZE 16 /* max group transaction size */ /* bursts size */ enum dw_dma_msize { @@ -284,6 +285,7 @@ struct dw_dma_chan { /* hardware configuration */ unsigned int block_size; bool nollp; + u32 max_burst; /* custom slave configuration */ struct dw_dma_slave dws; diff --git a/include/linux/platform_data/dma-dw.h b/include/linux/platform_data/dma-dw.h index 369e41e9dcc95..4f681df85c273 100644 --- a/include/linux/platform_data/dma-dw.h +++ b/include/linux/platform_data/dma-dw.h @@ -44,6 +44,8 @@ struct dw_dma_slave { * @data_width: Maximum data width supported by hardware per AHB master * (in bytes, power of 2) * @multi_block: Multi block transfers supported by hardware per channel. + * @max_burst: Maximum value of burst transaction size supported by hardware + * per channel (in units of CTL.SRC_TR_WIDTH/CTL.DST_TR_WIDTH). * @protctl: Protection control signals setting per channel. */ struct dw_dma_platform_data { @@ -58,6 +60,7 @@ struct dw_dma_platform_data { unsigned char nr_masters; unsigned char data_width[DW_DMA_MAX_NR_MASTERS]; unsigned char multi_block[DW_DMA_MAX_NR_CHANNELS]; + u32 max_burst[DW_DMA_MAX_NR_CHANNELS]; #define CHAN_PROTCTL_PRIVILEGED BIT(0) #define CHAN_PROTCTL_BUFFERABLE BIT(1) #define CHAN_PROTCTL_CACHEABLE BIT(2) -- GitLab From 0f9d5f008e2f096b14ff1f218a870199dd5264f4 Mon Sep 17 00:00:00 2001 From: Serge Semin Date: Thu, 23 Jul 2020 03:58:48 +0300 Subject: [PATCH 0926/1476] dmaengine: dw: Initialize max_sg_burst capability Multi-block support provides a way to map the kernel-specific SG-table so the DW DMA device would handle it as a whole instead of handling the SG-list items or so called LLP block items one by one. So if true LLP list isn't supported by the DW DMA engine, then soft-LLP mode will be utilized to load and execute each LLP-block one by one. The soft-LLP mode of the DMA transactions execution might not work well for some DMA consumers like SPI due to its Tx and Rx buffers inter-dependency. Let's initialize the max_sg_burst DMA channels capability based on the nollp flag state. If it's true, no hardware accelerated LLP is available and max_sg_burst should be set with 1, which means that the DMA engine can handle only a single SG list entry at a time. If noLLP is set to false, then hardware accelerated LLP is supported and the DMA engine can handle infinite number of SG entries in a single DMA transaction. Signed-off-by: Serge Semin Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/20200723005848.31907-11-Sergey.Semin@baikalelectronics.ru Signed-off-by: Vinod Koul --- drivers/dma/dw/core.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/drivers/dma/dw/core.c b/drivers/dma/dw/core.c index 588b9bae827c2..eb9175133034a 100644 --- a/drivers/dma/dw/core.c +++ b/drivers/dma/dw/core.c @@ -1059,6 +1059,18 @@ static void dwc_caps(struct dma_chan *chan, struct dma_slave_caps *caps) struct dw_dma_chan *dwc = to_dw_dma_chan(chan); caps->max_burst = dwc->max_burst; + + /* + * It might be crucial for some devices to have the hardware + * accelerated multi-block transfers supported, aka LLPs in DW DMAC + * notation. So if LLPs are supported then max_sg_burst is set to + * zero which means unlimited number of SG entries can be handled in a + * single DMA transaction, otherwise it's just one SG entry. + */ + if (dwc->nollp) + caps->max_sg_burst = 1; + else + caps->max_sg_burst = 0; } int do_dma_probe(struct dw_dma_chip *chip) -- GitLab From 1a0c02ba643ed05c07ddf14d87c3bec640666836 Mon Sep 17 00:00:00 2001 From: Dave Jiang Date: Mon, 20 Jul 2020 08:50:58 -0700 Subject: [PATCH 0927/1476] dmaengine: idxd: add missing invalid flags field to completion Add missing "invalid flags" field to completion record struct. Reported-by: Nikhil Rao Signed-off-by: Dave Jiang Link: https://lore.kernel.org/r/159526025819.49266.13176787210106133664.stgit@djiang5-desk3.ch.intel.com Signed-off-by: Vinod Koul --- include/uapi/linux/idxd.h | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/include/uapi/linux/idxd.h b/include/uapi/linux/idxd.h index 1f412fbf561bb..5d07a3ff9e963 100644 --- a/include/uapi/linux/idxd.h +++ b/include/uapi/linux/idxd.h @@ -178,6 +178,12 @@ struct dsa_completion_record { uint32_t bytes_completed; uint64_t fault_addr; union { + /* common record */ + struct { + uint32_t invalid_flags:24; + uint32_t rsvd2:8; + }; + uint16_t delta_rec_size; uint16_t crc_val; -- GitLab From bbeeb861971e5af5bea36818334522094878be77 Mon Sep 17 00:00:00 2001 From: Amit Singh Tomar Date: Sun, 19 Jul 2020 23:11:58 +0530 Subject: [PATCH 0928/1476] dt-bindings: dmaengine: convert Actions Semi Owl SoCs bindings to yaml Converts the device tree bindings for the Actions Semi Owl SoCs DMA Controller over to YAML schemas. It also adds new compatible string "actions,s700-dma". Signed-off-by: Amit Singh Tomar Reviewed-by: Rob Herring Link: https://lore.kernel.org/r/1595180527-11320-2-git-send-email-amittomer25@gmail.com Signed-off-by: Vinod Koul --- .../devicetree/bindings/dma/owl-dma.txt | 47 ----------- .../devicetree/bindings/dma/owl-dma.yaml | 79 +++++++++++++++++++ 2 files changed, 79 insertions(+), 47 deletions(-) delete mode 100644 Documentation/devicetree/bindings/dma/owl-dma.txt create mode 100644 Documentation/devicetree/bindings/dma/owl-dma.yaml diff --git a/Documentation/devicetree/bindings/dma/owl-dma.txt b/Documentation/devicetree/bindings/dma/owl-dma.txt deleted file mode 100644 index 03e9bb12b75f7..0000000000000 --- a/Documentation/devicetree/bindings/dma/owl-dma.txt +++ /dev/null @@ -1,47 +0,0 @@ -* Actions Semi Owl SoCs DMA controller - -This binding follows the generic DMA bindings defined in dma.txt. - -Required properties: -- compatible: Should be "actions,s900-dma". -- reg: Should contain DMA registers location and length. -- interrupts: Should contain 4 interrupts shared by all channel. -- #dma-cells: Must be <1>. Used to represent the number of integer - cells in the dmas property of client device. -- dma-channels: Physical channels supported. -- dma-requests: Number of DMA request signals supported by the controller. - Refer to Documentation/devicetree/bindings/dma/dma.txt -- clocks: Phandle and Specifier of the clock feeding the DMA controller. - -Example: - -Controller: - dma: dma-controller@e0260000 { - compatible = "actions,s900-dma"; - reg = <0x0 0xe0260000 0x0 0x1000>; - interrupts = , - , - , - ; - #dma-cells = <1>; - dma-channels = <12>; - dma-requests = <46>; - clocks = <&clock CLK_DMAC>; - }; - -Client: - -DMA clients connected to the Actions Semi Owl SoCs DMA controller must -use the format described in the dma.txt file, using a two-cell specifier -for each channel. - -The two cells in order are: -1. A phandle pointing to the DMA controller. -2. The channel id. - -uart5: serial@e012a000 { - ... - dma-names = "tx", "rx"; - dmas = <&dma 26>, <&dma 27>; - ... -}; diff --git a/Documentation/devicetree/bindings/dma/owl-dma.yaml b/Documentation/devicetree/bindings/dma/owl-dma.yaml new file mode 100644 index 0000000000000..256d62af2c64b --- /dev/null +++ b/Documentation/devicetree/bindings/dma/owl-dma.yaml @@ -0,0 +1,79 @@ +# SPDX-License-Identifier: GPL-2.0 +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/dma/owl-dma.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Actions Semi Owl SoCs DMA controller + +description: | + The OWL DMA is a general-purpose direct memory access controller capable of + supporting 10 and 12 independent DMA channels for S700 and S900 SoCs + respectively. + +maintainers: + - Manivannan Sadhasivam + +allOf: + - $ref: "dma-controller.yaml#" + +properties: + compatible: + enum: + - actions,s900-dma + - actions,s700-dma + + reg: + maxItems: 1 + + interrupts: + description: + controller supports 4 interrupts, which are freely assignable to the + DMA channels. + maxItems: 4 + + "#dma-cells": + const: 1 + + dma-channels: + maximum: 12 + + dma-requests: + maximum: 46 + + clocks: + maxItems: 1 + description: + Phandle and Specifier of the clock feeding the DMA controller. + + power-domains: + maxItems: 1 + +required: + - compatible + - reg + - interrupts + - "#dma-cells" + - dma-channels + - dma-requests + - clocks + +unevaluatedProperties: false + +examples: + - | + #include + dma: dma-controller@e0260000 { + compatible = "actions,s900-dma"; + reg = <0xe0260000 0x1000>; + interrupts = , + , + , + ; + #dma-cells = <1>; + dma-channels = <12>; + dma-requests = <46>; + clocks = <&clock 22>; + }; + +... -- GitLab From 57937faeed386a1d0617f99157e358ebd510e4cd Mon Sep 17 00:00:00 2001 From: Amit Singh Tomar Date: Sun, 19 Jul 2020 23:11:59 +0530 Subject: [PATCH 0929/1476] dmaengine: Actions: get rid of bit fields from dma descriptor At the moment, Driver uses bit fields to describe registers of the DMA descriptor structure that makes it less portable and maintainable, and Andre suugested(and even sketched important bits for it) to make use of array to describe this DMA descriptors instead. It gives the flexibility while extending support for other platform such as Actions S700. This commit removes the "owl_dma_lli_hw" (that includes bit-fields) and uses array to describe DMA descriptor. Suggested-by: Andre Przywara Signed-off-by: Amit Singh Tomar Reviewed-by: Manivannan Sadhasivam Link: https://lore.kernel.org/r/1595180527-11320-3-git-send-email-amittomer25@gmail.com Signed-off-by: Vinod Koul --- drivers/dma/owl-dma.c | 98 ++++++++++++++++++++++++------------------- 1 file changed, 56 insertions(+), 42 deletions(-) diff --git a/drivers/dma/owl-dma.c b/drivers/dma/owl-dma.c index 66ef70b00ec0b..948d1bead860a 100644 --- a/drivers/dma/owl-dma.c +++ b/drivers/dma/owl-dma.c @@ -120,30 +120,33 @@ #define BIT_FIELD(val, width, shift, newshift) \ ((((val) >> (shift)) & ((BIT(width)) - 1)) << (newshift)) +/* Frame count value is fixed as 1 */ +#define FCNT_VAL 0x1 + /** - * struct owl_dma_lli_hw - Hardware link list for dma transfer - * @next_lli: physical address of the next link list - * @saddr: source physical address - * @daddr: destination physical address - * @flen: frame length - * @fcnt: frame count - * @src_stride: source stride - * @dst_stride: destination stride - * @ctrla: dma_mode and linklist ctrl config - * @ctrlb: interrupt config - * @const_num: data for constant fill + * owl_dmadesc_offsets - Describe DMA descriptor, hardware link + * list for dma transfer + * @OWL_DMADESC_NEXT_LLI: physical address of the next link list + * @OWL_DMADESC_SADDR: source physical address + * @OWL_DMADESC_DADDR: destination physical address + * @OWL_DMADESC_FLEN: frame length + * @OWL_DMADESC_SRC_STRIDE: source stride + * @OWL_DMADESC_DST_STRIDE: destination stride + * @OWL_DMADESC_CTRLA: dma_mode and linklist ctrl config + * @OWL_DMADESC_CTRLB: interrupt config + * @OWL_DMADESC_CONST_NUM: data for constant fill */ -struct owl_dma_lli_hw { - u32 next_lli; - u32 saddr; - u32 daddr; - u32 flen:20; - u32 fcnt:12; - u32 src_stride; - u32 dst_stride; - u32 ctrla; - u32 ctrlb; - u32 const_num; +enum owl_dmadesc_offsets { + OWL_DMADESC_NEXT_LLI = 0, + OWL_DMADESC_SADDR, + OWL_DMADESC_DADDR, + OWL_DMADESC_FLEN, + OWL_DMADESC_SRC_STRIDE, + OWL_DMADESC_DST_STRIDE, + OWL_DMADESC_CTRLA, + OWL_DMADESC_CTRLB, + OWL_DMADESC_CONST_NUM, + OWL_DMADESC_SIZE }; /** @@ -153,7 +156,7 @@ struct owl_dma_lli_hw { * @node: node for txd's lli_list */ struct owl_dma_lli { - struct owl_dma_lli_hw hw; + u32 hw[OWL_DMADESC_SIZE]; dma_addr_t phys; struct list_head node; }; @@ -318,6 +321,11 @@ static inline u32 llc_hw_ctrlb(u32 int_ctl) return ctl; } +static u32 llc_hw_flen(struct owl_dma_lli *lli) +{ + return lli->hw[OWL_DMADESC_FLEN] & GENMASK(19, 0); +} + static void owl_dma_free_lli(struct owl_dma *od, struct owl_dma_lli *lli) { @@ -349,8 +357,9 @@ static struct owl_dma_lli *owl_dma_add_lli(struct owl_dma_txd *txd, list_add_tail(&next->node, &txd->lli_list); if (prev) { - prev->hw.next_lli = next->phys; - prev->hw.ctrla |= llc_hw_ctrla(OWL_DMA_MODE_LME, 0); + prev->hw[OWL_DMADESC_NEXT_LLI] = next->phys; + prev->hw[OWL_DMADESC_CTRLA] |= + llc_hw_ctrla(OWL_DMA_MODE_LME, 0); } return next; @@ -363,8 +372,7 @@ static inline int owl_dma_cfg_lli(struct owl_dma_vchan *vchan, struct dma_slave_config *sconfig, bool is_cyclic) { - struct owl_dma_lli_hw *hw = &lli->hw; - u32 mode; + u32 mode, ctrlb; mode = OWL_DMA_MODE_PW(0); @@ -405,22 +413,28 @@ static inline int owl_dma_cfg_lli(struct owl_dma_vchan *vchan, return -EINVAL; } - hw->next_lli = 0; /* One link list by default */ - hw->saddr = src; - hw->daddr = dst; - - hw->fcnt = 1; /* Frame count fixed as 1 */ - hw->flen = len; /* Max frame length is 1MB */ - hw->src_stride = 0; - hw->dst_stride = 0; - hw->ctrla = llc_hw_ctrla(mode, - OWL_DMA_LLC_SAV_LOAD_NEXT | - OWL_DMA_LLC_DAV_LOAD_NEXT); + lli->hw[OWL_DMADESC_CTRLA] = llc_hw_ctrla(mode, + OWL_DMA_LLC_SAV_LOAD_NEXT | + OWL_DMA_LLC_DAV_LOAD_NEXT); if (is_cyclic) - hw->ctrlb = llc_hw_ctrlb(OWL_DMA_INTCTL_BLOCK); + ctrlb = llc_hw_ctrlb(OWL_DMA_INTCTL_BLOCK); else - hw->ctrlb = llc_hw_ctrlb(OWL_DMA_INTCTL_SUPER_BLOCK); + ctrlb = llc_hw_ctrlb(OWL_DMA_INTCTL_SUPER_BLOCK); + + lli->hw[OWL_DMADESC_NEXT_LLI] = 0; /* One link list by default */ + lli->hw[OWL_DMADESC_SADDR] = src; + lli->hw[OWL_DMADESC_DADDR] = dst; + lli->hw[OWL_DMADESC_SRC_STRIDE] = 0; + lli->hw[OWL_DMADESC_DST_STRIDE] = 0; + /* + * Word starts from offset 0xC is shared between frame length + * (max frame length is 1MB) and frame count, where first 20 + * bits are for frame length and rest of 12 bits are for frame + * count. + */ + lli->hw[OWL_DMADESC_FLEN] = len | FCNT_VAL << 20; + lli->hw[OWL_DMADESC_CTRLB] = ctrlb; return 0; } @@ -752,7 +766,7 @@ static u32 owl_dma_getbytes_chan(struct owl_dma_vchan *vchan) /* Start from the next active node */ if (lli->phys == next_lli_phy) { list_for_each_entry(lli, &txd->lli_list, node) - bytes += lli->hw.flen; + bytes += llc_hw_flen(lli); break; } } @@ -783,7 +797,7 @@ static enum dma_status owl_dma_tx_status(struct dma_chan *chan, if (vd) { txd = to_owl_txd(&vd->tx); list_for_each_entry(lli, &txd->lli_list, node) - bytes += lli->hw.flen; + bytes += llc_hw_flen(lli); } else { bytes = owl_dma_getbytes_chan(vchan); } -- GitLab From 6f9e40d4ce0bf322c95c2155d99d90ae96c58777 Mon Sep 17 00:00:00 2001 From: Amit Singh Tomar Date: Sun, 19 Jul 2020 23:12:00 +0530 Subject: [PATCH 0930/1476] dmaengine: Actions: Add support for S700 DMA engine DMA controller present on S700 SoC is compatible with the one on S900 (as most of registers are same), but it has different DMA descriptor structure where registers "fcnt" and "ctrlb" uses different encoding. For instance, on S900 "fcnt" starts at offset 0x0c and uses upper 12 bits whereas on S700, it starts at offset 0x1c and uses lower 12 bits. This commit adds support for DMA controller present on S700. Signed-off-by: Amit Singh Tomar Link: https://lore.kernel.org/r/1595180527-11320-4-git-send-email-amittomer25@gmail.com Signed-off-by: Vinod Koul --- drivers/dma/owl-dma.c | 57 +++++++++++++++++++++++++++++++------------ 1 file changed, 42 insertions(+), 15 deletions(-) diff --git a/drivers/dma/owl-dma.c b/drivers/dma/owl-dma.c index 948d1bead860a..331c8d8b10a3e 100644 --- a/drivers/dma/owl-dma.c +++ b/drivers/dma/owl-dma.c @@ -149,6 +149,11 @@ enum owl_dmadesc_offsets { OWL_DMADESC_SIZE }; +enum owl_dma_id { + S900_DMA, + S700_DMA, +}; + /** * struct owl_dma_lli - Link list for dma transfer * @hw: hardware link list @@ -213,6 +218,7 @@ struct owl_dma_vchan { * @pchans: array of data for the physical channels * @nr_vchans: the number of physical channels * @vchans: array of data for the physical channels + * @devid: device id based on OWL SoC */ struct owl_dma { struct dma_device dma; @@ -227,6 +233,7 @@ struct owl_dma { unsigned int nr_vchans; struct owl_dma_vchan *vchans; + enum owl_dma_id devid; }; static void pchan_update(struct owl_dma_pchan *pchan, u32 reg, @@ -316,6 +323,10 @@ static inline u32 llc_hw_ctrlb(u32 int_ctl) { u32 ctl; + /* + * Irrespective of the SoC, ctrlb value starts filling from + * bit 18. + */ ctl = BIT_FIELD(int_ctl, 7, 0, 18); return ctl; @@ -372,6 +383,7 @@ static inline int owl_dma_cfg_lli(struct owl_dma_vchan *vchan, struct dma_slave_config *sconfig, bool is_cyclic) { + struct owl_dma *od = to_owl_dma(vchan->vc.chan.device); u32 mode, ctrlb; mode = OWL_DMA_MODE_PW(0); @@ -427,14 +439,26 @@ static inline int owl_dma_cfg_lli(struct owl_dma_vchan *vchan, lli->hw[OWL_DMADESC_DADDR] = dst; lli->hw[OWL_DMADESC_SRC_STRIDE] = 0; lli->hw[OWL_DMADESC_DST_STRIDE] = 0; - /* - * Word starts from offset 0xC is shared between frame length - * (max frame length is 1MB) and frame count, where first 20 - * bits are for frame length and rest of 12 bits are for frame - * count. - */ - lli->hw[OWL_DMADESC_FLEN] = len | FCNT_VAL << 20; - lli->hw[OWL_DMADESC_CTRLB] = ctrlb; + + if (od->devid == S700_DMA) { + /* Max frame length is 1MB */ + lli->hw[OWL_DMADESC_FLEN] = len; + /* + * On S700, word starts from offset 0x1C is shared between + * frame count and ctrlb, where first 12 bits are for frame + * count and rest of 20 bits are for ctrlb. + */ + lli->hw[OWL_DMADESC_CTRLB] = FCNT_VAL | ctrlb; + } else { + /* + * On S900, word starts from offset 0xC is shared between + * frame length (max frame length is 1MB) and frame count, + * where first 20 bits are for frame length and rest of + * 12 bits are for frame count. + */ + lli->hw[OWL_DMADESC_FLEN] = len | FCNT_VAL << 20; + lli->hw[OWL_DMADESC_CTRLB] = ctrlb; + } return 0; } @@ -596,7 +620,7 @@ static irqreturn_t owl_dma_interrupt(int irq, void *dev_id) global_irq_pending = dma_readl(od, OWL_DMA_IRQ_PD0); - if (chan_irq_pending && !(global_irq_pending & BIT(i))) { + if (chan_irq_pending && !(global_irq_pending & BIT(i))) { dev_dbg(od->dma.dev, "global and channel IRQ pending match err\n"); @@ -1054,6 +1078,13 @@ static struct dma_chan *owl_dma_of_xlate(struct of_phandle_args *dma_spec, return chan; } +static const struct of_device_id owl_dma_match[] = { + { .compatible = "actions,s900-dma", .data = (void *)S900_DMA,}, + { .compatible = "actions,s700-dma", .data = (void *)S700_DMA,}, + { /* sentinel */ }, +}; +MODULE_DEVICE_TABLE(of, owl_dma_match); + static int owl_dma_probe(struct platform_device *pdev) { struct device_node *np = pdev->dev.of_node; @@ -1083,6 +1114,8 @@ static int owl_dma_probe(struct platform_device *pdev) dev_info(&pdev->dev, "dma-channels %d, dma-requests %d\n", nr_channels, nr_requests); + od->devid = (enum owl_dma_id)of_device_get_match_data(&pdev->dev); + od->nr_pchans = nr_channels; od->nr_vchans = nr_requests; @@ -1215,12 +1248,6 @@ static int owl_dma_remove(struct platform_device *pdev) return 0; } -static const struct of_device_id owl_dma_match[] = { - { .compatible = "actions,s900-dma", }, - { /* sentinel */ } -}; -MODULE_DEVICE_TABLE(of, owl_dma_match); - static struct platform_driver owl_dma_driver = { .probe = owl_dma_probe, .remove = owl_dma_remove, -- GitLab From 6bd0dffa1a6e19e73964ae47c964f57c625cce05 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Tue, 21 Jul 2020 16:08:44 +0300 Subject: [PATCH 0931/1476] dmaengine: dw: Don't include unneeded header to platform data header Including device.h is too much for the dma-dw.h platform data header. Replace it with the headers of which dma-dw.h is direct user. Signed-off-by: Andy Shevchenko Link: https://lore.kernel.org/r/20200721130844.64162-1-andriy.shevchenko@linux.intel.com Signed-off-by: Vinod Koul --- include/linux/platform_data/dma-dw.h | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/include/linux/platform_data/dma-dw.h b/include/linux/platform_data/dma-dw.h index 4f681df85c273..fbbeb2f6189b8 100644 --- a/include/linux/platform_data/dma-dw.h +++ b/include/linux/platform_data/dma-dw.h @@ -8,13 +8,16 @@ #ifndef _PLATFORM_DATA_DMA_DW_H #define _PLATFORM_DATA_DMA_DW_H -#include +#include +#include #define DW_DMA_MAX_NR_MASTERS 4 #define DW_DMA_MAX_NR_CHANNELS 8 #define DW_DMA_MIN_BURST 1 #define DW_DMA_MAX_BURST 256 +struct device; + /** * struct dw_dma_slave - Controller-specific information about a slave * -- GitLab From d6af2ed29c7c1c311b96dac989dcb991e90ee195 Mon Sep 17 00:00:00 2001 From: Wei Hu Date: Mon, 27 Jul 2020 15:17:31 +0800 Subject: [PATCH 0932/1476] PCI: hv: Fix a timing issue which causes kdump to fail occasionally Kdump could fail sometime on Hyper-V guest because the retry in hv_pci_enter_d0() releases child device structures in hv_pci_bus_exit(). Although there is a second asynchronous device relations message sending from the host, if this message arrives to the guest after hv_send_resource_allocated() is called, the retry would fail. Fix the problem by moving retry to hv_pci_probe() and start the retry from hv_pci_query_relations() call. This will cause a device relations message to arrive to the guest synchronously; the guest would then be able to rebuild the child device structures before calling hv_send_resource_allocated(). Link: https://lore.kernel.org/r/20200727071731.18516-1-weh@microsoft.com Fixes: c81992e7f4aa ("PCI: hv: Retry PCI bus D0 entry on invalid device state") Signed-off-by: Wei Hu [lorenzo.pieralisi@arm.com: fixed a comment and commit log] Signed-off-by: Lorenzo Pieralisi Reviewed-by: Michael Kelley --- drivers/pci/controller/pci-hyperv.c | 71 +++++++++++++++-------------- 1 file changed, 37 insertions(+), 34 deletions(-) diff --git a/drivers/pci/controller/pci-hyperv.c b/drivers/pci/controller/pci-hyperv.c index bf40ff09c99d6..d0033ff6c1437 100644 --- a/drivers/pci/controller/pci-hyperv.c +++ b/drivers/pci/controller/pci-hyperv.c @@ -2759,10 +2759,8 @@ static int hv_pci_enter_d0(struct hv_device *hdev) struct pci_bus_d0_entry *d0_entry; struct hv_pci_compl comp_pkt; struct pci_packet *pkt; - bool retry = true; int ret; -enter_d0_retry: /* * Tell the host that the bus is ready to use, and moved into the * powered-on state. This includes telling the host which region @@ -2789,38 +2787,6 @@ enter_d0_retry: if (ret) goto exit; - /* - * In certain case (Kdump) the pci device of interest was - * not cleanly shut down and resource is still held on host - * side, the host could return invalid device status. - * We need to explicitly request host to release the resource - * and try to enter D0 again. - */ - if (comp_pkt.completion_status < 0 && retry) { - retry = false; - - dev_err(&hdev->device, "Retrying D0 Entry\n"); - - /* - * Hv_pci_bus_exit() calls hv_send_resource_released() - * to free up resources of its child devices. - * In the kdump kernel we need to set the - * wslot_res_allocated to 255 so it scans all child - * devices to release resources allocated in the - * normal kernel before panic happened. - */ - hbus->wslot_res_allocated = 255; - - ret = hv_pci_bus_exit(hdev, true); - - if (ret == 0) { - kfree(pkt); - goto enter_d0_retry; - } - dev_err(&hdev->device, - "Retrying D0 failed with ret %d\n", ret); - } - if (comp_pkt.completion_status < 0) { dev_err(&hdev->device, "PCI Pass-through VSP failed D0 Entry with status %x\n", @@ -3058,6 +3024,7 @@ static int hv_pci_probe(struct hv_device *hdev, struct hv_pcibus_device *hbus; u16 dom_req, dom; char *name; + bool enter_d0_retry = true; int ret; /* @@ -3178,11 +3145,47 @@ static int hv_pci_probe(struct hv_device *hdev, if (ret) goto free_fwnode; +retry: ret = hv_pci_query_relations(hdev); if (ret) goto free_irq_domain; ret = hv_pci_enter_d0(hdev); + /* + * In certain case (Kdump) the pci device of interest was + * not cleanly shut down and resource is still held on host + * side, the host could return invalid device status. + * We need to explicitly request host to release the resource + * and try to enter D0 again. + * Since the hv_pci_bus_exit() call releases structures + * of all its child devices, we need to start the retry from + * hv_pci_query_relations() call, requesting host to send + * the synchronous child device relations message before this + * information is needed in hv_send_resources_allocated() + * call later. + */ + if (ret == -EPROTO && enter_d0_retry) { + enter_d0_retry = false; + + dev_err(&hdev->device, "Retrying D0 Entry\n"); + + /* + * Hv_pci_bus_exit() calls hv_send_resources_released() + * to free up resources of its child devices. + * In the kdump kernel we need to set the + * wslot_res_allocated to 255 so it scans all child + * devices to release resources allocated in the + * normal kernel before panic happened. + */ + hbus->wslot_res_allocated = 255; + ret = hv_pci_bus_exit(hdev, true); + + if (ret == 0) + goto retry; + + dev_err(&hdev->device, + "Retrying D0 failed with ret %d\n", ret); + } if (ret) goto free_irq_domain; -- GitLab From 5d3d063abb27687941bff206dc6e4a2402d24933 Mon Sep 17 00:00:00 2001 From: Kishon Vijay Abraham I Date: Wed, 22 Jul 2020 16:33:03 +0530 Subject: [PATCH 0933/1476] PCI: cadence: Use "dma-ranges" instead of "cdns,no-bar-match-nbits" property Cadence PCIe core driver (host mode) uses "cdns,no-bar-match-nbits" property to configure the number of bits passed through from PCIe address to internal address in Inbound Address Translation register. This only used the NO MATCH BAR. However standard PCI dt-binding already defines "dma-ranges" to describe the address ranges accessible by PCIe controller. Add support in Cadence PCIe host driver to parse dma-ranges and configure the inbound regions for BAR0, BAR1 and NO MATCH BAR. Cadence IP specifies maximum size for BAR0 as 256GB, maximum size for BAR1 as 2 GB. This adds support to take the next biggest region in "dma-ranges" and find the smallest BAR that each of the regions fit in and if there is no BAR big enough to hold the region, split the region to see if it can be fitted using multiple BARs. "dma-ranges" of J721E will be dma-ranges = <0x02000000 0x0 0x0 0x0 0x0 0x10000 0x0>; Since there is no BAR which can hold 2^48 size, NO_MATCH_BAR will be used here. Legacy device tree binding compatibility is maintained by retaining support for "cdns,no-bar-match-nbits". Link: https://lore.kernel.org/r/20200722110317.4744-2-kishon@ti.com Signed-off-by: Kishon Vijay Abraham I Signed-off-by: Lorenzo Pieralisi Reviewed-by: Rob Herring --- .../controller/cadence/pcie-cadence-host.c | 252 +++++++++++++++++- drivers/pci/controller/cadence/pcie-cadence.h | 28 +- 2 files changed, 263 insertions(+), 17 deletions(-) diff --git a/drivers/pci/controller/cadence/pcie-cadence-host.c b/drivers/pci/controller/cadence/pcie-cadence-host.c index 8c2543f28ba05..2f599eb62bcfd 100644 --- a/drivers/pci/controller/cadence/pcie-cadence-host.c +++ b/drivers/pci/controller/cadence/pcie-cadence-host.c @@ -4,6 +4,7 @@ // Author: Cyrille Pitchen #include +#include #include #include #include @@ -11,6 +12,17 @@ #include "pcie-cadence.h" +static u64 bar_max_size[] = { + [RP_BAR0] = _ULL(128 * SZ_2G), + [RP_BAR1] = SZ_2G, + [RP_NO_BAR] = _BITULL(63), +}; + +static u8 bar_aperture_mask[] = { + [RP_BAR0] = 0x1F, + [RP_BAR1] = 0xF, +}; + static void __iomem *cdns_pci_map_bus(struct pci_bus *bus, unsigned int devfn, int where) { @@ -101,6 +113,218 @@ static int cdns_pcie_host_init_root_port(struct cdns_pcie_rc *rc) return 0; } +static int cdns_pcie_host_bar_ib_config(struct cdns_pcie_rc *rc, + enum cdns_pcie_rp_bar bar, + u64 cpu_addr, u64 size, + unsigned long flags) +{ + struct cdns_pcie *pcie = &rc->pcie; + u32 addr0, addr1, aperture, value; + + if (!rc->avail_ib_bar[bar]) + return -EBUSY; + + rc->avail_ib_bar[bar] = false; + + aperture = ilog2(size); + addr0 = CDNS_PCIE_AT_IB_RP_BAR_ADDR0_NBITS(aperture) | + (lower_32_bits(cpu_addr) & GENMASK(31, 8)); + addr1 = upper_32_bits(cpu_addr); + cdns_pcie_writel(pcie, CDNS_PCIE_AT_IB_RP_BAR_ADDR0(bar), addr0); + cdns_pcie_writel(pcie, CDNS_PCIE_AT_IB_RP_BAR_ADDR1(bar), addr1); + + if (bar == RP_NO_BAR) + return 0; + + value = cdns_pcie_readl(pcie, CDNS_PCIE_LM_RC_BAR_CFG); + value &= ~(LM_RC_BAR_CFG_CTRL_MEM_64BITS(bar) | + LM_RC_BAR_CFG_CTRL_PREF_MEM_64BITS(bar) | + LM_RC_BAR_CFG_CTRL_MEM_32BITS(bar) | + LM_RC_BAR_CFG_CTRL_PREF_MEM_32BITS(bar) | + LM_RC_BAR_CFG_APERTURE(bar, bar_aperture_mask[bar] + 2)); + if (size + cpu_addr >= SZ_4G) { + if (!(flags & IORESOURCE_PREFETCH)) + value |= LM_RC_BAR_CFG_CTRL_MEM_64BITS(bar); + value |= LM_RC_BAR_CFG_CTRL_PREF_MEM_64BITS(bar); + } else { + if (!(flags & IORESOURCE_PREFETCH)) + value |= LM_RC_BAR_CFG_CTRL_MEM_32BITS(bar); + value |= LM_RC_BAR_CFG_CTRL_PREF_MEM_32BITS(bar); + } + + value |= LM_RC_BAR_CFG_APERTURE(bar, aperture); + cdns_pcie_writel(pcie, CDNS_PCIE_LM_RC_BAR_CFG, value); + + return 0; +} + +static enum cdns_pcie_rp_bar +cdns_pcie_host_find_min_bar(struct cdns_pcie_rc *rc, u64 size) +{ + enum cdns_pcie_rp_bar bar, sel_bar; + + sel_bar = RP_BAR_UNDEFINED; + for (bar = RP_BAR0; bar <= RP_NO_BAR; bar++) { + if (!rc->avail_ib_bar[bar]) + continue; + + if (size <= bar_max_size[bar]) { + if (sel_bar == RP_BAR_UNDEFINED) { + sel_bar = bar; + continue; + } + + if (bar_max_size[bar] < bar_max_size[sel_bar]) + sel_bar = bar; + } + } + + return sel_bar; +} + +static enum cdns_pcie_rp_bar +cdns_pcie_host_find_max_bar(struct cdns_pcie_rc *rc, u64 size) +{ + enum cdns_pcie_rp_bar bar, sel_bar; + + sel_bar = RP_BAR_UNDEFINED; + for (bar = RP_BAR0; bar <= RP_NO_BAR; bar++) { + if (!rc->avail_ib_bar[bar]) + continue; + + if (size >= bar_max_size[bar]) { + if (sel_bar == RP_BAR_UNDEFINED) { + sel_bar = bar; + continue; + } + + if (bar_max_size[bar] > bar_max_size[sel_bar]) + sel_bar = bar; + } + } + + return sel_bar; +} + +static int cdns_pcie_host_bar_config(struct cdns_pcie_rc *rc, + struct resource_entry *entry) +{ + u64 cpu_addr, pci_addr, size, winsize; + struct cdns_pcie *pcie = &rc->pcie; + struct device *dev = pcie->dev; + enum cdns_pcie_rp_bar bar; + unsigned long flags; + int ret; + + cpu_addr = entry->res->start; + pci_addr = entry->res->start - entry->offset; + flags = entry->res->flags; + size = resource_size(entry->res); + + if (entry->offset) { + dev_err(dev, "PCI addr: %llx must be equal to CPU addr: %llx\n", + pci_addr, cpu_addr); + return -EINVAL; + } + + while (size > 0) { + /* + * Try to find a minimum BAR whose size is greater than + * or equal to the remaining resource_entry size. This will + * fail if the size of each of the available BARs is less than + * the remaining resource_entry size. + * If a minimum BAR is found, IB ATU will be configured and + * exited. + */ + bar = cdns_pcie_host_find_min_bar(rc, size); + if (bar != RP_BAR_UNDEFINED) { + ret = cdns_pcie_host_bar_ib_config(rc, bar, cpu_addr, + size, flags); + if (ret) + dev_err(dev, "IB BAR: %d config failed\n", bar); + return ret; + } + + /* + * If the control reaches here, it would mean the remaining + * resource_entry size cannot be fitted in a single BAR. So we + * find a maximum BAR whose size is less than or equal to the + * remaining resource_entry size and split the resource entry + * so that part of resource entry is fitted inside the maximum + * BAR. The remaining size would be fitted during the next + * iteration of the loop. + * If a maximum BAR is not found, there is no way we can fit + * this resource_entry, so we error out. + */ + bar = cdns_pcie_host_find_max_bar(rc, size); + if (bar == RP_BAR_UNDEFINED) { + dev_err(dev, "No free BAR to map cpu_addr %llx\n", + cpu_addr); + return -EINVAL; + } + + winsize = bar_max_size[bar]; + ret = cdns_pcie_host_bar_ib_config(rc, bar, cpu_addr, winsize, + flags); + if (ret) { + dev_err(dev, "IB BAR: %d config failed\n", bar); + return ret; + } + + size -= winsize; + cpu_addr += winsize; + } + + return 0; +} + +static int cdns_pcie_host_dma_ranges_cmp(void *priv, struct list_head *a, struct list_head *b) +{ + struct resource_entry *entry1, *entry2; + + entry1 = container_of(a, struct resource_entry, node); + entry2 = container_of(b, struct resource_entry, node); + + return resource_size(entry2->res) - resource_size(entry1->res); +} + +static int cdns_pcie_host_map_dma_ranges(struct cdns_pcie_rc *rc) +{ + struct cdns_pcie *pcie = &rc->pcie; + struct device *dev = pcie->dev; + struct device_node *np = dev->of_node; + struct pci_host_bridge *bridge; + struct resource_entry *entry; + u32 no_bar_nbits = 32; + int err; + + bridge = pci_host_bridge_from_priv(rc); + if (!bridge) + return -ENOMEM; + + if (list_empty(&bridge->dma_ranges)) { + of_property_read_u32(np, "cdns,no-bar-match-nbits", + &no_bar_nbits); + err = cdns_pcie_host_bar_ib_config(rc, RP_NO_BAR, 0x0, + (u64)1 << no_bar_nbits, 0); + if (err) + dev_err(dev, "IB BAR: %d config failed\n", RP_NO_BAR); + return err; + } + + list_sort(NULL, &bridge->dma_ranges, cdns_pcie_host_dma_ranges_cmp); + + resource_list_for_each_entry(entry, &bridge->dma_ranges) { + err = cdns_pcie_host_bar_config(rc, entry); + if (err) { + dev_err(dev, "Fail to configure IB using dma-ranges\n"); + return err; + } + } + + return 0; +} + static int cdns_pcie_host_init_address_translation(struct cdns_pcie_rc *rc) { struct cdns_pcie *pcie = &rc->pcie; @@ -154,16 +378,9 @@ static int cdns_pcie_host_init_address_translation(struct cdns_pcie_rc *rc) r++; } - /* - * Set Root Port no BAR match Inbound Translation registers: - * needed for MSI and DMA. - * Root Port BAR0 and BAR1 are disabled, hence no need to set their - * inbound translation registers. - */ - addr0 = CDNS_PCIE_AT_IB_RP_BAR_ADDR0_NBITS(rc->no_bar_nbits); - addr1 = 0; - cdns_pcie_writel(pcie, CDNS_PCIE_AT_IB_RP_BAR_ADDR0(RP_NO_BAR), addr0); - cdns_pcie_writel(pcie, CDNS_PCIE_AT_IB_RP_BAR_ADDR1(RP_NO_BAR), addr1); + err = cdns_pcie_host_map_dma_ranges(rc); + if (err) + return err; return 0; } @@ -173,10 +390,16 @@ static int cdns_pcie_host_init(struct device *dev, struct cdns_pcie_rc *rc) { struct resource *bus_range = NULL; + struct pci_host_bridge *bridge; int err; + bridge = pci_host_bridge_from_priv(rc); + if (!bridge) + return -ENOMEM; + /* Parse our PCI ranges and request their resources */ - err = pci_parse_request_of_pci_ranges(dev, resources, NULL, &bus_range); + err = pci_parse_request_of_pci_ranges(dev, resources, + &bridge->dma_ranges, &bus_range); if (err) return err; @@ -205,6 +428,7 @@ int cdns_pcie_host_setup(struct cdns_pcie_rc *rc) struct device_node *np = dev->of_node; struct pci_host_bridge *bridge; struct list_head resources; + enum cdns_pcie_rp_bar bar; struct cdns_pcie *pcie; struct resource *res; int ret; @@ -216,9 +440,6 @@ int cdns_pcie_host_setup(struct cdns_pcie_rc *rc) pcie = &rc->pcie; pcie->is_rc = true; - rc->no_bar_nbits = 32; - of_property_read_u32(np, "cdns,no-bar-match-nbits", &rc->no_bar_nbits); - rc->vendor_id = 0xffff; of_property_read_u32(np, "vendor-id", &rc->vendor_id); @@ -248,6 +469,9 @@ int cdns_pcie_host_setup(struct cdns_pcie_rc *rc) pcie->mem_res = res; + for (bar = RP_BAR0; bar <= RP_NO_BAR; bar++) + rc->avail_ib_bar[bar] = true; + ret = cdns_pcie_host_init(dev, &resources, rc); if (ret) goto err_init; diff --git a/drivers/pci/controller/cadence/pcie-cadence.h b/drivers/pci/controller/cadence/pcie-cadence.h index df14ad002fe92..bc49c22e48a94 100644 --- a/drivers/pci/controller/cadence/pcie-cadence.h +++ b/drivers/pci/controller/cadence/pcie-cadence.h @@ -87,6 +87,20 @@ #define CDNS_PCIE_LM_BAR_CFG_CTRL_MEM_64BITS 0x6 #define CDNS_PCIE_LM_BAR_CFG_CTRL_PREFETCH_MEM_64BITS 0x7 +#define LM_RC_BAR_CFG_CTRL_DISABLED(bar) \ + (CDNS_PCIE_LM_BAR_CFG_CTRL_DISABLED << (((bar) * 8) + 6)) +#define LM_RC_BAR_CFG_CTRL_IO_32BITS(bar) \ + (CDNS_PCIE_LM_BAR_CFG_CTRL_IO_32BITS << (((bar) * 8) + 6)) +#define LM_RC_BAR_CFG_CTRL_MEM_32BITS(bar) \ + (CDNS_PCIE_LM_BAR_CFG_CTRL_MEM_32BITS << (((bar) * 8) + 6)) +#define LM_RC_BAR_CFG_CTRL_PREF_MEM_32BITS(bar) \ + (CDNS_PCIE_LM_BAR_CFG_CTRL_PREFETCH_MEM_32BITS << (((bar) * 8) + 6)) +#define LM_RC_BAR_CFG_CTRL_MEM_64BITS(bar) \ + (CDNS_PCIE_LM_BAR_CFG_CTRL_MEM_64BITS << (((bar) * 8) + 6)) +#define LM_RC_BAR_CFG_CTRL_PREF_MEM_64BITS(bar) \ + (CDNS_PCIE_LM_BAR_CFG_CTRL_PREFETCH_MEM_64BITS << (((bar) * 8) + 6)) +#define LM_RC_BAR_CFG_APERTURE(bar, aperture) \ + (((aperture) - 2) << ((bar) * 8)) /* * Endpoint Function Registers (PCI configuration space for endpoint functions) @@ -170,11 +184,19 @@ #define CDNS_PCIE_AT_LINKDOWN (CDNS_PCIE_AT_BASE + 0x0824) enum cdns_pcie_rp_bar { + RP_BAR_UNDEFINED = -1, RP_BAR0, RP_BAR1, RP_NO_BAR }; +#define CDNS_PCIE_RP_MAX_IB 0x3 + +struct cdns_pcie_rp_ib_bar { + u64 size; + bool free; +}; + /* Endpoint Function BAR Inbound PCIe to AXI Address Translation Register */ #define CDNS_PCIE_AT_IB_EP_FUNC_BAR_ADDR0(fn, bar) \ (CDNS_PCIE_AT_BASE + 0x0840 + (fn) * 0x0040 + (bar) * 0x0008) @@ -251,19 +273,19 @@ struct cdns_pcie { * @bus_range: first/last buses behind the PCIe host controller * @cfg_base: IO mapped window to access the PCI configuration space of a * single function at a time - * @no_bar_nbits: Number of bits to keep for inbound (PCIe -> CPU) address - * translation (nbits sets into the "no BAR match" register) * @vendor_id: PCI vendor ID * @device_id: PCI device ID + * @avail_ib_bar: Satus of RP_BAR0, RP_BAR1 and RP_NO_BAR if it's free or + * available */ struct cdns_pcie_rc { struct cdns_pcie pcie; struct resource *cfg_res; struct resource *bus_range; void __iomem *cfg_base; - u32 no_bar_nbits; u32 vendor_id; u32 device_id; + bool avail_ib_bar[CDNS_PCIE_RP_MAX_IB]; }; /** -- GitLab From 19abcd790b51b26d775e1170ba2ac086823cceeb Mon Sep 17 00:00:00 2001 From: Kishon Vijay Abraham I Date: Wed, 22 Jul 2020 16:33:04 +0530 Subject: [PATCH 0934/1476] PCI: cadence: Fix cdns_pcie_{host|ep}_setup() error path commit bd22885aa188 ("PCI: cadence: Refactor driver to use as a core library") while refactoring the Cadence PCIe driver to be used as library, removed pm_runtime_get_sync() from cdns_pcie_ep_setup() and cdns_pcie_host_setup() but missed to remove the corresponding pm_runtime_put_sync() in the error path. Fix it here. Link: https://lore.kernel.org/r/20200722110317.4744-3-kishon@ti.com Fixes: bd22885aa188 ("PCI: cadence: Refactor driver to use as a core library") Signed-off-by: Kishon Vijay Abraham I Signed-off-by: Lorenzo Pieralisi Reviewed-by: Rob Herring --- drivers/pci/controller/cadence/pcie-cadence-ep.c | 9 ++------- drivers/pci/controller/cadence/pcie-cadence-host.c | 6 +----- 2 files changed, 3 insertions(+), 12 deletions(-) diff --git a/drivers/pci/controller/cadence/pcie-cadence-ep.c b/drivers/pci/controller/cadence/pcie-cadence-ep.c index 1c15c8352125e..4a829ccff7d0b 100644 --- a/drivers/pci/controller/cadence/pcie-cadence-ep.c +++ b/drivers/pci/controller/cadence/pcie-cadence-ep.c @@ -8,7 +8,6 @@ #include #include #include -#include #include #include "pcie-cadence.h" @@ -440,8 +439,7 @@ int cdns_pcie_ep_setup(struct cdns_pcie_ep *ep) epc = devm_pci_epc_create(dev, &cdns_pcie_epc_ops); if (IS_ERR(epc)) { dev_err(dev, "failed to create epc device\n"); - ret = PTR_ERR(epc); - goto err_init; + return PTR_ERR(epc); } epc_set_drvdata(epc, ep); @@ -453,7 +451,7 @@ int cdns_pcie_ep_setup(struct cdns_pcie_ep *ep) resource_size(pcie->mem_res), PAGE_SIZE); if (ret < 0) { dev_err(dev, "failed to initialize the memory space\n"); - goto err_init; + return ret; } ep->irq_cpu_addr = pci_epc_mem_alloc_addr(epc, &ep->irq_phys_addr, @@ -472,8 +470,5 @@ int cdns_pcie_ep_setup(struct cdns_pcie_ep *ep) free_epc_mem: pci_epc_mem_exit(epc); - err_init: - pm_runtime_put_sync(dev); - return ret; } diff --git a/drivers/pci/controller/cadence/pcie-cadence-host.c b/drivers/pci/controller/cadence/pcie-cadence-host.c index 2f599eb62bcfd..61161d2de96fa 100644 --- a/drivers/pci/controller/cadence/pcie-cadence-host.c +++ b/drivers/pci/controller/cadence/pcie-cadence-host.c @@ -8,7 +8,6 @@ #include #include #include -#include #include "pcie-cadence.h" @@ -474,7 +473,7 @@ int cdns_pcie_host_setup(struct cdns_pcie_rc *rc) ret = cdns_pcie_host_init(dev, &resources, rc); if (ret) - goto err_init; + return ret; list_splice_init(&resources, &bridge->windows); bridge->dev.parent = dev; @@ -492,8 +491,5 @@ int cdns_pcie_host_setup(struct cdns_pcie_rc *rc) err_host_probe: pci_free_resource_list(&resources); - err_init: - pm_runtime_put_sync(dev); - return ret; } -- GitLab From 229f5879facf96e5640c0385f62b8cb5f27b8a43 Mon Sep 17 00:00:00 2001 From: Kishon Vijay Abraham I Date: Wed, 22 Jul 2020 16:33:05 +0530 Subject: [PATCH 0935/1476] linux/kernel.h: Add PTR_ALIGN_DOWN macro Add a macro for aligning down a pointer. This is useful to get an aligned register address when a device allows only word access and doesn't allow half word or byte access. Link: https://lore.kernel.org/r/20200722110317.4744-4-kishon@ti.com Signed-off-by: Kishon Vijay Abraham I Signed-off-by: Lorenzo Pieralisi Acked-by: Rob Herring --- include/linux/kernel.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/linux/kernel.h b/include/linux/kernel.h index 82d91547d122d..7339a00c895eb 100644 --- a/include/linux/kernel.h +++ b/include/linux/kernel.h @@ -34,6 +34,7 @@ #define ALIGN_DOWN(x, a) __ALIGN_KERNEL((x) - ((a) - 1), (a)) #define __ALIGN_MASK(x, mask) __ALIGN_KERNEL_MASK((x), (mask)) #define PTR_ALIGN(p, a) ((typeof(p))ALIGN((unsigned long)(p), (a))) +#define PTR_ALIGN_DOWN(p, a) ((typeof(p))ALIGN_DOWN((unsigned long)(p), (a))) #define IS_ALIGNED(x, a) (((x) & ((typeof(x))(a) - 1)) == 0) /* generic data direction definitions */ -- GitLab From a8b661eb50abaac97401625d3ff28761bcf1822d Mon Sep 17 00:00:00 2001 From: Kishon Vijay Abraham I Date: Wed, 22 Jul 2020 16:33:06 +0530 Subject: [PATCH 0936/1476] PCI: cadence: Convert all r/w accessors to perform only 32-bit accesses Certain platforms like TI's J721E using Cadence PCIe IP can perform only 32-bit accesses for reading or writing to Cadence registers. Convert all read and write accesses to 32-bit in Cadence PCIe driver in preparation for adding PCIe support in TI's J721E SoC. Also add spin lock to disable interrupts while modifying PCI_STATUS register while raising legacy interrupt since PCI_STATUS is accessible by both remote RC and EP and time between read and write should be minimized. Link: https://lore.kernel.org/r/20200722110317.4744-5-kishon@ti.com Signed-off-by: Kishon Vijay Abraham I Signed-off-by: Lorenzo Pieralisi --- .../pci/controller/cadence/pcie-cadence-ep.c | 4 + drivers/pci/controller/cadence/pcie-cadence.h | 76 ++++++++++++++----- 2 files changed, 62 insertions(+), 18 deletions(-) diff --git a/drivers/pci/controller/cadence/pcie-cadence-ep.c b/drivers/pci/controller/cadence/pcie-cadence-ep.c index 4a829ccff7d0b..c6eb2db94680a 100644 --- a/drivers/pci/controller/cadence/pcie-cadence-ep.c +++ b/drivers/pci/controller/cadence/pcie-cadence-ep.c @@ -228,6 +228,7 @@ static void cdns_pcie_ep_assert_intx(struct cdns_pcie_ep *ep, u8 fn, u8 intx, bool is_asserted) { struct cdns_pcie *pcie = &ep->pcie; + unsigned long flags; u32 offset; u16 status; u8 msg_code; @@ -252,11 +253,13 @@ static void cdns_pcie_ep_assert_intx(struct cdns_pcie_ep *ep, u8 fn, msg_code = MSG_CODE_DEASSERT_INTA + intx; } + spin_lock_irqsave(&ep->lock, flags); status = cdns_pcie_ep_fn_readw(pcie, fn, PCI_STATUS); if (((status & PCI_STATUS_INTERRUPT) != 0) ^ (ep->irq_pending != 0)) { status ^= PCI_STATUS_INTERRUPT; cdns_pcie_ep_fn_writew(pcie, fn, PCI_STATUS, status); } + spin_unlock_irqrestore(&ep->lock, flags); offset = CDNS_PCIE_NORMAL_MSG_ROUTING(MSG_ROUTING_LOCAL) | CDNS_PCIE_NORMAL_MSG_CODE(msg_code) | @@ -464,6 +467,7 @@ int cdns_pcie_ep_setup(struct cdns_pcie_ep *ep) ep->irq_pci_addr = CDNS_PCIE_EP_IRQ_PCI_ADDR_NONE; /* Reserve region 0 for IRQs */ set_bit(0, &ep->ob_region_map); + spin_lock_init(&ep->lock); return 0; diff --git a/drivers/pci/controller/cadence/pcie-cadence.h b/drivers/pci/controller/cadence/pcie-cadence.h index bc49c22e48a94..736b072749814 100644 --- a/drivers/pci/controller/cadence/pcie-cadence.h +++ b/drivers/pci/controller/cadence/pcie-cadence.h @@ -304,6 +304,9 @@ struct cdns_pcie_rc { * @irq_pci_fn: the latest PCI function that has updated the mapping of * the MSI/legacy IRQ dedicated outbound region. * @irq_pending: bitmask of asserted legacy IRQs. + * @lock: spin lock to disable interrupts while modifying PCIe controller + * registers fields (RMW) accessible by both remote RC and EP to + * minimize time between read and write */ struct cdns_pcie_ep { struct cdns_pcie pcie; @@ -315,54 +318,94 @@ struct cdns_pcie_ep { u64 irq_pci_addr; u8 irq_pci_fn; u8 irq_pending; + /* protect writing to PCI_STATUS while raising legacy interrupts */ + spinlock_t lock; }; /* Register access */ -static inline void cdns_pcie_writeb(struct cdns_pcie *pcie, u32 reg, u8 value) +static inline void cdns_pcie_writel(struct cdns_pcie *pcie, u32 reg, u32 value) { - writeb(value, pcie->reg_base + reg); + writel(value, pcie->reg_base + reg); } -static inline void cdns_pcie_writew(struct cdns_pcie *pcie, u32 reg, u16 value) +static inline u32 cdns_pcie_readl(struct cdns_pcie *pcie, u32 reg) { - writew(value, pcie->reg_base + reg); + return readl(pcie->reg_base + reg); } -static inline void cdns_pcie_writel(struct cdns_pcie *pcie, u32 reg, u32 value) +static inline u32 cdns_pcie_read_sz(void __iomem *addr, int size) { - writel(value, pcie->reg_base + reg); + void __iomem *aligned_addr = PTR_ALIGN_DOWN(addr, 0x4); + unsigned int offset = (unsigned long)addr & 0x3; + u32 val = readl(aligned_addr); + + if (!IS_ALIGNED((uintptr_t)addr, size)) { + pr_warn("Address %p and size %d are not aligned\n", addr, size); + return 0; + } + + if (size > 2) + return val; + + return (val >> (8 * offset)) & ((1 << (size * 8)) - 1); } -static inline u32 cdns_pcie_readl(struct cdns_pcie *pcie, u32 reg) +static inline void cdns_pcie_write_sz(void __iomem *addr, int size, u32 value) { - return readl(pcie->reg_base + reg); + void __iomem *aligned_addr = PTR_ALIGN_DOWN(addr, 0x4); + unsigned int offset = (unsigned long)addr & 0x3; + u32 mask; + u32 val; + + if (!IS_ALIGNED((uintptr_t)addr, size)) { + pr_warn("Address %p and size %d are not aligned\n", addr, size); + return; + } + + if (size > 2) { + writel(value, addr); + return; + } + + mask = ~(((1 << (size * 8)) - 1) << (offset * 8)); + val = readl(aligned_addr) & mask; + val |= value << (offset * 8); + writel(val, aligned_addr); } /* Root Port register access */ static inline void cdns_pcie_rp_writeb(struct cdns_pcie *pcie, u32 reg, u8 value) { - writeb(value, pcie->reg_base + CDNS_PCIE_RP_BASE + reg); + void __iomem *addr = pcie->reg_base + CDNS_PCIE_RP_BASE + reg; + + cdns_pcie_write_sz(addr, 0x1, value); } static inline void cdns_pcie_rp_writew(struct cdns_pcie *pcie, u32 reg, u16 value) { - writew(value, pcie->reg_base + CDNS_PCIE_RP_BASE + reg); + void __iomem *addr = pcie->reg_base + CDNS_PCIE_RP_BASE + reg; + + cdns_pcie_write_sz(addr, 0x2, value); } /* Endpoint Function register access */ static inline void cdns_pcie_ep_fn_writeb(struct cdns_pcie *pcie, u8 fn, u32 reg, u8 value) { - writeb(value, pcie->reg_base + CDNS_PCIE_EP_FUNC_BASE(fn) + reg); + void __iomem *addr = pcie->reg_base + CDNS_PCIE_EP_FUNC_BASE(fn) + reg; + + cdns_pcie_write_sz(addr, 0x1, value); } static inline void cdns_pcie_ep_fn_writew(struct cdns_pcie *pcie, u8 fn, u32 reg, u16 value) { - writew(value, pcie->reg_base + CDNS_PCIE_EP_FUNC_BASE(fn) + reg); + void __iomem *addr = pcie->reg_base + CDNS_PCIE_EP_FUNC_BASE(fn) + reg; + + cdns_pcie_write_sz(addr, 0x2, value); } static inline void cdns_pcie_ep_fn_writel(struct cdns_pcie *pcie, u8 fn, @@ -371,14 +414,11 @@ static inline void cdns_pcie_ep_fn_writel(struct cdns_pcie *pcie, u8 fn, writel(value, pcie->reg_base + CDNS_PCIE_EP_FUNC_BASE(fn) + reg); } -static inline u8 cdns_pcie_ep_fn_readb(struct cdns_pcie *pcie, u8 fn, u32 reg) -{ - return readb(pcie->reg_base + CDNS_PCIE_EP_FUNC_BASE(fn) + reg); -} - static inline u16 cdns_pcie_ep_fn_readw(struct cdns_pcie *pcie, u8 fn, u32 reg) { - return readw(pcie->reg_base + CDNS_PCIE_EP_FUNC_BASE(fn) + reg); + void __iomem *addr = pcie->reg_base + CDNS_PCIE_EP_FUNC_BASE(fn) + reg; + + return cdns_pcie_read_sz(addr, 0x2); } static inline u32 cdns_pcie_ep_fn_readl(struct cdns_pcie *pcie, u8 fn, u32 reg) -- GitLab From 40d957e6f9eb3a8a585007b8b730340c829afbdb Mon Sep 17 00:00:00 2001 From: Kishon Vijay Abraham I Date: Wed, 22 Jul 2020 16:33:07 +0530 Subject: [PATCH 0937/1476] PCI: cadence: Add support to start link and verify link status Add cdns_pcie_ops to start link and verify link status. The registers to start link and to check link status is in Platform specific PCIe wrapper. Add support for platform specific drivers to add callback functions for the PCIe Cadence core to start link and verify link status. Link: https://lore.kernel.org/r/20200722110317.4744-6-kishon@ti.com Signed-off-by: Kishon Vijay Abraham I Signed-off-by: Lorenzo Pieralisi Reviewed-by: Rob Herring --- .../pci/controller/cadence/pcie-cadence-ep.c | 8 ++++ .../controller/cadence/pcie-cadence-host.c | 28 ++++++++++++++ drivers/pci/controller/cadence/pcie-cadence.h | 37 ++++++++++++++++++- 3 files changed, 72 insertions(+), 1 deletion(-) diff --git a/drivers/pci/controller/cadence/pcie-cadence-ep.c b/drivers/pci/controller/cadence/pcie-cadence-ep.c index c6eb2db94680a..034cb3cf726ea 100644 --- a/drivers/pci/controller/cadence/pcie-cadence-ep.c +++ b/drivers/pci/controller/cadence/pcie-cadence-ep.c @@ -357,8 +357,10 @@ static int cdns_pcie_ep_start(struct pci_epc *epc) { struct cdns_pcie_ep *ep = epc_get_drvdata(epc); struct cdns_pcie *pcie = &ep->pcie; + struct device *dev = pcie->dev; struct pci_epf *epf; u32 cfg; + int ret; /* * BIT(0) is hardwired to 1, hence function 0 is always enabled @@ -369,6 +371,12 @@ static int cdns_pcie_ep_start(struct pci_epc *epc) cfg |= BIT(epf->func_no); cdns_pcie_writel(pcie, CDNS_PCIE_LM_EP_FUNC_CFG, cfg); + ret = cdns_pcie_start_link(pcie); + if (ret) { + dev_err(dev, "Failed to start link\n"); + return ret; + } + return 0; } diff --git a/drivers/pci/controller/cadence/pcie-cadence-host.c b/drivers/pci/controller/cadence/pcie-cadence-host.c index 61161d2de96fa..4e650c7a00323 100644 --- a/drivers/pci/controller/cadence/pcie-cadence-host.c +++ b/drivers/pci/controller/cadence/pcie-cadence-host.c @@ -3,6 +3,7 @@ // Cadence PCIe host controller driver. // Author: Cyrille Pitchen +#include #include #include #include @@ -420,6 +421,23 @@ static int cdns_pcie_host_init(struct device *dev, return err; } +static int cdns_pcie_host_wait_for_link(struct cdns_pcie *pcie) +{ + struct device *dev = pcie->dev; + int retries; + + /* Check if the link is up or not */ + for (retries = 0; retries < LINK_WAIT_MAX_RETRIES; retries++) { + if (cdns_pcie_link_up(pcie)) { + dev_info(dev, "Link up\n"); + return 0; + } + usleep_range(LINK_WAIT_USLEEP_MIN, LINK_WAIT_USLEEP_MAX); + } + + return -ETIMEDOUT; +} + int cdns_pcie_host_setup(struct cdns_pcie_rc *rc) { struct device *dev = rc->pcie.dev; @@ -468,6 +486,16 @@ int cdns_pcie_host_setup(struct cdns_pcie_rc *rc) pcie->mem_res = res; + ret = cdns_pcie_start_link(pcie); + if (ret) { + dev_err(dev, "Failed to start link\n"); + return ret; + } + + ret = cdns_pcie_host_wait_for_link(pcie); + if (ret) + dev_dbg(dev, "PCIe link never came up\n"); + for (bar = RP_BAR0; bar <= RP_NO_BAR; bar++) rc->avail_ib_bar[bar] = true; diff --git a/drivers/pci/controller/cadence/pcie-cadence.h b/drivers/pci/controller/cadence/pcie-cadence.h index 736b072749814..36c493fa4fdef 100644 --- a/drivers/pci/controller/cadence/pcie-cadence.h +++ b/drivers/pci/controller/cadence/pcie-cadence.h @@ -10,6 +10,11 @@ #include #include +/* Parameters for the waiting for link up routine */ +#define LINK_WAIT_MAX_RETRIES 10 +#define LINK_WAIT_USLEEP_MIN 90000 +#define LINK_WAIT_USLEEP_MAX 100000 + /* * Local Management Registers */ @@ -245,12 +250,20 @@ enum cdns_pcie_msg_routing { MSG_ROUTING_GATHER, }; +struct cdns_pcie_ops { + int (*start_link)(struct cdns_pcie *pcie); + void (*stop_link)(struct cdns_pcie *pcie); + bool (*link_up)(struct cdns_pcie *pcie); +}; + /** * struct cdns_pcie - private data for Cadence PCIe controller drivers * @reg_base: IO mapped register base * @mem_res: start/end offsets in the physical system memory to map PCI accesses * @is_rc: tell whether the PCIe controller mode is Root Complex or Endpoint. * @bus: In Root Complex mode, the bus number + * @ops: Platform specific ops to control various inputs from Cadence PCIe + * wrapper */ struct cdns_pcie { void __iomem *reg_base; @@ -261,7 +274,7 @@ struct cdns_pcie { int phy_count; struct phy **phy; struct device_link **link; - const struct cdns_pcie_common_ops *ops; + const struct cdns_pcie_ops *ops; }; /** @@ -426,6 +439,28 @@ static inline u32 cdns_pcie_ep_fn_readl(struct cdns_pcie *pcie, u8 fn, u32 reg) return readl(pcie->reg_base + CDNS_PCIE_EP_FUNC_BASE(fn) + reg); } +static inline int cdns_pcie_start_link(struct cdns_pcie *pcie) +{ + if (pcie->ops->start_link) + return pcie->ops->start_link(pcie); + + return 0; +} + +static inline void cdns_pcie_stop_link(struct cdns_pcie *pcie) +{ + if (pcie->ops->stop_link) + pcie->ops->stop_link(pcie); +} + +static inline bool cdns_pcie_link_up(struct cdns_pcie *pcie) +{ + if (pcie->ops->link_up) + return pcie->ops->link_up(pcie); + + return true; +} + #ifdef CONFIG_PCIE_CADENCE_HOST int cdns_pcie_host_setup(struct cdns_pcie_rc *rc); #else -- GitLab From c4c10c0125a705c606cb0cc85e18f13b490d026f Mon Sep 17 00:00:00 2001 From: Kishon Vijay Abraham I Date: Wed, 22 Jul 2020 16:33:08 +0530 Subject: [PATCH 0938/1476] PCI: cadence: Allow pci_host_bridge to have custom pci_ops Certain platforms like TI's J721E allows only 32-bit configuration space access. In such cases pci_generic_config_read and pci_generic_config_write cannot be used. Add support in Cadence core to let pci_host_bridge have custom pci_ops. Link: https://lore.kernel.org/r/20200722110317.4744-7-kishon@ti.com Signed-off-by: Kishon Vijay Abraham I Signed-off-by: Lorenzo Pieralisi --- drivers/pci/controller/cadence/pcie-cadence-host.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/pci/controller/cadence/pcie-cadence-host.c b/drivers/pci/controller/cadence/pcie-cadence-host.c index 4e650c7a00323..f889914563fe8 100644 --- a/drivers/pci/controller/cadence/pcie-cadence-host.c +++ b/drivers/pci/controller/cadence/pcie-cadence-host.c @@ -506,7 +506,8 @@ int cdns_pcie_host_setup(struct cdns_pcie_rc *rc) list_splice_init(&resources, &bridge->windows); bridge->dev.parent = dev; bridge->busnr = pcie->bus; - bridge->ops = &cdns_pcie_host_ops; + if (!bridge->ops) + bridge->ops = &cdns_pcie_host_ops; bridge->map_irq = of_irq_parse_and_map_pci; bridge->swizzle_irq = pci_common_swizzle; -- GitLab From f87b8383da6022cf2e42fc7f40f4d782cc85144b Mon Sep 17 00:00:00 2001 From: Kishon Vijay Abraham I Date: Wed, 22 Jul 2020 16:33:09 +0530 Subject: [PATCH 0939/1476] dt-bindings: PCI: cadence: Remove "mem" from reg binding "mem" is not a memory resource and it overlaps with PCIe config space and memory region. Remove "mem" from reg binding. Link: https://lore.kernel.org/r/20200722110317.4744-8-kishon@ti.com Signed-off-by: Kishon Vijay Abraham I Signed-off-by: Lorenzo Pieralisi Reviewed-by: Rob Herring --- .../devicetree/bindings/pci/cdns,cdns-pcie-host.yaml | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/Documentation/devicetree/bindings/pci/cdns,cdns-pcie-host.yaml b/Documentation/devicetree/bindings/pci/cdns,cdns-pcie-host.yaml index 84a8f095d0311..6d67067843bfc 100644 --- a/Documentation/devicetree/bindings/pci/cdns,cdns-pcie-host.yaml +++ b/Documentation/devicetree/bindings/pci/cdns,cdns-pcie-host.yaml @@ -18,13 +18,12 @@ properties: const: cdns,cdns-pcie-host reg: - maxItems: 3 + maxItems: 2 reg-names: items: - const: reg - const: cfg - - const: mem msi-parent: true @@ -49,9 +48,8 @@ examples: device-id = <0x0200>; reg = <0x0 0xfb000000 0x0 0x01000000>, - <0x0 0x41000000 0x0 0x00001000>, - <0x0 0x40000000 0x0 0x04000000>; - reg-names = "reg", "cfg", "mem"; + <0x0 0x41000000 0x0 0x00001000>; + reg-names = "reg", "cfg"; ranges = <0x02000000 0x0 0x42000000 0x0 0x42000000 0x0 0x1000000>, <0x01000000 0x0 0x43000000 0x0 0x43000000 0x0 0x0010000>; -- GitLab From d07701a17aec3e3a777b882b46b0af6650f7cd1d Mon Sep 17 00:00:00 2001 From: Kishon Vijay Abraham I Date: Wed, 22 Jul 2020 16:33:10 +0530 Subject: [PATCH 0940/1476] PCI: cadence: Add new *ops* for CPU addr fixup Cadence driver uses "mem" memory resource to obtain the offset of configuration space address region, memory space address region and message space address region. The obtained offset is used to program the Address Translation Unit (ATU). However certain platforms like TI's J721E SoC require the absolute address to be programmed in the ATU and not just the offset. Add new *ops* for CPU addr fixup for the platform drivers to provide the correct address to be programmed in the ATU. Link: https://lore.kernel.org/r/20200722110317.4744-9-kishon@ti.com Signed-off-by: Kishon Vijay Abraham I Signed-off-by: Lorenzo Pieralisi --- .../pci/controller/cadence/pcie-cadence-host.c | 15 ++++----------- .../pci/controller/cadence/pcie-cadence-plat.c | 13 +++++++++++++ drivers/pci/controller/cadence/pcie-cadence.c | 8 ++++++-- drivers/pci/controller/cadence/pcie-cadence.h | 1 + 4 files changed, 24 insertions(+), 13 deletions(-) diff --git a/drivers/pci/controller/cadence/pcie-cadence-host.c b/drivers/pci/controller/cadence/pcie-cadence-host.c index f889914563fe8..6069a46c8ef1a 100644 --- a/drivers/pci/controller/cadence/pcie-cadence-host.c +++ b/drivers/pci/controller/cadence/pcie-cadence-host.c @@ -328,15 +328,14 @@ static int cdns_pcie_host_map_dma_ranges(struct cdns_pcie_rc *rc) static int cdns_pcie_host_init_address_translation(struct cdns_pcie_rc *rc) { struct cdns_pcie *pcie = &rc->pcie; - struct resource *mem_res = pcie->mem_res; struct resource *bus_range = rc->bus_range; struct resource *cfg_res = rc->cfg_res; struct device *dev = pcie->dev; struct device_node *np = dev->of_node; struct of_pci_range_parser parser; + u64 cpu_addr = cfg_res->start; struct of_pci_range range; u32 addr0, addr1, desc1; - u64 cpu_addr; int r, err; /* @@ -349,7 +348,9 @@ static int cdns_pcie_host_init_address_translation(struct cdns_pcie_rc *rc) cdns_pcie_writel(pcie, CDNS_PCIE_AT_OB_REGION_PCI_ADDR1(0), addr1); cdns_pcie_writel(pcie, CDNS_PCIE_AT_OB_REGION_DESC1(0), desc1); - cpu_addr = cfg_res->start - mem_res->start; + if (pcie->ops->cpu_addr_fixup) + cpu_addr = pcie->ops->cpu_addr_fixup(pcie, cpu_addr); + addr0 = CDNS_PCIE_AT_OB_REGION_CPU_ADDR0_NBITS(12) | (lower_32_bits(cpu_addr) & GENMASK(31, 8)); addr1 = upper_32_bits(cpu_addr); @@ -478,14 +479,6 @@ int cdns_pcie_host_setup(struct cdns_pcie_rc *rc) } rc->cfg_res = res; - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "mem"); - if (!res) { - dev_err(dev, "missing \"mem\"\n"); - return -EINVAL; - } - - pcie->mem_res = res; - ret = cdns_pcie_start_link(pcie); if (ret) { dev_err(dev, "Failed to start link\n"); diff --git a/drivers/pci/controller/cadence/pcie-cadence-plat.c b/drivers/pci/controller/cadence/pcie-cadence-plat.c index f5c6bf6dfcb8b..6f5f07b3eed1e 100644 --- a/drivers/pci/controller/cadence/pcie-cadence-plat.c +++ b/drivers/pci/controller/cadence/pcie-cadence-plat.c @@ -13,6 +13,8 @@ #include #include "pcie-cadence.h" +#define CDNS_PLAT_CPU_TO_BUS_ADDR 0x0FFFFFFF + /** * struct cdns_plat_pcie - private data for this PCIe platform driver * @pcie: Cadence PCIe controller @@ -30,6 +32,15 @@ struct cdns_plat_pcie_of_data { static const struct of_device_id cdns_plat_pcie_of_match[]; +static u64 cdns_plat_cpu_addr_fixup(struct cdns_pcie *pcie, u64 cpu_addr) +{ + return cpu_addr & CDNS_PLAT_CPU_TO_BUS_ADDR; +} + +static const struct cdns_pcie_ops cdns_plat_ops = { + .cpu_addr_fixup = cdns_plat_cpu_addr_fixup, +}; + static int cdns_plat_pcie_probe(struct platform_device *pdev) { const struct cdns_plat_pcie_of_data *data; @@ -66,6 +77,7 @@ static int cdns_plat_pcie_probe(struct platform_device *pdev) rc = pci_host_bridge_priv(bridge); rc->pcie.dev = dev; + rc->pcie.ops = &cdns_plat_ops; cdns_plat_pcie->pcie = &rc->pcie; cdns_plat_pcie->is_rc = is_rc; @@ -93,6 +105,7 @@ static int cdns_plat_pcie_probe(struct platform_device *pdev) return -ENOMEM; ep->pcie.dev = dev; + ep->pcie.ops = &cdns_plat_ops; cdns_plat_pcie->pcie = &ep->pcie; cdns_plat_pcie->is_rc = is_rc; diff --git a/drivers/pci/controller/cadence/pcie-cadence.c b/drivers/pci/controller/cadence/pcie-cadence.c index cd795f6fc1e23..8a02981fd4569 100644 --- a/drivers/pci/controller/cadence/pcie-cadence.c +++ b/drivers/pci/controller/cadence/pcie-cadence.c @@ -73,7 +73,9 @@ void cdns_pcie_set_outbound_region(struct cdns_pcie *pcie, u8 fn, cdns_pcie_writel(pcie, CDNS_PCIE_AT_OB_REGION_DESC1(r), desc1); /* Set the CPU address */ - cpu_addr -= pcie->mem_res->start; + if (pcie->ops->cpu_addr_fixup) + cpu_addr = pcie->ops->cpu_addr_fixup(pcie, cpu_addr); + addr0 = CDNS_PCIE_AT_OB_REGION_CPU_ADDR0_NBITS(nbits) | (lower_32_bits(cpu_addr) & GENMASK(31, 8)); addr1 = upper_32_bits(cpu_addr); @@ -100,7 +102,9 @@ void cdns_pcie_set_outbound_region_for_normal_msg(struct cdns_pcie *pcie, u8 fn, } /* Set the CPU address */ - cpu_addr -= pcie->mem_res->start; + if (pcie->ops->cpu_addr_fixup) + cpu_addr = pcie->ops->cpu_addr_fixup(pcie, cpu_addr); + addr0 = CDNS_PCIE_AT_OB_REGION_CPU_ADDR0_NBITS(17) | (lower_32_bits(cpu_addr) & GENMASK(31, 8)); addr1 = upper_32_bits(cpu_addr); diff --git a/drivers/pci/controller/cadence/pcie-cadence.h b/drivers/pci/controller/cadence/pcie-cadence.h index 36c493fa4fdef..7c804ac1dbc2a 100644 --- a/drivers/pci/controller/cadence/pcie-cadence.h +++ b/drivers/pci/controller/cadence/pcie-cadence.h @@ -254,6 +254,7 @@ struct cdns_pcie_ops { int (*start_link)(struct cdns_pcie *pcie); void (*stop_link)(struct cdns_pcie *pcie); bool (*link_up)(struct cdns_pcie *pcie); + u64 (*cpu_addr_fixup)(struct cdns_pcie *pcie, u64 cpu_addr); }; /** -- GitLab From e3bca37d15dca118f2ef1f0a068bb6e07846ea20 Mon Sep 17 00:00:00 2001 From: Kishon Vijay Abraham I Date: Wed, 22 Jul 2020 16:33:11 +0530 Subject: [PATCH 0941/1476] PCI: cadence: Fix updating Vendor ID and Subsystem Vendor ID register Commit 1b79c5284439 ("PCI: cadence: Add host driver for Cadence PCIe controller") in order to update Vendor ID, directly wrote to PCI_VENDOR_ID register. However PCI_VENDOR_ID in root port configuration space is read-only register and writing to it will have no effect. Use local management register to configure Vendor ID and Subsystem Vendor ID. Link: https://lore.kernel.org/r/20200722110317.4744-10-kishon@ti.com Fixes: 1b79c5284439 ("PCI: cadence: Add host driver for Cadence PCIe controller") Signed-off-by: Kishon Vijay Abraham I Signed-off-by: Lorenzo Pieralisi Reviewed-by: Rob Herring --- drivers/pci/controller/cadence/pcie-cadence-host.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/drivers/pci/controller/cadence/pcie-cadence-host.c b/drivers/pci/controller/cadence/pcie-cadence-host.c index 6069a46c8ef1a..89d26324b2a8d 100644 --- a/drivers/pci/controller/cadence/pcie-cadence-host.c +++ b/drivers/pci/controller/cadence/pcie-cadence-host.c @@ -82,6 +82,7 @@ static int cdns_pcie_host_init_root_port(struct cdns_pcie_rc *rc) { struct cdns_pcie *pcie = &rc->pcie; u32 value, ctrl; + u32 id; /* * Set the root complex BAR configuration register: @@ -101,8 +102,12 @@ static int cdns_pcie_host_init_root_port(struct cdns_pcie_rc *rc) cdns_pcie_writel(pcie, CDNS_PCIE_LM_RC_BAR_CFG, value); /* Set root port configuration space */ - if (rc->vendor_id != 0xffff) - cdns_pcie_rp_writew(pcie, PCI_VENDOR_ID, rc->vendor_id); + if (rc->vendor_id != 0xffff) { + id = CDNS_PCIE_LM_ID_VENDOR(rc->vendor_id) | + CDNS_PCIE_LM_ID_SUBSYS(rc->vendor_id); + cdns_pcie_writel(pcie, CDNS_PCIE_LM_ID, id); + } + if (rc->device_id != 0xffff) cdns_pcie_rp_writew(pcie, PCI_DEVICE_ID, rc->device_id); -- GitLab From 98a7fbf391dbef6b11bc0a9039de0e81994ff923 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sat, 6 Jun 2020 23:56:53 -0400 Subject: [PATCH 0942/1476] sparc32: get rid of odd callers of copy_regset_from_user() [a couple of unused variables left behind by the previous version spotted by kernel test robot ] Signed-off-by: Al Viro --- arch/sparc/kernel/ptrace_32.c | 113 ++++++++++++++++++++++------------ 1 file changed, 75 insertions(+), 38 deletions(-) diff --git a/arch/sparc/kernel/ptrace_32.c b/arch/sparc/kernel/ptrace_32.c index f72b7d2c4716d..144e5a6d16aad 100644 --- a/arch/sparc/kernel/ptrace_32.c +++ b/arch/sparc/kernel/ptrace_32.c @@ -137,19 +137,18 @@ static int genregs32_set(struct task_struct *target, if (ret || !count) return ret; - if (pos < 32 * sizeof(u32)) { - if (regwindow32_get(target, regs, uregs)) - return -EFAULT; - ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, - uregs, - 16 * sizeof(u32), 32 * sizeof(u32)); - if (ret) - return ret; - if (regwindow32_set(target, regs, uregs)) - return -EFAULT; - if (!count) - return 0; - } + if (regwindow32_get(target, regs, uregs)) + return -EFAULT; + ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, + uregs, + 16 * sizeof(u32), 32 * sizeof(u32)); + if (ret) + return ret; + if (regwindow32_set(target, regs, uregs)) + return -EFAULT; + if (!count) + return 0; + ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, &psr, 32 * sizeof(u32), 33 * sizeof(u32)); @@ -241,13 +240,11 @@ static int fpregs32_set(struct task_struct *target, user_regset_copyin_ignore(&pos, &count, &kbuf, &ubuf, 32 * sizeof(u32), 33 * sizeof(u32)); - if (!ret && count > 0) { + if (!ret) ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, &target->thread.fsr, 33 * sizeof(u32), 34 * sizeof(u32)); - } - if (!ret) ret = user_regset_copyin_ignore(&pos, &count, &kbuf, &ubuf, 34 * sizeof(u32), -1); @@ -313,6 +310,33 @@ static int getregs_get(struct task_struct *target, 4 * sizeof(u32), 19 * sizeof(u32)); } +static int setregs_set(struct task_struct *target, + const struct user_regset *regset, + unsigned int pos, unsigned int count, + const void *kbuf, const void __user *ubuf) +{ + struct pt_regs *regs = target->thread.kregs; + u32 v[4]; + int ret; + + if (target == current) + flush_user_windows(); + + ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, + v, + 0, 4 * sizeof(u32)); + if (ret) + return ret; + regs->psr = (regs->psr & ~(PSR_ICC | PSR_SYSCALL)) | + (v[0] & (PSR_ICC | PSR_SYSCALL)); + regs->pc = v[1]; + regs->npc = v[2]; + regs->y = v[3]; + return user_regset_copyin(&pos, &count, &kbuf, &ubuf, + regs->u_regs + 1, + 4 * sizeof(u32) , 19 * sizeof(u32)); +} + static int getfpregs_get(struct task_struct *target, const struct user_regset *regset, unsigned int pos, unsigned int count, @@ -340,12 +364,37 @@ static int getfpregs_get(struct task_struct *target, 33 * sizeof(u32), 68 * sizeof(u32)); } +static int setfpregs_set(struct task_struct *target, + const struct user_regset *regset, + unsigned int pos, unsigned int count, + const void *kbuf, const void __user *ubuf) +{ + unsigned long *fpregs = target->thread.float_regs; + int ret; + +#if 0 + if (target == current) + save_and_clear_fpu(); +#endif + ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, + fpregs, + 0, 32 * sizeof(u32)); + if (ret) + return ret; + return user_regset_copyin(&pos, &count, &kbuf, &ubuf, + &target->thread.fsr, + 32 * sizeof(u32), + 33 * sizeof(u32)); +} + static const struct user_regset ptrace32_regsets[] = { [REGSET_GENERAL] = { - .n = 19, .size = sizeof(u32), .get = getregs_get, + .n = 19, .size = sizeof(u32), + .get = getregs_get, .set = setregs_set, }, [REGSET_FP] = { - .n = 68, .size = sizeof(u32), .get = getfpregs_get, + .n = 68, .size = sizeof(u32), + .get = getfpregs_get, .set = setfpregs_set, }, }; @@ -380,12 +429,10 @@ long arch_ptrace(struct task_struct *child, long request, { unsigned long addr2 = current->thread.kregs->u_regs[UREG_I4]; void __user *addr2p; - const struct user_regset_view *view; struct pt_regs __user *pregs; struct fps __user *fps; int ret; - view = task_user_regset_view(current); addr2p = (void __user *) addr2; pregs = (struct pt_regs __user *) addr; fps = (struct fps __user *) addr; @@ -400,15 +447,10 @@ long arch_ptrace(struct task_struct *child, long request, } case PTRACE_SETREGS: { - ret = copy_regset_from_user(child, view, REGSET_GENERAL, - 32 * sizeof(u32), - 4 * sizeof(u32), - &pregs->psr); - if (!ret) - copy_regset_from_user(child, view, REGSET_GENERAL, - 1 * sizeof(u32), - 15 * sizeof(u32), - &pregs->u_regs[0]); + ret = copy_regset_from_user(child, &ptrace32_view, + REGSET_GENERAL, 0, + 19 * sizeof(u32), + pregs); break; } @@ -421,15 +463,10 @@ long arch_ptrace(struct task_struct *child, long request, } case PTRACE_SETFPREGS: { - ret = copy_regset_from_user(child, view, REGSET_FP, - 0 * sizeof(u32), - 32 * sizeof(u32), - &fps->regs[0]); - if (!ret) - ret = copy_regset_from_user(child, view, REGSET_FP, - 33 * sizeof(u32), - 1 * sizeof(u32), - &fps->fsr); + ret = copy_regset_from_user(child, &ptrace32_view, + REGSET_FP, 0, + 33 * sizeof(u32), + fps); break; } -- GitLab From b7e46c527d226bb654df615d742c41b7920d387e Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sun, 7 Jun 2020 00:34:26 -0400 Subject: [PATCH 0943/1476] sparc64: get rid of odd callers of copy_regset_from_user() Signed-off-by: Al Viro --- arch/sparc/kernel/ptrace_64.c | 177 +++++++++++++++++++++++++++------- 1 file changed, 144 insertions(+), 33 deletions(-) diff --git a/arch/sparc/kernel/ptrace_64.c b/arch/sparc/kernel/ptrace_64.c index 1b1910b67ca4e..3c9eee12102ad 100644 --- a/arch/sparc/kernel/ptrace_64.c +++ b/arch/sparc/kernel/ptrace_64.c @@ -541,6 +541,60 @@ static int getregs64_get(struct task_struct *target, return ret; } +static int setregs64_set(struct task_struct *target, + const struct user_regset *regset, + unsigned int pos, unsigned int count, + const void *kbuf, const void __user *ubuf) +{ + struct pt_regs *regs = task_pt_regs(target); + unsigned long y = regs->y; + unsigned long tstate; + int ret; + + if (target == current) + flushw_user(); + + ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, + regs->u_regs + 1, + 0 * sizeof(u64), + 15 * sizeof(u64)); + if (ret) + return ret; + ret =user_regset_copyin_ignore(&pos, &count, &kbuf, &ubuf, + 15 * sizeof(u64), 16 * sizeof(u64)); + if (ret) + return ret; + /* TSTATE */ + ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, + &tstate, + 16 * sizeof(u64), + 17 * sizeof(u64)); + if (ret) + return ret; + /* Only the condition codes and the "in syscall" + * state can be modified in the %tstate register. + */ + tstate &= (TSTATE_ICC | TSTATE_XCC | TSTATE_SYSCALL); + regs->tstate &= ~(TSTATE_ICC | TSTATE_XCC | TSTATE_SYSCALL); + regs->tstate |= tstate; + + /* TPC, TNPC */ + ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, + ®s->tpc, + 17 * sizeof(u64), + 19 * sizeof(u64)); + if (ret) + return ret; + /* Y */ + ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, + &y, + 19 * sizeof(u64), + 20 * sizeof(u64)); + if (!ret) + regs->y = y; + return ret; +} + static const struct user_regset ptrace64_regsets[] = { /* Format is: * G1 --> G7 @@ -549,7 +603,8 @@ static const struct user_regset ptrace64_regsets[] = { * TSTATE, TPC, TNPC, Y */ [REGSET_GENERAL] = { - .n = 20, .size = sizeof(u64), .get = getregs64_get, + .n = 20, .size = sizeof(u64), + .get = getregs64_get, .set = setregs64_set, }, }; @@ -914,6 +969,40 @@ static int getregs_get(struct task_struct *target, 0, 19 * sizeof(u32)); } +static int setregs_set(struct task_struct *target, + const struct user_regset *regset, + unsigned int pos, unsigned int count, + const void *kbuf, const void __user *ubuf) +{ + struct pt_regs *regs = task_pt_regs(target); + unsigned long tstate; + u32 uregs[19]; + int i, ret; + + if (target == current) + flushw_user(); + + ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, + uregs, + 0, 19 * sizeof(u32)); + if (ret) + return ret; + + tstate = regs->tstate; + tstate &= ~(TSTATE_ICC | TSTATE_XCC | TSTATE_SYSCALL); + tstate |= psr_to_tstate_icc(uregs[0]); + if (uregs[0] & PSR_SYSCALL) + tstate |= TSTATE_SYSCALL; + regs->tstate = tstate; + regs->tpc = uregs[1]; + regs->tnpc = uregs[2]; + regs->y = uregs[3]; + + for (i = 1; i < 15; i++) + regs->u_regs[i] = uregs[3 + i]; + return 0; +} + static int getfpregs_get(struct task_struct *target, const struct user_regset *regset, unsigned int pos, unsigned int count, @@ -948,12 +1037,52 @@ static int getfpregs_get(struct task_struct *target, return ret; } +static int setfpregs_set(struct task_struct *target, + const struct user_regset *regset, + unsigned int pos, unsigned int count, + const void *kbuf, const void __user *ubuf) +{ + unsigned long *fpregs = task_thread_info(target)->fpregs; + unsigned long fprs; + int ret; + + if (target == current) + save_and_clear_fpu(); + + fprs = task_thread_info(target)->fpsaved[0]; + + ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, + fpregs, + 0, 32 * sizeof(u32)); + if (!ret) { + compat_ulong_t fsr; + unsigned long val; + + ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, + &fsr, + 32 * sizeof(u32), + 33 * sizeof(u32)); + if (!ret) { + val = task_thread_info(target)->xfsr[0]; + val &= 0xffffffff00000000UL; + val |= fsr; + task_thread_info(target)->xfsr[0] = val; + } + } + + fprs |= (FPRS_FEF | FPRS_DL); + task_thread_info(target)->fpsaved[0] = fprs; + return ret; +} + static const struct user_regset ptrace32_regsets[] = { [REGSET_GENERAL] = { - .n = 19, .size = sizeof(u32), .get = getregs_get, + .n = 19, .size = sizeof(u32), + .get = getregs_get, .set = setregs_set, }, [REGSET_FP] = { - .n = 68, .size = sizeof(u32), .get = getfpregs_get, + .n = 68, .size = sizeof(u32), + .get = getfpregs_get, .set = setfpregs_set, }, }; @@ -992,7 +1121,6 @@ struct compat_fps { long compat_arch_ptrace(struct task_struct *child, compat_long_t request, compat_ulong_t caddr, compat_ulong_t cdata) { - const struct user_regset_view *view = task_user_regset_view(current); compat_ulong_t caddr2 = task_pt_regs(current)->u_regs[UREG_I4]; struct pt_regs32 __user *pregs; struct compat_fps __user *fps; @@ -1017,15 +1145,10 @@ long compat_arch_ptrace(struct task_struct *child, compat_long_t request, break; case PTRACE_SETREGS: - ret = copy_regset_from_user(child, view, REGSET_GENERAL, - 32 * sizeof(u32), - 4 * sizeof(u32), - &pregs->psr); - if (!ret) - ret = copy_regset_from_user(child, view, REGSET_GENERAL, - 1 * sizeof(u32), - 15 * sizeof(u32), - &pregs->u_regs[0]); + ret = copy_regset_from_user(child, &ptrace32_view, + REGSET_GENERAL, 0, + 19 * sizeof(u32), + pregs); break; case PTRACE_GETFPREGS: @@ -1036,15 +1159,10 @@ long compat_arch_ptrace(struct task_struct *child, compat_long_t request, break; case PTRACE_SETFPREGS: - ret = copy_regset_from_user(child, view, REGSET_FP, - 0 * sizeof(u32), - 32 * sizeof(u32), - &fps->regs[0]); - if (!ret) - ret = copy_regset_from_user(child, view, REGSET_FP, - 33 * sizeof(u32), - 1 * sizeof(u32), - &fps->fsr); + ret = copy_regset_from_user(child, &ptrace32_view, + REGSET_FP, 0, + 33 * sizeof(u32), + fps); break; case PTRACE_READTEXT: @@ -1110,17 +1228,10 @@ long arch_ptrace(struct task_struct *child, long request, break; case PTRACE_SETREGS64: - ret = copy_regset_from_user(child, view, REGSET_GENERAL, - 1 * sizeof(u64), - 15 * sizeof(u64), - &pregs->u_regs[0]); - if (!ret) { - /* XXX doesn't handle 'y' register correctly XXX */ - ret = copy_regset_from_user(child, view, REGSET_GENERAL, - 32 * sizeof(u64), - 4 * sizeof(u64), - &pregs->tstate); - } + ret = copy_regset_from_user(child, &ptrace64_view, + REGSET_GENERAL, 0, + 19 * sizeof(u64), + pregs); break; case PTRACE_GETFPREGS64: -- GitLab From c2fa6cf76d204e296e2e295d821500e0b291697d Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Fri, 24 Jul 2020 16:42:04 -0600 Subject: [PATCH 0944/1476] PCI: dwc: hisi: Remove non-ECAM HiSilicon hip05/hip06 driver The HiSilicon non-ECAM PCIe has been broken since March 2016 commit 7e57fd1444bf ("PCI: designware: Move Root Complex setup code to dw_pcie_setup_rc()"). The reason is this commit moved the iATU setup code from dw_pcie_host_init() to dw_pcie_setup_rc(), but the hisi driver never calls dw_pcie_setup_rc(). The result is the PCI memory space is never configured and the driver can't work. It's also clear it has an iATU as the config space accesses use it. There's also no dts file using either "hisilicon,hip05-pcie" or "hisilicon,hip06-pcie". Link: https://lore.kernel.org/r/20200724224204.3249055-1-robh@kernel.org Signed-off-by: Rob Herring Signed-off-by: Lorenzo Pieralisi Cc: Zhou Wang Cc: Lorenzo Pieralisi Cc: Bjorn Helgaas --- drivers/pci/controller/dwc/pcie-hisi.c | 219 ------------------------- 1 file changed, 219 deletions(-) diff --git a/drivers/pci/controller/dwc/pcie-hisi.c b/drivers/pci/controller/dwc/pcie-hisi.c index 0ad4e07dd4c25..5ca86796d43af 100644 --- a/drivers/pci/controller/dwc/pcie-hisi.c +++ b/drivers/pci/controller/dwc/pcie-hisi.c @@ -10,15 +10,10 @@ */ #include #include -#include -#include -#include #include -#include #include #include #include -#include #include "../../pci.h" #if defined(CONFIG_PCI_HISI) || (defined(CONFIG_ACPI) && defined(CONFIG_PCI_QUIRKS)) @@ -118,220 +113,6 @@ const struct pci_ecam_ops hisi_pcie_ops = { #ifdef CONFIG_PCI_HISI -#include "pcie-designware.h" - -#define PCIE_SUBCTRL_SYS_STATE4_REG 0x6818 -#define PCIE_HIP06_CTRL_OFF 0x1000 -#define PCIE_SYS_STATE4 (PCIE_HIP06_CTRL_OFF + 0x31c) -#define PCIE_LTSSM_LINKUP_STATE 0x11 -#define PCIE_LTSSM_STATE_MASK 0x3F - -#define to_hisi_pcie(x) dev_get_drvdata((x)->dev) - -struct hisi_pcie; - -struct pcie_soc_ops { - int (*hisi_pcie_link_up)(struct hisi_pcie *hisi_pcie); -}; - -struct hisi_pcie { - struct dw_pcie *pci; - struct regmap *subctrl; - u32 port_id; - const struct pcie_soc_ops *soc_ops; -}; - -/* HipXX PCIe host only supports 32-bit config access */ -static int hisi_pcie_cfg_read(struct pcie_port *pp, int where, int size, - u32 *val) -{ - u32 reg; - u32 reg_val; - void *walker = ®_val; - struct dw_pcie *pci = to_dw_pcie_from_pp(pp); - - walker += (where & 0x3); - reg = where & ~0x3; - reg_val = dw_pcie_readl_dbi(pci, reg); - - if (size == 1) - *val = *(u8 __force *) walker; - else if (size == 2) - *val = *(u16 __force *) walker; - else if (size == 4) - *val = reg_val; - else - return PCIBIOS_BAD_REGISTER_NUMBER; - - return PCIBIOS_SUCCESSFUL; -} - -/* HipXX PCIe host only supports 32-bit config access */ -static int hisi_pcie_cfg_write(struct pcie_port *pp, int where, int size, - u32 val) -{ - u32 reg_val; - u32 reg; - void *walker = ®_val; - struct dw_pcie *pci = to_dw_pcie_from_pp(pp); - - walker += (where & 0x3); - reg = where & ~0x3; - if (size == 4) - dw_pcie_writel_dbi(pci, reg, val); - else if (size == 2) { - reg_val = dw_pcie_readl_dbi(pci, reg); - *(u16 __force *) walker = val; - dw_pcie_writel_dbi(pci, reg, reg_val); - } else if (size == 1) { - reg_val = dw_pcie_readl_dbi(pci, reg); - *(u8 __force *) walker = val; - dw_pcie_writel_dbi(pci, reg, reg_val); - } else - return PCIBIOS_BAD_REGISTER_NUMBER; - - return PCIBIOS_SUCCESSFUL; -} - -static int hisi_pcie_link_up_hip05(struct hisi_pcie *hisi_pcie) -{ - u32 val; - - regmap_read(hisi_pcie->subctrl, PCIE_SUBCTRL_SYS_STATE4_REG + - 0x100 * hisi_pcie->port_id, &val); - - return ((val & PCIE_LTSSM_STATE_MASK) == PCIE_LTSSM_LINKUP_STATE); -} - -static int hisi_pcie_link_up_hip06(struct hisi_pcie *hisi_pcie) -{ - struct dw_pcie *pci = hisi_pcie->pci; - u32 val; - - val = dw_pcie_readl_dbi(pci, PCIE_SYS_STATE4); - - return ((val & PCIE_LTSSM_STATE_MASK) == PCIE_LTSSM_LINKUP_STATE); -} - -static int hisi_pcie_link_up(struct dw_pcie *pci) -{ - struct hisi_pcie *hisi_pcie = to_hisi_pcie(pci); - - return hisi_pcie->soc_ops->hisi_pcie_link_up(hisi_pcie); -} - -static const struct dw_pcie_host_ops hisi_pcie_host_ops = { - .rd_own_conf = hisi_pcie_cfg_read, - .wr_own_conf = hisi_pcie_cfg_write, -}; - -static int hisi_add_pcie_port(struct hisi_pcie *hisi_pcie, - struct platform_device *pdev) -{ - struct dw_pcie *pci = hisi_pcie->pci; - struct pcie_port *pp = &pci->pp; - struct device *dev = &pdev->dev; - int ret; - u32 port_id; - - if (of_property_read_u32(dev->of_node, "port-id", &port_id)) { - dev_err(dev, "failed to read port-id\n"); - return -EINVAL; - } - if (port_id > 3) { - dev_err(dev, "Invalid port-id: %d\n", port_id); - return -EINVAL; - } - hisi_pcie->port_id = port_id; - - pp->ops = &hisi_pcie_host_ops; - - ret = dw_pcie_host_init(pp); - if (ret) { - dev_err(dev, "failed to initialize host\n"); - return ret; - } - - return 0; -} - -static const struct dw_pcie_ops dw_pcie_ops = { - .link_up = hisi_pcie_link_up, -}; - -static int hisi_pcie_probe(struct platform_device *pdev) -{ - struct device *dev = &pdev->dev; - struct dw_pcie *pci; - struct hisi_pcie *hisi_pcie; - struct resource *reg; - int ret; - - hisi_pcie = devm_kzalloc(dev, sizeof(*hisi_pcie), GFP_KERNEL); - if (!hisi_pcie) - return -ENOMEM; - - pci = devm_kzalloc(dev, sizeof(*pci), GFP_KERNEL); - if (!pci) - return -ENOMEM; - - pci->dev = dev; - pci->ops = &dw_pcie_ops; - - hisi_pcie->pci = pci; - - hisi_pcie->soc_ops = of_device_get_match_data(dev); - - hisi_pcie->subctrl = - syscon_regmap_lookup_by_compatible("hisilicon,pcie-sas-subctrl"); - if (IS_ERR(hisi_pcie->subctrl)) { - dev_err(dev, "cannot get subctrl base\n"); - return PTR_ERR(hisi_pcie->subctrl); - } - - reg = platform_get_resource_byname(pdev, IORESOURCE_MEM, "rc_dbi"); - pci->dbi_base = devm_pci_remap_cfg_resource(dev, reg); - if (IS_ERR(pci->dbi_base)) - return PTR_ERR(pci->dbi_base); - platform_set_drvdata(pdev, hisi_pcie); - - ret = hisi_add_pcie_port(hisi_pcie, pdev); - if (ret) - return ret; - - return 0; -} - -static struct pcie_soc_ops hip05_ops = { - &hisi_pcie_link_up_hip05 -}; - -static struct pcie_soc_ops hip06_ops = { - &hisi_pcie_link_up_hip06 -}; - -static const struct of_device_id hisi_pcie_of_match[] = { - { - .compatible = "hisilicon,hip05-pcie", - .data = (void *) &hip05_ops, - }, - { - .compatible = "hisilicon,hip06-pcie", - .data = (void *) &hip06_ops, - }, - {}, -}; - -static struct platform_driver hisi_pcie_driver = { - .probe = hisi_pcie_probe, - .driver = { - .name = "hisi-pcie", - .of_match_table = hisi_pcie_of_match, - .suppress_bind_attrs = true, - }, -}; -builtin_platform_driver(hisi_pcie_driver); - static int hisi_pcie_platform_init(struct pci_config_window *cfg) { struct device *dev = cfg->parent; -- GitLab From b4e9c9549f62329d2412f899635fddc5212b9cd4 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Mon, 1 Jun 2020 19:42:40 -0400 Subject: [PATCH 0945/1476] introduction of regset ->get() wrappers, switching ELF coredumps to those Two new helpers: given a process and regset, dump into a buffer. regset_get() takes a buffer and size, regset_get_alloc() takes size and allocates a buffer. Return value in both cases is the amount of data actually dumped in case of success or -E... on error. In both cases the size is capped by regset->n * regset->size, so ->get() is called with offset 0 and size no more than what regset expects. binfmt_elf.c callers of ->get() are switched to using those; the other caller (copy_regset_to_user()) will need some preparations to switch. Signed-off-by: Al Viro --- fs/binfmt_elf.c | 54 ++++++++++++++++++++---------------------- include/linux/regset.h | 9 +++++++ kernel/Makefile | 2 +- kernel/regset.c | 54 ++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 90 insertions(+), 29 deletions(-) create mode 100644 kernel/regset.c diff --git a/fs/binfmt_elf.c b/fs/binfmt_elf.c index 9fe3b51c116a6..e922a6abdca83 100644 --- a/fs/binfmt_elf.c +++ b/fs/binfmt_elf.c @@ -1821,7 +1821,7 @@ static int fill_thread_core_info(struct elf_thread_core_info *t, long signr, size_t *total) { unsigned int i; - unsigned int regset0_size = regset_size(t->task, &view->regsets[0]); + int regset0_size; /* * NT_PRSTATUS is the one special case, because the regset data @@ -1830,8 +1830,10 @@ static int fill_thread_core_info(struct elf_thread_core_info *t, * We assume that regset 0 is NT_PRSTATUS. */ fill_prstatus(&t->prstatus, t->task, signr); - (void) view->regsets[0].get(t->task, &view->regsets[0], 0, regset0_size, - &t->prstatus.pr_reg, NULL); + regset0_size = regset_get(t->task, &view->regsets[0], + sizeof(t->prstatus.pr_reg), &t->prstatus.pr_reg); + if (regset0_size < 0) + return 0; fill_note(&t->notes[0], "CORE", NT_PRSTATUS, PRSTATUS_SIZE(t->prstatus, regset0_size), &t->prstatus); @@ -1846,32 +1848,28 @@ static int fill_thread_core_info(struct elf_thread_core_info *t, */ for (i = 1; i < view->n; ++i) { const struct user_regset *regset = &view->regsets[i]; + int note_type = regset->core_note_type; + bool is_fpreg = note_type == NT_PRFPREG; + void *data; + int ret; + do_thread_regset_writeback(t->task, regset); - if (regset->core_note_type && regset->get && - (!regset->active || regset->active(t->task, regset) > 0)) { - int ret; - size_t size = regset_size(t->task, regset); - void *data = kzalloc(size, GFP_KERNEL); - if (unlikely(!data)) - return 0; - ret = regset->get(t->task, regset, - 0, size, data, NULL); - if (unlikely(ret)) - kfree(data); - else { - if (regset->core_note_type != NT_PRFPREG) - fill_note(&t->notes[i], "LINUX", - regset->core_note_type, - size, data); - else { - SET_PR_FPVALID(&t->prstatus, - 1, regset0_size); - fill_note(&t->notes[i], "CORE", - NT_PRFPREG, size, data); - } - *total += notesize(&t->notes[i]); - } - } + if (!note_type) // not for coredumps + continue; + if (regset->active && regset->active(t->task, regset) <= 0) + continue; + + ret = regset_get_alloc(t->task, regset, ~0U, &data); + if (ret < 0) + continue; + + if (is_fpreg) + SET_PR_FPVALID(&t->prstatus, 1, regset0_size); + + fill_note(&t->notes[i], is_fpreg ? "CORE" : "LINUX", + note_type, ret, data); + + *total += notesize(&t->notes[i]); } return 1; diff --git a/include/linux/regset.h b/include/linux/regset.h index 46d6ae68c4550..968a032922d58 100644 --- a/include/linux/regset.h +++ b/include/linux/regset.h @@ -353,6 +353,15 @@ static inline int user_regset_copyin_ignore(unsigned int *pos, return 0; } +extern int regset_get(struct task_struct *target, + const struct user_regset *regset, + unsigned int size, void *data); + +extern int regset_get_alloc(struct task_struct *target, + const struct user_regset *regset, + unsigned int size, + void **data); + /** * copy_regset_to_user - fetch a thread's user_regset data into user memory * @target: thread to be examined diff --git a/kernel/Makefile b/kernel/Makefile index f3218bc5ec69f..e6e03380a0f14 100644 --- a/kernel/Makefile +++ b/kernel/Makefile @@ -10,7 +10,7 @@ obj-y = fork.o exec_domain.o panic.o \ extable.o params.o \ kthread.o sys_ni.o nsproxy.o \ notifier.o ksysfs.o cred.o reboot.o \ - async.o range.o smpboot.o ucount.o + async.o range.o smpboot.o ucount.o regset.o obj-$(CONFIG_MODULES) += kmod.o obj-$(CONFIG_MULTIUSER) += groups.o diff --git a/kernel/regset.c b/kernel/regset.c new file mode 100644 index 0000000000000..6b39fa0993eca --- /dev/null +++ b/kernel/regset.c @@ -0,0 +1,54 @@ +// SPDX-License-Identifier: GPL-2.0-only +#include +#include +#include + +static int __regset_get(struct task_struct *target, + const struct user_regset *regset, + unsigned int size, + void **data) +{ + void *p = *data, *to_free = NULL; + int res; + + if (!regset->get) + return -EOPNOTSUPP; + if (size > regset->n * regset->size) + size = regset->n * regset->size; + if (!p) { + to_free = p = kzalloc(size, GFP_KERNEL); + if (!p) + return -ENOMEM; + } + res = regset->get(target, regset, 0, size, p, NULL); + if (unlikely(res < 0)) { + kfree(to_free); + return res; + } + *data = p; + if (regset->get_size) { // arm64-only kludge, will go away + unsigned max_size = regset->get_size(target, regset); + if (size > max_size) + size = max_size; + } + return size; +} + +int regset_get(struct task_struct *target, + const struct user_regset *regset, + unsigned int size, + void *data) +{ + return __regset_get(target, regset, size, &data); +} +EXPORT_SYMBOL(regset_get); + +int regset_get_alloc(struct task_struct *target, + const struct user_regset *regset, + unsigned int size, + void **data) +{ + *data = NULL; + return __regset_get(target, regset, size, data); +} +EXPORT_SYMBOL(regset_get_alloc); -- GitLab From 1e6b57d6421f0343dd11619612e5ff8930cddf38 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Thu, 11 Jun 2020 11:11:32 -0400 Subject: [PATCH 0946/1476] unexport linux/elfcore.h It's unusable from userland - it uses elf_gregset_t, which is not provided by exported headers. glibc has it in sys/procfs.h, but the same file defines struct elf_prstatus, so linux/elfcore.h can't be included once sys/procfs.h has been pulled. Same goes for uclibc and dietlibc simply doesn't have elf_gregset_t defined anywhere. IOW, no userland source is including that thing. Signed-off-by: Al Viro --- include/linux/elfcore.h | 69 ++++++++++++++++++++++-- include/uapi/linux/elfcore.h | 101 ----------------------------------- scripts/headers_install.sh | 1 - usr/include/Makefile | 1 - 4 files changed, 66 insertions(+), 106 deletions(-) delete mode 100644 include/uapi/linux/elfcore.h diff --git a/include/linux/elfcore.h b/include/linux/elfcore.h index 4cad0e784b286..96ab215dad2da 100644 --- a/include/linux/elfcore.h +++ b/include/linux/elfcore.h @@ -5,12 +5,75 @@ #include #include #include - -#include -#include +#include +#include +#include +#include +#include +#include struct coredump_params; +struct elf_siginfo +{ + int si_signo; /* signal number */ + int si_code; /* extra code */ + int si_errno; /* errno */ +}; + +/* + * Definitions to generate Intel SVR4-like core files. + * These mostly have the same names as the SVR4 types with "elf_" + * tacked on the front to prevent clashes with linux definitions, + * and the typedef forms have been avoided. This is mostly like + * the SVR4 structure, but more Linuxy, with things that Linux does + * not support and which gdb doesn't really use excluded. + */ +struct elf_prstatus +{ + struct elf_siginfo pr_info; /* Info associated with signal */ + short pr_cursig; /* Current signal */ + unsigned long pr_sigpend; /* Set of pending signals */ + unsigned long pr_sighold; /* Set of held signals */ + pid_t pr_pid; + pid_t pr_ppid; + pid_t pr_pgrp; + pid_t pr_sid; + struct __kernel_old_timeval pr_utime; /* User time */ + struct __kernel_old_timeval pr_stime; /* System time */ + struct __kernel_old_timeval pr_cutime; /* Cumulative user time */ + struct __kernel_old_timeval pr_cstime; /* Cumulative system time */ + elf_gregset_t pr_reg; /* GP registers */ +#ifdef CONFIG_BINFMT_ELF_FDPIC + /* When using FDPIC, the loadmap addresses need to be communicated + * to GDB in order for GDB to do the necessary relocations. The + * fields (below) used to communicate this information are placed + * immediately after ``pr_reg'', so that the loadmap addresses may + * be viewed as part of the register set if so desired. + */ + unsigned long pr_exec_fdpic_loadmap; + unsigned long pr_interp_fdpic_loadmap; +#endif + int pr_fpvalid; /* True if math co-processor being used. */ +}; + +#define ELF_PRARGSZ (80) /* Number of chars for args */ + +struct elf_prpsinfo +{ + char pr_state; /* numeric process state */ + char pr_sname; /* char for pr_state */ + char pr_zomb; /* zombie */ + char pr_nice; /* nice val */ + unsigned long pr_flag; /* flags */ + __kernel_uid_t pr_uid; + __kernel_gid_t pr_gid; + pid_t pr_pid, pr_ppid, pr_pgrp, pr_sid; + /* Lots missing */ + char pr_fname[16]; /* filename of executable */ + char pr_psargs[ELF_PRARGSZ]; /* initial part of arg list */ +}; + static inline void elf_core_copy_regs(elf_gregset_t *elfregs, struct pt_regs *regs) { #ifdef ELF_CORE_COPY_REGS diff --git a/include/uapi/linux/elfcore.h b/include/uapi/linux/elfcore.h deleted file mode 100644 index baf03562306d8..0000000000000 --- a/include/uapi/linux/elfcore.h +++ /dev/null @@ -1,101 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ -#ifndef _UAPI_LINUX_ELFCORE_H -#define _UAPI_LINUX_ELFCORE_H - -#include -#include -#include -#include -#include -#include - -struct elf_siginfo -{ - int si_signo; /* signal number */ - int si_code; /* extra code */ - int si_errno; /* errno */ -}; - - -#ifndef __KERNEL__ -typedef elf_greg_t greg_t; -typedef elf_gregset_t gregset_t; -typedef elf_fpregset_t fpregset_t; -typedef elf_fpxregset_t fpxregset_t; -#define NGREG ELF_NGREG -#endif - -/* - * Definitions to generate Intel SVR4-like core files. - * These mostly have the same names as the SVR4 types with "elf_" - * tacked on the front to prevent clashes with linux definitions, - * and the typedef forms have been avoided. This is mostly like - * the SVR4 structure, but more Linuxy, with things that Linux does - * not support and which gdb doesn't really use excluded. - * Fields present but not used are marked with "XXX". - */ -struct elf_prstatus -{ -#if 0 - long pr_flags; /* XXX Process flags */ - short pr_why; /* XXX Reason for process halt */ - short pr_what; /* XXX More detailed reason */ -#endif - struct elf_siginfo pr_info; /* Info associated with signal */ - short pr_cursig; /* Current signal */ - unsigned long pr_sigpend; /* Set of pending signals */ - unsigned long pr_sighold; /* Set of held signals */ -#if 0 - struct sigaltstack pr_altstack; /* Alternate stack info */ - struct sigaction pr_action; /* Signal action for current sig */ -#endif - pid_t pr_pid; - pid_t pr_ppid; - pid_t pr_pgrp; - pid_t pr_sid; - struct __kernel_old_timeval pr_utime; /* User time */ - struct __kernel_old_timeval pr_stime; /* System time */ - struct __kernel_old_timeval pr_cutime; /* Cumulative user time */ - struct __kernel_old_timeval pr_cstime; /* Cumulative system time */ -#if 0 - long pr_instr; /* Current instruction */ -#endif - elf_gregset_t pr_reg; /* GP registers */ -#ifdef CONFIG_BINFMT_ELF_FDPIC - /* When using FDPIC, the loadmap addresses need to be communicated - * to GDB in order for GDB to do the necessary relocations. The - * fields (below) used to communicate this information are placed - * immediately after ``pr_reg'', so that the loadmap addresses may - * be viewed as part of the register set if so desired. - */ - unsigned long pr_exec_fdpic_loadmap; - unsigned long pr_interp_fdpic_loadmap; -#endif - int pr_fpvalid; /* True if math co-processor being used. */ -}; - -#define ELF_PRARGSZ (80) /* Number of chars for args */ - -struct elf_prpsinfo -{ - char pr_state; /* numeric process state */ - char pr_sname; /* char for pr_state */ - char pr_zomb; /* zombie */ - char pr_nice; /* nice val */ - unsigned long pr_flag; /* flags */ - __kernel_uid_t pr_uid; - __kernel_gid_t pr_gid; - pid_t pr_pid, pr_ppid, pr_pgrp, pr_sid; - /* Lots missing */ - char pr_fname[16]; /* filename of executable */ - char pr_psargs[ELF_PRARGSZ]; /* initial part of arg list */ -}; - -#ifndef __KERNEL__ -typedef struct elf_prstatus prstatus_t; -typedef struct elf_prpsinfo prpsinfo_t; -#define PRARGSZ ELF_PRARGSZ -#endif - - -#endif /* _UAPI_LINUX_ELFCORE_H */ diff --git a/scripts/headers_install.sh b/scripts/headers_install.sh index 955cf3aedf21a..9314247bb2229 100755 --- a/scripts/headers_install.sh +++ b/scripts/headers_install.sh @@ -86,7 +86,6 @@ arch/x86/include/uapi/asm/auxvec.h:CONFIG_X86_64 arch/x86/include/uapi/asm/mman.h:CONFIG_X86_INTEL_MEMORY_PROTECTION_KEYS include/uapi/asm-generic/fcntl.h:CONFIG_64BIT include/uapi/linux/atmdev.h:CONFIG_COMPAT -include/uapi/linux/elfcore.h:CONFIG_BINFMT_ELF_FDPIC include/uapi/linux/eventpoll.h:CONFIG_PM_SLEEP include/uapi/linux/hw_breakpoint.h:CONFIG_HAVE_MIXED_BREAKPOINTS_REGS include/uapi/linux/pktcdvd.h:CONFIG_CDROM_PKTCDVD_WCACHE diff --git a/usr/include/Makefile b/usr/include/Makefile index 55362f3ab3930..f6b3c85d900ed 100644 --- a/usr/include/Makefile +++ b/usr/include/Makefile @@ -28,7 +28,6 @@ no-header-test += linux/am437x-vpfe.h no-header-test += linux/android/binder.h no-header-test += linux/android/binderfs.h no-header-test += linux/coda.h -no-header-test += linux/elfcore.h no-header-test += linux/errqueue.h no-header-test += linux/fsmap.h no-header-test += linux/hdlc/ioctl.h -- GitLab From 16aead81018ca404efe9bd928786824e7168151f Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sun, 14 Jun 2020 09:52:06 -0400 Subject: [PATCH 0947/1476] take fdpic-related parts of elf_prstatus out The only architecture where we might end up using both is arm, and there we definitely don't want fdpic-related fields in elf_prstatus - coredump layout of ELF binaries should not depend upon having the kernel built with the support of ELF_FDPIC ones. Just move the fdpic-modified variant into binfmt_elf_fdpic.c (and call it elf_prstatus_fdpic there) [name stolen from nico] Signed-off-by: Al Viro --- fs/binfmt_elf_fdpic.c | 32 +++++++++++++++++++++++++++++--- include/linux/elfcore-compat.h | 4 ---- include/linux/elfcore.h | 10 ---------- 3 files changed, 29 insertions(+), 17 deletions(-) diff --git a/fs/binfmt_elf_fdpic.c b/fs/binfmt_elf_fdpic.c index 0f45521b237cd..6e13d8bea32d3 100644 --- a/fs/binfmt_elf_fdpic.c +++ b/fs/binfmt_elf_fdpic.c @@ -1189,6 +1189,32 @@ static int elf_fdpic_map_file_by_direct_mmap(struct elf_fdpic_params *params, */ #ifdef CONFIG_ELF_CORE +struct elf_prstatus_fdpic +{ + struct elf_siginfo pr_info; /* Info associated with signal */ + short pr_cursig; /* Current signal */ + unsigned long pr_sigpend; /* Set of pending signals */ + unsigned long pr_sighold; /* Set of held signals */ + pid_t pr_pid; + pid_t pr_ppid; + pid_t pr_pgrp; + pid_t pr_sid; + struct __kernel_old_timeval pr_utime; /* User time */ + struct __kernel_old_timeval pr_stime; /* System time */ + struct __kernel_old_timeval pr_cutime; /* Cumulative user time */ + struct __kernel_old_timeval pr_cstime; /* Cumulative system time */ + elf_gregset_t pr_reg; /* GP registers */ + /* When using FDPIC, the loadmap addresses need to be communicated + * to GDB in order for GDB to do the necessary relocations. The + * fields (below) used to communicate this information are placed + * immediately after ``pr_reg'', so that the loadmap addresses may + * be viewed as part of the register set if so desired. + */ + unsigned long pr_exec_fdpic_loadmap; + unsigned long pr_interp_fdpic_loadmap; + int pr_fpvalid; /* True if math co-processor being used. */ +}; + /* * Decide whether a segment is worth dumping; default is yes to be * sure (missing info is worse than too much; etc). @@ -1345,7 +1371,7 @@ static inline void fill_note(struct memelfnote *note, const char *name, int type * fill up all the fields in prstatus from the given task struct, except * registers which need to be filled up separately. */ -static void fill_prstatus(struct elf_prstatus *prstatus, +static void fill_prstatus(struct elf_prstatus_fdpic *prstatus, struct task_struct *p, long signr) { prstatus->pr_info.si_signo = prstatus->pr_cursig = signr; @@ -1428,7 +1454,7 @@ static int fill_psinfo(struct elf_prpsinfo *psinfo, struct task_struct *p, struct elf_thread_status { struct list_head list; - struct elf_prstatus prstatus; /* NT_PRSTATUS */ + struct elf_prstatus_fdpic prstatus; /* NT_PRSTATUS */ elf_fpregset_t fpu; /* NT_PRFPREG */ struct task_struct *thread; #ifdef ELF_CORE_COPY_XFPREGS @@ -1562,7 +1588,7 @@ static int elf_fdpic_core_dump(struct coredump_params *cprm) loff_t offset = 0, dataoff; int numnote; struct memelfnote *notes = NULL; - struct elf_prstatus *prstatus = NULL; /* NT_PRSTATUS */ + struct elf_prstatus_fdpic *prstatus = NULL; /* NT_PRSTATUS */ struct elf_prpsinfo *psinfo = NULL; /* NT_PRPSINFO */ LIST_HEAD(thread_list); struct list_head *t; diff --git a/include/linux/elfcore-compat.h b/include/linux/elfcore-compat.h index 7a37f4ce9fd2a..10485f0c9740c 100644 --- a/include/linux/elfcore-compat.h +++ b/include/linux/elfcore-compat.h @@ -32,10 +32,6 @@ struct compat_elf_prstatus struct old_timeval32 pr_cutime; struct old_timeval32 pr_cstime; compat_elf_gregset_t pr_reg; -#ifdef CONFIG_BINFMT_ELF_FDPIC - compat_ulong_t pr_exec_fdpic_loadmap; - compat_ulong_t pr_interp_fdpic_loadmap; -#endif compat_int_t pr_fpvalid; }; diff --git a/include/linux/elfcore.h b/include/linux/elfcore.h index 96ab215dad2da..adb8ee89f3fd3 100644 --- a/include/linux/elfcore.h +++ b/include/linux/elfcore.h @@ -44,16 +44,6 @@ struct elf_prstatus struct __kernel_old_timeval pr_cutime; /* Cumulative user time */ struct __kernel_old_timeval pr_cstime; /* Cumulative system time */ elf_gregset_t pr_reg; /* GP registers */ -#ifdef CONFIG_BINFMT_ELF_FDPIC - /* When using FDPIC, the loadmap addresses need to be communicated - * to GDB in order for GDB to do the necessary relocations. The - * fields (below) used to communicate this information are placed - * immediately after ``pr_reg'', so that the loadmap addresses may - * be viewed as part of the register set if so desired. - */ - unsigned long pr_exec_fdpic_loadmap; - unsigned long pr_interp_fdpic_loadmap; -#endif int pr_fpvalid; /* True if math co-processor being used. */ }; -- GitLab From 7a896028adcfbff4552e6748e8fc8d06036c132c Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sat, 13 Jun 2020 00:23:31 -0400 Subject: [PATCH 0948/1476] kill elf_fpxregs_t all uses are conditional upon ELF_CORE_COPY_XFPREGS, which has not been defined on any architecture since 2010 Signed-off-by: Al Viro --- arch/ia64/include/asm/elf.h | 2 -- arch/powerpc/include/asm/elf.h | 2 -- arch/x86/include/asm/elf.h | 2 -- fs/binfmt_elf.c | 30 ------------------------------ fs/binfmt_elf_fdpic.c | 28 ---------------------------- include/linux/elfcore.h | 7 ------- 6 files changed, 71 deletions(-) diff --git a/arch/ia64/include/asm/elf.h b/arch/ia64/include/asm/elf.h index c70bb9c11f526..6629301a26205 100644 --- a/arch/ia64/include/asm/elf.h +++ b/arch/ia64/include/asm/elf.h @@ -179,8 +179,6 @@ extern void ia64_init_addr_space (void); #define ELF_AR_SSD_OFFSET (56 * sizeof(elf_greg_t)) #define ELF_AR_END_OFFSET (57 * sizeof(elf_greg_t)) -typedef unsigned long elf_fpxregset_t; - typedef unsigned long elf_greg_t; typedef elf_greg_t elf_gregset_t[ELF_NGREG]; diff --git a/arch/powerpc/include/asm/elf.h b/arch/powerpc/include/asm/elf.h index 57c229a86f081..53ed2ca401515 100644 --- a/arch/powerpc/include/asm/elf.h +++ b/arch/powerpc/include/asm/elf.h @@ -53,8 +53,6 @@ static inline void ppc_elf_core_copy_regs(elf_gregset_t elf_regs, } #define ELF_CORE_COPY_REGS(gregs, regs) ppc_elf_core_copy_regs(gregs, regs); -typedef elf_vrregset_t elf_fpxregset_t; - /* ELF_HWCAP yields a mask that user programs can use to figure out what instruction set this cpu supports. This could be done in userspace, but it's not easy, and we've already done it here. */ diff --git a/arch/x86/include/asm/elf.h b/arch/x86/include/asm/elf.h index 452beed7892bb..b9a5d488f1a5e 100644 --- a/arch/x86/include/asm/elf.h +++ b/arch/x86/include/asm/elf.h @@ -21,8 +21,6 @@ typedef struct user_i387_struct elf_fpregset_t; #ifdef __i386__ -typedef struct user_fxsr_struct elf_fpxregset_t; - #define R_386_NONE 0 #define R_386_32 1 #define R_386_PC32 2 diff --git a/fs/binfmt_elf.c b/fs/binfmt_elf.c index e922a6abdca83..13d053982dd73 100644 --- a/fs/binfmt_elf.c +++ b/fs/binfmt_elf.c @@ -2038,9 +2038,6 @@ struct elf_thread_status struct elf_prstatus prstatus; /* NT_PRSTATUS */ elf_fpregset_t fpu; /* NT_PRFPREG */ struct task_struct *thread; -#ifdef ELF_CORE_COPY_XFPREGS - elf_fpxregset_t xfpu; /* ELF_CORE_XFPREG_TYPE */ -#endif struct memelfnote notes[3]; int num_notes; }; @@ -2071,15 +2068,6 @@ static int elf_dump_thread_status(long signr, struct elf_thread_status *t) t->num_notes++; sz += notesize(&t->notes[1]); } - -#ifdef ELF_CORE_COPY_XFPREGS - if (elf_core_copy_task_xfpregs(p, &t->xfpu)) { - fill_note(&t->notes[2], "LINUX", ELF_CORE_XFPREG_TYPE, - sizeof(t->xfpu), &t->xfpu); - t->num_notes++; - sz += notesize(&t->notes[2]); - } -#endif return sz; } @@ -2090,9 +2078,6 @@ struct elf_note_info { struct elf_prpsinfo *psinfo; /* NT_PRPSINFO */ struct list_head thread_list; elf_fpregset_t *fpu; -#ifdef ELF_CORE_COPY_XFPREGS - elf_fpxregset_t *xfpu; -#endif user_siginfo_t csigdata; int thread_status_size; int numnote; @@ -2116,11 +2101,6 @@ static int elf_note_info_init(struct elf_note_info *info) info->fpu = kmalloc(sizeof(*info->fpu), GFP_KERNEL); if (!info->fpu) return 0; -#ifdef ELF_CORE_COPY_XFPREGS - info->xfpu = kmalloc(sizeof(*info->xfpu), GFP_KERNEL); - if (!info->xfpu) - return 0; -#endif return 1; } @@ -2184,13 +2164,6 @@ static int fill_note_info(struct elfhdr *elf, int phdrs, if (info->prstatus->pr_fpvalid) fill_note(info->notes + info->numnote++, "CORE", NT_PRFPREG, sizeof(*info->fpu), info->fpu); -#ifdef ELF_CORE_COPY_XFPREGS - if (elf_core_copy_task_xfpregs(current, info->xfpu)) - fill_note(info->notes + info->numnote++, - "LINUX", ELF_CORE_XFPREG_TYPE, - sizeof(*info->xfpu), info->xfpu); -#endif - return 1; } @@ -2243,9 +2216,6 @@ static void free_note_info(struct elf_note_info *info) kfree(info->psinfo); kfree(info->notes); kfree(info->fpu); -#ifdef ELF_CORE_COPY_XFPREGS - kfree(info->xfpu); -#endif } #endif diff --git a/fs/binfmt_elf_fdpic.c b/fs/binfmt_elf_fdpic.c index 6e13d8bea32d3..a6ee92137529c 100644 --- a/fs/binfmt_elf_fdpic.c +++ b/fs/binfmt_elf_fdpic.c @@ -1457,9 +1457,6 @@ struct elf_thread_status struct elf_prstatus_fdpic prstatus; /* NT_PRSTATUS */ elf_fpregset_t fpu; /* NT_PRFPREG */ struct task_struct *thread; -#ifdef ELF_CORE_COPY_XFPREGS - elf_fpxregset_t xfpu; /* ELF_CORE_XFPREG_TYPE */ -#endif struct memelfnote notes[3]; int num_notes; }; @@ -1491,15 +1488,6 @@ static int elf_dump_thread_status(long signr, struct elf_thread_status *t) t->num_notes++; sz += notesize(&t->notes[1]); } - -#ifdef ELF_CORE_COPY_XFPREGS - if (elf_core_copy_task_xfpregs(p, &t->xfpu)) { - fill_note(&t->notes[2], "LINUX", ELF_CORE_XFPREG_TYPE, - sizeof(t->xfpu), &t->xfpu); - t->num_notes++; - sz += notesize(&t->notes[2]); - } -#endif return sz; } @@ -1593,9 +1581,6 @@ static int elf_fdpic_core_dump(struct coredump_params *cprm) LIST_HEAD(thread_list); struct list_head *t; elf_fpregset_t *fpu = NULL; -#ifdef ELF_CORE_COPY_XFPREGS - elf_fpxregset_t *xfpu = NULL; -#endif int thread_status_size = 0; elf_addr_t *auxv; struct elf_phdr *phdr4note = NULL; @@ -1634,11 +1619,6 @@ static int elf_fdpic_core_dump(struct coredump_params *cprm) fpu = kmalloc(sizeof(*fpu), GFP_KERNEL); if (!fpu) goto end_coredump; -#ifdef ELF_CORE_COPY_XFPREGS - xfpu = kmalloc(sizeof(*xfpu), GFP_KERNEL); - if (!xfpu) - goto end_coredump; -#endif for (ct = current->mm->core_state->dumper.next; ct; ct = ct->next) { @@ -1703,11 +1683,6 @@ static int elf_fdpic_core_dump(struct coredump_params *cprm) elf_core_copy_task_fpregs(current, cprm->regs, fpu))) fill_note(notes + numnote++, "CORE", NT_PRFPREG, sizeof(*fpu), fpu); -#ifdef ELF_CORE_COPY_XFPREGS - if (elf_core_copy_task_xfpregs(current, xfpu)) - fill_note(notes + numnote++, - "LINUX", ELF_CORE_XFPREG_TYPE, sizeof(*xfpu), xfpu); -#endif offset += sizeof(*elf); /* Elf header */ offset += segs * sizeof(struct elf_phdr); /* Program headers */ @@ -1828,9 +1803,6 @@ end_coredump: kfree(notes); kfree(fpu); kfree(shdr4extnum); -#ifdef ELF_CORE_COPY_XFPREGS - kfree(xfpu); -#endif return has_dumped; #undef NUM_NOTES } diff --git a/include/linux/elfcore.h b/include/linux/elfcore.h index adb8ee89f3fd3..46c3d691f6776 100644 --- a/include/linux/elfcore.h +++ b/include/linux/elfcore.h @@ -104,13 +104,6 @@ static inline int elf_core_copy_task_fpregs(struct task_struct *t, struct pt_reg #endif } -#ifdef ELF_CORE_COPY_XFPREGS -static inline int elf_core_copy_task_xfpregs(struct task_struct *t, elf_fpxregset_t *xfpu) -{ - return ELF_CORE_COPY_XFPREGS(t, xfpu); -} -#endif - /* * These functions parameterize elf_core_dump in fs/binfmt_elf.c to write out * extra segments containing the gate DSO contents. Dumping its -- GitLab From 5074c7f69f9e86a8f83955bc9ca4f2634261917e Mon Sep 17 00:00:00 2001 From: Al Viro Date: Mon, 15 Jun 2020 10:19:31 -0400 Subject: [PATCH 0949/1476] [elf-fdpic] coredump: don't bother with cyclic list for per-thread objects plain single-linked list is just fine here... Signed-off-by: Al Viro --- fs/binfmt_elf_fdpic.c | 29 +++++++++++------------------ 1 file changed, 11 insertions(+), 18 deletions(-) diff --git a/fs/binfmt_elf_fdpic.c b/fs/binfmt_elf_fdpic.c index a6ee92137529c..bcbf756fba399 100644 --- a/fs/binfmt_elf_fdpic.c +++ b/fs/binfmt_elf_fdpic.c @@ -1453,7 +1453,7 @@ static int fill_psinfo(struct elf_prpsinfo *psinfo, struct task_struct *p, /* Here is the structure in which status of each thread is captured. */ struct elf_thread_status { - struct list_head list; + struct elf_thread_status *next; struct elf_prstatus_fdpic prstatus; /* NT_PRSTATUS */ elf_fpregset_t fpu; /* NT_PRFPREG */ struct task_struct *thread; @@ -1578,8 +1578,7 @@ static int elf_fdpic_core_dump(struct coredump_params *cprm) struct memelfnote *notes = NULL; struct elf_prstatus_fdpic *prstatus = NULL; /* NT_PRSTATUS */ struct elf_prpsinfo *psinfo = NULL; /* NT_PRPSINFO */ - LIST_HEAD(thread_list); - struct list_head *t; + struct elf_thread_status *thread_list = NULL; elf_fpregset_t *fpu = NULL; int thread_status_size = 0; elf_addr_t *auxv; @@ -1627,15 +1626,12 @@ static int elf_fdpic_core_dump(struct coredump_params *cprm) goto end_coredump; tmp->thread = ct->task; - list_add(&tmp->list, &thread_list); + tmp->next = thread_list; + thread_list = tmp; } - list_for_each(t, &thread_list) { - struct elf_thread_status *tmp; - int sz; - - tmp = list_entry(t, struct elf_thread_status, list); - sz = elf_dump_thread_status(cprm->siginfo->si_signo, tmp); + for (tmp = thread_list; tmp; tmp = tmp->next) { + int sz = elf_dump_thread_status(cprm->siginfo->si_signo, tmp); thread_status_size += sz; } @@ -1760,10 +1756,7 @@ static int elf_fdpic_core_dump(struct coredump_params *cprm) goto end_coredump; /* write out the thread status notes section */ - list_for_each(t, &thread_list) { - struct elf_thread_status *tmp = - list_entry(t, struct elf_thread_status, list); - + for (tmp = thread_list; tmp; tmp = tmp->next) { for (i = 0; i < tmp->num_notes; i++) if (!writenote(&tmp->notes[i], cprm)) goto end_coredump; @@ -1791,10 +1784,10 @@ static int elf_fdpic_core_dump(struct coredump_params *cprm) } end_coredump: - while (!list_empty(&thread_list)) { - struct list_head *tmp = thread_list.next; - list_del(tmp); - kfree(list_entry(tmp, struct elf_thread_status, list)); + while (thread_list) { + tmp = thread_list; + thread_list = thread_list->next; + kfree(tmp); } kfree(phdr4note); kfree(elf); -- GitLab From 38a62779ae8e7a3b5fcf0ed3ee0bd6620809891c Mon Sep 17 00:00:00 2001 From: Al Viro Date: Mon, 15 Jun 2020 10:41:08 -0400 Subject: [PATCH 0950/1476] [elf-fdpic] move allocation of elf_thread_status into elf_dump_thread_status() Signed-off-by: Al Viro --- fs/binfmt_elf_fdpic.c | 24 ++++++++++-------------- 1 file changed, 10 insertions(+), 14 deletions(-) diff --git a/fs/binfmt_elf_fdpic.c b/fs/binfmt_elf_fdpic.c index bcbf756fba399..ba4f264dff3a2 100644 --- a/fs/binfmt_elf_fdpic.c +++ b/fs/binfmt_elf_fdpic.c @@ -1466,12 +1466,13 @@ struct elf_thread_status * we need to keep a linked list of every thread's pr_status and then create * a single section for them in the final core file. */ -static int elf_dump_thread_status(long signr, struct elf_thread_status *t) +static struct elf_thread_status *elf_dump_thread_status(long signr, struct task_struct *p, int *sz) { - struct task_struct *p = t->thread; - int sz = 0; + struct elf_thread_status *t; - t->num_notes = 0; + t = kzalloc(sizeof(struct elf_thread_status), GFP_KERNEL); + if (!t) + return t; fill_prstatus(&t->prstatus, p, signr); elf_core_copy_task_regs(p, &t->prstatus.pr_reg); @@ -1479,16 +1480,16 @@ static int elf_dump_thread_status(long signr, struct elf_thread_status *t) fill_note(&t->notes[0], "CORE", NT_PRSTATUS, sizeof(t->prstatus), &t->prstatus); t->num_notes++; - sz += notesize(&t->notes[0]); + *sz += notesize(&t->notes[0]); t->prstatus.pr_fpvalid = elf_core_copy_task_fpregs(p, NULL, &t->fpu); if (t->prstatus.pr_fpvalid) { fill_note(&t->notes[1], "CORE", NT_PRFPREG, sizeof(t->fpu), &t->fpu); t->num_notes++; - sz += notesize(&t->notes[1]); + *sz += notesize(&t->notes[1]); } - return sz; + return t; } static void fill_extnum_info(struct elfhdr *elf, struct elf_shdr *shdr4extnum, @@ -1621,20 +1622,15 @@ static int elf_fdpic_core_dump(struct coredump_params *cprm) for (ct = current->mm->core_state->dumper.next; ct; ct = ct->next) { - tmp = kzalloc(sizeof(*tmp), GFP_KERNEL); + tmp = elf_dump_thread_status(cprm->siginfo->si_signo, + ct->task, &thread_status_size); if (!tmp) goto end_coredump; - tmp->thread = ct->task; tmp->next = thread_list; thread_list = tmp; } - for (tmp = thread_list; tmp; tmp = tmp->next) { - int sz = elf_dump_thread_status(cprm->siginfo->si_signo, tmp); - thread_status_size += sz; - } - /* now collect the dump for the current */ fill_prstatus(prstatus, current, cprm->siginfo->si_signo); elf_core_copy_regs(&prstatus->pr_reg, cprm->regs); -- GitLab From d2f581684a292ece5c10cd56ad51bf4f702b7beb Mon Sep 17 00:00:00 2001 From: Al Viro Date: Mon, 15 Jun 2020 11:03:28 -0400 Subject: [PATCH 0951/1476] [elf-fdpic] use elf_dump_thread_status() for the dumper thread as well the only reason to have it open-coded for the first (dumper) thread is that coredump has a couple of process-wide notes stuck right after the first (NT_PRSTATUS) note of the first thread. But we don't need to make the data collection side irregular for the first thread to handle that - it's only the logics ordering the calls of writenote() that needs to take care of that. Signed-off-by: Al Viro --- fs/binfmt_elf_fdpic.c | 81 +++++++++++++++---------------------------- 1 file changed, 28 insertions(+), 53 deletions(-) diff --git a/fs/binfmt_elf_fdpic.c b/fs/binfmt_elf_fdpic.c index ba4f264dff3a2..34c45410d587c 100644 --- a/fs/binfmt_elf_fdpic.c +++ b/fs/binfmt_elf_fdpic.c @@ -1482,7 +1482,8 @@ static struct elf_thread_status *elf_dump_thread_status(long signr, struct task_ t->num_notes++; *sz += notesize(&t->notes[0]); - t->prstatus.pr_fpvalid = elf_core_copy_task_fpregs(p, NULL, &t->fpu); + t->prstatus.pr_fpvalid = elf_core_copy_task_fpregs(p, task_pt_regs(p), + &t->fpu); if (t->prstatus.pr_fpvalid) { fill_note(&t->notes[1], "CORE", NT_PRFPREG, sizeof(t->fpu), &t->fpu); @@ -1568,19 +1569,15 @@ static size_t elf_core_vma_data_size(unsigned long mm_flags) */ static int elf_fdpic_core_dump(struct coredump_params *cprm) { -#define NUM_NOTES 6 int has_dumped = 0; int segs; int i; struct vm_area_struct *vma; struct elfhdr *elf = NULL; loff_t offset = 0, dataoff; - int numnote; - struct memelfnote *notes = NULL; - struct elf_prstatus_fdpic *prstatus = NULL; /* NT_PRSTATUS */ + struct memelfnote psinfo_note, auxv_note; struct elf_prpsinfo *psinfo = NULL; /* NT_PRPSINFO */ struct elf_thread_status *thread_list = NULL; - elf_fpregset_t *fpu = NULL; int thread_status_size = 0; elf_addr_t *auxv; struct elf_phdr *phdr4note = NULL; @@ -1606,19 +1603,9 @@ static int elf_fdpic_core_dump(struct coredump_params *cprm) elf = kmalloc(sizeof(*elf), GFP_KERNEL); if (!elf) goto end_coredump; - prstatus = kzalloc(sizeof(*prstatus), GFP_KERNEL); - if (!prstatus) - goto end_coredump; psinfo = kmalloc(sizeof(*psinfo), GFP_KERNEL); if (!psinfo) goto end_coredump; - notes = kmalloc_array(NUM_NOTES, sizeof(struct memelfnote), - GFP_KERNEL); - if (!notes) - goto end_coredump; - fpu = kmalloc(sizeof(*fpu), GFP_KERNEL); - if (!fpu) - goto end_coredump; for (ct = current->mm->core_state->dumper.next; ct; ct = ct->next) { @@ -1632,8 +1619,12 @@ static int elf_fdpic_core_dump(struct coredump_params *cprm) } /* now collect the dump for the current */ - fill_prstatus(prstatus, current, cprm->siginfo->si_signo); - elf_core_copy_regs(&prstatus->pr_reg, cprm->regs); + tmp = elf_dump_thread_status(cprm->siginfo->si_signo, + current, &thread_status_size); + if (!tmp) + goto end_coredump; + tmp->next = thread_list; + thread_list = tmp; segs = current->mm->map_count; segs += elf_core_extra_phdrs(); @@ -1655,46 +1646,28 @@ static int elf_fdpic_core_dump(struct coredump_params *cprm) * with info from their /proc. */ - fill_note(notes + 0, "CORE", NT_PRSTATUS, sizeof(*prstatus), prstatus); fill_psinfo(psinfo, current->group_leader, current->mm); - fill_note(notes + 1, "CORE", NT_PRPSINFO, sizeof(*psinfo), psinfo); - - numnote = 2; + fill_note(&psinfo_note, "CORE", NT_PRPSINFO, sizeof(*psinfo), psinfo); + thread_status_size += notesize(&psinfo_note); auxv = (elf_addr_t *) current->mm->saved_auxv; - i = 0; do i += 2; while (auxv[i - 2] != AT_NULL); - fill_note(¬es[numnote++], "CORE", NT_AUXV, - i * sizeof(elf_addr_t), auxv); + fill_note(&auxv_note, "CORE", NT_AUXV, i * sizeof(elf_addr_t), auxv); + thread_status_size += notesize(&auxv_note); - /* Try to dump the FPU. */ - if ((prstatus->pr_fpvalid = - elf_core_copy_task_fpregs(current, cprm->regs, fpu))) - fill_note(notes + numnote++, - "CORE", NT_PRFPREG, sizeof(*fpu), fpu); - - offset += sizeof(*elf); /* Elf header */ + offset = sizeof(*elf); /* Elf header */ offset += segs * sizeof(struct elf_phdr); /* Program headers */ /* Write notes phdr entry */ - { - int sz = 0; - - for (i = 0; i < numnote; i++) - sz += notesize(notes + i); - - sz += thread_status_size; - - phdr4note = kmalloc(sizeof(*phdr4note), GFP_KERNEL); - if (!phdr4note) - goto end_coredump; + phdr4note = kmalloc(sizeof(*phdr4note), GFP_KERNEL); + if (!phdr4note) + goto end_coredump; - fill_elf_note_phdr(phdr4note, sz, offset); - offset += sz; - } + fill_elf_note_phdr(phdr4note, thread_status_size, offset); + offset += thread_status_size; /* Page-align dumped data */ dataoff = offset = roundup(offset, ELF_EXEC_PAGESIZE); @@ -1747,12 +1720,18 @@ static int elf_fdpic_core_dump(struct coredump_params *cprm) goto end_coredump; /* write out the notes section */ - for (i = 0; i < numnote; i++) - if (!writenote(notes + i, cprm)) + if (!writenote(thread_list->notes, cprm)) + goto end_coredump; + if (!writenote(&psinfo_note, cprm)) + goto end_coredump; + if (!writenote(&auxv_note, cprm)) + goto end_coredump; + for (i = 1; i < thread_list->num_notes; i++) + if (!writenote(thread_list->notes + i, cprm)) goto end_coredump; /* write out the thread status notes section */ - for (tmp = thread_list; tmp; tmp = tmp->next) { + for (tmp = thread_list->next; tmp; tmp = tmp->next) { for (i = 0; i < tmp->num_notes; i++) if (!writenote(&tmp->notes[i], cprm)) goto end_coredump; @@ -1787,13 +1766,9 @@ end_coredump: } kfree(phdr4note); kfree(elf); - kfree(prstatus); kfree(psinfo); - kfree(notes); - kfree(fpu); kfree(shdr4extnum); return has_dumped; -#undef NUM_NOTES } #endif /* CONFIG_ELF_CORE */ -- GitLab From 1697a322e28ba96d35953c5d824540d172546d36 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Mon, 15 Jun 2020 11:22:55 -0400 Subject: [PATCH 0952/1476] [elf-fdpic] switch coredump to regsets similar to how elf coredump is working on architectures that have regsets, and all architectures with elf-fdpic support *do* have that. Signed-off-by: Al Viro --- fs/binfmt_elf_fdpic.c | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/fs/binfmt_elf_fdpic.c b/fs/binfmt_elf_fdpic.c index 34c45410d587c..1af03c8d3c094 100644 --- a/fs/binfmt_elf_fdpic.c +++ b/fs/binfmt_elf_fdpic.c @@ -35,6 +35,7 @@ #include #include #include +#include #include #include @@ -1456,8 +1457,7 @@ struct elf_thread_status struct elf_thread_status *next; struct elf_prstatus_fdpic prstatus; /* NT_PRSTATUS */ elf_fpregset_t fpu; /* NT_PRFPREG */ - struct task_struct *thread; - struct memelfnote notes[3]; + struct memelfnote notes[2]; int num_notes; }; @@ -1468,22 +1468,35 @@ struct elf_thread_status */ static struct elf_thread_status *elf_dump_thread_status(long signr, struct task_struct *p, int *sz) { + const struct user_regset_view *view = task_user_regset_view(p); struct elf_thread_status *t; + int i, ret; t = kzalloc(sizeof(struct elf_thread_status), GFP_KERNEL); if (!t) return t; fill_prstatus(&t->prstatus, p, signr); - elf_core_copy_task_regs(p, &t->prstatus.pr_reg); + regset_get(p, &view->regsets[0], + sizeof(t->prstatus.pr_reg), &t->prstatus.pr_reg); fill_note(&t->notes[0], "CORE", NT_PRSTATUS, sizeof(t->prstatus), &t->prstatus); t->num_notes++; *sz += notesize(&t->notes[0]); - t->prstatus.pr_fpvalid = elf_core_copy_task_fpregs(p, task_pt_regs(p), - &t->fpu); + for (i = 1; i < view->n; ++i) { + const struct user_regset *regset = &view->regsets[i]; + if (regset->core_note_type != NT_PRFPREG) + continue; + if (regset->active && regset->active(p, regset) <= 0) + continue; + ret = regset_get(p, regset, sizeof(t->fpu), &t->fpu); + if (ret >= 0) + t->prstatus.pr_fpvalid = 1; + break; + } + if (t->prstatus.pr_fpvalid) { fill_note(&t->notes[1], "CORE", NT_PRFPREG, sizeof(t->fpu), &t->fpu); -- GitLab From dc12d7968f9c9540494deb1285854b18ca4465ec Mon Sep 17 00:00:00 2001 From: Al Viro Date: Mon, 17 Feb 2020 12:25:14 -0500 Subject: [PATCH 0953/1476] copy_regset_to_user(): do all copyout at once. Turn copy_regset_to_user() into regset_get_alloc() + copy_to_user(). Now all ->get() calls have a kernel buffer as destination. Note that we'd already eliminated the callers of copy_regset_to_user() with non-zero offset; now that argument is simply unused. Uninlined, while we are at it. Signed-off-by: Al Viro --- include/linux/regset.h | 29 ++++------------------------- kernel/regset.c | 26 ++++++++++++++++++++++++++ 2 files changed, 30 insertions(+), 25 deletions(-) diff --git a/include/linux/regset.h b/include/linux/regset.h index 968a032922d58..af57c1db19249 100644 --- a/include/linux/regset.h +++ b/include/linux/regset.h @@ -362,31 +362,10 @@ extern int regset_get_alloc(struct task_struct *target, unsigned int size, void **data); -/** - * copy_regset_to_user - fetch a thread's user_regset data into user memory - * @target: thread to be examined - * @view: &struct user_regset_view describing user thread machine state - * @setno: index in @view->regsets - * @offset: offset into the regset data, in bytes - * @size: amount of data to copy, in bytes - * @data: user-mode pointer to copy into - */ -static inline int copy_regset_to_user(struct task_struct *target, - const struct user_regset_view *view, - unsigned int setno, - unsigned int offset, unsigned int size, - void __user *data) -{ - const struct user_regset *regset = &view->regsets[setno]; - - if (!regset->get) - return -EOPNOTSUPP; - - if (!access_ok(data, size)) - return -EFAULT; - - return regset->get(target, regset, offset, size, NULL, data); -} +extern int copy_regset_to_user(struct task_struct *target, + const struct user_regset_view *view, + unsigned int setno, unsigned int offset, + unsigned int size, void __user *data); /** * copy_regset_from_user - store into thread's user_regset data from user memory diff --git a/kernel/regset.c b/kernel/regset.c index 6b39fa0993eca..0a610983ce436 100644 --- a/kernel/regset.c +++ b/kernel/regset.c @@ -52,3 +52,29 @@ int regset_get_alloc(struct task_struct *target, return __regset_get(target, regset, size, data); } EXPORT_SYMBOL(regset_get_alloc); + +/** + * copy_regset_to_user - fetch a thread's user_regset data into user memory + * @target: thread to be examined + * @view: &struct user_regset_view describing user thread machine state + * @setno: index in @view->regsets + * @offset: offset into the regset data, in bytes + * @size: amount of data to copy, in bytes + * @data: user-mode pointer to copy into + */ +int copy_regset_to_user(struct task_struct *target, + const struct user_regset_view *view, + unsigned int setno, + unsigned int offset, unsigned int size, + void __user *data) +{ + const struct user_regset *regset = &view->regsets[setno]; + void *buf; + int ret; + + ret = regset_get_alloc(target, regset, size, &buf); + if (ret > 0) + ret = copy_to_user(data, buf, ret) ? -EFAULT : 0; + kfree(buf); + return ret; +} -- GitLab From 7717cb9bdd0421faa432a4e0d499fdba6e2394c8 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Thu, 20 Feb 2020 20:48:16 -0500 Subject: [PATCH 0954/1476] regset: new method and helpers for it ->regset_get() takes task+regset+buffer, returns the amount of free space left in the buffer on success and -E... on error. buffer is represented as struct membuf - a pair of (kernel) pointer and amount of space left Primitives for writing to such: * membuf_write(buf, data, size) * membuf_zero(buf, size) * membuf_store(buf, value) These are implemented as inlines (in case of membuf_store - a macro). All writes are sequential; they become no-ops when there's no space left. Return value of all primitives is the amount of space left after the operation, so they can be used as return values of ->regset_get(). Example of use: // stores pt_regs of task + 64 bytes worth of zeroes + 32bit PID of task int foo_get(struct task_struct *task, const struct regset *regset, struct membuf to) { membuf_write(&to, task_pt_regs(task), sizeof(struct pt_regs)); membuf_zero(&to, 64); return membuf_store(&to, (u32)task_tgid_vnr(task)); } regset_get()/regset_get_alloc() taught to use that thing if present. By the end of the series all users of ->get() will be converted; then ->get() and ->get_size() can go. Note that unlike ->get() this thing always starts at offset 0 and, since it only writes to kernel buffer, can't fail on copyout. It can, of course, fail for other reasons, but those tend to be less numerous. The caller guarantees that the buffer size won't be bigger than regset->n * regset->size. That simplifies life for quite a few instances. Signed-off-by: Al Viro --- include/linux/regset.h | 51 ++++++++++++++++++++++++++++++++++++++++++ kernel/regset.c | 12 +++++++++- 2 files changed, 62 insertions(+), 1 deletion(-) diff --git a/include/linux/regset.h b/include/linux/regset.h index af57c1db19249..f6125a7d949dc 100644 --- a/include/linux/regset.h +++ b/include/linux/regset.h @@ -17,6 +17,52 @@ struct task_struct; struct user_regset; +struct membuf { + void *p; + size_t left; +}; + +static inline int membuf_zero(struct membuf *s, size_t size) +{ + if (s->left) { + if (size > s->left) + size = s->left; + memset(s->p, 0, size); + s->p += size; + s->left -= size; + } + return s->left; +} + +static inline int membuf_write(struct membuf *s, const void *v, size_t size) +{ + if (s->left) { + if (size > s->left) + size = s->left; + memcpy(s->p, v, size); + s->p += size; + s->left -= size; + } + return s->left; +} + +/* current s->p must be aligned for v; v must be a scalar */ +#define membuf_store(s, v) \ +({ \ + struct membuf *__s = (s); \ + if (__s->left) { \ + typeof(v) __v = (v); \ + size_t __size = sizeof(__v); \ + if (unlikely(__size > __s->left)) { \ + __size = __s->left; \ + memcpy(__s->p, &__v, __size); \ + } else { \ + *(typeof(__v + 0) *)__s->p = __v; \ + } \ + __s->p += __size; \ + __s->left -= __size; \ + } \ + __s->left;}) /** * user_regset_active_fn - type of @active function in &struct user_regset @@ -57,6 +103,10 @@ typedef int user_regset_get_fn(struct task_struct *target, unsigned int pos, unsigned int count, void *kbuf, void __user *ubuf); +typedef int user_regset_get2_fn(struct task_struct *target, + const struct user_regset *regset, + struct membuf to); + /** * user_regset_set_fn - type of @set function in &struct user_regset * @target: thread being examined @@ -186,6 +236,7 @@ typedef unsigned int user_regset_get_size_fn(struct task_struct *target, */ struct user_regset { user_regset_get_fn *get; + user_regset_get2_fn *regset_get; user_regset_set_fn *set; user_regset_active_fn *active; user_regset_writeback_fn *writeback; diff --git a/kernel/regset.c b/kernel/regset.c index 0a610983ce436..eaeaefbbd39ec 100644 --- a/kernel/regset.c +++ b/kernel/regset.c @@ -11,7 +11,7 @@ static int __regset_get(struct task_struct *target, void *p = *data, *to_free = NULL; int res; - if (!regset->get) + if (!regset->get && !regset->regset_get) return -EOPNOTSUPP; if (size > regset->n * regset->size) size = regset->n * regset->size; @@ -20,6 +20,16 @@ static int __regset_get(struct task_struct *target, if (!p) return -ENOMEM; } + if (regset->regset_get) { + res = regset->regset_get(target, regset, + (struct membuf){.p = p, .left = size}); + if (res < 0) { + kfree(to_free); + return res; + } + *data = p; + return size - res; + } res = regset->get(target, regset, 0, size, p, NULL); if (unlikely(res < 0)) { kfree(to_free); -- GitLab From 0557d64d983e3dedead2d4b4e8abc49620d5f5d2 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Tue, 18 Feb 2020 12:14:34 -0500 Subject: [PATCH 0955/1476] x86: switch to ->regset_get() All instances of ->get() in arch/x86 switched; that might or might not be worth splitting up. Notes: * for xstateregs_get() the amount we want to store is determined at the boot time; see init_xstate_size() and update_regset_xstate_info() for details. task->thread.fpu.state.xsave ends with a flexible array member and the amount of data in it depends upon the FPU features supported/enabled. * fpregs_get() writes slightly less than full ->thread.fpu.state.fsave (the last word is not copied); we pass the full size of state.fsave and let membuf_write() trim to the amount declared by regset - __regset_get() will make sure that the space in buffer is no more than that. * copy_xstate_to_user() and its helpers are gone now. * fpregs_soft_get() was getting user_regset_copyout() arguments wrong. Since "x86: x86 user_regset math_emu" back in 2008... I really doubt that it's worth splitting out for -stable, though - you need a 486SX box for that to trigger... [Kevin's braino fix for copy_xstate_to_kernel() essentially duplicated here] Signed-off-by: Al Viro --- arch/x86/include/asm/fpu/regset.h | 4 +- arch/x86/include/asm/fpu/xstate.h | 4 +- arch/x86/kernel/fpu/regset.c | 39 +++---- arch/x86/kernel/fpu/signal.c | 3 +- arch/x86/kernel/fpu/xstate.c | 164 +++++------------------------- arch/x86/kernel/ptrace.c | 75 +++++--------- arch/x86/kernel/tls.c | 32 ++---- arch/x86/kernel/tls.h | 2 +- arch/x86/math-emu/fpu_entry.c | 19 +--- 9 files changed, 83 insertions(+), 259 deletions(-) diff --git a/arch/x86/include/asm/fpu/regset.h b/arch/x86/include/asm/fpu/regset.h index d5bdffb9d27fe..4f928d6a367b8 100644 --- a/arch/x86/include/asm/fpu/regset.h +++ b/arch/x86/include/asm/fpu/regset.h @@ -8,8 +8,8 @@ #include extern user_regset_active_fn regset_fpregs_active, regset_xregset_fpregs_active; -extern user_regset_get_fn fpregs_get, xfpregs_get, fpregs_soft_get, - xstateregs_get; +extern user_regset_get2_fn fpregs_get, xfpregs_get, fpregs_soft_get, + xstateregs_get; extern user_regset_set_fn fpregs_set, xfpregs_set, fpregs_soft_set, xstateregs_set; diff --git a/arch/x86/include/asm/fpu/xstate.h b/arch/x86/include/asm/fpu/xstate.h index 422d8369012a9..f691ea1bc086f 100644 --- a/arch/x86/include/asm/fpu/xstate.h +++ b/arch/x86/include/asm/fpu/xstate.h @@ -71,8 +71,8 @@ extern void __init update_regset_xstate_info(unsigned int size, void *get_xsave_addr(struct xregs_state *xsave, int xfeature_nr); const void *get_xsave_field_ptr(int xfeature_nr); int using_compacted_format(void); -int copy_xstate_to_kernel(void *kbuf, struct xregs_state *xsave, unsigned int offset, unsigned int size); -int copy_xstate_to_user(void __user *ubuf, struct xregs_state *xsave, unsigned int offset, unsigned int size); +struct membuf; +void copy_xstate_to_kernel(struct membuf to, struct xregs_state *xsave); int copy_kernel_to_xstate(struct xregs_state *xsave, const void *kbuf); int copy_user_to_xstate(struct xregs_state *xsave, const void __user *ubuf); void copy_supervisor_to_kernel(struct xregs_state *xsave); diff --git a/arch/x86/kernel/fpu/regset.c b/arch/x86/kernel/fpu/regset.c index 91f80aca27fbe..c413756ba89fa 100644 --- a/arch/x86/kernel/fpu/regset.c +++ b/arch/x86/kernel/fpu/regset.c @@ -27,8 +27,7 @@ int regset_xregset_fpregs_active(struct task_struct *target, const struct user_r } int xfpregs_get(struct task_struct *target, const struct user_regset *regset, - unsigned int pos, unsigned int count, - void *kbuf, void __user *ubuf) + struct membuf to) { struct fpu *fpu = &target->thread.fpu; @@ -38,8 +37,7 @@ int xfpregs_get(struct task_struct *target, const struct user_regset *regset, fpu__prepare_read(fpu); fpstate_sanitize_xstate(fpu); - return user_regset_copyout(&pos, &count, &kbuf, &ubuf, - &fpu->state.fxsave, 0, -1); + return membuf_write(&to, &fpu->state.fxsave, sizeof(struct fxregs_state)); } int xfpregs_set(struct task_struct *target, const struct user_regset *regset, @@ -74,12 +72,10 @@ int xfpregs_set(struct task_struct *target, const struct user_regset *regset, } int xstateregs_get(struct task_struct *target, const struct user_regset *regset, - unsigned int pos, unsigned int count, - void *kbuf, void __user *ubuf) + struct membuf to) { struct fpu *fpu = &target->thread.fpu; struct xregs_state *xsave; - int ret; if (!boot_cpu_has(X86_FEATURE_XSAVE)) return -ENODEV; @@ -89,10 +85,8 @@ int xstateregs_get(struct task_struct *target, const struct user_regset *regset, fpu__prepare_read(fpu); if (using_compacted_format()) { - if (kbuf) - ret = copy_xstate_to_kernel(kbuf, xsave, pos, count); - else - ret = copy_xstate_to_user(ubuf, xsave, pos, count); + copy_xstate_to_kernel(to, xsave); + return 0; } else { fpstate_sanitize_xstate(fpu); /* @@ -105,9 +99,8 @@ int xstateregs_get(struct task_struct *target, const struct user_regset *regset, /* * Copy the xstate memory layout. */ - ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, xsave, 0, -1); + return membuf_write(&to, xsave, fpu_user_xstate_size); } - return ret; } int xstateregs_set(struct task_struct *target, const struct user_regset *regset, @@ -293,8 +286,7 @@ void convert_to_fxsr(struct fxregs_state *fxsave, } int fpregs_get(struct task_struct *target, const struct user_regset *regset, - unsigned int pos, unsigned int count, - void *kbuf, void __user *ubuf) + struct membuf to) { struct fpu *fpu = &target->thread.fpu; struct user_i387_ia32_struct env; @@ -302,23 +294,22 @@ int fpregs_get(struct task_struct *target, const struct user_regset *regset, fpu__prepare_read(fpu); if (!boot_cpu_has(X86_FEATURE_FPU)) - return fpregs_soft_get(target, regset, pos, count, kbuf, ubuf); + return fpregs_soft_get(target, regset, to); - if (!boot_cpu_has(X86_FEATURE_FXSR)) - return user_regset_copyout(&pos, &count, &kbuf, &ubuf, - &fpu->state.fsave, 0, - -1); + if (!boot_cpu_has(X86_FEATURE_FXSR)) { + return membuf_write(&to, &fpu->state.fsave, + sizeof(struct fregs_state)); + } fpstate_sanitize_xstate(fpu); - if (kbuf && pos == 0 && count == sizeof(env)) { - convert_from_fxsr(kbuf, target); + if (to.left == sizeof(env)) { + convert_from_fxsr(to.p, target); return 0; } convert_from_fxsr(&env, target); - - return user_regset_copyout(&pos, &count, &kbuf, &ubuf, &env, 0, -1); + return membuf_write(&to, &env, sizeof(env)); } int fpregs_set(struct task_struct *target, const struct user_regset *regset, diff --git a/arch/x86/kernel/fpu/signal.c b/arch/x86/kernel/fpu/signal.c index e0b832df74043..a4ec65317a7fa 100644 --- a/arch/x86/kernel/fpu/signal.c +++ b/arch/x86/kernel/fpu/signal.c @@ -172,7 +172,8 @@ int copy_fpstate_to_sigframe(void __user *buf, void __user *buf_fx, int size) if (!static_cpu_has(X86_FEATURE_FPU)) { struct user_i387_ia32_struct fp; - fpregs_soft_get(current, NULL, 0, sizeof(fp), &fp, NULL); + fpregs_soft_get(current, NULL, (struct membuf){.p = &fp, + .left = sizeof(fp)}); return copy_to_user(buf, &fp, sizeof(fp)) ? -EFAULT : 0; } diff --git a/arch/x86/kernel/fpu/xstate.c b/arch/x86/kernel/fpu/xstate.c index bda2e5eaca0e0..4f8e4287c1760 100644 --- a/arch/x86/kernel/fpu/xstate.c +++ b/arch/x86/kernel/fpu/xstate.c @@ -1009,32 +1009,20 @@ static inline bool xfeatures_mxcsr_quirk(u64 xfeatures) return true; } -static void fill_gap(unsigned to, void **kbuf, unsigned *pos, unsigned *count) +static void fill_gap(struct membuf *to, unsigned *last, unsigned offset) { - if (*pos < to) { - unsigned size = to - *pos; - - if (size > *count) - size = *count; - memcpy(*kbuf, (void *)&init_fpstate.xsave + *pos, size); - *kbuf += size; - *pos += size; - *count -= size; - } + if (*last >= offset) + return; + membuf_write(to, (void *)&init_fpstate.xsave + *last, offset - *last); + *last = offset; } -static void copy_part(unsigned offset, unsigned size, void *from, - void **kbuf, unsigned *pos, unsigned *count) +static void copy_part(struct membuf *to, unsigned *last, unsigned offset, + unsigned size, void *from) { - fill_gap(offset, kbuf, pos, count); - if (size > *count) - size = *count; - if (size) { - memcpy(*kbuf, from, size); - *kbuf += size; - *pos += size; - *count -= size; - } + fill_gap(to, last, offset); + membuf_write(to, from, size); + *last = offset + size; } /* @@ -1044,19 +1032,14 @@ static void copy_part(unsigned offset, unsigned size, void *from, * It supports partial copy but pos always starts from zero. This is called * from xstateregs_get() and there we check the CPU has XSAVES. */ -int copy_xstate_to_kernel(void *kbuf, struct xregs_state *xsave, unsigned int offset_start, unsigned int size_total) +void copy_xstate_to_kernel(struct membuf to, struct xregs_state *xsave) { struct xstate_header header; const unsigned off_mxcsr = offsetof(struct fxregs_state, mxcsr); - unsigned count = size_total; + unsigned size = to.left; + unsigned last = 0; int i; - /* - * Currently copy_regset_to_user() starts from pos 0: - */ - if (unlikely(offset_start != 0)) - return -EFAULT; - /* * The destination is a ptrace buffer; we put in only user xstates: */ @@ -1065,27 +1048,26 @@ int copy_xstate_to_kernel(void *kbuf, struct xregs_state *xsave, unsigned int of header.xfeatures &= xfeatures_mask_user(); if (header.xfeatures & XFEATURE_MASK_FP) - copy_part(0, off_mxcsr, - &xsave->i387, &kbuf, &offset_start, &count); + copy_part(&to, &last, 0, off_mxcsr, &xsave->i387); if (header.xfeatures & (XFEATURE_MASK_SSE | XFEATURE_MASK_YMM)) - copy_part(off_mxcsr, MXCSR_AND_FLAGS_SIZE, - &xsave->i387.mxcsr, &kbuf, &offset_start, &count); + copy_part(&to, &last, off_mxcsr, + MXCSR_AND_FLAGS_SIZE, &xsave->i387.mxcsr); if (header.xfeatures & XFEATURE_MASK_FP) - copy_part(offsetof(struct fxregs_state, st_space), 128, - &xsave->i387.st_space, &kbuf, &offset_start, &count); + copy_part(&to, &last, offsetof(struct fxregs_state, st_space), + 128, &xsave->i387.st_space); if (header.xfeatures & XFEATURE_MASK_SSE) - copy_part(xstate_offsets[XFEATURE_MASK_SSE], 256, - &xsave->i387.xmm_space, &kbuf, &offset_start, &count); + copy_part(&to, &last, xstate_offsets[XFEATURE_SSE], + 256, &xsave->i387.xmm_space); /* * Fill xsave->i387.sw_reserved value for ptrace frame: */ - copy_part(offsetof(struct fxregs_state, sw_reserved), 48, - xstate_fx_sw_bytes, &kbuf, &offset_start, &count); + copy_part(&to, &last, offsetof(struct fxregs_state, sw_reserved), + 48, xstate_fx_sw_bytes); /* * Copy xregs_state->header: */ - copy_part(offsetof(struct xregs_state, header), sizeof(header), - &header, &kbuf, &offset_start, &count); + copy_part(&to, &last, offsetof(struct xregs_state, header), + sizeof(header), &header); for (i = FIRST_EXTENDED_XFEATURE; i < XFEATURE_MAX; i++) { /* @@ -1094,104 +1076,12 @@ int copy_xstate_to_kernel(void *kbuf, struct xregs_state *xsave, unsigned int of if ((header.xfeatures >> i) & 1) { void *src = __raw_xsave_addr(xsave, i); - copy_part(xstate_offsets[i], xstate_sizes[i], - src, &kbuf, &offset_start, &count); + copy_part(&to, &last, xstate_offsets[i], + xstate_sizes[i], src); } } - fill_gap(size_total, &kbuf, &offset_start, &count); - - return 0; -} - -static inline int -__copy_xstate_to_user(void __user *ubuf, const void *data, unsigned int offset, unsigned int size, unsigned int size_total) -{ - if (!size) - return 0; - - if (offset < size_total) { - unsigned int copy = min(size, size_total - offset); - - if (__copy_to_user(ubuf + offset, data, copy)) - return -EFAULT; - } - return 0; -} - -/* - * Convert from kernel XSAVES compacted format to standard format and copy - * to a user-space buffer. It supports partial copy but pos always starts from - * zero. This is called from xstateregs_get() and there we check the CPU - * has XSAVES. - */ -int copy_xstate_to_user(void __user *ubuf, struct xregs_state *xsave, unsigned int offset_start, unsigned int size_total) -{ - unsigned int offset, size; - int ret, i; - struct xstate_header header; - - /* - * Currently copy_regset_to_user() starts from pos 0: - */ - if (unlikely(offset_start != 0)) - return -EFAULT; - - /* - * The destination is a ptrace buffer; we put in only user xstates: - */ - memset(&header, 0, sizeof(header)); - header.xfeatures = xsave->header.xfeatures; - header.xfeatures &= xfeatures_mask_user(); - - /* - * Copy xregs_state->header: - */ - offset = offsetof(struct xregs_state, header); - size = sizeof(header); - - ret = __copy_xstate_to_user(ubuf, &header, offset, size, size_total); - if (ret) - return ret; - - for (i = 0; i < XFEATURE_MAX; i++) { - /* - * Copy only in-use xstates: - */ - if ((header.xfeatures >> i) & 1) { - void *src = __raw_xsave_addr(xsave, i); - - offset = xstate_offsets[i]; - size = xstate_sizes[i]; - - /* The next component has to fit fully into the output buffer: */ - if (offset + size > size_total) - break; - - ret = __copy_xstate_to_user(ubuf, src, offset, size, size_total); - if (ret) - return ret; - } - - } - - if (xfeatures_mxcsr_quirk(header.xfeatures)) { - offset = offsetof(struct fxregs_state, mxcsr); - size = MXCSR_AND_FLAGS_SIZE; - __copy_xstate_to_user(ubuf, &xsave->i387.mxcsr, offset, size, size_total); - } - - /* - * Fill xsave->i387.sw_reserved value for ptrace frame: - */ - offset = offsetof(struct fxregs_state, sw_reserved); - size = sizeof(xstate_fx_sw_bytes); - - ret = __copy_xstate_to_user(ubuf, xstate_fx_sw_bytes, offset, size, size_total); - if (ret) - return ret; - - return 0; + fill_gap(&to, &last, size); } /* diff --git a/arch/x86/kernel/ptrace.c b/arch/x86/kernel/ptrace.c index 44130588987f5..8413cb1285a7c 100644 --- a/arch/x86/kernel/ptrace.c +++ b/arch/x86/kernel/ptrace.c @@ -433,26 +433,12 @@ static unsigned long getreg(struct task_struct *task, unsigned long offset) static int genregs_get(struct task_struct *target, const struct user_regset *regset, - unsigned int pos, unsigned int count, - void *kbuf, void __user *ubuf) + struct membuf to) { - if (kbuf) { - unsigned long *k = kbuf; - while (count >= sizeof(*k)) { - *k++ = getreg(target, pos); - count -= sizeof(*k); - pos += sizeof(*k); - } - } else { - unsigned long __user *u = ubuf; - while (count >= sizeof(*u)) { - if (__put_user(getreg(target, pos), u++)) - return -EFAULT; - count -= sizeof(*u); - pos += sizeof(*u); - } - } + int reg; + for (reg = 0; to.left; reg++) + membuf_store(&to, getreg(target, reg * sizeof(unsigned long))); return 0; } @@ -716,16 +702,14 @@ static int ioperm_active(struct task_struct *target, static int ioperm_get(struct task_struct *target, const struct user_regset *regset, - unsigned int pos, unsigned int count, - void *kbuf, void __user *ubuf) + struct membuf to) { struct io_bitmap *iobm = target->thread.io_bitmap; if (!iobm) return -ENXIO; - return user_regset_copyout(&pos, &count, &kbuf, &ubuf, - iobm->bitmap, 0, IO_BITMAP_BYTES); + return membuf_write(&to, iobm->bitmap, IO_BITMAP_BYTES); } /* @@ -1003,28 +987,15 @@ static int getreg32(struct task_struct *child, unsigned regno, u32 *val) static int genregs32_get(struct task_struct *target, const struct user_regset *regset, - unsigned int pos, unsigned int count, - void *kbuf, void __user *ubuf) + struct membuf to) { - if (kbuf) { - compat_ulong_t *k = kbuf; - while (count >= sizeof(*k)) { - getreg32(target, pos, k++); - count -= sizeof(*k); - pos += sizeof(*k); - } - } else { - compat_ulong_t __user *u = ubuf; - while (count >= sizeof(*u)) { - compat_ulong_t word; - getreg32(target, pos, &word); - if (__put_user(word, u++)) - return -EFAULT; - count -= sizeof(*u); - pos += sizeof(*u); - } - } + int reg; + for (reg = 0; to.left; reg++) { + u32 val; + getreg32(target, reg * 4, &val); + membuf_store(&to, val); + } return 0; } @@ -1234,25 +1205,25 @@ static struct user_regset x86_64_regsets[] __ro_after_init = { .core_note_type = NT_PRSTATUS, .n = sizeof(struct user_regs_struct) / sizeof(long), .size = sizeof(long), .align = sizeof(long), - .get = genregs_get, .set = genregs_set + .regset_get = genregs_get, .set = genregs_set }, [REGSET_FP] = { .core_note_type = NT_PRFPREG, .n = sizeof(struct user_i387_struct) / sizeof(long), .size = sizeof(long), .align = sizeof(long), - .active = regset_xregset_fpregs_active, .get = xfpregs_get, .set = xfpregs_set + .active = regset_xregset_fpregs_active, .regset_get = xfpregs_get, .set = xfpregs_set }, [REGSET_XSTATE] = { .core_note_type = NT_X86_XSTATE, .size = sizeof(u64), .align = sizeof(u64), - .active = xstateregs_active, .get = xstateregs_get, + .active = xstateregs_active, .regset_get = xstateregs_get, .set = xstateregs_set }, [REGSET_IOPERM64] = { .core_note_type = NT_386_IOPERM, .n = IO_BITMAP_LONGS, .size = sizeof(long), .align = sizeof(long), - .active = ioperm_active, .get = ioperm_get + .active = ioperm_active, .regset_get = ioperm_get }, }; @@ -1275,24 +1246,24 @@ static struct user_regset x86_32_regsets[] __ro_after_init = { .core_note_type = NT_PRSTATUS, .n = sizeof(struct user_regs_struct32) / sizeof(u32), .size = sizeof(u32), .align = sizeof(u32), - .get = genregs32_get, .set = genregs32_set + .regset_get = genregs32_get, .set = genregs32_set }, [REGSET_FP] = { .core_note_type = NT_PRFPREG, .n = sizeof(struct user_i387_ia32_struct) / sizeof(u32), .size = sizeof(u32), .align = sizeof(u32), - .active = regset_fpregs_active, .get = fpregs_get, .set = fpregs_set + .active = regset_fpregs_active, .regset_get = fpregs_get, .set = fpregs_set }, [REGSET_XFP] = { .core_note_type = NT_PRXFPREG, .n = sizeof(struct user32_fxsr_struct) / sizeof(u32), .size = sizeof(u32), .align = sizeof(u32), - .active = regset_xregset_fpregs_active, .get = xfpregs_get, .set = xfpregs_set + .active = regset_xregset_fpregs_active, .regset_get = xfpregs_get, .set = xfpregs_set }, [REGSET_XSTATE] = { .core_note_type = NT_X86_XSTATE, .size = sizeof(u64), .align = sizeof(u64), - .active = xstateregs_active, .get = xstateregs_get, + .active = xstateregs_active, .regset_get = xstateregs_get, .set = xstateregs_set }, [REGSET_TLS] = { @@ -1301,13 +1272,13 @@ static struct user_regset x86_32_regsets[] __ro_after_init = { .size = sizeof(struct user_desc), .align = sizeof(struct user_desc), .active = regset_tls_active, - .get = regset_tls_get, .set = regset_tls_set + .regset_get = regset_tls_get, .set = regset_tls_set }, [REGSET_IOPERM32] = { .core_note_type = NT_386_IOPERM, .n = IO_BITMAP_BYTES / sizeof(u32), .size = sizeof(u32), .align = sizeof(u32), - .active = ioperm_active, .get = ioperm_get + .active = ioperm_active, .regset_get = ioperm_get }, }; diff --git a/arch/x86/kernel/tls.c b/arch/x86/kernel/tls.c index 71d3fef1edc92..64a496a0687f6 100644 --- a/arch/x86/kernel/tls.c +++ b/arch/x86/kernel/tls.c @@ -256,36 +256,16 @@ int regset_tls_active(struct task_struct *target, } int regset_tls_get(struct task_struct *target, const struct user_regset *regset, - unsigned int pos, unsigned int count, - void *kbuf, void __user *ubuf) + struct membuf to) { const struct desc_struct *tls; + struct user_desc v; + int pos; - if (pos >= GDT_ENTRY_TLS_ENTRIES * sizeof(struct user_desc) || - (pos % sizeof(struct user_desc)) != 0 || - (count % sizeof(struct user_desc)) != 0) - return -EINVAL; - - pos /= sizeof(struct user_desc); - count /= sizeof(struct user_desc); - - tls = &target->thread.tls_array[pos]; - - if (kbuf) { - struct user_desc *info = kbuf; - while (count-- > 0) - fill_user_desc(info++, GDT_ENTRY_TLS_MIN + pos++, - tls++); - } else { - struct user_desc __user *u_info = ubuf; - while (count-- > 0) { - struct user_desc info; - fill_user_desc(&info, GDT_ENTRY_TLS_MIN + pos++, tls++); - if (__copy_to_user(u_info++, &info, sizeof(info))) - return -EFAULT; - } + for (pos = 0, tls = target->thread.tls_array; to.left; pos++, tls++) { + fill_user_desc(&v, GDT_ENTRY_TLS_MIN + pos, tls); + membuf_write(&to, &v, sizeof(v)); } - return 0; } diff --git a/arch/x86/kernel/tls.h b/arch/x86/kernel/tls.h index 3a76e1d3535e6..fc39447a0c1ab 100644 --- a/arch/x86/kernel/tls.h +++ b/arch/x86/kernel/tls.h @@ -12,7 +12,7 @@ #include extern user_regset_active_fn regset_tls_active; -extern user_regset_get_fn regset_tls_get; +extern user_regset_get2_fn regset_tls_get; extern user_regset_set_fn regset_tls_set; #endif /* _ARCH_X86_KERNEL_TLS_H */ diff --git a/arch/x86/math-emu/fpu_entry.c b/arch/x86/math-emu/fpu_entry.c index a873da6b46d6b..8679a9d6c47f9 100644 --- a/arch/x86/math-emu/fpu_entry.c +++ b/arch/x86/math-emu/fpu_entry.c @@ -689,12 +689,10 @@ int fpregs_soft_set(struct task_struct *target, int fpregs_soft_get(struct task_struct *target, const struct user_regset *regset, - unsigned int pos, unsigned int count, - void *kbuf, void __user *ubuf) + struct membuf to) { struct swregs_state *s387 = &target->thread.fpu.state.soft; const void *space = s387->st_space; - int ret; int offset = (S387->ftop & 7) * 10, other = 80 - offset; RE_ENTRANT_CHECK_OFF; @@ -709,18 +707,11 @@ int fpregs_soft_get(struct task_struct *target, S387->fos |= 0xffff0000; #endif /* PECULIAR_486 */ - ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, s387, 0, - offsetof(struct swregs_state, st_space)); - - /* Copy all registers in stack order. */ - if (!ret) - ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, - space + offset, 0, other); - if (!ret) - ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, - space, 0, offset); + membuf_write(&to, s387, offsetof(struct swregs_state, st_space)); + membuf_write(&to, space + offset, other); + membuf_write(&to, space, offset); RE_ENTRANT_CHECK_ON; - return ret; + return 0; } -- GitLab From 47e12855a91d2a9ca18d8fe10382e5a8d87b1417 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sat, 22 Feb 2020 11:01:44 -0500 Subject: [PATCH 0956/1476] powerpc: switch to ->regset_get() Note: compat variant of REGSET_TM_CGPR is almost certainly wrong; it claims to be 48*64bit, but just as compat REGSET_GPR it stores 44*32bit of (truncated) registers + 4 32bit zeros... followed by 48 more 32bit zeroes. Might be too late to change - it's a userland ABI, after all ;-/ Signed-off-by: Al Viro --- arch/powerpc/kernel/ptrace/ptrace-altivec.c | 37 ++-- arch/powerpc/kernel/ptrace/ptrace-decl.h | 44 ++--- arch/powerpc/kernel/ptrace/ptrace-novsx.c | 5 +- arch/powerpc/kernel/ptrace/ptrace-spe.c | 16 +- arch/powerpc/kernel/ptrace/ptrace-tm.c | 152 +++++----------- arch/powerpc/kernel/ptrace/ptrace-view.c | 185 +++++++------------- arch/powerpc/kernel/ptrace/ptrace-vsx.c | 13 +- 7 files changed, 148 insertions(+), 304 deletions(-) diff --git a/arch/powerpc/kernel/ptrace/ptrace-altivec.c b/arch/powerpc/kernel/ptrace/ptrace-altivec.c index dd8b75dfbd068..0d9bc4bd4972b 100644 --- a/arch/powerpc/kernel/ptrace/ptrace-altivec.c +++ b/arch/powerpc/kernel/ptrace/ptrace-altivec.c @@ -41,38 +41,25 @@ int vr_active(struct task_struct *target, const struct user_regset *regset) * }; */ int vr_get(struct task_struct *target, const struct user_regset *regset, - unsigned int pos, unsigned int count, void *kbuf, void __user *ubuf) + struct membuf to) { - int ret; + union { + elf_vrreg_t reg; + u32 word; + } vrsave; flush_altivec_to_thread(target); BUILD_BUG_ON(offsetof(struct thread_vr_state, vscr) != offsetof(struct thread_vr_state, vr[32])); - ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, - &target->thread.vr_state, 0, - 33 * sizeof(vector128)); - if (!ret) { - /* - * Copy out only the low-order word of vrsave. - */ - int start, end; - union { - elf_vrreg_t reg; - u32 word; - } vrsave; - memset(&vrsave, 0, sizeof(vrsave)); - - vrsave.word = target->thread.vrsave; - - start = 33 * sizeof(vector128); - end = start + sizeof(vrsave); - ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, &vrsave, - start, end); - } - - return ret; + membuf_write(&to, &target->thread.vr_state, 33 * sizeof(vector128)); + /* + * Copy out only the low-order word of vrsave. + */ + memset(&vrsave, 0, sizeof(vrsave)); + vrsave.word = target->thread.vrsave; + return membuf_write(&to, &vrsave, sizeof(vrsave)); } /* diff --git a/arch/powerpc/kernel/ptrace/ptrace-decl.h b/arch/powerpc/kernel/ptrace/ptrace-decl.h index 3c8a819992925..67447a6197ebe 100644 --- a/arch/powerpc/kernel/ptrace/ptrace-decl.h +++ b/arch/powerpc/kernel/ptrace/ptrace-decl.h @@ -63,8 +63,7 @@ enum powerpc_regset { /* ptrace-(no)vsx */ -int fpr_get(struct task_struct *target, const struct user_regset *regset, - unsigned int pos, unsigned int count, void *kbuf, void __user *ubuf); +user_regset_get2_fn fpr_get; int fpr_set(struct task_struct *target, const struct user_regset *regset, unsigned int pos, unsigned int count, const void *kbuf, const void __user *ubuf); @@ -72,8 +71,7 @@ int fpr_set(struct task_struct *target, const struct user_regset *regset, /* ptrace-vsx */ int vsr_active(struct task_struct *target, const struct user_regset *regset); -int vsr_get(struct task_struct *target, const struct user_regset *regset, - unsigned int pos, unsigned int count, void *kbuf, void __user *ubuf); +user_regset_get2_fn vsr_get; int vsr_set(struct task_struct *target, const struct user_regset *regset, unsigned int pos, unsigned int count, const void *kbuf, const void __user *ubuf); @@ -81,8 +79,7 @@ int vsr_set(struct task_struct *target, const struct user_regset *regset, /* ptrace-altivec */ int vr_active(struct task_struct *target, const struct user_regset *regset); -int vr_get(struct task_struct *target, const struct user_regset *regset, - unsigned int pos, unsigned int count, void *kbuf, void __user *ubuf); +user_regset_get2_fn vr_get; int vr_set(struct task_struct *target, const struct user_regset *regset, unsigned int pos, unsigned int count, const void *kbuf, const void __user *ubuf); @@ -90,8 +87,7 @@ int vr_set(struct task_struct *target, const struct user_regset *regset, /* ptrace-spe */ int evr_active(struct task_struct *target, const struct user_regset *regset); -int evr_get(struct task_struct *target, const struct user_regset *regset, - unsigned int pos, unsigned int count, void *kbuf, void __user *ubuf); +user_regset_get2_fn evr_get; int evr_set(struct task_struct *target, const struct user_regset *regset, unsigned int pos, unsigned int count, const void *kbuf, const void __user *ubuf); @@ -100,9 +96,8 @@ int evr_set(struct task_struct *target, const struct user_regset *regset, int gpr32_get_common(struct task_struct *target, const struct user_regset *regset, - unsigned int pos, unsigned int count, - void *kbuf, void __user *ubuf, - unsigned long *regs); + struct membuf to, + unsigned long *regs); int gpr32_set_common(struct task_struct *target, const struct user_regset *regset, unsigned int pos, unsigned int count, @@ -118,55 +113,46 @@ static inline void flush_tmregs_to_thread(struct task_struct *tsk) { } #endif int tm_cgpr_active(struct task_struct *target, const struct user_regset *regset); -int tm_cgpr_get(struct task_struct *target, const struct user_regset *regset, - unsigned int pos, unsigned int count, void *kbuf, void __user *ubuf); +user_regset_get2_fn tm_cgpr_get; int tm_cgpr_set(struct task_struct *target, const struct user_regset *regset, unsigned int pos, unsigned int count, const void *kbuf, const void __user *ubuf); int tm_cfpr_active(struct task_struct *target, const struct user_regset *regset); -int tm_cfpr_get(struct task_struct *target, const struct user_regset *regset, - unsigned int pos, unsigned int count, void *kbuf, void __user *ubuf); +user_regset_get2_fn tm_cfpr_get; int tm_cfpr_set(struct task_struct *target, const struct user_regset *regset, unsigned int pos, unsigned int count, const void *kbuf, const void __user *ubuf); int tm_cvmx_active(struct task_struct *target, const struct user_regset *regset); -int tm_cvmx_get(struct task_struct *target, const struct user_regset *regset, - unsigned int pos, unsigned int count, void *kbuf, void __user *ubuf); +user_regset_get2_fn tm_cvmx_get; int tm_cvmx_set(struct task_struct *target, const struct user_regset *regset, unsigned int pos, unsigned int count, const void *kbuf, const void __user *ubuf); int tm_cvsx_active(struct task_struct *target, const struct user_regset *regset); -int tm_cvsx_get(struct task_struct *target, const struct user_regset *regset, - unsigned int pos, unsigned int count, void *kbuf, void __user *ubuf); +user_regset_get2_fn tm_cvsx_get; int tm_cvsx_set(struct task_struct *target, const struct user_regset *regset, unsigned int pos, unsigned int count, const void *kbuf, const void __user *ubuf); int tm_spr_active(struct task_struct *target, const struct user_regset *regset); -int tm_spr_get(struct task_struct *target, const struct user_regset *regset, - unsigned int pos, unsigned int count, void *kbuf, void __user *ubuf); +user_regset_get2_fn tm_spr_get; int tm_spr_set(struct task_struct *target, const struct user_regset *regset, unsigned int pos, unsigned int count, const void *kbuf, const void __user *ubuf); int tm_tar_active(struct task_struct *target, const struct user_regset *regset); -int tm_tar_get(struct task_struct *target, const struct user_regset *regset, - unsigned int pos, unsigned int count, void *kbuf, void __user *ubuf); +user_regset_get2_fn tm_tar_get; int tm_tar_set(struct task_struct *target, const struct user_regset *regset, unsigned int pos, unsigned int count, const void *kbuf, const void __user *ubuf); int tm_ppr_active(struct task_struct *target, const struct user_regset *regset); -int tm_ppr_get(struct task_struct *target, const struct user_regset *regset, - unsigned int pos, unsigned int count, void *kbuf, void __user *ubuf); +user_regset_get2_fn tm_ppr_get; int tm_ppr_set(struct task_struct *target, const struct user_regset *regset, unsigned int pos, unsigned int count, const void *kbuf, const void __user *ubuf); int tm_dscr_active(struct task_struct *target, const struct user_regset *regset); -int tm_dscr_get(struct task_struct *target, const struct user_regset *regset, - unsigned int pos, unsigned int count, void *kbuf, void __user *ubuf); +user_regset_get2_fn tm_dscr_get; int tm_dscr_set(struct task_struct *target, const struct user_regset *regset, unsigned int pos, unsigned int count, const void *kbuf, const void __user *ubuf); -int tm_cgpr32_get(struct task_struct *target, const struct user_regset *regset, - unsigned int pos, unsigned int count, void *kbuf, void __user *ubuf); +user_regset_get2_fn tm_cgpr32_get; int tm_cgpr32_set(struct task_struct *target, const struct user_regset *regset, unsigned int pos, unsigned int count, const void *kbuf, const void __user *ubuf); diff --git a/arch/powerpc/kernel/ptrace/ptrace-novsx.c b/arch/powerpc/kernel/ptrace/ptrace-novsx.c index b2dc4e92d11ab..b3b36835658af 100644 --- a/arch/powerpc/kernel/ptrace/ptrace-novsx.c +++ b/arch/powerpc/kernel/ptrace/ptrace-novsx.c @@ -19,15 +19,14 @@ * }; */ int fpr_get(struct task_struct *target, const struct user_regset *regset, - unsigned int pos, unsigned int count, void *kbuf, void __user *ubuf) + struct membuf to) { BUILD_BUG_ON(offsetof(struct thread_fp_state, fpscr) != offsetof(struct thread_fp_state, fpr[32])); flush_fp_to_thread(target); - return user_regset_copyout(&pos, &count, &kbuf, &ubuf, - &target->thread.fp_state, 0, -1); + return membuf_write(&to, &target->thread.fp_state, 33 * sizeof(u64)); } /* diff --git a/arch/powerpc/kernel/ptrace/ptrace-spe.c b/arch/powerpc/kernel/ptrace/ptrace-spe.c index 68b86b4a4be46..47034d0690452 100644 --- a/arch/powerpc/kernel/ptrace/ptrace-spe.c +++ b/arch/powerpc/kernel/ptrace/ptrace-spe.c @@ -23,25 +23,17 @@ int evr_active(struct task_struct *target, const struct user_regset *regset) } int evr_get(struct task_struct *target, const struct user_regset *regset, - unsigned int pos, unsigned int count, void *kbuf, void __user *ubuf) + struct membuf to) { - int ret; - flush_spe_to_thread(target); - ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, - &target->thread.evr, - 0, sizeof(target->thread.evr)); + membuf_write(&to, &target->thread.evr, sizeof(target->thread.evr)); BUILD_BUG_ON(offsetof(struct thread_struct, acc) + sizeof(u64) != offsetof(struct thread_struct, spefscr)); - if (!ret) - ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, - &target->thread.acc, - sizeof(target->thread.evr), -1); - - return ret; + return membuf_write(&to, &target->thread.acc, + sizeof(u64) + sizeof(u32)); } int evr_set(struct task_struct *target, const struct user_regset *regset, diff --git a/arch/powerpc/kernel/ptrace/ptrace-tm.c b/arch/powerpc/kernel/ptrace/ptrace-tm.c index 32d62c606681e..54f2d076206fa 100644 --- a/arch/powerpc/kernel/ptrace/ptrace-tm.c +++ b/arch/powerpc/kernel/ptrace/ptrace-tm.c @@ -70,10 +70,7 @@ int tm_cgpr_active(struct task_struct *target, const struct user_regset *regset) * tm_cgpr_get - get CGPR registers * @target: The target task. * @regset: The user regset structure. - * @pos: The buffer position. - * @count: Number of bytes to copy. - * @kbuf: Kernel buffer to copy from. - * @ubuf: User buffer to copy into. + * @to: Destination of copy. * * This function gets transaction checkpointed GPR registers. * @@ -87,10 +84,8 @@ int tm_cgpr_active(struct task_struct *target, const struct user_regset *regset) * }; */ int tm_cgpr_get(struct task_struct *target, const struct user_regset *regset, - unsigned int pos, unsigned int count, void *kbuf, void __user *ubuf) + struct membuf to) { - int ret; - if (!cpu_has_feature(CPU_FTR_TM)) return -ENODEV; @@ -101,31 +96,18 @@ int tm_cgpr_get(struct task_struct *target, const struct user_regset *regset, flush_fp_to_thread(target); flush_altivec_to_thread(target); - ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, - &target->thread.ckpt_regs, - 0, offsetof(struct pt_regs, msr)); - if (!ret) { - unsigned long msr = get_user_ckpt_msr(target); - - ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, &msr, - offsetof(struct pt_regs, msr), - offsetof(struct pt_regs, msr) + - sizeof(msr)); - } + membuf_write(&to, &target->thread.ckpt_regs, + offsetof(struct pt_regs, msr)); + membuf_store(&to, get_user_ckpt_msr(target)); BUILD_BUG_ON(offsetof(struct pt_regs, orig_gpr3) != offsetof(struct pt_regs, msr) + sizeof(long)); - if (!ret) - ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, - &target->thread.ckpt_regs.orig_gpr3, - offsetof(struct pt_regs, orig_gpr3), - sizeof(struct user_pt_regs)); - if (!ret) - ret = user_regset_copyout_zero(&pos, &count, &kbuf, &ubuf, - sizeof(struct user_pt_regs), -1); - - return ret; + membuf_write(&to, &target->thread.ckpt_regs.orig_gpr3, + sizeof(struct user_pt_regs) - + offsetof(struct pt_regs, orig_gpr3)); + return membuf_zero(&to, ELF_NGREG * sizeof(unsigned long) - + sizeof(struct user_pt_regs)); } /* @@ -229,10 +211,7 @@ int tm_cfpr_active(struct task_struct *target, const struct user_regset *regset) * tm_cfpr_get - get CFPR registers * @target: The target task. * @regset: The user regset structure. - * @pos: The buffer position. - * @count: Number of bytes to copy. - * @kbuf: Kernel buffer to copy from. - * @ubuf: User buffer to copy into. + * @to: Destination of copy. * * This function gets in transaction checkpointed FPR registers. * @@ -247,7 +226,7 @@ int tm_cfpr_active(struct task_struct *target, const struct user_regset *regset) *}; */ int tm_cfpr_get(struct task_struct *target, const struct user_regset *regset, - unsigned int pos, unsigned int count, void *kbuf, void __user *ubuf) + struct membuf to) { u64 buf[33]; int i; @@ -266,7 +245,7 @@ int tm_cfpr_get(struct task_struct *target, const struct user_regset *regset, for (i = 0; i < 32 ; i++) buf[i] = target->thread.TS_CKFPR(i); buf[32] = target->thread.ckfp_state.fpscr; - return user_regset_copyout(&pos, &count, &kbuf, &ubuf, buf, 0, -1); + return membuf_write(&to, buf, sizeof(buf)); } /** @@ -344,10 +323,7 @@ int tm_cvmx_active(struct task_struct *target, const struct user_regset *regset) * tm_cvmx_get - get CMVX registers * @target: The target task. * @regset: The user regset structure. - * @pos: The buffer position. - * @count: Number of bytes to copy. - * @kbuf: Kernel buffer to copy from. - * @ubuf: User buffer to copy into. + * @to: Destination of copy. * * This function gets in transaction checkpointed VMX registers. * @@ -363,10 +339,12 @@ int tm_cvmx_active(struct task_struct *target, const struct user_regset *regset) *}; */ int tm_cvmx_get(struct task_struct *target, const struct user_regset *regset, - unsigned int pos, unsigned int count, void *kbuf, void __user *ubuf) + struct membuf to) { - int ret; - + union { + elf_vrreg_t reg; + u32 word; + } vrsave; BUILD_BUG_ON(TVSO(vscr) != TVSO(vr[32])); if (!cpu_has_feature(CPU_FTR_TM)) @@ -380,23 +358,13 @@ int tm_cvmx_get(struct task_struct *target, const struct user_regset *regset, flush_fp_to_thread(target); flush_altivec_to_thread(target); - ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, &target->thread.ckvr_state, - 0, 33 * sizeof(vector128)); - if (!ret) { - /* - * Copy out only the low-order word of vrsave. - */ - union { - elf_vrreg_t reg; - u32 word; - } vrsave; - memset(&vrsave, 0, sizeof(vrsave)); - vrsave.word = target->thread.ckvrsave; - ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, &vrsave, - 33 * sizeof(vector128), -1); - } - - return ret; + membuf_write(&to, &target->thread.ckvr_state, 33 * sizeof(vector128)); + /* + * Copy out only the low-order word of vrsave. + */ + memset(&vrsave, 0, sizeof(vrsave)); + vrsave.word = target->thread.ckvrsave; + return membuf_write(&to, &vrsave, sizeof(vrsave)); } /** @@ -484,10 +452,7 @@ int tm_cvsx_active(struct task_struct *target, const struct user_regset *regset) * tm_cvsx_get - get CVSX registers * @target: The target task. * @regset: The user regset structure. - * @pos: The buffer position. - * @count: Number of bytes to copy. - * @kbuf: Kernel buffer to copy from. - * @ubuf: User buffer to copy into. + * @to: Destination of copy. * * This function gets in transaction checkpointed VSX registers. * @@ -501,10 +466,10 @@ int tm_cvsx_active(struct task_struct *target, const struct user_regset *regset) *}; */ int tm_cvsx_get(struct task_struct *target, const struct user_regset *regset, - unsigned int pos, unsigned int count, void *kbuf, void __user *ubuf) + struct membuf to) { u64 buf[32]; - int ret, i; + int i; if (!cpu_has_feature(CPU_FTR_TM)) return -ENODEV; @@ -520,10 +485,7 @@ int tm_cvsx_get(struct task_struct *target, const struct user_regset *regset, for (i = 0; i < 32 ; i++) buf[i] = target->thread.ckfp_state.fpr[i][TS_VSRLOWOFFSET]; - ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, - buf, 0, 32 * sizeof(double)); - - return ret; + return membuf_write(&to, buf, 32 * sizeof(double)); } /** @@ -597,10 +559,7 @@ int tm_spr_active(struct task_struct *target, const struct user_regset *regset) * tm_spr_get - get the TM related SPR registers * @target: The target task. * @regset: The user regset structure. - * @pos: The buffer position. - * @count: Number of bytes to copy. - * @kbuf: Kernel buffer to copy from. - * @ubuf: User buffer to copy into. + * @to: Destination of copy. * * This function gets transactional memory related SPR registers. * The userspace interface buffer layout is as follows. @@ -612,10 +571,8 @@ int tm_spr_active(struct task_struct *target, const struct user_regset *regset) * }; */ int tm_spr_get(struct task_struct *target, const struct user_regset *regset, - unsigned int pos, unsigned int count, void *kbuf, void __user *ubuf) + struct membuf to) { - int ret; - /* Build tests */ BUILD_BUG_ON(TSO(tm_tfhar) + sizeof(u64) != TSO(tm_texasr)); BUILD_BUG_ON(TSO(tm_texasr) + sizeof(u64) != TSO(tm_tfiar)); @@ -630,21 +587,11 @@ int tm_spr_get(struct task_struct *target, const struct user_regset *regset, flush_altivec_to_thread(target); /* TFHAR register */ - ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, - &target->thread.tm_tfhar, 0, sizeof(u64)); - + membuf_write(&to, &target->thread.tm_tfhar, sizeof(u64)); /* TEXASR register */ - if (!ret) - ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, - &target->thread.tm_texasr, sizeof(u64), - 2 * sizeof(u64)); - + membuf_write(&to, &target->thread.tm_texasr, sizeof(u64)); /* TFIAR register */ - if (!ret) - ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, - &target->thread.tm_tfiar, - 2 * sizeof(u64), 3 * sizeof(u64)); - return ret; + return membuf_write(&to, &target->thread.tm_tfiar, sizeof(u64)); } /** @@ -714,19 +661,15 @@ int tm_tar_active(struct task_struct *target, const struct user_regset *regset) } int tm_tar_get(struct task_struct *target, const struct user_regset *regset, - unsigned int pos, unsigned int count, void *kbuf, void __user *ubuf) + struct membuf to) { - int ret; - if (!cpu_has_feature(CPU_FTR_TM)) return -ENODEV; if (!MSR_TM_ACTIVE(target->thread.regs->msr)) return -ENODATA; - ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, - &target->thread.tm_tar, 0, sizeof(u64)); - return ret; + return membuf_write(&to, &target->thread.tm_tar, sizeof(u64)); } int tm_tar_set(struct task_struct *target, const struct user_regset *regset, @@ -759,19 +702,15 @@ int tm_ppr_active(struct task_struct *target, const struct user_regset *regset) int tm_ppr_get(struct task_struct *target, const struct user_regset *regset, - unsigned int pos, unsigned int count, void *kbuf, void __user *ubuf) + struct membuf to) { - int ret; - if (!cpu_has_feature(CPU_FTR_TM)) return -ENODEV; if (!MSR_TM_ACTIVE(target->thread.regs->msr)) return -ENODATA; - ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, - &target->thread.tm_ppr, 0, sizeof(u64)); - return ret; + return membuf_write(&to, &target->thread.tm_ppr, sizeof(u64)); } int tm_ppr_set(struct task_struct *target, const struct user_regset *regset, @@ -803,19 +742,15 @@ int tm_dscr_active(struct task_struct *target, const struct user_regset *regset) } int tm_dscr_get(struct task_struct *target, const struct user_regset *regset, - unsigned int pos, unsigned int count, void *kbuf, void __user *ubuf) + struct membuf to) { - int ret; - if (!cpu_has_feature(CPU_FTR_TM)) return -ENODEV; if (!MSR_TM_ACTIVE(target->thread.regs->msr)) return -ENODATA; - ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, - &target->thread.tm_dscr, 0, sizeof(u64)); - return ret; + return membuf_write(&to, &target->thread.tm_dscr, sizeof(u64)); } int tm_dscr_set(struct task_struct *target, const struct user_regset *regset, @@ -836,10 +771,11 @@ int tm_dscr_set(struct task_struct *target, const struct user_regset *regset, } int tm_cgpr32_get(struct task_struct *target, const struct user_regset *regset, - unsigned int pos, unsigned int count, void *kbuf, void __user *ubuf) + struct membuf to) { - return gpr32_get_common(target, regset, pos, count, kbuf, ubuf, + gpr32_get_common(target, regset, to, &target->thread.ckpt_regs.gpr[0]); + return membuf_zero(&to, ELF_NGREG * sizeof(u32)); } int tm_cgpr32_set(struct task_struct *target, const struct user_regset *regset, diff --git a/arch/powerpc/kernel/ptrace/ptrace-view.c b/arch/powerpc/kernel/ptrace/ptrace-view.c index caeb5822a8f4a..13208a9a02ca4 100644 --- a/arch/powerpc/kernel/ptrace/ptrace-view.c +++ b/arch/powerpc/kernel/ptrace/ptrace-view.c @@ -215,9 +215,9 @@ int ptrace_put_reg(struct task_struct *task, int regno, unsigned long data) } static int gpr_get(struct task_struct *target, const struct user_regset *regset, - unsigned int pos, unsigned int count, void *kbuf, void __user *ubuf) + struct membuf to) { - int i, ret; + int i; if (target->thread.regs == NULL) return -EIO; @@ -228,30 +228,17 @@ static int gpr_get(struct task_struct *target, const struct user_regset *regset, target->thread.regs->gpr[i] = NV_REG_POISON; } - ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, - target->thread.regs, - 0, offsetof(struct pt_regs, msr)); - if (!ret) { - unsigned long msr = get_user_msr(target); - ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, &msr, - offsetof(struct pt_regs, msr), - offsetof(struct pt_regs, msr) + - sizeof(msr)); - } + membuf_write(&to, target->thread.regs, offsetof(struct pt_regs, msr)); + membuf_store(&to, get_user_msr(target)); BUILD_BUG_ON(offsetof(struct pt_regs, orig_gpr3) != offsetof(struct pt_regs, msr) + sizeof(long)); - if (!ret) - ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, - &target->thread.regs->orig_gpr3, - offsetof(struct pt_regs, orig_gpr3), - sizeof(struct user_pt_regs)); - if (!ret) - ret = user_regset_copyout_zero(&pos, &count, &kbuf, &ubuf, - sizeof(struct user_pt_regs), -1); - - return ret; + membuf_write(&to, &target->thread.regs->orig_gpr3, + sizeof(struct user_pt_regs) - + offsetof(struct pt_regs, orig_gpr3)); + return membuf_zero(&to, ELF_NGREG * sizeof(unsigned long) - + sizeof(struct user_pt_regs)); } static int gpr_set(struct task_struct *target, const struct user_regset *regset, @@ -309,10 +296,9 @@ static int gpr_set(struct task_struct *target, const struct user_regset *regset, #ifdef CONFIG_PPC64 static int ppr_get(struct task_struct *target, const struct user_regset *regset, - unsigned int pos, unsigned int count, void *kbuf, void __user *ubuf) + struct membuf to) { - return user_regset_copyout(&pos, &count, &kbuf, &ubuf, - &target->thread.regs->ppr, 0, sizeof(u64)); + return membuf_write(&to, &target->thread.regs->ppr, sizeof(u64)); } static int ppr_set(struct task_struct *target, const struct user_regset *regset, @@ -324,10 +310,9 @@ static int ppr_set(struct task_struct *target, const struct user_regset *regset, } static int dscr_get(struct task_struct *target, const struct user_regset *regset, - unsigned int pos, unsigned int count, void *kbuf, void __user *ubuf) + struct membuf to) { - return user_regset_copyout(&pos, &count, &kbuf, &ubuf, - &target->thread.dscr, 0, sizeof(u64)); + return membuf_write(&to, &target->thread.dscr, sizeof(u64)); } static int dscr_set(struct task_struct *target, const struct user_regset *regset, unsigned int pos, unsigned int count, const void *kbuf, @@ -339,10 +324,9 @@ static int dscr_set(struct task_struct *target, const struct user_regset *regset #endif #ifdef CONFIG_PPC_BOOK3S_64 static int tar_get(struct task_struct *target, const struct user_regset *regset, - unsigned int pos, unsigned int count, void *kbuf, void __user *ubuf) + struct membuf to) { - return user_regset_copyout(&pos, &count, &kbuf, &ubuf, - &target->thread.tar, 0, sizeof(u64)); + return membuf_write(&to, &target->thread.tar, sizeof(u64)); } static int tar_set(struct task_struct *target, const struct user_regset *regset, unsigned int pos, unsigned int count, const void *kbuf, @@ -364,7 +348,7 @@ static int ebb_active(struct task_struct *target, const struct user_regset *regs } static int ebb_get(struct task_struct *target, const struct user_regset *regset, - unsigned int pos, unsigned int count, void *kbuf, void __user *ubuf) + struct membuf to) { /* Build tests */ BUILD_BUG_ON(TSO(ebbrr) + sizeof(unsigned long) != TSO(ebbhr)); @@ -376,8 +360,7 @@ static int ebb_get(struct task_struct *target, const struct user_regset *regset, if (!target->thread.used_ebb) return -ENODATA; - return user_regset_copyout(&pos, &count, &kbuf, &ubuf, &target->thread.ebbrr, - 0, 3 * sizeof(unsigned long)); + return membuf_write(&to, &target->thread.ebbrr, 3 * sizeof(unsigned long)); } static int ebb_set(struct task_struct *target, const struct user_regset *regset, @@ -420,7 +403,7 @@ static int pmu_active(struct task_struct *target, const struct user_regset *regs } static int pmu_get(struct task_struct *target, const struct user_regset *regset, - unsigned int pos, unsigned int count, void *kbuf, void __user *ubuf) + struct membuf to) { /* Build tests */ BUILD_BUG_ON(TSO(siar) + sizeof(unsigned long) != TSO(sdar)); @@ -431,8 +414,7 @@ static int pmu_get(struct task_struct *target, const struct user_regset *regset, if (!cpu_has_feature(CPU_FTR_ARCH_207S)) return -ENODEV; - return user_regset_copyout(&pos, &count, &kbuf, &ubuf, &target->thread.siar, - 0, 5 * sizeof(unsigned long)); + return membuf_write(&to, &target->thread.siar, 5 * sizeof(unsigned long)); } static int pmu_set(struct task_struct *target, const struct user_regset *regset, @@ -486,7 +468,7 @@ static int pkey_active(struct task_struct *target, const struct user_regset *reg } static int pkey_get(struct task_struct *target, const struct user_regset *regset, - unsigned int pos, unsigned int count, void *kbuf, void __user *ubuf) + struct membuf to) { BUILD_BUG_ON(TSO(amr) + sizeof(unsigned long) != TSO(iamr)); BUILD_BUG_ON(TSO(iamr) + sizeof(unsigned long) != TSO(uamor)); @@ -494,8 +476,7 @@ static int pkey_get(struct task_struct *target, const struct user_regset *regset if (!arch_pkeys_enabled()) return -ENODEV; - return user_regset_copyout(&pos, &count, &kbuf, &ubuf, &target->thread.amr, - 0, ELF_NPKEY * sizeof(unsigned long)); + return membuf_write(&to, &target->thread.amr, ELF_NPKEY * sizeof(unsigned long)); } static int pkey_set(struct task_struct *target, const struct user_regset *regset, @@ -529,110 +510,110 @@ static const struct user_regset native_regsets[] = { [REGSET_GPR] = { .core_note_type = NT_PRSTATUS, .n = ELF_NGREG, .size = sizeof(long), .align = sizeof(long), - .get = gpr_get, .set = gpr_set + .regset_get = gpr_get, .set = gpr_set }, [REGSET_FPR] = { .core_note_type = NT_PRFPREG, .n = ELF_NFPREG, .size = sizeof(double), .align = sizeof(double), - .get = fpr_get, .set = fpr_set + .regset_get = fpr_get, .set = fpr_set }, #ifdef CONFIG_ALTIVEC [REGSET_VMX] = { .core_note_type = NT_PPC_VMX, .n = 34, .size = sizeof(vector128), .align = sizeof(vector128), - .active = vr_active, .get = vr_get, .set = vr_set + .active = vr_active, .regset_get = vr_get, .set = vr_set }, #endif #ifdef CONFIG_VSX [REGSET_VSX] = { .core_note_type = NT_PPC_VSX, .n = 32, .size = sizeof(double), .align = sizeof(double), - .active = vsr_active, .get = vsr_get, .set = vsr_set + .active = vsr_active, .regset_get = vsr_get, .set = vsr_set }, #endif #ifdef CONFIG_SPE [REGSET_SPE] = { .core_note_type = NT_PPC_SPE, .n = 35, .size = sizeof(u32), .align = sizeof(u32), - .active = evr_active, .get = evr_get, .set = evr_set + .active = evr_active, .regset_get = evr_get, .set = evr_set }, #endif #ifdef CONFIG_PPC_TRANSACTIONAL_MEM [REGSET_TM_CGPR] = { .core_note_type = NT_PPC_TM_CGPR, .n = ELF_NGREG, .size = sizeof(long), .align = sizeof(long), - .active = tm_cgpr_active, .get = tm_cgpr_get, .set = tm_cgpr_set + .active = tm_cgpr_active, .regset_get = tm_cgpr_get, .set = tm_cgpr_set }, [REGSET_TM_CFPR] = { .core_note_type = NT_PPC_TM_CFPR, .n = ELF_NFPREG, .size = sizeof(double), .align = sizeof(double), - .active = tm_cfpr_active, .get = tm_cfpr_get, .set = tm_cfpr_set + .active = tm_cfpr_active, .regset_get = tm_cfpr_get, .set = tm_cfpr_set }, [REGSET_TM_CVMX] = { .core_note_type = NT_PPC_TM_CVMX, .n = ELF_NVMX, .size = sizeof(vector128), .align = sizeof(vector128), - .active = tm_cvmx_active, .get = tm_cvmx_get, .set = tm_cvmx_set + .active = tm_cvmx_active, .regset_get = tm_cvmx_get, .set = tm_cvmx_set }, [REGSET_TM_CVSX] = { .core_note_type = NT_PPC_TM_CVSX, .n = ELF_NVSX, .size = sizeof(double), .align = sizeof(double), - .active = tm_cvsx_active, .get = tm_cvsx_get, .set = tm_cvsx_set + .active = tm_cvsx_active, .regset_get = tm_cvsx_get, .set = tm_cvsx_set }, [REGSET_TM_SPR] = { .core_note_type = NT_PPC_TM_SPR, .n = ELF_NTMSPRREG, .size = sizeof(u64), .align = sizeof(u64), - .active = tm_spr_active, .get = tm_spr_get, .set = tm_spr_set + .active = tm_spr_active, .regset_get = tm_spr_get, .set = tm_spr_set }, [REGSET_TM_CTAR] = { .core_note_type = NT_PPC_TM_CTAR, .n = 1, .size = sizeof(u64), .align = sizeof(u64), - .active = tm_tar_active, .get = tm_tar_get, .set = tm_tar_set + .active = tm_tar_active, .regset_get = tm_tar_get, .set = tm_tar_set }, [REGSET_TM_CPPR] = { .core_note_type = NT_PPC_TM_CPPR, .n = 1, .size = sizeof(u64), .align = sizeof(u64), - .active = tm_ppr_active, .get = tm_ppr_get, .set = tm_ppr_set + .active = tm_ppr_active, .regset_get = tm_ppr_get, .set = tm_ppr_set }, [REGSET_TM_CDSCR] = { .core_note_type = NT_PPC_TM_CDSCR, .n = 1, .size = sizeof(u64), .align = sizeof(u64), - .active = tm_dscr_active, .get = tm_dscr_get, .set = tm_dscr_set + .active = tm_dscr_active, .regset_get = tm_dscr_get, .set = tm_dscr_set }, #endif #ifdef CONFIG_PPC64 [REGSET_PPR] = { .core_note_type = NT_PPC_PPR, .n = 1, .size = sizeof(u64), .align = sizeof(u64), - .get = ppr_get, .set = ppr_set + .regset_get = ppr_get, .set = ppr_set }, [REGSET_DSCR] = { .core_note_type = NT_PPC_DSCR, .n = 1, .size = sizeof(u64), .align = sizeof(u64), - .get = dscr_get, .set = dscr_set + .regset_get = dscr_get, .set = dscr_set }, #endif #ifdef CONFIG_PPC_BOOK3S_64 [REGSET_TAR] = { .core_note_type = NT_PPC_TAR, .n = 1, .size = sizeof(u64), .align = sizeof(u64), - .get = tar_get, .set = tar_set + .regset_get = tar_get, .set = tar_set }, [REGSET_EBB] = { .core_note_type = NT_PPC_EBB, .n = ELF_NEBB, .size = sizeof(u64), .align = sizeof(u64), - .active = ebb_active, .get = ebb_get, .set = ebb_set + .active = ebb_active, .regset_get = ebb_get, .set = ebb_set }, [REGSET_PMR] = { .core_note_type = NT_PPC_PMU, .n = ELF_NPMU, .size = sizeof(u64), .align = sizeof(u64), - .active = pmu_active, .get = pmu_get, .set = pmu_set + .active = pmu_active, .regset_get = pmu_get, .set = pmu_set }, #endif #ifdef CONFIG_PPC_MEM_KEYS [REGSET_PKEY] = { .core_note_type = NT_PPC_PKEY, .n = ELF_NPKEY, .size = sizeof(u64), .align = sizeof(u64), - .active = pkey_active, .get = pkey_get, .set = pkey_set + .active = pkey_active, .regset_get = pkey_get, .set = pkey_set }, #endif }; @@ -646,49 +627,16 @@ const struct user_regset_view user_ppc_native_view = { int gpr32_get_common(struct task_struct *target, const struct user_regset *regset, - unsigned int pos, unsigned int count, - void *kbuf, void __user *ubuf, - unsigned long *regs) + struct membuf to, unsigned long *regs) { - compat_ulong_t *k = kbuf; - compat_ulong_t __user *u = ubuf; - compat_ulong_t reg; - - pos /= sizeof(reg); - count /= sizeof(reg); - - if (kbuf) - for (; count > 0 && pos < PT_MSR; --count) - *k++ = regs[pos++]; - else - for (; count > 0 && pos < PT_MSR; --count) - if (__put_user((compat_ulong_t)regs[pos++], u++)) - return -EFAULT; - - if (count > 0 && pos == PT_MSR) { - reg = get_user_msr(target); - if (kbuf) - *k++ = reg; - else if (__put_user(reg, u++)) - return -EFAULT; - ++pos; - --count; - } - - if (kbuf) - for (; count > 0 && pos < PT_REGS_COUNT; --count) - *k++ = regs[pos++]; - else - for (; count > 0 && pos < PT_REGS_COUNT; --count) - if (__put_user((compat_ulong_t)regs[pos++], u++)) - return -EFAULT; + int i; - kbuf = k; - ubuf = u; - pos *= sizeof(reg); - count *= sizeof(reg); - return user_regset_copyout_zero(&pos, &count, &kbuf, &ubuf, - PT_REGS_COUNT * sizeof(reg), -1); + for (i = 0; i < PT_MSR; i++) + membuf_store(&to, (u32)regs[i]); + membuf_store(&to, (u32)get_user_msr(target)); + for (i++ ; i < PT_REGS_COUNT; i++) + membuf_store(&to, (u32)regs[i]); + return membuf_zero(&to, (ELF_NGREG - PT_REGS_COUNT) * sizeof(u32)); } int gpr32_set_common(struct task_struct *target, @@ -761,8 +709,7 @@ int gpr32_set_common(struct task_struct *target, static int gpr32_get(struct task_struct *target, const struct user_regset *regset, - unsigned int pos, unsigned int count, - void *kbuf, void __user *ubuf) + struct membuf to) { int i; @@ -777,7 +724,7 @@ static int gpr32_get(struct task_struct *target, for (i = 14; i < 32; i++) target->thread.regs->gpr[i] = NV_REG_POISON; } - return gpr32_get_common(target, regset, pos, count, kbuf, ubuf, + return gpr32_get_common(target, regset, to, &target->thread.regs->gpr[0]); } @@ -801,25 +748,25 @@ static const struct user_regset compat_regsets[] = { [REGSET_GPR] = { .core_note_type = NT_PRSTATUS, .n = ELF_NGREG, .size = sizeof(compat_long_t), .align = sizeof(compat_long_t), - .get = gpr32_get, .set = gpr32_set + .regset_get = gpr32_get, .set = gpr32_set }, [REGSET_FPR] = { .core_note_type = NT_PRFPREG, .n = ELF_NFPREG, .size = sizeof(double), .align = sizeof(double), - .get = fpr_get, .set = fpr_set + .regset_get = fpr_get, .set = fpr_set }, #ifdef CONFIG_ALTIVEC [REGSET_VMX] = { .core_note_type = NT_PPC_VMX, .n = 34, .size = sizeof(vector128), .align = sizeof(vector128), - .active = vr_active, .get = vr_get, .set = vr_set + .active = vr_active, .regset_get = vr_get, .set = vr_set }, #endif #ifdef CONFIG_SPE [REGSET_SPE] = { .core_note_type = NT_PPC_SPE, .n = 35, .size = sizeof(u32), .align = sizeof(u32), - .active = evr_active, .get = evr_get, .set = evr_set + .active = evr_active, .regset_get = evr_get, .set = evr_set }, #endif #ifdef CONFIG_PPC_TRANSACTIONAL_MEM @@ -827,66 +774,66 @@ static const struct user_regset compat_regsets[] = { .core_note_type = NT_PPC_TM_CGPR, .n = ELF_NGREG, .size = sizeof(long), .align = sizeof(long), .active = tm_cgpr_active, - .get = tm_cgpr32_get, .set = tm_cgpr32_set + .regset_get = tm_cgpr32_get, .set = tm_cgpr32_set }, [REGSET_TM_CFPR] = { .core_note_type = NT_PPC_TM_CFPR, .n = ELF_NFPREG, .size = sizeof(double), .align = sizeof(double), - .active = tm_cfpr_active, .get = tm_cfpr_get, .set = tm_cfpr_set + .active = tm_cfpr_active, .regset_get = tm_cfpr_get, .set = tm_cfpr_set }, [REGSET_TM_CVMX] = { .core_note_type = NT_PPC_TM_CVMX, .n = ELF_NVMX, .size = sizeof(vector128), .align = sizeof(vector128), - .active = tm_cvmx_active, .get = tm_cvmx_get, .set = tm_cvmx_set + .active = tm_cvmx_active, .regset_get = tm_cvmx_get, .set = tm_cvmx_set }, [REGSET_TM_CVSX] = { .core_note_type = NT_PPC_TM_CVSX, .n = ELF_NVSX, .size = sizeof(double), .align = sizeof(double), - .active = tm_cvsx_active, .get = tm_cvsx_get, .set = tm_cvsx_set + .active = tm_cvsx_active, .regset_get = tm_cvsx_get, .set = tm_cvsx_set }, [REGSET_TM_SPR] = { .core_note_type = NT_PPC_TM_SPR, .n = ELF_NTMSPRREG, .size = sizeof(u64), .align = sizeof(u64), - .active = tm_spr_active, .get = tm_spr_get, .set = tm_spr_set + .active = tm_spr_active, .regset_get = tm_spr_get, .set = tm_spr_set }, [REGSET_TM_CTAR] = { .core_note_type = NT_PPC_TM_CTAR, .n = 1, .size = sizeof(u64), .align = sizeof(u64), - .active = tm_tar_active, .get = tm_tar_get, .set = tm_tar_set + .active = tm_tar_active, .regset_get = tm_tar_get, .set = tm_tar_set }, [REGSET_TM_CPPR] = { .core_note_type = NT_PPC_TM_CPPR, .n = 1, .size = sizeof(u64), .align = sizeof(u64), - .active = tm_ppr_active, .get = tm_ppr_get, .set = tm_ppr_set + .active = tm_ppr_active, .regset_get = tm_ppr_get, .set = tm_ppr_set }, [REGSET_TM_CDSCR] = { .core_note_type = NT_PPC_TM_CDSCR, .n = 1, .size = sizeof(u64), .align = sizeof(u64), - .active = tm_dscr_active, .get = tm_dscr_get, .set = tm_dscr_set + .active = tm_dscr_active, .regset_get = tm_dscr_get, .set = tm_dscr_set }, #endif #ifdef CONFIG_PPC64 [REGSET_PPR] = { .core_note_type = NT_PPC_PPR, .n = 1, .size = sizeof(u64), .align = sizeof(u64), - .get = ppr_get, .set = ppr_set + .regset_get = ppr_get, .set = ppr_set }, [REGSET_DSCR] = { .core_note_type = NT_PPC_DSCR, .n = 1, .size = sizeof(u64), .align = sizeof(u64), - .get = dscr_get, .set = dscr_set + .regset_get = dscr_get, .set = dscr_set }, #endif #ifdef CONFIG_PPC_BOOK3S_64 [REGSET_TAR] = { .core_note_type = NT_PPC_TAR, .n = 1, .size = sizeof(u64), .align = sizeof(u64), - .get = tar_get, .set = tar_set + .regset_get = tar_get, .set = tar_set }, [REGSET_EBB] = { .core_note_type = NT_PPC_EBB, .n = ELF_NEBB, .size = sizeof(u64), .align = sizeof(u64), - .active = ebb_active, .get = ebb_get, .set = ebb_set + .active = ebb_active, .regset_get = ebb_get, .set = ebb_set }, #endif }; diff --git a/arch/powerpc/kernel/ptrace/ptrace-vsx.c b/arch/powerpc/kernel/ptrace/ptrace-vsx.c index d53466d49cc0e..1da4303128ef8 100644 --- a/arch/powerpc/kernel/ptrace/ptrace-vsx.c +++ b/arch/powerpc/kernel/ptrace/ptrace-vsx.c @@ -19,7 +19,7 @@ * }; */ int fpr_get(struct task_struct *target, const struct user_regset *regset, - unsigned int pos, unsigned int count, void *kbuf, void __user *ubuf) + struct membuf to) { u64 buf[33]; int i; @@ -30,7 +30,7 @@ int fpr_get(struct task_struct *target, const struct user_regset *regset, for (i = 0; i < 32 ; i++) buf[i] = target->thread.TS_FPR(i); buf[32] = target->thread.fp_state.fpscr; - return user_regset_copyout(&pos, &count, &kbuf, &ubuf, buf, 0, -1); + return membuf_write(&to, buf, 33 * sizeof(u64)); } /* @@ -95,10 +95,10 @@ int vsr_active(struct task_struct *target, const struct user_regset *regset) * }; */ int vsr_get(struct task_struct *target, const struct user_regset *regset, - unsigned int pos, unsigned int count, void *kbuf, void __user *ubuf) + struct membuf to) { u64 buf[32]; - int ret, i; + int i; flush_tmregs_to_thread(target); flush_fp_to_thread(target); @@ -108,10 +108,7 @@ int vsr_get(struct task_struct *target, const struct user_regset *regset, for (i = 0; i < 32 ; i++) buf[i] = target->thread.fp_state.fpr[i][TS_VSRLOWOFFSET]; - ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, - buf, 0, 32 * sizeof(double)); - - return ret; + return membuf_write(&to, buf, 32 * sizeof(double)); } /* -- GitLab From b69c6320528a7def8b696fc4383b505588440787 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Mon, 11 May 2020 18:35:55 -0400 Subject: [PATCH 0957/1476] s390: switch to ->regset_get() NB: compat NT_S390_LAST_BREAK might be better as compat_long_t rather than long. User-visible ABI, again... Signed-off-by: Al Viro --- arch/s390/kernel/ptrace.c | 199 +++++++++++--------------------------- 1 file changed, 58 insertions(+), 141 deletions(-) diff --git a/arch/s390/kernel/ptrace.c b/arch/s390/kernel/ptrace.c index ce60a459a143c..367a6878bf6e2 100644 --- a/arch/s390/kernel/ptrace.c +++ b/arch/s390/kernel/ptrace.c @@ -890,28 +890,14 @@ asmlinkage void do_syscall_trace_exit(struct pt_regs *regs) static int s390_regs_get(struct task_struct *target, const struct user_regset *regset, - unsigned int pos, unsigned int count, - void *kbuf, void __user *ubuf) + struct membuf to) { + unsigned pos; if (target == current) save_access_regs(target->thread.acrs); - if (kbuf) { - unsigned long *k = kbuf; - while (count > 0) { - *k++ = __peek_user(target, pos); - count -= sizeof(*k); - pos += sizeof(*k); - } - } else { - unsigned long __user *u = ubuf; - while (count > 0) { - if (__put_user(__peek_user(target, pos), u++)) - return -EFAULT; - count -= sizeof(*u); - pos += sizeof(*u); - } - } + for (pos = 0; pos < sizeof(s390_regs); pos += sizeof(long)) + membuf_store(&to, __peek_user(target, pos)); return 0; } @@ -952,8 +938,8 @@ static int s390_regs_set(struct task_struct *target, } static int s390_fpregs_get(struct task_struct *target, - const struct user_regset *regset, unsigned int pos, - unsigned int count, void *kbuf, void __user *ubuf) + const struct user_regset *regset, + struct membuf to) { _s390_fp_regs fp_regs; @@ -963,8 +949,7 @@ static int s390_fpregs_get(struct task_struct *target, fp_regs.fpc = target->thread.fpu.fpc; fpregs_store(&fp_regs, &target->thread.fpu); - return user_regset_copyout(&pos, &count, &kbuf, &ubuf, - &fp_regs, 0, -1); + return membuf_write(&to, &fp_regs, sizeof(fp_regs)); } static int s390_fpregs_set(struct task_struct *target, @@ -1011,20 +996,9 @@ static int s390_fpregs_set(struct task_struct *target, static int s390_last_break_get(struct task_struct *target, const struct user_regset *regset, - unsigned int pos, unsigned int count, - void *kbuf, void __user *ubuf) + struct membuf to) { - if (count > 0) { - if (kbuf) { - unsigned long *k = kbuf; - *k = target->thread.last_break; - } else { - unsigned long __user *u = ubuf; - if (__put_user(target->thread.last_break, u)) - return -EFAULT; - } - } - return 0; + return membuf_store(&to, target->thread.last_break); } static int s390_last_break_set(struct task_struct *target, @@ -1037,16 +1011,13 @@ static int s390_last_break_set(struct task_struct *target, static int s390_tdb_get(struct task_struct *target, const struct user_regset *regset, - unsigned int pos, unsigned int count, - void *kbuf, void __user *ubuf) + struct membuf to) { struct pt_regs *regs = task_pt_regs(target); - unsigned char *data; if (!(regs->int_code & 0x200)) return -ENODATA; - data = target->thread.trap_tdb; - return user_regset_copyout(&pos, &count, &kbuf, &ubuf, data, 0, 256); + return membuf_write(&to, target->thread.trap_tdb, 256); } static int s390_tdb_set(struct task_struct *target, @@ -1059,8 +1030,7 @@ static int s390_tdb_set(struct task_struct *target, static int s390_vxrs_low_get(struct task_struct *target, const struct user_regset *regset, - unsigned int pos, unsigned int count, - void *kbuf, void __user *ubuf) + struct membuf to) { __u64 vxrs[__NUM_VXRS_LOW]; int i; @@ -1071,7 +1041,7 @@ static int s390_vxrs_low_get(struct task_struct *target, save_fpu_regs(); for (i = 0; i < __NUM_VXRS_LOW; i++) vxrs[i] = *((__u64 *)(target->thread.fpu.vxrs + i) + 1); - return user_regset_copyout(&pos, &count, &kbuf, &ubuf, vxrs, 0, -1); + return membuf_write(&to, vxrs, sizeof(vxrs)); } static int s390_vxrs_low_set(struct task_struct *target, @@ -1100,18 +1070,14 @@ static int s390_vxrs_low_set(struct task_struct *target, static int s390_vxrs_high_get(struct task_struct *target, const struct user_regset *regset, - unsigned int pos, unsigned int count, - void *kbuf, void __user *ubuf) + struct membuf to) { - __vector128 vxrs[__NUM_VXRS_HIGH]; - if (!MACHINE_HAS_VX) return -ENODEV; if (target == current) save_fpu_regs(); - memcpy(vxrs, target->thread.fpu.vxrs + __NUM_VXRS_LOW, sizeof(vxrs)); - - return user_regset_copyout(&pos, &count, &kbuf, &ubuf, vxrs, 0, -1); + return membuf_write(&to, target->thread.fpu.vxrs + __NUM_VXRS_LOW, + __NUM_VXRS_HIGH * sizeof(__vector128)); } static int s390_vxrs_high_set(struct task_struct *target, @@ -1133,12 +1099,9 @@ static int s390_vxrs_high_set(struct task_struct *target, static int s390_system_call_get(struct task_struct *target, const struct user_regset *regset, - unsigned int pos, unsigned int count, - void *kbuf, void __user *ubuf) + struct membuf to) { - unsigned int *data = &target->thread.system_call; - return user_regset_copyout(&pos, &count, &kbuf, &ubuf, - data, 0, sizeof(unsigned int)); + return membuf_store(&to, target->thread.system_call); } static int s390_system_call_set(struct task_struct *target, @@ -1153,8 +1116,7 @@ static int s390_system_call_set(struct task_struct *target, static int s390_gs_cb_get(struct task_struct *target, const struct user_regset *regset, - unsigned int pos, unsigned int count, - void *kbuf, void __user *ubuf) + struct membuf to) { struct gs_cb *data = target->thread.gs_cb; @@ -1164,8 +1126,7 @@ static int s390_gs_cb_get(struct task_struct *target, return -ENODATA; if (target == current) save_gs_cb(data); - return user_regset_copyout(&pos, &count, &kbuf, &ubuf, - data, 0, sizeof(struct gs_cb)); + return membuf_write(&to, data, sizeof(struct gs_cb)); } static int s390_gs_cb_set(struct task_struct *target, @@ -1209,8 +1170,7 @@ static int s390_gs_cb_set(struct task_struct *target, static int s390_gs_bc_get(struct task_struct *target, const struct user_regset *regset, - unsigned int pos, unsigned int count, - void *kbuf, void __user *ubuf) + struct membuf to) { struct gs_cb *data = target->thread.gs_bc_cb; @@ -1218,8 +1178,7 @@ static int s390_gs_bc_get(struct task_struct *target, return -ENODEV; if (!data) return -ENODATA; - return user_regset_copyout(&pos, &count, &kbuf, &ubuf, - data, 0, sizeof(struct gs_cb)); + return membuf_write(&to, data, sizeof(struct gs_cb)); } static int s390_gs_bc_set(struct task_struct *target, @@ -1270,8 +1229,7 @@ static bool is_ri_cb_valid(struct runtime_instr_cb *cb) static int s390_runtime_instr_get(struct task_struct *target, const struct user_regset *regset, - unsigned int pos, unsigned int count, - void *kbuf, void __user *ubuf) + struct membuf to) { struct runtime_instr_cb *data = target->thread.ri_cb; @@ -1280,8 +1238,7 @@ static int s390_runtime_instr_get(struct task_struct *target, if (!data) return -ENODATA; - return user_regset_copyout(&pos, &count, &kbuf, &ubuf, - data, 0, sizeof(struct runtime_instr_cb)); + return membuf_write(&to, data, sizeof(struct runtime_instr_cb)); } static int s390_runtime_instr_set(struct task_struct *target, @@ -1337,7 +1294,7 @@ static const struct user_regset s390_regsets[] = { .n = sizeof(s390_regs) / sizeof(long), .size = sizeof(long), .align = sizeof(long), - .get = s390_regs_get, + .regset_get = s390_regs_get, .set = s390_regs_set, }, { @@ -1345,7 +1302,7 @@ static const struct user_regset s390_regsets[] = { .n = sizeof(s390_fp_regs) / sizeof(long), .size = sizeof(long), .align = sizeof(long), - .get = s390_fpregs_get, + .regset_get = s390_fpregs_get, .set = s390_fpregs_set, }, { @@ -1353,7 +1310,7 @@ static const struct user_regset s390_regsets[] = { .n = 1, .size = sizeof(unsigned int), .align = sizeof(unsigned int), - .get = s390_system_call_get, + .regset_get = s390_system_call_get, .set = s390_system_call_set, }, { @@ -1361,7 +1318,7 @@ static const struct user_regset s390_regsets[] = { .n = 1, .size = sizeof(long), .align = sizeof(long), - .get = s390_last_break_get, + .regset_get = s390_last_break_get, .set = s390_last_break_set, }, { @@ -1369,7 +1326,7 @@ static const struct user_regset s390_regsets[] = { .n = 1, .size = 256, .align = 1, - .get = s390_tdb_get, + .regset_get = s390_tdb_get, .set = s390_tdb_set, }, { @@ -1377,7 +1334,7 @@ static const struct user_regset s390_regsets[] = { .n = __NUM_VXRS_LOW, .size = sizeof(__u64), .align = sizeof(__u64), - .get = s390_vxrs_low_get, + .regset_get = s390_vxrs_low_get, .set = s390_vxrs_low_set, }, { @@ -1385,7 +1342,7 @@ static const struct user_regset s390_regsets[] = { .n = __NUM_VXRS_HIGH, .size = sizeof(__vector128), .align = sizeof(__vector128), - .get = s390_vxrs_high_get, + .regset_get = s390_vxrs_high_get, .set = s390_vxrs_high_set, }, { @@ -1393,7 +1350,7 @@ static const struct user_regset s390_regsets[] = { .n = sizeof(struct gs_cb) / sizeof(__u64), .size = sizeof(__u64), .align = sizeof(__u64), - .get = s390_gs_cb_get, + .regset_get = s390_gs_cb_get, .set = s390_gs_cb_set, }, { @@ -1401,7 +1358,7 @@ static const struct user_regset s390_regsets[] = { .n = sizeof(struct gs_cb) / sizeof(__u64), .size = sizeof(__u64), .align = sizeof(__u64), - .get = s390_gs_bc_get, + .regset_get = s390_gs_bc_get, .set = s390_gs_bc_set, }, { @@ -1409,7 +1366,7 @@ static const struct user_regset s390_regsets[] = { .n = sizeof(struct runtime_instr_cb) / sizeof(__u64), .size = sizeof(__u64), .align = sizeof(__u64), - .get = s390_runtime_instr_get, + .regset_get = s390_runtime_instr_get, .set = s390_runtime_instr_set, }, }; @@ -1424,28 +1381,15 @@ static const struct user_regset_view user_s390_view = { #ifdef CONFIG_COMPAT static int s390_compat_regs_get(struct task_struct *target, const struct user_regset *regset, - unsigned int pos, unsigned int count, - void *kbuf, void __user *ubuf) + struct membuf to) { + unsigned n; + if (target == current) save_access_regs(target->thread.acrs); - if (kbuf) { - compat_ulong_t *k = kbuf; - while (count > 0) { - *k++ = __peek_user_compat(target, pos); - count -= sizeof(*k); - pos += sizeof(*k); - } - } else { - compat_ulong_t __user *u = ubuf; - while (count > 0) { - if (__put_user(__peek_user_compat(target, pos), u++)) - return -EFAULT; - count -= sizeof(*u); - pos += sizeof(*u); - } - } + for (n = 0; n < sizeof(s390_compat_regs); n += sizeof(compat_ulong_t)) + membuf_store(&to, __peek_user_compat(target, n)); return 0; } @@ -1487,29 +1431,14 @@ static int s390_compat_regs_set(struct task_struct *target, static int s390_compat_regs_high_get(struct task_struct *target, const struct user_regset *regset, - unsigned int pos, unsigned int count, - void *kbuf, void __user *ubuf) + struct membuf to) { compat_ulong_t *gprs_high; + int i; - gprs_high = (compat_ulong_t *) - &task_pt_regs(target)->gprs[pos / sizeof(compat_ulong_t)]; - if (kbuf) { - compat_ulong_t *k = kbuf; - while (count > 0) { - *k++ = *gprs_high; - gprs_high += 2; - count -= sizeof(*k); - } - } else { - compat_ulong_t __user *u = ubuf; - while (count > 0) { - if (__put_user(*gprs_high, u++)) - return -EFAULT; - gprs_high += 2; - count -= sizeof(*u); - } - } + gprs_high = (compat_ulong_t *)task_pt_regs(target)->gprs; + for (i = 0; i < NUM_GPRS; i++, gprs_high += 2) + membuf_store(&to, *gprs_high); return 0; } @@ -1548,23 +1477,11 @@ static int s390_compat_regs_high_set(struct task_struct *target, static int s390_compat_last_break_get(struct task_struct *target, const struct user_regset *regset, - unsigned int pos, unsigned int count, - void *kbuf, void __user *ubuf) + struct membuf to) { - compat_ulong_t last_break; + compat_ulong_t last_break = target->thread.last_break; - if (count > 0) { - last_break = target->thread.last_break; - if (kbuf) { - unsigned long *k = kbuf; - *k = last_break; - } else { - unsigned long __user *u = ubuf; - if (__put_user(last_break, u)) - return -EFAULT; - } - } - return 0; + return membuf_store(&to, (unsigned long)last_break); } static int s390_compat_last_break_set(struct task_struct *target, @@ -1581,7 +1498,7 @@ static const struct user_regset s390_compat_regsets[] = { .n = sizeof(s390_compat_regs) / sizeof(compat_long_t), .size = sizeof(compat_long_t), .align = sizeof(compat_long_t), - .get = s390_compat_regs_get, + .regset_get = s390_compat_regs_get, .set = s390_compat_regs_set, }, { @@ -1589,7 +1506,7 @@ static const struct user_regset s390_compat_regsets[] = { .n = sizeof(s390_fp_regs) / sizeof(compat_long_t), .size = sizeof(compat_long_t), .align = sizeof(compat_long_t), - .get = s390_fpregs_get, + .regset_get = s390_fpregs_get, .set = s390_fpregs_set, }, { @@ -1597,7 +1514,7 @@ static const struct user_regset s390_compat_regsets[] = { .n = 1, .size = sizeof(compat_uint_t), .align = sizeof(compat_uint_t), - .get = s390_system_call_get, + .regset_get = s390_system_call_get, .set = s390_system_call_set, }, { @@ -1605,7 +1522,7 @@ static const struct user_regset s390_compat_regsets[] = { .n = 1, .size = sizeof(long), .align = sizeof(long), - .get = s390_compat_last_break_get, + .regset_get = s390_compat_last_break_get, .set = s390_compat_last_break_set, }, { @@ -1613,7 +1530,7 @@ static const struct user_regset s390_compat_regsets[] = { .n = 1, .size = 256, .align = 1, - .get = s390_tdb_get, + .regset_get = s390_tdb_get, .set = s390_tdb_set, }, { @@ -1621,7 +1538,7 @@ static const struct user_regset s390_compat_regsets[] = { .n = __NUM_VXRS_LOW, .size = sizeof(__u64), .align = sizeof(__u64), - .get = s390_vxrs_low_get, + .regset_get = s390_vxrs_low_get, .set = s390_vxrs_low_set, }, { @@ -1629,7 +1546,7 @@ static const struct user_regset s390_compat_regsets[] = { .n = __NUM_VXRS_HIGH, .size = sizeof(__vector128), .align = sizeof(__vector128), - .get = s390_vxrs_high_get, + .regset_get = s390_vxrs_high_get, .set = s390_vxrs_high_set, }, { @@ -1637,7 +1554,7 @@ static const struct user_regset s390_compat_regsets[] = { .n = sizeof(s390_compat_regs_high) / sizeof(compat_long_t), .size = sizeof(compat_long_t), .align = sizeof(compat_long_t), - .get = s390_compat_regs_high_get, + .regset_get = s390_compat_regs_high_get, .set = s390_compat_regs_high_set, }, { @@ -1645,7 +1562,7 @@ static const struct user_regset s390_compat_regsets[] = { .n = sizeof(struct gs_cb) / sizeof(__u64), .size = sizeof(__u64), .align = sizeof(__u64), - .get = s390_gs_cb_get, + .regset_get = s390_gs_cb_get, .set = s390_gs_cb_set, }, { @@ -1653,7 +1570,7 @@ static const struct user_regset s390_compat_regsets[] = { .n = sizeof(struct gs_cb) / sizeof(__u64), .size = sizeof(__u64), .align = sizeof(__u64), - .get = s390_gs_bc_get, + .regset_get = s390_gs_bc_get, .set = s390_gs_bc_set, }, { @@ -1661,7 +1578,7 @@ static const struct user_regset s390_compat_regsets[] = { .n = sizeof(struct runtime_instr_cb) / sizeof(__u64), .size = sizeof(__u64), .align = sizeof(__u64), - .get = s390_runtime_instr_get, + .regset_get = s390_runtime_instr_get, .set = s390_runtime_instr_set, }, }; -- GitLab From 4d617aaae4e81067777c95978e1bce4f8363d06a Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sat, 22 Feb 2020 00:19:46 -0500 Subject: [PATCH 0958/1476] sparc: switch to ->regset_get() Signed-off-by: Al Viro --- arch/sparc/kernel/ptrace_32.c | 127 +++--------- arch/sparc/kernel/ptrace_64.c | 352 ++++++++-------------------------- 2 files changed, 110 insertions(+), 369 deletions(-) diff --git a/arch/sparc/kernel/ptrace_32.c b/arch/sparc/kernel/ptrace_32.c index 144e5a6d16aad..5318174a0268c 100644 --- a/arch/sparc/kernel/ptrace_32.c +++ b/arch/sparc/kernel/ptrace_32.c @@ -83,39 +83,25 @@ static int regwindow32_set(struct task_struct *target, static int genregs32_get(struct task_struct *target, const struct user_regset *regset, - unsigned int pos, unsigned int count, - void *kbuf, void __user *ubuf) + struct membuf to) { const struct pt_regs *regs = target->thread.kregs; u32 uregs[16]; - int ret; if (target == current) flush_user_windows(); - ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, - regs->u_regs, - 0, 16 * sizeof(u32)); - if (ret || !count) - return ret; - + membuf_write(&to, regs->u_regs, 16 * sizeof(u32)); + if (!to.left) + return 0; if (regwindow32_get(target, regs, uregs)) return -EFAULT; - ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, - uregs, - 16 * sizeof(u32), 32 * sizeof(u32)); - if (ret) - return ret; - - uregs[0] = regs->psr; - uregs[1] = regs->pc; - uregs[2] = regs->npc; - uregs[3] = regs->y; - uregs[4] = 0; /* WIM */ - uregs[5] = 0; /* TBR */ - return user_regset_copyout(&pos, &count, &kbuf, &ubuf, - uregs, - 32 * sizeof(u32), 38 * sizeof(u32)); + membuf_write(&to, uregs, 16 * sizeof(u32)); + membuf_store(&to, regs->psr); + membuf_store(&to, regs->pc); + membuf_store(&to, regs->npc); + membuf_store(&to, regs->y); + return membuf_zero(&to, 2 * sizeof(u32)); } static int genregs32_set(struct task_struct *target, @@ -179,46 +165,18 @@ static int genregs32_set(struct task_struct *target, static int fpregs32_get(struct task_struct *target, const struct user_regset *regset, - unsigned int pos, unsigned int count, - void *kbuf, void __user *ubuf) + struct membuf to) { - const unsigned long *fpregs = target->thread.float_regs; - int ret = 0; - #if 0 if (target == current) save_and_clear_fpu(); #endif - ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, - fpregs, - 0, 32 * sizeof(u32)); - - if (!ret) - ret = user_regset_copyout_zero(&pos, &count, &kbuf, &ubuf, - 32 * sizeof(u32), - 33 * sizeof(u32)); - if (!ret) - ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, - &target->thread.fsr, - 33 * sizeof(u32), - 34 * sizeof(u32)); - - if (!ret) { - unsigned long val; - - val = (1 << 8) | (8 << 16); - ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, - &val, - 34 * sizeof(u32), - 35 * sizeof(u32)); - } - - if (!ret) - ret = user_regset_copyout_zero(&pos, &count, &kbuf, &ubuf, - 35 * sizeof(u32), -1); - - return ret; + membuf_write(&to, target->thread.float_regs, 32 * sizeof(u32)); + membuf_zero(&to, sizeof(u32)); + membuf_write(&to, &target->thread.fsr, sizeof(u32)); + membuf_store(&to, (u32)((1 << 8) | (8 << 16))); + return membuf_zero(&to, 64 * sizeof(u32)); } static int fpregs32_set(struct task_struct *target, @@ -263,7 +221,7 @@ static const struct user_regset sparc32_regsets[] = { .core_note_type = NT_PRSTATUS, .n = 38, .size = sizeof(u32), .align = sizeof(u32), - .get = genregs32_get, .set = genregs32_set + .regset_get = genregs32_get, .set = genregs32_set }, /* Format is: * F0 --> F31 @@ -279,35 +237,24 @@ static const struct user_regset sparc32_regsets[] = { .core_note_type = NT_PRFPREG, .n = 99, .size = sizeof(u32), .align = sizeof(u32), - .get = fpregs32_get, .set = fpregs32_set + .regset_get = fpregs32_get, .set = fpregs32_set }, }; static int getregs_get(struct task_struct *target, const struct user_regset *regset, - unsigned int pos, unsigned int count, - void *kbuf, void __user *ubuf) + struct membuf to) { const struct pt_regs *regs = target->thread.kregs; - u32 v[4]; - int ret; if (target == current) flush_user_windows(); - v[0] = regs->psr; - v[1] = regs->pc; - v[2] = regs->npc; - v[3] = regs->y; - ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, - v, - 0 * sizeof(u32), 4 * sizeof(u32)); - if (ret) - return ret; - - return user_regset_copyout(&pos, &count, &kbuf, &ubuf, - regs->u_regs + 1, - 4 * sizeof(u32), 19 * sizeof(u32)); + membuf_store(&to, regs->psr); + membuf_store(&to, regs->pc); + membuf_store(&to, regs->npc); + membuf_store(&to, regs->y); + return membuf_write(&to, regs->u_regs + 1, 15 * sizeof(u32)); } static int setregs_set(struct task_struct *target, @@ -339,29 +286,15 @@ static int setregs_set(struct task_struct *target, static int getfpregs_get(struct task_struct *target, const struct user_regset *regset, - unsigned int pos, unsigned int count, - void *kbuf, void __user *ubuf) + struct membuf to) { - const unsigned long *fpregs = target->thread.float_regs; - int ret = 0; - #if 0 if (target == current) save_and_clear_fpu(); #endif - - ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, - fpregs, - 0, 32 * sizeof(u32)); - if (ret) - return ret; - ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, - &target->thread.fsr, - 32 * sizeof(u32), 33 * sizeof(u32)); - if (ret) - return ret; - return user_regset_copyout_zero(&pos, &count, &kbuf, &ubuf, - 33 * sizeof(u32), 68 * sizeof(u32)); + membuf_write(&to, &target->thread.float_regs, 32 * sizeof(u32)); + membuf_write(&to, &target->thread.fsr, sizeof(u32)); + return membuf_zero(&to, 35 * sizeof(u32)); } static int setfpregs_set(struct task_struct *target, @@ -390,11 +323,11 @@ static int setfpregs_set(struct task_struct *target, static const struct user_regset ptrace32_regsets[] = { [REGSET_GENERAL] = { .n = 19, .size = sizeof(u32), - .get = getregs_get, .set = setregs_set, + .regset_get = getregs_get, .set = setregs_set, }, [REGSET_FP] = { .n = 68, .size = sizeof(u32), - .get = getfpregs_get, .set = setfpregs_set, + .regset_get = getfpregs_get, .set = setfpregs_set, }, }; diff --git a/arch/sparc/kernel/ptrace_64.c b/arch/sparc/kernel/ptrace_64.c index 3c9eee12102ad..2b92155db8a5f 100644 --- a/arch/sparc/kernel/ptrace_64.c +++ b/arch/sparc/kernel/ptrace_64.c @@ -246,52 +246,23 @@ enum sparc_regset { static int genregs64_get(struct task_struct *target, const struct user_regset *regset, - unsigned int pos, unsigned int count, - void *kbuf, void __user *ubuf) + struct membuf to) { const struct pt_regs *regs = task_pt_regs(target); - int ret; + struct reg_window window; if (target == current) flushw_user(); - ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, - regs->u_regs, - 0, 16 * sizeof(u64)); - if (!ret && count) { - struct reg_window window; - - if (regwindow64_get(target, regs, &window)) - return -EFAULT; - ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, - &window, - 16 * sizeof(u64), - 32 * sizeof(u64)); - } - - if (!ret) { - /* TSTATE, TPC, TNPC */ - ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, - ®s->tstate, - 32 * sizeof(u64), - 35 * sizeof(u64)); - } - - if (!ret) { - unsigned long y = regs->y; - - ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, - &y, - 35 * sizeof(u64), - 36 * sizeof(u64)); - } - - if (!ret) { - ret = user_regset_copyout_zero(&pos, &count, &kbuf, &ubuf, - 36 * sizeof(u64), -1); - - } - return ret; + membuf_write(&to, regs->u_regs, 16 * sizeof(u64)); + if (!to.left) + return 0; + if (regwindow64_get(target, regs, &window)) + return -EFAULT; + membuf_write(&to, &window, 16 * sizeof(u64)); + /* TSTATE, TPC, TNPC */ + membuf_write(&to, ®s->tstate, 3 * sizeof(u64)); + return membuf_store(&to, (u64)regs->y); } static int genregs64_set(struct task_struct *target, @@ -370,69 +341,32 @@ static int genregs64_set(struct task_struct *target, static int fpregs64_get(struct task_struct *target, const struct user_regset *regset, - unsigned int pos, unsigned int count, - void *kbuf, void __user *ubuf) + struct membuf to) { - const unsigned long *fpregs = task_thread_info(target)->fpregs; - unsigned long fprs, fsr, gsr; - int ret; + struct thread_info *t = task_thread_info(target); + unsigned long fprs; if (target == current) save_and_clear_fpu(); - fprs = task_thread_info(target)->fpsaved[0]; + fprs = t->fpsaved[0]; if (fprs & FPRS_DL) - ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, - fpregs, - 0, 16 * sizeof(u64)); + membuf_write(&to, t->fpregs, 16 * sizeof(u64)); else - ret = user_regset_copyout_zero(&pos, &count, &kbuf, &ubuf, - 0, - 16 * sizeof(u64)); - - if (!ret) { - if (fprs & FPRS_DU) - ret = user_regset_copyout(&pos, &count, - &kbuf, &ubuf, - fpregs + 16, - 16 * sizeof(u64), - 32 * sizeof(u64)); - else - ret = user_regset_copyout_zero(&pos, &count, - &kbuf, &ubuf, - 16 * sizeof(u64), - 32 * sizeof(u64)); - } + membuf_zero(&to, 16 * sizeof(u64)); + if (fprs & FPRS_DU) + membuf_write(&to, t->fpregs + 16, 16 * sizeof(u64)); + else + membuf_zero(&to, 16 * sizeof(u64)); if (fprs & FPRS_FEF) { - fsr = task_thread_info(target)->xfsr[0]; - gsr = task_thread_info(target)->gsr[0]; + membuf_store(&to, t->xfsr[0]); + membuf_store(&to, t->gsr[0]); } else { - fsr = gsr = 0; + membuf_zero(&to, 2 * sizeof(u64)); } - - if (!ret) - ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, - &fsr, - 32 * sizeof(u64), - 33 * sizeof(u64)); - if (!ret) - ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, - &gsr, - 33 * sizeof(u64), - 34 * sizeof(u64)); - if (!ret) - ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, - &fprs, - 34 * sizeof(u64), - 35 * sizeof(u64)); - - if (!ret) - ret = user_regset_copyout_zero(&pos, &count, &kbuf, &ubuf, - 35 * sizeof(u64), -1); - - return ret; + return membuf_store(&to, fprs); } static int fpregs64_set(struct task_struct *target, @@ -490,7 +424,7 @@ static const struct user_regset sparc64_regsets[] = { .core_note_type = NT_PRSTATUS, .n = 36, .size = sizeof(u64), .align = sizeof(u64), - .get = genregs64_get, .set = genregs64_set + .regset_get = genregs64_get, .set = genregs64_set }, /* Format is: * F0 --> F63 @@ -502,43 +436,23 @@ static const struct user_regset sparc64_regsets[] = { .core_note_type = NT_PRFPREG, .n = 35, .size = sizeof(u64), .align = sizeof(u64), - .get = fpregs64_get, .set = fpregs64_set + .regset_get = fpregs64_get, .set = fpregs64_set }, }; static int getregs64_get(struct task_struct *target, const struct user_regset *regset, - unsigned int pos, unsigned int count, - void *kbuf, void __user *ubuf) + struct membuf to) { const struct pt_regs *regs = task_pt_regs(target); - int ret; if (target == current) flushw_user(); - ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, - regs->u_regs + 1, - 0, 15 * sizeof(u64)); - if (!ret) - ret = user_regset_copyout_zero(&pos, &count, &kbuf, &ubuf, - 15 * sizeof(u64), 16 * sizeof(u64)); - if (!ret) { - /* TSTATE, TPC, TNPC */ - ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, - ®s->tstate, - 16 * sizeof(u64), - 19 * sizeof(u64)); - } - if (!ret) { - unsigned long y = regs->y; - - ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, - &y, - 19 * sizeof(u64), - 20 * sizeof(u64)); - } - return ret; + membuf_write(&to, regs->u_regs + 1, 15 * sizeof(u64)); + membuf_store(&to, (u64)0); + membuf_write(&to, ®s->tstate, 3 * sizeof(u64)); + return membuf_store(&to, (u64)regs->y); } static int setregs64_set(struct task_struct *target, @@ -604,7 +518,7 @@ static const struct user_regset ptrace64_regsets[] = { */ [REGSET_GENERAL] = { .n = 20, .size = sizeof(u64), - .get = getregs64_get, .set = setregs64_set, + .regset_get = getregs64_get, .set = setregs64_set, }, }; @@ -620,81 +534,28 @@ static const struct user_regset_view user_sparc64_view = { #ifdef CONFIG_COMPAT static int genregs32_get(struct task_struct *target, const struct user_regset *regset, - unsigned int pos, unsigned int count, - void *kbuf, void __user *ubuf) + struct membuf to) { const struct pt_regs *regs = task_pt_regs(target); - compat_ulong_t *k = kbuf; - compat_ulong_t __user *u = ubuf; u32 uregs[16]; - u32 reg; + int i; if (target == current) flushw_user(); - pos /= sizeof(reg); - count /= sizeof(reg); - - if (kbuf) { - for (; count > 0 && pos < 16; count--) - *k++ = regs->u_regs[pos++]; - - if (count) { - if (get_from_target(target, regs->u_regs[UREG_I6], - uregs, sizeof(uregs))) - return -EFAULT; - for (; count > 0 && pos < 32; count--) - *k++ = uregs[pos++ - 16]; - - } - } else { - for (; count > 0 && pos < 16; count--) - if (put_user((compat_ulong_t) regs->u_regs[pos++], u++)) - return -EFAULT; - if (count) { - if (get_from_target(target, regs->u_regs[UREG_I6], - uregs, sizeof(uregs))) - return -EFAULT; - for (; count > 0 && pos < 32; count--) - if (put_user(uregs[pos++ - 16], u++)) - return -EFAULT; - } - } - while (count > 0) { - switch (pos) { - case 32: /* PSR */ - reg = tstate_to_psr(regs->tstate); - break; - case 33: /* PC */ - reg = regs->tpc; - break; - case 34: /* NPC */ - reg = regs->tnpc; - break; - case 35: /* Y */ - reg = regs->y; - break; - case 36: /* WIM */ - case 37: /* TBR */ - reg = 0; - break; - default: - goto finish; - } - - if (kbuf) - *k++ = reg; - else if (put_user(reg, u++)) - return -EFAULT; - pos++; - count--; - } -finish: - pos *= sizeof(reg); - count *= sizeof(reg); - - return user_regset_copyout_zero(&pos, &count, &kbuf, &ubuf, - 38 * sizeof(reg), -1); + for (i = 0; i < 16; i++) + membuf_store(&to, (u32)regs->u_regs[i]); + if (!to.left) + return 0; + if (get_from_target(target, regs->u_regs[UREG_I6], + uregs, sizeof(uregs))) + return -EFAULT; + membuf_write(&to, uregs, 16 * sizeof(u32)); + membuf_store(&to, (u32)tstate_to_psr(regs->tstate)); + membuf_store(&to, (u32)(regs->tpc)); + membuf_store(&to, (u32)(regs->tnpc)); + membuf_store(&to, (u32)(regs->y)); + return membuf_zero(&to, 2 * sizeof(u32)); } static int genregs32_set(struct task_struct *target, @@ -816,56 +677,24 @@ finish: static int fpregs32_get(struct task_struct *target, const struct user_regset *regset, - unsigned int pos, unsigned int count, - void *kbuf, void __user *ubuf) + struct membuf to) { - const unsigned long *fpregs = task_thread_info(target)->fpregs; - compat_ulong_t enabled; - unsigned long fprs; - compat_ulong_t fsr; - int ret = 0; + struct thread_info *t = task_thread_info(target); + bool enabled; if (target == current) save_and_clear_fpu(); - fprs = task_thread_info(target)->fpsaved[0]; - if (fprs & FPRS_FEF) { - fsr = task_thread_info(target)->xfsr[0]; - enabled = 1; - } else { - fsr = 0; - enabled = 0; - } - - ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, - fpregs, - 0, 32 * sizeof(u32)); - - if (!ret) - ret = user_regset_copyout_zero(&pos, &count, &kbuf, &ubuf, - 32 * sizeof(u32), - 33 * sizeof(u32)); - if (!ret) - ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, - &fsr, - 33 * sizeof(u32), - 34 * sizeof(u32)); + enabled = t->fpsaved[0] & FPRS_FEF; - if (!ret) { - compat_ulong_t val; - - val = (enabled << 8) | (8 << 16); - ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, - &val, - 34 * sizeof(u32), - 35 * sizeof(u32)); - } - - if (!ret) - ret = user_regset_copyout_zero(&pos, &count, &kbuf, &ubuf, - 35 * sizeof(u32), -1); - - return ret; + membuf_write(&to, t->fpregs, 32 * sizeof(u32)); + membuf_zero(&to, sizeof(u32)); + if (enabled) + membuf_store(&to, (u32)t->xfsr[0]); + else + membuf_zero(&to, sizeof(u32)); + membuf_store(&to, (u32)((enabled << 8) | (8 << 16))); + return membuf_zero(&to, 64 * sizeof(u32)); } static int fpregs32_set(struct task_struct *target, @@ -926,7 +755,7 @@ static const struct user_regset sparc32_regsets[] = { .core_note_type = NT_PRSTATUS, .n = 38, .size = sizeof(u32), .align = sizeof(u32), - .get = genregs32_get, .set = genregs32_set + .regset_get = genregs32_get, .set = genregs32_set }, /* Format is: * F0 --> F31 @@ -942,31 +771,27 @@ static const struct user_regset sparc32_regsets[] = { .core_note_type = NT_PRFPREG, .n = 99, .size = sizeof(u32), .align = sizeof(u32), - .get = fpregs32_get, .set = fpregs32_set + .regset_get = fpregs32_get, .set = fpregs32_set }, }; static int getregs_get(struct task_struct *target, - const struct user_regset *regset, - unsigned int pos, unsigned int count, - void *kbuf, void __user *ubuf) + const struct user_regset *regset, + struct membuf to) { const struct pt_regs *regs = task_pt_regs(target); - u32 uregs[19]; int i; if (target == current) flushw_user(); - uregs[0] = tstate_to_psr(regs->tstate); - uregs[1] = regs->tpc; - uregs[2] = regs->tnpc; - uregs[3] = regs->y; + membuf_store(&to, (u32)tstate_to_psr(regs->tstate)); + membuf_store(&to, (u32)(regs->tpc)); + membuf_store(&to, (u32)(regs->tnpc)); + membuf_store(&to, (u32)(regs->y)); for (i = 1; i < 16; i++) - uregs[3 + i] = regs->u_regs[i]; - return user_regset_copyout(&pos, &count, &kbuf, &ubuf, - uregs, - 0, 19 * sizeof(u32)); + membuf_store(&to, (u32)regs->u_regs[i]); + return to.left; } static int setregs_set(struct task_struct *target, @@ -1005,36 +830,19 @@ static int setregs_set(struct task_struct *target, static int getfpregs_get(struct task_struct *target, const struct user_regset *regset, - unsigned int pos, unsigned int count, - void *kbuf, void __user *ubuf) + struct membuf to) { - const unsigned long *fpregs = task_thread_info(target)->fpregs; - unsigned long fprs; - compat_ulong_t fsr; - int ret = 0; + struct thread_info *t = task_thread_info(target); if (target == current) save_and_clear_fpu(); - fprs = task_thread_info(target)->fpsaved[0]; - if (fprs & FPRS_FEF) { - fsr = task_thread_info(target)->xfsr[0]; - } else { - fsr = 0; - } - - ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, - fpregs, - 0, 32 * sizeof(u32)); - if (!ret) - ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, - &fsr, - 32 * sizeof(u32), - 33 * sizeof(u32)); - if (!ret) - ret = user_regset_copyout_zero(&pos, &count, &kbuf, &ubuf, - 33 * sizeof(u32), 68 * sizeof(u32)); - return ret; + membuf_write(&to, t->fpregs, 32 * sizeof(u32)); + if (t->fpsaved[0] & FPRS_FEF) + membuf_store(&to, (u32)t->xfsr[0]); + else + membuf_zero(&to, sizeof(u32)); + return membuf_zero(&to, 35 * sizeof(u32)); } static int setfpregs_set(struct task_struct *target, @@ -1078,11 +886,11 @@ static int setfpregs_set(struct task_struct *target, static const struct user_regset ptrace32_regsets[] = { [REGSET_GENERAL] = { .n = 19, .size = sizeof(u32), - .get = getregs_get, .set = setregs_set, + .regset_get = getregs_get, .set = setregs_set, }, [REGSET_FP] = { .n = 68, .size = sizeof(u32), - .get = getfpregs_get, .set = setfpregs_set, + .regset_get = getfpregs_get, .set = setfpregs_set, }, }; -- GitLab From a0faf966a44134b56eee732d25717fad2993b0bc Mon Sep 17 00:00:00 2001 From: Al Viro Date: Fri, 22 May 2020 17:06:56 -0400 Subject: [PATCH 0959/1476] mips: switch to ->regset_get() Signed-off-by: Al Viro --- arch/mips/kernel/ptrace.c | 204 +++++++++++--------------------------- 1 file changed, 58 insertions(+), 146 deletions(-) diff --git a/arch/mips/kernel/ptrace.c b/arch/mips/kernel/ptrace.c index 2a61641c680b5..db7c5be1d4a35 100644 --- a/arch/mips/kernel/ptrace.c +++ b/arch/mips/kernel/ptrace.c @@ -210,15 +210,13 @@ int ptrace_set_watch_regs(struct task_struct *child, static int gpr32_get(struct task_struct *target, const struct user_regset *regset, - unsigned int pos, unsigned int count, - void *kbuf, void __user *ubuf) + struct membuf to) { struct pt_regs *regs = task_pt_regs(target); u32 uregs[ELF_NGREG] = {}; mips_dump_regs32(uregs, regs); - return user_regset_copyout(&pos, &count, &kbuf, &ubuf, uregs, 0, - sizeof(uregs)); + return membuf_write(&to, uregs, sizeof(uregs)); } static int gpr32_set(struct task_struct *target, @@ -277,15 +275,13 @@ static int gpr32_set(struct task_struct *target, static int gpr64_get(struct task_struct *target, const struct user_regset *regset, - unsigned int pos, unsigned int count, - void *kbuf, void __user *ubuf) + struct membuf to) { struct pt_regs *regs = task_pt_regs(target); u64 uregs[ELF_NGREG] = {}; mips_dump_regs64(uregs, regs); - return user_regset_copyout(&pos, &count, &kbuf, &ubuf, uregs, 0, - sizeof(uregs)); + return membuf_write(&to, uregs, sizeof(uregs)); } static int gpr64_set(struct task_struct *target, @@ -408,13 +404,11 @@ int ptrace_setfpregs(struct task_struct *child, __u32 __user *data) * !CONFIG_CPU_HAS_MSA variant. FP context's general register slots * correspond 1:1 to buffer slots. Only general registers are copied. */ -static int fpr_get_fpa(struct task_struct *target, - unsigned int *pos, unsigned int *count, - void **kbuf, void __user **ubuf) +static void fpr_get_fpa(struct task_struct *target, + struct membuf *to) { - return user_regset_copyout(pos, count, kbuf, ubuf, - &target->thread.fpu, - 0, NUM_FPU_REGS * sizeof(elf_fpreg_t)); + membuf_write(to, &target->thread.fpu, + NUM_FPU_REGS * sizeof(elf_fpreg_t)); } /* @@ -423,25 +417,13 @@ static int fpr_get_fpa(struct task_struct *target, * general register slots are copied to buffer slots. Only general * registers are copied. */ -static int fpr_get_msa(struct task_struct *target, - unsigned int *pos, unsigned int *count, - void **kbuf, void __user **ubuf) +static void fpr_get_msa(struct task_struct *target, struct membuf *to) { unsigned int i; - u64 fpr_val; - int err; - - BUILD_BUG_ON(sizeof(fpr_val) != sizeof(elf_fpreg_t)); - for (i = 0; i < NUM_FPU_REGS; i++) { - fpr_val = get_fpr64(&target->thread.fpu.fpr[i], 0); - err = user_regset_copyout(pos, count, kbuf, ubuf, - &fpr_val, i * sizeof(elf_fpreg_t), - (i + 1) * sizeof(elf_fpreg_t)); - if (err) - return err; - } - return 0; + BUILD_BUG_ON(sizeof(u64) != sizeof(elf_fpreg_t)); + for (i = 0; i < NUM_FPU_REGS; i++) + membuf_store(to, get_fpr64(&target->thread.fpu.fpr[i], 0)); } /* @@ -451,31 +433,16 @@ static int fpr_get_msa(struct task_struct *target, */ static int fpr_get(struct task_struct *target, const struct user_regset *regset, - unsigned int pos, unsigned int count, - void *kbuf, void __user *ubuf) + struct membuf to) { - const int fcr31_pos = NUM_FPU_REGS * sizeof(elf_fpreg_t); - const int fir_pos = fcr31_pos + sizeof(u32); - int err; - if (sizeof(target->thread.fpu.fpr[0]) == sizeof(elf_fpreg_t)) - err = fpr_get_fpa(target, &pos, &count, &kbuf, &ubuf); + fpr_get_fpa(target, &to); else - err = fpr_get_msa(target, &pos, &count, &kbuf, &ubuf); - if (err) - return err; - - err = user_regset_copyout(&pos, &count, &kbuf, &ubuf, - &target->thread.fpu.fcr31, - fcr31_pos, fcr31_pos + sizeof(u32)); - if (err) - return err; + fpr_get_msa(target, &to); - err = user_regset_copyout(&pos, &count, &kbuf, &ubuf, - &boot_cpu_data.fpu_id, - fir_pos, fir_pos + sizeof(u32)); - - return err; + membuf_write(&to, &target->thread.fpu.fcr31, sizeof(u32)); + membuf_write(&to, &boot_cpu_data.fpu_id, sizeof(u32)); + return 0; } /* @@ -576,14 +543,9 @@ static int fpr_set(struct task_struct *target, /* Copy the FP mode setting to the supplied NT_MIPS_FP_MODE buffer. */ static int fp_mode_get(struct task_struct *target, const struct user_regset *regset, - unsigned int pos, unsigned int count, - void *kbuf, void __user *ubuf) + struct membuf to) { - int fp_mode; - - fp_mode = mips_get_process_fp_mode(target); - return user_regset_copyout(&pos, &count, &kbuf, &ubuf, &fp_mode, 0, - sizeof(fp_mode)); + return membuf_store(&to, (int)mips_get_process_fp_mode(target)); } /* @@ -630,13 +592,12 @@ struct msa_control_regs { unsigned int msacsr; }; -static int copy_pad_fprs(struct task_struct *target, +static void copy_pad_fprs(struct task_struct *target, const struct user_regset *regset, - unsigned int *ppos, unsigned int *pcount, - void **pkbuf, void __user **pubuf, + struct membuf *to, unsigned int live_sz) { - int i, j, start, start_pad, err; + int i, j; unsigned long long fill = ~0ull; unsigned int cp_sz, pad_sz; @@ -644,28 +605,16 @@ static int copy_pad_fprs(struct task_struct *target, pad_sz = regset->size - cp_sz; WARN_ON(pad_sz % sizeof(fill)); - i = start = err = 0; - for (; i < NUM_FPU_REGS; i++, start += regset->size) { - err |= user_regset_copyout(ppos, pcount, pkbuf, pubuf, - &target->thread.fpu.fpr[i], - start, start + cp_sz); - - start_pad = start + cp_sz; - for (j = 0; j < (pad_sz / sizeof(fill)); j++) { - err |= user_regset_copyout(ppos, pcount, pkbuf, pubuf, - &fill, start_pad, - start_pad + sizeof(fill)); - start_pad += sizeof(fill); - } + for (i = 0; i < NUM_FPU_REGS; i++) { + membuf_write(to, &target->thread.fpu.fpr[i], cp_sz); + for (j = 0; j < (pad_sz / sizeof(fill)); j++) + membuf_store(to, fill); } - - return err; } static int msa_get(struct task_struct *target, const struct user_regset *regset, - unsigned int pos, unsigned int count, - void *kbuf, void __user *ubuf) + struct membuf to) { const unsigned int wr_size = NUM_FPU_REGS * regset->size; const struct msa_control_regs ctrl_regs = { @@ -674,32 +623,23 @@ static int msa_get(struct task_struct *target, .msair = boot_cpu_data.msa_id, .msacsr = target->thread.fpu.msacsr, }; - int err; if (!tsk_used_math(target)) { /* The task hasn't used FP or MSA, fill with 0xff */ - err = copy_pad_fprs(target, regset, &pos, &count, - &kbuf, &ubuf, 0); + copy_pad_fprs(target, regset, &to, 0); } else if (!test_tsk_thread_flag(target, TIF_MSA_CTX_LIVE)) { /* Copy scalar FP context, fill the rest with 0xff */ - err = copy_pad_fprs(target, regset, &pos, &count, - &kbuf, &ubuf, 8); + copy_pad_fprs(target, regset, &to, 8); } else if (sizeof(target->thread.fpu.fpr[0]) == regset->size) { /* Trivially copy the vector registers */ - err = user_regset_copyout(&pos, &count, &kbuf, &ubuf, - &target->thread.fpu.fpr, - 0, wr_size); + membuf_write(&to, &target->thread.fpu.fpr, wr_size); } else { /* Copy as much context as possible, fill the rest with 0xff */ - err = copy_pad_fprs(target, regset, &pos, &count, - &kbuf, &ubuf, - sizeof(target->thread.fpu.fpr[0])); + copy_pad_fprs(target, regset, &to, + sizeof(target->thread.fpu.fpr[0])); } - err |= user_regset_copyout(&pos, &count, &kbuf, &ubuf, - &ctrl_regs, wr_size, - wr_size + sizeof(ctrl_regs)); - return err; + return membuf_write(&to, &ctrl_regs, sizeof(ctrl_regs)); } static int msa_set(struct task_struct *target, @@ -752,34 +692,20 @@ static int msa_set(struct task_struct *target, */ static int dsp32_get(struct task_struct *target, const struct user_regset *regset, - unsigned int pos, unsigned int count, - void *kbuf, void __user *ubuf) + struct membuf to) { - unsigned int start, num_regs, i; u32 dspregs[NUM_DSP_REGS + 1]; + unsigned int i; - BUG_ON(count % sizeof(u32)); + BUG_ON(to.left % sizeof(u32)); if (!cpu_has_dsp) return -EIO; - start = pos / sizeof(u32); - num_regs = count / sizeof(u32); - - if (start + num_regs > NUM_DSP_REGS + 1) - return -EIO; - - for (i = start; i < num_regs; i++) - switch (i) { - case 0 ... NUM_DSP_REGS - 1: - dspregs[i] = target->thread.dsp.dspr[i]; - break; - case NUM_DSP_REGS: - dspregs[i] = target->thread.dsp.dspcontrol; - break; - } - return user_regset_copyout(&pos, &count, &kbuf, &ubuf, dspregs, 0, - sizeof(dspregs)); + for (i = 0; i < NUM_DSP_REGS; i++) + dspregs[i] = target->thread.dsp.dspr[i]; + dspregs[NUM_DSP_REGS] = target->thread.dsp.dspcontrol; + return membuf_write(&to, dspregs, sizeof(dspregs)); } /* @@ -832,34 +758,20 @@ static int dsp32_set(struct task_struct *target, */ static int dsp64_get(struct task_struct *target, const struct user_regset *regset, - unsigned int pos, unsigned int count, - void *kbuf, void __user *ubuf) + struct membuf to) { - unsigned int start, num_regs, i; u64 dspregs[NUM_DSP_REGS + 1]; + unsigned int i; - BUG_ON(count % sizeof(u64)); + BUG_ON(to.left % sizeof(u64)); if (!cpu_has_dsp) return -EIO; - start = pos / sizeof(u64); - num_regs = count / sizeof(u64); - - if (start + num_regs > NUM_DSP_REGS + 1) - return -EIO; - - for (i = start; i < num_regs; i++) - switch (i) { - case 0 ... NUM_DSP_REGS - 1: - dspregs[i] = target->thread.dsp.dspr[i]; - break; - case NUM_DSP_REGS: - dspregs[i] = target->thread.dsp.dspcontrol; - break; - } - return user_regset_copyout(&pos, &count, &kbuf, &ubuf, dspregs, 0, - sizeof(dspregs)); + for (i = 0; i < NUM_DSP_REGS; i++) + dspregs[i] = target->thread.dsp.dspr[i]; + dspregs[NUM_DSP_REGS] = target->thread.dsp.dspcontrol; + return membuf_write(&to, dspregs, sizeof(dspregs)); } /* @@ -1018,7 +930,7 @@ static const struct user_regset mips_regsets[] = { .n = ELF_NGREG, .size = sizeof(unsigned int), .align = sizeof(unsigned int), - .get = gpr32_get, + .regset_get = gpr32_get, .set = gpr32_set, }, [REGSET_DSP] = { @@ -1026,7 +938,7 @@ static const struct user_regset mips_regsets[] = { .n = NUM_DSP_REGS + 1, .size = sizeof(u32), .align = sizeof(u32), - .get = dsp32_get, + .regset_get = dsp32_get, .set = dsp32_set, .active = dsp_active, }, @@ -1036,7 +948,7 @@ static const struct user_regset mips_regsets[] = { .n = ELF_NFPREG, .size = sizeof(elf_fpreg_t), .align = sizeof(elf_fpreg_t), - .get = fpr_get, + .regset_get = fpr_get, .set = fpr_set, }, [REGSET_FP_MODE] = { @@ -1044,7 +956,7 @@ static const struct user_regset mips_regsets[] = { .n = 1, .size = sizeof(int), .align = sizeof(int), - .get = fp_mode_get, + .regset_get = fp_mode_get, .set = fp_mode_set, }, #endif @@ -1054,7 +966,7 @@ static const struct user_regset mips_regsets[] = { .n = NUM_FPU_REGS + 1, .size = 16, .align = 16, - .get = msa_get, + .regset_get = msa_get, .set = msa_set, }, #endif @@ -1078,7 +990,7 @@ static const struct user_regset mips64_regsets[] = { .n = ELF_NGREG, .size = sizeof(unsigned long), .align = sizeof(unsigned long), - .get = gpr64_get, + .regset_get = gpr64_get, .set = gpr64_set, }, [REGSET_DSP] = { @@ -1086,7 +998,7 @@ static const struct user_regset mips64_regsets[] = { .n = NUM_DSP_REGS + 1, .size = sizeof(u64), .align = sizeof(u64), - .get = dsp64_get, + .regset_get = dsp64_get, .set = dsp64_set, .active = dsp_active, }, @@ -1096,7 +1008,7 @@ static const struct user_regset mips64_regsets[] = { .n = 1, .size = sizeof(int), .align = sizeof(int), - .get = fp_mode_get, + .regset_get = fp_mode_get, .set = fp_mode_set, }, [REGSET_FPR] = { @@ -1104,7 +1016,7 @@ static const struct user_regset mips64_regsets[] = { .n = ELF_NFPREG, .size = sizeof(elf_fpreg_t), .align = sizeof(elf_fpreg_t), - .get = fpr_get, + .regset_get = fpr_get, .set = fpr_set, }, #endif @@ -1114,7 +1026,7 @@ static const struct user_regset mips64_regsets[] = { .n = NUM_FPU_REGS + 1, .size = 16, .align = 16, - .get = msa_get, + .regset_get = msa_get, .set = msa_set, }, #endif -- GitLab From 759de58f28b1cd90a485d3bf1ce49236a153ce51 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Wed, 27 May 2020 19:09:44 -0400 Subject: [PATCH 0960/1476] arm64: switch to ->regset_get() Signed-off-by: Al Viro --- arch/arm64/kernel/ptrace.c | 225 ++++++++++--------------------------- 1 file changed, 62 insertions(+), 163 deletions(-) diff --git a/arch/arm64/kernel/ptrace.c b/arch/arm64/kernel/ptrace.c index 9f769e862f687..902d987fdd352 100644 --- a/arch/arm64/kernel/ptrace.c +++ b/arch/arm64/kernel/ptrace.c @@ -474,11 +474,10 @@ static int ptrace_hbp_set_addr(unsigned int note_type, static int hw_break_get(struct task_struct *target, const struct user_regset *regset, - unsigned int pos, unsigned int count, - void *kbuf, void __user *ubuf) + struct membuf to) { unsigned int note_type = regset->core_note_type; - int ret, idx = 0, offset, limit; + int ret, idx = 0; u32 info, ctrl; u64 addr; @@ -487,49 +486,21 @@ static int hw_break_get(struct task_struct *target, if (ret) return ret; - ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, &info, 0, - sizeof(info)); - if (ret) - return ret; - - /* Pad */ - offset = offsetof(struct user_hwdebug_state, pad); - ret = user_regset_copyout_zero(&pos, &count, &kbuf, &ubuf, offset, - offset + PTRACE_HBP_PAD_SZ); - if (ret) - return ret; - + membuf_write(&to, &info, sizeof(info)); + membuf_zero(&to, sizeof(u32)); /* (address, ctrl) registers */ - offset = offsetof(struct user_hwdebug_state, dbg_regs); - limit = regset->n * regset->size; - while (count && offset < limit) { + while (to.left) { ret = ptrace_hbp_get_addr(note_type, target, idx, &addr); if (ret) return ret; - ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, &addr, - offset, offset + PTRACE_HBP_ADDR_SZ); - if (ret) - return ret; - offset += PTRACE_HBP_ADDR_SZ; - ret = ptrace_hbp_get_ctrl(note_type, target, idx, &ctrl); if (ret) return ret; - ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, &ctrl, - offset, offset + PTRACE_HBP_CTRL_SZ); - if (ret) - return ret; - offset += PTRACE_HBP_CTRL_SZ; - - ret = user_regset_copyout_zero(&pos, &count, &kbuf, &ubuf, - offset, - offset + PTRACE_HBP_PAD_SZ); - if (ret) - return ret; - offset += PTRACE_HBP_PAD_SZ; + membuf_store(&to, addr); + membuf_store(&to, ctrl); + membuf_zero(&to, sizeof(u32)); idx++; } - return 0; } @@ -589,11 +560,10 @@ static int hw_break_set(struct task_struct *target, static int gpr_get(struct task_struct *target, const struct user_regset *regset, - unsigned int pos, unsigned int count, - void *kbuf, void __user *ubuf) + struct membuf to) { struct user_pt_regs *uregs = &task_pt_regs(target)->user_regs; - return user_regset_copyout(&pos, &count, &kbuf, &ubuf, uregs, 0, -1); + return membuf_write(&to, uregs, sizeof(*uregs)); } static int gpr_set(struct task_struct *target, const struct user_regset *regset, @@ -626,8 +596,7 @@ static int fpr_active(struct task_struct *target, const struct user_regset *regs */ static int __fpr_get(struct task_struct *target, const struct user_regset *regset, - unsigned int pos, unsigned int count, - void *kbuf, void __user *ubuf, unsigned int start_pos) + struct membuf to) { struct user_fpsimd_state *uregs; @@ -635,13 +604,11 @@ static int __fpr_get(struct task_struct *target, uregs = &target->thread.uw.fpsimd_state; - return user_regset_copyout(&pos, &count, &kbuf, &ubuf, uregs, - start_pos, start_pos + sizeof(*uregs)); + return membuf_write(&to, uregs, sizeof(*uregs)); } static int fpr_get(struct task_struct *target, const struct user_regset *regset, - unsigned int pos, unsigned int count, - void *kbuf, void __user *ubuf) + struct membuf to) { if (!system_supports_fpsimd()) return -EINVAL; @@ -649,7 +616,7 @@ static int fpr_get(struct task_struct *target, const struct user_regset *regset, if (target == current) fpsimd_preserve_current_state(); - return __fpr_get(target, regset, pos, count, kbuf, ubuf, 0); + return __fpr_get(target, regset, to); } static int __fpr_set(struct task_struct *target, @@ -699,15 +666,12 @@ static int fpr_set(struct task_struct *target, const struct user_regset *regset, } static int tls_get(struct task_struct *target, const struct user_regset *regset, - unsigned int pos, unsigned int count, - void *kbuf, void __user *ubuf) + struct membuf to) { - unsigned long *tls = &target->thread.uw.tp_value; - if (target == current) tls_preserve_current_state(); - return user_regset_copyout(&pos, &count, &kbuf, &ubuf, tls, 0, -1); + return membuf_store(&to, target->thread.uw.tp_value); } static int tls_set(struct task_struct *target, const struct user_regset *regset, @@ -727,13 +691,9 @@ static int tls_set(struct task_struct *target, const struct user_regset *regset, static int system_call_get(struct task_struct *target, const struct user_regset *regset, - unsigned int pos, unsigned int count, - void *kbuf, void __user *ubuf) + struct membuf to) { - int syscallno = task_pt_regs(target)->syscallno; - - return user_regset_copyout(&pos, &count, &kbuf, &ubuf, - &syscallno, 0, -1); + return membuf_store(&to, task_pt_regs(target)->syscallno); } static int system_call_set(struct task_struct *target, @@ -794,10 +754,8 @@ static unsigned int sve_get_size(struct task_struct *target, static int sve_get(struct task_struct *target, const struct user_regset *regset, - unsigned int pos, unsigned int count, - void *kbuf, void __user *ubuf) + struct membuf to) { - int ret; struct user_sve_header header; unsigned int vq; unsigned long start, end; @@ -809,10 +767,7 @@ static int sve_get(struct task_struct *target, sve_init_header_from_task(&header, target); vq = sve_vq_from_vl(header.vl); - ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, &header, - 0, sizeof(header)); - if (ret) - return ret; + membuf_write(&to, &header, sizeof(header)); if (target == current) fpsimd_preserve_current_state(); @@ -821,26 +776,18 @@ static int sve_get(struct task_struct *target, BUILD_BUG_ON(SVE_PT_FPSIMD_OFFSET != sizeof(header)); if ((header.flags & SVE_PT_REGS_MASK) == SVE_PT_REGS_FPSIMD) - return __fpr_get(target, regset, pos, count, kbuf, ubuf, - SVE_PT_FPSIMD_OFFSET); + return __fpr_get(target, regset, to); /* Otherwise: full SVE case */ BUILD_BUG_ON(SVE_PT_SVE_OFFSET != sizeof(header)); start = SVE_PT_SVE_OFFSET; end = SVE_PT_SVE_FFR_OFFSET(vq) + SVE_PT_SVE_FFR_SIZE(vq); - ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, - target->thread.sve_state, - start, end); - if (ret) - return ret; + membuf_write(&to, target->thread.sve_state, end - start); start = end; end = SVE_PT_SVE_FPSR_OFFSET(vq); - ret = user_regset_copyout_zero(&pos, &count, &kbuf, &ubuf, - start, end); - if (ret) - return ret; + membuf_zero(&to, end - start); /* * Copy fpsr, and fpcr which must follow contiguously in @@ -848,16 +795,11 @@ static int sve_get(struct task_struct *target, */ start = end; end = SVE_PT_SVE_FPCR_OFFSET(vq) + SVE_PT_SVE_FPCR_SIZE; - ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, - &target->thread.uw.fpsimd_state.fpsr, - start, end); - if (ret) - return ret; + membuf_write(&to, &target->thread.uw.fpsimd_state.fpsr, end - start); start = end; end = sve_size_from_header(&header); - return user_regset_copyout_zero(&pos, &count, &kbuf, &ubuf, - start, end); + return membuf_zero(&to, end - start); } static int sve_set(struct task_struct *target, @@ -961,8 +903,7 @@ out: #ifdef CONFIG_ARM64_PTR_AUTH static int pac_mask_get(struct task_struct *target, const struct user_regset *regset, - unsigned int pos, unsigned int count, - void *kbuf, void __user *ubuf) + struct membuf to) { /* * The PAC bits can differ across data and instruction pointers @@ -978,7 +919,7 @@ static int pac_mask_get(struct task_struct *target, if (!system_supports_address_auth()) return -EINVAL; - return user_regset_copyout(&pos, &count, &kbuf, &ubuf, &uregs, 0, -1); + return membuf_write(&to, &uregs, sizeof(uregs)); } #ifdef CONFIG_CHECKPOINT_RESTORE @@ -1017,8 +958,7 @@ static void pac_address_keys_from_user(struct ptrauth_keys_user *keys, static int pac_address_keys_get(struct task_struct *target, const struct user_regset *regset, - unsigned int pos, unsigned int count, - void *kbuf, void __user *ubuf) + struct membuf to) { struct ptrauth_keys_user *keys = &target->thread.keys_user; struct user_pac_address_keys user_keys; @@ -1028,8 +968,7 @@ static int pac_address_keys_get(struct task_struct *target, pac_address_keys_to_user(&user_keys, keys); - return user_regset_copyout(&pos, &count, &kbuf, &ubuf, - &user_keys, 0, -1); + return membuf_write(&to, &user_keys, sizeof(user_keys)); } static int pac_address_keys_set(struct task_struct *target, @@ -1068,8 +1007,7 @@ static void pac_generic_keys_from_user(struct ptrauth_keys_user *keys, static int pac_generic_keys_get(struct task_struct *target, const struct user_regset *regset, - unsigned int pos, unsigned int count, - void *kbuf, void __user *ubuf) + struct membuf to) { struct ptrauth_keys_user *keys = &target->thread.keys_user; struct user_pac_generic_keys user_keys; @@ -1079,8 +1017,7 @@ static int pac_generic_keys_get(struct task_struct *target, pac_generic_keys_to_user(&user_keys, keys); - return user_regset_copyout(&pos, &count, &kbuf, &ubuf, - &user_keys, 0, -1); + return membuf_write(&to, &user_keys, sizeof(user_keys)); } static int pac_generic_keys_set(struct task_struct *target, @@ -1134,7 +1071,7 @@ static const struct user_regset aarch64_regsets[] = { .n = sizeof(struct user_pt_regs) / sizeof(u64), .size = sizeof(u64), .align = sizeof(u64), - .get = gpr_get, + .regset_get = gpr_get, .set = gpr_set }, [REGSET_FPR] = { @@ -1147,7 +1084,7 @@ static const struct user_regset aarch64_regsets[] = { .size = sizeof(u32), .align = sizeof(u32), .active = fpr_active, - .get = fpr_get, + .regset_get = fpr_get, .set = fpr_set }, [REGSET_TLS] = { @@ -1155,7 +1092,7 @@ static const struct user_regset aarch64_regsets[] = { .n = 1, .size = sizeof(void *), .align = sizeof(void *), - .get = tls_get, + .regset_get = tls_get, .set = tls_set, }, #ifdef CONFIG_HAVE_HW_BREAKPOINT @@ -1164,7 +1101,7 @@ static const struct user_regset aarch64_regsets[] = { .n = sizeof(struct user_hwdebug_state) / sizeof(u32), .size = sizeof(u32), .align = sizeof(u32), - .get = hw_break_get, + .regset_get = hw_break_get, .set = hw_break_set, }, [REGSET_HW_WATCH] = { @@ -1172,7 +1109,7 @@ static const struct user_regset aarch64_regsets[] = { .n = sizeof(struct user_hwdebug_state) / sizeof(u32), .size = sizeof(u32), .align = sizeof(u32), - .get = hw_break_get, + .regset_get = hw_break_get, .set = hw_break_set, }, #endif @@ -1181,7 +1118,7 @@ static const struct user_regset aarch64_regsets[] = { .n = 1, .size = sizeof(int), .align = sizeof(int), - .get = system_call_get, + .regset_get = system_call_get, .set = system_call_set, }, #ifdef CONFIG_ARM64_SVE @@ -1191,7 +1128,7 @@ static const struct user_regset aarch64_regsets[] = { SVE_VQ_BYTES), .size = SVE_VQ_BYTES, .align = SVE_VQ_BYTES, - .get = sve_get, + .regset_get = sve_get, .set = sve_set, .get_size = sve_get_size, }, @@ -1202,7 +1139,7 @@ static const struct user_regset aarch64_regsets[] = { .n = sizeof(struct user_pac_mask) / sizeof(u64), .size = sizeof(u64), .align = sizeof(u64), - .get = pac_mask_get, + .regset_get = pac_mask_get, /* this cannot be set dynamically */ }, #ifdef CONFIG_CHECKPOINT_RESTORE @@ -1211,7 +1148,7 @@ static const struct user_regset aarch64_regsets[] = { .n = sizeof(struct user_pac_address_keys) / sizeof(__uint128_t), .size = sizeof(__uint128_t), .align = sizeof(__uint128_t), - .get = pac_address_keys_get, + .regset_get = pac_address_keys_get, .set = pac_address_keys_set, }, [REGSET_PACG_KEYS] = { @@ -1219,7 +1156,7 @@ static const struct user_regset aarch64_regsets[] = { .n = sizeof(struct user_pac_generic_keys) / sizeof(__uint128_t), .size = sizeof(__uint128_t), .align = sizeof(__uint128_t), - .get = pac_generic_keys_get, + .regset_get = pac_generic_keys_get, .set = pac_generic_keys_set, }, #endif @@ -1255,39 +1192,13 @@ static inline compat_ulong_t compat_get_user_reg(struct task_struct *task, int i static int compat_gpr_get(struct task_struct *target, const struct user_regset *regset, - unsigned int pos, unsigned int count, - void *kbuf, void __user *ubuf) + struct membuf to) { - int ret = 0; - unsigned int i, start, num_regs; - - /* Calculate the number of AArch32 registers contained in count */ - num_regs = count / regset->size; - - /* Convert pos into an register number */ - start = pos / regset->size; - - if (start + num_regs > regset->n) - return -EIO; - - for (i = 0; i < num_regs; ++i) { - compat_ulong_t reg = compat_get_user_reg(target, start + i); - - if (kbuf) { - memcpy(kbuf, ®, sizeof(reg)); - kbuf += sizeof(reg); - } else { - ret = copy_to_user(ubuf, ®, sizeof(reg)); - if (ret) { - ret = -EFAULT; - break; - } + int i = 0; - ubuf += sizeof(reg); - } - } - - return ret; + while (to.left) + membuf_store(&to, compat_get_user_reg(target, i++)); + return 0; } static int compat_gpr_set(struct task_struct *target, @@ -1354,12 +1265,10 @@ static int compat_gpr_set(struct task_struct *target, static int compat_vfp_get(struct task_struct *target, const struct user_regset *regset, - unsigned int pos, unsigned int count, - void *kbuf, void __user *ubuf) + struct membuf to) { struct user_fpsimd_state *uregs; compat_ulong_t fpscr; - int ret, vregs_end_pos; if (!system_supports_fpsimd()) return -EINVAL; @@ -1373,19 +1282,10 @@ static int compat_vfp_get(struct task_struct *target, * The VFP registers are packed into the fpsimd_state, so they all sit * nicely together for us. We just need to create the fpscr separately. */ - vregs_end_pos = VFP_STATE_SIZE - sizeof(compat_ulong_t); - ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, uregs, - 0, vregs_end_pos); - - if (count && !ret) { - fpscr = (uregs->fpsr & VFP_FPSCR_STAT_MASK) | - (uregs->fpcr & VFP_FPSCR_CTRL_MASK); - - ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, &fpscr, - vregs_end_pos, VFP_STATE_SIZE); - } - - return ret; + membuf_write(&to, uregs, VFP_STATE_SIZE - sizeof(compat_ulong_t)); + fpscr = (uregs->fpsr & VFP_FPSCR_STAT_MASK) | + (uregs->fpcr & VFP_FPSCR_CTRL_MASK); + return membuf_store(&to, fpscr); } static int compat_vfp_set(struct task_struct *target, @@ -1420,11 +1320,10 @@ static int compat_vfp_set(struct task_struct *target, } static int compat_tls_get(struct task_struct *target, - const struct user_regset *regset, unsigned int pos, - unsigned int count, void *kbuf, void __user *ubuf) + const struct user_regset *regset, + struct membuf to) { - compat_ulong_t tls = (compat_ulong_t)target->thread.uw.tp_value; - return user_regset_copyout(&pos, &count, &kbuf, &ubuf, &tls, 0, -1); + return membuf_store(&to, (compat_ulong_t)target->thread.uw.tp_value); } static int compat_tls_set(struct task_struct *target, @@ -1449,7 +1348,7 @@ static const struct user_regset aarch32_regsets[] = { .n = COMPAT_ELF_NGREG, .size = sizeof(compat_elf_greg_t), .align = sizeof(compat_elf_greg_t), - .get = compat_gpr_get, + .regset_get = compat_gpr_get, .set = compat_gpr_set }, [REGSET_COMPAT_VFP] = { @@ -1458,7 +1357,7 @@ static const struct user_regset aarch32_regsets[] = { .size = sizeof(compat_ulong_t), .align = sizeof(compat_ulong_t), .active = fpr_active, - .get = compat_vfp_get, + .regset_get = compat_vfp_get, .set = compat_vfp_set }, }; @@ -1474,7 +1373,7 @@ static const struct user_regset aarch32_ptrace_regsets[] = { .n = COMPAT_ELF_NGREG, .size = sizeof(compat_elf_greg_t), .align = sizeof(compat_elf_greg_t), - .get = compat_gpr_get, + .regset_get = compat_gpr_get, .set = compat_gpr_set }, [REGSET_FPR] = { @@ -1482,7 +1381,7 @@ static const struct user_regset aarch32_ptrace_regsets[] = { .n = VFP_STATE_SIZE / sizeof(compat_ulong_t), .size = sizeof(compat_ulong_t), .align = sizeof(compat_ulong_t), - .get = compat_vfp_get, + .regset_get = compat_vfp_get, .set = compat_vfp_set }, [REGSET_TLS] = { @@ -1490,7 +1389,7 @@ static const struct user_regset aarch32_ptrace_regsets[] = { .n = 1, .size = sizeof(compat_ulong_t), .align = sizeof(compat_ulong_t), - .get = compat_tls_get, + .regset_get = compat_tls_get, .set = compat_tls_set, }, #ifdef CONFIG_HAVE_HW_BREAKPOINT @@ -1499,7 +1398,7 @@ static const struct user_regset aarch32_ptrace_regsets[] = { .n = sizeof(struct user_hwdebug_state) / sizeof(u32), .size = sizeof(u32), .align = sizeof(u32), - .get = hw_break_get, + .regset_get = hw_break_get, .set = hw_break_set, }, [REGSET_HW_WATCH] = { @@ -1507,7 +1406,7 @@ static const struct user_regset aarch32_ptrace_regsets[] = { .n = sizeof(struct user_hwdebug_state) / sizeof(u32), .size = sizeof(u32), .align = sizeof(u32), - .get = hw_break_get, + .regset_get = hw_break_get, .set = hw_break_set, }, #endif @@ -1516,7 +1415,7 @@ static const struct user_regset aarch32_ptrace_regsets[] = { .n = 1, .size = sizeof(int), .align = sizeof(int), - .get = system_call_get, + .regset_get = system_call_get, .set = system_call_set, }, }; -- GitLab From 3399d90ce63edf7439900db50b5c02aa822f190b Mon Sep 17 00:00:00 2001 From: Al Viro Date: Tue, 16 Jun 2020 00:54:51 -0400 Subject: [PATCH 0961/1476] sh: convert to ->regset_get() NB: there's a direct call of fpregs_get() left in dump_fpu(). To be taken out once we convert ELF_FDPIC to use of regset. Signed-off-by: Al Viro --- arch/sh/kernel/process_32.c | 5 ++-- arch/sh/kernel/ptrace_32.c | 48 ++++++++----------------------------- 2 files changed, 12 insertions(+), 41 deletions(-) diff --git a/arch/sh/kernel/process_32.c b/arch/sh/kernel/process_32.c index 456cc8d171f72..6ab397bc47ed0 100644 --- a/arch/sh/kernel/process_32.c +++ b/arch/sh/kernel/process_32.c @@ -103,9 +103,8 @@ int dump_fpu(struct pt_regs *regs, elf_fpregset_t *fpu) fpvalid = !!tsk_used_math(tsk); if (fpvalid) - fpvalid = !fpregs_get(tsk, NULL, 0, - sizeof(struct user_fpu_struct), - fpu, NULL); + fpvalid = !fpregs_get(tsk, NULL, + (struct membuf){fpu, sizeof(*fpu)}); #endif return fpvalid; diff --git a/arch/sh/kernel/ptrace_32.c b/arch/sh/kernel/ptrace_32.c index 64bfb714943e2..5c93bdb6c41a5 100644 --- a/arch/sh/kernel/ptrace_32.c +++ b/arch/sh/kernel/ptrace_32.c @@ -134,26 +134,11 @@ void ptrace_disable(struct task_struct *child) static int genregs_get(struct task_struct *target, const struct user_regset *regset, - unsigned int pos, unsigned int count, - void *kbuf, void __user *ubuf) + struct membuf to) { const struct pt_regs *regs = task_pt_regs(target); - int ret; - ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, - regs->regs, - 0, 16 * sizeof(unsigned long)); - if (!ret) - /* PC, PR, SR, GBR, MACH, MACL, TRA */ - ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, - ®s->pc, - offsetof(struct pt_regs, pc), - sizeof(struct pt_regs)); - if (!ret) - ret = user_regset_copyout_zero(&pos, &count, &kbuf, &ubuf, - sizeof(struct pt_regs), -1); - - return ret; + return membuf_write(&to, regs, sizeof(struct pt_regs)); } static int genregs_set(struct task_struct *target, @@ -182,8 +167,7 @@ static int genregs_set(struct task_struct *target, #ifdef CONFIG_SH_FPU int fpregs_get(struct task_struct *target, const struct user_regset *regset, - unsigned int pos, unsigned int count, - void *kbuf, void __user *ubuf) + struct membuf to) { int ret; @@ -191,12 +175,8 @@ int fpregs_get(struct task_struct *target, if (ret) return ret; - if ((boot_cpu_data.flags & CPU_HAS_FPU)) - return user_regset_copyout(&pos, &count, &kbuf, &ubuf, - &target->thread.xstate->hardfpu, 0, -1); - - return user_regset_copyout(&pos, &count, &kbuf, &ubuf, - &target->thread.xstate->softfpu, 0, -1); + return membuf_write(&to, target->thread.xstate, + sizeof(struct user_fpu_struct)); } static int fpregs_set(struct task_struct *target, @@ -230,20 +210,12 @@ static int fpregs_active(struct task_struct *target, #ifdef CONFIG_SH_DSP static int dspregs_get(struct task_struct *target, const struct user_regset *regset, - unsigned int pos, unsigned int count, - void *kbuf, void __user *ubuf) + struct membuf to) { const struct pt_dspregs *regs = (struct pt_dspregs *)&target->thread.dsp_status.dsp_regs; - int ret; - ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, regs, - 0, sizeof(struct pt_dspregs)); - if (!ret) - ret = user_regset_copyout_zero(&pos, &count, &kbuf, &ubuf, - sizeof(struct pt_dspregs), -1); - - return ret; + return membuf_write(&to, regs, sizeof(struct pt_dspregs)); } static int dspregs_set(struct task_struct *target, @@ -324,7 +296,7 @@ static const struct user_regset sh_regsets[] = { .n = ELF_NGREG, .size = sizeof(long), .align = sizeof(long), - .get = genregs_get, + .regset_get = genregs_get, .set = genregs_set, }, @@ -334,7 +306,7 @@ static const struct user_regset sh_regsets[] = { .n = sizeof(struct user_fpu_struct) / sizeof(long), .size = sizeof(long), .align = sizeof(long), - .get = fpregs_get, + .regset_get = fpregs_get, .set = fpregs_set, .active = fpregs_active, }, @@ -345,7 +317,7 @@ static const struct user_regset sh_regsets[] = { .n = sizeof(struct pt_dspregs) / sizeof(long), .size = sizeof(long), .align = sizeof(long), - .get = dspregs_get, + .regset_get = dspregs_get, .set = dspregs_set, .active = dspregs_active, }, -- GitLab From 3598e9f09608608eea425bf228b98345967dc028 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Tue, 16 Jun 2020 01:10:27 -0400 Subject: [PATCH 0962/1476] arm: switch to ->regset_get() Signed-off-by: Al Viro --- arch/arm/kernel/ptrace.c | 52 ++++++++++------------------------------ 1 file changed, 12 insertions(+), 40 deletions(-) diff --git a/arch/arm/kernel/ptrace.c b/arch/arm/kernel/ptrace.c index d0f7c8896c967..2771e682220b4 100644 --- a/arch/arm/kernel/ptrace.c +++ b/arch/arm/kernel/ptrace.c @@ -569,14 +569,9 @@ out: static int gpr_get(struct task_struct *target, const struct user_regset *regset, - unsigned int pos, unsigned int count, - void *kbuf, void __user *ubuf) + struct membuf to) { - struct pt_regs *regs = task_pt_regs(target); - - return user_regset_copyout(&pos, &count, &kbuf, &ubuf, - regs, - 0, sizeof(*regs)); + return membuf_write(&to, task_pt_regs(target), sizeof(struct pt_regs)); } static int gpr_set(struct task_struct *target, @@ -602,12 +597,10 @@ static int gpr_set(struct task_struct *target, static int fpa_get(struct task_struct *target, const struct user_regset *regset, - unsigned int pos, unsigned int count, - void *kbuf, void __user *ubuf) + struct membuf to) { - return user_regset_copyout(&pos, &count, &kbuf, &ubuf, - &task_thread_info(target)->fpstate, - 0, sizeof(struct user_fp)); + return membuf_write(&to, &task_thread_info(target)->fpstate, + sizeof(struct user_fp)); } static int fpa_set(struct task_struct *target, @@ -642,41 +635,20 @@ static int fpa_set(struct task_struct *target, * vfp_set() ignores this chunk * * 1 word for the FPSCR - * - * The bounds-checking logic built into user_regset_copyout and friends - * means that we can make a simple sequence of calls to map the relevant data - * to/from the specified slice of the user regset structure. */ static int vfp_get(struct task_struct *target, const struct user_regset *regset, - unsigned int pos, unsigned int count, - void *kbuf, void __user *ubuf) + struct membuf to) { - int ret; struct thread_info *thread = task_thread_info(target); struct vfp_hard_struct const *vfp = &thread->vfpstate.hard; - const size_t user_fpregs_offset = offsetof(struct user_vfp, fpregs); const size_t user_fpscr_offset = offsetof(struct user_vfp, fpscr); vfp_sync_hwstate(thread); - ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, - &vfp->fpregs, - user_fpregs_offset, - user_fpregs_offset + sizeof(vfp->fpregs)); - if (ret) - return ret; - - ret = user_regset_copyout_zero(&pos, &count, &kbuf, &ubuf, - user_fpregs_offset + sizeof(vfp->fpregs), - user_fpscr_offset); - if (ret) - return ret; - - return user_regset_copyout(&pos, &count, &kbuf, &ubuf, - &vfp->fpscr, - user_fpscr_offset, - user_fpscr_offset + sizeof(vfp->fpscr)); + membuf_write(&to, vfp->fpregs, sizeof(vfp->fpregs)); + membuf_zero(&to, user_fpscr_offset - sizeof(vfp->fpregs)); + return membuf_store(&to, vfp->fpscr); } /* @@ -739,7 +711,7 @@ static const struct user_regset arm_regsets[] = { .n = ELF_NGREG, .size = sizeof(u32), .align = sizeof(u32), - .get = gpr_get, + .regset_get = gpr_get, .set = gpr_set }, [REGSET_FPR] = { @@ -751,7 +723,7 @@ static const struct user_regset arm_regsets[] = { .n = sizeof(struct user_fp) / sizeof(u32), .size = sizeof(u32), .align = sizeof(u32), - .get = fpa_get, + .regset_get = fpa_get, .set = fpa_set }, #ifdef CONFIG_VFP @@ -764,7 +736,7 @@ static const struct user_regset arm_regsets[] = { .n = ARM_VFPREGS_SIZE / sizeof(u32), .size = sizeof(u32), .align = sizeof(u32), - .get = vfp_get, + .regset_get = vfp_get, .set = vfp_set }, #endif /* CONFIG_VFP */ -- GitLab From 5a806e0a1c4d7c1800453686efafa4410cc137cf Mon Sep 17 00:00:00 2001 From: Al Viro Date: Tue, 16 Jun 2020 02:01:10 -0400 Subject: [PATCH 0963/1476] arc: switch to ->regset_get() NB: it used to do short store; fix is needed earlier in the series. Signed-off-by: Al Viro --- arch/arc/kernel/ptrace.c | 148 +++++++++++++++------------------------ 1 file changed, 58 insertions(+), 90 deletions(-) diff --git a/arch/arc/kernel/ptrace.c b/arch/arc/kernel/ptrace.c index f49a054a1016c..883391977fdfa 100644 --- a/arch/arc/kernel/ptrace.c +++ b/arch/arc/kernel/ptrace.c @@ -18,88 +18,61 @@ static struct callee_regs *task_callee_regs(struct task_struct *tsk) static int genregs_get(struct task_struct *target, const struct user_regset *regset, - unsigned int pos, unsigned int count, - void *kbuf, void __user *ubuf) + struct membuf to) { const struct pt_regs *ptregs = task_pt_regs(target); const struct callee_regs *cregs = task_callee_regs(target); - int ret = 0; unsigned int stop_pc_val; -#define REG_O_CHUNK(START, END, PTR) \ - if (!ret) \ - ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, PTR, \ - offsetof(struct user_regs_struct, START), \ - offsetof(struct user_regs_struct, END)); - -#define REG_O_ONE(LOC, PTR) \ - if (!ret) \ - ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, PTR, \ - offsetof(struct user_regs_struct, LOC), \ - offsetof(struct user_regs_struct, LOC) + 4); - -#define REG_O_ZERO(LOC) \ - if (!ret) \ - ret = user_regset_copyout_zero(&pos, &count, &kbuf, &ubuf, \ - offsetof(struct user_regs_struct, LOC), \ - offsetof(struct user_regs_struct, LOC) + 4); - - REG_O_ZERO(pad); - REG_O_ONE(scratch.bta, &ptregs->bta); - REG_O_ONE(scratch.lp_start, &ptregs->lp_start); - REG_O_ONE(scratch.lp_end, &ptregs->lp_end); - REG_O_ONE(scratch.lp_count, &ptregs->lp_count); - REG_O_ONE(scratch.status32, &ptregs->status32); - REG_O_ONE(scratch.ret, &ptregs->ret); - REG_O_ONE(scratch.blink, &ptregs->blink); - REG_O_ONE(scratch.fp, &ptregs->fp); - REG_O_ONE(scratch.gp, &ptregs->r26); - REG_O_ONE(scratch.r12, &ptregs->r12); - REG_O_ONE(scratch.r11, &ptregs->r11); - REG_O_ONE(scratch.r10, &ptregs->r10); - REG_O_ONE(scratch.r9, &ptregs->r9); - REG_O_ONE(scratch.r8, &ptregs->r8); - REG_O_ONE(scratch.r7, &ptregs->r7); - REG_O_ONE(scratch.r6, &ptregs->r6); - REG_O_ONE(scratch.r5, &ptregs->r5); - REG_O_ONE(scratch.r4, &ptregs->r4); - REG_O_ONE(scratch.r3, &ptregs->r3); - REG_O_ONE(scratch.r2, &ptregs->r2); - REG_O_ONE(scratch.r1, &ptregs->r1); - REG_O_ONE(scratch.r0, &ptregs->r0); - REG_O_ONE(scratch.sp, &ptregs->sp); - - REG_O_ZERO(pad2); - - REG_O_ONE(callee.r25, &cregs->r25); - REG_O_ONE(callee.r24, &cregs->r24); - REG_O_ONE(callee.r23, &cregs->r23); - REG_O_ONE(callee.r22, &cregs->r22); - REG_O_ONE(callee.r21, &cregs->r21); - REG_O_ONE(callee.r20, &cregs->r20); - REG_O_ONE(callee.r19, &cregs->r19); - REG_O_ONE(callee.r18, &cregs->r18); - REG_O_ONE(callee.r17, &cregs->r17); - REG_O_ONE(callee.r16, &cregs->r16); - REG_O_ONE(callee.r15, &cregs->r15); - REG_O_ONE(callee.r14, &cregs->r14); - REG_O_ONE(callee.r13, &cregs->r13); - - REG_O_ONE(efa, &target->thread.fault_address); - - if (!ret) { - if (in_brkpt_trap(ptregs)) { - stop_pc_val = target->thread.fault_address; - pr_debug("\t\tstop_pc (brk-pt)\n"); - } else { - stop_pc_val = ptregs->ret; - pr_debug("\t\tstop_pc (others)\n"); - } - - REG_O_ONE(stop_pc, &stop_pc_val); + membuf_zero(&to, 4); // pad + membuf_store(&to, ptregs->bta); + membuf_store(&to, ptregs->lp_start); + membuf_store(&to, ptregs->lp_end); + membuf_store(&to, ptregs->lp_count); + membuf_store(&to, ptregs->status32); + membuf_store(&to, ptregs->ret); + membuf_store(&to, ptregs->blink); + membuf_store(&to, ptregs->fp); + membuf_store(&to, ptregs->r26); // gp + membuf_store(&to, ptregs->r12); + membuf_store(&to, ptregs->r11); + membuf_store(&to, ptregs->r10); + membuf_store(&to, ptregs->r9); + membuf_store(&to, ptregs->r8); + membuf_store(&to, ptregs->r7); + membuf_store(&to, ptregs->r6); + membuf_store(&to, ptregs->r5); + membuf_store(&to, ptregs->r4); + membuf_store(&to, ptregs->r3); + membuf_store(&to, ptregs->r2); + membuf_store(&to, ptregs->r1); + membuf_store(&to, ptregs->r0); + membuf_store(&to, ptregs->sp); + membuf_zero(&to, 4); // pad2 + membuf_store(&to, cregs->r25); + membuf_store(&to, cregs->r24); + membuf_store(&to, cregs->r23); + membuf_store(&to, cregs->r22); + membuf_store(&to, cregs->r21); + membuf_store(&to, cregs->r20); + membuf_store(&to, cregs->r19); + membuf_store(&to, cregs->r18); + membuf_store(&to, cregs->r17); + membuf_store(&to, cregs->r16); + membuf_store(&to, cregs->r15); + membuf_store(&to, cregs->r14); + membuf_store(&to, cregs->r13); + membuf_store(&to, target->thread.fault_address); // efa + + if (in_brkpt_trap(ptregs)) { + stop_pc_val = target->thread.fault_address; + pr_debug("\t\tstop_pc (brk-pt)\n"); + } else { + stop_pc_val = ptregs->ret; + pr_debug("\t\tstop_pc (others)\n"); } - return ret; + return membuf_store(&to, stop_pc_val); // stop_pc } static int genregs_set(struct task_struct *target, @@ -184,25 +157,20 @@ static int genregs_set(struct task_struct *target, #ifdef CONFIG_ISA_ARCV2 static int arcv2regs_get(struct task_struct *target, const struct user_regset *regset, - unsigned int pos, unsigned int count, - void *kbuf, void __user *ubuf) + struct membuf to) { const struct pt_regs *regs = task_pt_regs(target); - int ret, copy_sz; if (IS_ENABLED(CONFIG_ARC_HAS_ACCL_REGS)) - copy_sz = sizeof(struct user_regs_arcv2); - else - copy_sz = 4; /* r30 only */ + /* + * itemized copy not needed like above as layout of regs (r30,r58,r59) + * is exactly same in kernel (pt_regs) and userspace (user_regs_arcv2) + */ + return membuf_write(&to, ®s->r30, sizeof(struct user_regs_arcv2)); - /* - * itemized copy not needed like above as layout of regs (r30,r58,r59) - * is exactly same in kernel (pt_regs) and userspace (user_regs_arcv2) - */ - ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, ®s->r30, - 0, copy_sz); - return ret; + membuf_write(&to, ®s->r30, 4); /* r30 only */ + return membuf_zero(&to, sizeof(struct user_regs_arcv2) - 4); } static int arcv2regs_set(struct task_struct *target, @@ -237,7 +205,7 @@ static const struct user_regset arc_regsets[] = { .n = ELF_NGREG, .size = sizeof(unsigned long), .align = sizeof(unsigned long), - .get = genregs_get, + .regset_get = genregs_get, .set = genregs_set, }, #ifdef CONFIG_ISA_ARCV2 @@ -246,7 +214,7 @@ static const struct user_regset arc_regsets[] = { .n = ELF_ARCV2REG, .size = sizeof(unsigned long), .align = sizeof(unsigned long), - .get = arcv2regs_get, + .regset_get = arcv2regs_get, .set = arcv2regs_set, }, #endif -- GitLab From 4ff8a356daafaafbf90141ee7a3b8fdc18e560a8 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Tue, 16 Jun 2020 11:20:51 -0400 Subject: [PATCH 0964/1476] ia64: switch to ->regset_get() Signed-off-by: Al Viro --- arch/ia64/kernel/ptrace.c | 149 +++++++++++++++----------------------- 1 file changed, 58 insertions(+), 91 deletions(-) diff --git a/arch/ia64/kernel/ptrace.c b/arch/ia64/kernel/ptrace.c index eae7b094f6082..33ca9fa0fbf55 100644 --- a/arch/ia64/kernel/ptrace.c +++ b/arch/ia64/kernel/ptrace.c @@ -1489,9 +1489,17 @@ access_elf_reg(struct task_struct *target, struct unw_frame_info *info, return access_elf_areg(target, info, addr, data, write_access); } +struct regset_membuf { + struct membuf to; + int ret; +}; + void do_gpregs_get(struct unw_frame_info *info, void *arg) { - struct regset_getset *dst = arg; + struct regset_membuf *dst = arg; + struct membuf to = dst->to; + unsigned int n; + elf_greg_t reg; if (unw_unwind_to_user(info) < 0) return; @@ -1509,35 +1517,13 @@ void do_gpregs_get(struct unw_frame_info *info, void *arg) /* Skip r0 */ - if (dst->count > 0 && dst->pos < ELF_GR_OFFSET(1)) { - dst->ret = user_regset_copyout_zero(&dst->pos, &dst->count, - &dst->u.get.kbuf, - &dst->u.get.ubuf, - 0, ELF_GR_OFFSET(1)); - if (dst->ret) + membuf_zero(&to, 8); + for (n = 8; to.left && n < ELF_AR_END_OFFSET; n += 8) { + if (access_elf_reg(info->task, info, n, ®, 0) < 0) { + dst->ret = -EIO; return; - } - - while (dst->count && dst->pos < ELF_AR_END_OFFSET) { - unsigned int n, from, to; - elf_greg_t tmp[16]; - - from = dst->pos; - to = from + min(dst->count, (unsigned)sizeof(tmp)); - if (to > ELF_AR_END_OFFSET) - to = ELF_AR_END_OFFSET; - for (n = 0; from < to; from += sizeof(elf_greg_t), n++) { - if (access_elf_reg(dst->target, info, from, - &tmp[n], 0) < 0) { - dst->ret = -EIO; - return; - } } - dst->ret = user_regset_copyout(&dst->pos, &dst->count, - &dst->u.get.kbuf, &dst->u.get.ubuf, tmp, - dst->pos, to); - if (dst->ret) - return; + membuf_store(&to, reg); } } @@ -1588,60 +1574,36 @@ void do_gpregs_set(struct unw_frame_info *info, void *arg) void do_fpregs_get(struct unw_frame_info *info, void *arg) { - struct regset_getset *dst = arg; - struct task_struct *task = dst->target; - elf_fpreg_t tmp[30]; - int index, min_copy, i; + struct task_struct *task = info->task; + struct regset_membuf *dst = arg; + struct membuf to = dst->to; + elf_fpreg_t reg; + unsigned int n; if (unw_unwind_to_user(info) < 0) return; /* Skip pos 0 and 1 */ - if (dst->count > 0 && dst->pos < ELF_FP_OFFSET(2)) { - dst->ret = user_regset_copyout_zero(&dst->pos, &dst->count, - &dst->u.get.kbuf, - &dst->u.get.ubuf, - 0, ELF_FP_OFFSET(2)); - if (dst->count == 0 || dst->ret) - return; - } + membuf_zero(&to, 2 * sizeof(elf_fpreg_t)); /* fr2-fr31 */ - if (dst->count > 0 && dst->pos < ELF_FP_OFFSET(32)) { - index = (dst->pos - ELF_FP_OFFSET(2)) / sizeof(elf_fpreg_t); - - min_copy = min(((unsigned int)ELF_FP_OFFSET(32)), - dst->pos + dst->count); - for (i = dst->pos; i < min_copy; i += sizeof(elf_fpreg_t), - index++) - if (unw_get_fr(info, i / sizeof(elf_fpreg_t), - &tmp[index])) { - dst->ret = -EIO; - return; - } - dst->ret = user_regset_copyout(&dst->pos, &dst->count, - &dst->u.get.kbuf, &dst->u.get.ubuf, tmp, - ELF_FP_OFFSET(2), ELF_FP_OFFSET(32)); - if (dst->count == 0 || dst->ret) + for (n = 2; to.left && n < 32; n++) { + if (unw_get_fr(info, n, ®)) { + dst->ret = -EIO; return; + } + membuf_write(&to, ®, sizeof(reg)); } /* fph */ - if (dst->count > 0) { - ia64_flush_fph(dst->target); - if (task->thread.flags & IA64_THREAD_FPH_VALID) - dst->ret = user_regset_copyout( - &dst->pos, &dst->count, - &dst->u.get.kbuf, &dst->u.get.ubuf, - &dst->target->thread.fph, - ELF_FP_OFFSET(32), -1); - else - /* Zero fill instead. */ - dst->ret = user_regset_copyout_zero( - &dst->pos, &dst->count, - &dst->u.get.kbuf, &dst->u.get.ubuf, - ELF_FP_OFFSET(32), -1); - } + if (!to.left) + return; + + ia64_flush_fph(task); + if (task->thread.flags & IA64_THREAD_FPH_VALID) + membuf_write(&to, &task->thread.fph, 96 * sizeof(reg)); + else + membuf_zero(&to, 96 * sizeof(reg)); } void do_fpregs_set(struct unw_frame_info *info, void *arg) @@ -1717,6 +1679,20 @@ void do_fpregs_set(struct unw_frame_info *info, void *arg) } } +static void +unwind_and_call(void (*call)(struct unw_frame_info *, void *), + struct task_struct *target, void *data) +{ + if (target == current) + unw_init_running(call, data); + else { + struct unw_frame_info info; + memset(&info, 0, sizeof(info)); + unw_init_from_blocked_task(&info, target); + (*call)(&info, data); + } +} + static int do_regset_call(void (*call)(struct unw_frame_info *, void *), struct task_struct *target, @@ -1728,27 +1704,18 @@ do_regset_call(void (*call)(struct unw_frame_info *, void *), .pos = pos, .count = count, .u.set = { .kbuf = kbuf, .ubuf = ubuf }, .ret = 0 }; - - if (target == current) - unw_init_running(call, &info); - else { - struct unw_frame_info ufi; - memset(&ufi, 0, sizeof(ufi)); - unw_init_from_blocked_task(&ufi, target); - (*call)(&ufi, &info); - } - + unwind_and_call(call, target, &info); return info.ret; } static int gpregs_get(struct task_struct *target, const struct user_regset *regset, - unsigned int pos, unsigned int count, - void *kbuf, void __user *ubuf) + struct membuf to) { - return do_regset_call(do_gpregs_get, target, regset, pos, count, - kbuf, ubuf); + struct regset_membuf info = {.to = to}; + unwind_and_call(do_gpregs_get, target, &info); + return info.ret; } static int gpregs_set(struct task_struct *target, @@ -1790,11 +1757,11 @@ fpregs_active(struct task_struct *target, const struct user_regset *regset) static int fpregs_get(struct task_struct *target, const struct user_regset *regset, - unsigned int pos, unsigned int count, - void *kbuf, void __user *ubuf) + struct membuf to) { - return do_regset_call(do_fpregs_get, target, regset, pos, count, - kbuf, ubuf); + struct regset_membuf info = {.to = to}; + unwind_and_call(do_fpregs_get, target, &info); + return info.ret; } static int fpregs_set(struct task_struct *target, @@ -2033,14 +2000,14 @@ static const struct user_regset native_regsets[] = { .core_note_type = NT_PRSTATUS, .n = ELF_NGREG, .size = sizeof(elf_greg_t), .align = sizeof(elf_greg_t), - .get = gpregs_get, .set = gpregs_set, + .regset_get = gpregs_get, .set = gpregs_set, .writeback = gpregs_writeback }, { .core_note_type = NT_PRFPREG, .n = ELF_NFPREG, .size = sizeof(elf_fpreg_t), .align = sizeof(elf_fpreg_t), - .get = fpregs_get, .set = fpregs_set, .active = fpregs_active + .regset_get = fpregs_get, .set = fpregs_set, .active = fpregs_active }, }; -- GitLab From c7a0faa21e7e795778a978e8f894d3ae0fce60cf Mon Sep 17 00:00:00 2001 From: Al Viro Date: Tue, 16 Jun 2020 11:36:14 -0400 Subject: [PATCH 0965/1476] c6x: switch to ->regset_get() Signed-off-by: Al Viro --- arch/c6x/kernel/ptrace.c | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/arch/c6x/kernel/ptrace.c b/arch/c6x/kernel/ptrace.c index 67af1562da866..3cdaa8cf0ed6f 100644 --- a/arch/c6x/kernel/ptrace.c +++ b/arch/c6x/kernel/ptrace.c @@ -57,14 +57,9 @@ static inline int put_reg(struct task_struct *task, static int gpr_get(struct task_struct *target, const struct user_regset *regset, - unsigned int pos, unsigned int count, - void *kbuf, void __user *ubuf) + struct membuf to) { - struct pt_regs *regs = task_pt_regs(target); - - return user_regset_copyout(&pos, &count, &kbuf, &ubuf, - regs, - 0, sizeof(*regs)); + return membuf_write(&to, task_pt_regs(target), sizeof(struct pt_regs)); } enum c6x_regset { @@ -77,7 +72,7 @@ static const struct user_regset c6x_regsets[] = { .n = ELF_NGREG, .size = sizeof(u32), .align = sizeof(u32), - .get = gpr_get, + .regset_get = gpr_get, }, }; -- GitLab From 2cb6cd495d1780eb257e1626f82d4557c4b88ed5 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Tue, 16 Jun 2020 14:04:53 -0400 Subject: [PATCH 0966/1476] riscv: switch to ->regset_get() Note: riscv_fpr_get() used to forget to zero-pad at the end. Not worth -stable... Signed-off-by: Al Viro --- arch/riscv/kernel/ptrace.c | 33 +++++++++++---------------------- 1 file changed, 11 insertions(+), 22 deletions(-) diff --git a/arch/riscv/kernel/ptrace.c b/arch/riscv/kernel/ptrace.c index 444dc7b0fd78c..2d6395f5ad54f 100644 --- a/arch/riscv/kernel/ptrace.c +++ b/arch/riscv/kernel/ptrace.c @@ -30,13 +30,10 @@ enum riscv_regset { static int riscv_gpr_get(struct task_struct *target, const struct user_regset *regset, - unsigned int pos, unsigned int count, - void *kbuf, void __user *ubuf) + struct membuf to) { - struct pt_regs *regs; - - regs = task_pt_regs(target); - return user_regset_copyout(&pos, &count, &kbuf, &ubuf, regs, 0, -1); + return membuf_write(&to, task_pt_regs(target), + sizeof(struct user_regs_struct)); } static int riscv_gpr_set(struct task_struct *target, @@ -55,21 +52,13 @@ static int riscv_gpr_set(struct task_struct *target, #ifdef CONFIG_FPU static int riscv_fpr_get(struct task_struct *target, const struct user_regset *regset, - unsigned int pos, unsigned int count, - void *kbuf, void __user *ubuf) + struct membuf to) { - int ret; struct __riscv_d_ext_state *fstate = &target->thread.fstate; - ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, fstate, 0, - offsetof(struct __riscv_d_ext_state, fcsr)); - if (!ret) { - ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, fstate, 0, - offsetof(struct __riscv_d_ext_state, fcsr) + - sizeof(fstate->fcsr)); - } - - return ret; + membuf_write(&to, fstate, offsetof(struct __riscv_d_ext_state, fcsr)); + membuf_store(&to, fstate->fcsr); + return membuf_zero(&to, 4); // explicitly pad } static int riscv_fpr_set(struct task_struct *target, @@ -98,8 +87,8 @@ static const struct user_regset riscv_user_regset[] = { .n = ELF_NGREG, .size = sizeof(elf_greg_t), .align = sizeof(elf_greg_t), - .get = &riscv_gpr_get, - .set = &riscv_gpr_set, + .regset_get = riscv_gpr_get, + .set = riscv_gpr_set, }, #ifdef CONFIG_FPU [REGSET_F] = { @@ -107,8 +96,8 @@ static const struct user_regset riscv_user_regset[] = { .n = ELF_NFPREG, .size = sizeof(elf_fpreg_t), .align = sizeof(elf_fpreg_t), - .get = &riscv_fpr_get, - .set = &riscv_fpr_set, + .regset_get = riscv_fpr_get, + .set = riscv_fpr_set, }, #endif }; -- GitLab From f04b2b7cde01cecad58e1792ea5ae8576c1e8bee Mon Sep 17 00:00:00 2001 From: Al Viro Date: Tue, 16 Jun 2020 14:09:18 -0400 Subject: [PATCH 0967/1476] openrisc: switch to ->regset_get() Signed-off-by: Al Viro --- arch/openrisc/kernel/ptrace.c | 26 ++++++-------------------- 1 file changed, 6 insertions(+), 20 deletions(-) diff --git a/arch/openrisc/kernel/ptrace.c b/arch/openrisc/kernel/ptrace.c index c8f47a6237546..4d60ae2a12fac 100644 --- a/arch/openrisc/kernel/ptrace.c +++ b/arch/openrisc/kernel/ptrace.c @@ -44,29 +44,15 @@ */ static int genregs_get(struct task_struct *target, const struct user_regset *regset, - unsigned int pos, unsigned int count, - void *kbuf, void __user * ubuf) + struct membuf to) { const struct pt_regs *regs = task_pt_regs(target); - int ret; /* r0 */ - ret = user_regset_copyout_zero(&pos, &count, &kbuf, &ubuf, 0, 4); - - if (!ret) - ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, - regs->gpr+1, 4, 4*32); - if (!ret) - ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, - ®s->pc, 4*32, 4*33); - if (!ret) - ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, - ®s->sr, 4*33, 4*34); - if (!ret) - ret = user_regset_copyout_zero(&pos, &count, &kbuf, &ubuf, - 4*34, -1); - - return ret; + membuf_zero(&to, 4); + membuf_write(&to, regs->gpr + 1, 31 * 4); + membuf_store(&to, regs->pc); + return membuf_store(&to, regs->sr); } /* @@ -114,7 +100,7 @@ static const struct user_regset or1k_regsets[] = { .n = ELF_NGREG, .size = sizeof(long), .align = sizeof(long), - .get = genregs_get, + .regset_get = genregs_get, .set = genregs_set, }, }; -- GitLab From 4e5e7ea48e817a6bc8156ed78f819fc70eb6eca2 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Tue, 16 Jun 2020 14:13:39 -0400 Subject: [PATCH 0968/1476] h8300: switch to ->regset_get() Signed-off-by: Al Viro --- arch/h8300/kernel/ptrace.c | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/arch/h8300/kernel/ptrace.c b/arch/h8300/kernel/ptrace.c index 0dc1c8f622bc3..a11db009d0ea0 100644 --- a/arch/h8300/kernel/ptrace.c +++ b/arch/h8300/kernel/ptrace.c @@ -87,20 +87,15 @@ int h8300_put_reg(struct task_struct *task, int regno, unsigned long data) static int regs_get(struct task_struct *target, const struct user_regset *regset, - unsigned int pos, unsigned int count, - void *kbuf, void __user *ubuf) + struct membuf to) { int r; - struct user_regs_struct regs; - long *reg = (long *)®s; - /* build user regs in buffer */ - BUILD_BUG_ON(sizeof(regs) % sizeof(long) != 0); - for (r = 0; r < sizeof(regs) / sizeof(long); r++) - *reg++ = h8300_get_reg(target, r); + BUILD_BUG_ON(sizeof(struct user_regs_struct) % sizeof(long) != 0); + for (r = 0; r < ELF_NGREG; r++) + membuf_store(&to, h8300_get_reg(target, r)); - return user_regset_copyout(&pos, &count, &kbuf, &ubuf, - ®s, 0, sizeof(regs)); + return 0; } static int regs_set(struct task_struct *target, @@ -139,7 +134,7 @@ static const struct user_regset h8300_regsets[] = { .n = ELF_NGREG, .size = sizeof(long), .align = sizeof(long), - .get = regs_get, + .regset_get = regs_get, .set = regs_set, }, }; -- GitLab From 8320514c91beaef4aa9994a638ff90d7d3a279d6 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Tue, 16 Jun 2020 14:21:50 -0400 Subject: [PATCH 0969/1476] hexagon: switch to ->regset_get() Signed-off-by: Al Viro --- arch/hexagon/kernel/ptrace.c | 62 ++++++++++++------------------------ 1 file changed, 21 insertions(+), 41 deletions(-) diff --git a/arch/hexagon/kernel/ptrace.c b/arch/hexagon/kernel/ptrace.c index dcbf7ea960cc8..a5a89e944257c 100644 --- a/arch/hexagon/kernel/ptrace.c +++ b/arch/hexagon/kernel/ptrace.c @@ -35,58 +35,38 @@ void user_disable_single_step(struct task_struct *child) static int genregs_get(struct task_struct *target, const struct user_regset *regset, - unsigned int pos, unsigned int count, - void *kbuf, void __user *ubuf) + srtuct membuf to) { - int ret; - unsigned int dummy; struct pt_regs *regs = task_pt_regs(target); - - if (!regs) - return -EIO; - /* The general idea here is that the copyout must happen in * exactly the same order in which the userspace expects these * regs. Now, the sequence in userspace does not match the * sequence in the kernel, so everything past the 32 gprs * happens one at a time. */ - ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, - ®s->r00, 0, 32*sizeof(unsigned long)); - -#define ONEXT(KPT_REG, USR_REG) \ - if (!ret) \ - ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, \ - KPT_REG, offsetof(struct user_regs_struct, USR_REG), \ - offsetof(struct user_regs_struct, USR_REG) + \ - sizeof(unsigned long)); - + membuf_write(&to, ®s->r00, 32*sizeof(unsigned long)); /* Must be exactly same sequence as struct user_regs_struct */ - ONEXT(®s->sa0, sa0); - ONEXT(®s->lc0, lc0); - ONEXT(®s->sa1, sa1); - ONEXT(®s->lc1, lc1); - ONEXT(®s->m0, m0); - ONEXT(®s->m1, m1); - ONEXT(®s->usr, usr); - ONEXT(®s->preds, p3_0); - ONEXT(®s->gp, gp); - ONEXT(®s->ugp, ugp); - ONEXT(&pt_elr(regs), pc); - dummy = pt_cause(regs); - ONEXT(&dummy, cause); - ONEXT(&pt_badva(regs), badva); + membuf_store(&to, regs->sa0); + membuf_store(&to, regs->lc0); + membuf_store(&to, regs->sa1); + membuf_store(&to, regs->lc1); + membuf_store(&to, regs->m0); + membuf_store(&to, regs->m1); + membuf_store(&to, regs->usr); + membuf_store(&to, regs->p3_0); + membuf_store(&to, regs->gp); + membuf_store(&to, regs->ugp); + membuf_store(&to, pt_elr(regs)); // pc + membuf_store(&to, (unsigned long)pt_cause(regs)); // cause + membuf_store(&to, pt_badva(regs)); // badva #if CONFIG_HEXAGON_ARCH_VERSION >=4 - ONEXT(®s->cs0, cs0); - ONEXT(®s->cs1, cs1); + membuf_store(&to, regs->cs0); + membuf_store(&to, regs->cs1); + return membuf_zero(&to, sizeof(unsigned long)); +#else + return membuf_zero(&to, 3 * sizeof(unsigned long)); #endif - - /* Pad the rest with zeros, if needed */ - if (!ret) - ret = user_regset_copyout_zero(&pos, &count, &kbuf, &ubuf, - offsetof(struct user_regs_struct, pad1), -1); - return ret; } static int genregs_set(struct task_struct *target, @@ -159,7 +139,7 @@ static const struct user_regset hexagon_regsets[] = { .n = ELF_NGREG, .size = sizeof(unsigned long), .align = sizeof(unsigned long), - .get = genregs_get, + .regset_get = genregs_get, .set = genregs_set, }, }; -- GitLab From 6bda7688878b3e7bc328ae26e7df4f4298c63dc9 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Tue, 16 Jun 2020 14:28:44 -0400 Subject: [PATCH 0970/1476] nios2: switch to ->regset_get() Signed-off-by: Al Viro --- arch/nios2/kernel/ptrace.c | 51 +++++++++++--------------------------- 1 file changed, 15 insertions(+), 36 deletions(-) diff --git a/arch/nios2/kernel/ptrace.c b/arch/nios2/kernel/ptrace.c index de97bcb7dd443..a6ea9e1b4f616 100644 --- a/arch/nios2/kernel/ptrace.c +++ b/arch/nios2/kernel/ptrace.c @@ -21,45 +21,24 @@ static int genregs_get(struct task_struct *target, const struct user_regset *regset, - unsigned int pos, unsigned int count, - void *kbuf, void __user *ubuf) + struct membuf to) { const struct pt_regs *regs = task_pt_regs(target); const struct switch_stack *sw = (struct switch_stack *)regs - 1; - int ret = 0; - -#define REG_O_ZERO_RANGE(START, END) \ - if (!ret) \ - ret = user_regset_copyout_zero(&pos, &count, &kbuf, &ubuf, \ - START * 4, (END * 4) + 4); - -#define REG_O_ONE(PTR, LOC) \ - if (!ret) \ - ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, PTR, \ - LOC * 4, (LOC * 4) + 4); -#define REG_O_RANGE(PTR, START, END) \ - if (!ret) \ - ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, PTR, \ - START * 4, (END * 4) + 4); - - REG_O_ZERO_RANGE(PTR_R0, PTR_R0); - REG_O_RANGE(®s->r1, PTR_R1, PTR_R7); - REG_O_RANGE(®s->r8, PTR_R8, PTR_R15); - REG_O_RANGE(sw, PTR_R16, PTR_R23); - REG_O_ZERO_RANGE(PTR_R24, PTR_R25); /* et and bt */ - REG_O_ONE(®s->gp, PTR_GP); - REG_O_ONE(®s->sp, PTR_SP); - REG_O_ONE(®s->fp, PTR_FP); - REG_O_ONE(®s->ea, PTR_EA); - REG_O_ZERO_RANGE(PTR_BA, PTR_BA); - REG_O_ONE(®s->ra, PTR_RA); - REG_O_ONE(®s->ea, PTR_PC); /* use ea for PC */ - if (!ret) - ret = user_regset_copyout_zero(&pos, &count, &kbuf, &ubuf, - PTR_STATUS * 4, -1); - - return ret; + membuf_zero(&to, 4); // R0 + membuf_write(&to, ®s->r1, 7 * 4); // R1..R7 + membuf_write(&to, ®s->r8, 8 * 4); // R8..R15 + membuf_write(&to, sw, 8 * 4); // R16..R23 + membuf_zero(&to, 2 * 4); /* et and bt */ + membuf_store(&to, regs->gp); + membuf_store(&to, regs->sp); + membuf_store(&to, regs->fp); + membuf_store(&to, regs->ea); + membuf_zero(&to, 4); // PTR_BA + membuf_store(&to, regs->ra); + membuf_store(&to, regs->ea); /* use ea for PC */ + return membuf_zero(&to, (NUM_PTRACE_REG - PTR_PC) * 4); } /* @@ -121,7 +100,7 @@ static const struct user_regset nios2_regsets[] = { .n = NUM_PTRACE_REG, .size = sizeof(unsigned long), .align = sizeof(unsigned long), - .get = genregs_get, + .regset_get = genregs_get, .set = genregs_set, } }; -- GitLab From 1b5b456e39a0c9271575746197dd463be27463f6 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Tue, 16 Jun 2020 14:30:59 -0400 Subject: [PATCH 0971/1476] nds32: switch to ->regset_get() Signed-off-by: Al Viro --- arch/nds32/kernel/ptrace.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/arch/nds32/kernel/ptrace.c b/arch/nds32/kernel/ptrace.c index eaaf7a999b202..d0eda870fbc22 100644 --- a/arch/nds32/kernel/ptrace.c +++ b/arch/nds32/kernel/ptrace.c @@ -13,11 +13,10 @@ enum nds32_regset { static int gpr_get(struct task_struct *target, const struct user_regset *regset, - unsigned int pos, unsigned int count, - void *kbuf, void __user * ubuf) + struct membuf to) { - struct user_pt_regs *uregs = &task_pt_regs(target)->user_regs; - return user_regset_copyout(&pos, &count, &kbuf, &ubuf, uregs, 0, -1); + return membuf_write(&to, &task_pt_regs(target)->user_regs, + sizeof(struct user_pt_regs)); } static int gpr_set(struct task_struct *target, const struct user_regset *regset, @@ -41,7 +40,7 @@ static const struct user_regset nds32_regsets[] = { .n = sizeof(struct user_pt_regs) / sizeof(u32), .size = sizeof(elf_greg_t), .align = sizeof(elf_greg_t), - .get = gpr_get, + .regset_get = gpr_get, .set = gpr_set} }; -- GitLab From bd0409a856bf62d7eeb3807dba534abde067143d Mon Sep 17 00:00:00 2001 From: Al Viro Date: Tue, 16 Jun 2020 14:40:05 -0400 Subject: [PATCH 0972/1476] parisc: switch to ->regset_get() Signed-off-by: Al Viro --- arch/parisc/kernel/ptrace.c | 84 +++++++------------------------------ 1 file changed, 16 insertions(+), 68 deletions(-) diff --git a/arch/parisc/kernel/ptrace.c b/arch/parisc/kernel/ptrace.c index b51418ad86551..2127974982df9 100644 --- a/arch/parisc/kernel/ptrace.c +++ b/arch/parisc/kernel/ptrace.c @@ -391,31 +391,11 @@ void do_syscall_trace_exit(struct pt_regs *regs) static int fpr_get(struct task_struct *target, const struct user_regset *regset, - unsigned int pos, unsigned int count, - void *kbuf, void __user *ubuf) + struct membuf to) { struct pt_regs *regs = task_regs(target); - __u64 *k = kbuf; - __u64 __user *u = ubuf; - __u64 reg; - - pos /= sizeof(reg); - count /= sizeof(reg); - - if (kbuf) - for (; count > 0 && pos < ELF_NFPREG; --count) - *k++ = regs->fr[pos++]; - else - for (; count > 0 && pos < ELF_NFPREG; --count) - if (__put_user(regs->fr[pos++], u++)) - return -EFAULT; - kbuf = k; - ubuf = u; - pos *= sizeof(reg); - count *= sizeof(reg); - return user_regset_copyout_zero(&pos, &count, &kbuf, &ubuf, - ELF_NFPREG * sizeof(reg), -1); + return membuf_write(&to, regs->fr, ELF_NFPREG * sizeof(__u64)); } static int fpr_set(struct task_struct *target, @@ -527,30 +507,14 @@ static void set_reg(struct pt_regs *regs, int num, unsigned long val) static int gpr_get(struct task_struct *target, const struct user_regset *regset, - unsigned int pos, unsigned int count, - void *kbuf, void __user *ubuf) + struct membuf to) { struct pt_regs *regs = task_regs(target); - unsigned long *k = kbuf; - unsigned long __user *u = ubuf; - unsigned long reg; + unsigned int pos; - pos /= sizeof(reg); - count /= sizeof(reg); - - if (kbuf) - for (; count > 0 && pos < ELF_NGREG; --count) - *k++ = get_reg(regs, pos++); - else - for (; count > 0 && pos < ELF_NGREG; --count) - if (__put_user(get_reg(regs, pos++), u++)) - return -EFAULT; - kbuf = k; - ubuf = u; - pos *= sizeof(reg); - count *= sizeof(reg); - return user_regset_copyout_zero(&pos, &count, &kbuf, &ubuf, - ELF_NGREG * sizeof(reg), -1); + for (pos = 0; pos < ELF_NGREG; pos++) + membuf_store(&to, get_reg(regs, pos)); + return 0; } static int gpr_set(struct task_struct *target, @@ -588,12 +552,12 @@ static const struct user_regset native_regsets[] = { [REGSET_GENERAL] = { .core_note_type = NT_PRSTATUS, .n = ELF_NGREG, .size = sizeof(long), .align = sizeof(long), - .get = gpr_get, .set = gpr_set + .regset_get = gpr_get, .set = gpr_set }, [REGSET_FP] = { .core_note_type = NT_PRFPREG, .n = ELF_NFPREG, .size = sizeof(__u64), .align = sizeof(__u64), - .get = fpr_get, .set = fpr_set + .regset_get = fpr_get, .set = fpr_set } }; @@ -607,31 +571,15 @@ static const struct user_regset_view user_parisc_native_view = { static int gpr32_get(struct task_struct *target, const struct user_regset *regset, - unsigned int pos, unsigned int count, - void *kbuf, void __user *ubuf) + struct membuf to) { struct pt_regs *regs = task_regs(target); - compat_ulong_t *k = kbuf; - compat_ulong_t __user *u = ubuf; - compat_ulong_t reg; + unsigned int pos; - pos /= sizeof(reg); - count /= sizeof(reg); + for (pos = 0; pos < ELF_NGREG; pos++) + membuf_store(&to, (compat_ulong_t)get_reg(regs, pos)); - if (kbuf) - for (; count > 0 && pos < ELF_NGREG; --count) - *k++ = get_reg(regs, pos++); - else - for (; count > 0 && pos < ELF_NGREG; --count) - if (__put_user((compat_ulong_t) get_reg(regs, pos++), u++)) - return -EFAULT; - - kbuf = k; - ubuf = u; - pos *= sizeof(reg); - count *= sizeof(reg); - return user_regset_copyout_zero(&pos, &count, &kbuf, &ubuf, - ELF_NGREG * sizeof(reg), -1); + return 0; } static int gpr32_set(struct task_struct *target, @@ -672,12 +620,12 @@ static const struct user_regset compat_regsets[] = { [REGSET_GENERAL] = { .core_note_type = NT_PRSTATUS, .n = ELF_NGREG, .size = sizeof(compat_long_t), .align = sizeof(compat_long_t), - .get = gpr32_get, .set = gpr32_set + .regset_get = gpr32_get, .set = gpr32_set }, [REGSET_FP] = { .core_note_type = NT_PRFPREG, .n = ELF_NFPREG, .size = sizeof(__u64), .align = sizeof(__u64), - .get = fpr_get, .set = fpr_set + .regset_get = fpr_get, .set = fpr_set } }; -- GitLab From a6ea5b458815405e06df32a55aae7d2fa2f98c76 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Tue, 16 Jun 2020 14:43:54 -0400 Subject: [PATCH 0973/1476] xtensa: switch to ->regset_get() Signed-off-by: Al Viro --- arch/xtensa/kernel/ptrace.c | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/arch/xtensa/kernel/ptrace.c b/arch/xtensa/kernel/ptrace.c index b4c07bd890fef..620b8863b06e1 100644 --- a/arch/xtensa/kernel/ptrace.c +++ b/arch/xtensa/kernel/ptrace.c @@ -37,8 +37,7 @@ static int gpr_get(struct task_struct *target, const struct user_regset *regset, - unsigned int pos, unsigned int count, - void *kbuf, void __user *ubuf) + struct membuf to) { struct pt_regs *regs = task_pt_regs(target); struct user_pt_regs newregs = { @@ -60,8 +59,7 @@ static int gpr_get(struct task_struct *target, regs->areg, (WSBITS - regs->windowbase) * 16); - return user_regset_copyout(&pos, &count, &kbuf, &ubuf, - &newregs, 0, -1); + return membuf_write(&to, &newregs, sizeof(newregs)); } static int gpr_set(struct task_struct *target, @@ -115,8 +113,7 @@ static int gpr_set(struct task_struct *target, static int tie_get(struct task_struct *target, const struct user_regset *regset, - unsigned int pos, unsigned int count, - void *kbuf, void __user *ubuf) + struct membuf to) { int ret; struct pt_regs *regs = task_pt_regs(target); @@ -141,8 +138,7 @@ static int tie_get(struct task_struct *target, newregs->cp6 = ti->xtregs_cp.cp6; newregs->cp7 = ti->xtregs_cp.cp7; #endif - ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, - newregs, 0, -1); + ret = membuf_write(&to, newregs, sizeof(*newregs)); kfree(newregs); return ret; } @@ -197,7 +193,7 @@ static const struct user_regset xtensa_regsets[] = { .n = sizeof(struct user_pt_regs) / sizeof(u32), .size = sizeof(u32), .align = sizeof(u32), - .get = gpr_get, + .regset_get = gpr_get, .set = gpr_set, }, [REGSET_TIE] = { @@ -205,7 +201,7 @@ static const struct user_regset xtensa_regsets[] = { .n = sizeof(elf_xtregs_t) / sizeof(u32), .size = sizeof(u32), .align = sizeof(u32), - .get = tie_get, + .regset_get = tie_get, .set = tie_set, }, }; -- GitLab From dcad7854fcce6a2d49b6a3ead5bbefeff047e559 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Tue, 16 Jun 2020 15:28:29 -0400 Subject: [PATCH 0974/1476] csky: switch to ->regset_get() NB: WTF is fpregs_get() playing at??? Signed-off-by: Al Viro --- arch/csky/kernel/ptrace.c | 24 ++++++++++-------------- 1 file changed, 10 insertions(+), 14 deletions(-) diff --git a/arch/csky/kernel/ptrace.c b/arch/csky/kernel/ptrace.c index 944ca2fdcdd9d..6d93db8174291 100644 --- a/arch/csky/kernel/ptrace.c +++ b/arch/csky/kernel/ptrace.c @@ -76,17 +76,14 @@ enum csky_regset { static int gpr_get(struct task_struct *target, const struct user_regset *regset, - unsigned int pos, unsigned int count, - void *kbuf, void __user *ubuf) + struct membuf to) { - struct pt_regs *regs; - - regs = task_pt_regs(target); + struct pt_regs *regs = task_pt_regs(target); /* Abiv1 regs->tls is fake and we need sync here. */ regs->tls = task_thread_info(target)->tp_value; - return user_regset_copyout(&pos, &count, &kbuf, &ubuf, regs, 0, -1); + return membuf_write(&to, regs, sizeof(regs)); } static int gpr_set(struct task_struct *target, @@ -114,8 +111,7 @@ static int gpr_set(struct task_struct *target, static int fpr_get(struct task_struct *target, const struct user_regset *regset, - unsigned int pos, unsigned int count, - void *kbuf, void __user *ubuf) + struct membuf to) { struct user_fp *regs = (struct user_fp *)&target->thread.user_fp; @@ -131,9 +127,9 @@ static int fpr_get(struct task_struct *target, for (i = 0; i < 32; i++) tmp.vr[64 + i] = regs->vr[32 + i]; - return user_regset_copyout(&pos, &count, &kbuf, &ubuf, &tmp, 0, -1); + return membuf_write(&to, &tmp, sizeof(tmp)); #else - return user_regset_copyout(&pos, &count, &kbuf, &ubuf, regs, 0, -1); + return membuf_write(&to, regs, sizeof(*regs)); #endif } @@ -173,16 +169,16 @@ static const struct user_regset csky_regsets[] = { .n = sizeof(struct pt_regs) / sizeof(u32), .size = sizeof(u32), .align = sizeof(u32), - .get = &gpr_get, - .set = &gpr_set, + .regset_get = gpr_get, + .set = gpr_set, }, [REGSET_FPR] = { .core_note_type = NT_PRFPREG, .n = sizeof(struct user_fp) / sizeof(u32), .size = sizeof(u32), .align = sizeof(u32), - .get = &fpr_get, - .set = &fpr_set, + .regset_get = fpr_get, + .set = fpr_set, }, }; -- GitLab From 1e6986c9db21265bac1435a344b4446c51a3f4d8 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Tue, 16 Jun 2020 15:34:20 -0400 Subject: [PATCH 0975/1476] regset: kill ->get() no instances left Signed-off-by: Al Viro --- include/linux/regset.h | 22 ---------------------- kernel/regset.c | 24 +++++------------------- 2 files changed, 5 insertions(+), 41 deletions(-) diff --git a/include/linux/regset.h b/include/linux/regset.h index f6125a7d949dc..2a4a555b16172 100644 --- a/include/linux/regset.h +++ b/include/linux/regset.h @@ -82,27 +82,6 @@ static inline int membuf_write(struct membuf *s, const void *v, size_t size) typedef int user_regset_active_fn(struct task_struct *target, const struct user_regset *regset); -/** - * user_regset_get_fn - type of @get function in &struct user_regset - * @target: thread being examined - * @regset: regset being examined - * @pos: offset into the regset data to access, in bytes - * @count: amount of data to copy, in bytes - * @kbuf: if not %NULL, a kernel-space pointer to copy into - * @ubuf: if @kbuf is %NULL, a user-space pointer to copy into - * - * Fetch register values. Return %0 on success; -%EIO or -%ENODEV - * are usual failure returns. The @pos and @count values are in - * bytes, but must be properly aligned. If @kbuf is non-null, that - * buffer is used and @ubuf is ignored. If @kbuf is %NULL, then - * ubuf gives a userland pointer to access directly, and an -%EFAULT - * return value is possible. - */ -typedef int user_regset_get_fn(struct task_struct *target, - const struct user_regset *regset, - unsigned int pos, unsigned int count, - void *kbuf, void __user *ubuf); - typedef int user_regset_get2_fn(struct task_struct *target, const struct user_regset *regset, struct membuf to); @@ -235,7 +214,6 @@ typedef unsigned int user_regset_get_size_fn(struct task_struct *target, * omitted when there is an @active function and it returns zero. */ struct user_regset { - user_regset_get_fn *get; user_regset_get2_fn *regset_get; user_regset_set_fn *set; user_regset_active_fn *active; diff --git a/kernel/regset.c b/kernel/regset.c index eaeaefbbd39ec..586823786f397 100644 --- a/kernel/regset.c +++ b/kernel/regset.c @@ -11,7 +11,7 @@ static int __regset_get(struct task_struct *target, void *p = *data, *to_free = NULL; int res; - if (!regset->get && !regset->regset_get) + if (!regset->regset_get) return -EOPNOTSUPP; if (size > regset->n * regset->size) size = regset->n * regset->size; @@ -20,28 +20,14 @@ static int __regset_get(struct task_struct *target, if (!p) return -ENOMEM; } - if (regset->regset_get) { - res = regset->regset_get(target, regset, - (struct membuf){.p = p, .left = size}); - if (res < 0) { - kfree(to_free); - return res; - } - *data = p; - return size - res; - } - res = regset->get(target, regset, 0, size, p, NULL); - if (unlikely(res < 0)) { + res = regset->regset_get(target, regset, + (struct membuf){.p = p, .left = size}); + if (res < 0) { kfree(to_free); return res; } *data = p; - if (regset->get_size) { // arm64-only kludge, will go away - unsigned max_size = regset->get_size(target, regset); - if (size > max_size) - size = max_size; - } - return size; + return size - res; } int regset_get(struct task_struct *target, -- GitLab From c522401e0656b51e6a65ec112489cb078801aa9c Mon Sep 17 00:00:00 2001 From: Al Viro Date: Wed, 17 Jun 2020 09:57:08 -0400 Subject: [PATCH 0976/1476] regset(): kill ->get_size() not used anymore Signed-off-by: Al Viro --- arch/arm64/kernel/ptrace.c | 13 ----------- include/linux/regset.h | 48 +------------------------------------- 2 files changed, 1 insertion(+), 60 deletions(-) diff --git a/arch/arm64/kernel/ptrace.c b/arch/arm64/kernel/ptrace.c index 902d987fdd352..5bf737d38b262 100644 --- a/arch/arm64/kernel/ptrace.c +++ b/arch/arm64/kernel/ptrace.c @@ -740,18 +740,6 @@ static unsigned int sve_size_from_header(struct user_sve_header const *header) return ALIGN(header->size, SVE_VQ_BYTES); } -static unsigned int sve_get_size(struct task_struct *target, - const struct user_regset *regset) -{ - struct user_sve_header header; - - if (!system_supports_sve()) - return 0; - - sve_init_header_from_task(&header, target); - return sve_size_from_header(&header); -} - static int sve_get(struct task_struct *target, const struct user_regset *regset, struct membuf to) @@ -1130,7 +1118,6 @@ static const struct user_regset aarch64_regsets[] = { .align = SVE_VQ_BYTES, .regset_get = sve_get, .set = sve_set, - .get_size = sve_get_size, }, #endif #ifdef CONFIG_ARM64_PTR_AUTH diff --git a/include/linux/regset.h b/include/linux/regset.h index 2a4a555b16172..6b951a27bcaf0 100644 --- a/include/linux/regset.h +++ b/include/linux/regset.h @@ -132,28 +132,6 @@ typedef int user_regset_writeback_fn(struct task_struct *target, const struct user_regset *regset, int immediate); -/** - * user_regset_get_size_fn - type of @get_size function in &struct user_regset - * @target: thread being examined - * @regset: regset being examined - * - * This call is optional; usually the pointer is %NULL. - * - * When provided, this function must return the current size of regset - * data, as observed by the @get function in &struct user_regset. The - * value returned must be a multiple of @size. The returned size is - * required to be valid only until the next time (if any) @regset is - * modified for @target. - * - * This function is intended for dynamically sized regsets. A regset - * that is statically sized does not need to implement it. - * - * This function should not be called directly: instead, callers should - * call regset_size() to determine the current size of a regset. - */ -typedef unsigned int user_regset_get_size_fn(struct task_struct *target, - const struct user_regset *regset); - /** * struct user_regset - accessible thread CPU state * @n: Number of slots (registers). @@ -165,7 +143,6 @@ typedef unsigned int user_regset_get_size_fn(struct task_struct *target, * @set: Function to store values. * @active: Function to report if regset is active, or %NULL. * @writeback: Function to write data back to user memory, or %NULL. - * @get_size: Function to return the regset's size, or %NULL. * * This data structure describes a machine resource we call a register set. * This is part of the state of an individual thread, not necessarily @@ -173,12 +150,7 @@ typedef unsigned int user_regset_get_size_fn(struct task_struct *target, * similar slots, given by @n. Each slot is @size bytes, and aligned to * @align bytes (which is at least @size). For dynamically-sized * regsets, @n must contain the maximum possible number of slots for the - * regset, and @get_size must point to a function that returns the - * current regset size. - * - * Callers that need to know only the current size of the regset and do - * not care about its internal structure should call regset_size() - * instead of inspecting @n or calling @get_size. + * regset. * * For backward compatibility, the @get and @set methods must pad to, or * accept, @n * @size bytes, even if the current regset size is smaller. @@ -218,7 +190,6 @@ struct user_regset { user_regset_set_fn *set; user_regset_active_fn *active; user_regset_writeback_fn *writeback; - user_regset_get_size_fn *get_size; unsigned int n; unsigned int size; unsigned int align; @@ -422,21 +393,4 @@ static inline int copy_regset_from_user(struct task_struct *target, return regset->set(target, regset, offset, size, NULL, data); } -/** - * regset_size - determine the current size of a regset - * @target: thread to be examined - * @regset: regset to be examined - * - * Note that the returned size is valid only until the next time - * (if any) @regset is modified for @target. - */ -static inline unsigned int regset_size(struct task_struct *target, - const struct user_regset *regset) -{ - if (!regset->get_size) - return regset->n * regset->size; - else - return regset->get_size(target, regset); -} - #endif /* */ -- GitLab From ce327e1c54119179066d6f3573a28001febc9265 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Wed, 17 Jun 2020 13:40:03 -0400 Subject: [PATCH 0977/1476] regset: kill user_regset_copyout{,_zero}() no callers left Signed-off-by: Al Viro --- include/linux/regset.h | 67 ------------------------------------------ 1 file changed, 67 deletions(-) diff --git a/include/linux/regset.h b/include/linux/regset.h index 6b951a27bcaf0..c3403f328257b 100644 --- a/include/linux/regset.h +++ b/include/linux/regset.h @@ -238,44 +238,6 @@ struct user_regset_view { */ const struct user_regset_view *task_user_regset_view(struct task_struct *tsk); - -/* - * These are helpers for writing regset get/set functions in arch code. - * Because @start_pos and @end_pos are always compile-time constants, - * these are inlined into very little code though they look large. - * - * Use one or more calls sequentially for each chunk of regset data stored - * contiguously in memory. Call with constants for @start_pos and @end_pos, - * giving the range of byte positions in the regset that data corresponds - * to; @end_pos can be -1 if this chunk is at the end of the regset layout. - * Each call updates the arguments to point past its chunk. - */ - -static inline int user_regset_copyout(unsigned int *pos, unsigned int *count, - void **kbuf, - void __user **ubuf, const void *data, - const int start_pos, const int end_pos) -{ - if (*count == 0) - return 0; - BUG_ON(*pos < start_pos); - if (end_pos < 0 || *pos < end_pos) { - unsigned int copy = (end_pos < 0 ? *count - : min(*count, end_pos - *pos)); - data += *pos - start_pos; - if (*kbuf) { - memcpy(*kbuf, data, copy); - *kbuf += copy; - } else if (__copy_to_user(*ubuf, data, copy)) - return -EFAULT; - else - *ubuf += copy; - *pos += copy; - *count -= copy; - } - return 0; -} - static inline int user_regset_copyin(unsigned int *pos, unsigned int *count, const void **kbuf, const void __user **ubuf, void *data, @@ -301,35 +263,6 @@ static inline int user_regset_copyin(unsigned int *pos, unsigned int *count, return 0; } -/* - * These two parallel the two above, but for portions of a regset layout - * that always read as all-zero or for which writes are ignored. - */ -static inline int user_regset_copyout_zero(unsigned int *pos, - unsigned int *count, - void **kbuf, void __user **ubuf, - const int start_pos, - const int end_pos) -{ - if (*count == 0) - return 0; - BUG_ON(*pos < start_pos); - if (end_pos < 0 || *pos < end_pos) { - unsigned int copy = (end_pos < 0 ? *count - : min(*count, end_pos - *pos)); - if (*kbuf) { - memset(*kbuf, 0, copy); - *kbuf += copy; - } else if (clear_user(*ubuf, copy)) - return -EFAULT; - else - *ubuf += copy; - *pos += copy; - *count -= copy; - } - return 0; -} - static inline int user_regset_copyin_ignore(unsigned int *pos, unsigned int *count, const void **kbuf, -- GitLab From 6414e9b09ffd197803f8e86ce2fafdaf1de4e8e4 Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Sun, 12 Jul 2020 20:09:52 -0700 Subject: [PATCH 0978/1476] fs: define inode flags using bit numbers Define the VFS inode flags using bit numbers instead of hardcoding powers of 2, which has become unwieldy now that we're up to 65536. No change in the actual values. Signed-off-by: Eric Biggers Signed-off-by: Al Viro --- include/linux/fs.h | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/include/linux/fs.h b/include/linux/fs.h index 6c4ab4dc1cd71..9bf7a32f29328 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -1982,27 +1982,27 @@ struct super_operations { /* * Inode flags - they have no relation to superblock flags now */ -#define S_SYNC 1 /* Writes are synced at once */ -#define S_NOATIME 2 /* Do not update access times */ -#define S_APPEND 4 /* Append-only file */ -#define S_IMMUTABLE 8 /* Immutable file */ -#define S_DEAD 16 /* removed, but still open directory */ -#define S_NOQUOTA 32 /* Inode is not counted to quota */ -#define S_DIRSYNC 64 /* Directory modifications are synchronous */ -#define S_NOCMTIME 128 /* Do not update file c/mtime */ -#define S_SWAPFILE 256 /* Do not truncate: swapon got its bmaps */ -#define S_PRIVATE 512 /* Inode is fs-internal */ -#define S_IMA 1024 /* Inode has an associated IMA struct */ -#define S_AUTOMOUNT 2048 /* Automount/referral quasi-directory */ -#define S_NOSEC 4096 /* no suid or xattr security attributes */ +#define S_SYNC (1 << 0) /* Writes are synced at once */ +#define S_NOATIME (1 << 1) /* Do not update access times */ +#define S_APPEND (1 << 2) /* Append-only file */ +#define S_IMMUTABLE (1 << 3) /* Immutable file */ +#define S_DEAD (1 << 4) /* removed, but still open directory */ +#define S_NOQUOTA (1 << 5) /* Inode is not counted to quota */ +#define S_DIRSYNC (1 << 6) /* Directory modifications are synchronous */ +#define S_NOCMTIME (1 << 7) /* Do not update file c/mtime */ +#define S_SWAPFILE (1 << 8) /* Do not truncate: swapon got its bmaps */ +#define S_PRIVATE (1 << 9) /* Inode is fs-internal */ +#define S_IMA (1 << 10) /* Inode has an associated IMA struct */ +#define S_AUTOMOUNT (1 << 11) /* Automount/referral quasi-directory */ +#define S_NOSEC (1 << 12) /* no suid or xattr security attributes */ #ifdef CONFIG_FS_DAX -#define S_DAX 8192 /* Direct Access, avoiding the page cache */ +#define S_DAX (1 << 13) /* Direct Access, avoiding the page cache */ #else -#define S_DAX 0 /* Make all the DAX code disappear */ +#define S_DAX 0 /* Make all the DAX code disappear */ #endif -#define S_ENCRYPTED 16384 /* Encrypted file (using fs/crypto/) */ -#define S_CASEFOLD 32768 /* Casefolded file */ -#define S_VERITY 65536 /* Verity file (using fs/verity/) */ +#define S_ENCRYPTED (1 << 14) /* Encrypted file (using fs/crypto/) */ +#define S_CASEFOLD (1 << 15) /* Casefolded file */ +#define S_VERITY (1 << 16) /* Verity file (using fs/verity/) */ /* * Note that nosuid etc flags are inode-specific: setting some file-system -- GitLab From af60459a543483c5ad155a92daa8c3a6c00a0829 Mon Sep 17 00:00:00 2001 From: Chunyan Zhang Date: Fri, 24 Jul 2020 20:21:47 +0800 Subject: [PATCH 0979/1476] math64: New DIV_S64_ROUND_CLOSEST helper Provide DIV_S64_ROUND_CLOSEST helper which uses div_s64 to perform division rounded to the closest integer using signed 64bit dividend and signed 32bit divisor. Signed-off-by: Chunyan Zhang Signed-off-by: Sebastian Reichel --- include/linux/math64.h | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/include/linux/math64.h b/include/linux/math64.h index 11a267413e8e7..cd06939894367 100644 --- a/include/linux/math64.h +++ b/include/linux/math64.h @@ -279,4 +279,23 @@ static inline u64 mul_u64_u32_div(u64 a, u32 mul, u32 divisor) #define DIV64_U64_ROUND_CLOSEST(dividend, divisor) \ ({ u64 _tmp = (divisor); div64_u64((dividend) + _tmp / 2, _tmp); }) +/* + * DIV_S64_ROUND_CLOSEST - signed 64bit divide with 32bit divisor rounded to nearest integer + * @dividend: signed 64bit dividend + * @divisor: signed 32bit divisor + * + * Divide signed 64bit dividend by signed 32bit divisor + * and round to closest integer. + * + * Return: dividend / divisor rounded to nearest integer + */ +#define DIV_S64_ROUND_CLOSEST(dividend, divisor)( \ +{ \ + s64 __x = (dividend); \ + s32 __d = (divisor); \ + ((__x > 0) == (__d > 0)) ? \ + div_s64((__x + (__d / 2)), __d) : \ + div_s64((__x - (__d / 2)), __d); \ +} \ +) #endif /* _LINUX_MATH64_H */ -- GitLab From ac31585fca317b29b6b7093a5a6775137a852f94 Mon Sep 17 00:00:00 2001 From: Chunyan Zhang Date: Fri, 24 Jul 2020 20:21:48 +0800 Subject: [PATCH 0980/1476] power: supply: sc27xx: prevent adc * 1000 from overflow The input parameter is int type, cause adc * 1000 could overflow. Change to use s64 to avoid this issue. Signed-off-by: Chen Yongzhi Signed-off-by: Chunyan Zhang Signed-off-by: Sebastian Reichel --- drivers/power/supply/sc27xx_fuel_gauge.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/drivers/power/supply/sc27xx_fuel_gauge.c b/drivers/power/supply/sc27xx_fuel_gauge.c index be42e814ea34b..9c627618c2249 100644 --- a/drivers/power/supply/sc27xx_fuel_gauge.c +++ b/drivers/power/supply/sc27xx_fuel_gauge.c @@ -5,6 +5,7 @@ #include #include #include +#include #include #include #include @@ -133,14 +134,14 @@ static const char * const sc27xx_charger_supply_name[] = { "sc2723_charger", }; -static int sc27xx_fgu_adc_to_current(struct sc27xx_fgu_data *data, int adc) +static int sc27xx_fgu_adc_to_current(struct sc27xx_fgu_data *data, s64 adc) { - return DIV_ROUND_CLOSEST(adc * 1000, data->cur_1000ma_adc); + return DIV_S64_ROUND_CLOSEST(adc * 1000, data->cur_1000ma_adc); } -static int sc27xx_fgu_adc_to_voltage(struct sc27xx_fgu_data *data, int adc) +static int sc27xx_fgu_adc_to_voltage(struct sc27xx_fgu_data *data, s64 adc) { - return DIV_ROUND_CLOSEST(adc * 1000, data->vol_1000mv_adc); + return DIV_S64_ROUND_CLOSEST(adc * 1000, data->vol_1000mv_adc); } static int sc27xx_fgu_voltage_to_adc(struct sc27xx_fgu_data *data, int vol) -- GitLab From a7f79f99541eff4e6bcae0014eb08d3019337565 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Gonz=C3=A1lez=20Cabanelas?= Date: Wed, 15 Jul 2020 15:35:14 +0200 Subject: [PATCH 0981/1476] power: reset: add driver for LinkStation power off MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Some Buffalo LinkStations perform the power off operation, at restart time, depending on the state of an output pin (LED2/INTn) at the ethernet PHY. This pin is also used to wake the machine when a WoL packet is received by the PHY. The driver is required by the Buffalo LinkStation LS421DE (ARM MVEBU), and other models. Without it, the board remains forever halted if a power off command is executed, unless the PSU is disconnected and connected again. Add the driver to provide the power off function and also make the WoL feature to be available. Signed-off-by: Daniel González Cabanelas Signed-off-by: Sebastian Reichel --- drivers/power/reset/Kconfig | 11 ++ drivers/power/reset/Makefile | 1 + drivers/power/reset/linkstation-poweroff.c | 136 +++++++++++++++++++++ 3 files changed, 148 insertions(+) create mode 100644 drivers/power/reset/linkstation-poweroff.c diff --git a/drivers/power/reset/Kconfig b/drivers/power/reset/Kconfig index f07b982c8dff0..0a1fb5c74f83e 100644 --- a/drivers/power/reset/Kconfig +++ b/drivers/power/reset/Kconfig @@ -99,6 +99,17 @@ config POWER_RESET_HISI help Reboot support for Hisilicon boards. +config POWER_RESET_LINKSTATION + tristate "Buffalo LinkStation power-off driver" + depends on ARCH_MVEBU || COMPILE_TEST + depends on OF_MDIO && PHYLIB + help + This driver supports turning off some Buffalo LinkStations by + setting an output pin at the ethernet PHY to the correct state. + It also makes the device compatible with the WoL function. + + Say Y here if you have a Buffalo LinkStation LS421D/E. + config POWER_RESET_MSM bool "Qualcomm MSM power-off driver" depends on ARCH_QCOM diff --git a/drivers/power/reset/Makefile b/drivers/power/reset/Makefile index 5710ca4695170..c51eceba9ea39 100644 --- a/drivers/power/reset/Makefile +++ b/drivers/power/reset/Makefile @@ -10,6 +10,7 @@ obj-$(CONFIG_POWER_RESET_GEMINI_POWEROFF) += gemini-poweroff.o obj-$(CONFIG_POWER_RESET_GPIO) += gpio-poweroff.o obj-$(CONFIG_POWER_RESET_GPIO_RESTART) += gpio-restart.o obj-$(CONFIG_POWER_RESET_HISI) += hisi-reboot.o +obj-${CONFIG_POWER_RESET_LINKSTATION} += linkstation-poweroff.o obj-$(CONFIG_POWER_RESET_MSM) += msm-poweroff.o obj-$(CONFIG_POWER_RESET_MT6323) += mt6323-poweroff.o obj-$(CONFIG_POWER_RESET_OXNAS) += oxnas-restart.o diff --git a/drivers/power/reset/linkstation-poweroff.c b/drivers/power/reset/linkstation-poweroff.c new file mode 100644 index 0000000000000..39e89baedb5f9 --- /dev/null +++ b/drivers/power/reset/linkstation-poweroff.c @@ -0,0 +1,136 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * LinkStation power off restart driver + * Copyright (C) 2020 Daniel González Cabanelas + */ + +#include +#include +#include +#include +#include +#include +#include + +/* Defines from the eth phy Marvell driver */ +#define MII_MARVELL_COPPER_PAGE 0 +#define MII_MARVELL_LED_PAGE 3 +#define MII_MARVELL_WOL_PAGE 17 +#define MII_MARVELL_PHY_PAGE 22 + +#define MII_PHY_LED_CTRL 16 +#define MII_88E1318S_PHY_LED_TCR 18 +#define MII_88E1318S_PHY_WOL_CTRL 16 +#define MII_M1011_IEVENT 19 + +#define MII_88E1318S_PHY_LED_TCR_INTn_ENABLE BIT(7) +#define MII_88E1318S_PHY_LED_TCR_FORCE_INT BIT(15) +#define MII_88E1318S_PHY_WOL_CTRL_CLEAR_WOL_STATUS BIT(12) +#define LED2_FORCE_ON (0x8 << 8) +#define LEDMASK GENMASK(11,8) + +static struct phy_device *phydev; + +static void mvphy_reg_intn(u16 data) +{ + int rc = 0, saved_page; + + saved_page = phy_select_page(phydev, MII_MARVELL_LED_PAGE); + if (saved_page < 0) + goto err; + + /* Force manual LED2 control to let INTn work */ + __phy_modify(phydev, MII_PHY_LED_CTRL, LEDMASK, LED2_FORCE_ON); + + /* Set the LED[2]/INTn pin to the required state */ + __phy_modify(phydev, MII_88E1318S_PHY_LED_TCR, + MII_88E1318S_PHY_LED_TCR_FORCE_INT, + MII_88E1318S_PHY_LED_TCR_INTn_ENABLE | data); + + if (!data) { + /* Clear interrupts to ensure INTn won't be holded in high state */ + __phy_write(phydev, MII_MARVELL_PHY_PAGE, MII_MARVELL_COPPER_PAGE); + __phy_read(phydev, MII_M1011_IEVENT); + + /* If WOL was enabled and a magic packet was received before powering + * off, we won't be able to wake up by sending another magic packet. + * Clear WOL status. + */ + __phy_write(phydev, MII_MARVELL_PHY_PAGE, MII_MARVELL_WOL_PAGE); + __phy_set_bits(phydev, MII_88E1318S_PHY_WOL_CTRL, + MII_88E1318S_PHY_WOL_CTRL_CLEAR_WOL_STATUS); + } +err: + rc = phy_restore_page(phydev, saved_page, rc); + if (rc < 0) + dev_err(&phydev->mdio.dev, "Write register failed, %d\n", rc); +} + +static int linkstation_reboot_notifier(struct notifier_block *nb, + unsigned long action, void *unused) +{ + if (action == SYS_RESTART) + mvphy_reg_intn(MII_88E1318S_PHY_LED_TCR_FORCE_INT); + + return NOTIFY_DONE; +} + +static struct notifier_block linkstation_reboot_nb = { + .notifier_call = linkstation_reboot_notifier, +}; + +static void linkstation_poweroff(void) +{ + unregister_reboot_notifier(&linkstation_reboot_nb); + mvphy_reg_intn(0); + + kernel_restart("Power off"); +} + +static const struct of_device_id ls_poweroff_of_match[] = { + { .compatible = "buffalo,ls421d" }, + { .compatible = "buffalo,ls421de" }, + { }, +}; + +static int __init linkstation_poweroff_init(void) +{ + struct mii_bus *bus; + struct device_node *dn; + + dn = of_find_matching_node(NULL, ls_poweroff_of_match); + if (!dn) + return -ENODEV; + of_node_put(dn); + + dn = of_find_node_by_name(NULL, "mdio"); + if (!dn) + return -ENODEV; + + bus = of_mdio_find_bus(dn); + of_node_put(dn); + if (!bus) + return -EPROBE_DEFER; + + phydev = phy_find_first(bus); + if (!phydev) + return -EPROBE_DEFER; + + register_reboot_notifier(&linkstation_reboot_nb); + pm_power_off = linkstation_poweroff; + + return 0; +} + +static void __exit linkstation_poweroff_exit(void) +{ + pm_power_off = NULL; + unregister_reboot_notifier(&linkstation_reboot_nb); +} + +module_init(linkstation_poweroff_init); +module_exit(linkstation_poweroff_exit); + +MODULE_AUTHOR("Daniel González Cabanelas "); +MODULE_DESCRIPTION("LinkStation power off driver"); +MODULE_LICENSE("GPL v2"); -- GitLab From 81bd45fc36e3d01db66c17fa5ed5f39a61e67477 Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Mon, 13 Jul 2020 21:16:38 +0200 Subject: [PATCH 0982/1476] power: supply: bq2xxxx: Replace HTTP links with HTTPS ones Rationale: Reduces attack surface on kernel devs opening the links for MITM as HTTPS traffic is much harder to manipulate. Deterministic algorithm: For each file: If not .svg: For each line: If doesn't contain `\bxmlns\b`: For each link, `\bhttp://[^# \t\r\n]*(?:\w|/)`: If neither `\bgnu\.org/license`, nor `\bmozilla\.org/MPL\b`: If both the HTTP and HTTPS versions return 200 OK and serve the same content: Replace HTTP with HTTPS. Signed-off-by: Alexander A. Klimov [also update recently added ti.com http links] Signed-off-by: Sebastian Reichel --- drivers/power/supply/bq2415x_charger.c | 16 +++---- drivers/power/supply/bq24257_charger.c | 6 +-- drivers/power/supply/bq2515x_charger.c | 2 +- drivers/power/supply/bq27xxx_battery.c | 50 +++++++++++----------- drivers/power/supply/bq27xxx_battery_hdq.c | 2 +- drivers/power/supply/bq27xxx_battery_i2c.c | 2 +- 6 files changed, 39 insertions(+), 39 deletions(-) diff --git a/drivers/power/supply/bq2415x_charger.c b/drivers/power/supply/bq2415x_charger.c index a1f00ae1c1801..5724001e66b97 100644 --- a/drivers/power/supply/bq2415x_charger.c +++ b/drivers/power/supply/bq2415x_charger.c @@ -5,14 +5,14 @@ * Copyright (C) 2011-2013 Pali Rohár * * Datasheets: - * http://www.ti.com/product/bq24150 - * http://www.ti.com/product/bq24150a - * http://www.ti.com/product/bq24152 - * http://www.ti.com/product/bq24153 - * http://www.ti.com/product/bq24153a - * http://www.ti.com/product/bq24155 - * http://www.ti.com/product/bq24157s - * http://www.ti.com/product/bq24158 + * https://www.ti.com/product/bq24150 + * https://www.ti.com/product/bq24150a + * https://www.ti.com/product/bq24152 + * https://www.ti.com/product/bq24153 + * https://www.ti.com/product/bq24153a + * https://www.ti.com/product/bq24155 + * https://www.ti.com/product/bq24157s + * https://www.ti.com/product/bq24158 */ #include diff --git a/drivers/power/supply/bq24257_charger.c b/drivers/power/supply/bq24257_charger.c index eb151687beb3a..8e60cb0f3c3f2 100644 --- a/drivers/power/supply/bq24257_charger.c +++ b/drivers/power/supply/bq24257_charger.c @@ -5,9 +5,9 @@ * Copyright (C) 2015 Intel Corporation * * Datasheets: - * http://www.ti.com/product/bq24250 - * http://www.ti.com/product/bq24251 - * http://www.ti.com/product/bq24257 + * https://www.ti.com/product/bq24250 + * https://www.ti.com/product/bq24251 + * https://www.ti.com/product/bq24257 */ #include diff --git a/drivers/power/supply/bq2515x_charger.c b/drivers/power/supply/bq2515x_charger.c index 63e8e317af56c..36b0c8c98d40c 100644 --- a/drivers/power/supply/bq2515x_charger.c +++ b/drivers/power/supply/bq2515x_charger.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 // BQ2515X Battery Charger Driver -// Copyright (C) 2020 Texas Instruments Incorporated - http://www.ti.com/ +// Copyright (C) 2020 Texas Instruments Incorporated - https://www.ti.com/ #include #include diff --git a/drivers/power/supply/bq27xxx_battery.c b/drivers/power/supply/bq27xxx_battery.c index 942c92127b6d5..acaafed037be8 100644 --- a/drivers/power/supply/bq27xxx_battery.c +++ b/drivers/power/supply/bq27xxx_battery.c @@ -18,31 +18,31 @@ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. * * Datasheets: - * http://www.ti.com/product/bq27000 - * http://www.ti.com/product/bq27200 - * http://www.ti.com/product/bq27010 - * http://www.ti.com/product/bq27210 - * http://www.ti.com/product/bq27500 - * http://www.ti.com/product/bq27510-g1 - * http://www.ti.com/product/bq27510-g2 - * http://www.ti.com/product/bq27510-g3 - * http://www.ti.com/product/bq27520-g1 - * http://www.ti.com/product/bq27520-g2 - * http://www.ti.com/product/bq27520-g3 - * http://www.ti.com/product/bq27520-g4 - * http://www.ti.com/product/bq27530-g1 - * http://www.ti.com/product/bq27531-g1 - * http://www.ti.com/product/bq27541-g1 - * http://www.ti.com/product/bq27542-g1 - * http://www.ti.com/product/bq27546-g1 - * http://www.ti.com/product/bq27742-g1 - * http://www.ti.com/product/bq27545-g1 - * http://www.ti.com/product/bq27421-g1 - * http://www.ti.com/product/bq27425-g1 - * http://www.ti.com/product/bq27426 - * http://www.ti.com/product/bq27411-g1 - * http://www.ti.com/product/bq27441-g1 - * http://www.ti.com/product/bq27621-g1 + * https://www.ti.com/product/bq27000 + * https://www.ti.com/product/bq27200 + * https://www.ti.com/product/bq27010 + * https://www.ti.com/product/bq27210 + * https://www.ti.com/product/bq27500 + * https://www.ti.com/product/bq27510-g1 + * https://www.ti.com/product/bq27510-g2 + * https://www.ti.com/product/bq27510-g3 + * https://www.ti.com/product/bq27520-g1 + * https://www.ti.com/product/bq27520-g2 + * https://www.ti.com/product/bq27520-g3 + * https://www.ti.com/product/bq27520-g4 + * https://www.ti.com/product/bq27530-g1 + * https://www.ti.com/product/bq27531-g1 + * https://www.ti.com/product/bq27541-g1 + * https://www.ti.com/product/bq27542-g1 + * https://www.ti.com/product/bq27546-g1 + * https://www.ti.com/product/bq27742-g1 + * https://www.ti.com/product/bq27545-g1 + * https://www.ti.com/product/bq27421-g1 + * https://www.ti.com/product/bq27425-g1 + * https://www.ti.com/product/bq27426 + * https://www.ti.com/product/bq27411-g1 + * https://www.ti.com/product/bq27441-g1 + * https://www.ti.com/product/bq27621-g1 */ #include diff --git a/drivers/power/supply/bq27xxx_battery_hdq.c b/drivers/power/supply/bq27xxx_battery_hdq.c index 9aff896c98020..29771967df2e3 100644 --- a/drivers/power/supply/bq27xxx_battery_hdq.c +++ b/drivers/power/supply/bq27xxx_battery_hdq.c @@ -1,7 +1,7 @@ /* * BQ27xxx battery monitor HDQ/1-wire driver * - * Copyright (C) 2007-2017 Texas Instruments Incorporated - http://www.ti.com/ + * Copyright (C) 2007-2017 Texas Instruments Incorporated - https://www.ti.com/ * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as diff --git a/drivers/power/supply/bq27xxx_battery_i2c.c b/drivers/power/supply/bq27xxx_battery_i2c.c index 2677c38a8a424..8e114a7abfc93 100644 --- a/drivers/power/supply/bq27xxx_battery_i2c.c +++ b/drivers/power/supply/bq27xxx_battery_i2c.c @@ -1,7 +1,7 @@ /* * BQ27xxx battery monitor I2C driver * - * Copyright (C) 2015 Texas Instruments Incorporated - http://www.ti.com/ + * Copyright (C) 2015 Texas Instruments Incorporated - https://www.ti.com/ * Andrew F. Davis * * This program is free software; you can redistribute it and/or modify -- GitLab From ccf193dee1f0fff55b556928591f7818bac1b3b1 Mon Sep 17 00:00:00 2001 From: Tom Rix Date: Sun, 12 Jul 2020 12:23:51 -0700 Subject: [PATCH 0983/1476] power: supply: check if calc_soc succeeded in pm860x_init_battery clang static analysis flags this error 88pm860x_battery.c:522:19: warning: Assigned value is garbage or undefined [core.uninitialized.Assign] info->start_soc = soc; ^ ~~~ soc is set by calling calc_soc. But calc_soc can return without setting soc. So check the return status and bail similarly to other checks in pm860x_init_battery and initialize soc to silence the warning. Fixes: a830d28b48bf ("power_supply: Enable battery-charger for 88pm860x") Signed-off-by: Tom Rix Signed-off-by: Sebastian Reichel --- drivers/power/supply/88pm860x_battery.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/power/supply/88pm860x_battery.c b/drivers/power/supply/88pm860x_battery.c index 1308f3a185f3d..590da88a17a25 100644 --- a/drivers/power/supply/88pm860x_battery.c +++ b/drivers/power/supply/88pm860x_battery.c @@ -433,7 +433,7 @@ static void pm860x_init_battery(struct pm860x_battery_info *info) int ret; int data; int bat_remove; - int soc; + int soc = 0; /* measure enable on GPADC1 */ data = MEAS1_GP1; @@ -496,7 +496,9 @@ static void pm860x_init_battery(struct pm860x_battery_info *info) } mutex_unlock(&info->lock); - calc_soc(info, OCV_MODE_ACTIVE, &soc); + ret = calc_soc(info, OCV_MODE_ACTIVE, &soc); + if (ret < 0) + goto out; data = pm860x_reg_read(info->i2c, PM8607_POWER_UP_LOG); bat_remove = data & BAT_WU_LOG; -- GitLab From 2f38dc4d9190cd97b8ceb88be5b835ee5feb44d1 Mon Sep 17 00:00:00 2001 From: Matheus Castello Date: Sun, 5 Jul 2020 21:10:03 -0300 Subject: [PATCH 0984/1476] power: supply: max17040: Add POWER_SUPPLY_PROP_CAPACITY_ALERT_MIN Adds the property POWER_SUPPLY_PROP_CAPACITY_ALERT_MIN to export the chip->low_soc_alert and add the property as writeable, implementing max17040_prop_writeable and max17040_set_property, so with that the user space can readjust the alerts. Signed-off-by: Matheus Castello Signed-off-by: Sebastian Reichel --- drivers/power/supply/max17040_battery.c | 51 ++++++++++++++++++++++--- 1 file changed, 46 insertions(+), 5 deletions(-) diff --git a/drivers/power/supply/max17040_battery.c b/drivers/power/supply/max17040_battery.c index 48aa44665e2f1..6cb31b9a958dd 100644 --- a/drivers/power/supply/max17040_battery.c +++ b/drivers/power/supply/max17040_battery.c @@ -69,6 +69,9 @@ static int max17040_get_property(struct power_supply *psy, case POWER_SUPPLY_PROP_CAPACITY: val->intval = chip->soc; break; + case POWER_SUPPLY_PROP_CAPACITY_ALERT_MIN: + val->intval = chip->low_soc_alert; + break; default: return -EINVAL; } @@ -256,19 +259,57 @@ static int max17040_enable_alert_irq(struct max17040_chip *chip) return ret; } +static int max17040_prop_writeable(struct power_supply *psy, + enum power_supply_property psp) +{ + switch (psp) { + case POWER_SUPPLY_PROP_CAPACITY_ALERT_MIN: + return 1; + default: + return 0; + } +} + +static int max17040_set_property(struct power_supply *psy, + enum power_supply_property psp, + const union power_supply_propval *val) +{ + struct max17040_chip *chip = power_supply_get_drvdata(psy); + int ret; + + switch (psp) { + case POWER_SUPPLY_PROP_CAPACITY_ALERT_MIN: + /* alert threshold can be programmed from 1% up to 32% */ + if ((val->intval < 1) || (val->intval > 32)) { + ret = -EINVAL; + break; + } + ret = max17040_set_low_soc_alert(chip->client, val->intval); + chip->low_soc_alert = val->intval; + break; + default: + ret = -EINVAL; + } + + return ret; +} + static enum power_supply_property max17040_battery_props[] = { POWER_SUPPLY_PROP_STATUS, POWER_SUPPLY_PROP_ONLINE, POWER_SUPPLY_PROP_VOLTAGE_NOW, POWER_SUPPLY_PROP_CAPACITY, + POWER_SUPPLY_PROP_CAPACITY_ALERT_MIN, }; static const struct power_supply_desc max17040_battery_desc = { - .name = "battery", - .type = POWER_SUPPLY_TYPE_BATTERY, - .get_property = max17040_get_property, - .properties = max17040_battery_props, - .num_properties = ARRAY_SIZE(max17040_battery_props), + .name = "battery", + .type = POWER_SUPPLY_TYPE_BATTERY, + .get_property = max17040_get_property, + .set_property = max17040_set_property, + .property_is_writeable = max17040_prop_writeable, + .properties = max17040_battery_props, + .num_properties = ARRAY_SIZE(max17040_battery_props), }; static int max17040_probe(struct i2c_client *client, -- GitLab From 466bf93153b871f9fed93aea1389673c17d253e7 Mon Sep 17 00:00:00 2001 From: Peng Fan Date: Thu, 2 Jul 2020 16:58:52 +0800 Subject: [PATCH 0985/1476] power: supply: rt5033_battery: Fix error code in rt5033_battery_probe() In the function rt5033_battery_probe(), it should return -ENOMEM instead of -EINVAL when call function devm_kzalloc() failed. Signed-off-by: Peng Fan Signed-off-by: Sebastian Reichel --- drivers/power/supply/rt5033_battery.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/power/supply/rt5033_battery.c b/drivers/power/supply/rt5033_battery.c index d8667a9fc49b1..f330452341f02 100644 --- a/drivers/power/supply/rt5033_battery.c +++ b/drivers/power/supply/rt5033_battery.c @@ -125,7 +125,7 @@ static int rt5033_battery_probe(struct i2c_client *client, battery = devm_kzalloc(&client->dev, sizeof(*battery), GFP_KERNEL); if (!battery) - return -EINVAL; + return -ENOMEM; battery->client = client; battery->regmap = devm_regmap_init_i2c(client, -- GitLab From 980d1f2f3db618b58f9d1dab82534286e9e3cff1 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Tue, 23 Jun 2020 16:55:27 +0200 Subject: [PATCH 0986/1476] dt-bindings: pci: tegra: Remove PLL power supplies The XUSB pad controller, which provides access to various USB, PCI and SATA pads (or PHYs), needs to bring up the PLLs associated with these pads. In order to properly do so, it needs to control the power supplied to these PLLs. Remove the PLL power supplies from the PCIe controller because it does not need direct access to them. Instead it will only use the configured pads provided by the XUSB pad controller. Link: https://lore.kernel.org/r/20200623145528.1658337-1-thierry.reding@gmail.com Signed-off-by: Thierry Reding Signed-off-by: Lorenzo Pieralisi Reviewed-by: Rob Herring --- .../devicetree/bindings/pci/nvidia,tegra20-pcie.txt | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/Documentation/devicetree/bindings/pci/nvidia,tegra20-pcie.txt b/Documentation/devicetree/bindings/pci/nvidia,tegra20-pcie.txt index 7939bca478612..d099f3476cccd 100644 --- a/Documentation/devicetree/bindings/pci/nvidia,tegra20-pcie.txt +++ b/Documentation/devicetree/bindings/pci/nvidia,tegra20-pcie.txt @@ -112,28 +112,16 @@ Power supplies for Tegra124: - Required: - avddio-pex-supply: Power supply for analog PCIe logic. Must supply 1.05 V. - dvddio-pex-supply: Power supply for digital PCIe I/O. Must supply 1.05 V. - - avdd-pex-pll-supply: Power supply for dedicated (internal) PCIe PLL. Must - supply 1.05 V. - hvdd-pex-supply: High-voltage supply for PCIe I/O and PCIe output clocks. Must supply 3.3 V. - - hvdd-pex-pll-e-supply: High-voltage supply for PLLE (shared with USB3). - Must supply 3.3 V. - vddio-pex-ctl-supply: Power supply for PCIe control I/O partition. Must supply 2.8-3.3 V. - - avdd-pll-erefe-supply: Power supply for PLLE (shared with USB3). Must - supply 1.05 V. Power supplies for Tegra210: - Required: - - avdd-pll-uerefe-supply: Power supply for PLLE (shared with USB3). Must - supply 1.05 V. - hvddio-pex-supply: High-voltage supply for PCIe I/O and PCIe output clocks. Must supply 1.8 V. - dvddio-pex-supply: Power supply for digital PCIe I/O. Must supply 1.05 V. - - dvdd-pex-pll-supply: Power supply for dedicated (internal) PCIe PLL. Must - supply 1.05 V. - - hvdd-pex-pll-e-supply: High-voltage supply for PLLE (shared with USB3). - Must supply 3.3 V. - vddio-pex-ctl-supply: Power supply for PCIe control I/O partition. Must supply 1.8 V. -- GitLab From a6fbb4a4a7f0afeec287a7b1e6bdb9e48c75c4d0 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Tue, 23 Jun 2020 16:55:28 +0200 Subject: [PATCH 0987/1476] PCI: tegra: Remove PLL power supplies The Tegra PCI controller driver doesn't need to control the PLL power supplies directly, but rather uses the pads provided by the XUSB pad controller, which in turn is responsible for supplying power to the PLLs. Link: https://lore.kernel.org/r/20200623145528.1658337-2-thierry.reding@gmail.com Signed-off-by: Thierry Reding Signed-off-by: Lorenzo Pieralisi Reviewed-by: Rob Herring --- drivers/pci/controller/pci-tegra.c | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/drivers/pci/controller/pci-tegra.c b/drivers/pci/controller/pci-tegra.c index b532d5082fb6f..e25e661a652cd 100644 --- a/drivers/pci/controller/pci-tegra.c +++ b/drivers/pci/controller/pci-tegra.c @@ -2000,7 +2000,7 @@ static int tegra_pcie_get_regulators(struct tegra_pcie *pcie, u32 lane_mask) pcie->supplies[i++].supply = "hvdd-pex"; pcie->supplies[i++].supply = "vddio-pexctl-aud"; } else if (of_device_is_compatible(np, "nvidia,tegra210-pcie")) { - pcie->num_supplies = 6; + pcie->num_supplies = 3; pcie->supplies = devm_kcalloc(pcie->dev, pcie->num_supplies, sizeof(*pcie->supplies), @@ -2008,14 +2008,11 @@ static int tegra_pcie_get_regulators(struct tegra_pcie *pcie, u32 lane_mask) if (!pcie->supplies) return -ENOMEM; - pcie->supplies[i++].supply = "avdd-pll-uerefe"; pcie->supplies[i++].supply = "hvddio-pex"; pcie->supplies[i++].supply = "dvddio-pex"; - pcie->supplies[i++].supply = "dvdd-pex-pll"; - pcie->supplies[i++].supply = "hvdd-pex-pll-e"; pcie->supplies[i++].supply = "vddio-pex-ctl"; } else if (of_device_is_compatible(np, "nvidia,tegra124-pcie")) { - pcie->num_supplies = 7; + pcie->num_supplies = 4; pcie->supplies = devm_kcalloc(dev, pcie->num_supplies, sizeof(*pcie->supplies), @@ -2025,11 +2022,8 @@ static int tegra_pcie_get_regulators(struct tegra_pcie *pcie, u32 lane_mask) pcie->supplies[i++].supply = "avddio-pex"; pcie->supplies[i++].supply = "dvddio-pex"; - pcie->supplies[i++].supply = "avdd-pex-pll"; pcie->supplies[i++].supply = "hvdd-pex"; - pcie->supplies[i++].supply = "hvdd-pex-pll-e"; pcie->supplies[i++].supply = "vddio-pex-ctl"; - pcie->supplies[i++].supply = "avdd-pll-erefe"; } else if (of_device_is_compatible(np, "nvidia,tegra30-pcie")) { bool need_pexa = false, need_pexb = false; -- GitLab From a459d9e107887a1fef32d5c6d0951241876812d9 Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Mon, 6 Jul 2020 21:52:34 +0800 Subject: [PATCH 0988/1476] PCI: hv: Make some functions static sparse report build warning as follows: drivers/pci/controller/pci-hyperv.c:941:5: warning: symbol 'hv_read_config_block' was not declared. Should it be static? drivers/pci/controller/pci-hyperv.c:1021:5: warning: symbol 'hv_write_config_block' was not declared. Should it be static? drivers/pci/controller/pci-hyperv.c:1090:5: warning: symbol 'hv_register_block_invalidate' was not declared. Should it be static? Those functions are not used outside of this file, so mark them static. Link: https://lore.kernel.org/r/20200706135234.80758-1-weiyongjun1@huawei.com Reported-by: Hulk Robot Signed-off-by: Wei Yongjun Signed-off-by: Lorenzo Pieralisi --- drivers/pci/controller/pci-hyperv.c | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/drivers/pci/controller/pci-hyperv.c b/drivers/pci/controller/pci-hyperv.c index d0033ff6c1437..fc4c3a15e5707 100644 --- a/drivers/pci/controller/pci-hyperv.c +++ b/drivers/pci/controller/pci-hyperv.c @@ -938,8 +938,9 @@ out: * * Return: 0 on success, -errno on failure */ -int hv_read_config_block(struct pci_dev *pdev, void *buf, unsigned int len, - unsigned int block_id, unsigned int *bytes_returned) +static int hv_read_config_block(struct pci_dev *pdev, void *buf, + unsigned int len, unsigned int block_id, + unsigned int *bytes_returned) { struct hv_pcibus_device *hbus = container_of(pdev->bus->sysdata, struct hv_pcibus_device, @@ -1018,8 +1019,8 @@ static void hv_pci_write_config_compl(void *context, struct pci_response *resp, * * Return: 0 on success, -errno on failure */ -int hv_write_config_block(struct pci_dev *pdev, void *buf, unsigned int len, - unsigned int block_id) +static int hv_write_config_block(struct pci_dev *pdev, void *buf, + unsigned int len, unsigned int block_id) { struct hv_pcibus_device *hbus = container_of(pdev->bus->sysdata, struct hv_pcibus_device, @@ -1087,9 +1088,9 @@ int hv_write_config_block(struct pci_dev *pdev, void *buf, unsigned int len, * * Return: 0 on success, -errno on failure */ -int hv_register_block_invalidate(struct pci_dev *pdev, void *context, - void (*block_invalidate)(void *context, - u64 block_mask)) +static int hv_register_block_invalidate(struct pci_dev *pdev, void *context, + void (*block_invalidate)(void *context, + u64 block_mask)) { struct hv_pcibus_device *hbus = container_of(pdev->bus->sysdata, struct hv_pcibus_device, -- GitLab From 3ef5d16f50f8c32abd73e0c3bf4af690c9911cc7 Mon Sep 17 00:00:00 2001 From: Alan Douglas Date: Wed, 22 Jul 2020 16:33:12 +0530 Subject: [PATCH 0989/1476] PCI: cadence: Add MSI-X support to Endpoint driver Implement ->set_msix() and ->get_msix() callback functions in order to configure MSIX capability in the PCIe endpoint controller. Add cdns_pcie_ep_send_msix_irq() to send MSIX interrupts to Host. cdns_pcie_ep_send_msix_irq() gets the MSIX table address (virtual address) from "struct cdns_pcie_epf" that gets initialized in ->set_bar() call back function. [kishon@ti.com: Re-implement MSIX support in accordance with the re-designed core MSI-X interfaces] Link: https://lore.kernel.org/r/20200722110317.4744-11-kishon@ti.com Signed-off-by: Alan Douglas Signed-off-by: Kishon Vijay Abraham I Signed-off-by: Lorenzo Pieralisi --- .../pci/controller/cadence/pcie-cadence-ep.c | 107 +++++++++++++++++- drivers/pci/controller/cadence/pcie-cadence.h | 11 ++ 2 files changed, 117 insertions(+), 1 deletion(-) diff --git a/drivers/pci/controller/cadence/pcie-cadence-ep.c b/drivers/pci/controller/cadence/pcie-cadence-ep.c index 034cb3cf726ea..ec1306da301f0 100644 --- a/drivers/pci/controller/cadence/pcie-cadence-ep.c +++ b/drivers/pci/controller/cadence/pcie-cadence-ep.c @@ -51,6 +51,7 @@ static int cdns_pcie_ep_set_bar(struct pci_epc *epc, u8 fn, struct pci_epf_bar *epf_bar) { struct cdns_pcie_ep *ep = epc_get_drvdata(epc); + struct cdns_pcie_epf *epf = &ep->epf[fn]; struct cdns_pcie *pcie = &ep->pcie; dma_addr_t bar_phys = epf_bar->phys_addr; enum pci_barno bar = epf_bar->barno; @@ -111,6 +112,8 @@ static int cdns_pcie_ep_set_bar(struct pci_epc *epc, u8 fn, CDNS_PCIE_LM_EP_FUNC_BAR_CFG_BAR_CTRL(b, ctrl)); cdns_pcie_writel(pcie, reg, cfg); + epf->epf_bar[bar] = epf_bar; + return 0; } @@ -118,6 +121,7 @@ static void cdns_pcie_ep_clear_bar(struct pci_epc *epc, u8 fn, struct pci_epf_bar *epf_bar) { struct cdns_pcie_ep *ep = epc_get_drvdata(epc); + struct cdns_pcie_epf *epf = &ep->epf[fn]; struct cdns_pcie *pcie = &ep->pcie; enum pci_barno bar = epf_bar->barno; u32 reg, cfg, b, ctrl; @@ -139,6 +143,8 @@ static void cdns_pcie_ep_clear_bar(struct pci_epc *epc, u8 fn, cdns_pcie_writel(pcie, CDNS_PCIE_AT_IB_EP_FUNC_BAR_ADDR0(fn, bar), 0); cdns_pcie_writel(pcie, CDNS_PCIE_AT_IB_EP_FUNC_BAR_ADDR1(fn, bar), 0); + + epf->epf_bar[bar] = NULL; } static int cdns_pcie_ep_map_addr(struct pci_epc *epc, u8 fn, phys_addr_t addr, @@ -224,6 +230,50 @@ static int cdns_pcie_ep_get_msi(struct pci_epc *epc, u8 fn) return mme; } +static int cdns_pcie_ep_get_msix(struct pci_epc *epc, u8 func_no) +{ + struct cdns_pcie_ep *ep = epc_get_drvdata(epc); + struct cdns_pcie *pcie = &ep->pcie; + u32 cap = CDNS_PCIE_EP_FUNC_MSIX_CAP_OFFSET; + u32 val, reg; + + reg = cap + PCI_MSIX_FLAGS; + val = cdns_pcie_ep_fn_readw(pcie, func_no, reg); + if (!(val & PCI_MSIX_FLAGS_ENABLE)) + return -EINVAL; + + val &= PCI_MSIX_FLAGS_QSIZE; + + return val; +} + +static int cdns_pcie_ep_set_msix(struct pci_epc *epc, u8 fn, u16 interrupts, + enum pci_barno bir, u32 offset) +{ + struct cdns_pcie_ep *ep = epc_get_drvdata(epc); + struct cdns_pcie *pcie = &ep->pcie; + u32 cap = CDNS_PCIE_EP_FUNC_MSIX_CAP_OFFSET; + u32 val, reg; + + reg = cap + PCI_MSIX_FLAGS; + val = cdns_pcie_ep_fn_readw(pcie, fn, reg); + val &= ~PCI_MSIX_FLAGS_QSIZE; + val |= interrupts; + cdns_pcie_ep_fn_writew(pcie, fn, reg, val); + + /* Set MSIX BAR and offset */ + reg = cap + PCI_MSIX_TABLE; + val = offset | bir; + cdns_pcie_ep_fn_writel(pcie, fn, reg, val); + + /* Set PBA BAR and offset. BAR must match MSIX BAR */ + reg = cap + PCI_MSIX_PBA; + val = (offset + (interrupts * PCI_MSIX_ENTRY_SIZE)) | bir; + cdns_pcie_ep_fn_writel(pcie, fn, reg, val); + + return 0; +} + static void cdns_pcie_ep_assert_intx(struct cdns_pcie_ep *ep, u8 fn, u8 intx, bool is_asserted) { @@ -333,6 +383,51 @@ static int cdns_pcie_ep_send_msi_irq(struct cdns_pcie_ep *ep, u8 fn, return 0; } +static int cdns_pcie_ep_send_msix_irq(struct cdns_pcie_ep *ep, u8 fn, + u16 interrupt_num) +{ + u32 cap = CDNS_PCIE_EP_FUNC_MSIX_CAP_OFFSET; + u32 tbl_offset, msg_data, reg; + struct cdns_pcie *pcie = &ep->pcie; + struct pci_epf_msix_tbl *msix_tbl; + struct cdns_pcie_epf *epf; + u64 pci_addr_mask = 0xff; + u64 msg_addr; + u16 flags; + u8 bir; + + /* Check whether the MSI-X feature has been enabled by the PCI host. */ + flags = cdns_pcie_ep_fn_readw(pcie, fn, cap + PCI_MSIX_FLAGS); + if (!(flags & PCI_MSIX_FLAGS_ENABLE)) + return -EINVAL; + + reg = cap + PCI_MSIX_TABLE; + tbl_offset = cdns_pcie_ep_fn_readl(pcie, fn, reg); + bir = tbl_offset & PCI_MSIX_TABLE_BIR; + tbl_offset &= PCI_MSIX_TABLE_OFFSET; + + epf = &ep->epf[fn]; + msix_tbl = epf->epf_bar[bir]->addr + tbl_offset; + msg_addr = msix_tbl[(interrupt_num - 1)].msg_addr; + msg_data = msix_tbl[(interrupt_num - 1)].msg_data; + + /* Set the outbound region if needed. */ + if (ep->irq_pci_addr != (msg_addr & ~pci_addr_mask) || + ep->irq_pci_fn != fn) { + /* First region was reserved for IRQ writes. */ + cdns_pcie_set_outbound_region(pcie, fn, 0, + false, + ep->irq_phys_addr, + msg_addr & ~pci_addr_mask, + pci_addr_mask + 1); + ep->irq_pci_addr = (msg_addr & ~pci_addr_mask); + ep->irq_pci_fn = fn; + } + writel(msg_data, ep->irq_cpu_addr + (msg_addr & pci_addr_mask)); + + return 0; +} + static int cdns_pcie_ep_raise_irq(struct pci_epc *epc, u8 fn, enum pci_epc_irq_type type, u16 interrupt_num) @@ -346,6 +441,9 @@ static int cdns_pcie_ep_raise_irq(struct pci_epc *epc, u8 fn, case PCI_EPC_IRQ_MSI: return cdns_pcie_ep_send_msi_irq(ep, fn, interrupt_num); + case PCI_EPC_IRQ_MSIX: + return cdns_pcie_ep_send_msix_irq(ep, fn, interrupt_num); + default: break; } @@ -383,7 +481,7 @@ static int cdns_pcie_ep_start(struct pci_epc *epc) static const struct pci_epc_features cdns_pcie_epc_features = { .linkup_notifier = false, .msi_capable = true, - .msix_capable = false, + .msix_capable = true, }; static const struct pci_epc_features* @@ -400,6 +498,8 @@ static const struct pci_epc_ops cdns_pcie_epc_ops = { .unmap_addr = cdns_pcie_ep_unmap_addr, .set_msi = cdns_pcie_ep_set_msi, .get_msi = cdns_pcie_ep_get_msi, + .set_msix = cdns_pcie_ep_set_msix, + .get_msix = cdns_pcie_ep_get_msix, .raise_irq = cdns_pcie_ep_raise_irq, .start = cdns_pcie_ep_start, .get_features = cdns_pcie_ep_get_features, @@ -458,6 +558,11 @@ int cdns_pcie_ep_setup(struct cdns_pcie_ep *ep) if (of_property_read_u8(np, "max-functions", &epc->max_functions) < 0) epc->max_functions = 1; + ep->epf = devm_kcalloc(dev, epc->max_functions, sizeof(*ep->epf), + GFP_KERNEL); + if (!ep->epf) + return -ENOMEM; + ret = pci_epc_mem_init(epc, pcie->mem_res->start, resource_size(pcie->mem_res), PAGE_SIZE); if (ret < 0) { diff --git a/drivers/pci/controller/cadence/pcie-cadence.h b/drivers/pci/controller/cadence/pcie-cadence.h index 7c804ac1dbc2a..dd910a1c30fbf 100644 --- a/drivers/pci/controller/cadence/pcie-cadence.h +++ b/drivers/pci/controller/cadence/pcie-cadence.h @@ -113,6 +113,7 @@ #define CDNS_PCIE_EP_FUNC_BASE(fn) (((fn) << 12) & GENMASK(19, 12)) #define CDNS_PCIE_EP_FUNC_MSI_CAP_OFFSET 0x90 +#define CDNS_PCIE_EP_FUNC_MSIX_CAP_OFFSET 0xb0 /* * Root Port Registers (PCI configuration space for the root port function) @@ -302,6 +303,14 @@ struct cdns_pcie_rc { bool avail_ib_bar[CDNS_PCIE_RP_MAX_IB]; }; +/** + * struct cdns_pcie_epf - Structure to hold info about endpoint function + * @epf_bar: reference to the pci_epf_bar for the six Base Address Registers + */ +struct cdns_pcie_epf { + struct pci_epf_bar *epf_bar[PCI_STD_NUM_BARS]; +}; + /** * struct cdns_pcie_ep - private data for this PCIe endpoint controller driver * @pcie: Cadence PCIe controller @@ -321,6 +330,7 @@ struct cdns_pcie_rc { * @lock: spin lock to disable interrupts while modifying PCIe controller * registers fields (RMW) accessible by both remote RC and EP to * minimize time between read and write + * @epf: Structure to hold info about endpoint function */ struct cdns_pcie_ep { struct cdns_pcie pcie; @@ -334,6 +344,7 @@ struct cdns_pcie_ep { u8 irq_pending; /* protect writing to PCI_STATUS while raising legacy interrupts */ spinlock_t lock; + struct cdns_pcie_epf *epf; }; -- GitLab From 431b53b81cdc9912041285f25c35a0924c5a4929 Mon Sep 17 00:00:00 2001 From: Kishon Vijay Abraham I Date: Wed, 22 Jul 2020 16:33:13 +0530 Subject: [PATCH 0990/1476] dt-bindings: PCI: Add host mode dt-bindings for TI's J721E SoC Add host mode dt-bindings for TI's J721E SoC. Link: https://lore.kernel.org/r/20200722110317.4744-12-kishon@ti.com Signed-off-by: Kishon Vijay Abraham I Signed-off-by: Lorenzo Pieralisi Reviewed-by: Rob Herring --- .../bindings/pci/ti,j721e-pci-host.yaml | 113 ++++++++++++++++++ 1 file changed, 113 insertions(+) create mode 100644 Documentation/devicetree/bindings/pci/ti,j721e-pci-host.yaml diff --git a/Documentation/devicetree/bindings/pci/ti,j721e-pci-host.yaml b/Documentation/devicetree/bindings/pci/ti,j721e-pci-host.yaml new file mode 100644 index 0000000000000..d7b60487c6c30 --- /dev/null +++ b/Documentation/devicetree/bindings/pci/ti,j721e-pci-host.yaml @@ -0,0 +1,113 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +# Copyright (C) 2019 Texas Instruments Incorporated - http://www.ti.com/ +%YAML 1.2 +--- +$id: "http://devicetree.org/schemas/pci/ti,j721e-pci-host.yaml#" +$schema: "http://devicetree.org/meta-schemas/core.yaml#" + +title: TI J721E PCI Host (PCIe Wrapper) + +maintainers: + - Kishon Vijay Abraham I + +allOf: + - $ref: "cdns-pcie-host.yaml#" + +properties: + compatible: + enum: + - ti,j721e-pcie-host + + reg: + maxItems: 4 + + reg-names: + items: + - const: intd_cfg + - const: user_cfg + - const: reg + - const: cfg + + ti,syscon-pcie-ctrl: + description: Phandle to the SYSCON entry required for configuring PCIe mode + and link speed. + allOf: + - $ref: /schemas/types.yaml#/definitions/phandle + + power-domains: + maxItems: 1 + + clocks: + maxItems: 1 + description: clock-specifier to represent input to the PCIe + + clock-names: + items: + - const: fck + + vendor-id: + const: 0x104c + + device-id: + const: 0xb00d + + msi-map: true + +required: + - compatible + - reg + - reg-names + - ti,syscon-pcie-ctrl + - max-link-speed + - num-lanes + - power-domains + - clocks + - clock-names + - vendor-id + - device-id + - msi-map + - dma-coherent + - dma-ranges + - ranges + - reset-gpios + - phys + - phy-names + +examples: + - | + #include + #include + + bus { + #address-cells = <2>; + #size-cells = <2>; + + pcie0_rc: pcie@2900000 { + compatible = "ti,j721e-pcie-host"; + reg = <0x00 0x02900000 0x00 0x1000>, + <0x00 0x02907000 0x00 0x400>, + <0x00 0x0d000000 0x00 0x00800000>, + <0x00 0x10000000 0x00 0x00001000>; + reg-names = "intd_cfg", "user_cfg", "reg", "cfg"; + ti,syscon-pcie-ctrl = <&pcie0_ctrl>; + max-link-speed = <3>; + num-lanes = <2>; + power-domains = <&k3_pds 239 TI_SCI_PD_EXCLUSIVE>; + clocks = <&k3_clks 239 1>; + clock-names = "fck"; + device_type = "pci"; + #address-cells = <3>; + #size-cells = <2>; + bus-range = <0x0 0xf>; + vendor-id = <0x104c>; + device-id = <0xb00d>; + msi-map = <0x0 &gic_its 0x0 0x10000>; + dma-coherent; + reset-gpios = <&exp1 6 GPIO_ACTIVE_HIGH>; + phys = <&serdes0_pcie_link>; + phy-names = "pcie-phy"; + ranges = <0x01000000 0x0 0x10001000 0x00 0x10001000 0x0 0x0010000>, + <0x02000000 0x0 0x10011000 0x00 0x10011000 0x0 0x7fef000>; + dma-ranges = <0x02000000 0x0 0x0 0x0 0x0 0x10000 0x0>; + }; + }; -- GitLab From 45b39e928966fe520de9f4bab6c39a8eca4d3180 Mon Sep 17 00:00:00 2001 From: Kishon Vijay Abraham I Date: Wed, 22 Jul 2020 16:33:14 +0530 Subject: [PATCH 0991/1476] dt-bindings: PCI: Add EP mode dt-bindings for TI's J721E SoC Add PCIe EP mode dt-bindings for TI's J721E SoC. Link: https://lore.kernel.org/r/20200722110317.4744-13-kishon@ti.com Signed-off-by: Kishon Vijay Abraham I Signed-off-by: Lorenzo Pieralisi Reviewed-by: Rob Herring --- .../bindings/pci/ti,j721e-pci-ep.yaml | 94 +++++++++++++++++++ 1 file changed, 94 insertions(+) create mode 100644 Documentation/devicetree/bindings/pci/ti,j721e-pci-ep.yaml diff --git a/Documentation/devicetree/bindings/pci/ti,j721e-pci-ep.yaml b/Documentation/devicetree/bindings/pci/ti,j721e-pci-ep.yaml new file mode 100644 index 0000000000000..cfe25cface21f --- /dev/null +++ b/Documentation/devicetree/bindings/pci/ti,j721e-pci-ep.yaml @@ -0,0 +1,94 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +# Copyright (C) 2020 Texas Instruments Incorporated - http://www.ti.com/ +%YAML 1.2 +--- +$id: "http://devicetree.org/schemas/pci/ti,j721e-pci-ep.yaml#" +$schema: "http://devicetree.org/meta-schemas/core.yaml#" + +title: TI J721E PCI EP (PCIe Wrapper) + +maintainers: + - Kishon Vijay Abraham I + +allOf: + - $ref: "cdns-pcie-ep.yaml#" + +properties: + compatible: + enum: + - ti,j721e-pcie-ep + + reg: + maxItems: 4 + + reg-names: + items: + - const: intd_cfg + - const: user_cfg + - const: reg + - const: mem + + ti,syscon-pcie-ctrl: + description: Phandle to the SYSCON entry required for configuring PCIe mode + and link speed. + allOf: + - $ref: /schemas/types.yaml#/definitions/phandle + + power-domains: + maxItems: 1 + + clocks: + maxItems: 1 + description: clock-specifier to represent input to the PCIe + + clock-names: + items: + - const: fck + + dma-coherent: + description: Indicates that the PCIe IP block can ensure the coherency + +required: + - compatible + - reg + - reg-names + - ti,syscon-pcie-ctrl + - max-link-speed + - num-lanes + - power-domains + - clocks + - clock-names + - cdns,max-outbound-regions + - dma-coherent + - max-functions + - phys + - phy-names + +examples: + - | + #include + + bus { + #address-cells = <2>; + #size-cells = <2>; + + pcie0_ep: pcie-ep@d000000 { + compatible = "ti,j721e-pcie-ep"; + reg = <0x00 0x02900000 0x00 0x1000>, + <0x00 0x02907000 0x00 0x400>, + <0x00 0x0d000000 0x00 0x00800000>, + <0x00 0x10000000 0x00 0x08000000>; + reg-names = "intd_cfg", "user_cfg", "reg", "mem"; + ti,syscon-pcie-ctrl = <&pcie0_ctrl>; + max-link-speed = <3>; + num-lanes = <2>; + power-domains = <&k3_pds 239 TI_SCI_PD_EXCLUSIVE>; + clocks = <&k3_clks 239 1>; + clock-names = "fck"; + cdns,max-outbound-regions = <16>; + max-functions = /bits/ 8 <6>; + dma-coherent; + phys = <&serdes0_pcie_link>; + phy-names = "pcie-phy"; + }; + }; -- GitLab From f376b45e861d8b7b34bf0eceeecfdd00dbe65cde Mon Sep 17 00:00:00 2001 From: Brian Foster Date: Thu, 16 Jul 2020 07:39:29 -0700 Subject: [PATCH 0992/1476] xfs: drain the buf delwri queue before xfsaild idles xfsaild is racy with respect to transaction abort and shutdown in that the task can idle or exit with an empty AIL but buffers still on the delwri queue. This was partly addressed by cancelling the delwri queue before the task exits to prevent memory leaks, but it's also possible for xfsaild to empty and idle with buffers on the delwri queue. For example, a transaction that pins a buffer that also happens to sit on the AIL delwri queue will explicitly remove the associated log item from the AIL if the transaction aborts. The side effect of this is an unmount hang in xfs_wait_buftarg() as the associated buffers remain held by the delwri queue indefinitely. This is reproduced on repeated runs of generic/531 with an fs format (-mrmapbt=1 -bsize=1k) that happens to also reproduce transaction aborts. Update xfsaild to not idle until both the AIL and associated delwri queue are empty and update the push code to continue delwri queue submission attempts even when the AIL is empty. This allows the AIL to eventually release aborted buffers stranded on the delwri queue when they are unlocked by the associated transaction. This should have no significant effect on normal runtime behavior because the xfsaild currently idles only when the AIL is empty and in practice the AIL is rarely empty with a populated delwri queue. The items must be AIL resident to land in the queue in the first place and generally aren't removed until writeback completes. Note that the pre-existing delwri queue cancel logic in the exit path is retained because task stop is external, could technically come at any point, and xfsaild is still responsible to release its buffer references before it exits. Signed-off-by: Brian Foster Reviewed-by: Christoph Hellwig Reviewed-by: Darrick J. Wong Signed-off-by: Darrick J. Wong --- fs/xfs/xfs_trans_ail.c | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/fs/xfs/xfs_trans_ail.c b/fs/xfs/xfs_trans_ail.c index c3be6e4401343..0c783d3396750 100644 --- a/fs/xfs/xfs_trans_ail.c +++ b/fs/xfs/xfs_trans_ail.c @@ -448,16 +448,10 @@ xfsaild_push( target = ailp->ail_target; ailp->ail_target_prev = target; + /* we're done if the AIL is empty or our push has reached the end */ lip = xfs_trans_ail_cursor_first(ailp, &cur, ailp->ail_last_pushed_lsn); - if (!lip) { - /* - * If the AIL is empty or our push has reached the end we are - * done now. - */ - xfs_trans_ail_cursor_done(&cur); - spin_unlock(&ailp->ail_lock); + if (!lip) goto out_done; - } XFS_STATS_INC(mp, xs_push_ail); @@ -539,6 +533,8 @@ xfsaild_push( break; lsn = lip->li_lsn; } + +out_done: xfs_trans_ail_cursor_done(&cur); spin_unlock(&ailp->ail_lock); @@ -546,7 +542,6 @@ xfsaild_push( ailp->ail_log_flush++; if (!count || XFS_LSN_CMP(lsn, target) >= 0) { -out_done: /* * We reached the target or the AIL is empty, so wait a bit * longer for I/O to complete and remove pushed items from the @@ -638,7 +633,8 @@ xfsaild( */ smp_rmb(); if (!xfs_ail_min(ailp) && - ailp->ail_target == ailp->ail_target_prev) { + ailp->ail_target == ailp->ail_target_prev && + list_empty(&ailp->ail_buf_list)) { spin_unlock(&ailp->ail_lock); freezable_schedule(); tout = 0; -- GitLab From b2a8864728683443f34a9fd33a2b78b860934cc1 Mon Sep 17 00:00:00 2001 From: Brian Foster Date: Wed, 15 Jul 2020 18:44:50 -0700 Subject: [PATCH 0993/1476] xfs: fix inode allocation block res calculation precedence The block reservation calculation for inode allocation is supposed to consist of the blocks required for the inode chunk plus (maxlevels-1) of the inode btree multiplied by the number of inode btrees in the fs (2 when finobt is enabled, 1 otherwise). Instead, the macro returns (ialloc_blocks + 2) due to a precedence error in the calculation logic. This leads to block reservation overruns via generic/531 on small block filesystems with finobt enabled. Add braces to fix the calculation and reserve the appropriate number of blocks. Fixes: 9d43b180af67 ("xfs: update inode allocation/free transaction reservations for finobt") Signed-off-by: Brian Foster Reviewed-by: Darrick J. Wong Signed-off-by: Darrick J. Wong Reviewed-by: Christoph Hellwig --- fs/xfs/libxfs/xfs_trans_space.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/xfs/libxfs/xfs_trans_space.h b/fs/xfs/libxfs/xfs_trans_space.h index 88221c7a04ccf..c6df01a2a1585 100644 --- a/fs/xfs/libxfs/xfs_trans_space.h +++ b/fs/xfs/libxfs/xfs_trans_space.h @@ -57,7 +57,7 @@ XFS_DAREMOVE_SPACE_RES(mp, XFS_DATA_FORK) #define XFS_IALLOC_SPACE_RES(mp) \ (M_IGEO(mp)->ialloc_blks + \ - (xfs_sb_version_hasfinobt(&mp->m_sb) ? 2 : 1 * \ + ((xfs_sb_version_hasfinobt(&mp->m_sb) ? 2 : 1) * \ (M_IGEO(mp)->inobt_maxlevels - 1))) /* -- GitLab From c97738a960a86081a147e7d436138e6481757445 Mon Sep 17 00:00:00 2001 From: "Darrick J. Wong" Date: Tue, 14 Jul 2020 10:36:08 -0700 Subject: [PATCH 0994/1476] xfs: clear XFS_DQ_FREEING if we can't lock the dquot buffer to flush In commit 8d3d7e2b35ea, we changed xfs_qm_dqpurge to bail out if we can't lock the dquot buf to flush the dquot. This prevents the AIL from blocking on the dquot, but it also forgets to clear the FREEING flag on its way out. A subsequent purge attempt will see the FREEING flag is set and bail out, which leads to dqpurge_all failing to purge all the dquots. (copy-pasting from Dave Chinner's identical patch) This was found by inspection after having xfs/305 hang 1 in ~50 iterations in a quotaoff operation: [ 8872.301115] xfs_quota D13888 92262 91813 0x00004002 [ 8872.302538] Call Trace: [ 8872.303193] __schedule+0x2d2/0x780 [ 8872.304108] ? do_raw_spin_unlock+0x57/0xd0 [ 8872.305198] schedule+0x6e/0xe0 [ 8872.306021] schedule_timeout+0x14d/0x300 [ 8872.307060] ? __next_timer_interrupt+0xe0/0xe0 [ 8872.308231] ? xfs_qm_dqusage_adjust+0x200/0x200 [ 8872.309422] schedule_timeout_uninterruptible+0x2a/0x30 [ 8872.310759] xfs_qm_dquot_walk.isra.0+0x15a/0x1b0 [ 8872.311971] xfs_qm_dqpurge_all+0x7f/0x90 [ 8872.313022] xfs_qm_scall_quotaoff+0x18d/0x2b0 [ 8872.314163] xfs_quota_disable+0x3a/0x60 [ 8872.315179] kernel_quotactl+0x7e2/0x8d0 [ 8872.316196] ? __do_sys_newstat+0x51/0x80 [ 8872.317238] __x64_sys_quotactl+0x1e/0x30 [ 8872.318266] do_syscall_64+0x46/0x90 [ 8872.319193] entry_SYSCALL_64_after_hwframe+0x44/0xa9 [ 8872.320490] RIP: 0033:0x7f46b5490f2a [ 8872.321414] Code: Bad RIP value. Returning -EAGAIN from xfs_qm_dqpurge() without clearing the XFS_DQ_FREEING flag means the xfs_qm_dqpurge_all() code can never free the dquot, and we loop forever waiting for the XFS_DQ_FREEING flag to go away on the dquot that leaked it via -EAGAIN. Fixes: 8d3d7e2b35ea ("xfs: trylock underlying buffer on dquot flush") Signed-off-by: Darrick J. Wong Reviewed-by: Allison Collins Reviewed-by: Chandan Babu R Reviewed-by: Christoph Hellwig Reviewed-by: Brian Foster Signed-off-by: Dave Chinner Reviewed-by: Dave Chinner --- fs/xfs/xfs_qm.c | 1 + 1 file changed, 1 insertion(+) diff --git a/fs/xfs/xfs_qm.c b/fs/xfs/xfs_qm.c index d6cd833173447..938023dd8ce5d 100644 --- a/fs/xfs/xfs_qm.c +++ b/fs/xfs/xfs_qm.c @@ -148,6 +148,7 @@ xfs_qm_dqpurge( error = xfs_bwrite(bp); xfs_buf_relse(bp); } else if (error == -EAGAIN) { + dqp->dq_flags &= ~XFS_DQ_FREEING; goto out_unlock; } xfs_dqflock(dqp); -- GitLab From f959b5d037e71a4d69b5bf71faffa065d9269b4a Mon Sep 17 00:00:00 2001 From: "Darrick J. Wong" Date: Tue, 14 Jul 2020 10:36:09 -0700 Subject: [PATCH 0995/1476] xfs: fix inode quota reservation checks xfs_trans_dqresv is the function that we use to make reservations against resource quotas. Each resource contains two counters: the q_core counter, which tracks resources allocated on disk; and the dquot reservation counter, which tracks how much of that resource has either been allocated or reserved by threads that are working on metadata updates. For disk blocks, we compare the proposed reservation counter against the hard and soft limits to decide if we're going to fail the operation. However, for inodes we inexplicably compare against the q_core counter, not the incore reservation count. Since the q_core counter is always lower than the reservation count and we unlock the dquot between reservation and transaction commit, this means that multiple threads can reserve the last inode count before we hit the hard limit, and when they commit, we'll be well over the hard limit. Fix this by checking against the incore inode reservation counter, since we would appear to maintain that correctly (and that's what we report in GETQUOTA). Signed-off-by: Darrick J. Wong Reviewed-by: Allison Collins Reviewed-by: Chandan Babu R Reviewed-by: Christoph Hellwig --- fs/xfs/xfs_trans_dquot.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/xfs/xfs_trans_dquot.c b/fs/xfs/xfs_trans_dquot.c index c0f73b82c0551..ed0ce8b301b40 100644 --- a/fs/xfs/xfs_trans_dquot.c +++ b/fs/xfs/xfs_trans_dquot.c @@ -647,7 +647,7 @@ xfs_trans_dqresv( } } if (ninos > 0) { - total_count = be64_to_cpu(dqp->q_core.d_icount) + ninos; + total_count = dqp->q_res_icount + ninos; timer = be32_to_cpu(dqp->q_core.d_itimer); warns = be16_to_cpu(dqp->q_core.d_iwarns); warnlimit = defq->iwarnlimit; -- GitLab From afeda6000b0cb6d873920be4ce67f351f017a654 Mon Sep 17 00:00:00 2001 From: "Darrick J. Wong" Date: Tue, 14 Jul 2020 10:36:09 -0700 Subject: [PATCH 0996/1476] xfs: validate ondisk/incore dquot flags While loading dquot records off disk, make sure that the quota type flags are the same between the incore dquot and the ondisk dquot. Signed-off-by: Darrick J. Wong Reviewed-by: Christoph Hellwig Reviewed-by: Chandan Babu R --- fs/xfs/xfs_dquot.c | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/fs/xfs/xfs_dquot.c b/fs/xfs/xfs_dquot.c index 76353c9a723ee..7503c66955698 100644 --- a/fs/xfs/xfs_dquot.c +++ b/fs/xfs/xfs_dquot.c @@ -23,6 +23,7 @@ #include "xfs_trace.h" #include "xfs_log.h" #include "xfs_bmap_btree.h" +#include "xfs_error.h" /* * Lock order: @@ -524,13 +525,26 @@ xfs_dquot_alloc( } /* Copy the in-core quota fields in from the on-disk buffer. */ -STATIC void +STATIC int xfs_dquot_from_disk( struct xfs_dquot *dqp, struct xfs_buf *bp) { struct xfs_disk_dquot *ddqp = bp->b_addr + dqp->q_bufoffset; + /* + * Ensure that we got the type and ID we were looking for. + * Everything else was checked by the dquot buffer verifier. + */ + if ((ddqp->d_flags & XFS_DQ_ALLTYPES) != dqp->dq_flags || + ddqp->d_id != dqp->q_core.d_id) { + xfs_alert_tag(bp->b_mount, XFS_PTAG_VERIFIER_ERROR, + "Metadata corruption detected at %pS, quota %u", + __this_address, be32_to_cpu(dqp->q_core.d_id)); + xfs_alert(bp->b_mount, "Unmount and run xfs_repair"); + return -EFSCORRUPTED; + } + /* copy everything from disk dquot to the incore dquot */ memcpy(&dqp->q_core, ddqp, sizeof(struct xfs_disk_dquot)); @@ -544,6 +558,7 @@ xfs_dquot_from_disk( /* initialize the dquot speculative prealloc thresholds */ xfs_dquot_set_prealloc_limits(dqp); + return 0; } /* Allocate and initialize the dquot buffer for this in-core dquot. */ @@ -617,9 +632,11 @@ xfs_qm_dqread( * further. */ ASSERT(xfs_buf_islocked(bp)); - xfs_dquot_from_disk(dqp, bp); - + error = xfs_dquot_from_disk(dqp, bp); xfs_buf_relse(bp); + if (error) + goto err; + *dqpp = dqp; return error; -- GitLab From 41ed4a5f2ba41882d8fbdf4cf455855e80ab6b90 Mon Sep 17 00:00:00 2001 From: "Darrick J. Wong" Date: Tue, 14 Jul 2020 10:36:09 -0700 Subject: [PATCH 0997/1476] xfs: move the flags argument of xfs_qm_scall_trunc_qfiles to XFS_QMOPT_* Since xfs_qm_scall_trunc_qfiles can take a bitset of quota types that we want to truncate, change the flags argument to take XFS_QMOPT_[UGP}QUOTA so that the next patch can start to deprecate XFS_DQ_*. Signed-off-by: Darrick J. Wong Reviewed-by: Christoph Hellwig Reviewed-by: Chandan Babu R --- fs/xfs/xfs_qm_syscalls.c | 8 ++++---- fs/xfs/xfs_quotaops.c | 6 +++--- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/fs/xfs/xfs_qm_syscalls.c b/fs/xfs/xfs_qm_syscalls.c index 7effd7a281365..35fad348e3a22 100644 --- a/fs/xfs/xfs_qm_syscalls.c +++ b/fs/xfs/xfs_qm_syscalls.c @@ -322,23 +322,23 @@ xfs_qm_scall_trunc_qfiles( int error = -EINVAL; if (!xfs_sb_version_hasquota(&mp->m_sb) || flags == 0 || - (flags & ~XFS_DQ_ALLTYPES)) { + (flags & ~XFS_QMOPT_QUOTALL)) { xfs_debug(mp, "%s: flags=%x m_qflags=%x", __func__, flags, mp->m_qflags); return -EINVAL; } - if (flags & XFS_DQ_USER) { + if (flags & XFS_QMOPT_UQUOTA) { error = xfs_qm_scall_trunc_qfile(mp, mp->m_sb.sb_uquotino); if (error) return error; } - if (flags & XFS_DQ_GROUP) { + if (flags & XFS_QMOPT_GQUOTA) { error = xfs_qm_scall_trunc_qfile(mp, mp->m_sb.sb_gquotino); if (error) return error; } - if (flags & XFS_DQ_PROJ) + if (flags & XFS_QMOPT_PQUOTA) error = xfs_qm_scall_trunc_qfile(mp, mp->m_sb.sb_pquotino); return error; diff --git a/fs/xfs/xfs_quotaops.c b/fs/xfs/xfs_quotaops.c index bf809b77a3160..0868e6ee22191 100644 --- a/fs/xfs/xfs_quotaops.c +++ b/fs/xfs/xfs_quotaops.c @@ -205,11 +205,11 @@ xfs_fs_rm_xquota( return -EINVAL; if (uflags & FS_USER_QUOTA) - flags |= XFS_DQ_USER; + flags |= XFS_QMOPT_UQUOTA; if (uflags & FS_GROUP_QUOTA) - flags |= XFS_DQ_GROUP; + flags |= XFS_QMOPT_GQUOTA; if (uflags & FS_PROJ_QUOTA) - flags |= XFS_DQ_PROJ; + flags |= XFS_QMOPT_PQUOTA; return xfs_qm_scall_trunc_qfiles(mp, flags); } -- GitLab From 0dcc0728c119cb1b295c18c379bb39416ce86aeb Mon Sep 17 00:00:00 2001 From: "Darrick J. Wong" Date: Tue, 14 Jul 2020 10:37:12 -0700 Subject: [PATCH 0998/1476] xfs: refactor quotacheck flags usage We only use the XFS_QMOPT flags in quotacheck to signal the quota type, so rip out all the flags handling and just pass the type all the way through. Signed-off-by: Darrick J. Wong Reviewed-by: Christoph Hellwig Reviewed-by: Chandan Babu R --- fs/xfs/xfs_qm.c | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/fs/xfs/xfs_qm.c b/fs/xfs/xfs_qm.c index 938023dd8ce5d..259ec5738c338 100644 --- a/fs/xfs/xfs_qm.c +++ b/fs/xfs/xfs_qm.c @@ -902,17 +902,13 @@ xfs_qm_reset_dqcounts_all( xfs_dqid_t firstid, xfs_fsblock_t bno, xfs_filblks_t blkcnt, - uint flags, + uint type, struct list_head *buffer_list) { struct xfs_buf *bp; - int error; - int type; + int error = 0; ASSERT(blkcnt > 0); - type = flags & XFS_QMOPT_UQUOTA ? XFS_DQ_USER : - (flags & XFS_QMOPT_PQUOTA ? XFS_DQ_PROJ : XFS_DQ_GROUP); - error = 0; /* * Blkcnt arg can be a very big number, and might even be @@ -972,7 +968,7 @@ STATIC int xfs_qm_reset_dqcounts_buf( struct xfs_mount *mp, struct xfs_inode *qip, - uint flags, + uint type, struct list_head *buffer_list) { struct xfs_bmbt_irec *map; @@ -1048,7 +1044,7 @@ xfs_qm_reset_dqcounts_buf( error = xfs_qm_reset_dqcounts_all(mp, firstid, map[i].br_startblock, map[i].br_blockcount, - flags, buffer_list); + type, buffer_list); if (error) goto out; } @@ -1292,7 +1288,7 @@ xfs_qm_quotacheck( * We don't log our changes till later. */ if (uip) { - error = xfs_qm_reset_dqcounts_buf(mp, uip, XFS_QMOPT_UQUOTA, + error = xfs_qm_reset_dqcounts_buf(mp, uip, XFS_DQ_USER, &buffer_list); if (error) goto error_return; @@ -1300,7 +1296,7 @@ xfs_qm_quotacheck( } if (gip) { - error = xfs_qm_reset_dqcounts_buf(mp, gip, XFS_QMOPT_GQUOTA, + error = xfs_qm_reset_dqcounts_buf(mp, gip, XFS_DQ_GROUP, &buffer_list); if (error) goto error_return; @@ -1308,7 +1304,7 @@ xfs_qm_quotacheck( } if (pip) { - error = xfs_qm_reset_dqcounts_buf(mp, pip, XFS_QMOPT_PQUOTA, + error = xfs_qm_reset_dqcounts_buf(mp, pip, XFS_DQ_PROJ, &buffer_list); if (error) goto error_return; -- GitLab From 985a78fdde15e1730383f99867ca38b5648444bf Mon Sep 17 00:00:00 2001 From: "Darrick J. Wong" Date: Tue, 14 Jul 2020 10:37:13 -0700 Subject: [PATCH 0999/1476] xfs: rename dquot incore state flags Rename the existing incore dquot "dq_flags" field to "q_flags" to match everything else in the structure, then move the two actual dquot state flags to the XFS_DQFLAG_ namespace from XFS_DQ_. Signed-off-by: Darrick J. Wong Reviewed-by: Christoph Hellwig Reviewed-by: Chandan Babu R --- fs/xfs/libxfs/xfs_quota_defs.h | 10 +++++----- fs/xfs/xfs_dquot.c | 6 +++--- fs/xfs/xfs_dquot.h | 5 +++-- fs/xfs/xfs_qm.c | 12 ++++++------ fs/xfs/xfs_qm_syscalls.c | 2 +- fs/xfs/xfs_trace.h | 4 ++-- fs/xfs/xfs_trans_dquot.c | 2 +- 7 files changed, 21 insertions(+), 20 deletions(-) diff --git a/fs/xfs/libxfs/xfs_quota_defs.h b/fs/xfs/libxfs/xfs_quota_defs.h index 56d9dd787e7b6..e2da08055e6b7 100644 --- a/fs/xfs/libxfs/xfs_quota_defs.h +++ b/fs/xfs/libxfs/xfs_quota_defs.h @@ -24,17 +24,17 @@ typedef uint16_t xfs_qwarncnt_t; #define XFS_DQ_USER 0x0001 /* a user quota */ #define XFS_DQ_PROJ 0x0002 /* project quota */ #define XFS_DQ_GROUP 0x0004 /* a group quota */ -#define XFS_DQ_DIRTY 0x0008 /* dquot is dirty */ -#define XFS_DQ_FREEING 0x0010 /* dquot is being torn down */ +#define XFS_DQFLAG_DIRTY 0x0008 /* dquot is dirty */ +#define XFS_DQFLAG_FREEING 0x0010 /* dquot is being torn down */ #define XFS_DQ_ALLTYPES (XFS_DQ_USER|XFS_DQ_PROJ|XFS_DQ_GROUP) -#define XFS_DQ_FLAGS \ +#define XFS_DQFLAG_STRINGS \ { XFS_DQ_USER, "USER" }, \ { XFS_DQ_PROJ, "PROJ" }, \ { XFS_DQ_GROUP, "GROUP" }, \ - { XFS_DQ_DIRTY, "DIRTY" }, \ - { XFS_DQ_FREEING, "FREEING" } + { XFS_DQFLAG_DIRTY, "DIRTY" }, \ + { XFS_DQFLAG_FREEING, "FREEING" } /* * We have the possibility of all three quota types being active at once, and diff --git a/fs/xfs/xfs_dquot.c b/fs/xfs/xfs_dquot.c index 7503c66955698..5408ec82e0d9f 100644 --- a/fs/xfs/xfs_dquot.c +++ b/fs/xfs/xfs_dquot.c @@ -723,7 +723,7 @@ restart: } xfs_dqlock(dqp); - if (dqp->dq_flags & XFS_DQ_FREEING) { + if (dqp->q_flags & XFS_DQFLAG_FREEING) { xfs_dqunlock(dqp); mutex_unlock(&qi->qi_tree_lock); trace_xfs_dqget_freeing(dqp); @@ -1179,7 +1179,7 @@ xfs_qm_dqflush( /* * Clear the dirty field and remember the flush lsn for later use. */ - dqp->dq_flags &= ~XFS_DQ_DIRTY; + dqp->q_flags &= ~XFS_DQFLAG_DIRTY; xfs_trans_ail_copy_lsn(mp->m_ail, &dqp->q_logitem.qli_flush_lsn, &dqp->q_logitem.qli_item.li_lsn); @@ -1220,7 +1220,7 @@ xfs_qm_dqflush( return 0; out_abort: - dqp->dq_flags &= ~XFS_DQ_DIRTY; + dqp->q_flags &= ~XFS_DQFLAG_DIRTY; xfs_trans_ail_delete(lip, 0); xfs_force_shutdown(mp, SHUTDOWN_CORRUPT_INCORE); out_unlock: diff --git a/fs/xfs/xfs_dquot.h b/fs/xfs/xfs_dquot.h index 71e36c85e20b6..6d43c48c67a17 100644 --- a/fs/xfs/xfs_dquot.h +++ b/fs/xfs/xfs_dquot.h @@ -31,9 +31,10 @@ enum { * The incore dquot structure */ struct xfs_dquot { - uint dq_flags; struct list_head q_lru; struct xfs_mount *q_mount; + uint8_t dq_flags; + uint16_t q_flags; uint q_nrefs; xfs_daddr_t q_blkno; int q_bufoffset; @@ -145,7 +146,7 @@ static inline bool xfs_dquot_lowsp(struct xfs_dquot *dqp) } #define XFS_DQ_IS_LOCKED(dqp) (mutex_is_locked(&((dqp)->q_qlock))) -#define XFS_DQ_IS_DIRTY(dqp) ((dqp)->dq_flags & XFS_DQ_DIRTY) +#define XFS_DQ_IS_DIRTY(dqp) ((dqp)->q_flags & XFS_DQFLAG_DIRTY) #define XFS_QM_ISUDQ(dqp) ((dqp)->dq_flags & XFS_DQ_USER) #define XFS_QM_ISPDQ(dqp) ((dqp)->dq_flags & XFS_DQ_PROJ) #define XFS_QM_ISGDQ(dqp) ((dqp)->dq_flags & XFS_DQ_GROUP) diff --git a/fs/xfs/xfs_qm.c b/fs/xfs/xfs_qm.c index 259ec5738c338..f80bcba83616e 100644 --- a/fs/xfs/xfs_qm.c +++ b/fs/xfs/xfs_qm.c @@ -124,10 +124,10 @@ xfs_qm_dqpurge( int error = -EAGAIN; xfs_dqlock(dqp); - if ((dqp->dq_flags & XFS_DQ_FREEING) || dqp->q_nrefs != 0) + if ((dqp->q_flags & XFS_DQFLAG_FREEING) || dqp->q_nrefs != 0) goto out_unlock; - dqp->dq_flags |= XFS_DQ_FREEING; + dqp->q_flags |= XFS_DQFLAG_FREEING; xfs_dqflock(dqp); @@ -148,7 +148,7 @@ xfs_qm_dqpurge( error = xfs_bwrite(bp); xfs_buf_relse(bp); } else if (error == -EAGAIN) { - dqp->dq_flags &= ~XFS_DQ_FREEING; + dqp->q_flags &= ~XFS_DQFLAG_FREEING; goto out_unlock; } xfs_dqflock(dqp); @@ -474,7 +474,7 @@ xfs_qm_dquot_isolate( /* * Prevent lookups now that we are past the point of no return. */ - dqp->dq_flags |= XFS_DQ_FREEING; + dqp->q_flags |= XFS_DQFLAG_FREEING; xfs_dqunlock(dqp); ASSERT(dqp->q_nrefs == 0); @@ -1113,7 +1113,7 @@ xfs_qm_quotacheck_dqadjust( xfs_qm_adjust_dqtimers(mp, dqp); } - dqp->dq_flags |= XFS_DQ_DIRTY; + dqp->q_flags |= XFS_DQFLAG_DIRTY; xfs_qm_dqput(dqp); return 0; } @@ -1219,7 +1219,7 @@ xfs_qm_flush_one( int error = 0; xfs_dqlock(dqp); - if (dqp->dq_flags & XFS_DQ_FREEING) + if (dqp->q_flags & XFS_DQFLAG_FREEING) goto out_unlock; if (!XFS_DQ_IS_DIRTY(dqp)) goto out_unlock; diff --git a/fs/xfs/xfs_qm_syscalls.c b/fs/xfs/xfs_qm_syscalls.c index 35fad348e3a22..a9e4a7e1b9d9a 100644 --- a/fs/xfs/xfs_qm_syscalls.c +++ b/fs/xfs/xfs_qm_syscalls.c @@ -598,7 +598,7 @@ xfs_qm_scall_setqlim( */ xfs_qm_adjust_dqtimers(mp, dqp); } - dqp->dq_flags |= XFS_DQ_DIRTY; + dqp->q_flags |= XFS_DQFLAG_DIRTY; xfs_trans_log_dquot(tp, dqp); error = xfs_trans_commit(tp); diff --git a/fs/xfs/xfs_trace.h b/fs/xfs/xfs_trace.h index 50c478374a31b..97d8daf118163 100644 --- a/fs/xfs/xfs_trace.h +++ b/fs/xfs/xfs_trace.h @@ -877,7 +877,7 @@ DECLARE_EVENT_CLASS(xfs_dquot_class, TP_fast_assign( __entry->dev = dqp->q_mount->m_super->s_dev; __entry->id = be32_to_cpu(dqp->q_core.d_id); - __entry->flags = dqp->dq_flags; + __entry->flags = dqp->dq_flags | dqp->q_flags; __entry->nrefs = dqp->q_nrefs; __entry->res_bcount = dqp->q_res_bcount; __entry->bcount = be64_to_cpu(dqp->q_core.d_bcount); @@ -896,7 +896,7 @@ DECLARE_EVENT_CLASS(xfs_dquot_class, "icnt 0x%llx ihardlimit 0x%llx isoftlimit 0x%llx]", MAJOR(__entry->dev), MINOR(__entry->dev), __entry->id, - __print_flags(__entry->flags, "|", XFS_DQ_FLAGS), + __print_flags(__entry->flags, "|", XFS_DQFLAG_STRINGS), __entry->nrefs, __entry->res_bcount, __entry->bcount, diff --git a/fs/xfs/xfs_trans_dquot.c b/fs/xfs/xfs_trans_dquot.c index ed0ce8b301b40..8963cfac2a6a2 100644 --- a/fs/xfs/xfs_trans_dquot.c +++ b/fs/xfs/xfs_trans_dquot.c @@ -391,7 +391,7 @@ xfs_trans_apply_dquot_deltas( xfs_qm_adjust_dqtimers(tp->t_mountp, dqp); } - dqp->dq_flags |= XFS_DQ_DIRTY; + dqp->q_flags |= XFS_DQFLAG_DIRTY; /* * add this to the list of items to get logged */ -- GitLab From cb64e1299364a51bf60e96f2a35df31f47aa2eee Mon Sep 17 00:00:00 2001 From: "Darrick J. Wong" Date: Tue, 14 Jul 2020 10:37:13 -0700 Subject: [PATCH 1000/1476] xfs: make XFS_DQUOT_CLUSTER_SIZE_FSB part of the ondisk format Move the dquot cluster size #define to xfs_format.h. It is an important part of the ondisk format because the ondisk dquot record size is not an even power of two, which means that the buffer size we use is significant here because the kernel leaves slack space at the end of the buffer to avoid having to deal with a dquot record crossing a block boundary. This is also an excuse to fix one of the longstanding discrepancies between kernel and userspace libxfs headers. Signed-off-by: Darrick J. Wong Reviewed-by: Christoph Hellwig Reviewed-by: Chandan Babu R --- fs/xfs/libxfs/xfs_format.h | 16 ++++++++++++++++ fs/xfs/xfs_qm.h | 11 ----------- 2 files changed, 16 insertions(+), 11 deletions(-) diff --git a/fs/xfs/libxfs/xfs_format.h b/fs/xfs/libxfs/xfs_format.h index b42a52bfa1e97..4f665646ba7da 100644 --- a/fs/xfs/libxfs/xfs_format.h +++ b/fs/xfs/libxfs/xfs_format.h @@ -1198,6 +1198,22 @@ typedef struct xfs_dqblk { #define XFS_DQUOT_CRC_OFF offsetof(struct xfs_dqblk, dd_crc) +/* + * This defines the unit of allocation of dquots. + * + * Currently, it is just one file system block, and a 4K blk contains 30 + * (136 * 30 = 4080) dquots. It's probably not worth trying to make + * this more dynamic. + * + * However, if this number is changed, we have to make sure that we don't + * implicitly assume that we do allocations in chunks of a single filesystem + * block in the dquot/xqm code. + * + * This is part of the ondisk format because the structure size is not a power + * of two, which leaves slack at the end of the disk block. + */ +#define XFS_DQUOT_CLUSTER_SIZE_FSB (xfs_filblks_t)1 + /* * Remote symlink format and access functions. */ diff --git a/fs/xfs/xfs_qm.h b/fs/xfs/xfs_qm.h index 7b0e771fcbceb..2c8ca9df23af2 100644 --- a/fs/xfs/xfs_qm.h +++ b/fs/xfs/xfs_qm.h @@ -30,17 +30,6 @@ extern struct kmem_zone *xfs_qm_dqtrxzone; !dqp->q_core.d_rtbcount && \ !dqp->q_core.d_icount) -/* - * This defines the unit of allocation of dquots. - * Currently, it is just one file system block, and a 4K blk contains 30 - * (136 * 30 = 4080) dquots. It's probably not worth trying to make - * this more dynamic. - * XXXsup However, if this number is changed, we have to make sure that we don't - * implicitly assume that we do allocations in chunks of a single filesystem - * block in the dquot/xqm code. - */ -#define XFS_DQUOT_CLUSTER_SIZE_FSB (xfs_filblks_t)1 - /* Defaults for each quota type: time limits, warn limits, usage limits */ struct xfs_def_quota { time64_t btimelimit; /* limit for blks timer */ -- GitLab From 0b0fa1d1d16794ce48188cc6434ec38f6e1f1b4b Mon Sep 17 00:00:00 2001 From: "Darrick J. Wong" Date: Tue, 14 Jul 2020 10:37:22 -0700 Subject: [PATCH 1001/1476] xfs: stop using q_core.d_flags in the quota code Use the incore dq_flags to figure out the dquot type. This is the first step towards removing xfs_disk_dquot from the incore dquot. Signed-off-by: Darrick J. Wong Reviewed-by: Christoph Hellwig Reviewed-by: Chandan Babu R --- fs/xfs/xfs_dquot.c | 36 ++++++++++++++++++++++++++++++++++-- fs/xfs/xfs_dquot.h | 2 ++ fs/xfs/xfs_dquot_item.c | 6 ++++-- 3 files changed, 40 insertions(+), 4 deletions(-) diff --git a/fs/xfs/xfs_dquot.c b/fs/xfs/xfs_dquot.c index 5408ec82e0d9f..3d1d876b45fcb 100644 --- a/fs/xfs/xfs_dquot.c +++ b/fs/xfs/xfs_dquot.c @@ -561,6 +561,15 @@ xfs_dquot_from_disk( return 0; } +/* Copy the in-core quota fields into the on-disk buffer. */ +void +xfs_dquot_to_disk( + struct xfs_disk_dquot *ddqp, + struct xfs_dquot *dqp) +{ + memcpy(ddqp, &dqp->q_core, sizeof(struct xfs_disk_dquot)); +} + /* Allocate and initialize the dquot buffer for this in-core dquot. */ static int xfs_qm_dqread_alloc( @@ -1115,6 +1124,21 @@ xfs_dquot_done( } } +/* Check incore dquot for errors before we flush. */ +static xfs_failaddr_t +xfs_qm_dqflush_check( + struct xfs_dquot *dqp) +{ + __u8 type = dqp->dq_flags & XFS_DQ_ALLTYPES; + + if (type != XFS_DQ_USER && + type != XFS_DQ_GROUP && + type != XFS_DQ_PROJ) + return __this_address; + + return NULL; +} + /* * Write a modified dquot to disk. * The dquot must be locked and the flush lock too taken by caller. @@ -1173,8 +1197,16 @@ xfs_qm_dqflush( goto out_abort; } - /* This is the only portion of data that needs to persist */ - memcpy(ddqp, &dqp->q_core, sizeof(struct xfs_disk_dquot)); + fa = xfs_qm_dqflush_check(dqp); + if (fa) { + xfs_alert(mp, "corrupt dquot ID 0x%x in memory at %pS", + be32_to_cpu(dqp->q_core.d_id), fa); + xfs_buf_relse(bp); + error = -EFSCORRUPTED; + goto out_abort; + } + + xfs_dquot_to_disk(ddqp, dqp); /* * Clear the dirty field and remember the flush lsn for later use. diff --git a/fs/xfs/xfs_dquot.h b/fs/xfs/xfs_dquot.h index 6d43c48c67a17..944e43a0e2028 100644 --- a/fs/xfs/xfs_dquot.h +++ b/fs/xfs/xfs_dquot.h @@ -145,6 +145,8 @@ static inline bool xfs_dquot_lowsp(struct xfs_dquot *dqp) return false; } +void xfs_dquot_to_disk(struct xfs_disk_dquot *ddqp, struct xfs_dquot *dqp); + #define XFS_DQ_IS_LOCKED(dqp) (mutex_is_locked(&((dqp)->q_qlock))) #define XFS_DQ_IS_DIRTY(dqp) ((dqp)->q_flags & XFS_DQFLAG_DIRTY) #define XFS_QM_ISUDQ(dqp) ((dqp)->dq_flags & XFS_DQ_USER) diff --git a/fs/xfs/xfs_dquot_item.c b/fs/xfs/xfs_dquot_item.c index d7e4de7151d7f..fc21e48c889c8 100644 --- a/fs/xfs/xfs_dquot_item.c +++ b/fs/xfs/xfs_dquot_item.c @@ -45,6 +45,7 @@ xfs_qm_dquot_logitem_format( struct xfs_log_item *lip, struct xfs_log_vec *lv) { + struct xfs_disk_dquot ddq; struct xfs_dq_logitem *qlip = DQUOT_ITEM(lip); struct xfs_log_iovec *vecp = NULL; struct xfs_dq_logformat *qlf; @@ -58,8 +59,9 @@ xfs_qm_dquot_logitem_format( qlf->qlf_boffset = qlip->qli_dquot->q_bufoffset; xlog_finish_iovec(lv, vecp, sizeof(struct xfs_dq_logformat)); - xlog_copy_iovec(lv, &vecp, XLOG_REG_TYPE_DQUOT, - &qlip->qli_dquot->q_core, + xfs_dquot_to_disk(&ddq, qlip->qli_dquot); + + xlog_copy_iovec(lv, &vecp, XLOG_REG_TYPE_DQUOT, &ddq, sizeof(struct xfs_disk_dquot)); } -- GitLab From c51df7334167e445f2cafc5511d6e2407a32e8f1 Mon Sep 17 00:00:00 2001 From: "Darrick J. Wong" Date: Tue, 14 Jul 2020 10:37:30 -0700 Subject: [PATCH 1002/1476] xfs: stop using q_core.d_id in the quota code Add a dquot id field to the incore dquot, and use that instead of the one in qcore. This eliminates a bunch of endian conversions and will eventually allow us to remove qcore entirely. We also rearrange the start of xfs_dquot to remove padding holes, saving 8 bytes. Signed-off-by: Darrick J. Wong Reviewed-by: Chandan Babu R Reviewed-by: Christoph Hellwig Reviewed-by: Allison Collins --- fs/xfs/scrub/quota.c | 19 ++++++++++++------- fs/xfs/xfs_dquot.c | 25 +++++++++++-------------- fs/xfs/xfs_dquot.h | 3 ++- fs/xfs/xfs_dquot_item.c | 2 +- fs/xfs/xfs_qm.c | 22 ++++++++++------------ fs/xfs/xfs_qm_syscalls.c | 4 ++-- fs/xfs/xfs_trace.h | 2 +- fs/xfs/xfs_trans_dquot.c | 8 +++----- 8 files changed, 42 insertions(+), 43 deletions(-) diff --git a/fs/xfs/scrub/quota.c b/fs/xfs/scrub/quota.c index 905a345583615..145f8710bac97 100644 --- a/fs/xfs/scrub/quota.c +++ b/fs/xfs/scrub/quota.c @@ -92,7 +92,6 @@ xchk_quota_item( unsigned long long icount; unsigned long long rcount; xfs_ino_t fs_icount; - xfs_dqid_t id = be32_to_cpu(d->d_id); int error = 0; if (xchk_should_terminate(sc, &error)) @@ -102,11 +101,11 @@ xchk_quota_item( * Except for the root dquot, the actual dquot we got must either have * the same or higher id as we saw before. */ - offset = id / qi->qi_dqperchunk; - if (id && id <= sqi->last_id) + offset = dq->q_id / qi->qi_dqperchunk; + if (dq->q_id && dq->q_id <= sqi->last_id) xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, offset); - sqi->last_id = id; + sqi->last_id = dq->q_id; /* Did we get the dquot type we wanted? */ if (dqtype != (d->d_flags & XFS_DQ_ALLTYPES)) @@ -175,13 +174,19 @@ xchk_quota_item( * lower limit than the actual usage. However, we flag it for * admin review. */ - if (id != 0 && bhard != 0 && bcount > bhard) + if (dq->q_id == 0) + goto out; + + if (bhard != 0 && bcount > bhard) xchk_fblock_set_warning(sc, XFS_DATA_FORK, offset); - if (id != 0 && ihard != 0 && icount > ihard) + + if (ihard != 0 && icount > ihard) xchk_fblock_set_warning(sc, XFS_DATA_FORK, offset); - if (id != 0 && rhard != 0 && rcount > rhard) + + if (rhard != 0 && rcount > rhard) xchk_fblock_set_warning(sc, XFS_DATA_FORK, offset); +out: if (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT) return -EFSCORRUPTED; diff --git a/fs/xfs/xfs_dquot.c b/fs/xfs/xfs_dquot.c index 3d1d876b45fcb..a91a0c7e91037 100644 --- a/fs/xfs/xfs_dquot.c +++ b/fs/xfs/xfs_dquot.c @@ -75,7 +75,7 @@ xfs_qm_adjust_dqlimits( struct xfs_def_quota *defq; int prealloc = 0; - ASSERT(d->d_id); + ASSERT(dq->q_id); defq = xfs_get_defquota(q, xfs_dquot_type(dq)); if (defq->bsoftlimit && !d->d_blk_softlimit) { @@ -121,7 +121,7 @@ xfs_qm_adjust_dqtimers( struct xfs_disk_dquot *d = &dq->q_core; struct xfs_def_quota *defq; - ASSERT(d->d_id); + ASSERT(dq->q_id); defq = xfs_get_defquota(qi, xfs_dquot_type(dq)); #ifdef DEBUG @@ -366,7 +366,7 @@ xfs_dquot_disk_alloc( * Make a chunk of dquots out of this buffer and log * the entire thing. */ - xfs_qm_init_dquot_blk(tp, mp, be32_to_cpu(dqp->q_core.d_id), + xfs_qm_init_dquot_blk(tp, mp, dqp->q_id, dqp->dq_flags & XFS_DQ_ALLTYPES, bp); xfs_buf_set_ref(bp, XFS_DQUOT_REF); @@ -479,7 +479,7 @@ xfs_dquot_alloc( dqp = kmem_zone_zalloc(xfs_qm_dqzone, 0); dqp->dq_flags = type; - dqp->q_core.d_id = cpu_to_be32(id); + dqp->q_id = id; dqp->q_mount = mp; INIT_LIST_HEAD(&dqp->q_lru); mutex_init(&dqp->q_qlock); @@ -537,10 +537,10 @@ xfs_dquot_from_disk( * Everything else was checked by the dquot buffer verifier. */ if ((ddqp->d_flags & XFS_DQ_ALLTYPES) != dqp->dq_flags || - ddqp->d_id != dqp->q_core.d_id) { + be32_to_cpu(ddqp->d_id) != dqp->q_id) { xfs_alert_tag(bp->b_mount, XFS_PTAG_VERIFIER_ERROR, "Metadata corruption detected at %pS, quota %u", - __this_address, be32_to_cpu(dqp->q_core.d_id)); + __this_address, dqp->q_id); xfs_alert(bp->b_mount, "Unmount and run xfs_repair"); return -EFSCORRUPTED; } @@ -1187,11 +1187,10 @@ xfs_qm_dqflush( ddqp = &dqb->dd_diskdq; /* sanity check the in-core structure before we flush */ - fa = xfs_dquot_verify(mp, &dqp->q_core, be32_to_cpu(dqp->q_core.d_id), - 0); + fa = xfs_dquot_verify(mp, &dqp->q_core, dqp->q_id, 0); if (fa) { xfs_alert(mp, "corrupt dquot ID 0x%x in memory at %pS", - be32_to_cpu(dqp->q_core.d_id), fa); + dqp->q_id, fa); xfs_buf_relse(bp); error = -EFSCORRUPTED; goto out_abort; @@ -1200,7 +1199,7 @@ xfs_qm_dqflush( fa = xfs_qm_dqflush_check(dqp); if (fa) { xfs_alert(mp, "corrupt dquot ID 0x%x in memory at %pS", - be32_to_cpu(dqp->q_core.d_id), fa); + dqp->q_id, fa); xfs_buf_relse(bp); error = -EFSCORRUPTED; goto out_abort; @@ -1273,8 +1272,7 @@ xfs_dqlock2( { if (d1 && d2) { ASSERT(d1 != d2); - if (be32_to_cpu(d1->q_core.d_id) > - be32_to_cpu(d2->q_core.d_id)) { + if (d1->q_id > d2->q_id) { mutex_lock(&d2->q_qlock); mutex_lock_nested(&d1->q_qlock, XFS_QLOCK_NESTED); } else { @@ -1342,9 +1340,8 @@ xfs_qm_dqiterate( return error; error = iter_fn(dq, dqtype, priv); - id = be32_to_cpu(dq->q_core.d_id); + id = dq->q_id; xfs_qm_dqput(dq); - id++; } while (error == 0 && id != 0); return error; diff --git a/fs/xfs/xfs_dquot.h b/fs/xfs/xfs_dquot.h index 944e43a0e2028..41664a1fc073a 100644 --- a/fs/xfs/xfs_dquot.h +++ b/fs/xfs/xfs_dquot.h @@ -35,9 +35,10 @@ struct xfs_dquot { struct xfs_mount *q_mount; uint8_t dq_flags; uint16_t q_flags; + xfs_dqid_t q_id; uint q_nrefs; - xfs_daddr_t q_blkno; int q_bufoffset; + xfs_daddr_t q_blkno; xfs_fileoff_t q_fileoffset; struct xfs_disk_dquot q_core; diff --git a/fs/xfs/xfs_dquot_item.c b/fs/xfs/xfs_dquot_item.c index fc21e48c889c8..8c1fdf37ee8f0 100644 --- a/fs/xfs/xfs_dquot_item.c +++ b/fs/xfs/xfs_dquot_item.c @@ -53,7 +53,7 @@ xfs_qm_dquot_logitem_format( qlf = xlog_prepare_iovec(lv, &vecp, XLOG_REG_TYPE_QFORMAT); qlf->qlf_type = XFS_LI_DQUOT; qlf->qlf_size = 2; - qlf->qlf_id = be32_to_cpu(qlip->qli_dquot->q_core.d_id); + qlf->qlf_id = qlip->qli_dquot->q_id; qlf->qlf_blkno = qlip->qli_dquot->q_blkno; qlf->qlf_len = 1; qlf->qlf_boffset = qlip->qli_dquot->q_bufoffset; diff --git a/fs/xfs/xfs_qm.c b/fs/xfs/xfs_qm.c index f80bcba83616e..a2a860ae15645 100644 --- a/fs/xfs/xfs_qm.c +++ b/fs/xfs/xfs_qm.c @@ -79,7 +79,7 @@ restart: for (i = 0; i < nr_found; i++) { struct xfs_dquot *dqp = batch[i]; - next_index = be32_to_cpu(dqp->q_core.d_id) + 1; + next_index = dqp->q_id + 1; error = execute(batch[i], data); if (error == -EAGAIN) { @@ -161,8 +161,7 @@ xfs_qm_dqpurge( xfs_dqfunlock(dqp); xfs_dqunlock(dqp); - radix_tree_delete(xfs_dquot_tree(qi, dqp->q_core.d_flags), - be32_to_cpu(dqp->q_core.d_id)); + radix_tree_delete(xfs_dquot_tree(qi, dqp->q_core.d_flags), dqp->q_id); qi->qi_dquots--; /* @@ -1108,7 +1107,7 @@ xfs_qm_quotacheck_dqadjust( * * There are no timers for the default values set in the root dquot. */ - if (dqp->q_core.d_id) { + if (dqp->q_id) { xfs_qm_adjust_dqlimits(mp, dqp); xfs_qm_adjust_dqtimers(mp, dqp); } @@ -1594,8 +1593,7 @@ xfs_qm_dqfree_one( struct xfs_quotainfo *qi = mp->m_quotainfo; mutex_lock(&qi->qi_tree_lock); - radix_tree_delete(xfs_dquot_tree(qi, dqp->q_core.d_flags), - be32_to_cpu(dqp->q_core.d_id)); + radix_tree_delete(xfs_dquot_tree(qi, dqp->q_core.d_flags), dqp->q_id); qi->qi_dquots--; mutex_unlock(&qi->qi_tree_lock); @@ -1819,7 +1817,7 @@ xfs_qm_vop_chown_reserve( XFS_QMOPT_RES_RTBLKS : XFS_QMOPT_RES_REGBLKS; if (XFS_IS_UQUOTA_ON(mp) && udqp && - i_uid_read(VFS_I(ip)) != be32_to_cpu(udqp->q_core.d_id)) { + i_uid_read(VFS_I(ip)) != udqp->q_id) { udq_delblks = udqp; /* * If there are delayed allocation blocks, then we have to @@ -1832,7 +1830,7 @@ xfs_qm_vop_chown_reserve( } } if (XFS_IS_GQUOTA_ON(ip->i_mount) && gdqp && - i_gid_read(VFS_I(ip)) != be32_to_cpu(gdqp->q_core.d_id)) { + i_gid_read(VFS_I(ip)) != gdqp->q_id) { gdq_delblks = gdqp; if (delblks) { ASSERT(ip->i_gdquot); @@ -1841,7 +1839,7 @@ xfs_qm_vop_chown_reserve( } if (XFS_IS_PQUOTA_ON(ip->i_mount) && pdqp && - ip->i_d.di_projid != be32_to_cpu(pdqp->q_core.d_id)) { + ip->i_d.di_projid != pdqp->q_id) { pdq_delblks = pdqp; if (delblks) { ASSERT(ip->i_pdquot); @@ -1925,21 +1923,21 @@ xfs_qm_vop_create_dqattach( if (udqp && XFS_IS_UQUOTA_ON(mp)) { ASSERT(ip->i_udquot == NULL); - ASSERT(i_uid_read(VFS_I(ip)) == be32_to_cpu(udqp->q_core.d_id)); + ASSERT(i_uid_read(VFS_I(ip)) == udqp->q_id); ip->i_udquot = xfs_qm_dqhold(udqp); xfs_trans_mod_dquot(tp, udqp, XFS_TRANS_DQ_ICOUNT, 1); } if (gdqp && XFS_IS_GQUOTA_ON(mp)) { ASSERT(ip->i_gdquot == NULL); - ASSERT(i_gid_read(VFS_I(ip)) == be32_to_cpu(gdqp->q_core.d_id)); + ASSERT(i_gid_read(VFS_I(ip)) == gdqp->q_id); ip->i_gdquot = xfs_qm_dqhold(gdqp); xfs_trans_mod_dquot(tp, gdqp, XFS_TRANS_DQ_ICOUNT, 1); } if (pdqp && XFS_IS_PQUOTA_ON(mp)) { ASSERT(ip->i_pdquot == NULL); - ASSERT(ip->i_d.di_projid == be32_to_cpu(pdqp->q_core.d_id)); + ASSERT(ip->i_d.di_projid == pdqp->q_id); ip->i_pdquot = xfs_qm_dqhold(pdqp); xfs_trans_mod_dquot(tp, pdqp, XFS_TRANS_DQ_ICOUNT, 1); diff --git a/fs/xfs/xfs_qm_syscalls.c b/fs/xfs/xfs_qm_syscalls.c index a9e4a7e1b9d9a..8529eee3454cf 100644 --- a/fs/xfs/xfs_qm_syscalls.c +++ b/fs/xfs/xfs_qm_syscalls.c @@ -659,7 +659,7 @@ xfs_qm_scall_getquota_fill_qc( if (((XFS_IS_UQUOTA_ENFORCED(mp) && type == XFS_DQ_USER) || (XFS_IS_GQUOTA_ENFORCED(mp) && type == XFS_DQ_GROUP) || (XFS_IS_PQUOTA_ENFORCED(mp) && type == XFS_DQ_PROJ)) && - dqp->q_core.d_id != 0) { + dqp->q_id != 0) { if ((dst->d_space > dst->d_spc_softlimit) && (dst->d_spc_softlimit > 0)) { ASSERT(dst->d_spc_timer != 0); @@ -726,7 +726,7 @@ xfs_qm_scall_getquota_next( return error; /* Fill in the ID we actually read from disk */ - *id = be32_to_cpu(dqp->q_core.d_id); + *id = dqp->q_id; xfs_qm_scall_getquota_fill_qc(mp, type, dqp, dst); diff --git a/fs/xfs/xfs_trace.h b/fs/xfs/xfs_trace.h index 97d8daf118163..39f82dbafa370 100644 --- a/fs/xfs/xfs_trace.h +++ b/fs/xfs/xfs_trace.h @@ -876,7 +876,7 @@ DECLARE_EVENT_CLASS(xfs_dquot_class, ), \ TP_fast_assign( __entry->dev = dqp->q_mount->m_super->s_dev; - __entry->id = be32_to_cpu(dqp->q_core.d_id); + __entry->id = dqp->q_id; __entry->flags = dqp->dq_flags | dqp->q_flags; __entry->nrefs = dqp->q_nrefs; __entry->res_bcount = dqp->q_res_bcount; diff --git a/fs/xfs/xfs_trans_dquot.c b/fs/xfs/xfs_trans_dquot.c index 8963cfac2a6a2..8dd6aa6e8fa35 100644 --- a/fs/xfs/xfs_trans_dquot.c +++ b/fs/xfs/xfs_trans_dquot.c @@ -386,7 +386,7 @@ xfs_trans_apply_dquot_deltas( * Get any default limits in use. * Start/reset the timer(s) if needed. */ - if (d->d_id) { + if (dqp->q_id) { xfs_qm_adjust_dqlimits(tp->t_mountp, dqp); xfs_qm_adjust_dqtimers(tp->t_mountp, dqp); } @@ -558,8 +558,7 @@ xfs_quota_warn( else qtype = GRPQUOTA; - quota_send_warning(make_kqid(&init_user_ns, qtype, - be32_to_cpu(dqp->q_core.d_id)), + quota_send_warning(make_kqid(&init_user_ns, qtype, dqp->q_id), mp->m_super->s_dev, type); } @@ -618,8 +617,7 @@ xfs_trans_dqresv( resbcountp = &dqp->q_res_rtbcount; } - if ((flags & XFS_QMOPT_FORCE_RES) == 0 && - dqp->q_core.d_id && + if ((flags & XFS_QMOPT_FORCE_RES) == 0 && dqp->q_id && ((XFS_IS_UQUOTA_ENFORCED(dqp->q_mount) && XFS_QM_ISUDQ(dqp)) || (XFS_IS_GQUOTA_ENFORCED(dqp->q_mount) && XFS_QM_ISGDQ(dqp)) || (XFS_IS_PQUOTA_ENFORCED(dqp->q_mount) && XFS_QM_ISPDQ(dqp)))) { -- GitLab From 784e80f5640db9a925af1143a25e9bb98624350d Mon Sep 17 00:00:00 2001 From: "Darrick J. Wong" Date: Tue, 14 Jul 2020 10:37:30 -0700 Subject: [PATCH 1003/1476] xfs: use a per-resource struct for incore dquot data Introduce a new struct xfs_dquot_res that we'll use to track all the incore data for a particular resource type (block, inode, rt block). This will help us (once we've eliminated q_core) to declutter quota functions that currently open-code field access or pass around fields around explicitly. Signed-off-by: Darrick J. Wong Reviewed-by: Chandan Babu R Reviewed-by: Christoph Hellwig Reviewed-by: Allison Collins --- fs/xfs/xfs_dquot.c | 6 +++--- fs/xfs/xfs_dquot.h | 18 +++++++++------- fs/xfs/xfs_iomap.c | 6 +++--- fs/xfs/xfs_qm.c | 6 +++--- fs/xfs/xfs_qm_bhv.c | 8 ++++---- fs/xfs/xfs_qm_syscalls.c | 6 +++--- fs/xfs/xfs_trace.h | 2 +- fs/xfs/xfs_trans_dquot.c | 44 ++++++++++++++++++++-------------------- 8 files changed, 50 insertions(+), 46 deletions(-) diff --git a/fs/xfs/xfs_dquot.c b/fs/xfs/xfs_dquot.c index a91a0c7e91037..8fc7ae1f4653e 100644 --- a/fs/xfs/xfs_dquot.c +++ b/fs/xfs/xfs_dquot.c @@ -552,9 +552,9 @@ xfs_dquot_from_disk( * Reservation counters are defined as reservation plus current usage * to avoid having to add every time. */ - dqp->q_res_bcount = be64_to_cpu(ddqp->d_bcount); - dqp->q_res_icount = be64_to_cpu(ddqp->d_icount); - dqp->q_res_rtbcount = be64_to_cpu(ddqp->d_rtbcount); + dqp->q_blk.reserved = be64_to_cpu(ddqp->d_bcount); + dqp->q_ino.reserved = be64_to_cpu(ddqp->d_icount); + dqp->q_rtb.reserved = be64_to_cpu(ddqp->d_rtbcount); /* initialize the dquot speculative prealloc thresholds */ xfs_dquot_set_prealloc_limits(dqp); diff --git a/fs/xfs/xfs_dquot.h b/fs/xfs/xfs_dquot.h index 41664a1fc073a..c2f841ed26612 100644 --- a/fs/xfs/xfs_dquot.h +++ b/fs/xfs/xfs_dquot.h @@ -27,6 +27,11 @@ enum { XFS_QLOWSP_MAX }; +struct xfs_dquot_res { + /* Total resources allocated and reserved. */ + xfs_qcnt_t reserved; +}; + /* * The incore dquot structure */ @@ -41,14 +46,13 @@ struct xfs_dquot { xfs_daddr_t q_blkno; xfs_fileoff_t q_fileoffset; + struct xfs_dquot_res q_blk; /* regular blocks */ + struct xfs_dquot_res q_ino; /* inodes */ + struct xfs_dquot_res q_rtb; /* realtime blocks */ + struct xfs_disk_dquot q_core; struct xfs_dq_logitem q_logitem; - /* total regular nblks used+reserved */ - xfs_qcnt_t q_res_bcount; - /* total inos allocd+reserved */ - xfs_qcnt_t q_res_icount; - /* total realtime blks used+reserved */ - xfs_qcnt_t q_res_rtbcount; + xfs_qcnt_t q_prealloc_lo_wmark; xfs_qcnt_t q_prealloc_hi_wmark; int64_t q_low_space[XFS_QLOWSP_MAX]; @@ -139,7 +143,7 @@ static inline bool xfs_dquot_lowsp(struct xfs_dquot *dqp) { int64_t freesp; - freesp = be64_to_cpu(dqp->q_core.d_blk_hardlimit) - dqp->q_res_bcount; + freesp = be64_to_cpu(dqp->q_core.d_blk_hardlimit) - dqp->q_blk.reserved; if (freesp < dqp->q_low_space[XFS_QLOWSP_1_PCNT]) return true; diff --git a/fs/xfs/xfs_iomap.c b/fs/xfs/xfs_iomap.c index b9a8c3798e08f..f60a6e44363b6 100644 --- a/fs/xfs/xfs_iomap.c +++ b/fs/xfs/xfs_iomap.c @@ -307,7 +307,7 @@ xfs_quota_need_throttle( return false; /* under the lo watermark, no throttle */ - if (dq->q_res_bcount + alloc_blocks < dq->q_prealloc_lo_wmark) + if (dq->q_blk.reserved + alloc_blocks < dq->q_prealloc_lo_wmark) return false; return true; @@ -326,13 +326,13 @@ xfs_quota_calc_throttle( struct xfs_dquot *dq = xfs_inode_dquot(ip, type); /* no dq, or over hi wmark, squash the prealloc completely */ - if (!dq || dq->q_res_bcount >= dq->q_prealloc_hi_wmark) { + if (!dq || dq->q_blk.reserved >= dq->q_prealloc_hi_wmark) { *qblocks = 0; *qfreesp = 0; return; } - freesp = dq->q_prealloc_hi_wmark - dq->q_res_bcount; + freesp = dq->q_prealloc_hi_wmark - dq->q_blk.reserved; if (freesp < dq->q_low_space[XFS_QLOWSP_5_PCNT]) { shift = 2; if (freesp < dq->q_low_space[XFS_QLOWSP_3_PCNT]) diff --git a/fs/xfs/xfs_qm.c b/fs/xfs/xfs_qm.c index a2a860ae15645..aed4a76b8d613 100644 --- a/fs/xfs/xfs_qm.c +++ b/fs/xfs/xfs_qm.c @@ -1092,14 +1092,14 @@ xfs_qm_quotacheck_dqadjust( * resource usage. */ be64_add_cpu(&dqp->q_core.d_icount, 1); - dqp->q_res_icount++; + dqp->q_ino.reserved++; if (nblks) { be64_add_cpu(&dqp->q_core.d_bcount, nblks); - dqp->q_res_bcount += nblks; + dqp->q_blk.reserved += nblks; } if (rtblks) { be64_add_cpu(&dqp->q_core.d_rtbcount, rtblks); - dqp->q_res_rtbcount += rtblks; + dqp->q_rtb.reserved += rtblks; } /* diff --git a/fs/xfs/xfs_qm_bhv.c b/fs/xfs/xfs_qm_bhv.c index fc2fa418919f7..94b2b4b0fc174 100644 --- a/fs/xfs/xfs_qm_bhv.c +++ b/fs/xfs/xfs_qm_bhv.c @@ -29,8 +29,8 @@ xfs_fill_statvfs_from_dquot( if (limit && statp->f_blocks > limit) { statp->f_blocks = limit; statp->f_bfree = statp->f_bavail = - (statp->f_blocks > dqp->q_res_bcount) ? - (statp->f_blocks - dqp->q_res_bcount) : 0; + (statp->f_blocks > dqp->q_blk.reserved) ? + (statp->f_blocks - dqp->q_blk.reserved) : 0; } limit = dqp->q_core.d_ino_softlimit ? @@ -39,8 +39,8 @@ xfs_fill_statvfs_from_dquot( if (limit && statp->f_files > limit) { statp->f_files = limit; statp->f_ffree = - (statp->f_files > dqp->q_res_icount) ? - (statp->f_files - dqp->q_res_icount) : 0; + (statp->f_files > dqp->q_ino.reserved) ? + (statp->f_files - dqp->q_ino.reserved) : 0; } } diff --git a/fs/xfs/xfs_qm_syscalls.c b/fs/xfs/xfs_qm_syscalls.c index 8529eee3454cf..a65ea8b4d24d3 100644 --- a/fs/xfs/xfs_qm_syscalls.c +++ b/fs/xfs/xfs_qm_syscalls.c @@ -625,8 +625,8 @@ xfs_qm_scall_getquota_fill_qc( XFS_FSB_TO_B(mp, be64_to_cpu(dqp->q_core.d_blk_softlimit)); dst->d_ino_hardlimit = be64_to_cpu(dqp->q_core.d_ino_hardlimit); dst->d_ino_softlimit = be64_to_cpu(dqp->q_core.d_ino_softlimit); - dst->d_space = XFS_FSB_TO_B(mp, dqp->q_res_bcount); - dst->d_ino_count = dqp->q_res_icount; + dst->d_space = XFS_FSB_TO_B(mp, dqp->q_blk.reserved); + dst->d_ino_count = dqp->q_ino.reserved; dst->d_spc_timer = be32_to_cpu(dqp->q_core.d_btimer); dst->d_ino_timer = be32_to_cpu(dqp->q_core.d_itimer); dst->d_ino_warns = be16_to_cpu(dqp->q_core.d_iwarns); @@ -635,7 +635,7 @@ xfs_qm_scall_getquota_fill_qc( XFS_FSB_TO_B(mp, be64_to_cpu(dqp->q_core.d_rtb_hardlimit)); dst->d_rt_spc_softlimit = XFS_FSB_TO_B(mp, be64_to_cpu(dqp->q_core.d_rtb_softlimit)); - dst->d_rt_space = XFS_FSB_TO_B(mp, dqp->q_res_rtbcount); + dst->d_rt_space = XFS_FSB_TO_B(mp, dqp->q_rtb.reserved); dst->d_rt_spc_timer = be32_to_cpu(dqp->q_core.d_rtbtimer); dst->d_rt_spc_warns = be16_to_cpu(dqp->q_core.d_rtbwarns); diff --git a/fs/xfs/xfs_trace.h b/fs/xfs/xfs_trace.h index 39f82dbafa370..b49c672d1674c 100644 --- a/fs/xfs/xfs_trace.h +++ b/fs/xfs/xfs_trace.h @@ -879,7 +879,7 @@ DECLARE_EVENT_CLASS(xfs_dquot_class, __entry->id = dqp->q_id; __entry->flags = dqp->dq_flags | dqp->q_flags; __entry->nrefs = dqp->q_nrefs; - __entry->res_bcount = dqp->q_res_bcount; + __entry->res_bcount = dqp->q_blk.reserved; __entry->bcount = be64_to_cpu(dqp->q_core.d_bcount); __entry->icount = be64_to_cpu(dqp->q_core.d_icount); __entry->blk_hardlimit = diff --git a/fs/xfs/xfs_trans_dquot.c b/fs/xfs/xfs_trans_dquot.c index 8dd6aa6e8fa35..ed3928e9392fe 100644 --- a/fs/xfs/xfs_trans_dquot.c +++ b/fs/xfs/xfs_trans_dquot.c @@ -409,11 +409,11 @@ xfs_trans_apply_dquot_deltas( if (qtrx->qt_blk_res != blk_res_used) { if (qtrx->qt_blk_res > blk_res_used) - dqp->q_res_bcount -= (xfs_qcnt_t) + dqp->q_blk.reserved -= (xfs_qcnt_t) (qtrx->qt_blk_res - blk_res_used); else - dqp->q_res_bcount -= (xfs_qcnt_t) + dqp->q_blk.reserved -= (xfs_qcnt_t) (blk_res_used - qtrx->qt_blk_res); } @@ -426,7 +426,7 @@ xfs_trans_apply_dquot_deltas( * deliberately skip quota reservations. */ if (qtrx->qt_bcount_delta) { - dqp->q_res_bcount += + dqp->q_blk.reserved += (xfs_qcnt_t)qtrx->qt_bcount_delta; } } @@ -437,17 +437,17 @@ xfs_trans_apply_dquot_deltas( if (qtrx->qt_rtblk_res != qtrx->qt_rtblk_res_used) { if (qtrx->qt_rtblk_res > qtrx->qt_rtblk_res_used) - dqp->q_res_rtbcount -= (xfs_qcnt_t) + dqp->q_rtb.reserved -= (xfs_qcnt_t) (qtrx->qt_rtblk_res - qtrx->qt_rtblk_res_used); else - dqp->q_res_rtbcount -= (xfs_qcnt_t) + dqp->q_rtb.reserved -= (xfs_qcnt_t) (qtrx->qt_rtblk_res_used - qtrx->qt_rtblk_res); } } else { if (qtrx->qt_rtbcount_delta) - dqp->q_res_rtbcount += + dqp->q_rtb.reserved += (xfs_qcnt_t)qtrx->qt_rtbcount_delta; } @@ -458,20 +458,20 @@ xfs_trans_apply_dquot_deltas( ASSERT(qtrx->qt_ino_res >= qtrx->qt_ino_res_used); if (qtrx->qt_ino_res > qtrx->qt_ino_res_used) - dqp->q_res_icount -= (xfs_qcnt_t) + dqp->q_ino.reserved -= (xfs_qcnt_t) (qtrx->qt_ino_res - qtrx->qt_ino_res_used); } else { if (qtrx->qt_icount_delta) - dqp->q_res_icount += + dqp->q_ino.reserved += (xfs_qcnt_t)qtrx->qt_icount_delta; } - ASSERT(dqp->q_res_bcount >= + ASSERT(dqp->q_blk.reserved >= be64_to_cpu(dqp->q_core.d_bcount)); - ASSERT(dqp->q_res_icount >= + ASSERT(dqp->q_ino.reserved >= be64_to_cpu(dqp->q_core.d_icount)); - ASSERT(dqp->q_res_rtbcount >= + ASSERT(dqp->q_rtb.reserved >= be64_to_cpu(dqp->q_core.d_rtbcount)); } } @@ -516,7 +516,7 @@ xfs_trans_unreserve_and_mod_dquots( if (qtrx->qt_blk_res) { xfs_dqlock(dqp); locked = true; - dqp->q_res_bcount -= + dqp->q_blk.reserved -= (xfs_qcnt_t)qtrx->qt_blk_res; } if (qtrx->qt_ino_res) { @@ -524,7 +524,7 @@ xfs_trans_unreserve_and_mod_dquots( xfs_dqlock(dqp); locked = true; } - dqp->q_res_icount -= + dqp->q_ino.reserved -= (xfs_qcnt_t)qtrx->qt_ino_res; } @@ -533,7 +533,7 @@ xfs_trans_unreserve_and_mod_dquots( xfs_dqlock(dqp); locked = true; } - dqp->q_res_rtbcount -= + dqp->q_rtb.reserved -= (xfs_qcnt_t)qtrx->qt_rtblk_res; } if (locked) @@ -602,7 +602,7 @@ xfs_trans_dqresv( timer = be32_to_cpu(dqp->q_core.d_btimer); warns = be16_to_cpu(dqp->q_core.d_bwarns); warnlimit = defq->bwarnlimit; - resbcountp = &dqp->q_res_bcount; + resbcountp = &dqp->q_blk.reserved; } else { ASSERT(flags & XFS_TRANS_DQ_RES_RTBLKS); hardlimit = be64_to_cpu(dqp->q_core.d_rtb_hardlimit); @@ -614,7 +614,7 @@ xfs_trans_dqresv( timer = be32_to_cpu(dqp->q_core.d_rtbtimer); warns = be16_to_cpu(dqp->q_core.d_rtbwarns); warnlimit = defq->rtbwarnlimit; - resbcountp = &dqp->q_res_rtbcount; + resbcountp = &dqp->q_rtb.reserved; } if ((flags & XFS_QMOPT_FORCE_RES) == 0 && dqp->q_id && @@ -645,7 +645,7 @@ xfs_trans_dqresv( } } if (ninos > 0) { - total_count = dqp->q_res_icount + ninos; + total_count = dqp->q_ino.reserved + ninos; timer = be32_to_cpu(dqp->q_core.d_itimer); warns = be16_to_cpu(dqp->q_core.d_iwarns); warnlimit = defq->iwarnlimit; @@ -675,11 +675,11 @@ xfs_trans_dqresv( /* * Change the reservation, but not the actual usage. - * Note that q_res_bcount = q_core.d_bcount + resv + * Note that q_blk.reserved = q_core.d_bcount + resv */ (*resbcountp) += (xfs_qcnt_t)nblks; if (ninos != 0) - dqp->q_res_icount += (xfs_qcnt_t)ninos; + dqp->q_ino.reserved += (xfs_qcnt_t)ninos; /* * note the reservation amt in the trans struct too, @@ -700,9 +700,9 @@ xfs_trans_dqresv( XFS_TRANS_DQ_RES_INOS, ninos); } - ASSERT(dqp->q_res_bcount >= be64_to_cpu(dqp->q_core.d_bcount)); - ASSERT(dqp->q_res_rtbcount >= be64_to_cpu(dqp->q_core.d_rtbcount)); - ASSERT(dqp->q_res_icount >= be64_to_cpu(dqp->q_core.d_icount)); + ASSERT(dqp->q_blk.reserved >= be64_to_cpu(dqp->q_core.d_bcount)); + ASSERT(dqp->q_rtb.reserved >= be64_to_cpu(dqp->q_core.d_rtbcount)); + ASSERT(dqp->q_ino.reserved >= be64_to_cpu(dqp->q_core.d_icount)); xfs_dqunlock(dqp); return 0; -- GitLab From d3537cf93e5e2f8b4e95cfe8bc8fa03b58c88e32 Mon Sep 17 00:00:00 2001 From: "Darrick J. Wong" Date: Tue, 14 Jul 2020 10:37:31 -0700 Subject: [PATCH 1004/1476] xfs: stop using q_core limits in the quota code Add limits fields in the incore dquot, and use that instead of the ones in qcore. This eliminates a bunch of endian conversions and will eventually allow us to remove qcore entirely. Signed-off-by: Darrick J. Wong Reviewed-by: Chandan Babu R Reviewed-by: Christoph Hellwig Reviewed-by: Allison Collins --- fs/xfs/scrub/quota.c | 36 ++++------- fs/xfs/xfs_dquot.c | 135 ++++++++++++++++++++++----------------- fs/xfs/xfs_dquot.h | 6 +- fs/xfs/xfs_qm.c | 14 ++-- fs/xfs/xfs_qm.h | 12 ++-- fs/xfs/xfs_qm_bhv.c | 12 ++-- fs/xfs/xfs_qm_syscalls.c | 40 ++++++------ fs/xfs/xfs_trace.h | 12 ++-- fs/xfs/xfs_trans_dquot.c | 12 ++-- 9 files changed, 138 insertions(+), 141 deletions(-) diff --git a/fs/xfs/scrub/quota.c b/fs/xfs/scrub/quota.c index 145f8710bac97..e73e6c88e76a8 100644 --- a/fs/xfs/scrub/quota.c +++ b/fs/xfs/scrub/quota.c @@ -82,12 +82,6 @@ xchk_quota_item( struct xfs_disk_dquot *d = &dq->q_core; struct xfs_quotainfo *qi = mp->m_quotainfo; xfs_fileoff_t offset; - unsigned long long bsoft; - unsigned long long isoft; - unsigned long long rsoft; - unsigned long long bhard; - unsigned long long ihard; - unsigned long long rhard; unsigned long long bcount; unsigned long long icount; unsigned long long rcount; @@ -114,15 +108,6 @@ xchk_quota_item( if (d->d_pad0 != cpu_to_be32(0) || d->d_pad != cpu_to_be16(0)) xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, offset); - /* Check the limits. */ - bhard = be64_to_cpu(d->d_blk_hardlimit); - ihard = be64_to_cpu(d->d_ino_hardlimit); - rhard = be64_to_cpu(d->d_rtb_hardlimit); - - bsoft = be64_to_cpu(d->d_blk_softlimit); - isoft = be64_to_cpu(d->d_ino_softlimit); - rsoft = be64_to_cpu(d->d_rtb_softlimit); - /* * Warn if the hard limits are larger than the fs. * Administrators can do this, though in production this seems @@ -131,19 +116,19 @@ xchk_quota_item( * Complain about corruption if the soft limit is greater than * the hard limit. */ - if (bhard > mp->m_sb.sb_dblocks) + if (dq->q_blk.hardlimit > mp->m_sb.sb_dblocks) xchk_fblock_set_warning(sc, XFS_DATA_FORK, offset); - if (bsoft > bhard) + if (dq->q_blk.softlimit > dq->q_blk.hardlimit) xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, offset); - if (ihard > M_IGEO(mp)->maxicount) + if (dq->q_ino.hardlimit > M_IGEO(mp)->maxicount) xchk_fblock_set_warning(sc, XFS_DATA_FORK, offset); - if (isoft > ihard) + if (dq->q_ino.softlimit > dq->q_ino.hardlimit) xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, offset); - if (rhard > mp->m_sb.sb_rblocks) + if (dq->q_rtb.hardlimit > mp->m_sb.sb_rblocks) xchk_fblock_set_warning(sc, XFS_DATA_FORK, offset); - if (rsoft > rhard) + if (dq->q_rtb.softlimit > dq->q_rtb.hardlimit) xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, offset); /* Check the resource counts. */ @@ -177,13 +162,16 @@ xchk_quota_item( if (dq->q_id == 0) goto out; - if (bhard != 0 && bcount > bhard) + if (dq->q_blk.hardlimit != 0 && + bcount > dq->q_blk.hardlimit) xchk_fblock_set_warning(sc, XFS_DATA_FORK, offset); - if (ihard != 0 && icount > ihard) + if (dq->q_ino.hardlimit != 0 && + icount > dq->q_ino.hardlimit) xchk_fblock_set_warning(sc, XFS_DATA_FORK, offset); - if (rhard != 0 && rcount > rhard) + if (dq->q_rtb.hardlimit != 0 && + rcount > dq->q_rtb.hardlimit) xchk_fblock_set_warning(sc, XFS_DATA_FORK, offset); out: diff --git a/fs/xfs/xfs_dquot.c b/fs/xfs/xfs_dquot.c index 8fc7ae1f4653e..9116e6ad7e9e6 100644 --- a/fs/xfs/xfs_dquot.c +++ b/fs/xfs/xfs_dquot.c @@ -71,29 +71,28 @@ xfs_qm_adjust_dqlimits( struct xfs_dquot *dq) { struct xfs_quotainfo *q = mp->m_quotainfo; - struct xfs_disk_dquot *d = &dq->q_core; struct xfs_def_quota *defq; int prealloc = 0; ASSERT(dq->q_id); defq = xfs_get_defquota(q, xfs_dquot_type(dq)); - if (defq->bsoftlimit && !d->d_blk_softlimit) { - d->d_blk_softlimit = cpu_to_be64(defq->bsoftlimit); + if (defq->bsoftlimit && !dq->q_blk.softlimit) { + dq->q_blk.softlimit = defq->bsoftlimit; prealloc = 1; } - if (defq->bhardlimit && !d->d_blk_hardlimit) { - d->d_blk_hardlimit = cpu_to_be64(defq->bhardlimit); + if (defq->bhardlimit && !dq->q_blk.hardlimit) { + dq->q_blk.hardlimit = defq->bhardlimit; prealloc = 1; } - if (defq->isoftlimit && !d->d_ino_softlimit) - d->d_ino_softlimit = cpu_to_be64(defq->isoftlimit); - if (defq->ihardlimit && !d->d_ino_hardlimit) - d->d_ino_hardlimit = cpu_to_be64(defq->ihardlimit); - if (defq->rtbsoftlimit && !d->d_rtb_softlimit) - d->d_rtb_softlimit = cpu_to_be64(defq->rtbsoftlimit); - if (defq->rtbhardlimit && !d->d_rtb_hardlimit) - d->d_rtb_hardlimit = cpu_to_be64(defq->rtbhardlimit); + if (defq->isoftlimit && !dq->q_ino.softlimit) + dq->q_ino.softlimit = defq->isoftlimit; + if (defq->ihardlimit && !dq->q_ino.hardlimit) + dq->q_ino.hardlimit = defq->ihardlimit; + if (defq->rtbsoftlimit && !dq->q_rtb.softlimit) + dq->q_rtb.softlimit = defq->rtbsoftlimit; + if (defq->rtbhardlimit && !dq->q_rtb.hardlimit) + dq->q_rtb.hardlimit = defq->rtbhardlimit; if (prealloc) xfs_dquot_set_prealloc_limits(dq); @@ -125,82 +124,67 @@ xfs_qm_adjust_dqtimers( defq = xfs_get_defquota(qi, xfs_dquot_type(dq)); #ifdef DEBUG - if (d->d_blk_hardlimit) - ASSERT(be64_to_cpu(d->d_blk_softlimit) <= - be64_to_cpu(d->d_blk_hardlimit)); - if (d->d_ino_hardlimit) - ASSERT(be64_to_cpu(d->d_ino_softlimit) <= - be64_to_cpu(d->d_ino_hardlimit)); - if (d->d_rtb_hardlimit) - ASSERT(be64_to_cpu(d->d_rtb_softlimit) <= - be64_to_cpu(d->d_rtb_hardlimit)); + if (dq->q_blk.hardlimit) + ASSERT(dq->q_blk.softlimit <= dq->q_blk.hardlimit); + if (dq->q_ino.hardlimit) + ASSERT(dq->q_ino.softlimit <= dq->q_ino.hardlimit); + if (dq->q_rtb.hardlimit) + ASSERT(dq->q_rtb.softlimit <= dq->q_rtb.hardlimit); #endif if (!d->d_btimer) { - if ((d->d_blk_softlimit && - (be64_to_cpu(d->d_bcount) > - be64_to_cpu(d->d_blk_softlimit))) || - (d->d_blk_hardlimit && - (be64_to_cpu(d->d_bcount) > - be64_to_cpu(d->d_blk_hardlimit)))) { + if ((dq->q_blk.softlimit && + (be64_to_cpu(d->d_bcount) > dq->q_blk.softlimit)) || + (dq->q_blk.hardlimit && + (be64_to_cpu(d->d_bcount) > dq->q_blk.hardlimit))) { d->d_btimer = cpu_to_be32(ktime_get_real_seconds() + defq->btimelimit); } else { d->d_bwarns = 0; } } else { - if ((!d->d_blk_softlimit || - (be64_to_cpu(d->d_bcount) <= - be64_to_cpu(d->d_blk_softlimit))) && - (!d->d_blk_hardlimit || - (be64_to_cpu(d->d_bcount) <= - be64_to_cpu(d->d_blk_hardlimit)))) { + if ((!dq->q_blk.softlimit || + (be64_to_cpu(d->d_bcount) <= dq->q_blk.softlimit)) && + (!dq->q_blk.hardlimit || + (be64_to_cpu(d->d_bcount) <= dq->q_blk.hardlimit))) { d->d_btimer = 0; } } if (!d->d_itimer) { - if ((d->d_ino_softlimit && - (be64_to_cpu(d->d_icount) > - be64_to_cpu(d->d_ino_softlimit))) || - (d->d_ino_hardlimit && - (be64_to_cpu(d->d_icount) > - be64_to_cpu(d->d_ino_hardlimit)))) { + if ((dq->q_ino.softlimit && + (be64_to_cpu(d->d_icount) > dq->q_ino.softlimit)) || + (dq->q_ino.hardlimit && + (be64_to_cpu(d->d_icount) > dq->q_ino.hardlimit))) { d->d_itimer = cpu_to_be32(ktime_get_real_seconds() + defq->itimelimit); } else { d->d_iwarns = 0; } } else { - if ((!d->d_ino_softlimit || - (be64_to_cpu(d->d_icount) <= - be64_to_cpu(d->d_ino_softlimit))) && - (!d->d_ino_hardlimit || - (be64_to_cpu(d->d_icount) <= - be64_to_cpu(d->d_ino_hardlimit)))) { + if ((!dq->q_ino.softlimit || + (be64_to_cpu(d->d_icount) <= dq->q_ino.softlimit)) && + (!dq->q_ino.hardlimit || + (be64_to_cpu(d->d_icount) <= dq->q_ino.hardlimit))) { d->d_itimer = 0; } } if (!d->d_rtbtimer) { - if ((d->d_rtb_softlimit && - (be64_to_cpu(d->d_rtbcount) > - be64_to_cpu(d->d_rtb_softlimit))) || - (d->d_rtb_hardlimit && - (be64_to_cpu(d->d_rtbcount) > - be64_to_cpu(d->d_rtb_hardlimit)))) { + if ((dq->q_rtb.softlimit && + (be64_to_cpu(d->d_rtbcount) > dq->q_rtb.softlimit)) || + (dq->q_rtb.hardlimit && + (be64_to_cpu(d->d_rtbcount) > dq->q_rtb.hardlimit))) { d->d_rtbtimer = cpu_to_be32(ktime_get_real_seconds() + defq->rtbtimelimit); } else { d->d_rtbwarns = 0; } } else { - if ((!d->d_rtb_softlimit || - (be64_to_cpu(d->d_rtbcount) <= - be64_to_cpu(d->d_rtb_softlimit))) && - (!d->d_rtb_hardlimit || - (be64_to_cpu(d->d_rtbcount) <= - be64_to_cpu(d->d_rtb_hardlimit)))) { + if ((!dq->q_rtb.softlimit || + (be64_to_cpu(d->d_rtbcount) <= dq->q_rtb.softlimit)) && + (!dq->q_rtb.hardlimit || + (be64_to_cpu(d->d_rtbcount) <= dq->q_rtb.hardlimit))) { d->d_rtbtimer = 0; } } @@ -291,8 +275,8 @@ xfs_dquot_set_prealloc_limits(struct xfs_dquot *dqp) { uint64_t space; - dqp->q_prealloc_hi_wmark = be64_to_cpu(dqp->q_core.d_blk_hardlimit); - dqp->q_prealloc_lo_wmark = be64_to_cpu(dqp->q_core.d_blk_softlimit); + dqp->q_prealloc_hi_wmark = dqp->q_blk.hardlimit; + dqp->q_prealloc_lo_wmark = dqp->q_blk.softlimit; if (!dqp->q_prealloc_lo_wmark) { dqp->q_prealloc_lo_wmark = dqp->q_prealloc_hi_wmark; do_div(dqp->q_prealloc_lo_wmark, 100); @@ -547,6 +531,12 @@ xfs_dquot_from_disk( /* copy everything from disk dquot to the incore dquot */ memcpy(&dqp->q_core, ddqp, sizeof(struct xfs_disk_dquot)); + dqp->q_blk.hardlimit = be64_to_cpu(ddqp->d_blk_hardlimit); + dqp->q_blk.softlimit = be64_to_cpu(ddqp->d_blk_softlimit); + dqp->q_ino.hardlimit = be64_to_cpu(ddqp->d_ino_hardlimit); + dqp->q_ino.softlimit = be64_to_cpu(ddqp->d_ino_softlimit); + dqp->q_rtb.hardlimit = be64_to_cpu(ddqp->d_rtb_hardlimit); + dqp->q_rtb.softlimit = be64_to_cpu(ddqp->d_rtb_softlimit); /* * Reservation counters are defined as reservation plus current usage @@ -568,6 +558,12 @@ xfs_dquot_to_disk( struct xfs_dquot *dqp) { memcpy(ddqp, &dqp->q_core, sizeof(struct xfs_disk_dquot)); + ddqp->d_blk_hardlimit = cpu_to_be64(dqp->q_blk.hardlimit); + ddqp->d_blk_softlimit = cpu_to_be64(dqp->q_blk.softlimit); + ddqp->d_ino_hardlimit = cpu_to_be64(dqp->q_ino.hardlimit); + ddqp->d_ino_softlimit = cpu_to_be64(dqp->q_ino.softlimit); + ddqp->d_rtb_hardlimit = cpu_to_be64(dqp->q_rtb.hardlimit); + ddqp->d_rtb_softlimit = cpu_to_be64(dqp->q_rtb.softlimit); } /* Allocate and initialize the dquot buffer for this in-core dquot. */ @@ -1129,6 +1125,7 @@ static xfs_failaddr_t xfs_qm_dqflush_check( struct xfs_dquot *dqp) { + struct xfs_disk_dquot *ddq = &dqp->q_core; __u8 type = dqp->dq_flags & XFS_DQ_ALLTYPES; if (type != XFS_DQ_USER && @@ -1136,6 +1133,24 @@ xfs_qm_dqflush_check( type != XFS_DQ_PROJ) return __this_address; + if (dqp->q_id == 0) + return NULL; + + if (dqp->q_blk.softlimit && + be64_to_cpu(ddq->d_bcount) > dqp->q_blk.softlimit && + !ddq->d_btimer) + return __this_address; + + if (dqp->q_ino.softlimit && + be64_to_cpu(ddq->d_icount) > dqp->q_ino.softlimit && + !ddq->d_itimer) + return __this_address; + + if (dqp->q_rtb.softlimit && + be64_to_cpu(ddq->d_rtbcount) > dqp->q_rtb.softlimit && + !ddq->d_rtbtimer) + return __this_address; + return NULL; } diff --git a/fs/xfs/xfs_dquot.h b/fs/xfs/xfs_dquot.h index c2f841ed26612..59790140fcb61 100644 --- a/fs/xfs/xfs_dquot.h +++ b/fs/xfs/xfs_dquot.h @@ -30,6 +30,10 @@ enum { struct xfs_dquot_res { /* Total resources allocated and reserved. */ xfs_qcnt_t reserved; + + /* Absolute and preferred limits. */ + xfs_qcnt_t hardlimit; + xfs_qcnt_t softlimit; }; /* @@ -143,7 +147,7 @@ static inline bool xfs_dquot_lowsp(struct xfs_dquot *dqp) { int64_t freesp; - freesp = be64_to_cpu(dqp->q_core.d_blk_hardlimit) - dqp->q_blk.reserved; + freesp = dqp->q_blk.hardlimit - dqp->q_blk.reserved; if (freesp < dqp->q_low_space[XFS_QLOWSP_1_PCNT]) return true; diff --git a/fs/xfs/xfs_qm.c b/fs/xfs/xfs_qm.c index aed4a76b8d613..83ae59536b2b0 100644 --- a/fs/xfs/xfs_qm.c +++ b/fs/xfs/xfs_qm.c @@ -550,26 +550,24 @@ xfs_qm_set_defquota( { struct xfs_dquot *dqp; struct xfs_def_quota *defq; - struct xfs_disk_dquot *ddqp; int error; error = xfs_qm_dqget_uncached(mp, 0, type, &dqp); if (error) return; - ddqp = &dqp->q_core; defq = xfs_get_defquota(qinf, xfs_dquot_type(dqp)); /* * Timers and warnings have been already set, let's just set the * default limits for this quota type */ - defq->bhardlimit = be64_to_cpu(ddqp->d_blk_hardlimit); - defq->bsoftlimit = be64_to_cpu(ddqp->d_blk_softlimit); - defq->ihardlimit = be64_to_cpu(ddqp->d_ino_hardlimit); - defq->isoftlimit = be64_to_cpu(ddqp->d_ino_softlimit); - defq->rtbhardlimit = be64_to_cpu(ddqp->d_rtb_hardlimit); - defq->rtbsoftlimit = be64_to_cpu(ddqp->d_rtb_softlimit); + defq->bhardlimit = dqp->q_blk.hardlimit; + defq->bsoftlimit = dqp->q_blk.softlimit; + defq->ihardlimit = dqp->q_ino.hardlimit; + defq->isoftlimit = dqp->q_ino.softlimit; + defq->rtbhardlimit = dqp->q_rtb.hardlimit; + defq->rtbsoftlimit = dqp->q_rtb.softlimit; xfs_qm_dqdestroy(dqp); } diff --git a/fs/xfs/xfs_qm.h b/fs/xfs/xfs_qm.h index 2c8ca9df23af2..eb5fb7d9d9951 100644 --- a/fs/xfs/xfs_qm.h +++ b/fs/xfs/xfs_qm.h @@ -20,12 +20,12 @@ extern struct kmem_zone *xfs_qm_dqtrxzone; #define XFS_DQITER_MAP_SIZE 10 #define XFS_IS_DQUOT_UNINITIALIZED(dqp) ( \ - !dqp->q_core.d_blk_hardlimit && \ - !dqp->q_core.d_blk_softlimit && \ - !dqp->q_core.d_rtb_hardlimit && \ - !dqp->q_core.d_rtb_softlimit && \ - !dqp->q_core.d_ino_hardlimit && \ - !dqp->q_core.d_ino_softlimit && \ + !dqp->q_blk.hardlimit && \ + !dqp->q_blk.softlimit && \ + !dqp->q_rtb.hardlimit && \ + !dqp->q_rtb.softlimit && \ + !dqp->q_ino.hardlimit && \ + !dqp->q_ino.softlimit && \ !dqp->q_core.d_bcount && \ !dqp->q_core.d_rtbcount && \ !dqp->q_core.d_icount) diff --git a/fs/xfs/xfs_qm_bhv.c b/fs/xfs/xfs_qm_bhv.c index 94b2b4b0fc174..0993217e5ac86 100644 --- a/fs/xfs/xfs_qm_bhv.c +++ b/fs/xfs/xfs_qm_bhv.c @@ -23,9 +23,9 @@ xfs_fill_statvfs_from_dquot( { uint64_t limit; - limit = dqp->q_core.d_blk_softlimit ? - be64_to_cpu(dqp->q_core.d_blk_softlimit) : - be64_to_cpu(dqp->q_core.d_blk_hardlimit); + limit = dqp->q_blk.softlimit ? + dqp->q_blk.softlimit : + dqp->q_blk.hardlimit; if (limit && statp->f_blocks > limit) { statp->f_blocks = limit; statp->f_bfree = statp->f_bavail = @@ -33,9 +33,9 @@ xfs_fill_statvfs_from_dquot( (statp->f_blocks - dqp->q_blk.reserved) : 0; } - limit = dqp->q_core.d_ino_softlimit ? - be64_to_cpu(dqp->q_core.d_ino_softlimit) : - be64_to_cpu(dqp->q_core.d_ino_hardlimit); + limit = dqp->q_ino.softlimit ? + dqp->q_ino.softlimit : + dqp->q_ino.hardlimit; if (limit && statp->f_files > limit) { statp->f_files = limit; statp->f_ffree = diff --git a/fs/xfs/xfs_qm_syscalls.c b/fs/xfs/xfs_qm_syscalls.c index a65ea8b4d24d3..7aab60ea67e2a 100644 --- a/fs/xfs/xfs_qm_syscalls.c +++ b/fs/xfs/xfs_qm_syscalls.c @@ -495,13 +495,13 @@ xfs_qm_scall_setqlim( */ hard = (newlim->d_fieldmask & QC_SPC_HARD) ? (xfs_qcnt_t) XFS_B_TO_FSB(mp, newlim->d_spc_hardlimit) : - be64_to_cpu(ddq->d_blk_hardlimit); + dqp->q_blk.hardlimit; soft = (newlim->d_fieldmask & QC_SPC_SOFT) ? (xfs_qcnt_t) XFS_B_TO_FSB(mp, newlim->d_spc_softlimit) : - be64_to_cpu(ddq->d_blk_softlimit); + dqp->q_blk.softlimit; if (hard == 0 || hard >= soft) { - ddq->d_blk_hardlimit = cpu_to_be64(hard); - ddq->d_blk_softlimit = cpu_to_be64(soft); + dqp->q_blk.hardlimit = hard; + dqp->q_blk.softlimit = soft; xfs_dquot_set_prealloc_limits(dqp); if (id == 0) { defq->bhardlimit = hard; @@ -512,13 +512,13 @@ xfs_qm_scall_setqlim( } hard = (newlim->d_fieldmask & QC_RT_SPC_HARD) ? (xfs_qcnt_t) XFS_B_TO_FSB(mp, newlim->d_rt_spc_hardlimit) : - be64_to_cpu(ddq->d_rtb_hardlimit); + dqp->q_rtb.hardlimit; soft = (newlim->d_fieldmask & QC_RT_SPC_SOFT) ? (xfs_qcnt_t) XFS_B_TO_FSB(mp, newlim->d_rt_spc_softlimit) : - be64_to_cpu(ddq->d_rtb_softlimit); + dqp->q_rtb.softlimit; if (hard == 0 || hard >= soft) { - ddq->d_rtb_hardlimit = cpu_to_be64(hard); - ddq->d_rtb_softlimit = cpu_to_be64(soft); + dqp->q_rtb.hardlimit = hard; + dqp->q_rtb.softlimit = soft; if (id == 0) { defq->rtbhardlimit = hard; defq->rtbsoftlimit = soft; @@ -529,13 +529,13 @@ xfs_qm_scall_setqlim( hard = (newlim->d_fieldmask & QC_INO_HARD) ? (xfs_qcnt_t) newlim->d_ino_hardlimit : - be64_to_cpu(ddq->d_ino_hardlimit); + dqp->q_ino.hardlimit; soft = (newlim->d_fieldmask & QC_INO_SOFT) ? (xfs_qcnt_t) newlim->d_ino_softlimit : - be64_to_cpu(ddq->d_ino_softlimit); + dqp->q_ino.softlimit; if (hard == 0 || hard >= soft) { - ddq->d_ino_hardlimit = cpu_to_be64(hard); - ddq->d_ino_softlimit = cpu_to_be64(soft); + dqp->q_ino.hardlimit = hard; + dqp->q_ino.softlimit = soft; if (id == 0) { defq->ihardlimit = hard; defq->isoftlimit = soft; @@ -619,10 +619,8 @@ xfs_qm_scall_getquota_fill_qc( struct qc_dqblk *dst) { memset(dst, 0, sizeof(*dst)); - dst->d_spc_hardlimit = - XFS_FSB_TO_B(mp, be64_to_cpu(dqp->q_core.d_blk_hardlimit)); - dst->d_spc_softlimit = - XFS_FSB_TO_B(mp, be64_to_cpu(dqp->q_core.d_blk_softlimit)); + dst->d_spc_hardlimit = XFS_FSB_TO_B(mp, dqp->q_blk.hardlimit); + dst->d_spc_softlimit = XFS_FSB_TO_B(mp, dqp->q_blk.softlimit); dst->d_ino_hardlimit = be64_to_cpu(dqp->q_core.d_ino_hardlimit); dst->d_ino_softlimit = be64_to_cpu(dqp->q_core.d_ino_softlimit); dst->d_space = XFS_FSB_TO_B(mp, dqp->q_blk.reserved); @@ -631,10 +629,8 @@ xfs_qm_scall_getquota_fill_qc( dst->d_ino_timer = be32_to_cpu(dqp->q_core.d_itimer); dst->d_ino_warns = be16_to_cpu(dqp->q_core.d_iwarns); dst->d_spc_warns = be16_to_cpu(dqp->q_core.d_bwarns); - dst->d_rt_spc_hardlimit = - XFS_FSB_TO_B(mp, be64_to_cpu(dqp->q_core.d_rtb_hardlimit)); - dst->d_rt_spc_softlimit = - XFS_FSB_TO_B(mp, be64_to_cpu(dqp->q_core.d_rtb_softlimit)); + dst->d_rt_spc_hardlimit = XFS_FSB_TO_B(mp, dqp->q_rtb.hardlimit); + dst->d_rt_spc_softlimit = XFS_FSB_TO_B(mp, dqp->q_rtb.softlimit); dst->d_rt_space = XFS_FSB_TO_B(mp, dqp->q_rtb.reserved); dst->d_rt_spc_timer = be32_to_cpu(dqp->q_core.d_rtbtimer); dst->d_rt_spc_warns = be16_to_cpu(dqp->q_core.d_rtbwarns); @@ -664,8 +660,8 @@ xfs_qm_scall_getquota_fill_qc( (dst->d_spc_softlimit > 0)) { ASSERT(dst->d_spc_timer != 0); } - if ((dst->d_ino_count > dst->d_ino_softlimit) && - (dst->d_ino_softlimit > 0)) { + if ((dst->d_ino_count > dqp->q_ino.softlimit) && + (dqp->q_ino.softlimit > 0)) { ASSERT(dst->d_ino_timer != 0); } } diff --git a/fs/xfs/xfs_trace.h b/fs/xfs/xfs_trace.h index b49c672d1674c..93fe31a22ce8c 100644 --- a/fs/xfs/xfs_trace.h +++ b/fs/xfs/xfs_trace.h @@ -882,14 +882,10 @@ DECLARE_EVENT_CLASS(xfs_dquot_class, __entry->res_bcount = dqp->q_blk.reserved; __entry->bcount = be64_to_cpu(dqp->q_core.d_bcount); __entry->icount = be64_to_cpu(dqp->q_core.d_icount); - __entry->blk_hardlimit = - be64_to_cpu(dqp->q_core.d_blk_hardlimit); - __entry->blk_softlimit = - be64_to_cpu(dqp->q_core.d_blk_softlimit); - __entry->ino_hardlimit = - be64_to_cpu(dqp->q_core.d_ino_hardlimit); - __entry->ino_softlimit = - be64_to_cpu(dqp->q_core.d_ino_softlimit); + __entry->blk_hardlimit = dqp->q_blk.hardlimit; + __entry->blk_softlimit = dqp->q_blk.softlimit; + __entry->ino_hardlimit = dqp->q_ino.hardlimit; + __entry->ino_softlimit = dqp->q_ino.softlimit; ), TP_printk("dev %d:%d id 0x%x flags %s nrefs %u res_bc 0x%llx " "bcnt 0x%llx bhardlimit 0x%llx bsoftlimit 0x%llx " diff --git a/fs/xfs/xfs_trans_dquot.c b/fs/xfs/xfs_trans_dquot.c index ed3928e9392fe..0d6a69a81a58e 100644 --- a/fs/xfs/xfs_trans_dquot.c +++ b/fs/xfs/xfs_trans_dquot.c @@ -593,10 +593,10 @@ xfs_trans_dqresv( defq = xfs_get_defquota(q, xfs_dquot_type(dqp)); if (flags & XFS_TRANS_DQ_RES_BLKS) { - hardlimit = be64_to_cpu(dqp->q_core.d_blk_hardlimit); + hardlimit = dqp->q_blk.hardlimit; if (!hardlimit) hardlimit = defq->bhardlimit; - softlimit = be64_to_cpu(dqp->q_core.d_blk_softlimit); + softlimit = dqp->q_blk.softlimit; if (!softlimit) softlimit = defq->bsoftlimit; timer = be32_to_cpu(dqp->q_core.d_btimer); @@ -605,10 +605,10 @@ xfs_trans_dqresv( resbcountp = &dqp->q_blk.reserved; } else { ASSERT(flags & XFS_TRANS_DQ_RES_RTBLKS); - hardlimit = be64_to_cpu(dqp->q_core.d_rtb_hardlimit); + hardlimit = dqp->q_rtb.hardlimit; if (!hardlimit) hardlimit = defq->rtbhardlimit; - softlimit = be64_to_cpu(dqp->q_core.d_rtb_softlimit); + softlimit = dqp->q_rtb.softlimit; if (!softlimit) softlimit = defq->rtbsoftlimit; timer = be32_to_cpu(dqp->q_core.d_rtbtimer); @@ -649,10 +649,10 @@ xfs_trans_dqresv( timer = be32_to_cpu(dqp->q_core.d_itimer); warns = be16_to_cpu(dqp->q_core.d_iwarns); warnlimit = defq->iwarnlimit; - hardlimit = be64_to_cpu(dqp->q_core.d_ino_hardlimit); + hardlimit = dqp->q_ino.hardlimit; if (!hardlimit) hardlimit = defq->ihardlimit; - softlimit = be64_to_cpu(dqp->q_core.d_ino_softlimit); + softlimit = dqp->q_ino.softlimit; if (!softlimit) softlimit = defq->isoftlimit; -- GitLab From be37d40c1ba0b5484ea2f8c109a9eda13e4c690a Mon Sep 17 00:00:00 2001 From: "Darrick J. Wong" Date: Tue, 14 Jul 2020 10:37:31 -0700 Subject: [PATCH 1005/1476] xfs: stop using q_core counters in the quota code Add counter fields to the incore dquot, and use that instead of the ones in qcore. This eliminates a bunch of endian conversions and will eventually allow us to remove qcore entirely. Signed-off-by: Darrick J. Wong Reviewed-by: Chandan Babu R Reviewed-by: Christoph Hellwig Reviewed-by: Allison Collins --- fs/xfs/scrub/quota.c | 18 +++++---------- fs/xfs/xfs_dquot.c | 47 ++++++++++++++++++++++------------------ fs/xfs/xfs_dquot.h | 3 +++ fs/xfs/xfs_qm.c | 6 ++--- fs/xfs/xfs_qm.h | 6 ++--- fs/xfs/xfs_trace.h | 4 ++-- fs/xfs/xfs_trans_dquot.c | 34 +++++++++++------------------ 7 files changed, 56 insertions(+), 62 deletions(-) diff --git a/fs/xfs/scrub/quota.c b/fs/xfs/scrub/quota.c index e73e6c88e76a8..20bc763e88b48 100644 --- a/fs/xfs/scrub/quota.c +++ b/fs/xfs/scrub/quota.c @@ -82,9 +82,6 @@ xchk_quota_item( struct xfs_disk_dquot *d = &dq->q_core; struct xfs_quotainfo *qi = mp->m_quotainfo; xfs_fileoff_t offset; - unsigned long long bcount; - unsigned long long icount; - unsigned long long rcount; xfs_ino_t fs_icount; int error = 0; @@ -132,9 +129,6 @@ xchk_quota_item( xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, offset); /* Check the resource counts. */ - bcount = be64_to_cpu(d->d_bcount); - icount = be64_to_cpu(d->d_icount); - rcount = be64_to_cpu(d->d_rtbcount); fs_icount = percpu_counter_sum(&mp->m_icount); /* @@ -143,15 +137,15 @@ xchk_quota_item( * if there are no quota limits. */ if (xfs_sb_version_hasreflink(&mp->m_sb)) { - if (mp->m_sb.sb_dblocks < bcount) + if (mp->m_sb.sb_dblocks < dq->q_blk.count) xchk_fblock_set_warning(sc, XFS_DATA_FORK, offset); } else { - if (mp->m_sb.sb_dblocks < bcount) + if (mp->m_sb.sb_dblocks < dq->q_blk.count) xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, offset); } - if (icount > fs_icount || rcount > mp->m_sb.sb_rblocks) + if (dq->q_ino.count > fs_icount || dq->q_rtb.count > mp->m_sb.sb_rblocks) xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, offset); /* @@ -163,15 +157,15 @@ xchk_quota_item( goto out; if (dq->q_blk.hardlimit != 0 && - bcount > dq->q_blk.hardlimit) + dq->q_blk.count > dq->q_blk.hardlimit) xchk_fblock_set_warning(sc, XFS_DATA_FORK, offset); if (dq->q_ino.hardlimit != 0 && - icount > dq->q_ino.hardlimit) + dq->q_ino.count > dq->q_ino.hardlimit) xchk_fblock_set_warning(sc, XFS_DATA_FORK, offset); if (dq->q_rtb.hardlimit != 0 && - rcount > dq->q_rtb.hardlimit) + dq->q_rtb.count > dq->q_rtb.hardlimit) xchk_fblock_set_warning(sc, XFS_DATA_FORK, offset); out: diff --git a/fs/xfs/xfs_dquot.c b/fs/xfs/xfs_dquot.c index 9116e6ad7e9e6..dc3bfce7f28b3 100644 --- a/fs/xfs/xfs_dquot.c +++ b/fs/xfs/xfs_dquot.c @@ -134,9 +134,9 @@ xfs_qm_adjust_dqtimers( if (!d->d_btimer) { if ((dq->q_blk.softlimit && - (be64_to_cpu(d->d_bcount) > dq->q_blk.softlimit)) || + (dq->q_blk.count > dq->q_blk.softlimit)) || (dq->q_blk.hardlimit && - (be64_to_cpu(d->d_bcount) > dq->q_blk.hardlimit))) { + (dq->q_blk.count > dq->q_blk.hardlimit))) { d->d_btimer = cpu_to_be32(ktime_get_real_seconds() + defq->btimelimit); } else { @@ -144,18 +144,18 @@ xfs_qm_adjust_dqtimers( } } else { if ((!dq->q_blk.softlimit || - (be64_to_cpu(d->d_bcount) <= dq->q_blk.softlimit)) && + (dq->q_blk.count <= dq->q_blk.softlimit)) && (!dq->q_blk.hardlimit || - (be64_to_cpu(d->d_bcount) <= dq->q_blk.hardlimit))) { + (dq->q_blk.count <= dq->q_blk.hardlimit))) { d->d_btimer = 0; } } if (!d->d_itimer) { if ((dq->q_ino.softlimit && - (be64_to_cpu(d->d_icount) > dq->q_ino.softlimit)) || + (dq->q_ino.count > dq->q_ino.softlimit)) || (dq->q_ino.hardlimit && - (be64_to_cpu(d->d_icount) > dq->q_ino.hardlimit))) { + (dq->q_ino.count > dq->q_ino.hardlimit))) { d->d_itimer = cpu_to_be32(ktime_get_real_seconds() + defq->itimelimit); } else { @@ -163,18 +163,18 @@ xfs_qm_adjust_dqtimers( } } else { if ((!dq->q_ino.softlimit || - (be64_to_cpu(d->d_icount) <= dq->q_ino.softlimit)) && + (dq->q_ino.count <= dq->q_ino.softlimit)) && (!dq->q_ino.hardlimit || - (be64_to_cpu(d->d_icount) <= dq->q_ino.hardlimit))) { + (dq->q_ino.count <= dq->q_ino.hardlimit))) { d->d_itimer = 0; } } if (!d->d_rtbtimer) { if ((dq->q_rtb.softlimit && - (be64_to_cpu(d->d_rtbcount) > dq->q_rtb.softlimit)) || + (dq->q_rtb.count > dq->q_rtb.softlimit)) || (dq->q_rtb.hardlimit && - (be64_to_cpu(d->d_rtbcount) > dq->q_rtb.hardlimit))) { + (dq->q_rtb.count > dq->q_rtb.hardlimit))) { d->d_rtbtimer = cpu_to_be32(ktime_get_real_seconds() + defq->rtbtimelimit); } else { @@ -182,9 +182,9 @@ xfs_qm_adjust_dqtimers( } } else { if ((!dq->q_rtb.softlimit || - (be64_to_cpu(d->d_rtbcount) <= dq->q_rtb.softlimit)) && + (dq->q_rtb.count <= dq->q_rtb.softlimit)) && (!dq->q_rtb.hardlimit || - (be64_to_cpu(d->d_rtbcount) <= dq->q_rtb.hardlimit))) { + (dq->q_rtb.count <= dq->q_rtb.hardlimit))) { d->d_rtbtimer = 0; } } @@ -538,13 +538,17 @@ xfs_dquot_from_disk( dqp->q_rtb.hardlimit = be64_to_cpu(ddqp->d_rtb_hardlimit); dqp->q_rtb.softlimit = be64_to_cpu(ddqp->d_rtb_softlimit); + dqp->q_blk.count = be64_to_cpu(ddqp->d_bcount); + dqp->q_ino.count = be64_to_cpu(ddqp->d_icount); + dqp->q_rtb.count = be64_to_cpu(ddqp->d_rtbcount); + /* * Reservation counters are defined as reservation plus current usage * to avoid having to add every time. */ - dqp->q_blk.reserved = be64_to_cpu(ddqp->d_bcount); - dqp->q_ino.reserved = be64_to_cpu(ddqp->d_icount); - dqp->q_rtb.reserved = be64_to_cpu(ddqp->d_rtbcount); + dqp->q_blk.reserved = dqp->q_blk.count; + dqp->q_ino.reserved = dqp->q_ino.count; + dqp->q_rtb.reserved = dqp->q_rtb.count; /* initialize the dquot speculative prealloc thresholds */ xfs_dquot_set_prealloc_limits(dqp); @@ -564,6 +568,10 @@ xfs_dquot_to_disk( ddqp->d_ino_softlimit = cpu_to_be64(dqp->q_ino.softlimit); ddqp->d_rtb_hardlimit = cpu_to_be64(dqp->q_rtb.hardlimit); ddqp->d_rtb_softlimit = cpu_to_be64(dqp->q_rtb.softlimit); + + ddqp->d_bcount = cpu_to_be64(dqp->q_blk.count); + ddqp->d_icount = cpu_to_be64(dqp->q_ino.count); + ddqp->d_rtbcount = cpu_to_be64(dqp->q_rtb.count); } /* Allocate and initialize the dquot buffer for this in-core dquot. */ @@ -1136,18 +1144,15 @@ xfs_qm_dqflush_check( if (dqp->q_id == 0) return NULL; - if (dqp->q_blk.softlimit && - be64_to_cpu(ddq->d_bcount) > dqp->q_blk.softlimit && + if (dqp->q_blk.softlimit && dqp->q_blk.count > dqp->q_blk.softlimit && !ddq->d_btimer) return __this_address; - if (dqp->q_ino.softlimit && - be64_to_cpu(ddq->d_icount) > dqp->q_ino.softlimit && + if (dqp->q_ino.softlimit && dqp->q_ino.count > dqp->q_ino.softlimit && !ddq->d_itimer) return __this_address; - if (dqp->q_rtb.softlimit && - be64_to_cpu(ddq->d_rtbcount) > dqp->q_rtb.softlimit && + if (dqp->q_rtb.softlimit && dqp->q_rtb.count > dqp->q_rtb.softlimit && !ddq->d_rtbtimer) return __this_address; diff --git a/fs/xfs/xfs_dquot.h b/fs/xfs/xfs_dquot.h index 59790140fcb61..f5f0a15c0f7bf 100644 --- a/fs/xfs/xfs_dquot.h +++ b/fs/xfs/xfs_dquot.h @@ -31,6 +31,9 @@ struct xfs_dquot_res { /* Total resources allocated and reserved. */ xfs_qcnt_t reserved; + /* Total resources allocated. */ + xfs_qcnt_t count; + /* Absolute and preferred limits. */ xfs_qcnt_t hardlimit; xfs_qcnt_t softlimit; diff --git a/fs/xfs/xfs_qm.c b/fs/xfs/xfs_qm.c index 83ae59536b2b0..d6df2a4ca4cac 100644 --- a/fs/xfs/xfs_qm.c +++ b/fs/xfs/xfs_qm.c @@ -1089,14 +1089,14 @@ xfs_qm_quotacheck_dqadjust( * Adjust the inode count and the block count to reflect this inode's * resource usage. */ - be64_add_cpu(&dqp->q_core.d_icount, 1); + dqp->q_ino.count++; dqp->q_ino.reserved++; if (nblks) { - be64_add_cpu(&dqp->q_core.d_bcount, nblks); + dqp->q_blk.count += nblks; dqp->q_blk.reserved += nblks; } if (rtblks) { - be64_add_cpu(&dqp->q_core.d_rtbcount, rtblks); + dqp->q_rtb.count += rtblks; dqp->q_rtb.reserved += rtblks; } diff --git a/fs/xfs/xfs_qm.h b/fs/xfs/xfs_qm.h index eb5fb7d9d9951..57bddadbc051d 100644 --- a/fs/xfs/xfs_qm.h +++ b/fs/xfs/xfs_qm.h @@ -26,9 +26,9 @@ extern struct kmem_zone *xfs_qm_dqtrxzone; !dqp->q_rtb.softlimit && \ !dqp->q_ino.hardlimit && \ !dqp->q_ino.softlimit && \ - !dqp->q_core.d_bcount && \ - !dqp->q_core.d_rtbcount && \ - !dqp->q_core.d_icount) + !dqp->q_blk.count && \ + !dqp->q_rtb.count && \ + !dqp->q_ino.count) /* Defaults for each quota type: time limits, warn limits, usage limits */ struct xfs_def_quota { diff --git a/fs/xfs/xfs_trace.h b/fs/xfs/xfs_trace.h index 93fe31a22ce8c..f0c2bce69a362 100644 --- a/fs/xfs/xfs_trace.h +++ b/fs/xfs/xfs_trace.h @@ -880,8 +880,8 @@ DECLARE_EVENT_CLASS(xfs_dquot_class, __entry->flags = dqp->dq_flags | dqp->q_flags; __entry->nrefs = dqp->q_nrefs; __entry->res_bcount = dqp->q_blk.reserved; - __entry->bcount = be64_to_cpu(dqp->q_core.d_bcount); - __entry->icount = be64_to_cpu(dqp->q_core.d_icount); + __entry->bcount = dqp->q_blk.count; + __entry->icount = dqp->q_ino.count; __entry->blk_hardlimit = dqp->q_blk.hardlimit; __entry->blk_softlimit = dqp->q_blk.softlimit; __entry->ino_hardlimit = dqp->q_ino.hardlimit; diff --git a/fs/xfs/xfs_trans_dquot.c b/fs/xfs/xfs_trans_dquot.c index 0d6a69a81a58e..1c2a45989aafe 100644 --- a/fs/xfs/xfs_trans_dquot.c +++ b/fs/xfs/xfs_trans_dquot.c @@ -309,7 +309,6 @@ xfs_trans_apply_dquot_deltas( int i, j; struct xfs_dquot *dqp; struct xfs_dqtrx *qtrx, *qa; - struct xfs_disk_dquot *d; int64_t totalbdelta; int64_t totalrtbdelta; @@ -341,7 +340,6 @@ xfs_trans_apply_dquot_deltas( /* * adjust the actual number of blocks used */ - d = &dqp->q_core; /* * The issue here is - sometimes we don't make a blkquota @@ -362,25 +360,22 @@ xfs_trans_apply_dquot_deltas( qtrx->qt_delrtb_delta; #ifdef DEBUG if (totalbdelta < 0) - ASSERT(be64_to_cpu(d->d_bcount) >= - -totalbdelta); + ASSERT(dqp->q_blk.count >= -totalbdelta); if (totalrtbdelta < 0) - ASSERT(be64_to_cpu(d->d_rtbcount) >= - -totalrtbdelta); + ASSERT(dqp->q_rtb.count >= -totalrtbdelta); if (qtrx->qt_icount_delta < 0) - ASSERT(be64_to_cpu(d->d_icount) >= - -qtrx->qt_icount_delta); + ASSERT(dqp->q_ino.count >= -qtrx->qt_icount_delta); #endif if (totalbdelta) - be64_add_cpu(&d->d_bcount, (xfs_qcnt_t)totalbdelta); + dqp->q_blk.count += totalbdelta; if (qtrx->qt_icount_delta) - be64_add_cpu(&d->d_icount, (xfs_qcnt_t)qtrx->qt_icount_delta); + dqp->q_ino.count += qtrx->qt_icount_delta; if (totalrtbdelta) - be64_add_cpu(&d->d_rtbcount, (xfs_qcnt_t)totalrtbdelta); + dqp->q_rtb.count += totalrtbdelta; /* * Get any default limits in use. @@ -467,12 +462,9 @@ xfs_trans_apply_dquot_deltas( (xfs_qcnt_t)qtrx->qt_icount_delta; } - ASSERT(dqp->q_blk.reserved >= - be64_to_cpu(dqp->q_core.d_bcount)); - ASSERT(dqp->q_ino.reserved >= - be64_to_cpu(dqp->q_core.d_icount)); - ASSERT(dqp->q_rtb.reserved >= - be64_to_cpu(dqp->q_core.d_rtbcount)); + ASSERT(dqp->q_blk.reserved >= dqp->q_blk.count); + ASSERT(dqp->q_ino.reserved >= dqp->q_ino.count); + ASSERT(dqp->q_rtb.reserved >= dqp->q_rtb.count); } } } @@ -675,7 +667,7 @@ xfs_trans_dqresv( /* * Change the reservation, but not the actual usage. - * Note that q_blk.reserved = q_core.d_bcount + resv + * Note that q_blk.reserved = q_blk.count + resv */ (*resbcountp) += (xfs_qcnt_t)nblks; if (ninos != 0) @@ -700,9 +692,9 @@ xfs_trans_dqresv( XFS_TRANS_DQ_RES_INOS, ninos); } - ASSERT(dqp->q_blk.reserved >= be64_to_cpu(dqp->q_core.d_bcount)); - ASSERT(dqp->q_rtb.reserved >= be64_to_cpu(dqp->q_core.d_rtbcount)); - ASSERT(dqp->q_ino.reserved >= be64_to_cpu(dqp->q_core.d_icount)); + ASSERT(dqp->q_blk.reserved >= dqp->q_blk.count); + ASSERT(dqp->q_rtb.reserved >= dqp->q_rtb.count); + ASSERT(dqp->q_ino.reserved >= dqp->q_ino.count); xfs_dqunlock(dqp); return 0; -- GitLab From c8c45fb2f614e1d30526c5aa304352923ad76416 Mon Sep 17 00:00:00 2001 From: "Darrick J. Wong" Date: Tue, 14 Jul 2020 10:37:31 -0700 Subject: [PATCH 1006/1476] xfs: stop using q_core warning counters in the quota code Add warning counter fields to the incore dquot, and use that instead of the ones in qcore. This eliminates a bunch of endian conversions and will eventually allow us to remove qcore entirely. Signed-off-by: Darrick J. Wong Reviewed-by: Chandan Babu R Reviewed-by: Christoph Hellwig Reviewed-by: Allison Collins --- fs/xfs/xfs_dquot.c | 14 +++++++++++--- fs/xfs/xfs_dquot.h | 8 ++++++++ fs/xfs/xfs_qm.c | 12 ++++++------ fs/xfs/xfs_qm_syscalls.c | 12 ++++++------ fs/xfs/xfs_trans_dquot.c | 6 +++--- 5 files changed, 34 insertions(+), 18 deletions(-) diff --git a/fs/xfs/xfs_dquot.c b/fs/xfs/xfs_dquot.c index dc3bfce7f28b3..9ba21523a4817 100644 --- a/fs/xfs/xfs_dquot.c +++ b/fs/xfs/xfs_dquot.c @@ -140,7 +140,7 @@ xfs_qm_adjust_dqtimers( d->d_btimer = cpu_to_be32(ktime_get_real_seconds() + defq->btimelimit); } else { - d->d_bwarns = 0; + dq->q_blk.warnings = 0; } } else { if ((!dq->q_blk.softlimit || @@ -159,7 +159,7 @@ xfs_qm_adjust_dqtimers( d->d_itimer = cpu_to_be32(ktime_get_real_seconds() + defq->itimelimit); } else { - d->d_iwarns = 0; + dq->q_ino.warnings = 0; } } else { if ((!dq->q_ino.softlimit || @@ -178,7 +178,7 @@ xfs_qm_adjust_dqtimers( d->d_rtbtimer = cpu_to_be32(ktime_get_real_seconds() + defq->rtbtimelimit); } else { - d->d_rtbwarns = 0; + dq->q_rtb.warnings = 0; } } else { if ((!dq->q_rtb.softlimit || @@ -542,6 +542,10 @@ xfs_dquot_from_disk( dqp->q_ino.count = be64_to_cpu(ddqp->d_icount); dqp->q_rtb.count = be64_to_cpu(ddqp->d_rtbcount); + dqp->q_blk.warnings = be16_to_cpu(ddqp->d_bwarns); + dqp->q_ino.warnings = be16_to_cpu(ddqp->d_iwarns); + dqp->q_rtb.warnings = be16_to_cpu(ddqp->d_rtbwarns); + /* * Reservation counters are defined as reservation plus current usage * to avoid having to add every time. @@ -572,6 +576,10 @@ xfs_dquot_to_disk( ddqp->d_bcount = cpu_to_be64(dqp->q_blk.count); ddqp->d_icount = cpu_to_be64(dqp->q_ino.count); ddqp->d_rtbcount = cpu_to_be64(dqp->q_rtb.count); + + ddqp->d_bwarns = cpu_to_be16(dqp->q_blk.warnings); + ddqp->d_iwarns = cpu_to_be16(dqp->q_ino.warnings); + ddqp->d_rtbwarns = cpu_to_be16(dqp->q_rtb.warnings); } /* Allocate and initialize the dquot buffer for this in-core dquot. */ diff --git a/fs/xfs/xfs_dquot.h b/fs/xfs/xfs_dquot.h index f5f0a15c0f7bf..afac622c0fde8 100644 --- a/fs/xfs/xfs_dquot.h +++ b/fs/xfs/xfs_dquot.h @@ -37,6 +37,14 @@ struct xfs_dquot_res { /* Absolute and preferred limits. */ xfs_qcnt_t hardlimit; xfs_qcnt_t softlimit; + + /* + * For root dquots, this is the maximum number of warnings that will + * be issued for this quota type. Otherwise, this is the number of + * warnings issued against this quota. Note that none of this is + * implemented. + */ + xfs_qwarncnt_t warnings; }; /* diff --git a/fs/xfs/xfs_qm.c b/fs/xfs/xfs_qm.c index d6df2a4ca4cac..4dc989551710e 100644 --- a/fs/xfs/xfs_qm.c +++ b/fs/xfs/xfs_qm.c @@ -616,12 +616,12 @@ xfs_qm_init_timelimits( defq->itimelimit = be32_to_cpu(ddqp->d_itimer); if (ddqp->d_rtbtimer) defq->rtbtimelimit = be32_to_cpu(ddqp->d_rtbtimer); - if (ddqp->d_bwarns) - defq->bwarnlimit = be16_to_cpu(ddqp->d_bwarns); - if (ddqp->d_iwarns) - defq->iwarnlimit = be16_to_cpu(ddqp->d_iwarns); - if (ddqp->d_rtbwarns) - defq->rtbwarnlimit = be16_to_cpu(ddqp->d_rtbwarns); + if (dqp->q_blk.warnings) + defq->bwarnlimit = dqp->q_blk.warnings; + if (dqp->q_ino.warnings) + defq->iwarnlimit = dqp->q_ino.warnings; + if (dqp->q_rtb.warnings) + defq->rtbwarnlimit = dqp->q_rtb.warnings; xfs_qm_dqdestroy(dqp); } diff --git a/fs/xfs/xfs_qm_syscalls.c b/fs/xfs/xfs_qm_syscalls.c index 7aab60ea67e2a..acea375c0d178 100644 --- a/fs/xfs/xfs_qm_syscalls.c +++ b/fs/xfs/xfs_qm_syscalls.c @@ -548,11 +548,11 @@ xfs_qm_scall_setqlim( * Update warnings counter(s) if requested */ if (newlim->d_fieldmask & QC_SPC_WARNS) - ddq->d_bwarns = cpu_to_be16(newlim->d_spc_warns); + dqp->q_blk.warnings = newlim->d_spc_warns; if (newlim->d_fieldmask & QC_INO_WARNS) - ddq->d_iwarns = cpu_to_be16(newlim->d_ino_warns); + dqp->q_ino.warnings = newlim->d_ino_warns; if (newlim->d_fieldmask & QC_RT_SPC_WARNS) - ddq->d_rtbwarns = cpu_to_be16(newlim->d_rt_spc_warns); + dqp->q_rtb.warnings = newlim->d_rt_spc_warns; if (id == 0) { if (newlim->d_fieldmask & QC_SPC_WARNS) @@ -627,13 +627,13 @@ xfs_qm_scall_getquota_fill_qc( dst->d_ino_count = dqp->q_ino.reserved; dst->d_spc_timer = be32_to_cpu(dqp->q_core.d_btimer); dst->d_ino_timer = be32_to_cpu(dqp->q_core.d_itimer); - dst->d_ino_warns = be16_to_cpu(dqp->q_core.d_iwarns); - dst->d_spc_warns = be16_to_cpu(dqp->q_core.d_bwarns); + dst->d_ino_warns = dqp->q_ino.warnings; + dst->d_spc_warns = dqp->q_blk.warnings; dst->d_rt_spc_hardlimit = XFS_FSB_TO_B(mp, dqp->q_rtb.hardlimit); dst->d_rt_spc_softlimit = XFS_FSB_TO_B(mp, dqp->q_rtb.softlimit); dst->d_rt_space = XFS_FSB_TO_B(mp, dqp->q_rtb.reserved); dst->d_rt_spc_timer = be32_to_cpu(dqp->q_core.d_rtbtimer); - dst->d_rt_spc_warns = be16_to_cpu(dqp->q_core.d_rtbwarns); + dst->d_rt_spc_warns = dqp->q_rtb.warnings; /* * Internally, we don't reset all the timers when quota enforcement diff --git a/fs/xfs/xfs_trans_dquot.c b/fs/xfs/xfs_trans_dquot.c index 1c2a45989aafe..58cbc8f216f19 100644 --- a/fs/xfs/xfs_trans_dquot.c +++ b/fs/xfs/xfs_trans_dquot.c @@ -592,7 +592,7 @@ xfs_trans_dqresv( if (!softlimit) softlimit = defq->bsoftlimit; timer = be32_to_cpu(dqp->q_core.d_btimer); - warns = be16_to_cpu(dqp->q_core.d_bwarns); + warns = dqp->q_blk.warnings; warnlimit = defq->bwarnlimit; resbcountp = &dqp->q_blk.reserved; } else { @@ -604,7 +604,7 @@ xfs_trans_dqresv( if (!softlimit) softlimit = defq->rtbsoftlimit; timer = be32_to_cpu(dqp->q_core.d_rtbtimer); - warns = be16_to_cpu(dqp->q_core.d_rtbwarns); + warns = dqp->q_rtb.warnings; warnlimit = defq->rtbwarnlimit; resbcountp = &dqp->q_rtb.reserved; } @@ -639,7 +639,7 @@ xfs_trans_dqresv( if (ninos > 0) { total_count = dqp->q_ino.reserved + ninos; timer = be32_to_cpu(dqp->q_core.d_itimer); - warns = be16_to_cpu(dqp->q_core.d_iwarns); + warns = dqp->q_ino.warnings; warnlimit = defq->iwarnlimit; hardlimit = dqp->q_ino.hardlimit; if (!hardlimit) -- GitLab From 19dce7eaef7f8fdecab965afacc7a7bf3eb4e0a1 Mon Sep 17 00:00:00 2001 From: "Darrick J. Wong" Date: Tue, 14 Jul 2020 10:37:32 -0700 Subject: [PATCH 1007/1476] xfs: stop using q_core timers in the quota code Add timers fields to the incore dquot, and use that instead of the ones in qcore. This eliminates a bunch of endian conversions and will eventually allow us to remove qcore entirely. Signed-off-by: Darrick J. Wong Reviewed-by: Chandan Babu R Reviewed-by: Christoph Hellwig Reviewed-by: Allison Collins --- fs/xfs/xfs_dquot.c | 40 +++++++++++++++++++++++----------------- fs/xfs/xfs_dquot.h | 7 +++++++ fs/xfs/xfs_qm.c | 15 ++++++--------- fs/xfs/xfs_qm_syscalls.c | 18 ++++++++---------- fs/xfs/xfs_trans_dquot.c | 6 +++--- 5 files changed, 47 insertions(+), 39 deletions(-) diff --git a/fs/xfs/xfs_dquot.c b/fs/xfs/xfs_dquot.c index 9ba21523a4817..b094fc495407f 100644 --- a/fs/xfs/xfs_dquot.c +++ b/fs/xfs/xfs_dquot.c @@ -117,7 +117,6 @@ xfs_qm_adjust_dqtimers( struct xfs_dquot *dq) { struct xfs_quotainfo *qi = mp->m_quotainfo; - struct xfs_disk_dquot *d = &dq->q_core; struct xfs_def_quota *defq; ASSERT(dq->q_id); @@ -132,13 +131,13 @@ xfs_qm_adjust_dqtimers( ASSERT(dq->q_rtb.softlimit <= dq->q_rtb.hardlimit); #endif - if (!d->d_btimer) { + if (!dq->q_blk.timer) { if ((dq->q_blk.softlimit && (dq->q_blk.count > dq->q_blk.softlimit)) || (dq->q_blk.hardlimit && (dq->q_blk.count > dq->q_blk.hardlimit))) { - d->d_btimer = cpu_to_be32(ktime_get_real_seconds() + - defq->btimelimit); + dq->q_blk.timer = ktime_get_real_seconds() + + defq->btimelimit; } else { dq->q_blk.warnings = 0; } @@ -147,17 +146,17 @@ xfs_qm_adjust_dqtimers( (dq->q_blk.count <= dq->q_blk.softlimit)) && (!dq->q_blk.hardlimit || (dq->q_blk.count <= dq->q_blk.hardlimit))) { - d->d_btimer = 0; + dq->q_blk.timer = 0; } } - if (!d->d_itimer) { + if (!dq->q_ino.timer) { if ((dq->q_ino.softlimit && (dq->q_ino.count > dq->q_ino.softlimit)) || (dq->q_ino.hardlimit && (dq->q_ino.count > dq->q_ino.hardlimit))) { - d->d_itimer = cpu_to_be32(ktime_get_real_seconds() + - defq->itimelimit); + dq->q_ino.timer = ktime_get_real_seconds() + + defq->itimelimit; } else { dq->q_ino.warnings = 0; } @@ -166,17 +165,17 @@ xfs_qm_adjust_dqtimers( (dq->q_ino.count <= dq->q_ino.softlimit)) && (!dq->q_ino.hardlimit || (dq->q_ino.count <= dq->q_ino.hardlimit))) { - d->d_itimer = 0; + dq->q_ino.timer = 0; } } - if (!d->d_rtbtimer) { + if (!dq->q_rtb.timer) { if ((dq->q_rtb.softlimit && (dq->q_rtb.count > dq->q_rtb.softlimit)) || (dq->q_rtb.hardlimit && (dq->q_rtb.count > dq->q_rtb.hardlimit))) { - d->d_rtbtimer = cpu_to_be32(ktime_get_real_seconds() + - defq->rtbtimelimit); + dq->q_rtb.timer = ktime_get_real_seconds() + + defq->rtbtimelimit; } else { dq->q_rtb.warnings = 0; } @@ -185,7 +184,7 @@ xfs_qm_adjust_dqtimers( (dq->q_rtb.count <= dq->q_rtb.softlimit)) && (!dq->q_rtb.hardlimit || (dq->q_rtb.count <= dq->q_rtb.hardlimit))) { - d->d_rtbtimer = 0; + dq->q_rtb.timer = 0; } } } @@ -546,6 +545,10 @@ xfs_dquot_from_disk( dqp->q_ino.warnings = be16_to_cpu(ddqp->d_iwarns); dqp->q_rtb.warnings = be16_to_cpu(ddqp->d_rtbwarns); + dqp->q_blk.timer = be32_to_cpu(ddqp->d_btimer); + dqp->q_ino.timer = be32_to_cpu(ddqp->d_itimer); + dqp->q_rtb.timer = be32_to_cpu(ddqp->d_rtbtimer); + /* * Reservation counters are defined as reservation plus current usage * to avoid having to add every time. @@ -580,6 +583,10 @@ xfs_dquot_to_disk( ddqp->d_bwarns = cpu_to_be16(dqp->q_blk.warnings); ddqp->d_iwarns = cpu_to_be16(dqp->q_ino.warnings); ddqp->d_rtbwarns = cpu_to_be16(dqp->q_rtb.warnings); + + ddqp->d_btimer = cpu_to_be32(dqp->q_blk.timer); + ddqp->d_itimer = cpu_to_be32(dqp->q_ino.timer); + ddqp->d_rtbtimer = cpu_to_be32(dqp->q_rtb.timer); } /* Allocate and initialize the dquot buffer for this in-core dquot. */ @@ -1141,7 +1148,6 @@ static xfs_failaddr_t xfs_qm_dqflush_check( struct xfs_dquot *dqp) { - struct xfs_disk_dquot *ddq = &dqp->q_core; __u8 type = dqp->dq_flags & XFS_DQ_ALLTYPES; if (type != XFS_DQ_USER && @@ -1153,15 +1159,15 @@ xfs_qm_dqflush_check( return NULL; if (dqp->q_blk.softlimit && dqp->q_blk.count > dqp->q_blk.softlimit && - !ddq->d_btimer) + !dqp->q_blk.timer) return __this_address; if (dqp->q_ino.softlimit && dqp->q_ino.count > dqp->q_ino.softlimit && - !ddq->d_itimer) + !dqp->q_ino.timer) return __this_address; if (dqp->q_rtb.softlimit && dqp->q_rtb.count > dqp->q_rtb.softlimit && - !ddq->d_rtbtimer) + !dqp->q_rtb.timer) return __this_address; return NULL; diff --git a/fs/xfs/xfs_dquot.h b/fs/xfs/xfs_dquot.h index afac622c0fde8..69dbf3d95bf36 100644 --- a/fs/xfs/xfs_dquot.h +++ b/fs/xfs/xfs_dquot.h @@ -38,6 +38,13 @@ struct xfs_dquot_res { xfs_qcnt_t hardlimit; xfs_qcnt_t softlimit; + /* + * For root dquots, this is the default grace period, in seconds. + * Otherwise, this is when the quota grace period expires, + * in seconds since the Unix epoch. + */ + time64_t timer; + /* * For root dquots, this is the maximum number of warnings that will * be issued for this quota type. Otherwise, this is the number of diff --git a/fs/xfs/xfs_qm.c b/fs/xfs/xfs_qm.c index 4dc989551710e..73fa0b5d37b38 100644 --- a/fs/xfs/xfs_qm.c +++ b/fs/xfs/xfs_qm.c @@ -579,7 +579,6 @@ xfs_qm_init_timelimits( { struct xfs_quotainfo *qinf = mp->m_quotainfo; struct xfs_def_quota *defq; - struct xfs_disk_dquot *ddqp; struct xfs_dquot *dqp; int error; @@ -603,19 +602,17 @@ xfs_qm_init_timelimits( if (error) return; - ddqp = &dqp->q_core; - /* * The warnings and timers set the grace period given to * a user or group before he or she can not perform any * more writing. If it is zero, a default is used. */ - if (ddqp->d_btimer) - defq->btimelimit = be32_to_cpu(ddqp->d_btimer); - if (ddqp->d_itimer) - defq->itimelimit = be32_to_cpu(ddqp->d_itimer); - if (ddqp->d_rtbtimer) - defq->rtbtimelimit = be32_to_cpu(ddqp->d_rtbtimer); + if (dqp->q_blk.timer) + defq->btimelimit = dqp->q_blk.timer; + if (dqp->q_ino.timer) + defq->itimelimit = dqp->q_ino.timer; + if (dqp->q_rtb.timer) + defq->rtbtimelimit = dqp->q_rtb.timer; if (dqp->q_blk.warnings) defq->bwarnlimit = dqp->q_blk.warnings; if (dqp->q_ino.warnings) diff --git a/fs/xfs/xfs_qm_syscalls.c b/fs/xfs/xfs_qm_syscalls.c index acea375c0d178..9e3507b083ceb 100644 --- a/fs/xfs/xfs_qm_syscalls.c +++ b/fs/xfs/xfs_qm_syscalls.c @@ -447,7 +447,6 @@ xfs_qm_scall_setqlim( struct qc_dqblk *newlim) { struct xfs_quotainfo *q = mp->m_quotainfo; - struct xfs_disk_dquot *ddq; struct xfs_dquot *dqp; struct xfs_trans *tp; struct xfs_def_quota *defq; @@ -488,7 +487,6 @@ xfs_qm_scall_setqlim( xfs_dqlock(dqp); xfs_trans_dqjoin(tp, dqp); - ddq = &dqp->q_core; /* * Make sure that hardlimits are >= soft limits before changing. @@ -573,11 +571,11 @@ xfs_qm_scall_setqlim( * the soft limit. */ if (newlim->d_fieldmask & QC_SPC_TIMER) - ddq->d_btimer = cpu_to_be32(newlim->d_spc_timer); + dqp->q_blk.timer = newlim->d_spc_timer; if (newlim->d_fieldmask & QC_INO_TIMER) - ddq->d_itimer = cpu_to_be32(newlim->d_ino_timer); + dqp->q_ino.timer = newlim->d_ino_timer; if (newlim->d_fieldmask & QC_RT_SPC_TIMER) - ddq->d_rtbtimer = cpu_to_be32(newlim->d_rt_spc_timer); + dqp->q_rtb.timer = newlim->d_rt_spc_timer; if (id == 0) { if (newlim->d_fieldmask & QC_SPC_TIMER) @@ -621,18 +619,18 @@ xfs_qm_scall_getquota_fill_qc( memset(dst, 0, sizeof(*dst)); dst->d_spc_hardlimit = XFS_FSB_TO_B(mp, dqp->q_blk.hardlimit); dst->d_spc_softlimit = XFS_FSB_TO_B(mp, dqp->q_blk.softlimit); - dst->d_ino_hardlimit = be64_to_cpu(dqp->q_core.d_ino_hardlimit); - dst->d_ino_softlimit = be64_to_cpu(dqp->q_core.d_ino_softlimit); + dst->d_ino_hardlimit = dqp->q_ino.hardlimit; + dst->d_ino_softlimit = dqp->q_ino.softlimit; dst->d_space = XFS_FSB_TO_B(mp, dqp->q_blk.reserved); dst->d_ino_count = dqp->q_ino.reserved; - dst->d_spc_timer = be32_to_cpu(dqp->q_core.d_btimer); - dst->d_ino_timer = be32_to_cpu(dqp->q_core.d_itimer); + dst->d_spc_timer = dqp->q_blk.timer; + dst->d_ino_timer = dqp->q_ino.timer; dst->d_ino_warns = dqp->q_ino.warnings; dst->d_spc_warns = dqp->q_blk.warnings; dst->d_rt_spc_hardlimit = XFS_FSB_TO_B(mp, dqp->q_rtb.hardlimit); dst->d_rt_spc_softlimit = XFS_FSB_TO_B(mp, dqp->q_rtb.softlimit); dst->d_rt_space = XFS_FSB_TO_B(mp, dqp->q_rtb.reserved); - dst->d_rt_spc_timer = be32_to_cpu(dqp->q_core.d_rtbtimer); + dst->d_rt_spc_timer = dqp->q_rtb.timer; dst->d_rt_spc_warns = dqp->q_rtb.warnings; /* diff --git a/fs/xfs/xfs_trans_dquot.c b/fs/xfs/xfs_trans_dquot.c index 58cbc8f216f19..817c3d48b911b 100644 --- a/fs/xfs/xfs_trans_dquot.c +++ b/fs/xfs/xfs_trans_dquot.c @@ -591,7 +591,7 @@ xfs_trans_dqresv( softlimit = dqp->q_blk.softlimit; if (!softlimit) softlimit = defq->bsoftlimit; - timer = be32_to_cpu(dqp->q_core.d_btimer); + timer = dqp->q_blk.timer; warns = dqp->q_blk.warnings; warnlimit = defq->bwarnlimit; resbcountp = &dqp->q_blk.reserved; @@ -603,7 +603,7 @@ xfs_trans_dqresv( softlimit = dqp->q_rtb.softlimit; if (!softlimit) softlimit = defq->rtbsoftlimit; - timer = be32_to_cpu(dqp->q_core.d_rtbtimer); + timer = dqp->q_rtb.timer; warns = dqp->q_rtb.warnings; warnlimit = defq->rtbwarnlimit; resbcountp = &dqp->q_rtb.reserved; @@ -638,7 +638,7 @@ xfs_trans_dqresv( } if (ninos > 0) { total_count = dqp->q_ino.reserved + ninos; - timer = be32_to_cpu(dqp->q_core.d_itimer); + timer = dqp->q_ino.timer; warns = dqp->q_ino.warnings; warnlimit = defq->iwarnlimit; hardlimit = dqp->q_ino.hardlimit; -- GitLab From 51dbb1be52fedfe7f612854bfcba3400043a7a75 Mon Sep 17 00:00:00 2001 From: "Darrick J. Wong" Date: Tue, 14 Jul 2020 10:37:32 -0700 Subject: [PATCH 1008/1476] xfs: remove qcore from incore dquots Now that we've stopped using qcore entirely, drop it from the incore dquot. Signed-off-by: Darrick J. Wong Reviewed-by: Chandan Babu R Reviewed-by: Christoph Hellwig --- fs/xfs/libxfs/xfs_format.h | 7 +++---- fs/xfs/scrub/quota.c | 8 -------- fs/xfs/xfs_dquot.c | 36 +++++++++++++----------------------- fs/xfs/xfs_dquot.h | 1 - fs/xfs/xfs_qm.c | 4 ++-- fs/xfs/xfs_qm_syscalls.c | 9 +++------ 6 files changed, 21 insertions(+), 44 deletions(-) diff --git a/fs/xfs/libxfs/xfs_format.h b/fs/xfs/libxfs/xfs_format.h index 4f665646ba7da..a534ebee92b9e 100644 --- a/fs/xfs/libxfs/xfs_format.h +++ b/fs/xfs/libxfs/xfs_format.h @@ -1150,10 +1150,9 @@ static inline void xfs_dinode_put_rdev(struct xfs_dinode *dip, xfs_dev_t rdev) #define XFS_DQUOT_VERSION (uint8_t)0x01 /* latest version number */ /* - * This is the main portion of the on-disk representation of quota - * information for a user. This is the q_core of the struct xfs_dquot that - * is kept in kernel memory. We pad this with some more expansion room - * to construct the on disk structure. + * This is the main portion of the on-disk representation of quota information + * for a user. We pad this with some more expansion room to construct the on + * disk structure. */ struct xfs_disk_dquot { __be16 d_magic; /* dquot magic = XFS_DQUOT_MAGIC */ diff --git a/fs/xfs/scrub/quota.c b/fs/xfs/scrub/quota.c index 20bc763e88b48..f4aad5b001883 100644 --- a/fs/xfs/scrub/quota.c +++ b/fs/xfs/scrub/quota.c @@ -79,7 +79,6 @@ xchk_quota_item( struct xchk_quota_info *sqi = priv; struct xfs_scrub *sc = sqi->sc; struct xfs_mount *mp = sc->mp; - struct xfs_disk_dquot *d = &dq->q_core; struct xfs_quotainfo *qi = mp->m_quotainfo; xfs_fileoff_t offset; xfs_ino_t fs_icount; @@ -98,13 +97,6 @@ xchk_quota_item( sqi->last_id = dq->q_id; - /* Did we get the dquot type we wanted? */ - if (dqtype != (d->d_flags & XFS_DQ_ALLTYPES)) - xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, offset); - - if (d->d_pad0 != cpu_to_be32(0) || d->d_pad != cpu_to_be16(0)) - xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, offset); - /* * Warn if the hard limits are larger than the fs. * Administrators can do this, though in production this seems diff --git a/fs/xfs/xfs_dquot.c b/fs/xfs/xfs_dquot.c index b094fc495407f..6ed3cdeb67f01 100644 --- a/fs/xfs/xfs_dquot.c +++ b/fs/xfs/xfs_dquot.c @@ -529,7 +529,6 @@ xfs_dquot_from_disk( } /* copy everything from disk dquot to the incore dquot */ - memcpy(&dqp->q_core, ddqp, sizeof(struct xfs_disk_dquot)); dqp->q_blk.hardlimit = be64_to_cpu(ddqp->d_blk_hardlimit); dqp->q_blk.softlimit = be64_to_cpu(ddqp->d_blk_softlimit); dqp->q_ino.hardlimit = be64_to_cpu(ddqp->d_ino_hardlimit); @@ -568,7 +567,13 @@ xfs_dquot_to_disk( struct xfs_disk_dquot *ddqp, struct xfs_dquot *dqp) { - memcpy(ddqp, &dqp->q_core, sizeof(struct xfs_disk_dquot)); + ddqp->d_magic = cpu_to_be16(XFS_DQUOT_MAGIC); + ddqp->d_version = XFS_DQUOT_VERSION; + ddqp->d_flags = dqp->dq_flags & XFS_DQ_ALLTYPES; + ddqp->d_id = cpu_to_be32(dqp->q_id); + ddqp->d_pad0 = 0; + ddqp->d_pad = 0; + ddqp->d_blk_hardlimit = cpu_to_be64(dqp->q_blk.hardlimit); ddqp->d_blk_softlimit = cpu_to_be64(dqp->q_blk.softlimit); ddqp->d_ino_hardlimit = cpu_to_be64(dqp->q_ino.hardlimit); @@ -1189,8 +1194,7 @@ xfs_qm_dqflush( struct xfs_mount *mp = dqp->q_mount; struct xfs_log_item *lip = &dqp->q_logitem.qli_item; struct xfs_buf *bp; - struct xfs_dqblk *dqb; - struct xfs_disk_dquot *ddqp; + struct xfs_dqblk *dqblk; xfs_failaddr_t fa; int error; @@ -1214,22 +1218,6 @@ xfs_qm_dqflush( if (error) goto out_abort; - /* - * Calculate the location of the dquot inside the buffer. - */ - dqb = bp->b_addr + dqp->q_bufoffset; - ddqp = &dqb->dd_diskdq; - - /* sanity check the in-core structure before we flush */ - fa = xfs_dquot_verify(mp, &dqp->q_core, dqp->q_id, 0); - if (fa) { - xfs_alert(mp, "corrupt dquot ID 0x%x in memory at %pS", - dqp->q_id, fa); - xfs_buf_relse(bp); - error = -EFSCORRUPTED; - goto out_abort; - } - fa = xfs_qm_dqflush_check(dqp); if (fa) { xfs_alert(mp, "corrupt dquot ID 0x%x in memory at %pS", @@ -1239,7 +1227,9 @@ xfs_qm_dqflush( goto out_abort; } - xfs_dquot_to_disk(ddqp, dqp); + /* Flush the incore dquot to the ondisk buffer. */ + dqblk = bp->b_addr + dqp->q_bufoffset; + xfs_dquot_to_disk(&dqblk->dd_diskdq, dqp); /* * Clear the dirty field and remember the flush lsn for later use. @@ -1259,8 +1249,8 @@ xfs_qm_dqflush( * of a dquot without an up-to-date CRC getting to disk. */ if (xfs_sb_version_hascrc(&mp->m_sb)) { - dqb->dd_lsn = cpu_to_be64(dqp->q_logitem.qli_item.li_lsn); - xfs_update_cksum((char *)dqb, sizeof(struct xfs_dqblk), + dqblk->dd_lsn = cpu_to_be64(dqp->q_logitem.qli_item.li_lsn); + xfs_update_cksum((char *)dqblk, sizeof(struct xfs_dqblk), XFS_DQUOT_CRC_OFF); } diff --git a/fs/xfs/xfs_dquot.h b/fs/xfs/xfs_dquot.h index 69dbf3d95bf36..48fb0f263981b 100644 --- a/fs/xfs/xfs_dquot.h +++ b/fs/xfs/xfs_dquot.h @@ -72,7 +72,6 @@ struct xfs_dquot { struct xfs_dquot_res q_ino; /* inodes */ struct xfs_dquot_res q_rtb; /* realtime blocks */ - struct xfs_disk_dquot q_core; struct xfs_dq_logitem q_logitem; xfs_qcnt_t q_prealloc_lo_wmark; diff --git a/fs/xfs/xfs_qm.c b/fs/xfs/xfs_qm.c index 73fa0b5d37b38..1ef666314fa8e 100644 --- a/fs/xfs/xfs_qm.c +++ b/fs/xfs/xfs_qm.c @@ -161,7 +161,7 @@ xfs_qm_dqpurge( xfs_dqfunlock(dqp); xfs_dqunlock(dqp); - radix_tree_delete(xfs_dquot_tree(qi, dqp->q_core.d_flags), dqp->q_id); + radix_tree_delete(xfs_dquot_tree(qi, xfs_dquot_type(dqp)), dqp->q_id); qi->qi_dquots--; /* @@ -1588,7 +1588,7 @@ xfs_qm_dqfree_one( struct xfs_quotainfo *qi = mp->m_quotainfo; mutex_lock(&qi->qi_tree_lock); - radix_tree_delete(xfs_dquot_tree(qi, dqp->q_core.d_flags), dqp->q_id); + radix_tree_delete(xfs_dquot_tree(qi, xfs_dquot_type(dqp)), dqp->q_id); qi->qi_dquots--; mutex_unlock(&qi->qi_tree_lock); diff --git a/fs/xfs/xfs_qm_syscalls.c b/fs/xfs/xfs_qm_syscalls.c index 9e3507b083ceb..d05b16ea78c5f 100644 --- a/fs/xfs/xfs_qm_syscalls.c +++ b/fs/xfs/xfs_qm_syscalls.c @@ -638,12 +638,9 @@ xfs_qm_scall_getquota_fill_qc( * gets turned off. No need to confuse the user level code, * so return zeroes in that case. */ - if ((!XFS_IS_UQUOTA_ENFORCED(mp) && - dqp->q_core.d_flags == XFS_DQ_USER) || - (!XFS_IS_GQUOTA_ENFORCED(mp) && - dqp->q_core.d_flags == XFS_DQ_GROUP) || - (!XFS_IS_PQUOTA_ENFORCED(mp) && - dqp->q_core.d_flags == XFS_DQ_PROJ)) { + if ((!XFS_IS_UQUOTA_ENFORCED(mp) && type == XFS_DQ_USER) || + (!XFS_IS_GQUOTA_ENFORCED(mp) && type == XFS_DQ_GROUP) || + (!XFS_IS_PQUOTA_ENFORCED(mp) && type == XFS_DQ_PROJ)) { dst->d_spc_timer = 0; dst->d_ino_timer = 0; dst->d_rt_spc_timer = 0; -- GitLab From 438769e31e4410a20ea0df0ed1769516ae80740d Mon Sep 17 00:00:00 2001 From: "Darrick J. Wong" Date: Tue, 14 Jul 2020 10:37:32 -0700 Subject: [PATCH 1009/1476] xfs: refactor default quota limits by resource Now that we've split up the dquot resource fields into separate structs, do the same for the default limits to enable further refactoring. Signed-off-by: Darrick J. Wong Reviewed-by: Chandan Babu R Reviewed-by: Christoph Hellwig --- fs/xfs/xfs_dquot.c | 30 +++++++++++++++--------------- fs/xfs/xfs_qm.c | 36 ++++++++++++++++++------------------ fs/xfs/xfs_qm.h | 22 ++++++++++------------ fs/xfs/xfs_qm_syscalls.c | 24 ++++++++++++------------ fs/xfs/xfs_quotaops.c | 12 ++++++------ fs/xfs/xfs_trans_dquot.c | 18 +++++++++--------- 6 files changed, 70 insertions(+), 72 deletions(-) diff --git a/fs/xfs/xfs_dquot.c b/fs/xfs/xfs_dquot.c index 6ed3cdeb67f01..1827a1043faa1 100644 --- a/fs/xfs/xfs_dquot.c +++ b/fs/xfs/xfs_dquot.c @@ -77,22 +77,22 @@ xfs_qm_adjust_dqlimits( ASSERT(dq->q_id); defq = xfs_get_defquota(q, xfs_dquot_type(dq)); - if (defq->bsoftlimit && !dq->q_blk.softlimit) { - dq->q_blk.softlimit = defq->bsoftlimit; + if (defq->blk.soft && !dq->q_blk.softlimit) { + dq->q_blk.softlimit = defq->blk.soft; prealloc = 1; } - if (defq->bhardlimit && !dq->q_blk.hardlimit) { - dq->q_blk.hardlimit = defq->bhardlimit; + if (defq->blk.hard && !dq->q_blk.hardlimit) { + dq->q_blk.hardlimit = defq->blk.hard; prealloc = 1; } - if (defq->isoftlimit && !dq->q_ino.softlimit) - dq->q_ino.softlimit = defq->isoftlimit; - if (defq->ihardlimit && !dq->q_ino.hardlimit) - dq->q_ino.hardlimit = defq->ihardlimit; - if (defq->rtbsoftlimit && !dq->q_rtb.softlimit) - dq->q_rtb.softlimit = defq->rtbsoftlimit; - if (defq->rtbhardlimit && !dq->q_rtb.hardlimit) - dq->q_rtb.hardlimit = defq->rtbhardlimit; + if (defq->ino.soft && !dq->q_ino.softlimit) + dq->q_ino.softlimit = defq->ino.soft; + if (defq->ino.hard && !dq->q_ino.hardlimit) + dq->q_ino.hardlimit = defq->ino.hard; + if (defq->rtb.soft && !dq->q_rtb.softlimit) + dq->q_rtb.softlimit = defq->rtb.soft; + if (defq->rtb.hard && !dq->q_rtb.hardlimit) + dq->q_rtb.hardlimit = defq->rtb.hard; if (prealloc) xfs_dquot_set_prealloc_limits(dq); @@ -137,7 +137,7 @@ xfs_qm_adjust_dqtimers( (dq->q_blk.hardlimit && (dq->q_blk.count > dq->q_blk.hardlimit))) { dq->q_blk.timer = ktime_get_real_seconds() + - defq->btimelimit; + defq->blk.time; } else { dq->q_blk.warnings = 0; } @@ -156,7 +156,7 @@ xfs_qm_adjust_dqtimers( (dq->q_ino.hardlimit && (dq->q_ino.count > dq->q_ino.hardlimit))) { dq->q_ino.timer = ktime_get_real_seconds() + - defq->itimelimit; + defq->ino.time; } else { dq->q_ino.warnings = 0; } @@ -175,7 +175,7 @@ xfs_qm_adjust_dqtimers( (dq->q_rtb.hardlimit && (dq->q_rtb.count > dq->q_rtb.hardlimit))) { dq->q_rtb.timer = ktime_get_real_seconds() + - defq->rtbtimelimit; + defq->rtb.time; } else { dq->q_rtb.warnings = 0; } diff --git a/fs/xfs/xfs_qm.c b/fs/xfs/xfs_qm.c index 1ef666314fa8e..8ab4dd4f842d0 100644 --- a/fs/xfs/xfs_qm.c +++ b/fs/xfs/xfs_qm.c @@ -562,12 +562,12 @@ xfs_qm_set_defquota( * Timers and warnings have been already set, let's just set the * default limits for this quota type */ - defq->bhardlimit = dqp->q_blk.hardlimit; - defq->bsoftlimit = dqp->q_blk.softlimit; - defq->ihardlimit = dqp->q_ino.hardlimit; - defq->isoftlimit = dqp->q_ino.softlimit; - defq->rtbhardlimit = dqp->q_rtb.hardlimit; - defq->rtbsoftlimit = dqp->q_rtb.softlimit; + defq->blk.hard = dqp->q_blk.hardlimit; + defq->blk.soft = dqp->q_blk.softlimit; + defq->ino.hard = dqp->q_ino.hardlimit; + defq->ino.soft = dqp->q_ino.softlimit; + defq->rtb.hard = dqp->q_rtb.hardlimit; + defq->rtb.soft = dqp->q_rtb.softlimit; xfs_qm_dqdestroy(dqp); } @@ -584,12 +584,12 @@ xfs_qm_init_timelimits( defq = xfs_get_defquota(qinf, type); - defq->btimelimit = XFS_QM_BTIMELIMIT; - defq->itimelimit = XFS_QM_ITIMELIMIT; - defq->rtbtimelimit = XFS_QM_RTBTIMELIMIT; - defq->bwarnlimit = XFS_QM_BWARNLIMIT; - defq->iwarnlimit = XFS_QM_IWARNLIMIT; - defq->rtbwarnlimit = XFS_QM_RTBWARNLIMIT; + defq->blk.time = XFS_QM_BTIMELIMIT; + defq->ino.time = XFS_QM_ITIMELIMIT; + defq->rtb.time = XFS_QM_RTBTIMELIMIT; + defq->blk.warn = XFS_QM_BWARNLIMIT; + defq->ino.warn = XFS_QM_IWARNLIMIT; + defq->rtb.warn = XFS_QM_RTBWARNLIMIT; /* * We try to get the limits from the superuser's limits fields. @@ -608,17 +608,17 @@ xfs_qm_init_timelimits( * more writing. If it is zero, a default is used. */ if (dqp->q_blk.timer) - defq->btimelimit = dqp->q_blk.timer; + defq->blk.time = dqp->q_blk.timer; if (dqp->q_ino.timer) - defq->itimelimit = dqp->q_ino.timer; + defq->ino.time = dqp->q_ino.timer; if (dqp->q_rtb.timer) - defq->rtbtimelimit = dqp->q_rtb.timer; + defq->rtb.time = dqp->q_rtb.timer; if (dqp->q_blk.warnings) - defq->bwarnlimit = dqp->q_blk.warnings; + defq->blk.warn = dqp->q_blk.warnings; if (dqp->q_ino.warnings) - defq->iwarnlimit = dqp->q_ino.warnings; + defq->ino.warn = dqp->q_ino.warnings; if (dqp->q_rtb.warnings) - defq->rtbwarnlimit = dqp->q_rtb.warnings; + defq->rtb.warn = dqp->q_rtb.warnings; xfs_qm_dqdestroy(dqp); } diff --git a/fs/xfs/xfs_qm.h b/fs/xfs/xfs_qm.h index 57bddadbc051d..11c28ff0298c2 100644 --- a/fs/xfs/xfs_qm.h +++ b/fs/xfs/xfs_qm.h @@ -30,20 +30,18 @@ extern struct kmem_zone *xfs_qm_dqtrxzone; !dqp->q_rtb.count && \ !dqp->q_ino.count) +struct xfs_quota_limits { + xfs_qcnt_t hard; /* default hard limit */ + xfs_qcnt_t soft; /* default soft limit */ + time64_t time; /* limit for timers */ + xfs_qwarncnt_t warn; /* limit for warnings */ +}; + /* Defaults for each quota type: time limits, warn limits, usage limits */ struct xfs_def_quota { - time64_t btimelimit; /* limit for blks timer */ - time64_t itimelimit; /* limit for inodes timer */ - time64_t rtbtimelimit; /* limit for rt blks timer */ - xfs_qwarncnt_t bwarnlimit; /* limit for blks warnings */ - xfs_qwarncnt_t iwarnlimit; /* limit for inodes warnings */ - xfs_qwarncnt_t rtbwarnlimit; /* limit for rt blks warnings */ - xfs_qcnt_t bhardlimit; /* default data blk hard limit */ - xfs_qcnt_t bsoftlimit; /* default data blk soft limit */ - xfs_qcnt_t ihardlimit; /* default inode count hard limit */ - xfs_qcnt_t isoftlimit; /* default inode count soft limit */ - xfs_qcnt_t rtbhardlimit; /* default realtime blk hard limit */ - xfs_qcnt_t rtbsoftlimit; /* default realtime blk soft limit */ + struct xfs_quota_limits blk; + struct xfs_quota_limits ino; + struct xfs_quota_limits rtb; }; /* diff --git a/fs/xfs/xfs_qm_syscalls.c b/fs/xfs/xfs_qm_syscalls.c index d05b16ea78c5f..51941c1b04927 100644 --- a/fs/xfs/xfs_qm_syscalls.c +++ b/fs/xfs/xfs_qm_syscalls.c @@ -502,8 +502,8 @@ xfs_qm_scall_setqlim( dqp->q_blk.softlimit = soft; xfs_dquot_set_prealloc_limits(dqp); if (id == 0) { - defq->bhardlimit = hard; - defq->bsoftlimit = soft; + defq->blk.hard = hard; + defq->blk.soft = soft; } } else { xfs_debug(mp, "blkhard %Ld < blksoft %Ld", hard, soft); @@ -518,8 +518,8 @@ xfs_qm_scall_setqlim( dqp->q_rtb.hardlimit = hard; dqp->q_rtb.softlimit = soft; if (id == 0) { - defq->rtbhardlimit = hard; - defq->rtbsoftlimit = soft; + defq->rtb.hard = hard; + defq->rtb.soft = soft; } } else { xfs_debug(mp, "rtbhard %Ld < rtbsoft %Ld", hard, soft); @@ -535,8 +535,8 @@ xfs_qm_scall_setqlim( dqp->q_ino.hardlimit = hard; dqp->q_ino.softlimit = soft; if (id == 0) { - defq->ihardlimit = hard; - defq->isoftlimit = soft; + defq->ino.hard = hard; + defq->ino.soft = soft; } } else { xfs_debug(mp, "ihard %Ld < isoft %Ld", hard, soft); @@ -554,11 +554,11 @@ xfs_qm_scall_setqlim( if (id == 0) { if (newlim->d_fieldmask & QC_SPC_WARNS) - defq->bwarnlimit = newlim->d_spc_warns; + defq->blk.warn = newlim->d_spc_warns; if (newlim->d_fieldmask & QC_INO_WARNS) - defq->iwarnlimit = newlim->d_ino_warns; + defq->ino.warn = newlim->d_ino_warns; if (newlim->d_fieldmask & QC_RT_SPC_WARNS) - defq->rtbwarnlimit = newlim->d_rt_spc_warns; + defq->rtb.warn = newlim->d_rt_spc_warns; } /* @@ -579,11 +579,11 @@ xfs_qm_scall_setqlim( if (id == 0) { if (newlim->d_fieldmask & QC_SPC_TIMER) - defq->btimelimit = newlim->d_spc_timer; + defq->blk.time = newlim->d_spc_timer; if (newlim->d_fieldmask & QC_INO_TIMER) - defq->itimelimit = newlim->d_ino_timer; + defq->ino.time = newlim->d_ino_timer; if (newlim->d_fieldmask & QC_RT_SPC_TIMER) - defq->rtbtimelimit = newlim->d_rt_spc_timer; + defq->rtb.time = newlim->d_rt_spc_timer; } if (id != 0) { diff --git a/fs/xfs/xfs_quotaops.c b/fs/xfs/xfs_quotaops.c index 0868e6ee22191..299695a068f37 100644 --- a/fs/xfs/xfs_quotaops.c +++ b/fs/xfs/xfs_quotaops.c @@ -37,12 +37,12 @@ xfs_qm_fill_state( tstate->flags |= QCI_SYSFILE; tstate->blocks = ip->i_d.di_nblocks; tstate->nextents = ip->i_df.if_nextents; - tstate->spc_timelimit = (u32)defq->btimelimit; - tstate->ino_timelimit = (u32)defq->itimelimit; - tstate->rt_spc_timelimit = (u32)defq->rtbtimelimit; - tstate->spc_warnlimit = defq->bwarnlimit; - tstate->ino_warnlimit = defq->iwarnlimit; - tstate->rt_spc_warnlimit = defq->rtbwarnlimit; + tstate->spc_timelimit = (u32)defq->blk.time; + tstate->ino_timelimit = (u32)defq->ino.time; + tstate->rt_spc_timelimit = (u32)defq->rtb.time; + tstate->spc_warnlimit = defq->blk.warn; + tstate->ino_warnlimit = defq->ino.warn; + tstate->rt_spc_warnlimit = defq->rtb.warn; if (tempqip) xfs_irele(ip); } diff --git a/fs/xfs/xfs_trans_dquot.c b/fs/xfs/xfs_trans_dquot.c index 817c3d48b911b..9adea8f6ba876 100644 --- a/fs/xfs/xfs_trans_dquot.c +++ b/fs/xfs/xfs_trans_dquot.c @@ -587,25 +587,25 @@ xfs_trans_dqresv( if (flags & XFS_TRANS_DQ_RES_BLKS) { hardlimit = dqp->q_blk.hardlimit; if (!hardlimit) - hardlimit = defq->bhardlimit; + hardlimit = defq->blk.hard; softlimit = dqp->q_blk.softlimit; if (!softlimit) - softlimit = defq->bsoftlimit; + softlimit = defq->blk.soft; timer = dqp->q_blk.timer; warns = dqp->q_blk.warnings; - warnlimit = defq->bwarnlimit; + warnlimit = defq->blk.warn; resbcountp = &dqp->q_blk.reserved; } else { ASSERT(flags & XFS_TRANS_DQ_RES_RTBLKS); hardlimit = dqp->q_rtb.hardlimit; if (!hardlimit) - hardlimit = defq->rtbhardlimit; + hardlimit = defq->rtb.hard; softlimit = dqp->q_rtb.softlimit; if (!softlimit) - softlimit = defq->rtbsoftlimit; + softlimit = defq->rtb.soft; timer = dqp->q_rtb.timer; warns = dqp->q_rtb.warnings; - warnlimit = defq->rtbwarnlimit; + warnlimit = defq->rtb.warn; resbcountp = &dqp->q_rtb.reserved; } @@ -640,13 +640,13 @@ xfs_trans_dqresv( total_count = dqp->q_ino.reserved + ninos; timer = dqp->q_ino.timer; warns = dqp->q_ino.warnings; - warnlimit = defq->iwarnlimit; + warnlimit = defq->ino.warn; hardlimit = dqp->q_ino.hardlimit; if (!hardlimit) - hardlimit = defq->ihardlimit; + hardlimit = defq->ino.hard; softlimit = dqp->q_ino.softlimit; if (!softlimit) - softlimit = defq->isoftlimit; + softlimit = defq->ino.soft; if (hardlimit && total_count > hardlimit) { xfs_quota_warn(mp, dqp, QUOTA_NL_IHARDWARN); -- GitLab From c8c753e19a76507a2b686d9c521d5266351c75d0 Mon Sep 17 00:00:00 2001 From: "Darrick J. Wong" Date: Tue, 14 Jul 2020 10:37:33 -0700 Subject: [PATCH 1010/1476] xfs: remove unnecessary arguments from quota adjust functions struct xfs_dquot already has a pointer to the xfs mount, so remove the redundant parameter from xfs_qm_adjust_dq*. Signed-off-by: Darrick J. Wong Reviewed-by: Chandan Babu R Reviewed-by: Christoph Hellwig --- fs/xfs/xfs_dquot.c | 4 ++-- fs/xfs/xfs_dquot.h | 9 ++++----- fs/xfs/xfs_qm.c | 4 ++-- fs/xfs/xfs_qm_syscalls.c | 2 +- fs/xfs/xfs_trans_dquot.c | 4 ++-- 5 files changed, 11 insertions(+), 12 deletions(-) diff --git a/fs/xfs/xfs_dquot.c b/fs/xfs/xfs_dquot.c index 1827a1043faa1..c09d9257fad92 100644 --- a/fs/xfs/xfs_dquot.c +++ b/fs/xfs/xfs_dquot.c @@ -67,9 +67,9 @@ xfs_qm_dqdestroy( */ void xfs_qm_adjust_dqlimits( - struct xfs_mount *mp, struct xfs_dquot *dq) { + struct xfs_mount *mp = dq->q_mount; struct xfs_quotainfo *q = mp->m_quotainfo; struct xfs_def_quota *defq; int prealloc = 0; @@ -113,9 +113,9 @@ xfs_qm_adjust_dqlimits( */ void xfs_qm_adjust_dqtimers( - struct xfs_mount *mp, struct xfs_dquot *dq) { + struct xfs_mount *mp = dq->q_mount; struct xfs_quotainfo *qi = mp->m_quotainfo; struct xfs_def_quota *defq; diff --git a/fs/xfs/xfs_dquot.h b/fs/xfs/xfs_dquot.h index 48fb0f263981b..9e44da522684e 100644 --- a/fs/xfs/xfs_dquot.h +++ b/fs/xfs/xfs_dquot.h @@ -182,11 +182,10 @@ void xfs_dquot_to_disk(struct xfs_disk_dquot *ddqp, struct xfs_dquot *dqp); void xfs_qm_dqdestroy(struct xfs_dquot *dqp); int xfs_qm_dqflush(struct xfs_dquot *dqp, struct xfs_buf **bpp); void xfs_qm_dqunpin_wait(struct xfs_dquot *dqp); -void xfs_qm_adjust_dqtimers(struct xfs_mount *mp, - struct xfs_dquot *d); -void xfs_qm_adjust_dqlimits(struct xfs_mount *mp, - struct xfs_dquot *d); -xfs_dqid_t xfs_qm_id_for_quotatype(struct xfs_inode *ip, uint type); +void xfs_qm_adjust_dqtimers(struct xfs_dquot *d); +void xfs_qm_adjust_dqlimits(struct xfs_dquot *d); +xfs_dqid_t xfs_qm_id_for_quotatype(struct xfs_inode *ip, + uint type); int xfs_qm_dqget(struct xfs_mount *mp, xfs_dqid_t id, uint type, bool can_alloc, struct xfs_dquot **dqpp); diff --git a/fs/xfs/xfs_qm.c b/fs/xfs/xfs_qm.c index 8ab4dd4f842d0..7d83c1623cb2f 100644 --- a/fs/xfs/xfs_qm.c +++ b/fs/xfs/xfs_qm.c @@ -1103,8 +1103,8 @@ xfs_qm_quotacheck_dqadjust( * There are no timers for the default values set in the root dquot. */ if (dqp->q_id) { - xfs_qm_adjust_dqlimits(mp, dqp); - xfs_qm_adjust_dqtimers(mp, dqp); + xfs_qm_adjust_dqlimits(dqp); + xfs_qm_adjust_dqtimers(dqp); } dqp->q_flags |= XFS_DQFLAG_DIRTY; diff --git a/fs/xfs/xfs_qm_syscalls.c b/fs/xfs/xfs_qm_syscalls.c index 51941c1b04927..aeea59d6cf236 100644 --- a/fs/xfs/xfs_qm_syscalls.c +++ b/fs/xfs/xfs_qm_syscalls.c @@ -594,7 +594,7 @@ xfs_qm_scall_setqlim( * is on or off. We don't really want to bother with iterating * over all ondisk dquots and turning the timers on/off. */ - xfs_qm_adjust_dqtimers(mp, dqp); + xfs_qm_adjust_dqtimers(dqp); } dqp->q_flags |= XFS_DQFLAG_DIRTY; xfs_trans_log_dquot(tp, dqp); diff --git a/fs/xfs/xfs_trans_dquot.c b/fs/xfs/xfs_trans_dquot.c index 9adea8f6ba876..6f0bfd5221161 100644 --- a/fs/xfs/xfs_trans_dquot.c +++ b/fs/xfs/xfs_trans_dquot.c @@ -382,8 +382,8 @@ xfs_trans_apply_dquot_deltas( * Start/reset the timer(s) if needed. */ if (dqp->q_id) { - xfs_qm_adjust_dqlimits(tp->t_mountp, dqp); - xfs_qm_adjust_dqtimers(tp->t_mountp, dqp); + xfs_qm_adjust_dqlimits(dqp); + xfs_qm_adjust_dqtimers(dqp); } dqp->q_flags |= XFS_DQFLAG_DIRTY; -- GitLab From ea0cc6fa8f89a0089c561b65b909ceab48463338 Mon Sep 17 00:00:00 2001 From: "Darrick J. Wong" Date: Tue, 14 Jul 2020 10:37:33 -0700 Subject: [PATCH 1011/1476] xfs: refactor quota exceeded test Refactor the open-coded test for whether or not we're over quota. Signed-off-by: Darrick J. Wong Reviewed-by: Chandan Babu R Reviewed-by: Christoph Hellwig --- fs/xfs/xfs_dquot.c | 91 +++++++++++++--------------------------------- 1 file changed, 26 insertions(+), 65 deletions(-) diff --git a/fs/xfs/xfs_dquot.c b/fs/xfs/xfs_dquot.c index c09d9257fad92..98cb31f28aaf0 100644 --- a/fs/xfs/xfs_dquot.c +++ b/fs/xfs/xfs_dquot.c @@ -98,6 +98,29 @@ xfs_qm_adjust_dqlimits( xfs_dquot_set_prealloc_limits(dq); } +/* + * Determine if this quota counter is over either limit and set the quota + * timers as appropriate. + */ +static inline void +xfs_qm_adjust_res_timer( + struct xfs_dquot_res *res, + struct xfs_quota_limits *qlim) +{ + ASSERT(res->hardlimit == 0 || res->softlimit <= res->hardlimit); + + if ((res->softlimit && res->count > res->softlimit) || + (res->hardlimit && res->count > res->hardlimit)) { + if (res->timer == 0) + res->timer = ktime_get_real_seconds() + qlim->time; + } else { + if (res->timer == 0) + res->warnings = 0; + else + res->timer = 0; + } +} + /* * Check the limits and timers of a dquot and start or reset timers * if necessary. @@ -122,71 +145,9 @@ xfs_qm_adjust_dqtimers( ASSERT(dq->q_id); defq = xfs_get_defquota(qi, xfs_dquot_type(dq)); -#ifdef DEBUG - if (dq->q_blk.hardlimit) - ASSERT(dq->q_blk.softlimit <= dq->q_blk.hardlimit); - if (dq->q_ino.hardlimit) - ASSERT(dq->q_ino.softlimit <= dq->q_ino.hardlimit); - if (dq->q_rtb.hardlimit) - ASSERT(dq->q_rtb.softlimit <= dq->q_rtb.hardlimit); -#endif - - if (!dq->q_blk.timer) { - if ((dq->q_blk.softlimit && - (dq->q_blk.count > dq->q_blk.softlimit)) || - (dq->q_blk.hardlimit && - (dq->q_blk.count > dq->q_blk.hardlimit))) { - dq->q_blk.timer = ktime_get_real_seconds() + - defq->blk.time; - } else { - dq->q_blk.warnings = 0; - } - } else { - if ((!dq->q_blk.softlimit || - (dq->q_blk.count <= dq->q_blk.softlimit)) && - (!dq->q_blk.hardlimit || - (dq->q_blk.count <= dq->q_blk.hardlimit))) { - dq->q_blk.timer = 0; - } - } - - if (!dq->q_ino.timer) { - if ((dq->q_ino.softlimit && - (dq->q_ino.count > dq->q_ino.softlimit)) || - (dq->q_ino.hardlimit && - (dq->q_ino.count > dq->q_ino.hardlimit))) { - dq->q_ino.timer = ktime_get_real_seconds() + - defq->ino.time; - } else { - dq->q_ino.warnings = 0; - } - } else { - if ((!dq->q_ino.softlimit || - (dq->q_ino.count <= dq->q_ino.softlimit)) && - (!dq->q_ino.hardlimit || - (dq->q_ino.count <= dq->q_ino.hardlimit))) { - dq->q_ino.timer = 0; - } - } - - if (!dq->q_rtb.timer) { - if ((dq->q_rtb.softlimit && - (dq->q_rtb.count > dq->q_rtb.softlimit)) || - (dq->q_rtb.hardlimit && - (dq->q_rtb.count > dq->q_rtb.hardlimit))) { - dq->q_rtb.timer = ktime_get_real_seconds() + - defq->rtb.time; - } else { - dq->q_rtb.warnings = 0; - } - } else { - if ((!dq->q_rtb.softlimit || - (dq->q_rtb.count <= dq->q_rtb.softlimit)) && - (!dq->q_rtb.hardlimit || - (dq->q_rtb.count <= dq->q_rtb.hardlimit))) { - dq->q_rtb.timer = 0; - } - } + xfs_qm_adjust_res_timer(&dq->q_blk, &defq->blk); + xfs_qm_adjust_res_timer(&dq->q_ino, &defq->ino); + xfs_qm_adjust_res_timer(&dq->q_rtb, &defq->rtb); } /* -- GitLab From d1520deab039ca1bac50c53b941a3df3b3a680de Mon Sep 17 00:00:00 2001 From: "Darrick J. Wong" Date: Tue, 14 Jul 2020 10:37:33 -0700 Subject: [PATCH 1012/1476] xfs: refactor xfs_qm_scall_setqlim Now that we can pass around quota resource and limit structures, clean up the open-coded field setting in xfs_qm_scall_setqlim. Signed-off-by: Darrick J. Wong Reviewed-by: Christoph Hellwig Reviewed-by: Allison Collins Reviewed-by: Chandan Babu R --- fs/xfs/xfs_qm_syscalls.c | 164 ++++++++++++++++++++++----------------- 1 file changed, 93 insertions(+), 71 deletions(-) diff --git a/fs/xfs/xfs_qm_syscalls.c b/fs/xfs/xfs_qm_syscalls.c index aeea59d6cf236..cbe352187d328 100644 --- a/fs/xfs/xfs_qm_syscalls.c +++ b/fs/xfs/xfs_qm_syscalls.c @@ -436,6 +436,58 @@ xfs_qm_scall_quotaon( #define XFS_QC_MASK \ (QC_LIMIT_MASK | QC_TIMER_MASK | QC_WARNS_MASK) +/* + * Adjust limits of this quota, and the defaults if passed in. Returns true + * if the new limits made sense and were applied, false otherwise. + */ +static inline bool +xfs_setqlim_limits( + struct xfs_mount *mp, + struct xfs_dquot_res *res, + struct xfs_quota_limits *qlim, + xfs_qcnt_t hard, + xfs_qcnt_t soft, + const char *tag) +{ + /* The hard limit can't be less than the soft limit. */ + if (hard != 0 && hard < soft) { + xfs_debug(mp, "%shard %lld < %ssoft %lld", tag, hard, tag, + soft); + return false; + } + + res->hardlimit = hard; + res->softlimit = soft; + if (qlim) { + qlim->hard = hard; + qlim->soft = soft; + } + + return true; +} + +static inline void +xfs_setqlim_warns( + struct xfs_dquot_res *res, + struct xfs_quota_limits *qlim, + int warns) +{ + res->warnings = warns; + if (qlim) + qlim->warn = warns; +} + +static inline void +xfs_setqlim_timer( + struct xfs_dquot_res *res, + struct xfs_quota_limits *qlim, + s64 timer) +{ + res->timer = timer; + if (qlim) + qlim->time = timer; +} + /* * Adjust quota limits, and start/stop timers accordingly. */ @@ -450,6 +502,8 @@ xfs_qm_scall_setqlim( struct xfs_dquot *dqp; struct xfs_trans *tp; struct xfs_def_quota *defq; + struct xfs_dquot_res *res; + struct xfs_quota_limits *qlim; int error; xfs_qcnt_t hard, soft; @@ -489,102 +543,70 @@ xfs_qm_scall_setqlim( xfs_trans_dqjoin(tp, dqp); /* + * Update quota limits, warnings, and timers, and the defaults + * if we're touching id == 0. + * * Make sure that hardlimits are >= soft limits before changing. + * + * Update warnings counter(s) if requested. + * + * Timelimits for the super user set the relative time the other users + * can be over quota for this file system. If it is zero a default is + * used. Ditto for the default soft and hard limit values (already + * done, above), and for warnings. + * + * For other IDs, userspace can bump out the grace period if over + * the soft limit. */ + + /* Blocks on the data device. */ hard = (newlim->d_fieldmask & QC_SPC_HARD) ? (xfs_qcnt_t) XFS_B_TO_FSB(mp, newlim->d_spc_hardlimit) : dqp->q_blk.hardlimit; soft = (newlim->d_fieldmask & QC_SPC_SOFT) ? (xfs_qcnt_t) XFS_B_TO_FSB(mp, newlim->d_spc_softlimit) : dqp->q_blk.softlimit; - if (hard == 0 || hard >= soft) { - dqp->q_blk.hardlimit = hard; - dqp->q_blk.softlimit = soft; + res = &dqp->q_blk; + qlim = id == 0 ? &defq->blk : NULL; + + if (xfs_setqlim_limits(mp, res, qlim, hard, soft, "blk")) xfs_dquot_set_prealloc_limits(dqp); - if (id == 0) { - defq->blk.hard = hard; - defq->blk.soft = soft; - } - } else { - xfs_debug(mp, "blkhard %Ld < blksoft %Ld", hard, soft); - } + if (newlim->d_fieldmask & QC_SPC_WARNS) + xfs_setqlim_warns(res, qlim, newlim->d_spc_warns); + if (newlim->d_fieldmask & QC_SPC_TIMER) + xfs_setqlim_timer(res, qlim, newlim->d_spc_timer); + + /* Blocks on the realtime device. */ hard = (newlim->d_fieldmask & QC_RT_SPC_HARD) ? (xfs_qcnt_t) XFS_B_TO_FSB(mp, newlim->d_rt_spc_hardlimit) : dqp->q_rtb.hardlimit; soft = (newlim->d_fieldmask & QC_RT_SPC_SOFT) ? (xfs_qcnt_t) XFS_B_TO_FSB(mp, newlim->d_rt_spc_softlimit) : dqp->q_rtb.softlimit; - if (hard == 0 || hard >= soft) { - dqp->q_rtb.hardlimit = hard; - dqp->q_rtb.softlimit = soft; - if (id == 0) { - defq->rtb.hard = hard; - defq->rtb.soft = soft; - } - } else { - xfs_debug(mp, "rtbhard %Ld < rtbsoft %Ld", hard, soft); - } + res = &dqp->q_rtb; + qlim = id == 0 ? &defq->rtb : NULL; + xfs_setqlim_limits(mp, res, qlim, hard, soft, "rtb"); + if (newlim->d_fieldmask & QC_RT_SPC_WARNS) + xfs_setqlim_warns(res, qlim, newlim->d_rt_spc_warns); + if (newlim->d_fieldmask & QC_RT_SPC_TIMER) + xfs_setqlim_timer(res, qlim, newlim->d_rt_spc_timer); + + /* Inodes */ hard = (newlim->d_fieldmask & QC_INO_HARD) ? (xfs_qcnt_t) newlim->d_ino_hardlimit : dqp->q_ino.hardlimit; soft = (newlim->d_fieldmask & QC_INO_SOFT) ? (xfs_qcnt_t) newlim->d_ino_softlimit : dqp->q_ino.softlimit; - if (hard == 0 || hard >= soft) { - dqp->q_ino.hardlimit = hard; - dqp->q_ino.softlimit = soft; - if (id == 0) { - defq->ino.hard = hard; - defq->ino.soft = soft; - } - } else { - xfs_debug(mp, "ihard %Ld < isoft %Ld", hard, soft); - } + res = &dqp->q_ino; + qlim = id == 0 ? &defq->ino : NULL; - /* - * Update warnings counter(s) if requested - */ - if (newlim->d_fieldmask & QC_SPC_WARNS) - dqp->q_blk.warnings = newlim->d_spc_warns; + xfs_setqlim_limits(mp, res, qlim, hard, soft, "ino"); if (newlim->d_fieldmask & QC_INO_WARNS) - dqp->q_ino.warnings = newlim->d_ino_warns; - if (newlim->d_fieldmask & QC_RT_SPC_WARNS) - dqp->q_rtb.warnings = newlim->d_rt_spc_warns; - - if (id == 0) { - if (newlim->d_fieldmask & QC_SPC_WARNS) - defq->blk.warn = newlim->d_spc_warns; - if (newlim->d_fieldmask & QC_INO_WARNS) - defq->ino.warn = newlim->d_ino_warns; - if (newlim->d_fieldmask & QC_RT_SPC_WARNS) - defq->rtb.warn = newlim->d_rt_spc_warns; - } - - /* - * Timelimits for the super user set the relative time the other users - * can be over quota for this file system. If it is zero a default is - * used. Ditto for the default soft and hard limit values (already - * done, above), and for warnings. - * - * For other IDs, userspace can bump out the grace period if over - * the soft limit. - */ - if (newlim->d_fieldmask & QC_SPC_TIMER) - dqp->q_blk.timer = newlim->d_spc_timer; + xfs_setqlim_warns(res, qlim, newlim->d_ino_warns); if (newlim->d_fieldmask & QC_INO_TIMER) - dqp->q_ino.timer = newlim->d_ino_timer; - if (newlim->d_fieldmask & QC_RT_SPC_TIMER) - dqp->q_rtb.timer = newlim->d_rt_spc_timer; - - if (id == 0) { - if (newlim->d_fieldmask & QC_SPC_TIMER) - defq->blk.time = newlim->d_spc_timer; - if (newlim->d_fieldmask & QC_INO_TIMER) - defq->ino.time = newlim->d_ino_timer; - if (newlim->d_fieldmask & QC_RT_SPC_TIMER) - defq->rtb.time = newlim->d_rt_spc_timer; - } + xfs_setqlim_timer(res, qlim, newlim->d_ino_timer); if (id != 0) { /* -- GitLab From 292b47b4fcfad2b8b2bc749d68c8baa63fc8bf36 Mon Sep 17 00:00:00 2001 From: "Darrick J. Wong" Date: Tue, 14 Jul 2020 10:37:34 -0700 Subject: [PATCH 1013/1476] xfs: refactor xfs_trans_dqresv Now that we've refactored the resource usage and limits into per-resource structures, we can refactor some of the open-coded reservation limit checking in xfs_trans_dqresv. Signed-off-by: Darrick J. Wong Reviewed-by: Chandan Babu R Reviewed-by: Allison Collins Reviewed-by: Christoph Hellwig --- fs/xfs/xfs_trans_dquot.c | 155 ++++++++++++++++++++------------------- 1 file changed, 79 insertions(+), 76 deletions(-) diff --git a/fs/xfs/xfs_trans_dquot.c b/fs/xfs/xfs_trans_dquot.c index 6f0bfd5221161..9dc8dd9f852cc 100644 --- a/fs/xfs/xfs_trans_dquot.c +++ b/fs/xfs/xfs_trans_dquot.c @@ -554,6 +554,58 @@ xfs_quota_warn( mp->m_super->s_dev, type); } +/* + * Decide if we can make an additional reservation against a quota resource. + * Returns an inode QUOTA_NL_ warning code and whether or not it's fatal. + * + * Note that we assume that the numeric difference between the inode and block + * warning codes will always be 3 since it's userspace ABI now, and will never + * decrease the quota reservation, so the *BELOW messages are irrelevant. + */ +static inline int +xfs_dqresv_check( + struct xfs_dquot_res *res, + struct xfs_quota_limits *qlim, + int64_t delta, + bool *fatal) +{ + xfs_qcnt_t hardlimit = res->hardlimit; + xfs_qcnt_t softlimit = res->softlimit; + xfs_qcnt_t total_count = res->reserved + delta; + + BUILD_BUG_ON(QUOTA_NL_BHARDWARN != QUOTA_NL_IHARDWARN + 3); + BUILD_BUG_ON(QUOTA_NL_BSOFTLONGWARN != QUOTA_NL_ISOFTLONGWARN + 3); + BUILD_BUG_ON(QUOTA_NL_BSOFTWARN != QUOTA_NL_ISOFTWARN + 3); + + *fatal = false; + if (delta <= 0) + return QUOTA_NL_NOWARN; + + if (!hardlimit) + hardlimit = qlim->hard; + if (!softlimit) + softlimit = qlim->soft; + + if (hardlimit && total_count > hardlimit) { + *fatal = true; + return QUOTA_NL_IHARDWARN; + } + + if (softlimit && total_count > softlimit) { + time64_t now = ktime_get_real_seconds(); + + if ((res->timer != 0 && now > res->timer) || + (res->warnings != 0 && res->warnings >= qlim->warn)) { + *fatal = true; + return QUOTA_NL_ISOFTLONGWARN; + } + + return QUOTA_NL_ISOFTWARN; + } + + return QUOTA_NL_NOWARN; +} + /* * This reserves disk blocks and inodes against a dquot. * Flags indicate if the dquot is to be locked here and also @@ -569,99 +621,51 @@ xfs_trans_dqresv( long ninos, uint flags) { - xfs_qcnt_t hardlimit; - xfs_qcnt_t softlimit; - time64_t timer; - xfs_qwarncnt_t warns; - xfs_qwarncnt_t warnlimit; - xfs_qcnt_t total_count; - xfs_qcnt_t *resbcountp; struct xfs_quotainfo *q = mp->m_quotainfo; struct xfs_def_quota *defq; - + struct xfs_dquot_res *blkres; + struct xfs_quota_limits *qlim; xfs_dqlock(dqp); defq = xfs_get_defquota(q, xfs_dquot_type(dqp)); if (flags & XFS_TRANS_DQ_RES_BLKS) { - hardlimit = dqp->q_blk.hardlimit; - if (!hardlimit) - hardlimit = defq->blk.hard; - softlimit = dqp->q_blk.softlimit; - if (!softlimit) - softlimit = defq->blk.soft; - timer = dqp->q_blk.timer; - warns = dqp->q_blk.warnings; - warnlimit = defq->blk.warn; - resbcountp = &dqp->q_blk.reserved; + blkres = &dqp->q_blk; + qlim = &defq->blk; } else { - ASSERT(flags & XFS_TRANS_DQ_RES_RTBLKS); - hardlimit = dqp->q_rtb.hardlimit; - if (!hardlimit) - hardlimit = defq->rtb.hard; - softlimit = dqp->q_rtb.softlimit; - if (!softlimit) - softlimit = defq->rtb.soft; - timer = dqp->q_rtb.timer; - warns = dqp->q_rtb.warnings; - warnlimit = defq->rtb.warn; - resbcountp = &dqp->q_rtb.reserved; + blkres = &dqp->q_rtb; + qlim = &defq->rtb; } if ((flags & XFS_QMOPT_FORCE_RES) == 0 && dqp->q_id && ((XFS_IS_UQUOTA_ENFORCED(dqp->q_mount) && XFS_QM_ISUDQ(dqp)) || (XFS_IS_GQUOTA_ENFORCED(dqp->q_mount) && XFS_QM_ISGDQ(dqp)) || (XFS_IS_PQUOTA_ENFORCED(dqp->q_mount) && XFS_QM_ISPDQ(dqp)))) { - if (nblks > 0) { + int quota_nl; + bool fatal; + + /* + * dquot is locked already. See if we'd go over the hardlimit + * or exceed the timelimit if we'd reserve resources. + */ + quota_nl = xfs_dqresv_check(blkres, qlim, nblks, &fatal); + if (quota_nl != QUOTA_NL_NOWARN) { /* - * dquot is locked already. See if we'd go over the - * hardlimit or exceed the timelimit if we allocate - * nblks. + * Quota block warning codes are 3 more than the inode + * codes, which we check above. */ - total_count = *resbcountp + nblks; - if (hardlimit && total_count > hardlimit) { - xfs_quota_warn(mp, dqp, QUOTA_NL_BHARDWARN); + xfs_quota_warn(mp, dqp, quota_nl + 3); + if (fatal) goto error_return; - } - if (softlimit && total_count > softlimit) { - if ((timer != 0 && - ktime_get_real_seconds() > timer) || - (warns != 0 && warns >= warnlimit)) { - xfs_quota_warn(mp, dqp, - QUOTA_NL_BSOFTLONGWARN); - goto error_return; - } - - xfs_quota_warn(mp, dqp, QUOTA_NL_BSOFTWARN); - } } - if (ninos > 0) { - total_count = dqp->q_ino.reserved + ninos; - timer = dqp->q_ino.timer; - warns = dqp->q_ino.warnings; - warnlimit = defq->ino.warn; - hardlimit = dqp->q_ino.hardlimit; - if (!hardlimit) - hardlimit = defq->ino.hard; - softlimit = dqp->q_ino.softlimit; - if (!softlimit) - softlimit = defq->ino.soft; - - if (hardlimit && total_count > hardlimit) { - xfs_quota_warn(mp, dqp, QUOTA_NL_IHARDWARN); + + quota_nl = xfs_dqresv_check(&dqp->q_ino, &defq->ino, ninos, + &fatal); + if (quota_nl != QUOTA_NL_NOWARN) { + xfs_quota_warn(mp, dqp, quota_nl); + if (fatal) goto error_return; - } - if (softlimit && total_count > softlimit) { - if ((timer != 0 && - ktime_get_real_seconds() > timer) || - (warns != 0 && warns >= warnlimit)) { - xfs_quota_warn(mp, dqp, - QUOTA_NL_ISOFTLONGWARN); - goto error_return; - } - xfs_quota_warn(mp, dqp, QUOTA_NL_ISOFTWARN); - } } } @@ -669,9 +673,8 @@ xfs_trans_dqresv( * Change the reservation, but not the actual usage. * Note that q_blk.reserved = q_blk.count + resv */ - (*resbcountp) += (xfs_qcnt_t)nblks; - if (ninos != 0) - dqp->q_ino.reserved += (xfs_qcnt_t)ninos; + blkres->reserved += (xfs_qcnt_t)nblks; + dqp->q_ino.reserved += (xfs_qcnt_t)ninos; /* * note the reservation amt in the trans struct too, -- GitLab From d92c881538c40a18d0a86193898fc0b6e2598aa0 Mon Sep 17 00:00:00 2001 From: "Darrick J. Wong" Date: Tue, 14 Jul 2020 10:37:34 -0700 Subject: [PATCH 1014/1476] xfs: refactor xfs_trans_apply_dquot_deltas Hoist the code that adjusts the incore quota reservation count adjustments into a separate function, both to reduce the level of indentation and also to reduce the amount of open-coded logic. Signed-off-by: Darrick J. Wong Reviewed-by: Allison Collins Reviewed-by: Chandan Babu R Reviewed-by: Christoph Hellwig --- fs/xfs/xfs_trans_dquot.c | 105 ++++++++++++++++++--------------------- 1 file changed, 47 insertions(+), 58 deletions(-) diff --git a/fs/xfs/xfs_trans_dquot.c b/fs/xfs/xfs_trans_dquot.c index 9dc8dd9f852cc..510b9c3164d60 100644 --- a/fs/xfs/xfs_trans_dquot.c +++ b/fs/xfs/xfs_trans_dquot.c @@ -293,6 +293,37 @@ xfs_trans_dqlockedjoin( } } +/* Apply dqtrx changes to the quota reservation counters. */ +static inline void +xfs_apply_quota_reservation_deltas( + struct xfs_dquot_res *res, + uint64_t reserved, + int64_t res_used, + int64_t count_delta) +{ + if (reserved != 0) { + /* + * Subtle math here: If reserved > res_used (the normal case), + * we're simply subtracting the unused transaction quota + * reservation from the dquot reservation. + * + * If, however, res_used > reserved, then we have allocated + * more quota blocks than were reserved for the transaction. + * We must add that excess to the dquot reservation since it + * tracks (usage + resv) and by definition we didn't reserve + * that excess. + */ + res->reserved -= abs(reserved - res_used); + } else if (count_delta != 0) { + /* + * These blks were never reserved, either inside a transaction + * or outside one (in a delayed allocation). Also, this isn't + * always a negative number since we sometimes deliberately + * skip quota reservations. + */ + res->reserved += count_delta; + } +} /* * Called by xfs_trans_commit() and similar in spirit to @@ -327,6 +358,8 @@ xfs_trans_apply_dquot_deltas( xfs_trans_dqlockedjoin(tp, qa); for (i = 0; i < XFS_QM_TRANS_MAXDQS; i++) { + uint64_t blk_res_used; + qtrx = &qa[i]; /* * The array of dquots is filled @@ -396,71 +429,27 @@ xfs_trans_apply_dquot_deltas( * In case of delayed allocations, there's no * reservation that a transaction structure knows of. */ - if (qtrx->qt_blk_res != 0) { - uint64_t blk_res_used = 0; - - if (qtrx->qt_bcount_delta > 0) - blk_res_used = qtrx->qt_bcount_delta; - - if (qtrx->qt_blk_res != blk_res_used) { - if (qtrx->qt_blk_res > blk_res_used) - dqp->q_blk.reserved -= (xfs_qcnt_t) - (qtrx->qt_blk_res - - blk_res_used); - else - dqp->q_blk.reserved -= (xfs_qcnt_t) - (blk_res_used - - qtrx->qt_blk_res); - } - } else { - /* - * These blks were never reserved, either inside - * a transaction or outside one (in a delayed - * allocation). Also, this isn't always a - * negative number since we sometimes - * deliberately skip quota reservations. - */ - if (qtrx->qt_bcount_delta) { - dqp->q_blk.reserved += - (xfs_qcnt_t)qtrx->qt_bcount_delta; - } - } + blk_res_used = max_t(int64_t, 0, qtrx->qt_bcount_delta); + xfs_apply_quota_reservation_deltas(&dqp->q_blk, + qtrx->qt_blk_res, blk_res_used, + qtrx->qt_bcount_delta); + /* * Adjust the RT reservation. */ - if (qtrx->qt_rtblk_res != 0) { - if (qtrx->qt_rtblk_res != qtrx->qt_rtblk_res_used) { - if (qtrx->qt_rtblk_res > - qtrx->qt_rtblk_res_used) - dqp->q_rtb.reserved -= (xfs_qcnt_t) - (qtrx->qt_rtblk_res - - qtrx->qt_rtblk_res_used); - else - dqp->q_rtb.reserved -= (xfs_qcnt_t) - (qtrx->qt_rtblk_res_used - - qtrx->qt_rtblk_res); - } - } else { - if (qtrx->qt_rtbcount_delta) - dqp->q_rtb.reserved += - (xfs_qcnt_t)qtrx->qt_rtbcount_delta; - } + xfs_apply_quota_reservation_deltas(&dqp->q_rtb, + qtrx->qt_rtblk_res, + qtrx->qt_rtblk_res_used, + qtrx->qt_rtbcount_delta); /* * Adjust the inode reservation. */ - if (qtrx->qt_ino_res != 0) { - ASSERT(qtrx->qt_ino_res >= - qtrx->qt_ino_res_used); - if (qtrx->qt_ino_res > qtrx->qt_ino_res_used) - dqp->q_ino.reserved -= (xfs_qcnt_t) - (qtrx->qt_ino_res - - qtrx->qt_ino_res_used); - } else { - if (qtrx->qt_icount_delta) - dqp->q_ino.reserved += - (xfs_qcnt_t)qtrx->qt_icount_delta; - } + ASSERT(qtrx->qt_ino_res >= qtrx->qt_ino_res_used); + xfs_apply_quota_reservation_deltas(&dqp->q_ino, + qtrx->qt_ino_res, + qtrx->qt_ino_res_used, + qtrx->qt_icount_delta); ASSERT(dqp->q_blk.reserved >= dqp->q_blk.count); ASSERT(dqp->q_ino.reserved >= dqp->q_ino.count); -- GitLab From 12d720fb864547922ad4bf53120de72a91807999 Mon Sep 17 00:00:00 2001 From: "Darrick J. Wong" Date: Tue, 14 Jul 2020 10:37:34 -0700 Subject: [PATCH 1015/1476] xfs: assume the default quota limits are always set in xfs_qm_adjust_dqlimits We always initialize the default quota limits to something nowadays, so we don't need to check that the defaults are set to something before using them. Signed-off-by: Darrick J. Wong Reviewed-by: Christoph Hellwig Reviewed-by: Chandan Babu R --- fs/xfs/xfs_dquot.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/fs/xfs/xfs_dquot.c b/fs/xfs/xfs_dquot.c index 98cb31f28aaf0..8e84623cc3317 100644 --- a/fs/xfs/xfs_dquot.c +++ b/fs/xfs/xfs_dquot.c @@ -77,21 +77,21 @@ xfs_qm_adjust_dqlimits( ASSERT(dq->q_id); defq = xfs_get_defquota(q, xfs_dquot_type(dq)); - if (defq->blk.soft && !dq->q_blk.softlimit) { + if (!dq->q_blk.softlimit) { dq->q_blk.softlimit = defq->blk.soft; prealloc = 1; } - if (defq->blk.hard && !dq->q_blk.hardlimit) { + if (!dq->q_blk.hardlimit) { dq->q_blk.hardlimit = defq->blk.hard; prealloc = 1; } - if (defq->ino.soft && !dq->q_ino.softlimit) + if (!dq->q_ino.softlimit) dq->q_ino.softlimit = defq->ino.soft; - if (defq->ino.hard && !dq->q_ino.hardlimit) + if (!dq->q_ino.hardlimit) dq->q_ino.hardlimit = defq->ino.hard; - if (defq->rtb.soft && !dq->q_rtb.softlimit) + if (!dq->q_rtb.softlimit) dq->q_rtb.softlimit = defq->rtb.soft; - if (defq->rtb.hard && !dq->q_rtb.hardlimit) + if (!dq->q_rtb.hardlimit) dq->q_rtb.hardlimit = defq->rtb.hard; if (prealloc) -- GitLab From 4b8628d57b725b32616965e66975fcdebe008fe7 Mon Sep 17 00:00:00 2001 From: "Darrick J. Wong" Date: Tue, 14 Jul 2020 10:37:35 -0700 Subject: [PATCH 1016/1476] xfs: actually bump warning counts when we send warnings Currently, xfs quotas have the ability to send netlink warnings when a user exceeds the limits. They also have all the support code necessary to convert softlimit warnings into failures if the number of warnings exceeds a limit set by the administrator. Unfortunately, we never actually increase the warning counter, so this never actually happens. Make it so we actually do something useful with the warning counts. Signed-off-by: Darrick J. Wong Reviewed-by: Christoph Hellwig Reviewed-by: Chandan Babu R --- fs/xfs/xfs_trans_dquot.c | 1 + 1 file changed, 1 insertion(+) diff --git a/fs/xfs/xfs_trans_dquot.c b/fs/xfs/xfs_trans_dquot.c index 510b9c3164d60..6be6287da1ac2 100644 --- a/fs/xfs/xfs_trans_dquot.c +++ b/fs/xfs/xfs_trans_dquot.c @@ -589,6 +589,7 @@ xfs_dqresv_check( return QUOTA_NL_ISOFTLONGWARN; } + res->warnings++; return QUOTA_NL_ISOFTWARN; } -- GitLab From 2cb91bab4fa4effe56da1c7fe2fc5723c4935db1 Mon Sep 17 00:00:00 2001 From: "Darrick J. Wong" Date: Tue, 14 Jul 2020 10:37:35 -0700 Subject: [PATCH 1017/1476] xfs: add more dquot tracepoints Add all the xfs_dquot fields to the tracepoint for that type; add a new tracepoint type for the qtrx structure (dquot transaction deltas); and use our new tracepoints. This makes it easier for the author to trace changes to dquot counters for debugging. Signed-off-by: Darrick J. Wong Reviewed-by: Allison Collins Reviewed-by: Chandan Babu R Reviewed-by: Christoph Hellwig --- fs/xfs/xfs_trace.h | 140 ++++++++++++++++++++++++++++++++++++++- fs/xfs/xfs_trans_dquot.c | 21 ++++++ 2 files changed, 159 insertions(+), 2 deletions(-) diff --git a/fs/xfs/xfs_trace.h b/fs/xfs/xfs_trace.h index f0c2bce69a362..81534095f52be 100644 --- a/fs/xfs/xfs_trace.h +++ b/fs/xfs/xfs_trace.h @@ -36,6 +36,7 @@ struct xfs_owner_info; struct xfs_trans_res; struct xfs_inobt_rec_incore; union xfs_btree_ptr; +struct xfs_dqtrx; #define XFS_ATTR_FILTER_FLAGS \ { XFS_ATTR_ROOT, "ROOT" }, \ @@ -867,37 +868,59 @@ DECLARE_EVENT_CLASS(xfs_dquot_class, __field(unsigned, flags) __field(unsigned, nrefs) __field(unsigned long long, res_bcount) + __field(unsigned long long, res_rtbcount) + __field(unsigned long long, res_icount) + __field(unsigned long long, bcount) + __field(unsigned long long, rtbcount) __field(unsigned long long, icount) + __field(unsigned long long, blk_hardlimit) __field(unsigned long long, blk_softlimit) + __field(unsigned long long, rtb_hardlimit) + __field(unsigned long long, rtb_softlimit) __field(unsigned long long, ino_hardlimit) __field(unsigned long long, ino_softlimit) - ), \ + ), TP_fast_assign( __entry->dev = dqp->q_mount->m_super->s_dev; __entry->id = dqp->q_id; __entry->flags = dqp->dq_flags | dqp->q_flags; __entry->nrefs = dqp->q_nrefs; + __entry->res_bcount = dqp->q_blk.reserved; + __entry->res_rtbcount = dqp->q_rtb.reserved; + __entry->res_icount = dqp->q_ino.reserved; + __entry->bcount = dqp->q_blk.count; + __entry->rtbcount = dqp->q_rtb.count; __entry->icount = dqp->q_ino.count; + __entry->blk_hardlimit = dqp->q_blk.hardlimit; __entry->blk_softlimit = dqp->q_blk.softlimit; + __entry->rtb_hardlimit = dqp->q_rtb.hardlimit; + __entry->rtb_softlimit = dqp->q_rtb.softlimit; __entry->ino_hardlimit = dqp->q_ino.hardlimit; __entry->ino_softlimit = dqp->q_ino.softlimit; ), - TP_printk("dev %d:%d id 0x%x flags %s nrefs %u res_bc 0x%llx " + TP_printk("dev %d:%d id 0x%x flags %s nrefs %u " + "res_bc 0x%llx res_rtbc 0x%llx res_ic 0x%llx " "bcnt 0x%llx bhardlimit 0x%llx bsoftlimit 0x%llx " + "rtbcnt 0x%llx rtbhardlimit 0x%llx rtbsoftlimit 0x%llx " "icnt 0x%llx ihardlimit 0x%llx isoftlimit 0x%llx]", MAJOR(__entry->dev), MINOR(__entry->dev), __entry->id, __print_flags(__entry->flags, "|", XFS_DQFLAG_STRINGS), __entry->nrefs, __entry->res_bcount, + __entry->res_rtbcount, + __entry->res_icount, __entry->bcount, __entry->blk_hardlimit, __entry->blk_softlimit, + __entry->rtbcount, + __entry->rtb_hardlimit, + __entry->rtb_softlimit, __entry->icount, __entry->ino_hardlimit, __entry->ino_softlimit) @@ -928,6 +951,119 @@ DEFINE_DQUOT_EVENT(xfs_dqrele); DEFINE_DQUOT_EVENT(xfs_dqflush); DEFINE_DQUOT_EVENT(xfs_dqflush_force); DEFINE_DQUOT_EVENT(xfs_dqflush_done); +DEFINE_DQUOT_EVENT(xfs_trans_apply_dquot_deltas_before); +DEFINE_DQUOT_EVENT(xfs_trans_apply_dquot_deltas_after); + +#define XFS_QMOPT_FLAGS \ + { XFS_QMOPT_UQUOTA, "UQUOTA" }, \ + { XFS_QMOPT_PQUOTA, "PQUOTA" }, \ + { XFS_QMOPT_FORCE_RES, "FORCE_RES" }, \ + { XFS_QMOPT_SBVERSION, "SBVERSION" }, \ + { XFS_QMOPT_GQUOTA, "GQUOTA" }, \ + { XFS_QMOPT_INHERIT, "INHERIT" }, \ + { XFS_QMOPT_RES_REGBLKS, "RES_REGBLKS" }, \ + { XFS_QMOPT_RES_RTBLKS, "RES_RTBLKS" }, \ + { XFS_QMOPT_BCOUNT, "BCOUNT" }, \ + { XFS_QMOPT_ICOUNT, "ICOUNT" }, \ + { XFS_QMOPT_RTBCOUNT, "RTBCOUNT" }, \ + { XFS_QMOPT_DELBCOUNT, "DELBCOUNT" }, \ + { XFS_QMOPT_DELRTBCOUNT, "DELRTBCOUNT" }, \ + { XFS_QMOPT_RES_INOS, "RES_INOS" } + +TRACE_EVENT(xfs_trans_mod_dquot, + TP_PROTO(struct xfs_trans *tp, struct xfs_dquot *dqp, + unsigned int field, int64_t delta), + TP_ARGS(tp, dqp, field, delta), + TP_STRUCT__entry( + __field(dev_t, dev) + __field(unsigned int, flags) + __field(unsigned int, dqid) + __field(unsigned int, field) + __field(int64_t, delta) + ), + TP_fast_assign( + __entry->dev = tp->t_mountp->m_super->s_dev; + __entry->flags = dqp->dq_flags | dqp->q_flags; + __entry->dqid = dqp->q_id; + __entry->field = field; + __entry->delta = delta; + ), + TP_printk("dev %d:%d dquot id 0x%x flags %s field %s delta %lld", + MAJOR(__entry->dev), MINOR(__entry->dev), + __entry->dqid, + __print_flags(__entry->flags, "|", XFS_DQFLAG_STRINGS), + __print_flags(__entry->field, "|", XFS_QMOPT_FLAGS), + __entry->delta) +); + +DECLARE_EVENT_CLASS(xfs_dqtrx_class, + TP_PROTO(struct xfs_dqtrx *qtrx), + TP_ARGS(qtrx), + TP_STRUCT__entry( + __field(dev_t, dev) + __field(unsigned int, flags) + __field(u32, dqid) + + __field(uint64_t, blk_res) + __field(int64_t, bcount_delta) + __field(int64_t, delbcnt_delta) + + __field(uint64_t, rtblk_res) + __field(uint64_t, rtblk_res_used) + __field(int64_t, rtbcount_delta) + __field(int64_t, delrtb_delta) + + __field(uint64_t, ino_res) + __field(uint64_t, ino_res_used) + __field(int64_t, icount_delta) + ), + TP_fast_assign( + __entry->dev = qtrx->qt_dquot->q_mount->m_super->s_dev; + __entry->flags = qtrx->qt_dquot->dq_flags | qtrx->qt_dquot->q_flags; + __entry->dqid = qtrx->qt_dquot->q_id; + + __entry->blk_res = qtrx->qt_blk_res; + __entry->bcount_delta = qtrx->qt_bcount_delta; + __entry->delbcnt_delta = qtrx->qt_delbcnt_delta; + + __entry->rtblk_res = qtrx->qt_rtblk_res; + __entry->rtblk_res_used = qtrx->qt_rtblk_res_used; + __entry->rtbcount_delta = qtrx->qt_rtbcount_delta; + __entry->delrtb_delta = qtrx->qt_delrtb_delta; + + __entry->ino_res = qtrx->qt_ino_res; + __entry->ino_res_used = qtrx->qt_ino_res_used; + __entry->icount_delta = qtrx->qt_icount_delta; + ), + TP_printk("dev %d:%d dquot id 0x%x flags %s" + "blk_res %llu bcount_delta %lld delbcnt_delta %lld " + "rtblk_res %llu rtblk_res_used %llu rtbcount_delta %lld delrtb_delta %lld " + "ino_res %llu ino_res_used %llu icount_delta %lld", + MAJOR(__entry->dev), MINOR(__entry->dev), + __entry->dqid, + __print_flags(__entry->flags, "|", XFS_DQFLAG_STRINGS), + + __entry->blk_res, + __entry->bcount_delta, + __entry->delbcnt_delta, + + __entry->rtblk_res, + __entry->rtblk_res_used, + __entry->rtbcount_delta, + __entry->delrtb_delta, + + __entry->ino_res, + __entry->ino_res_used, + __entry->icount_delta) +) + +#define DEFINE_DQTRX_EVENT(name) \ +DEFINE_EVENT(xfs_dqtrx_class, name, \ + TP_PROTO(struct xfs_dqtrx *qtrx), \ + TP_ARGS(qtrx)) +DEFINE_DQTRX_EVENT(xfs_trans_apply_dquot_deltas); +DEFINE_DQTRX_EVENT(xfs_trans_mod_dquot_before); +DEFINE_DQTRX_EVENT(xfs_trans_mod_dquot_after); DECLARE_EVENT_CLASS(xfs_loggrant_class, TP_PROTO(struct xlog *log, struct xlog_ticket *tic), diff --git a/fs/xfs/xfs_trans_dquot.c b/fs/xfs/xfs_trans_dquot.c index 6be6287da1ac2..a8f480e5401fc 100644 --- a/fs/xfs/xfs_trans_dquot.c +++ b/fs/xfs/xfs_trans_dquot.c @@ -15,6 +15,7 @@ #include "xfs_trans_priv.h" #include "xfs_quota.h" #include "xfs_qm.h" +#include "xfs_trace.h" STATIC void xfs_trans_alloc_dqinfo(xfs_trans_t *); @@ -203,6 +204,11 @@ xfs_trans_mod_dquot( if (qtrx->qt_dquot == NULL) qtrx->qt_dquot = dqp; + if (delta) { + trace_xfs_trans_mod_dquot_before(qtrx); + trace_xfs_trans_mod_dquot(tp, dqp, field, delta); + } + switch (field) { /* @@ -266,6 +272,10 @@ xfs_trans_mod_dquot( default: ASSERT(0); } + + if (delta) + trace_xfs_trans_mod_dquot_after(qtrx); + tp->t_flags |= XFS_TRANS_DQ_DIRTY; } @@ -391,6 +401,13 @@ xfs_trans_apply_dquot_deltas( qtrx->qt_delbcnt_delta; totalrtbdelta = qtrx->qt_rtbcount_delta + qtrx->qt_delrtb_delta; + + if (totalbdelta != 0 || totalrtbdelta != 0 || + qtrx->qt_icount_delta != 0) { + trace_xfs_trans_apply_dquot_deltas_before(dqp); + trace_xfs_trans_apply_dquot_deltas(qtrx); + } + #ifdef DEBUG if (totalbdelta < 0) ASSERT(dqp->q_blk.count >= -totalbdelta); @@ -410,6 +427,10 @@ xfs_trans_apply_dquot_deltas( if (totalrtbdelta) dqp->q_rtb.count += totalrtbdelta; + if (totalbdelta != 0 || totalrtbdelta != 0 || + qtrx->qt_icount_delta != 0) + trace_xfs_trans_apply_dquot_deltas_after(dqp); + /* * Get any default limits in use. * Start/reset the timer(s) if needed. -- GitLab From f9751c4ad3d17fa93773c187732f10c8a49940e3 Mon Sep 17 00:00:00 2001 From: "Darrick J. Wong" Date: Wed, 15 Jul 2020 17:41:24 -0700 Subject: [PATCH 1018/1476] xfs: drop the type parameter from xfs_dquot_verify xfs_qm_reset_dqcounts (aka quotacheck) is the only xfs_dqblk_verify caller that actually knows the specific quota type that it's looking for. Since everything else just pass in type==0 (including the buffer verifier), drop the parameter and open-code the check like xfs_dquot_from_disk already does. Signed-off-by: Darrick J. Wong Reviewed-by: Dave Chinner Reviewed-by: Christoph Hellwig --- fs/xfs/libxfs/xfs_dquot_buf.c | 12 ++++-------- fs/xfs/libxfs/xfs_quota_defs.h | 4 ++-- fs/xfs/xfs_buf_item_recover.c | 3 +-- fs/xfs/xfs_dquot_item_recover.c | 2 +- fs/xfs/xfs_qm.c | 5 ++--- 5 files changed, 10 insertions(+), 16 deletions(-) diff --git a/fs/xfs/libxfs/xfs_dquot_buf.c b/fs/xfs/libxfs/xfs_dquot_buf.c index bedc1e752b601..eb2412e13f30b 100644 --- a/fs/xfs/libxfs/xfs_dquot_buf.c +++ b/fs/xfs/libxfs/xfs_dquot_buf.c @@ -37,8 +37,7 @@ xfs_failaddr_t xfs_dquot_verify( struct xfs_mount *mp, struct xfs_disk_dquot *ddq, - xfs_dqid_t id, - uint type) /* used only during quotacheck */ + xfs_dqid_t id) /* used only during quotacheck */ { /* * We can encounter an uninitialized dquot buffer for 2 reasons: @@ -60,8 +59,6 @@ xfs_dquot_verify( if (ddq->d_version != XFS_DQUOT_VERSION) return __this_address; - if (type && ddq->d_flags != type) - return __this_address; if (ddq->d_flags != XFS_DQ_USER && ddq->d_flags != XFS_DQ_PROJ && ddq->d_flags != XFS_DQ_GROUP) @@ -95,14 +92,13 @@ xfs_failaddr_t xfs_dqblk_verify( struct xfs_mount *mp, struct xfs_dqblk *dqb, - xfs_dqid_t id, - uint type) /* used only during quotacheck */ + xfs_dqid_t id) /* used only during quotacheck */ { if (xfs_sb_version_hascrc(&mp->m_sb) && !uuid_equal(&dqb->dd_uuid, &mp->m_sb.sb_meta_uuid)) return __this_address; - return xfs_dquot_verify(mp, &dqb->dd_diskdq, id, type); + return xfs_dquot_verify(mp, &dqb->dd_diskdq, id); } /* @@ -205,7 +201,7 @@ xfs_dquot_buf_verify( if (i == 0) id = be32_to_cpu(ddq->d_id); - fa = xfs_dqblk_verify(mp, &dqb[i], id + i, 0); + fa = xfs_dqblk_verify(mp, &dqb[i], id + i); if (fa) { if (!readahead) xfs_buf_verifier_error(bp, -EFSCORRUPTED, diff --git a/fs/xfs/libxfs/xfs_quota_defs.h b/fs/xfs/libxfs/xfs_quota_defs.h index e2da08055e6b7..d2245f375719a 100644 --- a/fs/xfs/libxfs/xfs_quota_defs.h +++ b/fs/xfs/libxfs/xfs_quota_defs.h @@ -137,9 +137,9 @@ typedef uint16_t xfs_qwarncnt_t; #define XFS_QMOPT_RESBLK_MASK (XFS_QMOPT_RES_REGBLKS | XFS_QMOPT_RES_RTBLKS) extern xfs_failaddr_t xfs_dquot_verify(struct xfs_mount *mp, - struct xfs_disk_dquot *ddq, xfs_dqid_t id, uint type); + struct xfs_disk_dquot *ddq, xfs_dqid_t id); extern xfs_failaddr_t xfs_dqblk_verify(struct xfs_mount *mp, - struct xfs_dqblk *dqb, xfs_dqid_t id, uint type); + struct xfs_dqblk *dqb, xfs_dqid_t id); extern int xfs_calc_dquots_per_chunk(unsigned int nbblks); extern void xfs_dqblk_repair(struct xfs_mount *mp, struct xfs_dqblk *dqb, xfs_dqid_t id, uint type); diff --git a/fs/xfs/xfs_buf_item_recover.c b/fs/xfs/xfs_buf_item_recover.c index 74c851f60eeeb..8bee582cf66ad 100644 --- a/fs/xfs/xfs_buf_item_recover.c +++ b/fs/xfs/xfs_buf_item_recover.c @@ -493,8 +493,7 @@ xlog_recover_do_reg_buffer( item->ri_buf[i].i_len, __func__); goto next; } - fa = xfs_dquot_verify(mp, item->ri_buf[i].i_addr, - -1, 0); + fa = xfs_dquot_verify(mp, item->ri_buf[i].i_addr, -1); if (fa) { xfs_alert(mp, "dquot corrupt at %pS trying to replay into block 0x%llx", diff --git a/fs/xfs/xfs_dquot_item_recover.c b/fs/xfs/xfs_dquot_item_recover.c index f9ea9f55aa7cc..9f64162ca3002 100644 --- a/fs/xfs/xfs_dquot_item_recover.c +++ b/fs/xfs/xfs_dquot_item_recover.c @@ -108,7 +108,7 @@ xlog_recover_dquot_commit_pass2( */ dq_f = item->ri_buf[0].i_addr; ASSERT(dq_f); - fa = xfs_dquot_verify(mp, recddq, dq_f->qlf_id, 0); + fa = xfs_dquot_verify(mp, recddq, dq_f->qlf_id); if (fa) { xfs_alert(mp, "corrupt dquot ID 0x%x in log at %pS", dq_f->qlf_id, fa); diff --git a/fs/xfs/xfs_qm.c b/fs/xfs/xfs_qm.c index 7d83c1623cb2f..bf94c1bbda164 100644 --- a/fs/xfs/xfs_qm.c +++ b/fs/xfs/xfs_qm.c @@ -830,7 +830,6 @@ xfs_qm_reset_dqcounts( { struct xfs_dqblk *dqb; int j; - xfs_failaddr_t fa; trace_xfs_reset_dqcounts(bp, _RET_IP_); @@ -855,8 +854,8 @@ xfs_qm_reset_dqcounts( * find uninitialised dquot blks. See comment in * xfs_dquot_verify. */ - fa = xfs_dqblk_verify(mp, &dqb[j], id + j, type); - if (fa) + if (xfs_dqblk_verify(mp, &dqb[j], id + j) || + (dqb[j].dd_diskdq.d_flags & XFS_DQ_ALLTYPES) != type) xfs_dqblk_repair(mp, &dqb[j], id + j, type); /* -- GitLab From 8cd4901da56caadc16b4e8d6b434291a8ce31d7c Mon Sep 17 00:00:00 2001 From: "Darrick J. Wong" Date: Wed, 15 Jul 2020 17:42:36 -0700 Subject: [PATCH 1019/1476] xfs: rename XFS_DQ_{USER,GROUP,PROJ} to XFS_DQTYPE_* We're going to split up the incore dquot state flags from the ondisk dquot flags (eventually renaming this "type") so start by renaming the three flags and the bitmask that are going to participate in this. Signed-off-by: Darrick J. Wong Reviewed-by: Dave Chinner Reviewed-by: Christoph Hellwig --- fs/xfs/libxfs/xfs_dquot_buf.c | 6 ++-- fs/xfs/libxfs/xfs_format.h | 2 +- fs/xfs/libxfs/xfs_quota_defs.h | 16 +++++----- fs/xfs/scrub/quota.c | 6 ++-- fs/xfs/scrub/repair.c | 6 ++-- fs/xfs/xfs_buf_item_recover.c | 6 ++-- fs/xfs/xfs_dquot.c | 36 +++++++++++------------ fs/xfs/xfs_dquot.h | 22 +++++++------- fs/xfs/xfs_dquot_item_recover.c | 10 +++---- fs/xfs/xfs_icache.c | 4 +-- fs/xfs/xfs_iomap.c | 12 ++++---- fs/xfs/xfs_qm.c | 52 ++++++++++++++++----------------- fs/xfs/xfs_qm.h | 26 ++++++++--------- fs/xfs/xfs_qm_bhv.c | 2 +- fs/xfs/xfs_qm_syscalls.c | 12 ++++---- fs/xfs/xfs_quota.h | 6 ++-- fs/xfs/xfs_quotaops.c | 6 ++-- fs/xfs/xfs_trans_dquot.c | 4 +-- 18 files changed, 118 insertions(+), 116 deletions(-) diff --git a/fs/xfs/libxfs/xfs_dquot_buf.c b/fs/xfs/libxfs/xfs_dquot_buf.c index eb2412e13f30b..450147df30424 100644 --- a/fs/xfs/libxfs/xfs_dquot_buf.c +++ b/fs/xfs/libxfs/xfs_dquot_buf.c @@ -59,9 +59,9 @@ xfs_dquot_verify( if (ddq->d_version != XFS_DQUOT_VERSION) return __this_address; - if (ddq->d_flags != XFS_DQ_USER && - ddq->d_flags != XFS_DQ_PROJ && - ddq->d_flags != XFS_DQ_GROUP) + if (ddq->d_flags != XFS_DQTYPE_USER && + ddq->d_flags != XFS_DQTYPE_PROJ && + ddq->d_flags != XFS_DQTYPE_GROUP) return __this_address; if (id != -1 && id != be32_to_cpu(ddq->d_id)) diff --git a/fs/xfs/libxfs/xfs_format.h b/fs/xfs/libxfs/xfs_format.h index a534ebee92b9e..5d5e0f5eda973 100644 --- a/fs/xfs/libxfs/xfs_format.h +++ b/fs/xfs/libxfs/xfs_format.h @@ -1157,7 +1157,7 @@ static inline void xfs_dinode_put_rdev(struct xfs_dinode *dip, xfs_dev_t rdev) struct xfs_disk_dquot { __be16 d_magic; /* dquot magic = XFS_DQUOT_MAGIC */ __u8 d_version; /* dquot version */ - __u8 d_flags; /* XFS_DQ_USER/PROJ/GROUP */ + __u8 d_flags; /* XFS_DQTYPE_USER/PROJ/GROUP */ __be32 d_id; /* user,project,group id */ __be64 d_blk_hardlimit;/* absolute limit on disk blks */ __be64 d_blk_softlimit;/* preferred limit on disk blks */ diff --git a/fs/xfs/libxfs/xfs_quota_defs.h b/fs/xfs/libxfs/xfs_quota_defs.h index d2245f375719a..baf6c4ad88afe 100644 --- a/fs/xfs/libxfs/xfs_quota_defs.h +++ b/fs/xfs/libxfs/xfs_quota_defs.h @@ -21,18 +21,20 @@ typedef uint16_t xfs_qwarncnt_t; /* * flags for q_flags field in the dquot. */ -#define XFS_DQ_USER 0x0001 /* a user quota */ -#define XFS_DQ_PROJ 0x0002 /* project quota */ -#define XFS_DQ_GROUP 0x0004 /* a group quota */ +#define XFS_DQTYPE_USER 0x0001 /* a user quota */ +#define XFS_DQTYPE_PROJ 0x0002 /* project quota */ +#define XFS_DQTYPE_GROUP 0x0004 /* a group quota */ #define XFS_DQFLAG_DIRTY 0x0008 /* dquot is dirty */ #define XFS_DQFLAG_FREEING 0x0010 /* dquot is being torn down */ -#define XFS_DQ_ALLTYPES (XFS_DQ_USER|XFS_DQ_PROJ|XFS_DQ_GROUP) +#define XFS_DQTYPE_REC_MASK (XFS_DQTYPE_USER | \ + XFS_DQTYPE_PROJ | \ + XFS_DQTYPE_GROUP) #define XFS_DQFLAG_STRINGS \ - { XFS_DQ_USER, "USER" }, \ - { XFS_DQ_PROJ, "PROJ" }, \ - { XFS_DQ_GROUP, "GROUP" }, \ + { XFS_DQTYPE_USER, "USER" }, \ + { XFS_DQTYPE_PROJ, "PROJ" }, \ + { XFS_DQTYPE_GROUP, "GROUP" }, \ { XFS_DQFLAG_DIRTY, "DIRTY" }, \ { XFS_DQFLAG_FREEING, "FREEING" } diff --git a/fs/xfs/scrub/quota.c b/fs/xfs/scrub/quota.c index f4aad5b001883..1db07485f1486 100644 --- a/fs/xfs/scrub/quota.c +++ b/fs/xfs/scrub/quota.c @@ -24,11 +24,11 @@ xchk_quota_to_dqtype( { switch (sc->sm->sm_type) { case XFS_SCRUB_TYPE_UQUOTA: - return XFS_DQ_USER; + return XFS_DQTYPE_USER; case XFS_SCRUB_TYPE_GQUOTA: - return XFS_DQ_GROUP; + return XFS_DQTYPE_GROUP; case XFS_SCRUB_TYPE_PQUOTA: - return XFS_DQ_PROJ; + return XFS_DQTYPE_PROJ; default: return 0; } diff --git a/fs/xfs/scrub/repair.c b/fs/xfs/scrub/repair.c index db3cfd12803d4..0746518965862 100644 --- a/fs/xfs/scrub/repair.c +++ b/fs/xfs/scrub/repair.c @@ -939,11 +939,11 @@ xrep_ino_dqattach( "inode %llu repair encountered quota error %d, quotacheck forced.", (unsigned long long)sc->ip->i_ino, error); if (XFS_IS_UQUOTA_ON(sc->mp) && !sc->ip->i_udquot) - xrep_force_quotacheck(sc, XFS_DQ_USER); + xrep_force_quotacheck(sc, XFS_DQTYPE_USER); if (XFS_IS_GQUOTA_ON(sc->mp) && !sc->ip->i_gdquot) - xrep_force_quotacheck(sc, XFS_DQ_GROUP); + xrep_force_quotacheck(sc, XFS_DQTYPE_GROUP); if (XFS_IS_PQUOTA_ON(sc->mp) && !sc->ip->i_pdquot) - xrep_force_quotacheck(sc, XFS_DQ_PROJ); + xrep_force_quotacheck(sc, XFS_DQTYPE_PROJ); /* fall through */ case -ESRCH: error = 0; diff --git a/fs/xfs/xfs_buf_item_recover.c b/fs/xfs/xfs_buf_item_recover.c index 8bee582cf66ad..d480f11e6b007 100644 --- a/fs/xfs/xfs_buf_item_recover.c +++ b/fs/xfs/xfs_buf_item_recover.c @@ -546,11 +546,11 @@ xlog_recover_do_dquot_buffer( type = 0; if (buf_f->blf_flags & XFS_BLF_UDQUOT_BUF) - type |= XFS_DQ_USER; + type |= XFS_DQTYPE_USER; if (buf_f->blf_flags & XFS_BLF_PDQUOT_BUF) - type |= XFS_DQ_PROJ; + type |= XFS_DQTYPE_PROJ; if (buf_f->blf_flags & XFS_BLF_GDQUOT_BUF) - type |= XFS_DQ_GROUP; + type |= XFS_DQTYPE_GROUP; /* * This type of quotas was turned off, so ignore this buffer */ diff --git a/fs/xfs/xfs_dquot.c b/fs/xfs/xfs_dquot.c index 8e84623cc3317..4053e7e390f19 100644 --- a/fs/xfs/xfs_dquot.c +++ b/fs/xfs/xfs_dquot.c @@ -190,10 +190,10 @@ xfs_qm_init_dquot_blk( } } - if (type & XFS_DQ_USER) { + if (type & XFS_DQTYPE_USER) { qflag = XFS_UQUOTA_CHKD; blftype = XFS_BLF_UDQUOT_BUF; - } else if (type & XFS_DQ_PROJ) { + } else if (type & XFS_DQTYPE_PROJ) { qflag = XFS_PQUOTA_CHKD; blftype = XFS_BLF_PDQUOT_BUF; } else { @@ -311,7 +311,7 @@ xfs_dquot_disk_alloc( * the entire thing. */ xfs_qm_init_dquot_blk(tp, mp, dqp->q_id, - dqp->dq_flags & XFS_DQ_ALLTYPES, bp); + dqp->dq_flags & XFS_DQTYPE_REC_MASK, bp); xfs_buf_set_ref(bp, XFS_DQUOT_REF); /* @@ -448,13 +448,13 @@ xfs_dquot_alloc( * quotas. */ switch (type) { - case XFS_DQ_USER: + case XFS_DQTYPE_USER: /* uses the default lock class */ break; - case XFS_DQ_GROUP: + case XFS_DQTYPE_GROUP: lockdep_set_class(&dqp->q_qlock, &xfs_dquot_group_class); break; - case XFS_DQ_PROJ: + case XFS_DQTYPE_PROJ: lockdep_set_class(&dqp->q_qlock, &xfs_dquot_project_class); break; default: @@ -480,7 +480,7 @@ xfs_dquot_from_disk( * Ensure that we got the type and ID we were looking for. * Everything else was checked by the dquot buffer verifier. */ - if ((ddqp->d_flags & XFS_DQ_ALLTYPES) != dqp->dq_flags || + if ((ddqp->d_flags & XFS_DQTYPE_REC_MASK) != dqp->dq_flags || be32_to_cpu(ddqp->d_id) != dqp->q_id) { xfs_alert_tag(bp->b_mount, XFS_PTAG_VERIFIER_ERROR, "Metadata corruption detected at %pS, quota %u", @@ -530,7 +530,7 @@ xfs_dquot_to_disk( { ddqp->d_magic = cpu_to_be16(XFS_DQUOT_MAGIC); ddqp->d_version = XFS_DQUOT_VERSION; - ddqp->d_flags = dqp->dq_flags & XFS_DQ_ALLTYPES; + ddqp->d_flags = dqp->dq_flags & XFS_DQTYPE_REC_MASK; ddqp->d_id = cpu_to_be32(dqp->q_id); ddqp->d_pad0 = 0; ddqp->d_pad = 0; @@ -779,15 +779,15 @@ xfs_qm_dqget_checks( return -ESRCH; switch (type) { - case XFS_DQ_USER: + case XFS_DQTYPE_USER: if (!XFS_IS_UQUOTA_ON(mp)) return -ESRCH; return 0; - case XFS_DQ_GROUP: + case XFS_DQTYPE_GROUP: if (!XFS_IS_GQUOTA_ON(mp)) return -ESRCH; return 0; - case XFS_DQ_PROJ: + case XFS_DQTYPE_PROJ: if (!XFS_IS_PQUOTA_ON(mp)) return -ESRCH; return 0; @@ -874,11 +874,11 @@ xfs_qm_id_for_quotatype( uint type) { switch (type) { - case XFS_DQ_USER: + case XFS_DQTYPE_USER: return i_uid_read(VFS_I(ip)); - case XFS_DQ_GROUP: + case XFS_DQTYPE_GROUP: return i_gid_read(VFS_I(ip)); - case XFS_DQ_PROJ: + case XFS_DQTYPE_PROJ: return ip->i_d.di_projid; } ASSERT(0); @@ -1114,11 +1114,11 @@ static xfs_failaddr_t xfs_qm_dqflush_check( struct xfs_dquot *dqp) { - __u8 type = dqp->dq_flags & XFS_DQ_ALLTYPES; + __u8 type = dqp->dq_flags & XFS_DQTYPE_REC_MASK; - if (type != XFS_DQ_USER && - type != XFS_DQ_GROUP && - type != XFS_DQ_PROJ) + if (type != XFS_DQTYPE_USER && + type != XFS_DQTYPE_GROUP && + type != XFS_DQTYPE_PROJ) return __this_address; if (dqp->q_id == 0) diff --git a/fs/xfs/xfs_dquot.h b/fs/xfs/xfs_dquot.h index 9e44da522684e..17a21677723f6 100644 --- a/fs/xfs/xfs_dquot.h +++ b/fs/xfs/xfs_dquot.h @@ -130,12 +130,12 @@ static inline void xfs_dqunlock(struct xfs_dquot *dqp) static inline int xfs_this_quota_on(struct xfs_mount *mp, int type) { - switch (type & XFS_DQ_ALLTYPES) { - case XFS_DQ_USER: + switch (type & XFS_DQTYPE_REC_MASK) { + case XFS_DQTYPE_USER: return XFS_IS_UQUOTA_ON(mp); - case XFS_DQ_GROUP: + case XFS_DQTYPE_GROUP: return XFS_IS_GQUOTA_ON(mp); - case XFS_DQ_PROJ: + case XFS_DQTYPE_PROJ: return XFS_IS_PQUOTA_ON(mp); default: return 0; @@ -144,12 +144,12 @@ static inline int xfs_this_quota_on(struct xfs_mount *mp, int type) static inline struct xfs_dquot *xfs_inode_dquot(struct xfs_inode *ip, int type) { - switch (type & XFS_DQ_ALLTYPES) { - case XFS_DQ_USER: + switch (type & XFS_DQTYPE_REC_MASK) { + case XFS_DQTYPE_USER: return ip->i_udquot; - case XFS_DQ_GROUP: + case XFS_DQTYPE_GROUP: return ip->i_gdquot; - case XFS_DQ_PROJ: + case XFS_DQTYPE_PROJ: return ip->i_pdquot; default: return NULL; @@ -175,9 +175,9 @@ void xfs_dquot_to_disk(struct xfs_disk_dquot *ddqp, struct xfs_dquot *dqp); #define XFS_DQ_IS_LOCKED(dqp) (mutex_is_locked(&((dqp)->q_qlock))) #define XFS_DQ_IS_DIRTY(dqp) ((dqp)->q_flags & XFS_DQFLAG_DIRTY) -#define XFS_QM_ISUDQ(dqp) ((dqp)->dq_flags & XFS_DQ_USER) -#define XFS_QM_ISPDQ(dqp) ((dqp)->dq_flags & XFS_DQ_PROJ) -#define XFS_QM_ISGDQ(dqp) ((dqp)->dq_flags & XFS_DQ_GROUP) +#define XFS_QM_ISUDQ(dqp) ((dqp)->dq_flags & XFS_DQTYPE_USER) +#define XFS_QM_ISPDQ(dqp) ((dqp)->dq_flags & XFS_DQTYPE_PROJ) +#define XFS_QM_ISGDQ(dqp) ((dqp)->dq_flags & XFS_DQTYPE_GROUP) void xfs_qm_dqdestroy(struct xfs_dquot *dqp); int xfs_qm_dqflush(struct xfs_dquot *dqp, struct xfs_buf **bpp); diff --git a/fs/xfs/xfs_dquot_item_recover.c b/fs/xfs/xfs_dquot_item_recover.c index 9f64162ca3002..d7eb85c7d394d 100644 --- a/fs/xfs/xfs_dquot_item_recover.c +++ b/fs/xfs/xfs_dquot_item_recover.c @@ -39,7 +39,7 @@ xlog_recover_dquot_ra_pass2( if (item->ri_buf[1].i_len < sizeof(struct xfs_disk_dquot)) return; - type = recddq->d_flags & (XFS_DQ_USER | XFS_DQ_PROJ | XFS_DQ_GROUP); + type = recddq->d_flags & (XFS_DQTYPE_USER | XFS_DQTYPE_PROJ | XFS_DQTYPE_GROUP); ASSERT(type); if (log->l_quotaoffs_flag & type) return; @@ -91,7 +91,7 @@ xlog_recover_dquot_commit_pass2( /* * This type of quotas was turned off, so ignore this record. */ - type = recddq->d_flags & (XFS_DQ_USER | XFS_DQ_PROJ | XFS_DQ_GROUP); + type = recddq->d_flags & (XFS_DQTYPE_USER | XFS_DQTYPE_PROJ | XFS_DQTYPE_GROUP); ASSERT(type); if (log->l_quotaoffs_flag & type) return 0; @@ -185,11 +185,11 @@ xlog_recover_quotaoff_commit_pass1( * group/project quotaoff or both. */ if (qoff_f->qf_flags & XFS_UQUOTA_ACCT) - log->l_quotaoffs_flag |= XFS_DQ_USER; + log->l_quotaoffs_flag |= XFS_DQTYPE_USER; if (qoff_f->qf_flags & XFS_PQUOTA_ACCT) - log->l_quotaoffs_flag |= XFS_DQ_PROJ; + log->l_quotaoffs_flag |= XFS_DQTYPE_PROJ; if (qoff_f->qf_flags & XFS_GQUOTA_ACCT) - log->l_quotaoffs_flag |= XFS_DQ_GROUP; + log->l_quotaoffs_flag |= XFS_DQTYPE_GROUP; return 0; } diff --git a/fs/xfs/xfs_icache.c b/fs/xfs/xfs_icache.c index 58a750ce689c0..3c6e936d2f992 100644 --- a/fs/xfs/xfs_icache.c +++ b/fs/xfs/xfs_icache.c @@ -1424,7 +1424,7 @@ __xfs_inode_free_quota_eofblocks( eofb.eof_flags = XFS_EOF_FLAGS_UNION|XFS_EOF_FLAGS_SYNC; if (XFS_IS_UQUOTA_ENFORCED(ip->i_mount)) { - dq = xfs_inode_dquot(ip, XFS_DQ_USER); + dq = xfs_inode_dquot(ip, XFS_DQTYPE_USER); if (dq && xfs_dquot_lowsp(dq)) { eofb.eof_uid = VFS_I(ip)->i_uid; eofb.eof_flags |= XFS_EOF_FLAGS_UID; @@ -1433,7 +1433,7 @@ __xfs_inode_free_quota_eofblocks( } if (XFS_IS_GQUOTA_ENFORCED(ip->i_mount)) { - dq = xfs_inode_dquot(ip, XFS_DQ_GROUP); + dq = xfs_inode_dquot(ip, XFS_DQTYPE_GROUP); if (dq && xfs_dquot_lowsp(dq)) { eofb.eof_gid = VFS_I(ip)->i_gid; eofb.eof_flags |= XFS_EOF_FLAGS_GID; diff --git a/fs/xfs/xfs_iomap.c b/fs/xfs/xfs_iomap.c index f60a6e44363b6..d3dc4106a35cd 100644 --- a/fs/xfs/xfs_iomap.c +++ b/fs/xfs/xfs_iomap.c @@ -450,14 +450,14 @@ xfs_iomap_prealloc_size( * Check each quota to cap the prealloc size, provide a shift value to * throttle with and adjust amount of available space. */ - if (xfs_quota_need_throttle(ip, XFS_DQ_USER, alloc_blocks)) - xfs_quota_calc_throttle(ip, XFS_DQ_USER, &qblocks, &qshift, + if (xfs_quota_need_throttle(ip, XFS_DQTYPE_USER, alloc_blocks)) + xfs_quota_calc_throttle(ip, XFS_DQTYPE_USER, &qblocks, &qshift, &freesp); - if (xfs_quota_need_throttle(ip, XFS_DQ_GROUP, alloc_blocks)) - xfs_quota_calc_throttle(ip, XFS_DQ_GROUP, &qblocks, &qshift, + if (xfs_quota_need_throttle(ip, XFS_DQTYPE_GROUP, alloc_blocks)) + xfs_quota_calc_throttle(ip, XFS_DQTYPE_GROUP, &qblocks, &qshift, &freesp); - if (xfs_quota_need_throttle(ip, XFS_DQ_PROJ, alloc_blocks)) - xfs_quota_calc_throttle(ip, XFS_DQ_PROJ, &qblocks, &qshift, + if (xfs_quota_need_throttle(ip, XFS_DQTYPE_PROJ, alloc_blocks)) + xfs_quota_calc_throttle(ip, XFS_DQTYPE_PROJ, &qblocks, &qshift, &freesp); /* diff --git a/fs/xfs/xfs_qm.c b/fs/xfs/xfs_qm.c index bf94c1bbda164..47d4b6937c84c 100644 --- a/fs/xfs/xfs_qm.c +++ b/fs/xfs/xfs_qm.c @@ -189,11 +189,11 @@ xfs_qm_dqpurge_all( uint flags) { if (flags & XFS_QMOPT_UQUOTA) - xfs_qm_dquot_walk(mp, XFS_DQ_USER, xfs_qm_dqpurge, NULL); + xfs_qm_dquot_walk(mp, XFS_DQTYPE_USER, xfs_qm_dqpurge, NULL); if (flags & XFS_QMOPT_GQUOTA) - xfs_qm_dquot_walk(mp, XFS_DQ_GROUP, xfs_qm_dqpurge, NULL); + xfs_qm_dquot_walk(mp, XFS_DQTYPE_GROUP, xfs_qm_dqpurge, NULL); if (flags & XFS_QMOPT_PQUOTA) - xfs_qm_dquot_walk(mp, XFS_DQ_PROJ, xfs_qm_dqpurge, NULL); + xfs_qm_dquot_walk(mp, XFS_DQTYPE_PROJ, xfs_qm_dqpurge, NULL); } /* @@ -331,7 +331,7 @@ xfs_qm_dqattach_locked( if (XFS_IS_UQUOTA_ON(mp) && !ip->i_udquot) { error = xfs_qm_dqattach_one(ip, i_uid_read(VFS_I(ip)), - XFS_DQ_USER, doalloc, &ip->i_udquot); + XFS_DQTYPE_USER, doalloc, &ip->i_udquot); if (error) goto done; ASSERT(ip->i_udquot); @@ -339,14 +339,14 @@ xfs_qm_dqattach_locked( if (XFS_IS_GQUOTA_ON(mp) && !ip->i_gdquot) { error = xfs_qm_dqattach_one(ip, i_gid_read(VFS_I(ip)), - XFS_DQ_GROUP, doalloc, &ip->i_gdquot); + XFS_DQTYPE_GROUP, doalloc, &ip->i_gdquot); if (error) goto done; ASSERT(ip->i_gdquot); } if (XFS_IS_PQUOTA_ON(mp) && !ip->i_pdquot) { - error = xfs_qm_dqattach_one(ip, ip->i_d.di_projid, XFS_DQ_PROJ, + error = xfs_qm_dqattach_one(ip, ip->i_d.di_projid, XFS_DQTYPE_PROJ, doalloc, &ip->i_pdquot); if (error) goto done; @@ -664,16 +664,16 @@ xfs_qm_init_quotainfo( mp->m_qflags |= (mp->m_sb.sb_qflags & XFS_ALL_QUOTA_CHKD); - xfs_qm_init_timelimits(mp, XFS_DQ_USER); - xfs_qm_init_timelimits(mp, XFS_DQ_GROUP); - xfs_qm_init_timelimits(mp, XFS_DQ_PROJ); + xfs_qm_init_timelimits(mp, XFS_DQTYPE_USER); + xfs_qm_init_timelimits(mp, XFS_DQTYPE_GROUP); + xfs_qm_init_timelimits(mp, XFS_DQTYPE_PROJ); if (XFS_IS_UQUOTA_RUNNING(mp)) - xfs_qm_set_defquota(mp, XFS_DQ_USER, qinf); + xfs_qm_set_defquota(mp, XFS_DQTYPE_USER, qinf); if (XFS_IS_GQUOTA_RUNNING(mp)) - xfs_qm_set_defquota(mp, XFS_DQ_GROUP, qinf); + xfs_qm_set_defquota(mp, XFS_DQTYPE_GROUP, qinf); if (XFS_IS_PQUOTA_RUNNING(mp)) - xfs_qm_set_defquota(mp, XFS_DQ_PROJ, qinf); + xfs_qm_set_defquota(mp, XFS_DQTYPE_PROJ, qinf); qinf->qi_shrinker.count_objects = xfs_qm_shrink_count; qinf->qi_shrinker.scan_objects = xfs_qm_shrink_scan; @@ -855,7 +855,7 @@ xfs_qm_reset_dqcounts( * xfs_dquot_verify. */ if (xfs_dqblk_verify(mp, &dqb[j], id + j) || - (dqb[j].dd_diskdq.d_flags & XFS_DQ_ALLTYPES) != type) + (dqb[j].dd_diskdq.d_flags & XFS_DQTYPE_REC_MASK) != type) xfs_dqblk_repair(mp, &dqb[j], id + j, type); /* @@ -1176,21 +1176,21 @@ xfs_qm_dqusage_adjust( * and quotaoffs don't race. (Quotachecks happen at mount time only). */ if (XFS_IS_UQUOTA_ON(mp)) { - error = xfs_qm_quotacheck_dqadjust(ip, XFS_DQ_USER, nblks, + error = xfs_qm_quotacheck_dqadjust(ip, XFS_DQTYPE_USER, nblks, rtblks); if (error) goto error0; } if (XFS_IS_GQUOTA_ON(mp)) { - error = xfs_qm_quotacheck_dqadjust(ip, XFS_DQ_GROUP, nblks, + error = xfs_qm_quotacheck_dqadjust(ip, XFS_DQTYPE_GROUP, nblks, rtblks); if (error) goto error0; } if (XFS_IS_PQUOTA_ON(mp)) { - error = xfs_qm_quotacheck_dqadjust(ip, XFS_DQ_PROJ, nblks, + error = xfs_qm_quotacheck_dqadjust(ip, XFS_DQTYPE_PROJ, nblks, rtblks); if (error) goto error0; @@ -1281,7 +1281,7 @@ xfs_qm_quotacheck( * We don't log our changes till later. */ if (uip) { - error = xfs_qm_reset_dqcounts_buf(mp, uip, XFS_DQ_USER, + error = xfs_qm_reset_dqcounts_buf(mp, uip, XFS_DQTYPE_USER, &buffer_list); if (error) goto error_return; @@ -1289,7 +1289,7 @@ xfs_qm_quotacheck( } if (gip) { - error = xfs_qm_reset_dqcounts_buf(mp, gip, XFS_DQ_GROUP, + error = xfs_qm_reset_dqcounts_buf(mp, gip, XFS_DQTYPE_GROUP, &buffer_list); if (error) goto error_return; @@ -1297,7 +1297,7 @@ xfs_qm_quotacheck( } if (pip) { - error = xfs_qm_reset_dqcounts_buf(mp, pip, XFS_DQ_PROJ, + error = xfs_qm_reset_dqcounts_buf(mp, pip, XFS_DQTYPE_PROJ, &buffer_list); if (error) goto error_return; @@ -1314,17 +1314,17 @@ xfs_qm_quotacheck( * down to disk buffers if everything was updated successfully. */ if (XFS_IS_UQUOTA_ON(mp)) { - error = xfs_qm_dquot_walk(mp, XFS_DQ_USER, xfs_qm_flush_one, + error = xfs_qm_dquot_walk(mp, XFS_DQTYPE_USER, xfs_qm_flush_one, &buffer_list); } if (XFS_IS_GQUOTA_ON(mp)) { - error2 = xfs_qm_dquot_walk(mp, XFS_DQ_GROUP, xfs_qm_flush_one, + error2 = xfs_qm_dquot_walk(mp, XFS_DQTYPE_GROUP, xfs_qm_flush_one, &buffer_list); if (!error) error = error2; } if (XFS_IS_PQUOTA_ON(mp)) { - error2 = xfs_qm_dquot_walk(mp, XFS_DQ_PROJ, xfs_qm_flush_one, + error2 = xfs_qm_dquot_walk(mp, XFS_DQTYPE_PROJ, xfs_qm_flush_one, &buffer_list); if (!error) error = error2; @@ -1662,7 +1662,7 @@ xfs_qm_vop_dqalloc( */ xfs_iunlock(ip, lockflags); error = xfs_qm_dqget(mp, from_kuid(user_ns, uid), - XFS_DQ_USER, true, &uq); + XFS_DQTYPE_USER, true, &uq); if (error) { ASSERT(error != -ENOENT); return error; @@ -1686,7 +1686,7 @@ xfs_qm_vop_dqalloc( if (!gid_eq(inode->i_gid, gid)) { xfs_iunlock(ip, lockflags); error = xfs_qm_dqget(mp, from_kgid(user_ns, gid), - XFS_DQ_GROUP, true, &gq); + XFS_DQTYPE_GROUP, true, &gq); if (error) { ASSERT(error != -ENOENT); goto error_rele; @@ -1702,8 +1702,8 @@ xfs_qm_vop_dqalloc( if ((flags & XFS_QMOPT_PQUOTA) && XFS_IS_PQUOTA_ON(mp)) { if (ip->i_d.di_projid != prid) { xfs_iunlock(ip, lockflags); - error = xfs_qm_dqget(mp, (xfs_dqid_t)prid, XFS_DQ_PROJ, - true, &pq); + error = xfs_qm_dqget(mp, (xfs_dqid_t)prid, + XFS_DQTYPE_PROJ, true, &pq); if (error) { ASSERT(error != -ENOENT); goto error_rele; diff --git a/fs/xfs/xfs_qm.h b/fs/xfs/xfs_qm.h index 11c28ff0298c2..21bc67d4962c0 100644 --- a/fs/xfs/xfs_qm.h +++ b/fs/xfs/xfs_qm.h @@ -73,11 +73,11 @@ xfs_dquot_tree( int type) { switch (type) { - case XFS_DQ_USER: + case XFS_DQTYPE_USER: return &qi->qi_uquota_tree; - case XFS_DQ_GROUP: + case XFS_DQTYPE_GROUP: return &qi->qi_gquota_tree; - case XFS_DQ_PROJ: + case XFS_DQTYPE_PROJ: return &qi->qi_pquota_tree; default: ASSERT(0); @@ -88,12 +88,12 @@ xfs_dquot_tree( static inline struct xfs_inode * xfs_quota_inode(xfs_mount_t *mp, uint dq_flags) { - switch (dq_flags & XFS_DQ_ALLTYPES) { - case XFS_DQ_USER: + switch (dq_flags & XFS_DQTYPE_REC_MASK) { + case XFS_DQTYPE_USER: return mp->m_quotainfo->qi_uquotaip; - case XFS_DQ_GROUP: + case XFS_DQTYPE_GROUP: return mp->m_quotainfo->qi_gquotaip; - case XFS_DQ_PROJ: + case XFS_DQTYPE_PROJ: return mp->m_quotainfo->qi_pquotaip; default: ASSERT(0); @@ -105,11 +105,11 @@ static inline int xfs_dquot_type(struct xfs_dquot *dqp) { if (XFS_QM_ISUDQ(dqp)) - return XFS_DQ_USER; + return XFS_DQTYPE_USER; if (XFS_QM_ISGDQ(dqp)) - return XFS_DQ_GROUP; + return XFS_DQTYPE_GROUP; ASSERT(XFS_QM_ISPDQ(dqp)); - return XFS_DQ_PROJ; + return XFS_DQTYPE_PROJ; } extern void xfs_trans_mod_dquot(struct xfs_trans *tp, struct xfs_dquot *dqp, @@ -166,11 +166,11 @@ static inline struct xfs_def_quota * xfs_get_defquota(struct xfs_quotainfo *qi, int type) { switch (type) { - case XFS_DQ_USER: + case XFS_DQTYPE_USER: return &qi->qi_usr_default; - case XFS_DQ_GROUP: + case XFS_DQTYPE_GROUP: return &qi->qi_grp_default; - case XFS_DQ_PROJ: + case XFS_DQTYPE_PROJ: return &qi->qi_prj_default; default: ASSERT(0); diff --git a/fs/xfs/xfs_qm_bhv.c b/fs/xfs/xfs_qm_bhv.c index 0993217e5ac86..639398091ad6b 100644 --- a/fs/xfs/xfs_qm_bhv.c +++ b/fs/xfs/xfs_qm_bhv.c @@ -60,7 +60,7 @@ xfs_qm_statvfs( struct xfs_mount *mp = ip->i_mount; struct xfs_dquot *dqp; - if (!xfs_qm_dqget(mp, ip->i_d.di_projid, XFS_DQ_PROJ, false, &dqp)) { + if (!xfs_qm_dqget(mp, ip->i_d.di_projid, XFS_DQTYPE_PROJ, false, &dqp)) { xfs_fill_statvfs_from_dquot(statp, dqp); xfs_qm_dqput(dqp); } diff --git a/fs/xfs/xfs_qm_syscalls.c b/fs/xfs/xfs_qm_syscalls.c index cbe352187d328..119c3d7d5f51b 100644 --- a/fs/xfs/xfs_qm_syscalls.c +++ b/fs/xfs/xfs_qm_syscalls.c @@ -660,18 +660,18 @@ xfs_qm_scall_getquota_fill_qc( * gets turned off. No need to confuse the user level code, * so return zeroes in that case. */ - if ((!XFS_IS_UQUOTA_ENFORCED(mp) && type == XFS_DQ_USER) || - (!XFS_IS_GQUOTA_ENFORCED(mp) && type == XFS_DQ_GROUP) || - (!XFS_IS_PQUOTA_ENFORCED(mp) && type == XFS_DQ_PROJ)) { + if ((!XFS_IS_UQUOTA_ENFORCED(mp) && type == XFS_DQTYPE_USER) || + (!XFS_IS_GQUOTA_ENFORCED(mp) && type == XFS_DQTYPE_GROUP) || + (!XFS_IS_PQUOTA_ENFORCED(mp) && type == XFS_DQTYPE_PROJ)) { dst->d_spc_timer = 0; dst->d_ino_timer = 0; dst->d_rt_spc_timer = 0; } #ifdef DEBUG - if (((XFS_IS_UQUOTA_ENFORCED(mp) && type == XFS_DQ_USER) || - (XFS_IS_GQUOTA_ENFORCED(mp) && type == XFS_DQ_GROUP) || - (XFS_IS_PQUOTA_ENFORCED(mp) && type == XFS_DQ_PROJ)) && + if (((XFS_IS_UQUOTA_ENFORCED(mp) && type == XFS_DQTYPE_USER) || + (XFS_IS_GQUOTA_ENFORCED(mp) && type == XFS_DQTYPE_GROUP) || + (XFS_IS_PQUOTA_ENFORCED(mp) && type == XFS_DQTYPE_PROJ)) && dqp->q_id != 0) { if ((dst->d_space > dst->d_spc_softlimit) && (dst->d_spc_softlimit > 0)) { diff --git a/fs/xfs/xfs_quota.h b/fs/xfs/xfs_quota.h index c92ae5e02ce86..0ae35fb5cb892 100644 --- a/fs/xfs/xfs_quota.h +++ b/fs/xfs/xfs_quota.h @@ -42,11 +42,11 @@ xfs_quota_chkd_flag( uint dqtype) { switch (dqtype) { - case XFS_DQ_USER: + case XFS_DQTYPE_USER: return XFS_UQUOTA_CHKD; - case XFS_DQ_GROUP: + case XFS_DQTYPE_GROUP: return XFS_GQUOTA_CHKD; - case XFS_DQ_PROJ: + case XFS_DQTYPE_PROJ: return XFS_PQUOTA_CHKD; default: return 0; diff --git a/fs/xfs/xfs_quotaops.c b/fs/xfs/xfs_quotaops.c index 299695a068f37..ba69906edecf7 100644 --- a/fs/xfs/xfs_quotaops.c +++ b/fs/xfs/xfs_quotaops.c @@ -90,11 +90,11 @@ xfs_quota_type(int type) { switch (type) { case USRQUOTA: - return XFS_DQ_USER; + return XFS_DQTYPE_USER; case GRPQUOTA: - return XFS_DQ_GROUP; + return XFS_DQTYPE_GROUP; default: - return XFS_DQ_PROJ; + return XFS_DQTYPE_PROJ; } } diff --git a/fs/xfs/xfs_trans_dquot.c b/fs/xfs/xfs_trans_dquot.c index a8f480e5401fc..ea61e279f8317 100644 --- a/fs/xfs/xfs_trans_dquot.c +++ b/fs/xfs/xfs_trans_dquot.c @@ -553,9 +553,9 @@ xfs_quota_warn( { enum quota_type qtype; - if (dqp->dq_flags & XFS_DQ_PROJ) + if (dqp->dq_flags & XFS_DQTYPE_PROJ) qtype = PRJQUOTA; - else if (dqp->dq_flags & XFS_DQ_USER) + else if (dqp->dq_flags & XFS_DQTYPE_USER) qtype = USRQUOTA; else qtype = GRPQUOTA; -- GitLab From dbcbc7b90e8a43aa00412bfa601a2d110a29086a Mon Sep 17 00:00:00 2001 From: "Darrick J. Wong" Date: Wed, 15 Jul 2020 17:48:31 -0700 Subject: [PATCH 1020/1476] xfs: refactor testing if a particular dquot is being enforced Create a small helper to test if enforcement is enabled for a given incore dquot and replace the open-code logic testing. Signed-off-by: Darrick J. Wong Reviewed-by: Dave Chinner Reviewed-by: Christoph Hellwig --- fs/xfs/xfs_dquot.h | 17 +++++++++++++++++ fs/xfs/xfs_qm_syscalls.c | 9 ++------- fs/xfs/xfs_trans_dquot.c | 4 +--- 3 files changed, 20 insertions(+), 10 deletions(-) diff --git a/fs/xfs/xfs_dquot.h b/fs/xfs/xfs_dquot.h index 17a21677723f6..fcf9bd6766150 100644 --- a/fs/xfs/xfs_dquot.h +++ b/fs/xfs/xfs_dquot.h @@ -156,6 +156,23 @@ static inline struct xfs_dquot *xfs_inode_dquot(struct xfs_inode *ip, int type) } } +/* Decide if the dquot's limits are actually being enforced. */ +static inline bool +xfs_dquot_is_enforced( + const struct xfs_dquot *dqp) +{ + switch (dqp->dq_flags & XFS_DQTYPE_REC_MASK) { + case XFS_DQTYPE_USER: + return XFS_IS_UQUOTA_ENFORCED(dqp->q_mount); + case XFS_DQTYPE_GROUP: + return XFS_IS_GQUOTA_ENFORCED(dqp->q_mount); + case XFS_DQTYPE_PROJ: + return XFS_IS_PQUOTA_ENFORCED(dqp->q_mount); + } + ASSERT(0); + return false; +} + /* * Check whether a dquot is under low free space conditions. We assume the quota * is enabled and enforced. diff --git a/fs/xfs/xfs_qm_syscalls.c b/fs/xfs/xfs_qm_syscalls.c index 119c3d7d5f51b..f7dbc702e4d67 100644 --- a/fs/xfs/xfs_qm_syscalls.c +++ b/fs/xfs/xfs_qm_syscalls.c @@ -660,19 +660,14 @@ xfs_qm_scall_getquota_fill_qc( * gets turned off. No need to confuse the user level code, * so return zeroes in that case. */ - if ((!XFS_IS_UQUOTA_ENFORCED(mp) && type == XFS_DQTYPE_USER) || - (!XFS_IS_GQUOTA_ENFORCED(mp) && type == XFS_DQTYPE_GROUP) || - (!XFS_IS_PQUOTA_ENFORCED(mp) && type == XFS_DQTYPE_PROJ)) { + if (!xfs_dquot_is_enforced(dqp)) { dst->d_spc_timer = 0; dst->d_ino_timer = 0; dst->d_rt_spc_timer = 0; } #ifdef DEBUG - if (((XFS_IS_UQUOTA_ENFORCED(mp) && type == XFS_DQTYPE_USER) || - (XFS_IS_GQUOTA_ENFORCED(mp) && type == XFS_DQTYPE_GROUP) || - (XFS_IS_PQUOTA_ENFORCED(mp) && type == XFS_DQTYPE_PROJ)) && - dqp->q_id != 0) { + if (xfs_dquot_is_enforced(dqp) && dqp->q_id != 0) { if ((dst->d_space > dst->d_spc_softlimit) && (dst->d_spc_softlimit > 0)) { ASSERT(dst->d_spc_timer != 0); diff --git a/fs/xfs/xfs_trans_dquot.c b/fs/xfs/xfs_trans_dquot.c index ea61e279f8317..d7d710d25bbd9 100644 --- a/fs/xfs/xfs_trans_dquot.c +++ b/fs/xfs/xfs_trans_dquot.c @@ -650,9 +650,7 @@ xfs_trans_dqresv( } if ((flags & XFS_QMOPT_FORCE_RES) == 0 && dqp->q_id && - ((XFS_IS_UQUOTA_ENFORCED(dqp->q_mount) && XFS_QM_ISUDQ(dqp)) || - (XFS_IS_GQUOTA_ENFORCED(dqp->q_mount) && XFS_QM_ISGDQ(dqp)) || - (XFS_IS_PQUOTA_ENFORCED(dqp->q_mount) && XFS_QM_ISPDQ(dqp)))) { + xfs_dquot_is_enforced(dqp)) { int quota_nl; bool fatal; -- GitLab From 00a342e4965ee91fff06e01949fa27db53c0bb1c Mon Sep 17 00:00:00 2001 From: "Darrick J. Wong" Date: Wed, 15 Jul 2020 17:47:13 -0700 Subject: [PATCH 1021/1476] xfs: remove the XFS_QM_IS[UGP]DQ macros Remove these macros and use xfs_dquot_type() for everything. Signed-off-by: Darrick J. Wong Reviewed-by: Dave Chinner Reviewed-by: Christoph Hellwig --- fs/xfs/xfs_dquot.h | 9 ++++++--- fs/xfs/xfs_qm.h | 11 ----------- fs/xfs/xfs_trans_dquot.c | 15 ++++++++++----- 3 files changed, 16 insertions(+), 19 deletions(-) diff --git a/fs/xfs/xfs_dquot.h b/fs/xfs/xfs_dquot.h index fcf9bd6766150..60bccb5f74356 100644 --- a/fs/xfs/xfs_dquot.h +++ b/fs/xfs/xfs_dquot.h @@ -128,6 +128,12 @@ static inline void xfs_dqunlock(struct xfs_dquot *dqp) mutex_unlock(&dqp->q_qlock); } +static inline int +xfs_dquot_type(const struct xfs_dquot *dqp) +{ + return dqp->dq_flags & XFS_DQTYPE_REC_MASK; +} + static inline int xfs_this_quota_on(struct xfs_mount *mp, int type) { switch (type & XFS_DQTYPE_REC_MASK) { @@ -192,9 +198,6 @@ void xfs_dquot_to_disk(struct xfs_disk_dquot *ddqp, struct xfs_dquot *dqp); #define XFS_DQ_IS_LOCKED(dqp) (mutex_is_locked(&((dqp)->q_qlock))) #define XFS_DQ_IS_DIRTY(dqp) ((dqp)->q_flags & XFS_DQFLAG_DIRTY) -#define XFS_QM_ISUDQ(dqp) ((dqp)->dq_flags & XFS_DQTYPE_USER) -#define XFS_QM_ISPDQ(dqp) ((dqp)->dq_flags & XFS_DQTYPE_PROJ) -#define XFS_QM_ISGDQ(dqp) ((dqp)->dq_flags & XFS_DQTYPE_GROUP) void xfs_qm_dqdestroy(struct xfs_dquot *dqp); int xfs_qm_dqflush(struct xfs_dquot *dqp, struct xfs_buf **bpp); diff --git a/fs/xfs/xfs_qm.h b/fs/xfs/xfs_qm.h index 21bc67d4962c0..f04af35349d7c 100644 --- a/fs/xfs/xfs_qm.h +++ b/fs/xfs/xfs_qm.h @@ -101,17 +101,6 @@ xfs_quota_inode(xfs_mount_t *mp, uint dq_flags) return NULL; } -static inline int -xfs_dquot_type(struct xfs_dquot *dqp) -{ - if (XFS_QM_ISUDQ(dqp)) - return XFS_DQTYPE_USER; - if (XFS_QM_ISGDQ(dqp)) - return XFS_DQTYPE_GROUP; - ASSERT(XFS_QM_ISPDQ(dqp)); - return XFS_DQTYPE_PROJ; -} - extern void xfs_trans_mod_dquot(struct xfs_trans *tp, struct xfs_dquot *dqp, uint field, int64_t delta); extern void xfs_trans_dqjoin(struct xfs_trans *, struct xfs_dquot *); diff --git a/fs/xfs/xfs_trans_dquot.c b/fs/xfs/xfs_trans_dquot.c index d7d710d25bbd9..19d3e283aafaa 100644 --- a/fs/xfs/xfs_trans_dquot.c +++ b/fs/xfs/xfs_trans_dquot.c @@ -156,14 +156,19 @@ xfs_trans_get_dqtrx( int i; struct xfs_dqtrx *qa; - if (XFS_QM_ISUDQ(dqp)) + switch (xfs_dquot_type(dqp)) { + case XFS_DQTYPE_USER: qa = tp->t_dqinfo->dqs[XFS_QM_TRANS_USR]; - else if (XFS_QM_ISGDQ(dqp)) + break; + case XFS_DQTYPE_GROUP: qa = tp->t_dqinfo->dqs[XFS_QM_TRANS_GRP]; - else if (XFS_QM_ISPDQ(dqp)) + break; + case XFS_DQTYPE_PROJ: qa = tp->t_dqinfo->dqs[XFS_QM_TRANS_PRJ]; - else + break; + default: return NULL; + } for (i = 0; i < XFS_QM_TRANS_MAXDQS; i++) { if (qa[i].qt_dquot == NULL || @@ -713,7 +718,7 @@ xfs_trans_dqresv( error_return: xfs_dqunlock(dqp); - if (XFS_QM_ISPDQ(dqp)) + if (xfs_dquot_type(dqp) == XFS_DQTYPE_PROJ) return -ENOSPC; return -EDQUOT; } -- GitLab From e6eb603c7e4f39cbd41042c7ceb7141dffc08096 Mon Sep 17 00:00:00 2001 From: "Darrick J. Wong" Date: Wed, 15 Jul 2020 17:50:57 -0700 Subject: [PATCH 1022/1476] xfs: refactor quota type testing Certain functions can only act upon one quota type, so refactor those functions to use switch statements, in keeping with all the other high level xfs quota api calls. Signed-off-by: Darrick J. Wong Reviewed-by: Dave Chinner Reviewed-by: Christoph Hellwig --- fs/xfs/xfs_dquot.c | 29 ++++++++++++++++++----------- fs/xfs/xfs_trans_dquot.c | 15 +++++++++++---- 2 files changed, 29 insertions(+), 15 deletions(-) diff --git a/fs/xfs/xfs_dquot.c b/fs/xfs/xfs_dquot.c index 4053e7e390f19..ce946d53bb613 100644 --- a/fs/xfs/xfs_dquot.c +++ b/fs/xfs/xfs_dquot.c @@ -171,6 +171,24 @@ xfs_qm_init_dquot_blk( ASSERT(tp); ASSERT(xfs_buf_islocked(bp)); + switch (type) { + case XFS_DQTYPE_USER: + qflag = XFS_UQUOTA_CHKD; + blftype = XFS_BLF_UDQUOT_BUF; + break; + case XFS_DQTYPE_PROJ: + qflag = XFS_PQUOTA_CHKD; + blftype = XFS_BLF_PDQUOT_BUF; + break; + case XFS_DQTYPE_GROUP: + qflag = XFS_GQUOTA_CHKD; + blftype = XFS_BLF_GDQUOT_BUF; + break; + default: + ASSERT(0); + return; + } + d = bp->b_addr; /* @@ -190,17 +208,6 @@ xfs_qm_init_dquot_blk( } } - if (type & XFS_DQTYPE_USER) { - qflag = XFS_UQUOTA_CHKD; - blftype = XFS_BLF_UDQUOT_BUF; - } else if (type & XFS_DQTYPE_PROJ) { - qflag = XFS_PQUOTA_CHKD; - blftype = XFS_BLF_PDQUOT_BUF; - } else { - qflag = XFS_GQUOTA_CHKD; - blftype = XFS_BLF_GDQUOT_BUF; - } - xfs_trans_dquot_buf(tp, bp, blftype); /* diff --git a/fs/xfs/xfs_trans_dquot.c b/fs/xfs/xfs_trans_dquot.c index 19d3e283aafaa..518cf0347891c 100644 --- a/fs/xfs/xfs_trans_dquot.c +++ b/fs/xfs/xfs_trans_dquot.c @@ -556,14 +556,21 @@ xfs_quota_warn( struct xfs_dquot *dqp, int type) { - enum quota_type qtype; + enum quota_type qtype; - if (dqp->dq_flags & XFS_DQTYPE_PROJ) + switch (xfs_dquot_type(dqp)) { + case XFS_DQTYPE_PROJ: qtype = PRJQUOTA; - else if (dqp->dq_flags & XFS_DQTYPE_USER) + break; + case XFS_DQTYPE_USER: qtype = USRQUOTA; - else + break; + case XFS_DQTYPE_GROUP: qtype = GRPQUOTA; + break; + default: + return; + } quota_send_warning(make_kqid(&init_user_ns, qtype, dqp->q_id), mp->m_super->s_dev, type); -- GitLab From 0b04dd5d7ca79ccb88e258641b328fe4f548272a Mon Sep 17 00:00:00 2001 From: "Darrick J. Wong" Date: Wed, 15 Jul 2020 17:51:47 -0700 Subject: [PATCH 1023/1476] xfs: always use xfs_dquot_type when extracting type from a dquot Always use the xfs_dquot_type helper to extract the quota type from an incore dquot. This moves responsibility for filtering internal state information and whatnot to anybody passing around a struct xfs_dquot. Signed-off-by: Darrick J. Wong Reviewed-by: Dave Chinner Reviewed-by: Christoph Hellwig --- fs/xfs/xfs_dquot.c | 15 ++++++++------- fs/xfs/xfs_dquot.h | 2 +- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/fs/xfs/xfs_dquot.c b/fs/xfs/xfs_dquot.c index ce946d53bb613..5a60238fcdbaf 100644 --- a/fs/xfs/xfs_dquot.c +++ b/fs/xfs/xfs_dquot.c @@ -273,14 +273,15 @@ xfs_dquot_disk_alloc( struct xfs_trans *tp = *tpp; struct xfs_mount *mp = tp->t_mountp; struct xfs_buf *bp; - struct xfs_inode *quotip = xfs_quota_inode(mp, dqp->dq_flags); + uint qtype = xfs_dquot_type(dqp); + struct xfs_inode *quotip = xfs_quota_inode(mp, qtype); int nmaps = 1; int error; trace_xfs_dqalloc(dqp); xfs_ilock(quotip, XFS_ILOCK_EXCL); - if (!xfs_this_quota_on(dqp->q_mount, dqp->dq_flags)) { + if (!xfs_this_quota_on(dqp->q_mount, qtype)) { /* * Return if this type of quotas is turned off while we didn't * have an inode lock @@ -317,8 +318,7 @@ xfs_dquot_disk_alloc( * Make a chunk of dquots out of this buffer and log * the entire thing. */ - xfs_qm_init_dquot_blk(tp, mp, dqp->q_id, - dqp->dq_flags & XFS_DQTYPE_REC_MASK, bp); + xfs_qm_init_dquot_blk(tp, mp, dqp->q_id, qtype, bp); xfs_buf_set_ref(bp, XFS_DQUOT_REF); /* @@ -365,13 +365,14 @@ xfs_dquot_disk_read( { struct xfs_bmbt_irec map; struct xfs_buf *bp; - struct xfs_inode *quotip = xfs_quota_inode(mp, dqp->dq_flags); + uint qtype = xfs_dquot_type(dqp); + struct xfs_inode *quotip = xfs_quota_inode(mp, qtype); uint lock_mode; int nmaps = 1; int error; lock_mode = xfs_ilock_data_map_shared(quotip); - if (!xfs_this_quota_on(mp, dqp->dq_flags)) { + if (!xfs_this_quota_on(mp, qtype)) { /* * Return if this type of quotas is turned off while we * didn't have the quota inode lock. @@ -487,7 +488,7 @@ xfs_dquot_from_disk( * Ensure that we got the type and ID we were looking for. * Everything else was checked by the dquot buffer verifier. */ - if ((ddqp->d_flags & XFS_DQTYPE_REC_MASK) != dqp->dq_flags || + if ((ddqp->d_flags & XFS_DQTYPE_REC_MASK) != xfs_dquot_type(dqp) || be32_to_cpu(ddqp->d_id) != dqp->q_id) { xfs_alert_tag(bp->b_mount, XFS_PTAG_VERIFIER_ERROR, "Metadata corruption detected at %pS, quota %u", diff --git a/fs/xfs/xfs_dquot.h b/fs/xfs/xfs_dquot.h index 60bccb5f74356..07e18ce33560d 100644 --- a/fs/xfs/xfs_dquot.h +++ b/fs/xfs/xfs_dquot.h @@ -167,7 +167,7 @@ static inline bool xfs_dquot_is_enforced( const struct xfs_dquot *dqp) { - switch (dqp->dq_flags & XFS_DQTYPE_REC_MASK) { + switch (xfs_dquot_type(dqp)) { case XFS_DQTYPE_USER: return XFS_IS_UQUOTA_ENFORCED(dqp->q_mount); case XFS_DQTYPE_GROUP: -- GitLab From af1db8f12e2dc5a172f2e35cfcd8cc4cf57dbb6e Mon Sep 17 00:00:00 2001 From: "Darrick J. Wong" Date: Wed, 15 Jul 2020 17:52:57 -0700 Subject: [PATCH 1024/1476] xfs: remove unnecessary quota type masking When XFS' quota functions take a parameter for the quota type, they only care about the three quota record types (user, group, project). Internal state flags and whatnot should never be passed by callers and are an error. Now that we've moved responsibility for filtering out internal state to the callers, we can drop the masking everywhere else. In other words, if you call a quota function, you must only pass in one of XFS_DQTYPE_{USER,GROUP,PROJ}. Signed-off-by: Darrick J. Wong Reviewed-by: Dave Chinner Reviewed-by: Christoph Hellwig --- fs/xfs/xfs_dquot.h | 4 ++-- fs/xfs/xfs_qm.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/fs/xfs/xfs_dquot.h b/fs/xfs/xfs_dquot.h index 07e18ce33560d..81ba614439bd4 100644 --- a/fs/xfs/xfs_dquot.h +++ b/fs/xfs/xfs_dquot.h @@ -136,7 +136,7 @@ xfs_dquot_type(const struct xfs_dquot *dqp) static inline int xfs_this_quota_on(struct xfs_mount *mp, int type) { - switch (type & XFS_DQTYPE_REC_MASK) { + switch (type) { case XFS_DQTYPE_USER: return XFS_IS_UQUOTA_ON(mp); case XFS_DQTYPE_GROUP: @@ -150,7 +150,7 @@ static inline int xfs_this_quota_on(struct xfs_mount *mp, int type) static inline struct xfs_dquot *xfs_inode_dquot(struct xfs_inode *ip, int type) { - switch (type & XFS_DQTYPE_REC_MASK) { + switch (type) { case XFS_DQTYPE_USER: return ip->i_udquot; case XFS_DQTYPE_GROUP: diff --git a/fs/xfs/xfs_qm.h b/fs/xfs/xfs_qm.h index f04af35349d7c..fac6fa81f1fa4 100644 --- a/fs/xfs/xfs_qm.h +++ b/fs/xfs/xfs_qm.h @@ -88,7 +88,7 @@ xfs_dquot_tree( static inline struct xfs_inode * xfs_quota_inode(xfs_mount_t *mp, uint dq_flags) { - switch (dq_flags & XFS_DQTYPE_REC_MASK) { + switch (dq_flags) { case XFS_DQTYPE_USER: return mp->m_quotainfo->qi_uquotaip; case XFS_DQTYPE_GROUP: -- GitLab From 74ddd6b3dd553a48c294d671a6cfe02e57cfa4c7 Mon Sep 17 00:00:00 2001 From: "Darrick J. Wong" Date: Wed, 15 Jul 2020 17:53:18 -0700 Subject: [PATCH 1025/1476] xfs: replace a few open-coded XFS_DQTYPE_REC_MASK uses Fix a few places where we open-coded this mask constant. Signed-off-by: Darrick J. Wong Reviewed-by: Dave Chinner Reviewed-by: Christoph Hellwig --- fs/xfs/xfs_dquot_item_recover.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fs/xfs/xfs_dquot_item_recover.c b/fs/xfs/xfs_dquot_item_recover.c index d7eb85c7d394d..93178341569a7 100644 --- a/fs/xfs/xfs_dquot_item_recover.c +++ b/fs/xfs/xfs_dquot_item_recover.c @@ -39,7 +39,7 @@ xlog_recover_dquot_ra_pass2( if (item->ri_buf[1].i_len < sizeof(struct xfs_disk_dquot)) return; - type = recddq->d_flags & (XFS_DQTYPE_USER | XFS_DQTYPE_PROJ | XFS_DQTYPE_GROUP); + type = recddq->d_flags & XFS_DQTYPE_REC_MASK; ASSERT(type); if (log->l_quotaoffs_flag & type) return; @@ -91,7 +91,7 @@ xlog_recover_dquot_commit_pass2( /* * This type of quotas was turned off, so ignore this record. */ - type = recddq->d_flags & (XFS_DQTYPE_USER | XFS_DQTYPE_PROJ | XFS_DQTYPE_GROUP); + type = recddq->d_flags & XFS_DQTYPE_REC_MASK; ASSERT(type); if (log->l_quotaoffs_flag & type) return 0; -- GitLab From 1a7ed271653a4f418a6398465f861ee795d34468 Mon Sep 17 00:00:00 2001 From: "Darrick J. Wong" Date: Wed, 15 Jul 2020 17:53:43 -0700 Subject: [PATCH 1026/1476] xfs: create xfs_dqtype_t to represent quota types Create a new type (xfs_dqtype_t) to represent the type of an incore dquot (user, group, project, or none). Rename the incore dquot's dq_flags field to q_type. This allows us to replace all the "uint type" arguments to the quota functions with "xfs_dqtype_t type", to make it obvious when we're passing a quota type argument into a function. Signed-off-by: Darrick J. Wong Reviewed-by: Dave Chinner Reviewed-by: Christoph Hellwig --- fs/xfs/libxfs/xfs_dquot_buf.c | 2 +- fs/xfs/libxfs/xfs_format.h | 9 +++++++++ fs/xfs/libxfs/xfs_quota_defs.h | 23 +++++++++------------ fs/xfs/scrub/quota.c | 8 ++++---- fs/xfs/scrub/repair.c | 4 ++-- fs/xfs/scrub/repair.h | 4 +++- fs/xfs/xfs_dquot.c | 37 +++++++++++++++++----------------- fs/xfs/xfs_dquot.h | 33 +++++++++++++++--------------- fs/xfs/xfs_iomap.c | 24 +++++++++++----------- fs/xfs/xfs_qm.c | 22 ++++++++++---------- fs/xfs/xfs_qm.h | 26 +++++++++++++++--------- fs/xfs/xfs_qm_syscalls.c | 8 ++++---- fs/xfs/xfs_quota.h | 4 ++-- fs/xfs/xfs_quotaops.c | 2 +- fs/xfs/xfs_trace.h | 21 +++++++++++++------ 15 files changed, 126 insertions(+), 101 deletions(-) diff --git a/fs/xfs/libxfs/xfs_dquot_buf.c b/fs/xfs/libxfs/xfs_dquot_buf.c index 450147df30424..75c164ed141cb 100644 --- a/fs/xfs/libxfs/xfs_dquot_buf.c +++ b/fs/xfs/libxfs/xfs_dquot_buf.c @@ -109,7 +109,7 @@ xfs_dqblk_repair( struct xfs_mount *mp, struct xfs_dqblk *dqb, xfs_dqid_t id, - uint type) + xfs_dqtype_t type) { /* * Typically, a repair is only requested by quotacheck. diff --git a/fs/xfs/libxfs/xfs_format.h b/fs/xfs/libxfs/xfs_format.h index 5d5e0f5eda973..0fa969f6202c9 100644 --- a/fs/xfs/libxfs/xfs_format.h +++ b/fs/xfs/libxfs/xfs_format.h @@ -1149,6 +1149,15 @@ static inline void xfs_dinode_put_rdev(struct xfs_dinode *dip, xfs_dev_t rdev) #define XFS_DQUOT_MAGIC 0x4451 /* 'DQ' */ #define XFS_DQUOT_VERSION (uint8_t)0x01 /* latest version number */ +#define XFS_DQTYPE_USER 0x01 /* user dquot record */ +#define XFS_DQTYPE_PROJ 0x02 /* project dquot record */ +#define XFS_DQTYPE_GROUP 0x04 /* group dquot record */ + +/* bitmask to determine if this is a user/group/project dquot */ +#define XFS_DQTYPE_REC_MASK (XFS_DQTYPE_USER | \ + XFS_DQTYPE_PROJ | \ + XFS_DQTYPE_GROUP) + /* * This is the main portion of the on-disk representation of quota information * for a user. We pad this with some more expansion room to construct the on diff --git a/fs/xfs/libxfs/xfs_quota_defs.h b/fs/xfs/libxfs/xfs_quota_defs.h index baf6c4ad88afe..076bdc7037ee6 100644 --- a/fs/xfs/libxfs/xfs_quota_defs.h +++ b/fs/xfs/libxfs/xfs_quota_defs.h @@ -18,23 +18,20 @@ typedef uint64_t xfs_qcnt_t; typedef uint16_t xfs_qwarncnt_t; +typedef uint8_t xfs_dqtype_t; + +#define XFS_DQTYPE_STRINGS \ + { XFS_DQTYPE_USER, "USER" }, \ + { XFS_DQTYPE_PROJ, "PROJ" }, \ + { XFS_DQTYPE_GROUP, "GROUP" } + /* * flags for q_flags field in the dquot. */ -#define XFS_DQTYPE_USER 0x0001 /* a user quota */ -#define XFS_DQTYPE_PROJ 0x0002 /* project quota */ -#define XFS_DQTYPE_GROUP 0x0004 /* a group quota */ -#define XFS_DQFLAG_DIRTY 0x0008 /* dquot is dirty */ -#define XFS_DQFLAG_FREEING 0x0010 /* dquot is being torn down */ - -#define XFS_DQTYPE_REC_MASK (XFS_DQTYPE_USER | \ - XFS_DQTYPE_PROJ | \ - XFS_DQTYPE_GROUP) +#define XFS_DQFLAG_DIRTY (1 << 0) /* dquot is dirty */ +#define XFS_DQFLAG_FREEING (1 << 1) /* dquot is being torn down */ #define XFS_DQFLAG_STRINGS \ - { XFS_DQTYPE_USER, "USER" }, \ - { XFS_DQTYPE_PROJ, "PROJ" }, \ - { XFS_DQTYPE_GROUP, "GROUP" }, \ { XFS_DQFLAG_DIRTY, "DIRTY" }, \ { XFS_DQFLAG_FREEING, "FREEING" } @@ -144,6 +141,6 @@ extern xfs_failaddr_t xfs_dqblk_verify(struct xfs_mount *mp, struct xfs_dqblk *dqb, xfs_dqid_t id); extern int xfs_calc_dquots_per_chunk(unsigned int nbblks); extern void xfs_dqblk_repair(struct xfs_mount *mp, struct xfs_dqblk *dqb, - xfs_dqid_t id, uint type); + xfs_dqid_t id, xfs_dqtype_t type); #endif /* __XFS_QUOTA_H__ */ diff --git a/fs/xfs/scrub/quota.c b/fs/xfs/scrub/quota.c index 1db07485f1486..e34ca20ae8e40 100644 --- a/fs/xfs/scrub/quota.c +++ b/fs/xfs/scrub/quota.c @@ -18,7 +18,7 @@ #include "scrub/common.h" /* Convert a scrub type code to a DQ flag, or return 0 if error. */ -static inline uint +static inline xfs_dqtype_t xchk_quota_to_dqtype( struct xfs_scrub *sc) { @@ -40,7 +40,7 @@ xchk_setup_quota( struct xfs_scrub *sc, struct xfs_inode *ip) { - uint dqtype; + xfs_dqtype_t dqtype; int error; if (!XFS_IS_QUOTA_RUNNING(sc->mp) || !XFS_IS_QUOTA_ON(sc->mp)) @@ -73,7 +73,7 @@ struct xchk_quota_info { STATIC int xchk_quota_item( struct xfs_dquot *dq, - uint dqtype, + xfs_dqtype_t dqtype, void *priv) { struct xchk_quota_info *sqi = priv; @@ -214,7 +214,7 @@ xchk_quota( struct xchk_quota_info sqi; struct xfs_mount *mp = sc->mp; struct xfs_quotainfo *qi = mp->m_quotainfo; - uint dqtype; + xfs_dqtype_t dqtype; int error = 0; dqtype = xchk_quota_to_dqtype(sc); diff --git a/fs/xfs/scrub/repair.c b/fs/xfs/scrub/repair.c index 0746518965862..25e86c71e7b9a 100644 --- a/fs/xfs/scrub/repair.c +++ b/fs/xfs/scrub/repair.c @@ -899,11 +899,11 @@ xrep_find_ag_btree_roots( void xrep_force_quotacheck( struct xfs_scrub *sc, - uint dqtype) + xfs_dqtype_t type) { uint flag; - flag = xfs_quota_chkd_flag(dqtype); + flag = xfs_quota_chkd_flag(type); if (!(flag & sc->mp->m_qflags)) return; diff --git a/fs/xfs/scrub/repair.h b/fs/xfs/scrub/repair.h index 04a47d45605ba..fe77de01abe04 100644 --- a/fs/xfs/scrub/repair.h +++ b/fs/xfs/scrub/repair.h @@ -6,6 +6,8 @@ #ifndef __XFS_SCRUB_REPAIR_H__ #define __XFS_SCRUB_REPAIR_H__ +#include "xfs_quota_defs.h" + static inline int xrep_notsupported(struct xfs_scrub *sc) { return -EOPNOTSUPP; @@ -49,7 +51,7 @@ struct xrep_find_ag_btree { int xrep_find_ag_btree_roots(struct xfs_scrub *sc, struct xfs_buf *agf_bp, struct xrep_find_ag_btree *btree_info, struct xfs_buf *agfl_bp); -void xrep_force_quotacheck(struct xfs_scrub *sc, uint dqtype); +void xrep_force_quotacheck(struct xfs_scrub *sc, xfs_dqtype_t type); int xrep_ino_dqattach(struct xfs_scrub *sc); /* Metadata repairers */ diff --git a/fs/xfs/xfs_dquot.c b/fs/xfs/xfs_dquot.c index 5a60238fcdbaf..30cfa0c871754 100644 --- a/fs/xfs/xfs_dquot.c +++ b/fs/xfs/xfs_dquot.c @@ -158,7 +158,7 @@ xfs_qm_init_dquot_blk( struct xfs_trans *tp, struct xfs_mount *mp, xfs_dqid_t id, - uint type, + xfs_dqtype_t type, struct xfs_buf *bp) { struct xfs_quotainfo *q = mp->m_quotainfo; @@ -273,7 +273,7 @@ xfs_dquot_disk_alloc( struct xfs_trans *tp = *tpp; struct xfs_mount *mp = tp->t_mountp; struct xfs_buf *bp; - uint qtype = xfs_dquot_type(dqp); + xfs_dqtype_t qtype = xfs_dquot_type(dqp); struct xfs_inode *quotip = xfs_quota_inode(mp, qtype); int nmaps = 1; int error; @@ -365,7 +365,7 @@ xfs_dquot_disk_read( { struct xfs_bmbt_irec map; struct xfs_buf *bp; - uint qtype = xfs_dquot_type(dqp); + xfs_dqtype_t qtype = xfs_dquot_type(dqp); struct xfs_inode *quotip = xfs_quota_inode(mp, qtype); uint lock_mode; int nmaps = 1; @@ -424,13 +424,13 @@ STATIC struct xfs_dquot * xfs_dquot_alloc( struct xfs_mount *mp, xfs_dqid_t id, - uint type) + xfs_dqtype_t type) { struct xfs_dquot *dqp; dqp = kmem_zone_zalloc(xfs_qm_dqzone, 0); - dqp->dq_flags = type; + dqp->q_type = type; dqp->q_id = id; dqp->q_mount = mp; INIT_LIST_HEAD(&dqp->q_lru); @@ -498,6 +498,7 @@ xfs_dquot_from_disk( } /* copy everything from disk dquot to the incore dquot */ + dqp->q_type = ddqp->d_flags; dqp->q_blk.hardlimit = be64_to_cpu(ddqp->d_blk_hardlimit); dqp->q_blk.softlimit = be64_to_cpu(ddqp->d_blk_softlimit); dqp->q_ino.hardlimit = be64_to_cpu(ddqp->d_ino_hardlimit); @@ -538,7 +539,7 @@ xfs_dquot_to_disk( { ddqp->d_magic = cpu_to_be16(XFS_DQUOT_MAGIC); ddqp->d_version = XFS_DQUOT_VERSION; - ddqp->d_flags = dqp->dq_flags & XFS_DQTYPE_REC_MASK; + ddqp->d_flags = dqp->q_type; ddqp->d_id = cpu_to_be32(dqp->q_id); ddqp->d_pad0 = 0; ddqp->d_pad = 0; @@ -609,7 +610,7 @@ static int xfs_qm_dqread( struct xfs_mount *mp, xfs_dqid_t id, - uint type, + xfs_dqtype_t type, bool can_alloc, struct xfs_dquot **dqpp) { @@ -657,7 +658,7 @@ err: static int xfs_dq_get_next_id( struct xfs_mount *mp, - uint type, + xfs_dqtype_t type, xfs_dqid_t *id) { struct xfs_inode *quotip = xfs_quota_inode(mp, type); @@ -781,7 +782,7 @@ xfs_qm_dqget_cache_insert( static int xfs_qm_dqget_checks( struct xfs_mount *mp, - uint type) + xfs_dqtype_t type) { if (WARN_ON_ONCE(!XFS_IS_QUOTA_RUNNING(mp))) return -ESRCH; @@ -813,7 +814,7 @@ int xfs_qm_dqget( struct xfs_mount *mp, xfs_dqid_t id, - uint type, + xfs_dqtype_t type, bool can_alloc, struct xfs_dquot **O_dqpp) { @@ -863,7 +864,7 @@ int xfs_qm_dqget_uncached( struct xfs_mount *mp, xfs_dqid_t id, - uint type, + xfs_dqtype_t type, struct xfs_dquot **dqpp) { int error; @@ -879,7 +880,7 @@ xfs_qm_dqget_uncached( xfs_dqid_t xfs_qm_id_for_quotatype( struct xfs_inode *ip, - uint type) + xfs_dqtype_t type) { switch (type) { case XFS_DQTYPE_USER: @@ -901,7 +902,7 @@ xfs_qm_id_for_quotatype( int xfs_qm_dqget_inode( struct xfs_inode *ip, - uint type, + xfs_dqtype_t type, bool can_alloc, struct xfs_dquot **O_dqpp) { @@ -987,7 +988,7 @@ int xfs_qm_dqget_next( struct xfs_mount *mp, xfs_dqid_t id, - uint type, + xfs_dqtype_t type, struct xfs_dquot **dqpp) { struct xfs_dquot *dqp; @@ -1122,7 +1123,7 @@ static xfs_failaddr_t xfs_qm_dqflush_check( struct xfs_dquot *dqp) { - __u8 type = dqp->dq_flags & XFS_DQTYPE_REC_MASK; + xfs_dqtype_t type = xfs_dquot_type(dqp); if (type != XFS_DQTYPE_USER && type != XFS_DQTYPE_GROUP && @@ -1317,7 +1318,7 @@ xfs_qm_exit(void) int xfs_qm_dqiterate( struct xfs_mount *mp, - uint dqtype, + xfs_dqtype_t type, xfs_qm_dqiterate_fn iter_fn, void *priv) { @@ -1326,13 +1327,13 @@ xfs_qm_dqiterate( int error; do { - error = xfs_qm_dqget_next(mp, id, dqtype, &dq); + error = xfs_qm_dqget_next(mp, id, type, &dq); if (error == -ENOENT) return 0; if (error) return error; - error = iter_fn(dq, dqtype, priv); + error = iter_fn(dq, type, priv); id = dq->q_id; xfs_qm_dqput(dq); } while (error == 0 && id != 0); diff --git a/fs/xfs/xfs_dquot.h b/fs/xfs/xfs_dquot.h index 81ba614439bd4..282a65da93c79 100644 --- a/fs/xfs/xfs_dquot.h +++ b/fs/xfs/xfs_dquot.h @@ -60,7 +60,7 @@ struct xfs_dquot_res { struct xfs_dquot { struct list_head q_lru; struct xfs_mount *q_mount; - uint8_t dq_flags; + xfs_dqtype_t q_type; uint16_t q_flags; xfs_dqid_t q_id; uint q_nrefs; @@ -131,10 +131,10 @@ static inline void xfs_dqunlock(struct xfs_dquot *dqp) static inline int xfs_dquot_type(const struct xfs_dquot *dqp) { - return dqp->dq_flags & XFS_DQTYPE_REC_MASK; + return dqp->q_type & XFS_DQTYPE_REC_MASK; } -static inline int xfs_this_quota_on(struct xfs_mount *mp, int type) +static inline int xfs_this_quota_on(struct xfs_mount *mp, xfs_dqtype_t type) { switch (type) { case XFS_DQTYPE_USER: @@ -148,7 +148,9 @@ static inline int xfs_this_quota_on(struct xfs_mount *mp, int type) } } -static inline struct xfs_dquot *xfs_inode_dquot(struct xfs_inode *ip, int type) +static inline struct xfs_dquot *xfs_inode_dquot( + struct xfs_inode *ip, + xfs_dqtype_t type) { switch (type) { case XFS_DQTYPE_USER: @@ -205,18 +207,17 @@ void xfs_qm_dqunpin_wait(struct xfs_dquot *dqp); void xfs_qm_adjust_dqtimers(struct xfs_dquot *d); void xfs_qm_adjust_dqlimits(struct xfs_dquot *d); xfs_dqid_t xfs_qm_id_for_quotatype(struct xfs_inode *ip, - uint type); + xfs_dqtype_t type); int xfs_qm_dqget(struct xfs_mount *mp, xfs_dqid_t id, - uint type, bool can_alloc, - struct xfs_dquot **dqpp); -int xfs_qm_dqget_inode(struct xfs_inode *ip, uint type, - bool can_alloc, - struct xfs_dquot **dqpp); + xfs_dqtype_t type, bool can_alloc, + struct xfs_dquot **dqpp); +int xfs_qm_dqget_inode(struct xfs_inode *ip, xfs_dqtype_t type, + bool can_alloc, struct xfs_dquot **dqpp); int xfs_qm_dqget_next(struct xfs_mount *mp, xfs_dqid_t id, - uint type, struct xfs_dquot **dqpp); + xfs_dqtype_t type, struct xfs_dquot **dqpp); int xfs_qm_dqget_uncached(struct xfs_mount *mp, - xfs_dqid_t id, uint type, - struct xfs_dquot **dqpp); + xfs_dqid_t id, xfs_dqtype_t type, + struct xfs_dquot **dqpp); void xfs_qm_dqput(struct xfs_dquot *dqp); void xfs_dqlock2(struct xfs_dquot *, struct xfs_dquot *); @@ -231,9 +232,9 @@ static inline struct xfs_dquot *xfs_qm_dqhold(struct xfs_dquot *dqp) return dqp; } -typedef int (*xfs_qm_dqiterate_fn)(struct xfs_dquot *dq, uint dqtype, - void *priv); -int xfs_qm_dqiterate(struct xfs_mount *mp, uint dqtype, +typedef int (*xfs_qm_dqiterate_fn)(struct xfs_dquot *dq, + xfs_dqtype_t type, void *priv); +int xfs_qm_dqiterate(struct xfs_mount *mp, xfs_dqtype_t type, xfs_qm_dqiterate_fn iter_fn, void *priv); #endif /* __XFS_DQUOT_H__ */ diff --git a/fs/xfs/xfs_iomap.c b/fs/xfs/xfs_iomap.c index d3dc4106a35cd..0e3f62cde375f 100644 --- a/fs/xfs/xfs_iomap.c +++ b/fs/xfs/xfs_iomap.c @@ -293,11 +293,11 @@ out_trans_cancel: STATIC bool xfs_quota_need_throttle( - struct xfs_inode *ip, - int type, - xfs_fsblock_t alloc_blocks) + struct xfs_inode *ip, + xfs_dqtype_t type, + xfs_fsblock_t alloc_blocks) { - struct xfs_dquot *dq = xfs_inode_dquot(ip, type); + struct xfs_dquot *dq = xfs_inode_dquot(ip, type); if (!dq || !xfs_this_quota_on(ip->i_mount, type)) return false; @@ -315,15 +315,15 @@ xfs_quota_need_throttle( STATIC void xfs_quota_calc_throttle( - struct xfs_inode *ip, - int type, - xfs_fsblock_t *qblocks, - int *qshift, - int64_t *qfreesp) + struct xfs_inode *ip, + xfs_dqtype_t type, + xfs_fsblock_t *qblocks, + int *qshift, + int64_t *qfreesp) { - int64_t freesp; - int shift = 0; - struct xfs_dquot *dq = xfs_inode_dquot(ip, type); + struct xfs_dquot *dq = xfs_inode_dquot(ip, type); + int64_t freesp; + int shift = 0; /* no dq, or over hi wmark, squash the prealloc completely */ if (!dq || dq->q_blk.reserved >= dq->q_prealloc_hi_wmark) { diff --git a/fs/xfs/xfs_qm.c b/fs/xfs/xfs_qm.c index 47d4b6937c84c..123757717e21b 100644 --- a/fs/xfs/xfs_qm.c +++ b/fs/xfs/xfs_qm.c @@ -47,7 +47,7 @@ STATIC void xfs_qm_dqfree_one(struct xfs_dquot *dqp); STATIC int xfs_qm_dquot_walk( struct xfs_mount *mp, - int type, + xfs_dqtype_t type, int (*execute)(struct xfs_dquot *dqp, void *data), void *data) { @@ -250,7 +250,7 @@ STATIC int xfs_qm_dqattach_one( struct xfs_inode *ip, xfs_dqid_t id, - uint type, + xfs_dqtype_t type, bool doalloc, struct xfs_dquot **IO_idqpp) { @@ -545,7 +545,7 @@ xfs_qm_shrink_count( STATIC void xfs_qm_set_defquota( struct xfs_mount *mp, - uint type, + xfs_dqtype_t type, struct xfs_quotainfo *qinf) { struct xfs_dquot *dqp; @@ -575,7 +575,7 @@ xfs_qm_set_defquota( static void xfs_qm_init_timelimits( struct xfs_mount *mp, - uint type) + xfs_dqtype_t type) { struct xfs_quotainfo *qinf = mp->m_quotainfo; struct xfs_def_quota *defq; @@ -823,10 +823,10 @@ xfs_qm_qino_alloc( STATIC void xfs_qm_reset_dqcounts( - xfs_mount_t *mp, - xfs_buf_t *bp, - xfs_dqid_t id, - uint type) + struct xfs_mount *mp, + struct xfs_buf *bp, + xfs_dqid_t id, + xfs_dqtype_t type) { struct xfs_dqblk *dqb; int j; @@ -895,7 +895,7 @@ xfs_qm_reset_dqcounts_all( xfs_dqid_t firstid, xfs_fsblock_t bno, xfs_filblks_t blkcnt, - uint type, + xfs_dqtype_t type, struct list_head *buffer_list) { struct xfs_buf *bp; @@ -961,7 +961,7 @@ STATIC int xfs_qm_reset_dqcounts_buf( struct xfs_mount *mp, struct xfs_inode *qip, - uint type, + xfs_dqtype_t type, struct list_head *buffer_list) { struct xfs_bmbt_irec *map; @@ -1059,7 +1059,7 @@ out: STATIC int xfs_qm_quotacheck_dqadjust( struct xfs_inode *ip, - uint type, + xfs_dqtype_t type, xfs_qcnt_t nblks, xfs_qcnt_t rtblks) { diff --git a/fs/xfs/xfs_qm.h b/fs/xfs/xfs_qm.h index fac6fa81f1fa4..9c078c35d924d 100644 --- a/fs/xfs/xfs_qm.h +++ b/fs/xfs/xfs_qm.h @@ -70,7 +70,7 @@ struct xfs_quotainfo { static inline struct radix_tree_root * xfs_dquot_tree( struct xfs_quotainfo *qi, - int type) + xfs_dqtype_t type) { switch (type) { case XFS_DQTYPE_USER: @@ -86,9 +86,9 @@ xfs_dquot_tree( } static inline struct xfs_inode * -xfs_quota_inode(xfs_mount_t *mp, uint dq_flags) +xfs_quota_inode(struct xfs_mount *mp, xfs_dqtype_t type) { - switch (dq_flags) { + switch (type) { case XFS_DQTYPE_USER: return mp->m_quotainfo->qi_uquotaip; case XFS_DQTYPE_GROUP: @@ -142,17 +142,23 @@ extern void xfs_qm_dqrele_all_inodes(struct xfs_mount *, uint); /* quota ops */ extern int xfs_qm_scall_trunc_qfiles(struct xfs_mount *, uint); -extern int xfs_qm_scall_getquota(struct xfs_mount *, xfs_dqid_t, - uint, struct qc_dqblk *); -extern int xfs_qm_scall_getquota_next(struct xfs_mount *, - xfs_dqid_t *, uint, struct qc_dqblk *); -extern int xfs_qm_scall_setqlim(struct xfs_mount *, xfs_dqid_t, uint, - struct qc_dqblk *); +extern int xfs_qm_scall_getquota(struct xfs_mount *mp, + xfs_dqid_t id, + xfs_dqtype_t type, + struct qc_dqblk *dst); +extern int xfs_qm_scall_getquota_next(struct xfs_mount *mp, + xfs_dqid_t *id, + xfs_dqtype_t type, + struct qc_dqblk *dst); +extern int xfs_qm_scall_setqlim(struct xfs_mount *mp, + xfs_dqid_t id, + xfs_dqtype_t type, + struct qc_dqblk *newlim); extern int xfs_qm_scall_quotaon(struct xfs_mount *, uint); extern int xfs_qm_scall_quotaoff(struct xfs_mount *, uint); static inline struct xfs_def_quota * -xfs_get_defquota(struct xfs_quotainfo *qi, int type) +xfs_get_defquota(struct xfs_quotainfo *qi, xfs_dqtype_t type) { switch (type) { case XFS_DQTYPE_USER: diff --git a/fs/xfs/xfs_qm_syscalls.c b/fs/xfs/xfs_qm_syscalls.c index f7dbc702e4d67..1c542b4a52204 100644 --- a/fs/xfs/xfs_qm_syscalls.c +++ b/fs/xfs/xfs_qm_syscalls.c @@ -495,7 +495,7 @@ int xfs_qm_scall_setqlim( struct xfs_mount *mp, xfs_dqid_t id, - uint type, + xfs_dqtype_t type, struct qc_dqblk *newlim) { struct xfs_quotainfo *q = mp->m_quotainfo; @@ -634,7 +634,7 @@ out_unlock: static void xfs_qm_scall_getquota_fill_qc( struct xfs_mount *mp, - uint type, + xfs_dqtype_t type, const struct xfs_dquot *dqp, struct qc_dqblk *dst) { @@ -685,7 +685,7 @@ int xfs_qm_scall_getquota( struct xfs_mount *mp, xfs_dqid_t id, - uint type, + xfs_dqtype_t type, struct qc_dqblk *dst) { struct xfs_dquot *dqp; @@ -723,7 +723,7 @@ int xfs_qm_scall_getquota_next( struct xfs_mount *mp, xfs_dqid_t *id, - uint type, + xfs_dqtype_t type, struct qc_dqblk *dst) { struct xfs_dquot *dqp; diff --git a/fs/xfs/xfs_quota.h b/fs/xfs/xfs_quota.h index 0ae35fb5cb892..06b22e35fc902 100644 --- a/fs/xfs/xfs_quota.h +++ b/fs/xfs/xfs_quota.h @@ -39,9 +39,9 @@ struct xfs_buf; static inline uint xfs_quota_chkd_flag( - uint dqtype) + xfs_dqtype_t type) { - switch (dqtype) { + switch (type) { case XFS_DQTYPE_USER: return XFS_UQUOTA_CHKD; case XFS_DQTYPE_GROUP: diff --git a/fs/xfs/xfs_quotaops.c b/fs/xfs/xfs_quotaops.c index ba69906edecf7..d27c0e852c0b0 100644 --- a/fs/xfs/xfs_quotaops.c +++ b/fs/xfs/xfs_quotaops.c @@ -85,7 +85,7 @@ xfs_fs_get_quota_state( return 0; } -STATIC int +STATIC xfs_dqtype_t xfs_quota_type(int type) { switch (type) { diff --git a/fs/xfs/xfs_trace.h b/fs/xfs/xfs_trace.h index 81534095f52be..e9b2ce0948b66 100644 --- a/fs/xfs/xfs_trace.h +++ b/fs/xfs/xfs_trace.h @@ -865,6 +865,7 @@ DECLARE_EVENT_CLASS(xfs_dquot_class, TP_STRUCT__entry( __field(dev_t, dev) __field(u32, id) + __field(xfs_dqtype_t, type) __field(unsigned, flags) __field(unsigned, nrefs) __field(unsigned long long, res_bcount) @@ -885,7 +886,8 @@ DECLARE_EVENT_CLASS(xfs_dquot_class, TP_fast_assign( __entry->dev = dqp->q_mount->m_super->s_dev; __entry->id = dqp->q_id; - __entry->flags = dqp->dq_flags | dqp->q_flags; + __entry->type = dqp->q_type; + __entry->flags = dqp->q_flags; __entry->nrefs = dqp->q_nrefs; __entry->res_bcount = dqp->q_blk.reserved; @@ -903,13 +905,14 @@ DECLARE_EVENT_CLASS(xfs_dquot_class, __entry->ino_hardlimit = dqp->q_ino.hardlimit; __entry->ino_softlimit = dqp->q_ino.softlimit; ), - TP_printk("dev %d:%d id 0x%x flags %s nrefs %u " + TP_printk("dev %d:%d id 0x%x type %s flags %s nrefs %u " "res_bc 0x%llx res_rtbc 0x%llx res_ic 0x%llx " "bcnt 0x%llx bhardlimit 0x%llx bsoftlimit 0x%llx " "rtbcnt 0x%llx rtbhardlimit 0x%llx rtbsoftlimit 0x%llx " "icnt 0x%llx ihardlimit 0x%llx isoftlimit 0x%llx]", MAJOR(__entry->dev), MINOR(__entry->dev), __entry->id, + __print_flags(__entry->type, "|", XFS_DQTYPE_STRINGS), __print_flags(__entry->flags, "|", XFS_DQFLAG_STRINGS), __entry->nrefs, __entry->res_bcount, @@ -976,6 +979,7 @@ TRACE_EVENT(xfs_trans_mod_dquot, TP_ARGS(tp, dqp, field, delta), TP_STRUCT__entry( __field(dev_t, dev) + __field(xfs_dqtype_t, type) __field(unsigned int, flags) __field(unsigned int, dqid) __field(unsigned int, field) @@ -983,14 +987,16 @@ TRACE_EVENT(xfs_trans_mod_dquot, ), TP_fast_assign( __entry->dev = tp->t_mountp->m_super->s_dev; - __entry->flags = dqp->dq_flags | dqp->q_flags; + __entry->type = dqp->q_type; + __entry->flags = dqp->q_flags; __entry->dqid = dqp->q_id; __entry->field = field; __entry->delta = delta; ), - TP_printk("dev %d:%d dquot id 0x%x flags %s field %s delta %lld", + TP_printk("dev %d:%d dquot id 0x%x type %s flags %s field %s delta %lld", MAJOR(__entry->dev), MINOR(__entry->dev), __entry->dqid, + __print_flags(__entry->type, "|", XFS_DQTYPE_STRINGS), __print_flags(__entry->flags, "|", XFS_DQFLAG_STRINGS), __print_flags(__entry->field, "|", XFS_QMOPT_FLAGS), __entry->delta) @@ -1001,6 +1007,7 @@ DECLARE_EVENT_CLASS(xfs_dqtrx_class, TP_ARGS(qtrx), TP_STRUCT__entry( __field(dev_t, dev) + __field(xfs_dqtype_t, type) __field(unsigned int, flags) __field(u32, dqid) @@ -1019,7 +1026,8 @@ DECLARE_EVENT_CLASS(xfs_dqtrx_class, ), TP_fast_assign( __entry->dev = qtrx->qt_dquot->q_mount->m_super->s_dev; - __entry->flags = qtrx->qt_dquot->dq_flags | qtrx->qt_dquot->q_flags; + __entry->type = qtrx->qt_dquot->q_type; + __entry->flags = qtrx->qt_dquot->q_flags; __entry->dqid = qtrx->qt_dquot->q_id; __entry->blk_res = qtrx->qt_blk_res; @@ -1035,12 +1043,13 @@ DECLARE_EVENT_CLASS(xfs_dqtrx_class, __entry->ino_res_used = qtrx->qt_ino_res_used; __entry->icount_delta = qtrx->qt_icount_delta; ), - TP_printk("dev %d:%d dquot id 0x%x flags %s" + TP_printk("dev %d:%d dquot id 0x%x type %s flags %s" "blk_res %llu bcount_delta %lld delbcnt_delta %lld " "rtblk_res %llu rtblk_res_used %llu rtbcount_delta %lld delrtb_delta %lld " "ino_res %llu ino_res_used %llu icount_delta %lld", MAJOR(__entry->dev), MINOR(__entry->dev), __entry->dqid, + __print_flags(__entry->type, "|", XFS_DQTYPE_STRINGS), __print_flags(__entry->flags, "|", XFS_DQFLAG_STRINGS), __entry->blk_res, -- GitLab From a990f7a84edc9941956ea3c1dfb89733c80f9ad0 Mon Sep 17 00:00:00 2001 From: "Darrick J. Wong" Date: Wed, 15 Jul 2020 18:39:51 -0700 Subject: [PATCH 1027/1476] xfs: improve ondisk dquot flags checking Create an XFS_DQTYPE_ANY mask for ondisk dquots flags, and use that to ensure that we never accept any garbage flags when we're loading dquots. While we're at it, restructure the quota type flag checking to use the proper masking. Note that I plan to add y2038 support soon, which will require a new xfs_dqtype_t flag for extended timestamp support, hence all the work to make the type masking work correctly. Signed-off-by: Darrick J. Wong Reviewed-by: Dave Chinner Reviewed-by: Christoph Hellwig --- fs/xfs/libxfs/xfs_dquot_buf.c | 11 ++++++++--- fs/xfs/libxfs/xfs_format.h | 2 ++ 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/fs/xfs/libxfs/xfs_dquot_buf.c b/fs/xfs/libxfs/xfs_dquot_buf.c index 75c164ed141cb..39d64fbc6b87d 100644 --- a/fs/xfs/libxfs/xfs_dquot_buf.c +++ b/fs/xfs/libxfs/xfs_dquot_buf.c @@ -39,6 +39,8 @@ xfs_dquot_verify( struct xfs_disk_dquot *ddq, xfs_dqid_t id) /* used only during quotacheck */ { + __u8 ddq_type; + /* * We can encounter an uninitialized dquot buffer for 2 reasons: * 1. If we crash while deleting the quotainode(s), and those blks got @@ -59,9 +61,12 @@ xfs_dquot_verify( if (ddq->d_version != XFS_DQUOT_VERSION) return __this_address; - if (ddq->d_flags != XFS_DQTYPE_USER && - ddq->d_flags != XFS_DQTYPE_PROJ && - ddq->d_flags != XFS_DQTYPE_GROUP) + if (ddq->d_flags & ~XFS_DQTYPE_ANY) + return __this_address; + ddq_type = ddq->d_flags & XFS_DQTYPE_REC_MASK; + if (ddq_type != XFS_DQTYPE_USER && + ddq_type != XFS_DQTYPE_PROJ && + ddq_type != XFS_DQTYPE_GROUP) return __this_address; if (id != -1 && id != be32_to_cpu(ddq->d_id)) diff --git a/fs/xfs/libxfs/xfs_format.h b/fs/xfs/libxfs/xfs_format.h index 0fa969f6202c9..29564bd32befd 100644 --- a/fs/xfs/libxfs/xfs_format.h +++ b/fs/xfs/libxfs/xfs_format.h @@ -1158,6 +1158,8 @@ static inline void xfs_dinode_put_rdev(struct xfs_dinode *dip, xfs_dev_t rdev) XFS_DQTYPE_PROJ | \ XFS_DQTYPE_GROUP) +#define XFS_DQTYPE_ANY (XFS_DQTYPE_REC_MASK) + /* * This is the main portion of the on-disk representation of quota information * for a user. We pad this with some more expansion room to construct the on -- GitLab From d8c1af0d6ad617df4563e78bbee70691f25ace58 Mon Sep 17 00:00:00 2001 From: "Darrick J. Wong" Date: Wed, 15 Jul 2020 18:05:39 -0700 Subject: [PATCH 1028/1476] xfs: rename the ondisk dquot d_flags to d_type The ondisk dquot stores the quota record type in the flags field. Rename this field to d_type to make the _type relationship between the ondisk and incore dquot more obvious. Signed-off-by: Darrick J. Wong Reviewed-by: Dave Chinner Reviewed-by: Christoph Hellwig --- fs/xfs/libxfs/xfs_dquot_buf.c | 6 +++--- fs/xfs/libxfs/xfs_format.h | 2 +- fs/xfs/xfs_dquot.c | 8 ++++---- fs/xfs/xfs_dquot_item_recover.c | 4 ++-- fs/xfs/xfs_qm.c | 4 ++-- 5 files changed, 12 insertions(+), 12 deletions(-) diff --git a/fs/xfs/libxfs/xfs_dquot_buf.c b/fs/xfs/libxfs/xfs_dquot_buf.c index 39d64fbc6b87d..5a2db00b9d5f0 100644 --- a/fs/xfs/libxfs/xfs_dquot_buf.c +++ b/fs/xfs/libxfs/xfs_dquot_buf.c @@ -61,9 +61,9 @@ xfs_dquot_verify( if (ddq->d_version != XFS_DQUOT_VERSION) return __this_address; - if (ddq->d_flags & ~XFS_DQTYPE_ANY) + if (ddq->d_type & ~XFS_DQTYPE_ANY) return __this_address; - ddq_type = ddq->d_flags & XFS_DQTYPE_REC_MASK; + ddq_type = ddq->d_type & XFS_DQTYPE_REC_MASK; if (ddq_type != XFS_DQTYPE_USER && ddq_type != XFS_DQTYPE_PROJ && ddq_type != XFS_DQTYPE_GROUP) @@ -124,7 +124,7 @@ xfs_dqblk_repair( dqb->dd_diskdq.d_magic = cpu_to_be16(XFS_DQUOT_MAGIC); dqb->dd_diskdq.d_version = XFS_DQUOT_VERSION; - dqb->dd_diskdq.d_flags = type; + dqb->dd_diskdq.d_type = type; dqb->dd_diskdq.d_id = cpu_to_be32(id); if (xfs_sb_version_hascrc(&mp->m_sb)) { diff --git a/fs/xfs/libxfs/xfs_format.h b/fs/xfs/libxfs/xfs_format.h index 29564bd32befd..31b7ece985bb2 100644 --- a/fs/xfs/libxfs/xfs_format.h +++ b/fs/xfs/libxfs/xfs_format.h @@ -1168,7 +1168,7 @@ static inline void xfs_dinode_put_rdev(struct xfs_dinode *dip, xfs_dev_t rdev) struct xfs_disk_dquot { __be16 d_magic; /* dquot magic = XFS_DQUOT_MAGIC */ __u8 d_version; /* dquot version */ - __u8 d_flags; /* XFS_DQTYPE_USER/PROJ/GROUP */ + __u8 d_type; /* XFS_DQTYPE_USER/PROJ/GROUP */ __be32 d_id; /* user,project,group id */ __be64 d_blk_hardlimit;/* absolute limit on disk blks */ __be64 d_blk_softlimit;/* preferred limit on disk blks */ diff --git a/fs/xfs/xfs_dquot.c b/fs/xfs/xfs_dquot.c index 30cfa0c871754..799b986975e8e 100644 --- a/fs/xfs/xfs_dquot.c +++ b/fs/xfs/xfs_dquot.c @@ -200,7 +200,7 @@ xfs_qm_init_dquot_blk( d->dd_diskdq.d_magic = cpu_to_be16(XFS_DQUOT_MAGIC); d->dd_diskdq.d_version = XFS_DQUOT_VERSION; d->dd_diskdq.d_id = cpu_to_be32(curid); - d->dd_diskdq.d_flags = type; + d->dd_diskdq.d_type = type; if (xfs_sb_version_hascrc(&mp->m_sb)) { uuid_copy(&d->dd_uuid, &mp->m_sb.sb_meta_uuid); xfs_update_cksum((char *)d, sizeof(struct xfs_dqblk), @@ -488,7 +488,7 @@ xfs_dquot_from_disk( * Ensure that we got the type and ID we were looking for. * Everything else was checked by the dquot buffer verifier. */ - if ((ddqp->d_flags & XFS_DQTYPE_REC_MASK) != xfs_dquot_type(dqp) || + if ((ddqp->d_type & XFS_DQTYPE_REC_MASK) != xfs_dquot_type(dqp) || be32_to_cpu(ddqp->d_id) != dqp->q_id) { xfs_alert_tag(bp->b_mount, XFS_PTAG_VERIFIER_ERROR, "Metadata corruption detected at %pS, quota %u", @@ -498,7 +498,7 @@ xfs_dquot_from_disk( } /* copy everything from disk dquot to the incore dquot */ - dqp->q_type = ddqp->d_flags; + dqp->q_type = ddqp->d_type; dqp->q_blk.hardlimit = be64_to_cpu(ddqp->d_blk_hardlimit); dqp->q_blk.softlimit = be64_to_cpu(ddqp->d_blk_softlimit); dqp->q_ino.hardlimit = be64_to_cpu(ddqp->d_ino_hardlimit); @@ -539,7 +539,7 @@ xfs_dquot_to_disk( { ddqp->d_magic = cpu_to_be16(XFS_DQUOT_MAGIC); ddqp->d_version = XFS_DQUOT_VERSION; - ddqp->d_flags = dqp->q_type; + ddqp->d_type = dqp->q_type; ddqp->d_id = cpu_to_be32(dqp->q_id); ddqp->d_pad0 = 0; ddqp->d_pad = 0; diff --git a/fs/xfs/xfs_dquot_item_recover.c b/fs/xfs/xfs_dquot_item_recover.c index 93178341569a7..5875c7e1bd286 100644 --- a/fs/xfs/xfs_dquot_item_recover.c +++ b/fs/xfs/xfs_dquot_item_recover.c @@ -39,7 +39,7 @@ xlog_recover_dquot_ra_pass2( if (item->ri_buf[1].i_len < sizeof(struct xfs_disk_dquot)) return; - type = recddq->d_flags & XFS_DQTYPE_REC_MASK; + type = recddq->d_type & XFS_DQTYPE_REC_MASK; ASSERT(type); if (log->l_quotaoffs_flag & type) return; @@ -91,7 +91,7 @@ xlog_recover_dquot_commit_pass2( /* * This type of quotas was turned off, so ignore this record. */ - type = recddq->d_flags & XFS_DQTYPE_REC_MASK; + type = recddq->d_type & XFS_DQTYPE_REC_MASK; ASSERT(type); if (log->l_quotaoffs_flag & type) return 0; diff --git a/fs/xfs/xfs_qm.c b/fs/xfs/xfs_qm.c index 123757717e21b..be67570badf8d 100644 --- a/fs/xfs/xfs_qm.c +++ b/fs/xfs/xfs_qm.c @@ -855,14 +855,14 @@ xfs_qm_reset_dqcounts( * xfs_dquot_verify. */ if (xfs_dqblk_verify(mp, &dqb[j], id + j) || - (dqb[j].dd_diskdq.d_flags & XFS_DQTYPE_REC_MASK) != type) + (dqb[j].dd_diskdq.d_type & XFS_DQTYPE_REC_MASK) != type) xfs_dqblk_repair(mp, &dqb[j], id + j, type); /* * Reset type in case we are reusing group quota file for * project quotas or vice versa */ - ddq->d_flags = type; + ddq->d_type = type; ddq->d_bcount = 0; ddq->d_icount = 0; ddq->d_rtbcount = 0; -- GitLab From 26270c9f4cf77815397e749f38a343732c28c4a5 Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Sun, 19 Jul 2020 18:31:27 -0700 Subject: [PATCH 1029/1476] xfs: xfs_btree_staging.h: delete duplicated words Drop the repeated words "with" and "be" in comments. Signed-off-by: Randy Dunlap Cc: "Darrick J. Wong" Cc: linux-xfs@vger.kernel.org Reviewed-by: Darrick J. Wong Signed-off-by: Darrick J. Wong --- fs/xfs/libxfs/xfs_btree_staging.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/fs/xfs/libxfs/xfs_btree_staging.h b/fs/xfs/libxfs/xfs_btree_staging.h index 643f0f9b29942..f0d2976050aea 100644 --- a/fs/xfs/libxfs/xfs_btree_staging.h +++ b/fs/xfs/libxfs/xfs_btree_staging.h @@ -18,7 +18,7 @@ struct xbtree_afakeroot { unsigned int af_blocks; }; -/* Cursor interactions with with fake roots for AG-rooted btrees. */ +/* Cursor interactions with fake roots for AG-rooted btrees. */ void xfs_btree_stage_afakeroot(struct xfs_btree_cur *cur, struct xbtree_afakeroot *afake); void xfs_btree_commit_afakeroot(struct xfs_btree_cur *cur, struct xfs_trans *tp, @@ -45,7 +45,7 @@ struct xbtree_ifakeroot { unsigned int if_extents; }; -/* Cursor interactions with with fake roots for inode-rooted btrees. */ +/* Cursor interactions with fake roots for inode-rooted btrees. */ void xfs_btree_stage_ifakeroot(struct xfs_btree_cur *cur, struct xbtree_ifakeroot *ifake, struct xfs_btree_ops **new_ops); @@ -90,7 +90,7 @@ struct xfs_btree_bload { /* * Number of free records to leave in each leaf block. If the caller - * sets this to -1, the slack value will be calculated to be be halfway + * sets this to -1, the slack value will be calculated to be halfway * between maxrecs and minrecs. This typically leaves the block 75% * full. Note that slack values are not enforced on inode root blocks. */ -- GitLab From 3050bd0bfe706381c36e4b48bf4de465b0ab94f7 Mon Sep 17 00:00:00 2001 From: Carlos Maiolino Date: Wed, 22 Jul 2020 09:23:04 -0700 Subject: [PATCH 1030/1476] xfs: Remove kmem_zone_alloc() usage Use kmem_cache_alloc() directly. All kmem_zone_alloc() users pass 0 as flags, which are translated into: GFP_KERNEL | __GFP_NOWARN, and kmem_zone_alloc() loops forever until the allocation succeeds. We can use __GFP_NOFAIL to tell the allocator to loop forever rather than doing it ourself, and because the allocation will never fail, we do not need to use __GFP_NOWARN anymore. Hence, all callers can be converted to use GFP_KERNEL | __GFP_NOFAIL Signed-off-by: Carlos Maiolino Reviewed-by: Darrick J. Wong [darrick: add a comment back in about nofail] Signed-off-by: Darrick J. Wong Reviewed-by: Christoph Hellwig Reviewed-by: Dave Chinner --- fs/xfs/libxfs/xfs_alloc.c | 3 ++- fs/xfs/libxfs/xfs_bmap.c | 3 ++- fs/xfs/xfs_icache.c | 10 ++++------ 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/fs/xfs/libxfs/xfs_alloc.c b/fs/xfs/libxfs/xfs_alloc.c index bf4d07e5c73ff..852b536551b53 100644 --- a/fs/xfs/libxfs/xfs_alloc.c +++ b/fs/xfs/libxfs/xfs_alloc.c @@ -2462,7 +2462,8 @@ xfs_defer_agfl_block( ASSERT(xfs_bmap_free_item_zone != NULL); ASSERT(oinfo != NULL); - new = kmem_zone_alloc(xfs_bmap_free_item_zone, 0); + new = kmem_cache_alloc(xfs_bmap_free_item_zone, + GFP_KERNEL | __GFP_NOFAIL); new->xefi_startblock = XFS_AGB_TO_FSB(mp, agno, agbno); new->xefi_blockcount = 1; new->xefi_oinfo = *oinfo; diff --git a/fs/xfs/libxfs/xfs_bmap.c b/fs/xfs/libxfs/xfs_bmap.c index 667cdd0dfdf4a..fd5c0d669d0d7 100644 --- a/fs/xfs/libxfs/xfs_bmap.c +++ b/fs/xfs/libxfs/xfs_bmap.c @@ -553,7 +553,8 @@ __xfs_bmap_add_free( #endif ASSERT(xfs_bmap_free_item_zone != NULL); - new = kmem_zone_alloc(xfs_bmap_free_item_zone, 0); + new = kmem_cache_alloc(xfs_bmap_free_item_zone, + GFP_KERNEL | __GFP_NOFAIL); new->xefi_startblock = bno; new->xefi_blockcount = (xfs_extlen_t)len; if (oinfo) diff --git a/fs/xfs/xfs_icache.c b/fs/xfs/xfs_icache.c index 3c6e936d2f992..101028ebb5718 100644 --- a/fs/xfs/xfs_icache.c +++ b/fs/xfs/xfs_icache.c @@ -37,13 +37,11 @@ xfs_inode_alloc( struct xfs_inode *ip; /* - * if this didn't occur in transactions, we could use - * KM_MAYFAIL and return NULL here on ENOMEM. Set the - * code up to do this anyway. + * XXX: If this didn't occur in transactions, we could drop GFP_NOFAIL + * and return NULL here on ENOMEM. */ - ip = kmem_zone_alloc(xfs_inode_zone, 0); - if (!ip) - return NULL; + ip = kmem_cache_alloc(xfs_inode_zone, GFP_KERNEL | __GFP_NOFAIL); + if (inode_init_always(mp->m_super, VFS_I(ip))) { kmem_cache_free(xfs_inode_zone, ip); return NULL; -- GitLab From 32a2b11f467642ea700bc0b01f4693e52ec0fabd Mon Sep 17 00:00:00 2001 From: Carlos Maiolino Date: Wed, 22 Jul 2020 09:23:10 -0700 Subject: [PATCH 1031/1476] xfs: Remove kmem_zone_zalloc() usage Use kmem_cache_zalloc() directly. With the exception of xlog_ticket_alloc() which will be dealt on the next patch for readability. Reviewed-by: Christoph Hellwig Signed-off-by: Carlos Maiolino Reviewed-by: Darrick J. Wong Signed-off-by: Darrick J. Wong Reviewed-by: Dave Chinner --- fs/xfs/libxfs/xfs_alloc_btree.c | 2 +- fs/xfs/libxfs/xfs_bmap.c | 5 ++++- fs/xfs/libxfs/xfs_bmap_btree.c | 2 +- fs/xfs/libxfs/xfs_da_btree.c | 2 +- fs/xfs/libxfs/xfs_ialloc_btree.c | 2 +- fs/xfs/libxfs/xfs_inode_fork.c | 6 +++--- fs/xfs/libxfs/xfs_refcount_btree.c | 2 +- fs/xfs/libxfs/xfs_rmap_btree.c | 2 +- fs/xfs/xfs_bmap_item.c | 4 ++-- fs/xfs/xfs_buf.c | 4 +--- fs/xfs/xfs_buf_item.c | 2 +- fs/xfs/xfs_dquot.c | 2 +- fs/xfs/xfs_extfree_item.c | 6 ++++-- fs/xfs/xfs_icreate_item.c | 2 +- fs/xfs/xfs_inode_item.c | 3 ++- fs/xfs/xfs_refcount_item.c | 5 +++-- fs/xfs/xfs_rmap_item.c | 5 +++-- fs/xfs/xfs_trans.c | 4 ++-- fs/xfs/xfs_trans_dquot.c | 3 ++- 19 files changed, 35 insertions(+), 28 deletions(-) diff --git a/fs/xfs/libxfs/xfs_alloc_btree.c b/fs/xfs/libxfs/xfs_alloc_btree.c index 3d1226aa2eb52..8e01231b308eb 100644 --- a/fs/xfs/libxfs/xfs_alloc_btree.c +++ b/fs/xfs/libxfs/xfs_alloc_btree.c @@ -480,7 +480,7 @@ xfs_allocbt_init_common( ASSERT(btnum == XFS_BTNUM_BNO || btnum == XFS_BTNUM_CNT); - cur = kmem_zone_zalloc(xfs_btree_cur_zone, KM_NOFS); + cur = kmem_cache_zalloc(xfs_btree_cur_zone, GFP_NOFS | __GFP_NOFAIL); cur->bc_tp = tp; cur->bc_mp = mp; diff --git a/fs/xfs/libxfs/xfs_bmap.c b/fs/xfs/libxfs/xfs_bmap.c index fd5c0d669d0d7..9c40d59710357 100644 --- a/fs/xfs/libxfs/xfs_bmap.c +++ b/fs/xfs/libxfs/xfs_bmap.c @@ -1099,7 +1099,10 @@ xfs_bmap_add_attrfork( if (error) goto trans_cancel; ASSERT(ip->i_afp == NULL); - ip->i_afp = kmem_zone_zalloc(xfs_ifork_zone, 0); + + ip->i_afp = kmem_cache_zalloc(xfs_ifork_zone, + GFP_KERNEL | __GFP_NOFAIL); + ip->i_afp->if_format = XFS_DINODE_FMT_EXTENTS; ip->i_afp->if_flags = XFS_IFEXTENTS; logflags = 0; diff --git a/fs/xfs/libxfs/xfs_bmap_btree.c b/fs/xfs/libxfs/xfs_bmap_btree.c index d9c63f17d2dec..ecec604e6e4d7 100644 --- a/fs/xfs/libxfs/xfs_bmap_btree.c +++ b/fs/xfs/libxfs/xfs_bmap_btree.c @@ -552,7 +552,7 @@ xfs_bmbt_init_cursor( struct xfs_btree_cur *cur; ASSERT(whichfork != XFS_COW_FORK); - cur = kmem_zone_zalloc(xfs_btree_cur_zone, KM_NOFS); + cur = kmem_cache_zalloc(xfs_btree_cur_zone, GFP_NOFS | __GFP_NOFAIL); cur->bc_tp = tp; cur->bc_mp = mp; diff --git a/fs/xfs/libxfs/xfs_da_btree.c b/fs/xfs/libxfs/xfs_da_btree.c index 897749c41f36e..a4e1f01daf3d8 100644 --- a/fs/xfs/libxfs/xfs_da_btree.c +++ b/fs/xfs/libxfs/xfs_da_btree.c @@ -81,7 +81,7 @@ kmem_zone_t *xfs_da_state_zone; /* anchor for state struct zone */ xfs_da_state_t * xfs_da_state_alloc(void) { - return kmem_zone_zalloc(xfs_da_state_zone, KM_NOFS); + return kmem_cache_zalloc(xfs_da_state_zone, GFP_NOFS | __GFP_NOFAIL); } /* diff --git a/fs/xfs/libxfs/xfs_ialloc_btree.c b/fs/xfs/libxfs/xfs_ialloc_btree.c index b2c122ad8f0e9..3c8aebc36e643 100644 --- a/fs/xfs/libxfs/xfs_ialloc_btree.c +++ b/fs/xfs/libxfs/xfs_ialloc_btree.c @@ -411,7 +411,7 @@ xfs_inobt_init_common( { struct xfs_btree_cur *cur; - cur = kmem_zone_zalloc(xfs_btree_cur_zone, KM_NOFS); + cur = kmem_cache_zalloc(xfs_btree_cur_zone, GFP_NOFS | __GFP_NOFAIL); cur->bc_tp = tp; cur->bc_mp = mp; cur->bc_btnum = btnum; diff --git a/fs/xfs/libxfs/xfs_inode_fork.c b/fs/xfs/libxfs/xfs_inode_fork.c index 28b366275ae0e..0cf853d42d622 100644 --- a/fs/xfs/libxfs/xfs_inode_fork.c +++ b/fs/xfs/libxfs/xfs_inode_fork.c @@ -291,7 +291,7 @@ xfs_iformat_attr_fork( * Initialize the extent count early, as the per-format routines may * depend on it. */ - ip->i_afp = kmem_zone_zalloc(xfs_ifork_zone, KM_NOFS); + ip->i_afp = kmem_cache_zalloc(xfs_ifork_zone, GFP_NOFS | __GFP_NOFAIL); ip->i_afp->if_format = dip->di_aformat; if (unlikely(ip->i_afp->if_format == 0)) /* pre IRIX 6.2 file system */ ip->i_afp->if_format = XFS_DINODE_FMT_EXTENTS; @@ -673,8 +673,8 @@ xfs_ifork_init_cow( if (ip->i_cowfp) return; - ip->i_cowfp = kmem_zone_zalloc(xfs_ifork_zone, - KM_NOFS); + ip->i_cowfp = kmem_cache_zalloc(xfs_ifork_zone, + GFP_NOFS | __GFP_NOFAIL); ip->i_cowfp->if_flags = XFS_IFEXTENTS; ip->i_cowfp->if_format = XFS_DINODE_FMT_EXTENTS; } diff --git a/fs/xfs/libxfs/xfs_refcount_btree.c b/fs/xfs/libxfs/xfs_refcount_btree.c index c5296c124a4c4..a6ac60ae94216 100644 --- a/fs/xfs/libxfs/xfs_refcount_btree.c +++ b/fs/xfs/libxfs/xfs_refcount_btree.c @@ -323,7 +323,7 @@ xfs_refcountbt_init_common( ASSERT(agno != NULLAGNUMBER); ASSERT(agno < mp->m_sb.sb_agcount); - cur = kmem_zone_zalloc(xfs_btree_cur_zone, KM_NOFS); + cur = kmem_cache_zalloc(xfs_btree_cur_zone, GFP_NOFS | __GFP_NOFAIL); cur->bc_tp = tp; cur->bc_mp = mp; cur->bc_btnum = XFS_BTNUM_REFC; diff --git a/fs/xfs/libxfs/xfs_rmap_btree.c b/fs/xfs/libxfs/xfs_rmap_btree.c index 94948a53569f4..beb81c84a9375 100644 --- a/fs/xfs/libxfs/xfs_rmap_btree.c +++ b/fs/xfs/libxfs/xfs_rmap_btree.c @@ -456,7 +456,7 @@ xfs_rmapbt_init_common( { struct xfs_btree_cur *cur; - cur = kmem_zone_zalloc(xfs_btree_cur_zone, KM_NOFS); + cur = kmem_cache_zalloc(xfs_btree_cur_zone, GFP_NOFS | __GFP_NOFAIL); cur->bc_tp = tp; cur->bc_mp = mp; /* Overlapping btree; 2 keys per pointer. */ diff --git a/fs/xfs/xfs_bmap_item.c b/fs/xfs/xfs_bmap_item.c index 6736c5ab188f2..ec3691372e7c0 100644 --- a/fs/xfs/xfs_bmap_item.c +++ b/fs/xfs/xfs_bmap_item.c @@ -138,7 +138,7 @@ xfs_bui_init( { struct xfs_bui_log_item *buip; - buip = kmem_zone_zalloc(xfs_bui_zone, 0); + buip = kmem_cache_zalloc(xfs_bui_zone, GFP_KERNEL | __GFP_NOFAIL); xfs_log_item_init(mp, &buip->bui_item, XFS_LI_BUI, &xfs_bui_item_ops); buip->bui_format.bui_nextents = XFS_BUI_MAX_FAST_EXTENTS; @@ -215,7 +215,7 @@ xfs_trans_get_bud( { struct xfs_bud_log_item *budp; - budp = kmem_zone_zalloc(xfs_bud_zone, 0); + budp = kmem_cache_zalloc(xfs_bud_zone, GFP_KERNEL | __GFP_NOFAIL); xfs_log_item_init(tp->t_mountp, &budp->bud_item, XFS_LI_BUD, &xfs_bud_item_ops); budp->bud_buip = buip; diff --git a/fs/xfs/xfs_buf.c b/fs/xfs/xfs_buf.c index dda0c94458797..d4cdcb6fb2fe1 100644 --- a/fs/xfs/xfs_buf.c +++ b/fs/xfs/xfs_buf.c @@ -214,9 +214,7 @@ _xfs_buf_alloc( int i; *bpp = NULL; - bp = kmem_zone_zalloc(xfs_buf_zone, KM_NOFS); - if (unlikely(!bp)) - return -ENOMEM; + bp = kmem_cache_zalloc(xfs_buf_zone, GFP_NOFS | __GFP_NOFAIL); /* * We don't want certain flags to appear in b_flags unless they are diff --git a/fs/xfs/xfs_buf_item.c b/fs/xfs/xfs_buf_item.c index ed1bf1d99483a..5bb6f22cc11a8 100644 --- a/fs/xfs/xfs_buf_item.c +++ b/fs/xfs/xfs_buf_item.c @@ -738,7 +738,7 @@ xfs_buf_item_init( return 0; } - bip = kmem_zone_zalloc(xfs_buf_item_zone, 0); + bip = kmem_cache_zalloc(xfs_buf_item_zone, GFP_KERNEL | __GFP_NOFAIL); xfs_log_item_init(mp, &bip->bli_item, XFS_LI_BUF, &xfs_buf_item_ops); bip->bli_buf = bp; diff --git a/fs/xfs/xfs_dquot.c b/fs/xfs/xfs_dquot.c index 799b986975e8e..04dc2be19c3af 100644 --- a/fs/xfs/xfs_dquot.c +++ b/fs/xfs/xfs_dquot.c @@ -428,7 +428,7 @@ xfs_dquot_alloc( { struct xfs_dquot *dqp; - dqp = kmem_zone_zalloc(xfs_qm_dqzone, 0); + dqp = kmem_cache_zalloc(xfs_qm_dqzone, GFP_KERNEL | __GFP_NOFAIL); dqp->q_type = type; dqp->q_id = id; diff --git a/fs/xfs/xfs_extfree_item.c b/fs/xfs/xfs_extfree_item.c index b9c333bae0a12..6cb8cd11072a3 100644 --- a/fs/xfs/xfs_extfree_item.c +++ b/fs/xfs/xfs_extfree_item.c @@ -161,7 +161,8 @@ xfs_efi_init( ((nextents - 1) * sizeof(xfs_extent_t))); efip = kmem_zalloc(size, 0); } else { - efip = kmem_zone_zalloc(xfs_efi_zone, 0); + efip = kmem_cache_zalloc(xfs_efi_zone, + GFP_KERNEL | __GFP_NOFAIL); } xfs_log_item_init(mp, &efip->efi_item, XFS_LI_EFI, &xfs_efi_item_ops); @@ -332,7 +333,8 @@ xfs_trans_get_efd( (nextents - 1) * sizeof(struct xfs_extent), 0); } else { - efdp = kmem_zone_zalloc(xfs_efd_zone, 0); + efdp = kmem_cache_zalloc(xfs_efd_zone, + GFP_KERNEL | __GFP_NOFAIL); } xfs_log_item_init(tp->t_mountp, &efdp->efd_item, XFS_LI_EFD, diff --git a/fs/xfs/xfs_icreate_item.c b/fs/xfs/xfs_icreate_item.c index 287a9e5c7d758..9b3994b9c716d 100644 --- a/fs/xfs/xfs_icreate_item.c +++ b/fs/xfs/xfs_icreate_item.c @@ -97,7 +97,7 @@ xfs_icreate_log( { struct xfs_icreate_item *icp; - icp = kmem_zone_zalloc(xfs_icreate_zone, 0); + icp = kmem_cache_zalloc(xfs_icreate_zone, GFP_KERNEL | __GFP_NOFAIL); xfs_log_item_init(tp->t_mountp, &icp->ic_item, XFS_LI_ICREATE, &xfs_icreate_item_ops); diff --git a/fs/xfs/xfs_inode_item.c b/fs/xfs/xfs_inode_item.c index 3840117f8a5e2..895f61b2b4f01 100644 --- a/fs/xfs/xfs_inode_item.c +++ b/fs/xfs/xfs_inode_item.c @@ -615,7 +615,8 @@ xfs_inode_item_init( struct xfs_inode_log_item *iip; ASSERT(ip->i_itemp == NULL); - iip = ip->i_itemp = kmem_zone_zalloc(xfs_ili_zone, 0); + iip = ip->i_itemp = kmem_cache_zalloc(xfs_ili_zone, + GFP_KERNEL | __GFP_NOFAIL); iip->ili_inode = ip; spin_lock_init(&iip->ili_lock); diff --git a/fs/xfs/xfs_refcount_item.c b/fs/xfs/xfs_refcount_item.c index c81639891e298..7b2c72bc28582 100644 --- a/fs/xfs/xfs_refcount_item.c +++ b/fs/xfs/xfs_refcount_item.c @@ -143,7 +143,8 @@ xfs_cui_init( cuip = kmem_zalloc(xfs_cui_log_item_sizeof(nextents), 0); else - cuip = kmem_zone_zalloc(xfs_cui_zone, 0); + cuip = kmem_cache_zalloc(xfs_cui_zone, + GFP_KERNEL | __GFP_NOFAIL); xfs_log_item_init(mp, &cuip->cui_item, XFS_LI_CUI, &xfs_cui_item_ops); cuip->cui_format.cui_nextents = nextents; @@ -220,7 +221,7 @@ xfs_trans_get_cud( { struct xfs_cud_log_item *cudp; - cudp = kmem_zone_zalloc(xfs_cud_zone, 0); + cudp = kmem_cache_zalloc(xfs_cud_zone, GFP_KERNEL | __GFP_NOFAIL); xfs_log_item_init(tp->t_mountp, &cudp->cud_item, XFS_LI_CUD, &xfs_cud_item_ops); cudp->cud_cuip = cuip; diff --git a/fs/xfs/xfs_rmap_item.c b/fs/xfs/xfs_rmap_item.c index a86599db20a6f..dc5b0753cd519 100644 --- a/fs/xfs/xfs_rmap_item.c +++ b/fs/xfs/xfs_rmap_item.c @@ -141,7 +141,8 @@ xfs_rui_init( if (nextents > XFS_RUI_MAX_FAST_EXTENTS) ruip = kmem_zalloc(xfs_rui_log_item_sizeof(nextents), 0); else - ruip = kmem_zone_zalloc(xfs_rui_zone, 0); + ruip = kmem_cache_zalloc(xfs_rui_zone, + GFP_KERNEL | __GFP_NOFAIL); xfs_log_item_init(mp, &ruip->rui_item, XFS_LI_RUI, &xfs_rui_item_ops); ruip->rui_format.rui_nextents = nextents; @@ -243,7 +244,7 @@ xfs_trans_get_rud( { struct xfs_rud_log_item *rudp; - rudp = kmem_zone_zalloc(xfs_rud_zone, 0); + rudp = kmem_cache_zalloc(xfs_rud_zone, GFP_KERNEL | __GFP_NOFAIL); xfs_log_item_init(tp->t_mountp, &rudp->rud_item, XFS_LI_RUD, &xfs_rud_item_ops); rudp->rud_ruip = ruip; diff --git a/fs/xfs/xfs_trans.c b/fs/xfs/xfs_trans.c index 0ad72a83edac4..ed72867b1a193 100644 --- a/fs/xfs/xfs_trans.c +++ b/fs/xfs/xfs_trans.c @@ -90,7 +90,7 @@ xfs_trans_dup( trace_xfs_trans_dup(tp, _RET_IP_); - ntp = kmem_zone_zalloc(xfs_trans_zone, 0); + ntp = kmem_cache_zalloc(xfs_trans_zone, GFP_KERNEL | __GFP_NOFAIL); /* * Initialize the new transaction structure. @@ -263,7 +263,7 @@ xfs_trans_alloc( * GFP_NOFS allocation context so that we avoid lockdep false positives * by doing GFP_KERNEL allocations inside sb_start_intwrite(). */ - tp = kmem_zone_zalloc(xfs_trans_zone, 0); + tp = kmem_cache_zalloc(xfs_trans_zone, GFP_KERNEL | __GFP_NOFAIL); if (!(flags & XFS_TRANS_NO_WRITECOUNT)) sb_start_intwrite(mp->m_super); diff --git a/fs/xfs/xfs_trans_dquot.c b/fs/xfs/xfs_trans_dquot.c index 518cf0347891c..c6ba7ef18e06a 100644 --- a/fs/xfs/xfs_trans_dquot.c +++ b/fs/xfs/xfs_trans_dquot.c @@ -874,7 +874,8 @@ STATIC void xfs_trans_alloc_dqinfo( xfs_trans_t *tp) { - tp->t_dqinfo = kmem_zone_zalloc(xfs_qm_dqtrxzone, 0); + tp->t_dqinfo = kmem_cache_zalloc(xfs_qm_dqtrxzone, + GFP_KERNEL | __GFP_NOFAIL); } void -- GitLab From ca4f2589905954b155e920b543e13a370d578511 Mon Sep 17 00:00:00 2001 From: Carlos Maiolino Date: Wed, 22 Jul 2020 09:23:17 -0700 Subject: [PATCH 1032/1476] xfs: Modify xlog_ticket_alloc() to use kernel's MM API xlog_ticket_alloc() is always called under NOFS context, except from unmount path, which eitherway is holding many FS locks, so, there is no need for its callers to keep passing allocation flags into it. change xlog_ticket_alloc() to use default kmem_cache_zalloc(), remove its alloc_flags argument, and always use GFP_NOFS | __GFP_NOFAIL flags. Reviewed-by: Christoph Hellwig Signed-off-by: Carlos Maiolino Reviewed-by: Darrick J. Wong Signed-off-by: Darrick J. Wong Reviewed-by: Dave Chinner --- fs/xfs/xfs_log.c | 9 +++------ fs/xfs/xfs_log_cil.c | 3 +-- fs/xfs/xfs_log_priv.h | 4 +--- 3 files changed, 5 insertions(+), 11 deletions(-) diff --git a/fs/xfs/xfs_log.c b/fs/xfs/xfs_log.c index 00fda2e8e7380..ad0c69ee89475 100644 --- a/fs/xfs/xfs_log.c +++ b/fs/xfs/xfs_log.c @@ -433,7 +433,7 @@ xfs_log_reserve( XFS_STATS_INC(mp, xs_try_logspace); ASSERT(*ticp == NULL); - tic = xlog_ticket_alloc(log, unit_bytes, cnt, client, permanent, 0); + tic = xlog_ticket_alloc(log, unit_bytes, cnt, client, permanent); *ticp = tic; xlog_grant_push_ail(log, tic->t_cnt ? tic->t_unit_res * tic->t_cnt @@ -3408,15 +3408,12 @@ xlog_ticket_alloc( int unit_bytes, int cnt, char client, - bool permanent, - xfs_km_flags_t alloc_flags) + bool permanent) { struct xlog_ticket *tic; int unit_res; - tic = kmem_zone_zalloc(xfs_log_ticket_zone, alloc_flags); - if (!tic) - return NULL; + tic = kmem_cache_zalloc(xfs_log_ticket_zone, GFP_NOFS | __GFP_NOFAIL); unit_res = xfs_log_calc_unit_res(log->l_mp, unit_bytes); diff --git a/fs/xfs/xfs_log_cil.c b/fs/xfs/xfs_log_cil.c index 9ed90368ab311..56c32eecffead 100644 --- a/fs/xfs/xfs_log_cil.c +++ b/fs/xfs/xfs_log_cil.c @@ -37,8 +37,7 @@ xlog_cil_ticket_alloc( { struct xlog_ticket *tic; - tic = xlog_ticket_alloc(log, 0, 1, XFS_TRANSACTION, 0, - KM_NOFS); + tic = xlog_ticket_alloc(log, 0, 1, XFS_TRANSACTION, 0); /* * set the current reservation to zero so we know to steal the basic diff --git a/fs/xfs/xfs_log_priv.h b/fs/xfs/xfs_log_priv.h index 75a62870b63af..1c6fdbf3d5066 100644 --- a/fs/xfs/xfs_log_priv.h +++ b/fs/xfs/xfs_log_priv.h @@ -464,9 +464,7 @@ xlog_ticket_alloc( int unit_bytes, int count, char client, - bool permanent, - xfs_km_flags_t alloc_flags); - + bool permanent); static inline void xlog_write_adv_cnt(void **ptr, int *len, int *off, size_t bytes) -- GitLab From bae633a4a283afc8ceff5672c1f4f096d0558d70 Mon Sep 17 00:00:00 2001 From: Carlos Maiolino Date: Wed, 22 Jul 2020 09:23:17 -0700 Subject: [PATCH 1033/1476] xfs: remove xfs_zone_{alloc,zalloc} helpers All their users have been converted to use MM API directly, no need to keep them around anymore. Reviewed-by: Christoph Hellwig Reviewed-by: Dave Chinner Signed-off-by: Carlos Maiolino Reviewed-by: Darrick J. Wong Signed-off-by: Darrick J. Wong Reviewed-by: Dave Chinner --- fs/xfs/kmem.c | 21 --------------------- fs/xfs/kmem.h | 8 -------- fs/xfs/xfs_trace.h | 1 - 3 files changed, 30 deletions(-) diff --git a/fs/xfs/kmem.c b/fs/xfs/kmem.c index f1366475c389c..e841ed781a257 100644 --- a/fs/xfs/kmem.c +++ b/fs/xfs/kmem.c @@ -115,24 +115,3 @@ kmem_realloc(const void *old, size_t newsize, xfs_km_flags_t flags) congestion_wait(BLK_RW_ASYNC, HZ/50); } while (1); } - -void * -kmem_zone_alloc(kmem_zone_t *zone, xfs_km_flags_t flags) -{ - int retries = 0; - gfp_t lflags = kmem_flags_convert(flags); - void *ptr; - - trace_kmem_zone_alloc(kmem_cache_size(zone), flags, _RET_IP_); - do { - ptr = kmem_cache_alloc(zone, lflags); - if (ptr || (flags & KM_MAYFAIL)) - return ptr; - if (!(++retries % 100)) - xfs_err(NULL, - "%s(%u) possible memory allocation deadlock in %s (mode:0x%x)", - current->comm, current->pid, - __func__, lflags); - congestion_wait(BLK_RW_ASYNC, HZ/50); - } while (1); -} diff --git a/fs/xfs/kmem.h b/fs/xfs/kmem.h index 34cbcfde92281..8e8555817e6d3 100644 --- a/fs/xfs/kmem.h +++ b/fs/xfs/kmem.h @@ -85,14 +85,6 @@ kmem_zalloc_large(size_t size, xfs_km_flags_t flags) #define kmem_zone kmem_cache #define kmem_zone_t struct kmem_cache -extern void *kmem_zone_alloc(kmem_zone_t *, xfs_km_flags_t); - -static inline void * -kmem_zone_zalloc(kmem_zone_t *zone, xfs_km_flags_t flags) -{ - return kmem_zone_alloc(zone, flags | KM_ZERO); -} - static inline struct page * kmem_to_page(void *addr) { diff --git a/fs/xfs/xfs_trace.h b/fs/xfs/xfs_trace.h index e9b2ce0948b66..abb1d859f226a 100644 --- a/fs/xfs/xfs_trace.h +++ b/fs/xfs/xfs_trace.h @@ -3677,7 +3677,6 @@ DEFINE_KMEM_EVENT(kmem_alloc); DEFINE_KMEM_EVENT(kmem_alloc_io); DEFINE_KMEM_EVENT(kmem_alloc_large); DEFINE_KMEM_EVENT(kmem_realloc); -DEFINE_KMEM_EVENT(kmem_zone_alloc); TRACE_EVENT(xfs_check_new_dalign, TP_PROTO(struct xfs_mount *mp, int new_dalign, xfs_ino_t calc_rootino), -- GitLab From 4491a3dd7192fb0db54117c42f785a8eb524607d Mon Sep 17 00:00:00 2001 From: Carlos Maiolino Date: Wed, 22 Jul 2020 09:23:18 -0700 Subject: [PATCH 1034/1476] xfs: Refactor xfs_da_state_alloc() helper Every call to xfs_da_state_alloc() also requires setting up state->args and state->mp Change xfs_da_state_alloc() to receive an xfs_da_args_t as argument and return a xfs_da_state_t with both args and mp already set. Signed-off-by: Carlos Maiolino Reviewed-by: Darrick J. Wong [darrick: reduce struct typedef usage] Signed-off-by: Darrick J. Wong Reviewed-by: Christoph Hellwig Reviewed-by: Dave Chinner --- fs/xfs/libxfs/xfs_attr.c | 19 +++++-------------- fs/xfs/libxfs/xfs_da_btree.c | 12 +++++++++--- fs/xfs/libxfs/xfs_da_btree.h | 2 +- fs/xfs/libxfs/xfs_dir2_node.c | 17 +++++------------ fs/xfs/scrub/dabtree.c | 4 +--- 5 files changed, 21 insertions(+), 33 deletions(-) diff --git a/fs/xfs/libxfs/xfs_attr.c b/fs/xfs/libxfs/xfs_attr.c index 3b1bd6e112f89..6bb114af4535c 100644 --- a/fs/xfs/libxfs/xfs_attr.c +++ b/fs/xfs/libxfs/xfs_attr.c @@ -739,7 +739,6 @@ xfs_attr_node_addname( struct xfs_da_state *state; struct xfs_da_state_blk *blk; struct xfs_inode *dp; - struct xfs_mount *mp; int retval, error; trace_xfs_attr_node_addname(args); @@ -748,11 +747,8 @@ xfs_attr_node_addname( * Fill in bucket of arguments/results/context to carry around. */ dp = args->dp; - mp = dp->i_mount; restart: - state = xfs_da_state_alloc(); - state->args = args; - state->mp = mp; + state = xfs_da_state_alloc(args); /* * Search to see if name already exists, and get back a pointer @@ -899,9 +895,8 @@ restart: * attr, not the "new" one. */ args->attr_filter |= XFS_ATTR_INCOMPLETE; - state = xfs_da_state_alloc(); - state->args = args; - state->mp = mp; + state = xfs_da_state_alloc(args); + state->inleaf = 0; error = xfs_da3_node_lookup_int(state, &retval); if (error) @@ -975,9 +970,7 @@ xfs_attr_node_removename( * Tie a string around our finger to remind us where we are. */ dp = args->dp; - state = xfs_da_state_alloc(); - state->args = args; - state->mp = dp->i_mount; + state = xfs_da_state_alloc(args); /* * Search to see if name exists, and get back a pointer to it. @@ -1207,9 +1200,7 @@ xfs_attr_node_get(xfs_da_args_t *args) trace_xfs_attr_node_get(args); - state = xfs_da_state_alloc(); - state->args = args; - state->mp = args->dp->i_mount; + state = xfs_da_state_alloc(args); /* * Search to see if name exists, and get back a pointer to it. diff --git a/fs/xfs/libxfs/xfs_da_btree.c b/fs/xfs/libxfs/xfs_da_btree.c index a4e1f01daf3d8..e46bc03365db2 100644 --- a/fs/xfs/libxfs/xfs_da_btree.c +++ b/fs/xfs/libxfs/xfs_da_btree.c @@ -78,10 +78,16 @@ kmem_zone_t *xfs_da_state_zone; /* anchor for state struct zone */ * Allocate a dir-state structure. * We don't put them on the stack since they're large. */ -xfs_da_state_t * -xfs_da_state_alloc(void) +struct xfs_da_state * +xfs_da_state_alloc( + struct xfs_da_args *args) { - return kmem_cache_zalloc(xfs_da_state_zone, GFP_NOFS | __GFP_NOFAIL); + struct xfs_da_state *state; + + state = kmem_cache_zalloc(xfs_da_state_zone, GFP_NOFS | __GFP_NOFAIL); + state->args = args; + state->mp = args->dp->i_mount; + return state; } /* diff --git a/fs/xfs/libxfs/xfs_da_btree.h b/fs/xfs/libxfs/xfs_da_btree.h index 6e25de6621e4f..ad5dd324631ab 100644 --- a/fs/xfs/libxfs/xfs_da_btree.h +++ b/fs/xfs/libxfs/xfs_da_btree.h @@ -219,7 +219,7 @@ enum xfs_dacmp xfs_da_compname(struct xfs_da_args *args, const unsigned char *name, int len); -xfs_da_state_t *xfs_da_state_alloc(void); +struct xfs_da_state *xfs_da_state_alloc(struct xfs_da_args *args); void xfs_da_state_free(xfs_da_state_t *state); void xfs_da3_node_hdr_from_disk(struct xfs_mount *mp, diff --git a/fs/xfs/libxfs/xfs_dir2_node.c b/fs/xfs/libxfs/xfs_dir2_node.c index 6ac4aad98cd76..5d51265d29d6f 100644 --- a/fs/xfs/libxfs/xfs_dir2_node.c +++ b/fs/xfs/libxfs/xfs_dir2_node.c @@ -2015,9 +2015,7 @@ xfs_dir2_node_addname( /* * Allocate and initialize the state (btree cursor). */ - state = xfs_da_state_alloc(); - state->args = args; - state->mp = args->dp->i_mount; + state = xfs_da_state_alloc(args); /* * Look up the name. We're not supposed to find it, but * this gives us the insertion point. @@ -2086,9 +2084,8 @@ xfs_dir2_node_lookup( /* * Allocate and initialize the btree cursor. */ - state = xfs_da_state_alloc(); - state->args = args; - state->mp = args->dp->i_mount; + state = xfs_da_state_alloc(args); + /* * Fill in the path to the entry in the cursor. */ @@ -2139,9 +2136,7 @@ xfs_dir2_node_removename( /* * Allocate and initialize the btree cursor. */ - state = xfs_da_state_alloc(); - state->args = args; - state->mp = args->dp->i_mount; + state = xfs_da_state_alloc(args); /* Look up the entry we're deleting, set up the cursor. */ error = xfs_da3_node_lookup_int(state, &rval); @@ -2206,9 +2201,7 @@ xfs_dir2_node_replace( /* * Allocate and initialize the btree cursor. */ - state = xfs_da_state_alloc(); - state->args = args; - state->mp = args->dp->i_mount; + state = xfs_da_state_alloc(args); /* * We have to save new inode number and ftype since diff --git a/fs/xfs/scrub/dabtree.c b/fs/xfs/scrub/dabtree.c index 44b15015021f3..e56786f0a13c8 100644 --- a/fs/xfs/scrub/dabtree.c +++ b/fs/xfs/scrub/dabtree.c @@ -476,9 +476,7 @@ xchk_da_btree( ds.dargs.whichfork = whichfork; ds.dargs.trans = sc->tp; ds.dargs.op_flags = XFS_DA_OP_OKNOENT; - ds.state = xfs_da_state_alloc(); - ds.state->args = &ds.dargs; - ds.state->mp = mp; + ds.state = xfs_da_state_alloc(&ds.dargs); ds.sc = sc; ds.private = private; if (whichfork == XFS_ATTR_FORK) { -- GitLab From 07120f1abdff80f3d1351f733661abe28d609535 Mon Sep 17 00:00:00 2001 From: Allison Collins Date: Mon, 20 Jul 2020 21:47:22 -0700 Subject: [PATCH 1035/1476] xfs: Add xfs_has_attr and subroutines This patch adds a new functions to check for the existence of an attribute. Subroutines are also added to handle the cases of leaf blocks, nodes or shortform. Common code that appears in existing attr add and remove functions have been factored out to help reduce the appearance of duplicated code. We will need these routines later for delayed attributes since delayed operations cannot return error codes. Signed-off-by: Allison Collins Reviewed-by: Chandan Rajendra Reviewed-by: Brian Foster Reviewed-by: Darrick J. Wong [darrick: fix a leak-on-error bug reported by Dan Carpenter] [darrick: fix unused variable warning reported by 0day] Signed-off-by: Darrick J. Wong Acked-by: Dave Chinner Reported-by: dan.carpenter@oracle.com Reported-by: kernel test robot --- fs/xfs/libxfs/xfs_attr.c | 184 +++++++++++++++++++++++----------- fs/xfs/libxfs/xfs_attr.h | 1 + fs/xfs/libxfs/xfs_attr_leaf.c | 100 ++++++++++++------ fs/xfs/libxfs/xfs_attr_leaf.h | 3 + 4 files changed, 197 insertions(+), 91 deletions(-) diff --git a/fs/xfs/libxfs/xfs_attr.c b/fs/xfs/libxfs/xfs_attr.c index 6bb114af4535c..f7f796ea11fde 100644 --- a/fs/xfs/libxfs/xfs_attr.c +++ b/fs/xfs/libxfs/xfs_attr.c @@ -46,6 +46,7 @@ STATIC int xfs_attr_shortform_addname(xfs_da_args_t *args); STATIC int xfs_attr_leaf_get(xfs_da_args_t *args); STATIC int xfs_attr_leaf_addname(xfs_da_args_t *args); STATIC int xfs_attr_leaf_removename(xfs_da_args_t *args); +STATIC int xfs_attr_leaf_hasname(struct xfs_da_args *args, struct xfs_buf **bp); /* * Internal routines when attribute list is more than one block. @@ -53,6 +54,8 @@ STATIC int xfs_attr_leaf_removename(xfs_da_args_t *args); STATIC int xfs_attr_node_get(xfs_da_args_t *args); STATIC int xfs_attr_node_addname(xfs_da_args_t *args); STATIC int xfs_attr_node_removename(xfs_da_args_t *args); +STATIC int xfs_attr_node_hasname(xfs_da_args_t *args, + struct xfs_da_state **state); STATIC int xfs_attr_fillstate(xfs_da_state_t *state); STATIC int xfs_attr_refillstate(xfs_da_state_t *state); @@ -260,6 +263,37 @@ xfs_attr_set_args( return error; } +/* + * Return EEXIST if attr is found, or ENOATTR if not + */ +int +xfs_has_attr( + struct xfs_da_args *args) +{ + struct xfs_inode *dp = args->dp; + struct xfs_buf *bp = NULL; + int error; + + if (!xfs_inode_hasattr(dp)) + return -ENOATTR; + + if (dp->i_afp->if_format == XFS_DINODE_FMT_LOCAL) { + ASSERT(dp->i_afp->if_flags & XFS_IFINLINE); + return xfs_attr_sf_findname(args, NULL, NULL); + } + + if (xfs_bmap_one_block(dp, XFS_ATTR_FORK)) { + error = xfs_attr_leaf_hasname(args, &bp); + + if (bp) + xfs_trans_brelse(args->trans, bp); + + return error; + } + + return xfs_attr_node_hasname(args, NULL); +} + /* * Remove the attribute specified in @args. */ @@ -469,26 +503,19 @@ STATIC int xfs_attr_leaf_addname( struct xfs_da_args *args) { - struct xfs_inode *dp; struct xfs_buf *bp; int retval, error, forkoff; + struct xfs_inode *dp = args->dp; trace_xfs_attr_leaf_addname(args); - /* - * Read the (only) block in the attribute list in. - */ - dp = args->dp; - args->blkno = 0; - error = xfs_attr3_leaf_read(args->trans, args->dp, args->blkno, &bp); - if (error) - return error; - /* * Look up the given attribute in the leaf block. Figure out if * the given flags produce an error or call for an atomic rename. */ - retval = xfs_attr3_leaf_lookup_int(bp, args); + retval = xfs_attr_leaf_hasname(args, &bp); + if (retval != -ENOATTR && retval != -EEXIST) + return retval; if (retval == -ENOATTR && (args->attr_flags & XATTR_REPLACE)) goto out_brelse; if (retval == -EEXIST) { @@ -639,6 +666,27 @@ out_brelse: return retval; } +/* + * Return EEXIST if attr is found, or ENOATTR if not + */ +STATIC int +xfs_attr_leaf_hasname( + struct xfs_da_args *args, + struct xfs_buf **bp) +{ + int error = 0; + + error = xfs_attr3_leaf_read(args->trans, args->dp, 0, bp); + if (error) + return error; + + error = xfs_attr3_leaf_lookup_int(*bp, args); + if (error != -ENOATTR && error != -EEXIST) + xfs_trans_brelse(args->trans, *bp); + + return error; +} + /* * Remove a name from the leaf attribute list structure * @@ -659,16 +707,14 @@ xfs_attr_leaf_removename( * Remove the attribute. */ dp = args->dp; - args->blkno = 0; - error = xfs_attr3_leaf_read(args->trans, args->dp, args->blkno, &bp); - if (error) - return error; - error = xfs_attr3_leaf_lookup_int(bp, args); + error = xfs_attr_leaf_hasname(args, &bp); + if (error == -ENOATTR) { xfs_trans_brelse(args->trans, bp); return error; - } + } else if (error != -EEXIST) + return error; xfs_attr3_leaf_remove(bp, args); @@ -703,21 +749,53 @@ xfs_attr_leaf_get(xfs_da_args_t *args) trace_xfs_attr_leaf_get(args); - args->blkno = 0; - error = xfs_attr3_leaf_read(args->trans, args->dp, args->blkno, &bp); - if (error) - return error; + error = xfs_attr_leaf_hasname(args, &bp); - error = xfs_attr3_leaf_lookup_int(bp, args); - if (error != -EEXIST) { + if (error == -ENOATTR) { xfs_trans_brelse(args->trans, bp); return error; - } + } else if (error != -EEXIST) + return error; + + error = xfs_attr3_leaf_getvalue(bp, args); xfs_trans_brelse(args->trans, bp); return error; } +/* + * Return EEXIST if attr is found, or ENOATTR if not + * statep: If not null is set to point at the found state. Caller will + * be responsible for freeing the state in this case. + */ +STATIC int +xfs_attr_node_hasname( + struct xfs_da_args *args, + struct xfs_da_state **statep) +{ + struct xfs_da_state *state; + int retval, error; + + state = xfs_da_state_alloc(args); + if (statep != NULL) + *statep = NULL; + + /* + * Search to see if name exists, and get back a pointer to it. + */ + error = xfs_da3_node_lookup_int(state, &retval); + if (error) { + xfs_da_state_free(state); + return error; + } + + if (statep != NULL) + *statep = state; + else + xfs_da_state_free(state); + return retval; +} + /*======================================================================== * External routines when attribute list size > geo->blksize *========================================================================*/ @@ -748,15 +826,14 @@ xfs_attr_node_addname( */ dp = args->dp; restart: - state = xfs_da_state_alloc(args); - /* * Search to see if name already exists, and get back a pointer * to where it should go. */ - error = xfs_da3_node_lookup_int(state, &retval); - if (error) + retval = xfs_attr_node_hasname(args, &state); + if (retval != -ENOATTR && retval != -EEXIST) goto out; + blk = &state->path.blk[ state->path.active-1 ]; ASSERT(blk->magic == XFS_ATTR_LEAF_MAGIC); if (retval == -ENOATTR && (args->attr_flags & XATTR_REPLACE)) @@ -960,27 +1037,15 @@ xfs_attr_node_removename( { struct xfs_da_state *state; struct xfs_da_state_blk *blk; - struct xfs_inode *dp; struct xfs_buf *bp; int retval, error, forkoff; + struct xfs_inode *dp = args->dp; trace_xfs_attr_node_removename(args); - /* - * Tie a string around our finger to remind us where we are. - */ - dp = args->dp; - state = xfs_da_state_alloc(args); - - /* - * Search to see if name exists, and get back a pointer to it. - */ - error = xfs_da3_node_lookup_int(state, &retval); - if (error || (retval != -EEXIST)) { - if (error == 0) - error = retval; + error = xfs_attr_node_hasname(args, &state); + if (error != -EEXIST) goto out; - } /* * If there is an out-of-line value, de-allocate the blocks. @@ -1075,7 +1140,8 @@ xfs_attr_node_removename( error = 0; out: - xfs_da_state_free(state); + if (state) + xfs_da_state_free(state); return error; } @@ -1191,45 +1257,41 @@ xfs_attr_refillstate(xfs_da_state_t *state) * Returns 0 on successful retrieval, otherwise an error. */ STATIC int -xfs_attr_node_get(xfs_da_args_t *args) +xfs_attr_node_get( + struct xfs_da_args *args) { - xfs_da_state_t *state; - xfs_da_state_blk_t *blk; - int error, retval; - int i; + struct xfs_da_state *state; + struct xfs_da_state_blk *blk; + int i; + int error; trace_xfs_attr_node_get(args); - state = xfs_da_state_alloc(args); - /* * Search to see if name exists, and get back a pointer to it. */ - error = xfs_da3_node_lookup_int(state, &retval); - if (error) { - retval = error; - goto out_release; - } - if (retval != -EEXIST) + error = xfs_attr_node_hasname(args, &state); + if (error != -EEXIST) goto out_release; /* * Get the value, local or "remote" */ blk = &state->path.blk[state->path.active - 1]; - retval = xfs_attr3_leaf_getvalue(blk->bp, args); + error = xfs_attr3_leaf_getvalue(blk->bp, args); /* * If not in a transaction, we have to release all the buffers. */ out_release: - for (i = 0; i < state->path.active; i++) { + for (i = 0; state != NULL && i < state->path.active; i++) { xfs_trans_brelse(args->trans, state->path.blk[i].bp); state->path.blk[i].bp = NULL; } - xfs_da_state_free(state); - return retval; + if (state) + xfs_da_state_free(state); + return error; } /* Returns true if the attribute entry name is valid. */ diff --git a/fs/xfs/libxfs/xfs_attr.h b/fs/xfs/libxfs/xfs_attr.h index db4717657ca12..3e97a935e7121 100644 --- a/fs/xfs/libxfs/xfs_attr.h +++ b/fs/xfs/libxfs/xfs_attr.h @@ -89,6 +89,7 @@ int xfs_attr_get_ilocked(struct xfs_da_args *args); int xfs_attr_get(struct xfs_da_args *args); int xfs_attr_set(struct xfs_da_args *args); int xfs_attr_set_args(struct xfs_da_args *args); +int xfs_has_attr(struct xfs_da_args *args); int xfs_attr_remove_args(struct xfs_da_args *args); bool xfs_attr_namecheck(const void *name, size_t length); diff --git a/fs/xfs/libxfs/xfs_attr_leaf.c b/fs/xfs/libxfs/xfs_attr_leaf.c index 2f7e89e4be3e3..7ec757bb3b2d3 100644 --- a/fs/xfs/libxfs/xfs_attr_leaf.c +++ b/fs/xfs/libxfs/xfs_attr_leaf.c @@ -659,19 +659,66 @@ xfs_attr_shortform_create( xfs_trans_log_inode(args->trans, dp, XFS_ILOG_CORE | XFS_ILOG_ADATA); } +/* + * Return -EEXIST if attr is found, or -ENOATTR if not + * args: args containing attribute name and namelen + * sfep: If not null, pointer will be set to the last attr entry found on + -EEXIST. On -ENOATTR pointer is left at the last entry in the list + * basep: If not null, pointer is set to the byte offset of the entry in the + * list on -EEXIST. On -ENOATTR, pointer is left at the byte offset of + * the last entry in the list + */ +int +xfs_attr_sf_findname( + struct xfs_da_args *args, + struct xfs_attr_sf_entry **sfep, + unsigned int *basep) +{ + struct xfs_attr_shortform *sf; + struct xfs_attr_sf_entry *sfe; + unsigned int base = sizeof(struct xfs_attr_sf_hdr); + int size = 0; + int end; + int i; + + sf = (struct xfs_attr_shortform *)args->dp->i_afp->if_u1.if_data; + sfe = &sf->list[0]; + end = sf->hdr.count; + for (i = 0; i < end; sfe = XFS_ATTR_SF_NEXTENTRY(sfe), + base += size, i++) { + size = XFS_ATTR_SF_ENTSIZE(sfe); + if (!xfs_attr_match(args, sfe->namelen, sfe->nameval, + sfe->flags)) + continue; + break; + } + + if (sfep != NULL) + *sfep = sfe; + + if (basep != NULL) + *basep = base; + + if (i == end) + return -ENOATTR; + return -EEXIST; +} + /* * Add a name/value pair to the shortform attribute list. * Overflow from the inode has already been checked for. */ void -xfs_attr_shortform_add(xfs_da_args_t *args, int forkoff) +xfs_attr_shortform_add( + struct xfs_da_args *args, + int forkoff) { - xfs_attr_shortform_t *sf; - xfs_attr_sf_entry_t *sfe; - int i, offset, size; - xfs_mount_t *mp; - xfs_inode_t *dp; - struct xfs_ifork *ifp; + struct xfs_attr_shortform *sf; + struct xfs_attr_sf_entry *sfe; + int offset, size; + struct xfs_mount *mp; + struct xfs_inode *dp; + struct xfs_ifork *ifp; trace_xfs_attr_sf_add(args); @@ -682,11 +729,8 @@ xfs_attr_shortform_add(xfs_da_args_t *args, int forkoff) ifp = dp->i_afp; ASSERT(ifp->if_flags & XFS_IFINLINE); sf = (xfs_attr_shortform_t *)ifp->if_u1.if_data; - sfe = &sf->list[0]; - for (i = 0; i < sf->hdr.count; sfe = XFS_ATTR_SF_NEXTENTRY(sfe), i++) { - ASSERT(!xfs_attr_match(args, sfe->namelen, sfe->nameval, - sfe->flags)); - } + if (xfs_attr_sf_findname(args, &sfe, NULL) == -EEXIST) + ASSERT(0); offset = (char *)sfe - (char *)sf; size = XFS_ATTR_SF_ENTSIZE_BYNAME(args->namelen, args->valuelen); @@ -728,31 +772,27 @@ xfs_attr_fork_remove( * Remove an attribute from the shortform attribute list structure. */ int -xfs_attr_shortform_remove(xfs_da_args_t *args) +xfs_attr_shortform_remove( + struct xfs_da_args *args) { - xfs_attr_shortform_t *sf; - xfs_attr_sf_entry_t *sfe; - int base, size=0, end, totsize, i; - xfs_mount_t *mp; - xfs_inode_t *dp; + struct xfs_attr_shortform *sf; + struct xfs_attr_sf_entry *sfe; + int size = 0, end, totsize; + unsigned int base; + struct xfs_mount *mp; + struct xfs_inode *dp; + int error; trace_xfs_attr_sf_remove(args); dp = args->dp; mp = dp->i_mount; - base = sizeof(xfs_attr_sf_hdr_t); sf = (xfs_attr_shortform_t *)dp->i_afp->if_u1.if_data; - sfe = &sf->list[0]; - end = sf->hdr.count; - for (i = 0; i < end; sfe = XFS_ATTR_SF_NEXTENTRY(sfe), - base += size, i++) { - size = XFS_ATTR_SF_ENTSIZE(sfe); - if (xfs_attr_match(args, sfe->namelen, sfe->nameval, - sfe->flags)) - break; - } - if (i == end) - return -ENOATTR; + + error = xfs_attr_sf_findname(args, &sfe, &base); + if (error != -EEXIST) + return error; + size = XFS_ATTR_SF_ENTSIZE(sfe); /* * Fix up the attribute fork data, covering the hole diff --git a/fs/xfs/libxfs/xfs_attr_leaf.h b/fs/xfs/libxfs/xfs_attr_leaf.h index 5be6be3093023..9b1c59f40a26e 100644 --- a/fs/xfs/libxfs/xfs_attr_leaf.h +++ b/fs/xfs/libxfs/xfs_attr_leaf.h @@ -52,6 +52,9 @@ int xfs_attr_shortform_getvalue(struct xfs_da_args *args); int xfs_attr_shortform_to_leaf(struct xfs_da_args *args, struct xfs_buf **leaf_bp); int xfs_attr_shortform_remove(struct xfs_da_args *args); +int xfs_attr_sf_findname(struct xfs_da_args *args, + struct xfs_attr_sf_entry **sfep, + unsigned int *basep); int xfs_attr_shortform_allfit(struct xfs_buf *bp, struct xfs_inode *dp); int xfs_attr_shortform_bytesfit(struct xfs_inode *dp, int bytes); xfs_failaddr_t xfs_attr_shortform_verify(struct xfs_inode *ip); -- GitLab From deed9512872d094ad1eae4abd0ff1c674df251d5 Mon Sep 17 00:00:00 2001 From: Allison Collins Date: Mon, 20 Jul 2020 21:47:23 -0700 Subject: [PATCH 1036/1476] xfs: Check for -ENOATTR or -EEXIST Delayed operations cannot return error codes. So we must check for these conditions first before starting set or remove operations Signed-off-by: Allison Collins Reviewed-by: Darrick J. Wong Reviewed-by: Brian Foster Signed-off-by: Darrick J. Wong Acked-by: Dave Chinner --- fs/xfs/libxfs/xfs_attr.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/fs/xfs/libxfs/xfs_attr.c b/fs/xfs/libxfs/xfs_attr.c index f7f796ea11fde..1618efda03323 100644 --- a/fs/xfs/libxfs/xfs_attr.c +++ b/fs/xfs/libxfs/xfs_attr.c @@ -404,6 +404,15 @@ xfs_attr_set( args->total, 0, quota_flags); if (error) goto out_trans_cancel; + + error = xfs_has_attr(args); + if (error == -EEXIST && (args->attr_flags & XATTR_CREATE)) + goto out_trans_cancel; + if (error == -ENOATTR && (args->attr_flags & XATTR_REPLACE)) + goto out_trans_cancel; + if (error != -ENOATTR && error != -EEXIST) + goto out_trans_cancel; + error = xfs_attr_set_args(args); if (error) goto out_trans_cancel; @@ -411,6 +420,10 @@ xfs_attr_set( if (!args->trans) goto out_unlock; } else { + error = xfs_has_attr(args); + if (error != -EEXIST) + goto out_trans_cancel; + error = xfs_attr_remove_args(args); if (error) goto out_trans_cancel; -- GitLab From 1a485fc1e96533587e2eab0e4d60798035528b7a Mon Sep 17 00:00:00 2001 From: Allison Collins Date: Mon, 20 Jul 2020 21:47:23 -0700 Subject: [PATCH 1037/1476] xfs: Factor out new helper functions xfs_attr_rmtval_set Break xfs_attr_rmtval_set into two helper functions xfs_attr_rmt_find_hole and xfs_attr_rmtval_set_value. xfs_attr_rmtval_set rolls the transaction between the helpers, but delayed operations cannot. We will use the helpers later when constructing new delayed attribute routines. Signed-off-by: Allison Collins Reviewed-by: Brian Foster Reviewed-by: Darrick J. Wong Reviewed-by: Chandan Rajendra Signed-off-by: Darrick J. Wong Acked-by: Dave Chinner --- fs/xfs/libxfs/xfs_attr_remote.c | 149 ++++++++++++++++++++------------ 1 file changed, 92 insertions(+), 57 deletions(-) diff --git a/fs/xfs/libxfs/xfs_attr_remote.c b/fs/xfs/libxfs/xfs_attr_remote.c index 01ad7f353e088..f825eedc04b8c 100644 --- a/fs/xfs/libxfs/xfs_attr_remote.c +++ b/fs/xfs/libxfs/xfs_attr_remote.c @@ -440,32 +440,23 @@ xfs_attr_rmtval_get( } /* - * Write the value associated with an attribute into the out-of-line buffer - * that we have defined for it. + * Find a "hole" in the attribute address space large enough for us to drop the + * new attribute's value into */ -int -xfs_attr_rmtval_set( +STATIC int +xfs_attr_rmt_find_hole( struct xfs_da_args *args) { struct xfs_inode *dp = args->dp; struct xfs_mount *mp = dp->i_mount; - struct xfs_bmbt_irec map; - xfs_dablk_t lblkno; - xfs_fileoff_t lfileoff = 0; - uint8_t *src = args->value; - int blkcnt; - int valuelen; - int nmap; int error; - int offset = 0; - - trace_xfs_attr_rmtval_set(args); + int blkcnt; + xfs_fileoff_t lfileoff = 0; /* - * Find a "hole" in the attribute address space large enough for - * us to drop the new attribute's value into. Because CRC enable - * attributes have headers, we can't just do a straight byte to FSB - * conversion and have to take the header space into account. + * Because CRC enable attributes have headers, we can't just do a + * straight byte to FSB conversion and have to take the header space + * into account. */ blkcnt = xfs_attr3_rmt_blocks(mp, args->rmtvaluelen); error = xfs_bmap_first_unused(args->trans, args->dp, blkcnt, &lfileoff, @@ -473,48 +464,26 @@ xfs_attr_rmtval_set( if (error) return error; - args->rmtblkno = lblkno = (xfs_dablk_t)lfileoff; + args->rmtblkno = (xfs_dablk_t)lfileoff; args->rmtblkcnt = blkcnt; - /* - * Roll through the "value", allocating blocks on disk as required. - */ - while (blkcnt > 0) { - /* - * Allocate a single extent, up to the size of the value. - * - * Note that we have to consider this a data allocation as we - * write the remote attribute without logging the contents. - * Hence we must ensure that we aren't using blocks that are on - * the busy list so that we don't overwrite blocks which have - * recently been freed but their transactions are not yet - * committed to disk. If we overwrite the contents of a busy - * extent and then crash then the block may not contain the - * correct metadata after log recovery occurs. - */ - nmap = 1; - error = xfs_bmapi_write(args->trans, dp, (xfs_fileoff_t)lblkno, - blkcnt, XFS_BMAPI_ATTRFORK, args->total, &map, - &nmap); - if (error) - return error; - error = xfs_defer_finish(&args->trans); - if (error) - return error; - - ASSERT(nmap == 1); - ASSERT((map.br_startblock != DELAYSTARTBLOCK) && - (map.br_startblock != HOLESTARTBLOCK)); - lblkno += map.br_blockcount; - blkcnt -= map.br_blockcount; + return 0; +} - /* - * Start the next trans in the chain. - */ - error = xfs_trans_roll_inode(&args->trans, dp); - if (error) - return error; - } +STATIC int +xfs_attr_rmtval_set_value( + struct xfs_da_args *args) +{ + struct xfs_inode *dp = args->dp; + struct xfs_mount *mp = dp->i_mount; + struct xfs_bmbt_irec map; + xfs_dablk_t lblkno; + uint8_t *src = args->value; + int blkcnt; + int valuelen; + int nmap; + int error; + int offset = 0; /* * Roll through the "value", copying the attribute value to the @@ -594,6 +563,72 @@ xfs_attr_rmtval_stale( return 0; } +/* + * Write the value associated with an attribute into the out-of-line buffer + * that we have defined for it. + */ +int +xfs_attr_rmtval_set( + struct xfs_da_args *args) +{ + struct xfs_inode *dp = args->dp; + struct xfs_bmbt_irec map; + xfs_dablk_t lblkno; + int blkcnt; + int nmap; + int error; + + trace_xfs_attr_rmtval_set(args); + + error = xfs_attr_rmt_find_hole(args); + if (error) + return error; + + blkcnt = args->rmtblkcnt; + lblkno = (xfs_dablk_t)args->rmtblkno; + /* + * Roll through the "value", allocating blocks on disk as required. + */ + while (blkcnt > 0) { + /* + * Allocate a single extent, up to the size of the value. + * + * Note that we have to consider this a data allocation as we + * write the remote attribute without logging the contents. + * Hence we must ensure that we aren't using blocks that are on + * the busy list so that we don't overwrite blocks which have + * recently been freed but their transactions are not yet + * committed to disk. If we overwrite the contents of a busy + * extent and then crash then the block may not contain the + * correct metadata after log recovery occurs. + */ + nmap = 1; + error = xfs_bmapi_write(args->trans, dp, (xfs_fileoff_t)lblkno, + blkcnt, XFS_BMAPI_ATTRFORK, args->total, &map, + &nmap); + if (error) + return error; + error = xfs_defer_finish(&args->trans); + if (error) + return error; + + ASSERT(nmap == 1); + ASSERT((map.br_startblock != DELAYSTARTBLOCK) && + (map.br_startblock != HOLESTARTBLOCK)); + lblkno += map.br_blockcount; + blkcnt -= map.br_blockcount; + + /* + * Start the next trans in the chain. + */ + error = xfs_trans_roll_inode(&args->trans, dp); + if (error) + return error; + } + + return xfs_attr_rmtval_set_value(args); +} + /* * Remove the value associated with an attribute by deleting the * out-of-line buffer that it is stored on. -- GitLab From e3be1272ddaf5f7482008578b467cf0ca8c35695 Mon Sep 17 00:00:00 2001 From: Allison Collins Date: Mon, 20 Jul 2020 21:47:23 -0700 Subject: [PATCH 1038/1476] xfs: Pull up trans handling in xfs_attr3_leaf_flipflags Since delayed operations cannot roll transactions, pull up the transaction handling into the calling function Signed-off-by: Allison Collins Reviewed-by: Brian Foster Reviewed-by: Darrick J. Wong Reviewed-by: Chandan Rajendra Signed-off-by: Darrick J. Wong Acked-by: Dave Chinner --- fs/xfs/libxfs/xfs_attr.c | 14 ++++++++++++++ fs/xfs/libxfs/xfs_attr_leaf.c | 7 +------ 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/fs/xfs/libxfs/xfs_attr.c b/fs/xfs/libxfs/xfs_attr.c index 1618efda03323..7912e23ff9017 100644 --- a/fs/xfs/libxfs/xfs_attr.c +++ b/fs/xfs/libxfs/xfs_attr.c @@ -620,6 +620,13 @@ xfs_attr_leaf_addname( * "old" attr and clear the incomplete flag on the "new" attr. */ error = xfs_attr3_leaf_flipflags(args); + if (error) + return error; + /* + * Commit the flag value change and start the next trans in + * series. + */ + error = xfs_trans_roll_inode(&args->trans, args->dp); if (error) return error; @@ -961,6 +968,13 @@ restart: * "old" attr and clear the incomplete flag on the "new" attr. */ error = xfs_attr3_leaf_flipflags(args); + if (error) + goto out; + /* + * Commit the flag value change and start the next trans in + * series + */ + error = xfs_trans_roll_inode(&args->trans, args->dp); if (error) goto out; diff --git a/fs/xfs/libxfs/xfs_attr_leaf.c b/fs/xfs/libxfs/xfs_attr_leaf.c index 7ec757bb3b2d3..906049db57e44 100644 --- a/fs/xfs/libxfs/xfs_attr_leaf.c +++ b/fs/xfs/libxfs/xfs_attr_leaf.c @@ -2951,10 +2951,5 @@ xfs_attr3_leaf_flipflags( XFS_DA_LOGRANGE(leaf2, name_rmt, sizeof(*name_rmt))); } - /* - * Commit the flag value change and start the next trans in series. - */ - error = xfs_trans_roll_inode(&args->trans, args->dp); - - return error; + return 0; } -- GitLab From 7c93d4a8fc39cf209c6a67ff26fc83646c9f7d61 Mon Sep 17 00:00:00 2001 From: Allison Collins Date: Mon, 20 Jul 2020 21:47:24 -0700 Subject: [PATCH 1039/1476] xfs: Split apart xfs_attr_leaf_addname Split out new helper function xfs_attr_leaf_try_add from xfs_attr_leaf_addname. Because new delayed attribute routines cannot roll transactions, we split off the parts of xfs_attr_leaf_addname that we can use, and move the commit into the calling function. Signed-off-by: Allison Collins Reviewed-by: Brian Foster Reviewed-by: Chandan Rajendra Reviewed-by: Darrick J. Wong Signed-off-by: Darrick J. Wong Acked-by: Dave Chinner --- fs/xfs/libxfs/xfs_attr.c | 95 ++++++++++++++++++++++++++-------------- 1 file changed, 61 insertions(+), 34 deletions(-) diff --git a/fs/xfs/libxfs/xfs_attr.c b/fs/xfs/libxfs/xfs_attr.c index 7912e23ff9017..2fbd3cbf88499 100644 --- a/fs/xfs/libxfs/xfs_attr.c +++ b/fs/xfs/libxfs/xfs_attr.c @@ -256,10 +256,31 @@ xfs_attr_set_args( } } - if (xfs_bmap_one_block(dp, XFS_ATTR_FORK)) + if (xfs_bmap_one_block(dp, XFS_ATTR_FORK)) { error = xfs_attr_leaf_addname(args); - else - error = xfs_attr_node_addname(args); + if (error != -ENOSPC) + return error; + + /* + * Finish any deferred work items and roll the transaction once + * more. The goal here is to call node_addname with the inode + * and transaction in the same state (inode locked and joined, + * transaction clean) no matter how we got to this step. + */ + error = xfs_defer_finish(&args->trans); + if (error) + return error; + + /* + * Commit the current trans (including the inode) and + * start a new one. + */ + error = xfs_trans_roll_inode(&args->trans, dp); + if (error) + return error; + } + + error = xfs_attr_node_addname(args); return error; } @@ -507,20 +528,21 @@ xfs_attr_shortform_addname(xfs_da_args_t *args) *========================================================================*/ /* - * Add a name to the leaf attribute list structure + * Tries to add an attribute to an inode in leaf form * - * This leaf block cannot have a "remote" value, we only call this routine - * if bmap_one_block() says there is only one block (ie: no remote blks). + * This function is meant to execute as part of a delayed operation and leaves + * the transaction handling to the caller. On success the attribute is added + * and the inode and transaction are left dirty. If there is not enough space, + * the attr data is converted to node format and -ENOSPC is returned. Caller is + * responsible for handling the dirty inode and transaction or adding the attr + * in node format. */ STATIC int -xfs_attr_leaf_addname( - struct xfs_da_args *args) +xfs_attr_leaf_try_add( + struct xfs_da_args *args, + struct xfs_buf *bp) { - struct xfs_buf *bp; - int retval, error, forkoff; - struct xfs_inode *dp = args->dp; - - trace_xfs_attr_leaf_addname(args); + int retval, error; /* * Look up the given attribute in the leaf block. Figure out if @@ -562,31 +584,39 @@ xfs_attr_leaf_addname( retval = xfs_attr3_leaf_add(bp, args); if (retval == -ENOSPC) { /* - * Promote the attribute list to the Btree format, then - * Commit that transaction so that the node_addname() call - * can manage its own transactions. + * Promote the attribute list to the Btree format. Unless an + * error occurs, retain the -ENOSPC retval */ error = xfs_attr3_leaf_to_node(args); if (error) return error; - error = xfs_defer_finish(&args->trans); - if (error) - return error; + } + return retval; +out_brelse: + xfs_trans_brelse(args->trans, bp); + return retval; +} - /* - * Commit the current trans (including the inode) and start - * a new one. - */ - error = xfs_trans_roll_inode(&args->trans, dp); - if (error) - return error; - /* - * Fob the whole rest of the problem off on the Btree code. - */ - error = xfs_attr_node_addname(args); +/* + * Add a name to the leaf attribute list structure + * + * This leaf block cannot have a "remote" value, we only call this routine + * if bmap_one_block() says there is only one block (ie: no remote blks). + */ +STATIC int +xfs_attr_leaf_addname( + struct xfs_da_args *args) +{ + int error, forkoff; + struct xfs_buf *bp = NULL; + struct xfs_inode *dp = args->dp; + + trace_xfs_attr_leaf_addname(args); + + error = xfs_attr_leaf_try_add(args, bp); + if (error) return error; - } /* * Commit the transaction that added the attr name so that @@ -681,9 +711,6 @@ xfs_attr_leaf_addname( error = xfs_attr3_leaf_clearflag(args); } return error; -out_brelse: - xfs_trans_brelse(args->trans, bp); - return retval; } /* -- GitLab From 6cc5b5f89840cfe85cbd14e20500f25353a7f241 Mon Sep 17 00:00:00 2001 From: Allison Collins Date: Mon, 20 Jul 2020 21:47:24 -0700 Subject: [PATCH 1040/1476] xfs: Refactor xfs_attr_try_sf_addname To help pre-simplify xfs_attr_set_args, we need to hoist transaction handling up, while modularizing the adjacent code down into helpers. In this patch, hoist the commit in xfs_attr_try_sf_addname up into the calling function, and also pull the attr list creation down. Signed-off-by: Allison Collins Reviewed-by: Darrick J. Wong Reviewed-by: Amir Goldstein Reviewed-by: Brian Foster Reviewed-by: Chandan Rajendra Signed-off-by: Darrick J. Wong Acked-by: Dave Chinner --- fs/xfs/libxfs/xfs_attr.c | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/fs/xfs/libxfs/xfs_attr.c b/fs/xfs/libxfs/xfs_attr.c index 2fbd3cbf88499..9e57409edc555 100644 --- a/fs/xfs/libxfs/xfs_attr.c +++ b/fs/xfs/libxfs/xfs_attr.c @@ -178,8 +178,13 @@ xfs_attr_try_sf_addname( struct xfs_da_args *args) { - struct xfs_mount *mp = dp->i_mount; - int error, error2; + int error; + + /* + * Build initial attribute list (if required). + */ + if (dp->i_afp->if_format == XFS_DINODE_FMT_EXTENTS) + xfs_attr_shortform_create(args); error = xfs_attr_shortform_addname(args); if (error == -ENOSPC) @@ -192,12 +197,10 @@ xfs_attr_try_sf_addname( if (!error && !(args->op_flags & XFS_DA_OP_NOTIME)) xfs_trans_ichgtime(args->trans, dp, XFS_ICHGTIME_CHG); - if (mp->m_flags & XFS_MOUNT_WSYNC) + if (dp->i_mount->m_flags & XFS_MOUNT_WSYNC) xfs_trans_set_sync(args->trans); - error2 = xfs_trans_commit(args->trans); - args->trans = NULL; - return error ? error : error2; + return error; } /* @@ -209,7 +212,7 @@ xfs_attr_set_args( { struct xfs_inode *dp = args->dp; struct xfs_buf *leaf_bp = NULL; - int error; + int error, error2 = 0; /* * If the attribute list is non-existent or a shortform list, @@ -219,18 +222,15 @@ xfs_attr_set_args( (dp->i_afp->if_format == XFS_DINODE_FMT_EXTENTS && dp->i_afp->if_nextents == 0)) { - /* - * Build initial attribute list (if required). - */ - if (dp->i_afp->if_format == XFS_DINODE_FMT_EXTENTS) - xfs_attr_shortform_create(args); - /* * Try to add the attr to the attribute list in the inode. */ error = xfs_attr_try_sf_addname(dp, args); - if (error != -ENOSPC) - return error; + if (error != -ENOSPC) { + error2 = xfs_trans_commit(args->trans); + args->trans = NULL; + return error ? error : error2; + } /* * It won't fit in the shortform, transform to a leaf block. -- GitLab From 0949d317aee051fcb7ad9c8c7ec5d60b5cc412eb Mon Sep 17 00:00:00 2001 From: Allison Collins Date: Mon, 20 Jul 2020 21:47:25 -0700 Subject: [PATCH 1041/1476] xfs: Pull up trans roll from xfs_attr3_leaf_setflag New delayed allocation routines cannot be handling transactions so pull them up into the calling functions Signed-off-by: Allison Collins Reviewed-by: Darrick J. Wong Reviewed-by: Brian Foster Reviewed-by: Chandan Rajendra Signed-off-by: Darrick J. Wong Acked-by: Dave Chinner --- fs/xfs/libxfs/xfs_attr.c | 5 +++++ fs/xfs/libxfs/xfs_attr_leaf.c | 5 +---- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/fs/xfs/libxfs/xfs_attr.c b/fs/xfs/libxfs/xfs_attr.c index 9e57409edc555..d3ab32d0f6ce6 100644 --- a/fs/xfs/libxfs/xfs_attr.c +++ b/fs/xfs/libxfs/xfs_attr.c @@ -1126,6 +1126,11 @@ xfs_attr_node_removename( error = xfs_attr3_leaf_setflag(args); if (error) goto out; + + error = xfs_trans_roll_inode(&args->trans, args->dp); + if (error) + goto out; + error = xfs_attr_rmtval_remove(args); if (error) goto out; diff --git a/fs/xfs/libxfs/xfs_attr_leaf.c b/fs/xfs/libxfs/xfs_attr_leaf.c index 906049db57e44..7c72ff53f4bf3 100644 --- a/fs/xfs/libxfs/xfs_attr_leaf.c +++ b/fs/xfs/libxfs/xfs_attr_leaf.c @@ -2833,10 +2833,7 @@ xfs_attr3_leaf_setflag( XFS_DA_LOGRANGE(leaf, name_rmt, sizeof(*name_rmt))); } - /* - * Commit the flag value change and start the next trans in series. - */ - return xfs_trans_roll_inode(&args->trans, args->dp); + return 0; } /* -- GitLab From 795141099abc2ef4c801ca847190757880371390 Mon Sep 17 00:00:00 2001 From: Allison Collins Date: Mon, 20 Jul 2020 21:47:25 -0700 Subject: [PATCH 1042/1476] xfs: Factor out xfs_attr_rmtval_invalidate Because new delayed attribute routines cannot roll transactions, we carve off the parts of xfs_attr_rmtval_remove that we can use. This will help to reduce repetitive code later when we introduce delayed attributes. Signed-off-by: Allison Collins Reviewed-by: Brian Foster Reviewed-by: Darrick J. Wong Reviewed-by: Chandan Rajendra Signed-off-by: Darrick J. Wong Acked-by: Dave Chinner --- fs/xfs/libxfs/xfs_attr_remote.c | 26 +++++++++++++++++++++----- fs/xfs/libxfs/xfs_attr_remote.h | 2 +- 2 files changed, 22 insertions(+), 6 deletions(-) diff --git a/fs/xfs/libxfs/xfs_attr_remote.c b/fs/xfs/libxfs/xfs_attr_remote.c index f825eedc04b8c..4d519691daafc 100644 --- a/fs/xfs/libxfs/xfs_attr_remote.c +++ b/fs/xfs/libxfs/xfs_attr_remote.c @@ -634,15 +634,12 @@ xfs_attr_rmtval_set( * out-of-line buffer that it is stored on. */ int -xfs_attr_rmtval_remove( +xfs_attr_rmtval_invalidate( struct xfs_da_args *args) { xfs_dablk_t lblkno; int blkcnt; int error; - int done; - - trace_xfs_attr_rmtval_remove(args); /* * Roll through the "value", invalidating the attribute value's blocks. @@ -670,13 +667,32 @@ xfs_attr_rmtval_remove( lblkno += map.br_blockcount; blkcnt -= map.br_blockcount; } + return 0; +} +/* + * Remove the value associated with an attribute by deleting the + * out-of-line buffer that it is stored on. + */ +int +xfs_attr_rmtval_remove( + struct xfs_da_args *args) +{ + xfs_dablk_t lblkno; + int blkcnt; + int error = 0; + int done = 0; + + trace_xfs_attr_rmtval_remove(args); + + error = xfs_attr_rmtval_invalidate(args); + if (error) + return error; /* * Keep de-allocating extents until the remote-value region is gone. */ lblkno = args->rmtblkno; blkcnt = args->rmtblkcnt; - done = 0; while (!done) { error = xfs_bunmapi(args->trans, args->dp, lblkno, blkcnt, XFS_BMAPI_ATTRFORK, 1, &done); diff --git a/fs/xfs/libxfs/xfs_attr_remote.h b/fs/xfs/libxfs/xfs_attr_remote.h index e1144f22b0052..3616e888f7948 100644 --- a/fs/xfs/libxfs/xfs_attr_remote.h +++ b/fs/xfs/libxfs/xfs_attr_remote.h @@ -13,5 +13,5 @@ int xfs_attr_rmtval_set(struct xfs_da_args *args); int xfs_attr_rmtval_remove(struct xfs_da_args *args); int xfs_attr_rmtval_stale(struct xfs_inode *ip, struct xfs_bmbt_irec *map, xfs_buf_flags_t incore_flags); - +int xfs_attr_rmtval_invalidate(struct xfs_da_args *args); #endif /* __XFS_ATTR_REMOTE_H__ */ -- GitLab From 1fc618d76266750230f19e4053f5422a58b84141 Mon Sep 17 00:00:00 2001 From: Allison Collins Date: Mon, 20 Jul 2020 21:47:26 -0700 Subject: [PATCH 1043/1476] xfs: Pull up trans roll in xfs_attr3_leaf_clearflag New delayed allocation routines cannot be handling transactions so pull them out into the calling functions Signed-off-by: Allison Collins Reviewed-by: Brian Foster Reviewed-by: Darrick J. Wong Reviewed-by: Chandan Rajendra Signed-off-by: Darrick J. Wong Acked-by: Dave Chinner --- fs/xfs/libxfs/xfs_attr.c | 16 ++++++++++++++++ fs/xfs/libxfs/xfs_attr_leaf.c | 5 +---- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/fs/xfs/libxfs/xfs_attr.c b/fs/xfs/libxfs/xfs_attr.c index d3ab32d0f6ce6..2d84ab40b70b4 100644 --- a/fs/xfs/libxfs/xfs_attr.c +++ b/fs/xfs/libxfs/xfs_attr.c @@ -709,6 +709,14 @@ xfs_attr_leaf_addname( * Added a "remote" value, just clear the incomplete flag. */ error = xfs_attr3_leaf_clearflag(args); + if (error) + return error; + + /* + * Commit the flag value change and start the next trans in + * series. + */ + error = xfs_trans_roll_inode(&args->trans, args->dp); } return error; } @@ -1067,6 +1075,14 @@ restart: error = xfs_attr3_leaf_clearflag(args); if (error) goto out; + + /* + * Commit the flag value change and start the next trans in + * series. + */ + error = xfs_trans_roll_inode(&args->trans, args->dp); + if (error) + goto out; } retval = error = 0; diff --git a/fs/xfs/libxfs/xfs_attr_leaf.c b/fs/xfs/libxfs/xfs_attr_leaf.c index 7c72ff53f4bf3..8623c815164a6 100644 --- a/fs/xfs/libxfs/xfs_attr_leaf.c +++ b/fs/xfs/libxfs/xfs_attr_leaf.c @@ -2782,10 +2782,7 @@ xfs_attr3_leaf_clearflag( XFS_DA_LOGRANGE(leaf, name_rmt, sizeof(*name_rmt))); } - /* - * Commit the flag value change and start the next trans in series. - */ - return xfs_trans_roll_inode(&args->trans, args->dp); + return 0; } /* -- GitLab From 8b8e0cc0208befc5971d552a8798c8f5537afa45 Mon Sep 17 00:00:00 2001 From: Allison Collins Date: Mon, 20 Jul 2020 21:47:26 -0700 Subject: [PATCH 1044/1476] xfs: Refactor xfs_attr_rmtval_remove Refactor xfs_attr_rmtval_remove to add helper function __xfs_attr_rmtval_remove. We will use this later when we introduce delayed attributes. This function will eventually replace xfs_attr_rmtval_remove Signed-off-by: Allison Collins Reviewed-by: Chandan Rajendra Reviewed-by: Brian Foster Reviewed-by: Darrick J. Wong Signed-off-by: Darrick J. Wong Acked-by: Dave Chinner --- fs/xfs/libxfs/xfs_attr_remote.c | 52 +++++++++++++++++++++++---------- fs/xfs/libxfs/xfs_attr_remote.h | 1 + 2 files changed, 38 insertions(+), 15 deletions(-) diff --git a/fs/xfs/libxfs/xfs_attr_remote.c b/fs/xfs/libxfs/xfs_attr_remote.c index 4d519691daafc..3e7f6e0758604 100644 --- a/fs/xfs/libxfs/xfs_attr_remote.c +++ b/fs/xfs/libxfs/xfs_attr_remote.c @@ -678,10 +678,8 @@ int xfs_attr_rmtval_remove( struct xfs_da_args *args) { - xfs_dablk_t lblkno; - int blkcnt; - int error = 0; - int done = 0; + int error; + int retval; trace_xfs_attr_rmtval_remove(args); @@ -691,16 +689,10 @@ xfs_attr_rmtval_remove( /* * Keep de-allocating extents until the remote-value region is gone. */ - lblkno = args->rmtblkno; - blkcnt = args->rmtblkcnt; - while (!done) { - error = xfs_bunmapi(args->trans, args->dp, lblkno, blkcnt, - XFS_BMAPI_ATTRFORK, 1, &done); - if (error) - return error; - error = xfs_defer_finish(&args->trans); - if (error) - return error; + do { + retval = __xfs_attr_rmtval_remove(args); + if (retval && retval != -EAGAIN) + return retval; /* * Close out trans and start the next one in the chain. @@ -708,6 +700,36 @@ xfs_attr_rmtval_remove( error = xfs_trans_roll_inode(&args->trans, args->dp); if (error) return error; - } + } while (retval == -EAGAIN); + return 0; } + +/* + * Remove the value associated with an attribute by deleting the out-of-line + * buffer that it is stored on. Returns EAGAIN for the caller to refresh the + * transaction and re-call the function + */ +int +__xfs_attr_rmtval_remove( + struct xfs_da_args *args) +{ + int error, done; + + /* + * Unmap value blocks for this attr. + */ + error = xfs_bunmapi(args->trans, args->dp, args->rmtblkno, + args->rmtblkcnt, XFS_BMAPI_ATTRFORK, 1, &done); + if (error) + return error; + + error = xfs_defer_finish(&args->trans); + if (error) + return error; + + if (!done) + return -EAGAIN; + + return error; +} diff --git a/fs/xfs/libxfs/xfs_attr_remote.h b/fs/xfs/libxfs/xfs_attr_remote.h index 3616e888f7948..9eee615da1567 100644 --- a/fs/xfs/libxfs/xfs_attr_remote.h +++ b/fs/xfs/libxfs/xfs_attr_remote.h @@ -14,4 +14,5 @@ int xfs_attr_rmtval_remove(struct xfs_da_args *args); int xfs_attr_rmtval_stale(struct xfs_inode *ip, struct xfs_bmbt_irec *map, xfs_buf_flags_t incore_flags); int xfs_attr_rmtval_invalidate(struct xfs_da_args *args); +int __xfs_attr_rmtval_remove(struct xfs_da_args *args); #endif /* __XFS_ATTR_REMOTE_H__ */ -- GitLab From d4034c4662af5d40de0655c641ddc6eccde0e8fc Mon Sep 17 00:00:00 2001 From: Allison Collins Date: Mon, 20 Jul 2020 21:47:26 -0700 Subject: [PATCH 1045/1476] xfs: Pull up xfs_attr_rmtval_invalidate This patch pulls xfs_attr_rmtval_invalidate out of xfs_attr_rmtval_remove and into the calling functions. Eventually __xfs_attr_rmtval_remove will replace xfs_attr_rmtval_remove when we introduce delayed attributes. These functions are exepcted to return -EAGAIN when they need a new transaction. Because the invalidate does not need a new transaction, we need to separate it from the rest of the function that does. This will enable __xfs_attr_rmtval_remove to smoothly replace xfs_attr_rmtval_remove later. Signed-off-by: Allison Collins Reviewed-by: Brian Foster Reviewed-by: Darrick J. Wong Signed-off-by: Darrick J. Wong Acked-by: Dave Chinner --- fs/xfs/libxfs/xfs_attr.c | 12 ++++++++++++ fs/xfs/libxfs/xfs_attr_remote.c | 3 --- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/fs/xfs/libxfs/xfs_attr.c b/fs/xfs/libxfs/xfs_attr.c index 2d84ab40b70b4..2379d5b1a1b91 100644 --- a/fs/xfs/libxfs/xfs_attr.c +++ b/fs/xfs/libxfs/xfs_attr.c @@ -670,6 +670,10 @@ xfs_attr_leaf_addname( args->rmtblkcnt = args->rmtblkcnt2; args->rmtvaluelen = args->rmtvaluelen2; if (args->rmtblkno) { + error = xfs_attr_rmtval_invalidate(args); + if (error) + return error; + error = xfs_attr_rmtval_remove(args); if (error) return error; @@ -1023,6 +1027,10 @@ restart: args->rmtblkcnt = args->rmtblkcnt2; args->rmtvaluelen = args->rmtvaluelen2; if (args->rmtblkno) { + error = xfs_attr_rmtval_invalidate(args); + if (error) + return error; + error = xfs_attr_rmtval_remove(args); if (error) return error; @@ -1147,6 +1155,10 @@ xfs_attr_node_removename( if (error) goto out; + error = xfs_attr_rmtval_invalidate(args); + if (error) + return error; + error = xfs_attr_rmtval_remove(args); if (error) goto out; diff --git a/fs/xfs/libxfs/xfs_attr_remote.c b/fs/xfs/libxfs/xfs_attr_remote.c index 3e7f6e0758604..3f80cede74063 100644 --- a/fs/xfs/libxfs/xfs_attr_remote.c +++ b/fs/xfs/libxfs/xfs_attr_remote.c @@ -683,9 +683,6 @@ xfs_attr_rmtval_remove( trace_xfs_attr_rmtval_remove(args); - error = xfs_attr_rmtval_invalidate(args); - if (error) - return error; /* * Keep de-allocating extents until the remote-value region is gone. */ -- GitLab From 3f6e011ee2bfcf5b70c5f8978673cd0e5bc64a33 Mon Sep 17 00:00:00 2001 From: Allison Collins Date: Mon, 20 Jul 2020 21:47:27 -0700 Subject: [PATCH 1046/1476] xfs: Add helper function xfs_attr_node_shrink This patch adds a new helper function xfs_attr_node_shrink used to shrink an attr name into an inode if it is small enough. This helps to modularize the greater calling function xfs_attr_node_removename. Signed-off-by: Allison Collins Reviewed-by: Darrick J. Wong Reviewed-by: Brian Foster Signed-off-by: Darrick J. Wong Acked-by: Dave Chinner --- fs/xfs/libxfs/xfs_attr.c | 68 +++++++++++++++++++++++++--------------- 1 file changed, 42 insertions(+), 26 deletions(-) diff --git a/fs/xfs/libxfs/xfs_attr.c b/fs/xfs/libxfs/xfs_attr.c index 2379d5b1a1b91..b576f46d1d75e 100644 --- a/fs/xfs/libxfs/xfs_attr.c +++ b/fs/xfs/libxfs/xfs_attr.c @@ -1102,6 +1102,45 @@ out: return retval; } +/* + * Shrink an attribute from leaf to shortform + */ +STATIC int +xfs_attr_node_shrink( + struct xfs_da_args *args, + struct xfs_da_state *state) +{ + struct xfs_inode *dp = args->dp; + int error, forkoff; + struct xfs_buf *bp; + + /* + * Have to get rid of the copy of this dabuf in the state. + */ + ASSERT(state->path.active == 1); + ASSERT(state->path.blk[0].bp); + state->path.blk[0].bp = NULL; + + error = xfs_attr3_leaf_read(args->trans, args->dp, 0, &bp); + if (error) + return error; + + forkoff = xfs_attr_shortform_allfit(bp, dp); + if (forkoff) { + error = xfs_attr3_leaf_to_shortform(bp, args, forkoff); + /* bp is gone due to xfs_da_shrink_inode */ + if (error) + return error; + + error = xfs_defer_finish(&args->trans); + if (error) + return error; + } else + xfs_trans_brelse(args->trans, bp); + + return 0; +} + /* * Remove a name from a B-tree attribute list. * @@ -1115,8 +1154,7 @@ xfs_attr_node_removename( { struct xfs_da_state *state; struct xfs_da_state_blk *blk; - struct xfs_buf *bp; - int retval, error, forkoff; + int retval, error; struct xfs_inode *dp = args->dp; trace_xfs_attr_node_removename(args); @@ -1201,30 +1239,8 @@ xfs_attr_node_removename( /* * If the result is small enough, push it all into the inode. */ - if (xfs_bmap_one_block(dp, XFS_ATTR_FORK)) { - /* - * Have to get rid of the copy of this dabuf in the state. - */ - ASSERT(state->path.active == 1); - ASSERT(state->path.blk[0].bp); - state->path.blk[0].bp = NULL; - - error = xfs_attr3_leaf_read(args->trans, args->dp, 0, &bp); - if (error) - goto out; - - if ((forkoff = xfs_attr_shortform_allfit(bp, dp))) { - error = xfs_attr3_leaf_to_shortform(bp, args, forkoff); - /* bp is gone due to xfs_da_shrink_inode */ - if (error) - goto out; - error = xfs_defer_finish(&args->trans); - if (error) - goto out; - } else - xfs_trans_brelse(args->trans, bp); - } - error = 0; + if (xfs_bmap_one_block(dp, XFS_ATTR_FORK)) + error = xfs_attr_node_shrink(args, state); out: if (state) -- GitLab From 0feaef17db9ddf0d97676eaa258dc55ff504cfb6 Mon Sep 17 00:00:00 2001 From: Allison Collins Date: Mon, 20 Jul 2020 21:47:27 -0700 Subject: [PATCH 1047/1476] xfs: Remove unneeded xfs_trans_roll_inode calls Some calls to xfs_trans_roll_inode and xfs_defer_finish routines are not needed. If they are the last operations executed in these functions, and no further changes are made, then higher level routines will roll or commit the transactions. Signed-off-by: Allison Collins Reviewed-by: Brian Foster Reviewed-by: Darrick J. Wong Signed-off-by: Darrick J. Wong Acked-by: Dave Chinner --- fs/xfs/libxfs/xfs_attr.c | 61 +++++----------------------------------- 1 file changed, 7 insertions(+), 54 deletions(-) diff --git a/fs/xfs/libxfs/xfs_attr.c b/fs/xfs/libxfs/xfs_attr.c index b576f46d1d75e..2daf3084f3e38 100644 --- a/fs/xfs/libxfs/xfs_attr.c +++ b/fs/xfs/libxfs/xfs_attr.c @@ -693,34 +693,15 @@ xfs_attr_leaf_addname( /* * If the result is small enough, shrink it all into the inode. */ - if ((forkoff = xfs_attr_shortform_allfit(bp, dp))) { + forkoff = xfs_attr_shortform_allfit(bp, dp); + if (forkoff) error = xfs_attr3_leaf_to_shortform(bp, args, forkoff); /* bp is gone due to xfs_da_shrink_inode */ - if (error) - return error; - error = xfs_defer_finish(&args->trans); - if (error) - return error; - } - - /* - * Commit the remove and start the next trans in series. - */ - error = xfs_trans_roll_inode(&args->trans, dp); - } else if (args->rmtblkno > 0) { /* * Added a "remote" value, just clear the incomplete flag. */ error = xfs_attr3_leaf_clearflag(args); - if (error) - return error; - - /* - * Commit the flag value change and start the next trans in - * series. - */ - error = xfs_trans_roll_inode(&args->trans, args->dp); } return error; } @@ -780,15 +761,11 @@ xfs_attr_leaf_removename( /* * If the result is small enough, shrink it all into the inode. */ - if ((forkoff = xfs_attr_shortform_allfit(bp, dp))) { - error = xfs_attr3_leaf_to_shortform(bp, args, forkoff); + forkoff = xfs_attr_shortform_allfit(bp, dp); + if (forkoff) + return xfs_attr3_leaf_to_shortform(bp, args, forkoff); /* bp is gone due to xfs_da_shrink_inode */ - if (error) - return error; - error = xfs_defer_finish(&args->trans); - if (error) - return error; - } + return 0; } @@ -1064,18 +1041,8 @@ restart: error = xfs_da3_join(state); if (error) goto out; - error = xfs_defer_finish(&args->trans); - if (error) - goto out; } - /* - * Commit and start the next trans in the chain. - */ - error = xfs_trans_roll_inode(&args->trans, dp); - if (error) - goto out; - } else if (args->rmtblkno > 0) { /* * Added a "remote" value, just clear the incomplete flag. @@ -1083,14 +1050,6 @@ restart: error = xfs_attr3_leaf_clearflag(args); if (error) goto out; - - /* - * Commit the flag value change and start the next trans in - * series. - */ - error = xfs_trans_roll_inode(&args->trans, args->dp); - if (error) - goto out; } retval = error = 0; @@ -1129,16 +1088,10 @@ xfs_attr_node_shrink( if (forkoff) { error = xfs_attr3_leaf_to_shortform(bp, args, forkoff); /* bp is gone due to xfs_da_shrink_inode */ - if (error) - return error; - - error = xfs_defer_finish(&args->trans); - if (error) - return error; } else xfs_trans_brelse(args->trans, bp); - return 0; + return error; } /* -- GitLab From a237f2ddae4b79797fcb4290116acda38323da16 Mon Sep 17 00:00:00 2001 From: Allison Collins Date: Mon, 20 Jul 2020 21:47:28 -0700 Subject: [PATCH 1048/1476] xfs: Remove xfs_trans_roll in xfs_attr_node_removename A transaction roll is not necessary immediately after setting the INCOMPLETE flag when removing a node xattr entry with remote value blocks. The remote block invalidation that immediately follows setting the flag is an in-core only change. The next step after that is to start unmapping the remote blocks from the attr fork, but the xattr remove transaction reservation includes reservation for full tree splits of the dabtree and bmap tree. The remote block unmap code will roll the transaction as extents are unmapped and freed. Signed-off-by: Allison Collins Reviewed-by: Brian Foster Reviewed-by: Darrick J. Wong Signed-off-by: Darrick J. Wong Acked-by: Dave Chinner --- fs/xfs/libxfs/xfs_attr.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/fs/xfs/libxfs/xfs_attr.c b/fs/xfs/libxfs/xfs_attr.c index 2daf3084f3e38..780af0ef6bc64 100644 --- a/fs/xfs/libxfs/xfs_attr.c +++ b/fs/xfs/libxfs/xfs_attr.c @@ -1142,10 +1142,6 @@ xfs_attr_node_removename( if (error) goto out; - error = xfs_trans_roll_inode(&args->trans, args->dp); - if (error) - goto out; - error = xfs_attr_rmtval_invalidate(args); if (error) return error; -- GitLab From db1a28cc591c1abe5470bedca1a489bb165c4c7c Mon Sep 17 00:00:00 2001 From: Allison Collins Date: Mon, 20 Jul 2020 21:47:28 -0700 Subject: [PATCH 1049/1476] xfs: Add helpers xfs_attr_is_shortform and xfs_attr_set_shortform In this patch, we hoist code from xfs_attr_set_args into two new helpers xfs_attr_is_shortform and xfs_attr_set_shortform. These two will help to simplify xfs_attr_set_args when we get into delayed attrs later. Signed-off-by: Allison Collins Reviewed-by: Brian Foster Reviewed-by: Chandan Rajendra Reviewed-by: Darrick J. Wong Signed-off-by: Darrick J. Wong Acked-by: Dave Chinner --- fs/xfs/libxfs/xfs_attr.c | 107 ++++++++++++++++++++++++++------------- 1 file changed, 72 insertions(+), 35 deletions(-) diff --git a/fs/xfs/libxfs/xfs_attr.c b/fs/xfs/libxfs/xfs_attr.c index 780af0ef6bc64..93a1c55cceb29 100644 --- a/fs/xfs/libxfs/xfs_attr.c +++ b/fs/xfs/libxfs/xfs_attr.c @@ -203,6 +203,66 @@ xfs_attr_try_sf_addname( return error; } +/* + * Check to see if the attr should be upgraded from non-existent or shortform to + * single-leaf-block attribute list. + */ +static inline bool +xfs_attr_is_shortform( + struct xfs_inode *ip) +{ + return ip->i_afp->if_format == XFS_DINODE_FMT_LOCAL || + (ip->i_afp->if_format == XFS_DINODE_FMT_EXTENTS && + ip->i_afp->if_nextents == 0); +} + +/* + * Attempts to set an attr in shortform, or converts short form to leaf form if + * there is not enough room. If the attr is set, the transaction is committed + * and set to NULL. + */ +STATIC int +xfs_attr_set_shortform( + struct xfs_da_args *args, + struct xfs_buf **leaf_bp) +{ + struct xfs_inode *dp = args->dp; + int error, error2 = 0; + + /* + * Try to add the attr to the attribute list in the inode. + */ + error = xfs_attr_try_sf_addname(dp, args); + if (error != -ENOSPC) { + error2 = xfs_trans_commit(args->trans); + args->trans = NULL; + return error ? error : error2; + } + /* + * It won't fit in the shortform, transform to a leaf block. GROT: + * another possible req'mt for a double-split btree op. + */ + error = xfs_attr_shortform_to_leaf(args, leaf_bp); + if (error) + return error; + + /* + * Prevent the leaf buffer from being unlocked so that a concurrent AIL + * push cannot grab the half-baked leaf buffer and run into problems + * with the write verifier. Once we're done rolling the transaction we + * can release the hold and add the attr to the leaf. + */ + xfs_trans_bhold(args->trans, *leaf_bp); + error = xfs_defer_finish(&args->trans); + xfs_trans_bhold_release(args->trans, *leaf_bp); + if (error) { + xfs_trans_brelse(args->trans, *leaf_bp); + return error; + } + + return 0; +} + /* * Set the attribute specified in @args. */ @@ -212,48 +272,25 @@ xfs_attr_set_args( { struct xfs_inode *dp = args->dp; struct xfs_buf *leaf_bp = NULL; - int error, error2 = 0; + int error = 0; /* - * If the attribute list is non-existent or a shortform list, - * upgrade it to a single-leaf-block attribute list. + * If the attribute list is already in leaf format, jump straight to + * leaf handling. Otherwise, try to add the attribute to the shortform + * list; if there's no room then convert the list to leaf format and try + * again. */ - if (dp->i_afp->if_format == XFS_DINODE_FMT_LOCAL || - (dp->i_afp->if_format == XFS_DINODE_FMT_EXTENTS && - dp->i_afp->if_nextents == 0)) { + if (xfs_attr_is_shortform(dp)) { /* - * Try to add the attr to the attribute list in the inode. + * If the attr was successfully set in shortform, the + * transaction is committed and set to NULL. Otherwise, is it + * converted from shortform to leaf, and the transaction is + * retained. */ - error = xfs_attr_try_sf_addname(dp, args); - if (error != -ENOSPC) { - error2 = xfs_trans_commit(args->trans); - args->trans = NULL; - return error ? error : error2; - } - - /* - * It won't fit in the shortform, transform to a leaf block. - * GROT: another possible req'mt for a double-split btree op. - */ - error = xfs_attr_shortform_to_leaf(args, &leaf_bp); - if (error) - return error; - - /* - * Prevent the leaf buffer from being unlocked so that a - * concurrent AIL push cannot grab the half-baked leaf - * buffer and run into problems with the write verifier. - * Once we're done rolling the transaction we can release - * the hold and add the attr to the leaf. - */ - xfs_trans_bhold(args->trans, leaf_bp); - error = xfs_defer_finish(&args->trans); - xfs_trans_bhold_release(args->trans, leaf_bp); - if (error) { - xfs_trans_brelse(args->trans, leaf_bp); + error = xfs_attr_set_shortform(args, &leaf_bp); + if (error || !args->trans) return error; - } } if (xfs_bmap_one_block(dp, XFS_ATTR_FORK)) { -- GitLab From f44df68c82dc060b9b9942e204096447e1efc677 Mon Sep 17 00:00:00 2001 From: Allison Collins Date: Mon, 20 Jul 2020 21:47:28 -0700 Subject: [PATCH 1050/1476] xfs: Add helper function xfs_attr_leaf_mark_incomplete This patch helps to simplify xfs_attr_node_removename by modularizing the code around the transactions into helper functions. This will make the function easier to follow when we introduce delayed attributes. Signed-off-by: Allison Collins Reviewed-by: Amir Goldstein Reviewed-by: Chandan Rajendra Reviewed-by: Brian Foster Reviewed-by: Darrick J. Wong Signed-off-by: Darrick J. Wong Acked-by: Dave Chinner --- fs/xfs/libxfs/xfs_attr.c | 41 ++++++++++++++++++++++++++-------------- 1 file changed, 27 insertions(+), 14 deletions(-) diff --git a/fs/xfs/libxfs/xfs_attr.c b/fs/xfs/libxfs/xfs_attr.c index 93a1c55cceb29..1420f6ebd4969 100644 --- a/fs/xfs/libxfs/xfs_attr.c +++ b/fs/xfs/libxfs/xfs_attr.c @@ -1131,6 +1131,32 @@ xfs_attr_node_shrink( return error; } +/* + * Mark an attribute entry INCOMPLETE and save pointers to the relevant buffers + * for later deletion of the entry. + */ +STATIC int +xfs_attr_leaf_mark_incomplete( + struct xfs_da_args *args, + struct xfs_da_state *state) +{ + int error; + + /* + * Fill in disk block numbers in the state structure + * so that we can get the buffers back after we commit + * several transactions in the following calls. + */ + error = xfs_attr_fillstate(state); + if (error) + return error; + + /* + * Mark the attribute as INCOMPLETE + */ + return xfs_attr3_leaf_setflag(args); +} + /* * Remove a name from a B-tree attribute list. * @@ -1162,20 +1188,7 @@ xfs_attr_node_removename( ASSERT(blk->bp != NULL); ASSERT(blk->magic == XFS_ATTR_LEAF_MAGIC); if (args->rmtblkno > 0) { - /* - * Fill in disk block numbers in the state structure - * so that we can get the buffers back after we commit - * several transactions in the following calls. - */ - error = xfs_attr_fillstate(state); - if (error) - goto out; - - /* - * Mark the attribute as INCOMPLETE, then bunmapi() the - * remote value. - */ - error = xfs_attr3_leaf_setflag(args); + error = xfs_attr_leaf_mark_incomplete(args, state); if (error) goto out; -- GitLab From 410c19885db5f7c4fca55b437e199e18252142b6 Mon Sep 17 00:00:00 2001 From: Allison Collins Date: Mon, 20 Jul 2020 21:47:29 -0700 Subject: [PATCH 1051/1476] xfs: Add remote block helper functions This patch adds two new helper functions xfs_attr_store_rmt_blk and xfs_attr_restore_rmt_blk. These two helpers assist to remove redundant code associated with storing and retrieving remote blocks during the attr set operations. Signed-off-by: Allison Collins Reviewed-by: Chandan Rajendra Reviewed-by: Amir Goldstein Reviewed-by: Brian Foster Reviewed-by: Darrick J. Wong Signed-off-by: Darrick J. Wong Acked-by: Dave Chinner --- fs/xfs/libxfs/xfs_attr.c | 50 ++++++++++++++++++++++++---------------- 1 file changed, 30 insertions(+), 20 deletions(-) diff --git a/fs/xfs/libxfs/xfs_attr.c b/fs/xfs/libxfs/xfs_attr.c index 1420f6ebd4969..37fda9f90b7f3 100644 --- a/fs/xfs/libxfs/xfs_attr.c +++ b/fs/xfs/libxfs/xfs_attr.c @@ -564,6 +564,30 @@ xfs_attr_shortform_addname(xfs_da_args_t *args) * External routines when attribute list is one block *========================================================================*/ +/* Store info about a remote block */ +STATIC void +xfs_attr_save_rmt_blk( + struct xfs_da_args *args) +{ + args->blkno2 = args->blkno; + args->index2 = args->index; + args->rmtblkno2 = args->rmtblkno; + args->rmtblkcnt2 = args->rmtblkcnt; + args->rmtvaluelen2 = args->rmtvaluelen; +} + +/* Set stored info about a remote block */ +STATIC void +xfs_attr_restore_rmt_blk( + struct xfs_da_args *args) +{ + args->blkno = args->blkno2; + args->index = args->index2; + args->rmtblkno = args->rmtblkno2; + args->rmtblkcnt = args->rmtblkcnt2; + args->rmtvaluelen = args->rmtvaluelen2; +} + /* * Tries to add an attribute to an inode in leaf form * @@ -598,11 +622,7 @@ xfs_attr_leaf_try_add( /* save the attribute state for later removal*/ args->op_flags |= XFS_DA_OP_RENAME; /* an atomic rename */ - args->blkno2 = args->blkno; /* set 2nd entry info*/ - args->index2 = args->index; - args->rmtblkno2 = args->rmtblkno; - args->rmtblkcnt2 = args->rmtblkcnt; - args->rmtvaluelen2 = args->rmtvaluelen; + xfs_attr_save_rmt_blk(args); /* * clear the remote attr state now that it is saved so that the @@ -701,11 +721,8 @@ xfs_attr_leaf_addname( * Dismantle the "old" attribute/value pair by removing * a "remote" value (if it exists). */ - args->index = args->index2; - args->blkno = args->blkno2; - args->rmtblkno = args->rmtblkno2; - args->rmtblkcnt = args->rmtblkcnt2; - args->rmtvaluelen = args->rmtvaluelen2; + xfs_attr_restore_rmt_blk(args); + if (args->rmtblkno) { error = xfs_attr_rmtval_invalidate(args); if (error) @@ -919,11 +936,7 @@ restart: /* save the attribute state for later removal*/ args->op_flags |= XFS_DA_OP_RENAME; /* atomic rename op */ - args->blkno2 = args->blkno; /* set 2nd entry info*/ - args->index2 = args->index; - args->rmtblkno2 = args->rmtblkno; - args->rmtblkcnt2 = args->rmtblkcnt; - args->rmtvaluelen2 = args->rmtvaluelen; + xfs_attr_save_rmt_blk(args); /* * clear the remote attr state now that it is saved so that the @@ -1035,11 +1048,8 @@ restart: * Dismantle the "old" attribute/value pair by removing * a "remote" value (if it exists). */ - args->index = args->index2; - args->blkno = args->blkno2; - args->rmtblkno = args->rmtblkno2; - args->rmtblkcnt = args->rmtblkcnt2; - args->rmtvaluelen = args->rmtvaluelen2; + xfs_attr_restore_rmt_blk(args); + if (args->rmtblkno) { error = xfs_attr_rmtval_invalidate(args); if (error) -- GitLab From 674eb548cf0ced1487ee229f96af2c7cf0099d2a Mon Sep 17 00:00:00 2001 From: Allison Collins Date: Mon, 20 Jul 2020 21:47:30 -0700 Subject: [PATCH 1052/1476] xfs: Add helper function xfs_attr_node_removename_setup This patch adds a new helper function xfs_attr_node_removename_setup. This will help modularize xfs_attr_node_removename when we add delay ready attributes later. Signed-off-by: Allison Collins Reviewed-by: Brian Foster Reviewed-by: Chandan Rajendra [darrick: fix unused variable complaints by 0day robot] Reviewed-by: Darrick J. Wong Signed-off-by: Darrick J. Wong Acked-by: Dave Chinner Reported-by: kernel test robot --- fs/xfs/libxfs/xfs_attr.c | 46 ++++++++++++++++++++++++++++------------ 1 file changed, 33 insertions(+), 13 deletions(-) diff --git a/fs/xfs/libxfs/xfs_attr.c b/fs/xfs/libxfs/xfs_attr.c index 37fda9f90b7f3..bf91da55ef473 100644 --- a/fs/xfs/libxfs/xfs_attr.c +++ b/fs/xfs/libxfs/xfs_attr.c @@ -1167,6 +1167,37 @@ xfs_attr_leaf_mark_incomplete( return xfs_attr3_leaf_setflag(args); } +/* + * Initial setup for xfs_attr_node_removename. Make sure the attr is there and + * the blocks are valid. Attr keys with remote blocks will be marked + * incomplete. + */ +STATIC +int xfs_attr_node_removename_setup( + struct xfs_da_args *args, + struct xfs_da_state **state) +{ + int error; + + error = xfs_attr_node_hasname(args, state); + if (error != -EEXIST) + return error; + + ASSERT((*state)->path.blk[(*state)->path.active - 1].bp != NULL); + ASSERT((*state)->path.blk[(*state)->path.active - 1].magic == + XFS_ATTR_LEAF_MAGIC); + + if (args->rmtblkno > 0) { + error = xfs_attr_leaf_mark_incomplete(args, *state); + if (error) + return error; + + return xfs_attr_rmtval_invalidate(args); + } + + return 0; +} + /* * Remove a name from a B-tree attribute list. * @@ -1185,8 +1216,8 @@ xfs_attr_node_removename( trace_xfs_attr_node_removename(args); - error = xfs_attr_node_hasname(args, &state); - if (error != -EEXIST) + error = xfs_attr_node_removename_setup(args, &state); + if (error) goto out; /* @@ -1194,18 +1225,7 @@ xfs_attr_node_removename( * This is done before we remove the attribute so that we don't * overflow the maximum size of a transaction and/or hit a deadlock. */ - blk = &state->path.blk[ state->path.active-1 ]; - ASSERT(blk->bp != NULL); - ASSERT(blk->magic == XFS_ATTR_LEAF_MAGIC); if (args->rmtblkno > 0) { - error = xfs_attr_leaf_mark_incomplete(args, state); - if (error) - goto out; - - error = xfs_attr_rmtval_invalidate(args); - if (error) - return error; - error = xfs_attr_rmtval_remove(args); if (error) goto out; -- GitLab From 72b97ea40d23b333175323251d6afecb565a0b53 Mon Sep 17 00:00:00 2001 From: Allison Collins Date: Mon, 20 Jul 2020 21:47:30 -0700 Subject: [PATCH 1053/1476] xfs: Add helper function xfs_attr_node_removename_rmt This patch adds another new helper function xfs_attr_node_removename_rmt. This will also help modularize xfs_attr_node_removename when we add delay ready attributes later. Signed-off-by: Allison Collins Reviewed-by: Brian Foster Reviewed-by: Chandan Rajendra Reviewed-by: Darrick J. Wong Signed-off-by: Darrick J. Wong Acked-by: Dave Chinner --- fs/xfs/libxfs/xfs_attr.c | 28 +++++++++++++++++++--------- 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/fs/xfs/libxfs/xfs_attr.c b/fs/xfs/libxfs/xfs_attr.c index bf91da55ef473..82f7cc796c26d 100644 --- a/fs/xfs/libxfs/xfs_attr.c +++ b/fs/xfs/libxfs/xfs_attr.c @@ -1198,6 +1198,24 @@ int xfs_attr_node_removename_setup( return 0; } +STATIC int +xfs_attr_node_remove_rmt( + struct xfs_da_args *args, + struct xfs_da_state *state) +{ + int error = 0; + + error = xfs_attr_rmtval_remove(args); + if (error) + return error; + + /* + * Refill the state structure with buffers, the prior calls released our + * buffers. + */ + return xfs_attr_refillstate(state); +} + /* * Remove a name from a B-tree attribute list. * @@ -1226,15 +1244,7 @@ xfs_attr_node_removename( * overflow the maximum size of a transaction and/or hit a deadlock. */ if (args->rmtblkno > 0) { - error = xfs_attr_rmtval_remove(args); - if (error) - goto out; - - /* - * Refill the state structure with buffers, the prior calls - * released our buffers. - */ - error = xfs_attr_refillstate(state); + error = xfs_attr_node_remove_rmt(args, state); if (error) goto out; } -- GitLab From 5fdca0ad5c95464a60c5954f853c22127f8bb64f Mon Sep 17 00:00:00 2001 From: Allison Collins Date: Mon, 20 Jul 2020 21:47:31 -0700 Subject: [PATCH 1054/1476] xfs: Simplify xfs_attr_leaf_addname Invert the rename logic in xfs_attr_leaf_addname to simplify the delayed attr logic later. Signed-off-by: Allison Collins Reviewed-by: Brian Foster Reviewed-by: Darrick J. Wong Signed-off-by: Darrick J. Wong Acked-by: Dave Chinner --- fs/xfs/libxfs/xfs_attr.c | 107 ++++++++++++++++++++------------------- 1 file changed, 55 insertions(+), 52 deletions(-) diff --git a/fs/xfs/libxfs/xfs_attr.c b/fs/xfs/libxfs/xfs_attr.c index 82f7cc796c26d..44288e4baea7d 100644 --- a/fs/xfs/libxfs/xfs_attr.c +++ b/fs/xfs/libxfs/xfs_attr.c @@ -695,68 +695,71 @@ xfs_attr_leaf_addname( return error; } - /* - * If this is an atomic rename operation, we must "flip" the - * incomplete flags on the "new" and "old" attribute/value pairs - * so that one disappears and one appears atomically. Then we - * must remove the "old" attribute/value pair. - */ - if (args->op_flags & XFS_DA_OP_RENAME) { + if (!(args->op_flags & XFS_DA_OP_RENAME)) { /* - * In a separate transaction, set the incomplete flag on the - * "old" attr and clear the incomplete flag on the "new" attr. - */ - error = xfs_attr3_leaf_flipflags(args); - if (error) - return error; - /* - * Commit the flag value change and start the next trans in - * series. + * Added a "remote" value, just clear the incomplete flag. */ - error = xfs_trans_roll_inode(&args->trans, args->dp); - if (error) - return error; + if (args->rmtblkno > 0) + error = xfs_attr3_leaf_clearflag(args); - /* - * Dismantle the "old" attribute/value pair by removing - * a "remote" value (if it exists). - */ - xfs_attr_restore_rmt_blk(args); + return error; + } - if (args->rmtblkno) { - error = xfs_attr_rmtval_invalidate(args); - if (error) - return error; + /* + * If this is an atomic rename operation, we must "flip" the incomplete + * flags on the "new" and "old" attribute/value pairs so that one + * disappears and one appears atomically. Then we must remove the "old" + * attribute/value pair. + * + * In a separate transaction, set the incomplete flag on the "old" attr + * and clear the incomplete flag on the "new" attr. + */ - error = xfs_attr_rmtval_remove(args); - if (error) - return error; - } + error = xfs_attr3_leaf_flipflags(args); + if (error) + return error; + /* + * Commit the flag value change and start the next trans in series. + */ + error = xfs_trans_roll_inode(&args->trans, args->dp); + if (error) + return error; - /* - * Read in the block containing the "old" attr, then - * remove the "old" attr from that block (neat, huh!) - */ - error = xfs_attr3_leaf_read(args->trans, args->dp, args->blkno, - &bp); + /* + * Dismantle the "old" attribute/value pair by removing a "remote" value + * (if it exists). + */ + xfs_attr_restore_rmt_blk(args); + + if (args->rmtblkno) { + error = xfs_attr_rmtval_invalidate(args); if (error) return error; - xfs_attr3_leaf_remove(bp, args); - - /* - * If the result is small enough, shrink it all into the inode. - */ - forkoff = xfs_attr_shortform_allfit(bp, dp); - if (forkoff) - error = xfs_attr3_leaf_to_shortform(bp, args, forkoff); - /* bp is gone due to xfs_da_shrink_inode */ - } else if (args->rmtblkno > 0) { - /* - * Added a "remote" value, just clear the incomplete flag. - */ - error = xfs_attr3_leaf_clearflag(args); + error = xfs_attr_rmtval_remove(args); + if (error) + return error; } + + /* + * Read in the block containing the "old" attr, then remove the "old" + * attr from that block (neat, huh!) + */ + error = xfs_attr3_leaf_read(args->trans, args->dp, args->blkno, + &bp); + if (error) + return error; + + xfs_attr3_leaf_remove(bp, args); + + /* + * If the result is small enough, shrink it all into the inode. + */ + forkoff = xfs_attr_shortform_allfit(bp, dp); + if (forkoff) + error = xfs_attr3_leaf_to_shortform(bp, args, forkoff); + /* bp is gone due to xfs_da_shrink_inode */ + return error; } -- GitLab From bf4a5cfffee096624764d5aed82f786c708dfe90 Mon Sep 17 00:00:00 2001 From: Allison Collins Date: Mon, 20 Jul 2020 21:47:31 -0700 Subject: [PATCH 1055/1476] xfs: Simplify xfs_attr_node_addname Invert the rename logic in xfs_attr_node_addname to simplify the delayed attr logic later. Signed-off-by: Allison Collins Reviewed-by: Brian Foster Reviewed-by: Darrick J. Wong Signed-off-by: Darrick J. Wong Acked-by: Dave Chinner --- fs/xfs/libxfs/xfs_attr.c | 122 +++++++++++++++++++-------------------- 1 file changed, 59 insertions(+), 63 deletions(-) diff --git a/fs/xfs/libxfs/xfs_attr.c b/fs/xfs/libxfs/xfs_attr.c index 44288e4baea7d..cdfc136b5bfb9 100644 --- a/fs/xfs/libxfs/xfs_attr.c +++ b/fs/xfs/libxfs/xfs_attr.c @@ -1025,79 +1025,75 @@ restart: return error; } - /* - * If this is an atomic rename operation, we must "flip" the - * incomplete flags on the "new" and "old" attribute/value pairs - * so that one disappears and one appears atomically. Then we - * must remove the "old" attribute/value pair. - */ - if (args->op_flags & XFS_DA_OP_RENAME) { - /* - * In a separate transaction, set the incomplete flag on the - * "old" attr and clear the incomplete flag on the "new" attr. - */ - error = xfs_attr3_leaf_flipflags(args); - if (error) - goto out; - /* - * Commit the flag value change and start the next trans in - * series - */ - error = xfs_trans_roll_inode(&args->trans, args->dp); - if (error) - goto out; - + if (!(args->op_flags & XFS_DA_OP_RENAME)) { /* - * Dismantle the "old" attribute/value pair by removing - * a "remote" value (if it exists). + * Added a "remote" value, just clear the incomplete flag. */ - xfs_attr_restore_rmt_blk(args); + if (args->rmtblkno > 0) + error = xfs_attr3_leaf_clearflag(args); + retval = error; + goto out; + } - if (args->rmtblkno) { - error = xfs_attr_rmtval_invalidate(args); - if (error) - return error; + /* + * If this is an atomic rename operation, we must "flip" the incomplete + * flags on the "new" and "old" attribute/value pairs so that one + * disappears and one appears atomically. Then we must remove the "old" + * attribute/value pair. + * + * In a separate transaction, set the incomplete flag on the "old" attr + * and clear the incomplete flag on the "new" attr. + */ + error = xfs_attr3_leaf_flipflags(args); + if (error) + goto out; + /* + * Commit the flag value change and start the next trans in series + */ + error = xfs_trans_roll_inode(&args->trans, args->dp); + if (error) + goto out; - error = xfs_attr_rmtval_remove(args); - if (error) - return error; - } + /* + * Dismantle the "old" attribute/value pair by removing a "remote" value + * (if it exists). + */ + xfs_attr_restore_rmt_blk(args); - /* - * Re-find the "old" attribute entry after any split ops. - * The INCOMPLETE flag means that we will find the "old" - * attr, not the "new" one. - */ - args->attr_filter |= XFS_ATTR_INCOMPLETE; - state = xfs_da_state_alloc(args); + if (args->rmtblkno) { + error = xfs_attr_rmtval_invalidate(args); + if (error) + return error; - state->inleaf = 0; - error = xfs_da3_node_lookup_int(state, &retval); + error = xfs_attr_rmtval_remove(args); if (error) - goto out; + return error; + } - /* - * Remove the name and update the hashvals in the tree. - */ - blk = &state->path.blk[ state->path.active-1 ]; - ASSERT(blk->magic == XFS_ATTR_LEAF_MAGIC); - error = xfs_attr3_leaf_remove(blk->bp, args); - xfs_da3_fixhashpath(state, &state->path); + /* + * Re-find the "old" attribute entry after any split ops. The INCOMPLETE + * flag means that we will find the "old" attr, not the "new" one. + */ + args->attr_filter |= XFS_ATTR_INCOMPLETE; + state = xfs_da_state_alloc(args); + state->inleaf = 0; + error = xfs_da3_node_lookup_int(state, &retval); + if (error) + goto out; - /* - * Check to see if the tree needs to be collapsed. - */ - if (retval && (state->path.active > 1)) { - error = xfs_da3_join(state); - if (error) - goto out; - } + /* + * Remove the name and update the hashvals in the tree. + */ + blk = &state->path.blk[state->path.active-1]; + ASSERT(blk->magic == XFS_ATTR_LEAF_MAGIC); + error = xfs_attr3_leaf_remove(blk->bp, args); + xfs_da3_fixhashpath(state, &state->path); - } else if (args->rmtblkno > 0) { - /* - * Added a "remote" value, just clear the incomplete flag. - */ - error = xfs_attr3_leaf_clearflag(args); + /* + * Check to see if the tree needs to be collapsed. + */ + if (retval && (state->path.active > 1)) { + error = xfs_da3_join(state); if (error) goto out; } -- GitLab From 0f89edcd8e8484dd1790ec474a650dd774c6b75e Mon Sep 17 00:00:00 2001 From: Allison Collins Date: Mon, 20 Jul 2020 21:47:31 -0700 Subject: [PATCH 1056/1476] xfs: Lift -ENOSPC handler from xfs_attr_leaf_addname Lift -ENOSPC handler from xfs_attr_leaf_addname. This will help to reorganize transitions between the attr forms later. Signed-off-by: Allison Collins Reviewed-by: Darrick J. Wong Reviewed-by: Brian Foster Signed-off-by: Darrick J. Wong Acked-by: Dave Chinner --- fs/xfs/libxfs/xfs_attr.c | 25 +++++++++++-------------- 1 file changed, 11 insertions(+), 14 deletions(-) diff --git a/fs/xfs/libxfs/xfs_attr.c b/fs/xfs/libxfs/xfs_attr.c index cdfc136b5bfb9..2e055c079f397 100644 --- a/fs/xfs/libxfs/xfs_attr.c +++ b/fs/xfs/libxfs/xfs_attr.c @@ -298,6 +298,13 @@ xfs_attr_set_args( if (error != -ENOSPC) return error; + /* + * Promote the attribute list to the Btree format. + */ + error = xfs_attr3_leaf_to_node(args); + if (error) + return error; + /* * Finish any deferred work items and roll the transaction once * more. The goal here is to call node_addname with the inode @@ -603,7 +610,7 @@ xfs_attr_leaf_try_add( struct xfs_da_args *args, struct xfs_buf *bp) { - int retval, error; + int retval; /* * Look up the given attribute in the leaf block. Figure out if @@ -635,20 +642,10 @@ xfs_attr_leaf_try_add( } /* - * Add the attribute to the leaf block, transitioning to a Btree - * if required. + * Add the attribute to the leaf block */ - retval = xfs_attr3_leaf_add(bp, args); - if (retval == -ENOSPC) { - /* - * Promote the attribute list to the Btree format. Unless an - * error occurs, retain the -ENOSPC retval - */ - error = xfs_attr3_leaf_to_node(args); - if (error) - return error; - } - return retval; + return xfs_attr3_leaf_add(bp, args); + out_brelse: xfs_trans_brelse(args->trans, bp); return retval; -- GitLab From 818d5a91559ffe1e1f2095dcbbdb96c13fdb94ec Mon Sep 17 00:00:00 2001 From: Xiao Yang Date: Tue, 28 Jul 2020 08:57:21 -0700 Subject: [PATCH 1057/1476] fs/xfs: Support that ioctl(SETXFLAGS/GETXFLAGS) can set/get inode DAX on XFS. 1) FS_DAX_FL has been introduced by commit b383a73f2b83. 2) In future, chattr/lsattr command from e2fsprogs can set/get inode DAX on XFS by calling ioctl(SETXFLAGS/GETXFLAGS). Signed-off-by: Xiao Yang Reviewed-by: Darrick J. Wong Signed-off-by: Darrick J. Wong --- fs/xfs/xfs_ioctl.c | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/fs/xfs/xfs_ioctl.c b/fs/xfs/xfs_ioctl.c index a190212ca85d0..6f22a66777cd0 100644 --- a/fs/xfs/xfs_ioctl.c +++ b/fs/xfs/xfs_ioctl.c @@ -1075,13 +1075,18 @@ xfs_merge_ioc_xflags( xflags |= FS_XFLAG_NODUMP; else xflags &= ~FS_XFLAG_NODUMP; + if (flags & FS_DAX_FL) + xflags |= FS_XFLAG_DAX; + else + xflags &= ~FS_XFLAG_DAX; return xflags; } STATIC unsigned int xfs_di2lxflags( - uint16_t di_flags) + uint16_t di_flags, + uint64_t di_flags2) { unsigned int flags = 0; @@ -1095,6 +1100,9 @@ xfs_di2lxflags( flags |= FS_NOATIME_FL; if (di_flags & XFS_DIFLAG_NODUMP) flags |= FS_NODUMP_FL; + if (di_flags2 & XFS_DIFLAG2_DAX) { + flags |= FS_DAX_FL; + } return flags; } @@ -1565,7 +1573,7 @@ xfs_ioc_getxflags( { unsigned int flags; - flags = xfs_di2lxflags(ip->i_d.di_flags); + flags = xfs_di2lxflags(ip->i_d.di_flags, ip->i_d.di_flags2); if (copy_to_user(arg, &flags, sizeof(flags))) return -EFAULT; return 0; @@ -1588,7 +1596,7 @@ xfs_ioc_setxflags( if (flags & ~(FS_IMMUTABLE_FL | FS_APPEND_FL | \ FS_NOATIME_FL | FS_NODUMP_FL | \ - FS_SYNC_FL)) + FS_SYNC_FL | FS_DAX_FL)) return -EOPNOTSUPP; fa.fsx_xflags = xfs_merge_ioc_xflags(flags, xfs_ip2xflags(ip)); -- GitLab From 443359aebce0e17148251c0e316801fe69aa7d33 Mon Sep 17 00:00:00 2001 From: Athira Rajeev Date: Wed, 29 Jul 2020 00:16:54 -0400 Subject: [PATCH 1058/1476] powerpc/perf: Fix MMCRA_BHRB_DISABLE define for binutils < 2.28 Commit 9908c826d5ed ("powerpc/perf: Add Power10 PMU feature to DT CPU features") defines MMCRA_BHRB_DISABLE as `0x2000000000UL`. Binutils version less than 2.28 doesn't support UL suffix. arch/powerpc/kernel/cpu_setup_power.S: Assembler messages: arch/powerpc/kernel/cpu_setup_power.S:250: Error: found 'L', expected: ')' arch/powerpc/kernel/cpu_setup_power.S:250: Error: junk at end of line, first unrecognized character is `L' arch/powerpc/kernel/cpu_setup_power.S:250: Error: found 'L', expected: ')' arch/powerpc/kernel/cpu_setup_power.S:250: Error: found 'L', expected: ')' arch/powerpc/kernel/cpu_setup_power.S:250: Error: junk at end of line, first unrecognized character is `L' arch/powerpc/kernel/cpu_setup_power.S:250: Error: found 'L', expected: ')' arch/powerpc/kernel/cpu_setup_power.S:250: Error: found 'L', expected: ')' arch/powerpc/kernel/cpu_setup_power.S:250: Error: operand out of range (0x0000002000000000 is not between 0xffffffffffff8000 and 0x000000000000ffff) Fix this by wrapping it with the `_UL` macro. Fixes: 9908c826d5ed ("Add Power10 PMU feature to DT CPU features") Suggested-by: Michael Ellerman Signed-off-by: Athira Rajeev Reviewed-by: Madhavan Srinivasan Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/1595996214-5833-1-git-send-email-atrajeev@linux.vnet.ibm.com --- arch/powerpc/include/asm/reg.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/arch/powerpc/include/asm/reg.h b/arch/powerpc/include/asm/reg.h index ae71027d3ca23..41419f1fc00fc 100644 --- a/arch/powerpc/include/asm/reg.h +++ b/arch/powerpc/include/asm/reg.h @@ -12,6 +12,7 @@ #ifdef __KERNEL__ #include +#include #include #include #include @@ -888,7 +889,7 @@ #define MMCRA_SLOT 0x07000000UL /* SLOT bits (37-39) */ #define MMCRA_SLOT_SHIFT 24 #define MMCRA_SAMPLE_ENABLE 0x00000001UL /* enable sampling */ -#define MMCRA_BHRB_DISABLE 0x2000000000UL // BHRB disable bit for ISA v3.1 +#define MMCRA_BHRB_DISABLE _UL(0x2000000000) // BHRB disable bit for ISA v3.1 #define POWER6_MMCRA_SDSYNC 0x0000080000000000ULL /* SDAR/SIAR synced */ #define POWER6_MMCRA_SIHV 0x0000040000000000ULL #define POWER6_MMCRA_SIPR 0x0000020000000000ULL -- GitLab From 1f0ce497433f8944045ee1baae218e31a0d295ee Mon Sep 17 00:00:00 2001 From: Nicholas Piggin Date: Sun, 26 Jul 2020 13:51:53 +1000 Subject: [PATCH 1059/1476] powerpc: Inline doorbell sending functions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit These are only called in one place for a given platform, so inline them for performance. Signed-off-by: Nicholas Piggin Tested-by: Cédric Le Goater [mpe: Fix build errors related to KVM] Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20200726035155.1424103-2-npiggin@gmail.com --- arch/powerpc/include/asm/dbell.h | 64 ++++++++++++++++++++++++++-- arch/powerpc/include/asm/kvm_booke.h | 2 + arch/powerpc/kernel/dbell.c | 55 ------------------------ 3 files changed, 63 insertions(+), 58 deletions(-) diff --git a/arch/powerpc/include/asm/dbell.h b/arch/powerpc/include/asm/dbell.h index 4ce6808deed3c..3e9da22a27796 100644 --- a/arch/powerpc/include/asm/dbell.h +++ b/arch/powerpc/include/asm/dbell.h @@ -11,8 +11,10 @@ #include #include +#include #include #include +#include #define PPC_DBELL_MSG_BRDCAST (0x04000000) #define PPC_DBELL_TYPE(x) (((x) & 0xf) << (63-36)) @@ -87,9 +89,6 @@ static inline void ppc_msgsync(void) #endif /* CONFIG_PPC_BOOK3S */ -extern void doorbell_global_ipi(int cpu); -extern void doorbell_core_ipi(int cpu); -extern int doorbell_try_core_ipi(int cpu); extern void doorbell_exception(struct pt_regs *regs); static inline void ppc_msgsnd(enum ppc_dbell type, u32 flags, u32 tag) @@ -100,4 +99,63 @@ static inline void ppc_msgsnd(enum ppc_dbell type, u32 flags, u32 tag) _ppc_msgsnd(msg); } +#ifdef CONFIG_SMP + +/* + * Doorbells must only be used if CPU_FTR_DBELL is available. + * msgsnd is used in HV, and msgsndp is used in !HV. + * + * These should be used by platform code that is aware of restrictions. + * Other arch code should use ->cause_ipi. + * + * doorbell_global_ipi() sends a dbell to any target CPU. + * Must be used only by architectures that address msgsnd target + * by PIR/get_hard_smp_processor_id. + */ +static inline void doorbell_global_ipi(int cpu) +{ + u32 tag = get_hard_smp_processor_id(cpu); + + kvmppc_set_host_ipi(cpu); + /* Order previous accesses vs. msgsnd, which is treated as a store */ + ppc_msgsnd_sync(); + ppc_msgsnd(PPC_DBELL_MSGTYPE, 0, tag); +} + +/* + * doorbell_core_ipi() sends a dbell to a target CPU in the same core. + * Must be used only by architectures that address msgsnd target + * by TIR/cpu_thread_in_core. + */ +static inline void doorbell_core_ipi(int cpu) +{ + u32 tag = cpu_thread_in_core(cpu); + + kvmppc_set_host_ipi(cpu); + /* Order previous accesses vs. msgsnd, which is treated as a store */ + ppc_msgsnd_sync(); + ppc_msgsnd(PPC_DBELL_MSGTYPE, 0, tag); +} + +/* + * Attempt to cause a core doorbell if destination is on the same core. + * Returns 1 on success, 0 on failure. + */ +static inline int doorbell_try_core_ipi(int cpu) +{ + int this_cpu = get_cpu(); + int ret = 0; + + if (cpumask_test_cpu(cpu, cpu_sibling_mask(this_cpu))) { + doorbell_core_ipi(cpu); + ret = 1; + } + + put_cpu(); + + return ret; +} + +#endif /* CONFIG_SMP */ + #endif /* _ASM_POWERPC_DBELL_H */ diff --git a/arch/powerpc/include/asm/kvm_booke.h b/arch/powerpc/include/asm/kvm_booke.h index 310ba48d13f00..0c3401b2e19e2 100644 --- a/arch/powerpc/include/asm/kvm_booke.h +++ b/arch/powerpc/include/asm/kvm_booke.h @@ -89,10 +89,12 @@ static inline ulong kvmppc_get_pc(struct kvm_vcpu *vcpu) return vcpu->arch.regs.nip; } +#ifdef CONFIG_BOOKE static inline ulong kvmppc_get_fault_dar(struct kvm_vcpu *vcpu) { return vcpu->arch.fault_dear; } +#endif static inline bool kvmppc_supports_magic_page(struct kvm_vcpu *vcpu) { diff --git a/arch/powerpc/kernel/dbell.c b/arch/powerpc/kernel/dbell.c index f17ff1200eaae..52680cf07c9dc 100644 --- a/arch/powerpc/kernel/dbell.c +++ b/arch/powerpc/kernel/dbell.c @@ -18,61 +18,6 @@ #ifdef CONFIG_SMP -/* - * Doorbells must only be used if CPU_FTR_DBELL is available. - * msgsnd is used in HV, and msgsndp is used in !HV. - * - * These should be used by platform code that is aware of restrictions. - * Other arch code should use ->cause_ipi. - * - * doorbell_global_ipi() sends a dbell to any target CPU. - * Must be used only by architectures that address msgsnd target - * by PIR/get_hard_smp_processor_id. - */ -void doorbell_global_ipi(int cpu) -{ - u32 tag = get_hard_smp_processor_id(cpu); - - kvmppc_set_host_ipi(cpu); - /* Order previous accesses vs. msgsnd, which is treated as a store */ - ppc_msgsnd_sync(); - ppc_msgsnd(PPC_DBELL_MSGTYPE, 0, tag); -} - -/* - * doorbell_core_ipi() sends a dbell to a target CPU in the same core. - * Must be used only by architectures that address msgsnd target - * by TIR/cpu_thread_in_core. - */ -void doorbell_core_ipi(int cpu) -{ - u32 tag = cpu_thread_in_core(cpu); - - kvmppc_set_host_ipi(cpu); - /* Order previous accesses vs. msgsnd, which is treated as a store */ - ppc_msgsnd_sync(); - ppc_msgsnd(PPC_DBELL_MSGTYPE, 0, tag); -} - -/* - * Attempt to cause a core doorbell if destination is on the same core. - * Returns 1 on success, 0 on failure. - */ -int doorbell_try_core_ipi(int cpu) -{ - int this_cpu = get_cpu(); - int ret = 0; - - if (cpumask_test_cpu(cpu, cpu_sibling_mask(this_cpu))) { - doorbell_core_ipi(cpu); - ret = 1; - } - - put_cpu(); - - return ret; -} - void doorbell_exception(struct pt_regs *regs) { struct pt_regs *old_regs = set_irq_regs(regs); -- GitLab From 5b06d1679f2fe874ef49ea11324cd893ec9e2da8 Mon Sep 17 00:00:00 2001 From: Nicholas Piggin Date: Sun, 26 Jul 2020 13:51:54 +1000 Subject: [PATCH 1060/1476] powerpc/pseries: Use doorbells even if XIVE is available MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit KVM supports msgsndp in guests by trapping and emulating the instruction, so it was decided to always use XIVE for IPIs if it is available. However on PowerVM systems, msgsndp can be used and gives better performance. On large systems, high XIVE interrupt rates can have sub-linear scaling, and using msgsndp can reduce the load on the interrupt controller. So switch to using core local doorbells even if XIVE is available. This reduces performance for KVM guests with an SMT topology by about 50% for ping-pong context switching between SMT vCPUs. An option vector (or dt-cpu-ftrs) could be defined to disable msgsndp to get KVM performance back. Signed-off-by: Nicholas Piggin Tested-by: Cédric Le Goater Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20200726035155.1424103-3-npiggin@gmail.com --- arch/powerpc/platforms/pseries/smp.c | 54 ++++++++++++++++++---------- 1 file changed, 36 insertions(+), 18 deletions(-) diff --git a/arch/powerpc/platforms/pseries/smp.c b/arch/powerpc/platforms/pseries/smp.c index 7ebacac03dc36..a1cb861154e98 100644 --- a/arch/powerpc/platforms/pseries/smp.c +++ b/arch/powerpc/platforms/pseries/smp.c @@ -162,13 +162,16 @@ static int pseries_smp_prepare_cpu(int cpu) return 0; } -static void smp_pseries_cause_ipi(int cpu) +/* Cause IPI as setup by the interrupt controller (xics or xive) */ +static void (*ic_cause_ipi)(int cpu) __ro_after_init; + +/* Use msgsndp doorbells target is a sibling, else use interrupt controller */ +static void dbell_or_ic_cause_ipi(int cpu) { - /* POWER9 should not use this handler */ if (doorbell_try_core_ipi(cpu)) return; - icp_ops->cause_ipi(cpu); + ic_cause_ipi(cpu); } static int pseries_cause_nmi_ipi(int cpu) @@ -192,26 +195,41 @@ static int pseries_cause_nmi_ipi(int cpu) return 0; } -static __init void pSeries_smp_probe_xics(void) -{ - xics_smp_probe(); - - if (cpu_has_feature(CPU_FTR_DBELL) && !is_secure_guest()) - smp_ops->cause_ipi = smp_pseries_cause_ipi; - else - smp_ops->cause_ipi = icp_ops->cause_ipi; -} - static __init void pSeries_smp_probe(void) { if (xive_enabled()) - /* - * Don't use P9 doorbells when XIVE is enabled. IPIs - * using MMIOs should be faster - */ xive_smp_probe(); else - pSeries_smp_probe_xics(); + xics_smp_probe(); + + /* No doorbell facility, must use the interrupt controller for IPIs */ + if (!cpu_has_feature(CPU_FTR_DBELL)) + return; + + /* Doorbells can only be used for IPIs between SMT siblings */ + if (!cpu_has_feature(CPU_FTR_SMT)) + return; + + /* + * KVM emulates doorbells by disabling FSCR[MSGP] so msgsndp faults + * to the hypervisor which then reads the instruction from guest + * memory. This can't be done if the guest is secure, so don't use + * doorbells in secure guests. + * + * Under PowerVM, FSCR[MSGP] is enabled so doorbells could be used + * by secure guests if we distinguished this from KVM. + */ + if (is_secure_guest()) + return; + + /* + * The guest can use doobells for SMT sibling IPIs, which stay in + * the core rather than going to the interrupt controller. This + * tends to be slower under KVM where doorbells are emulated, but + * faster for PowerVM where they're enabled. + */ + ic_cause_ipi = smp_ops->cause_ipi; + smp_ops->cause_ipi = dbell_or_ic_cause_ipi; } static struct smp_ops_t pseries_smp_ops = { -- GitLab From 107c55005fbd5243ee31fb13b6f166cde9e3ade1 Mon Sep 17 00:00:00 2001 From: Nicholas Piggin Date: Sun, 26 Jul 2020 13:51:55 +1000 Subject: [PATCH 1061/1476] powerpc/pseries: Add KVM guest doorbell restrictions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit KVM guests have certain restrictions and performance quirks when using doorbells. This patch moves the EPAPR KVM guest test so it can be shared with PSERIES, and uses that in doorbell setup code to apply the KVM guest quirks and improves IPI performance for two cases: - PowerVM guests may now use doorbells even if they are secure. - KVM guests no longer use doorbells if XIVE is available. There is a valid complaint that "KVM guest" is not a very reasonable thing to test for, it's preferable for the hypervisor to advertise particular behaviours to the guest so they could change if the hypervisor implementation or configuration changes. However in this case we were already assuming a KVM guest worst case, so this patch is about containing those quirks. If KVM later advertises fast doorbells, we should test for that and override the quirks. Signed-off-by: Nicholas Piggin Tested-by: Cédric Le Goater Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20200726035155.1424103-4-npiggin@gmail.com --- arch/powerpc/include/asm/firmware.h | 6 +++++ arch/powerpc/include/asm/kvm_para.h | 26 +++---------------- arch/powerpc/kernel/Makefile | 5 ++-- arch/powerpc/kernel/firmware.c | 19 ++++++++++++++ arch/powerpc/platforms/pseries/smp.c | 38 +++++++++++++++++----------- 5 files changed, 53 insertions(+), 41 deletions(-) diff --git a/arch/powerpc/include/asm/firmware.h b/arch/powerpc/include/asm/firmware.h index 6003c2e533a08..f67efbaba17f5 100644 --- a/arch/powerpc/include/asm/firmware.h +++ b/arch/powerpc/include/asm/firmware.h @@ -132,6 +132,12 @@ extern int ibm_nmi_interlock_token; extern unsigned int __start___fw_ftr_fixup, __stop___fw_ftr_fixup; +#if defined(CONFIG_PPC_PSERIES) || defined(CONFIG_KVM_GUEST) +bool is_kvm_guest(void); +#else +static inline bool is_kvm_guest(void) { return false; } +#endif + #ifdef CONFIG_PPC_PSERIES void pseries_probe_fw_features(void); #else diff --git a/arch/powerpc/include/asm/kvm_para.h b/arch/powerpc/include/asm/kvm_para.h index 9c1f6b4b9bbf4..744612054c94c 100644 --- a/arch/powerpc/include/asm/kvm_para.h +++ b/arch/powerpc/include/asm/kvm_para.h @@ -8,35 +8,15 @@ #ifndef __POWERPC_KVM_PARA_H__ #define __POWERPC_KVM_PARA_H__ -#include - -#ifdef CONFIG_KVM_GUEST - -#include - -static inline int kvm_para_available(void) -{ - struct device_node *hyper_node; - - hyper_node = of_find_node_by_path("/hypervisor"); - if (!hyper_node) - return 0; +#include - if (!of_device_is_compatible(hyper_node, "linux,kvm")) - return 0; - - return 1; -} - -#else +#include static inline int kvm_para_available(void) { - return 0; + return IS_ENABLED(CONFIG_KVM_GUEST) && is_kvm_guest(); } -#endif - static inline unsigned int kvm_arch_para_features(void) { unsigned long r; diff --git a/arch/powerpc/kernel/Makefile b/arch/powerpc/kernel/Makefile index c5211bdcf1b62..d4d5946224f8e 100644 --- a/arch/powerpc/kernel/Makefile +++ b/arch/powerpc/kernel/Makefile @@ -45,11 +45,10 @@ obj-y := cputable.o syscalls.o \ signal.o sysfs.o cacheinfo.o time.o \ prom.o traps.o setup-common.o \ udbg.o misc.o io.o misc_$(BITS).o \ - of_platform.o prom_parse.o + of_platform.o prom_parse.o firmware.o obj-y += ptrace/ obj-$(CONFIG_PPC64) += setup_64.o \ - paca.o nvram_64.o firmware.o note.o \ - syscall_64.o + paca.o nvram_64.o note.o syscall_64.o obj-$(CONFIG_COMPAT) += sys_ppc32.o signal_32.o obj-$(CONFIG_VDSO32) += vdso32/ obj-$(CONFIG_PPC_WATCHDOG) += watchdog.o diff --git a/arch/powerpc/kernel/firmware.c b/arch/powerpc/kernel/firmware.c index cc4a5e3f51f14..fe48d319d490e 100644 --- a/arch/powerpc/kernel/firmware.c +++ b/arch/powerpc/kernel/firmware.c @@ -11,8 +11,27 @@ #include #include +#include #include +#ifdef CONFIG_PPC64 unsigned long powerpc_firmware_features __read_mostly; EXPORT_SYMBOL_GPL(powerpc_firmware_features); +#endif + +#if defined(CONFIG_PPC_PSERIES) || defined(CONFIG_KVM_GUEST) +bool is_kvm_guest(void) +{ + struct device_node *hyper_node; + + hyper_node = of_find_node_by_path("/hypervisor"); + if (!hyper_node) + return 0; + + if (!of_device_is_compatible(hyper_node, "linux,kvm")) + return 0; + + return 1; +} +#endif diff --git a/arch/powerpc/platforms/pseries/smp.c b/arch/powerpc/platforms/pseries/smp.c index a1cb861154e98..92922491a81c6 100644 --- a/arch/powerpc/platforms/pseries/smp.c +++ b/arch/powerpc/platforms/pseries/smp.c @@ -210,24 +210,32 @@ static __init void pSeries_smp_probe(void) if (!cpu_has_feature(CPU_FTR_SMT)) return; - /* - * KVM emulates doorbells by disabling FSCR[MSGP] so msgsndp faults - * to the hypervisor which then reads the instruction from guest - * memory. This can't be done if the guest is secure, so don't use - * doorbells in secure guests. - * - * Under PowerVM, FSCR[MSGP] is enabled so doorbells could be used - * by secure guests if we distinguished this from KVM. - */ - if (is_secure_guest()) - return; + if (is_kvm_guest()) { + /* + * KVM emulates doorbells by disabling FSCR[MSGP] so msgsndp + * faults to the hypervisor which then reads the instruction + * from guest memory, which tends to be slower than using XIVE. + */ + if (xive_enabled()) + return; + + /* + * XICS hcalls aren't as fast, so we can use msgsndp (which + * also helps exercise KVM emulation), however KVM can't + * emulate secure guests because it can't read the instruction + * out of their memory. + */ + if (is_secure_guest()) + return; + } /* - * The guest can use doobells for SMT sibling IPIs, which stay in - * the core rather than going to the interrupt controller. This - * tends to be slower under KVM where doorbells are emulated, but - * faster for PowerVM where they're enabled. + * Under PowerVM, FSCR[MSGP] is enabled as guest vCPU siblings are + * gang scheduled on the same physical core, so doorbells are always + * faster than the interrupt controller, and they can be used by + * secure guests. */ + ic_cause_ipi = smp_ops->cause_ipi; smp_ops->cause_ipi = dbell_or_ic_cause_ipi; } -- GitLab From 128d3d0210076232b7d54c361082c8ee17e4b669 Mon Sep 17 00:00:00 2001 From: Sandipan Das Date: Mon, 27 Jul 2020 09:30:35 +0530 Subject: [PATCH 1062/1476] selftests/powerpc: Move pkey helpers to headers This moves all the pkey-related helpers to a new header file and also a helper to print error messages in signal handlers to the existing utils header file. Signed-off-by: Sandipan Das Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/28e633fa9ec1a6500c12188e09ea1887b10a10c1.1595821792.git.sandipan@linux.ibm.com --- .../testing/selftests/powerpc/include/pkeys.h | 108 ++++++++++++++++++ .../testing/selftests/powerpc/include/utils.h | 4 + .../selftests/powerpc/mm/pkey_exec_prot.c | 100 +--------------- 3 files changed, 114 insertions(+), 98 deletions(-) create mode 100644 tools/testing/selftests/powerpc/include/pkeys.h diff --git a/tools/testing/selftests/powerpc/include/pkeys.h b/tools/testing/selftests/powerpc/include/pkeys.h new file mode 100644 index 0000000000000..9b53a97e664ea --- /dev/null +++ b/tools/testing/selftests/powerpc/include/pkeys.h @@ -0,0 +1,108 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright 2020, Sandipan Das, IBM Corp. + */ + +#ifndef _SELFTESTS_POWERPC_PKEYS_H +#define _SELFTESTS_POWERPC_PKEYS_H + +#include + +#include "reg.h" +#include "utils.h" + +/* + * Older versions of libc use the Intel-specific access rights. + * Hence, override the definitions as they might be incorrect. + */ +#undef PKEY_DISABLE_ACCESS +#define PKEY_DISABLE_ACCESS 0x3 + +#undef PKEY_DISABLE_WRITE +#define PKEY_DISABLE_WRITE 0x2 + +#undef PKEY_DISABLE_EXECUTE +#define PKEY_DISABLE_EXECUTE 0x4 + +/* Older versions of libc do not not define this */ +#ifndef SEGV_PKUERR +#define SEGV_PKUERR 4 +#endif + +#define SI_PKEY_OFFSET 0x20 + +#define SYS_pkey_mprotect 386 +#define SYS_pkey_alloc 384 +#define SYS_pkey_free 385 + +#define PKEY_BITS_PER_PKEY 2 +#define NR_PKEYS 32 +#define PKEY_BITS_MASK ((1UL << PKEY_BITS_PER_PKEY) - 1) + +inline unsigned long pkeyreg_get(void) +{ + return mfspr(SPRN_AMR); +} + +inline void pkeyreg_set(unsigned long amr) +{ + set_amr(amr); +} + +void pkey_set_rights(int pkey, unsigned long rights) +{ + unsigned long amr, shift; + + shift = (NR_PKEYS - pkey - 1) * PKEY_BITS_PER_PKEY; + amr = pkeyreg_get(); + amr &= ~(PKEY_BITS_MASK << shift); + amr |= (rights & PKEY_BITS_MASK) << shift; + pkeyreg_set(amr); +} + +int sys_pkey_mprotect(void *addr, size_t len, int prot, int pkey) +{ + return syscall(SYS_pkey_mprotect, addr, len, prot, pkey); +} + +int sys_pkey_alloc(unsigned long flags, unsigned long rights) +{ + return syscall(SYS_pkey_alloc, flags, rights); +} + +int sys_pkey_free(int pkey) +{ + return syscall(SYS_pkey_free, pkey); +} + +int pkeys_unsupported(void) +{ + bool hash_mmu = false; + int pkey; + + /* Protection keys are currently supported on Hash MMU only */ + FAIL_IF(using_hash_mmu(&hash_mmu)); + SKIP_IF(!hash_mmu); + + /* Check if the system call is supported */ + pkey = sys_pkey_alloc(0, 0); + SKIP_IF(pkey < 0); + sys_pkey_free(pkey); + + return 0; +} + +int siginfo_pkey(siginfo_t *si) +{ + /* + * In older versions of libc, siginfo_t does not have si_pkey as + * a member. + */ +#ifdef si_pkey + return si->si_pkey; +#else + return *((int *)(((char *) si) + SI_PKEY_OFFSET)); +#endif +} + +#endif /* _SELFTESTS_POWERPC_PKEYS_H */ diff --git a/tools/testing/selftests/powerpc/include/utils.h b/tools/testing/selftests/powerpc/include/utils.h index 9dbe607cc5ec3..7f259f36e23bc 100644 --- a/tools/testing/selftests/powerpc/include/utils.h +++ b/tools/testing/selftests/powerpc/include/utils.h @@ -97,6 +97,10 @@ do { \ #define _str(s) #s #define str(s) _str(s) +#define sigsafe_err(msg) ({ \ + ssize_t nbytes __attribute__((unused)); \ + nbytes = write(STDERR_FILENO, msg, strlen(msg)); }) + /* POWER9 feature */ #ifndef PPC_FEATURE2_ARCH_3_00 #define PPC_FEATURE2_ARCH_3_00 0x00800000 diff --git a/tools/testing/selftests/powerpc/mm/pkey_exec_prot.c b/tools/testing/selftests/powerpc/mm/pkey_exec_prot.c index 7c7c93425c5e9..1253ad6afba24 100644 --- a/tools/testing/selftests/powerpc/mm/pkey_exec_prot.c +++ b/tools/testing/selftests/powerpc/mm/pkey_exec_prot.c @@ -14,83 +14,13 @@ #include #include -#include -#include "reg.h" -#include "utils.h" - -/* - * Older versions of libc use the Intel-specific access rights. - * Hence, override the definitions as they might be incorrect. - */ -#undef PKEY_DISABLE_ACCESS -#define PKEY_DISABLE_ACCESS 0x3 - -#undef PKEY_DISABLE_WRITE -#define PKEY_DISABLE_WRITE 0x2 - -#undef PKEY_DISABLE_EXECUTE -#define PKEY_DISABLE_EXECUTE 0x4 - -/* Older versions of libc do not not define this */ -#ifndef SEGV_PKUERR -#define SEGV_PKUERR 4 -#endif - -#define SI_PKEY_OFFSET 0x20 - -#define SYS_pkey_mprotect 386 -#define SYS_pkey_alloc 384 -#define SYS_pkey_free 385 - -#define PKEY_BITS_PER_PKEY 2 -#define NR_PKEYS 32 -#define PKEY_BITS_MASK ((1UL << PKEY_BITS_PER_PKEY) - 1) +#include "pkeys.h" #define PPC_INST_NOP 0x60000000 #define PPC_INST_TRAP 0x7fe00008 #define PPC_INST_BLR 0x4e800020 -#define sigsafe_err(msg) ({ \ - ssize_t nbytes __attribute__((unused)); \ - nbytes = write(STDERR_FILENO, msg, strlen(msg)); }) - -static inline unsigned long pkeyreg_get(void) -{ - return mfspr(SPRN_AMR); -} - -static inline void pkeyreg_set(unsigned long amr) -{ - set_amr(amr); -} - -static void pkey_set_rights(int pkey, unsigned long rights) -{ - unsigned long amr, shift; - - shift = (NR_PKEYS - pkey - 1) * PKEY_BITS_PER_PKEY; - amr = pkeyreg_get(); - amr &= ~(PKEY_BITS_MASK << shift); - amr |= (rights & PKEY_BITS_MASK) << shift; - pkeyreg_set(amr); -} - -static int sys_pkey_mprotect(void *addr, size_t len, int prot, int pkey) -{ - return syscall(SYS_pkey_mprotect, addr, len, prot, pkey); -} - -static int sys_pkey_alloc(unsigned long flags, unsigned long rights) -{ - return syscall(SYS_pkey_alloc, flags, rights); -} - -static int sys_pkey_free(int pkey) -{ - return syscall(SYS_pkey_free, pkey); -} - static volatile sig_atomic_t fault_pkey, fault_code, fault_type; static volatile sig_atomic_t remaining_faults; static volatile unsigned int *fault_addr; @@ -110,16 +40,7 @@ static void segv_handler(int signum, siginfo_t *sinfo, void *ctx) { int signal_pkey; - /* - * In older versions of libc, siginfo_t does not have si_pkey as - * a member. - */ -#ifdef si_pkey - signal_pkey = sinfo->si_pkey; -#else - signal_pkey = *((int *)(((char *) sinfo) + SI_PKEY_OFFSET)); -#endif - + signal_pkey = siginfo_pkey(sinfo); fault_code = sinfo->si_code; /* Check if this fault originated from the expected address */ @@ -178,23 +99,6 @@ static void segv_handler(int signum, siginfo_t *sinfo, void *ctx) remaining_faults--; } -static int pkeys_unsupported(void) -{ - bool hash_mmu = false; - int pkey; - - /* Protection keys are currently supported on Hash MMU only */ - FAIL_IF(using_hash_mmu(&hash_mmu)); - SKIP_IF(!hash_mmu); - - /* Check if the system call is supported */ - pkey = sys_pkey_alloc(0, 0); - SKIP_IF(pkey < 0); - sys_pkey_free(pkey); - - return 0; -} - static int test(void) { struct sigaction segv_act, trap_act; -- GitLab From 264d7fccc4711328a19f07e6bd57aee4c68803aa Mon Sep 17 00:00:00 2001 From: Sandipan Das Date: Mon, 27 Jul 2020 09:30:36 +0530 Subject: [PATCH 1063/1476] selftests/powerpc: Add pkey helpers for rights This adds some new pkey-related helper to print access rights of a pkey in the "rwx" format and to generate different valid combinations of pkey rights starting from a given combination. Signed-off-by: Sandipan Das Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/6cc1c7d1f686618668a3e090f1d0c2a4cd9dea3f.1595821792.git.sandipan@linux.ibm.com --- .../testing/selftests/powerpc/include/pkeys.h | 28 +++++++++++++++ .../selftests/powerpc/mm/pkey_exec_prot.c | 36 ++++++++++--------- 2 files changed, 48 insertions(+), 16 deletions(-) diff --git a/tools/testing/selftests/powerpc/include/pkeys.h b/tools/testing/selftests/powerpc/include/pkeys.h index 9b53a97e664ea..6ba95039a0343 100644 --- a/tools/testing/selftests/powerpc/include/pkeys.h +++ b/tools/testing/selftests/powerpc/include/pkeys.h @@ -105,4 +105,32 @@ int siginfo_pkey(siginfo_t *si) #endif } +#define pkey_rights(r) ({ \ + static char buf[4] = "rwx"; \ + unsigned int amr_bits; \ + if ((r) & PKEY_DISABLE_EXECUTE) \ + buf[2] = '-'; \ + amr_bits = (r) & PKEY_BITS_MASK; \ + if (amr_bits & PKEY_DISABLE_WRITE) \ + buf[1] = '-'; \ + if (amr_bits & PKEY_DISABLE_ACCESS & ~PKEY_DISABLE_WRITE) \ + buf[0] = '-'; \ + buf; \ +}) + +unsigned long next_pkey_rights(unsigned long rights) +{ + if (rights == PKEY_DISABLE_ACCESS) + return PKEY_DISABLE_EXECUTE; + else if (rights == (PKEY_DISABLE_ACCESS | PKEY_DISABLE_EXECUTE)) + return 0; + + if ((rights & PKEY_BITS_MASK) == 0) + rights |= PKEY_DISABLE_WRITE; + else if ((rights & PKEY_BITS_MASK) == PKEY_DISABLE_WRITE) + rights |= PKEY_DISABLE_ACCESS; + + return rights; +} + #endif /* _SELFTESTS_POWERPC_PKEYS_H */ diff --git a/tools/testing/selftests/powerpc/mm/pkey_exec_prot.c b/tools/testing/selftests/powerpc/mm/pkey_exec_prot.c index 1253ad6afba24..18ebfe6bae1c9 100644 --- a/tools/testing/selftests/powerpc/mm/pkey_exec_prot.c +++ b/tools/testing/selftests/powerpc/mm/pkey_exec_prot.c @@ -102,6 +102,7 @@ static void segv_handler(int signum, siginfo_t *sinfo, void *ctx) static int test(void) { struct sigaction segv_act, trap_act; + unsigned long rights; int pkey, ret, i; ret = pkeys_unsupported(); @@ -150,7 +151,8 @@ static int test(void) insns[numinsns - 1] = PPC_INST_BLR; /* Allocate a pkey that restricts execution */ - pkey = sys_pkey_alloc(0, PKEY_DISABLE_EXECUTE); + rights = PKEY_DISABLE_EXECUTE; + pkey = sys_pkey_alloc(0, rights); FAIL_IF(pkey < 0); /* @@ -175,8 +177,8 @@ static int test(void) */ remaining_faults = 0; FAIL_IF(sys_pkey_mprotect(insns, pgsize, PROT_EXEC, pkey) != 0); - printf("read from %p, pkey is execute-disabled, access-enabled\n", - (void *) fault_addr); + printf("read from %p, pkey permissions are %s\n", fault_addr, + pkey_rights(rights)); i = *fault_addr; FAIL_IF(remaining_faults != 0); @@ -192,12 +194,13 @@ static int test(void) */ remaining_faults = 1; FAIL_IF(sys_pkey_mprotect(insns, pgsize, PROT_EXEC, pkey) != 0); - printf("write to %p, pkey is execute-disabled, access-enabled\n", - (void *) fault_addr); + printf("write to %p, pkey permissions are %s\n", fault_addr, + pkey_rights(rights)); *fault_addr = PPC_INST_TRAP; FAIL_IF(remaining_faults != 0 || fault_code != SEGV_ACCERR); /* The following three cases will generate SEGV_PKUERR */ + rights |= PKEY_DISABLE_ACCESS; fault_type = PKEY_DISABLE_ACCESS; fault_pkey = pkey; @@ -211,9 +214,9 @@ static int test(void) */ remaining_faults = 1; FAIL_IF(sys_pkey_mprotect(insns, pgsize, PROT_EXEC, pkey) != 0); - printf("read from %p, pkey is execute-disabled, access-disabled\n", - (void *) fault_addr); - pkey_set_rights(pkey, PKEY_DISABLE_ACCESS); + pkey_set_rights(pkey, rights); + printf("read from %p, pkey permissions are %s\n", fault_addr, + pkey_rights(rights)); i = *fault_addr; FAIL_IF(remaining_faults != 0 || fault_code != SEGV_PKUERR); @@ -228,9 +231,9 @@ static int test(void) */ remaining_faults = 2; FAIL_IF(sys_pkey_mprotect(insns, pgsize, PROT_EXEC, pkey) != 0); - printf("write to %p, pkey is execute-disabled, access-disabled\n", - (void *) fault_addr); - pkey_set_rights(pkey, PKEY_DISABLE_ACCESS); + pkey_set_rights(pkey, rights); + printf("write to %p, pkey permissions are %s\n", fault_addr, + pkey_rights(rights)); *fault_addr = PPC_INST_NOP; FAIL_IF(remaining_faults != 0 || fault_code != SEGV_ACCERR); @@ -251,8 +254,8 @@ static int test(void) remaining_faults = 1; FAIL_IF(sys_pkey_mprotect(insns, pgsize, PROT_EXEC, pkey) != 0); pkey_set_rights(pkey, PKEY_DISABLE_ACCESS); - printf("execute at %p, pkey is execute-disabled, access-disabled\n", - (void *) fault_addr); + printf("execute at %p, pkey permissions are %s\n", fault_addr, + pkey_rights(rights)); asm volatile("mtctr %0; bctrl" : : "r"(insns)); FAIL_IF(remaining_faults != 0 || fault_code != SEGV_PKUERR); @@ -261,7 +264,8 @@ static int test(void) * fully permissive. */ sys_pkey_free(pkey); - pkey = sys_pkey_alloc(0, 0); + rights = 0; + pkey = sys_pkey_alloc(0, rights); /* * Jump to the executable region when AMR bits are not set @@ -274,8 +278,8 @@ static int test(void) fault_pkey = pkey; remaining_faults = 0; FAIL_IF(sys_pkey_mprotect(insns, pgsize, PROT_EXEC, pkey) != 0); - printf("execute at %p, pkey is execute-enabled, access-enabled\n", - (void *) fault_addr); + printf("execute at %p, pkey permissions are %s\n", fault_addr, + pkey_rights(rights)); asm volatile("mtctr %0; bctrl" : : "r"(insns)); FAIL_IF(remaining_faults != 0); -- GitLab From 03634bbf5d8a6f2d97e6150a1b8ff03675badac3 Mon Sep 17 00:00:00 2001 From: Sandipan Das Date: Mon, 27 Jul 2020 09:30:37 +0530 Subject: [PATCH 1064/1476] selftests/powerpc: Harden test for execute-disabled pkeys Commit 192b6a7805989 ("powerpc/book3s64/pkeys: Fix pkey_access_permitted() for execute disable pkey") fixed a bug that caused repetitive faults for pkeys with no execute rights alongside some combination of read and write rights. This removes the last two cases of the test, which check the behaviour of pkeys with read, write but no execute rights and all the rights, in favour of checking all the possible combinations of read, write and execute rights to be able to detect bugs like the one mentioned above. Signed-off-by: Sandipan Das Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/db467500f8af47727bba6b35796e8974a78b71e5.1595821792.git.sandipan@linux.ibm.com --- .../selftests/powerpc/mm/pkey_exec_prot.c | 84 +++++++++---------- 1 file changed, 41 insertions(+), 43 deletions(-) diff --git a/tools/testing/selftests/powerpc/mm/pkey_exec_prot.c b/tools/testing/selftests/powerpc/mm/pkey_exec_prot.c index 18ebfe6bae1c9..9e5c7f3f498a7 100644 --- a/tools/testing/selftests/powerpc/mm/pkey_exec_prot.c +++ b/tools/testing/selftests/powerpc/mm/pkey_exec_prot.c @@ -237,55 +237,53 @@ static int test(void) *fault_addr = PPC_INST_NOP; FAIL_IF(remaining_faults != 0 || fault_code != SEGV_ACCERR); - /* - * Jump to the executable region when AMR bits are set i.e. - * the pkey permits neither read nor write access. - * - * This should generate a pkey fault based on IAMR bits which - * are set to not permit execution. AMR bits should not affect - * execution. - * - * This also checks if the overwrite of the first instruction - * word from a trap to a no-op succeeded. - */ - fault_addr = insns; - fault_type = PKEY_DISABLE_EXECUTE; - fault_pkey = pkey; - remaining_faults = 1; - FAIL_IF(sys_pkey_mprotect(insns, pgsize, PROT_EXEC, pkey) != 0); - pkey_set_rights(pkey, PKEY_DISABLE_ACCESS); - printf("execute at %p, pkey permissions are %s\n", fault_addr, - pkey_rights(rights)); - asm volatile("mtctr %0; bctrl" : : "r"(insns)); - FAIL_IF(remaining_faults != 0 || fault_code != SEGV_PKUERR); - - /* - * Free the current pkey and allocate a new one that is - * fully permissive. - */ + /* Free the current pkey */ sys_pkey_free(pkey); + rights = 0; - pkey = sys_pkey_alloc(0, rights); + do { + /* + * Allocate pkeys with all valid combinations of read, + * write and execute restrictions. + */ + pkey = sys_pkey_alloc(0, rights); + FAIL_IF(pkey < 0); + + /* + * Jump to the executable region. AMR bits may or may not + * be set but they should not affect execution. + * + * This should generate pkey faults based on IAMR bits which + * may be set to restrict execution. + * + * The first iteration also checks if the overwrite of the + * first instruction word from a trap to a no-op succeeded. + */ + fault_pkey = pkey; + fault_type = -1; + remaining_faults = 0; + if (rights & PKEY_DISABLE_EXECUTE) { + fault_type = PKEY_DISABLE_EXECUTE; + remaining_faults = 1; + } - /* - * Jump to the executable region when AMR bits are not set - * i.e. the pkey permits read and write access. - * - * This should not generate any faults as the IAMR bits are - * also not set and hence will the pkey will not restrict - * execution. - */ - fault_pkey = pkey; - remaining_faults = 0; - FAIL_IF(sys_pkey_mprotect(insns, pgsize, PROT_EXEC, pkey) != 0); - printf("execute at %p, pkey permissions are %s\n", fault_addr, - pkey_rights(rights)); - asm volatile("mtctr %0; bctrl" : : "r"(insns)); - FAIL_IF(remaining_faults != 0); + FAIL_IF(sys_pkey_mprotect(insns, pgsize, PROT_EXEC, pkey) != 0); + printf("execute at %p, pkey permissions are %s\n", fault_addr, + pkey_rights(rights)); + asm volatile("mtctr %0; bctrl" : : "r"(insns)); + FAIL_IF(remaining_faults != 0); + if (rights & PKEY_DISABLE_EXECUTE) + FAIL_IF(fault_code != SEGV_PKUERR); + + /* Free the current pkey */ + sys_pkey_free(pkey); + + /* Find next valid combination of pkey rights */ + rights = next_pkey_rights(rights); + } while (rights); /* Cleanup */ munmap((void *) insns, pgsize); - sys_pkey_free(pkey); return 0; } -- GitLab From e3333c599482245d08002725cc1b353e4963fa26 Mon Sep 17 00:00:00 2001 From: Sandipan Das Date: Mon, 27 Jul 2020 09:30:38 +0530 Subject: [PATCH 1065/1476] selftests/powerpc: Add helper to exit on failure This adds a helper similar to FAIL_IF() which lets a program exit with code 1 (to indicate failure) when the given condition is true. Signed-off-by: Sandipan Das Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/dac282d5c2e96e7816dc522e4e20d56d7c79c898.1595821792.git.sandipan@linux.ibm.com --- tools/testing/selftests/powerpc/include/utils.h | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/tools/testing/selftests/powerpc/include/utils.h b/tools/testing/selftests/powerpc/include/utils.h index 7f259f36e23bc..69d16875802da 100644 --- a/tools/testing/selftests/powerpc/include/utils.h +++ b/tools/testing/selftests/powerpc/include/utils.h @@ -72,6 +72,15 @@ do { \ } \ } while (0) +#define FAIL_IF_EXIT(x) \ +do { \ + if ((x)) { \ + fprintf(stderr, \ + "[FAIL] Test FAILED on line %d\n", __LINE__); \ + _exit(1); \ + } \ +} while (0) + /* The test harness uses this, yes it's gross */ #define MAGIC_SKIP_RETURN_VALUE 99 -- GitLab From 743f3544fffb9662aaf550c8358a8c1b6fcae707 Mon Sep 17 00:00:00 2001 From: Sandipan Das Date: Mon, 27 Jul 2020 09:30:39 +0530 Subject: [PATCH 1066/1476] selftests/powerpc: Add wrapper for gettid The gettid() syscall wrapper was first introduced in glibc 2.30. This adds a wrapper for use in distros running older versions. Suggested-by: Christophe Leroy Suggested-by: Michael Ellerman Signed-off-by: Sandipan Das Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/8ca3b0eeda989707815d1cf337cc33f090408965.1595821792.git.sandipan@linux.ibm.com --- tools/testing/selftests/powerpc/include/utils.h | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/tools/testing/selftests/powerpc/include/utils.h b/tools/testing/selftests/powerpc/include/utils.h index 69d16875802da..71d2924f5b8b3 100644 --- a/tools/testing/selftests/powerpc/include/utils.h +++ b/tools/testing/selftests/powerpc/include/utils.h @@ -42,6 +42,16 @@ int perf_event_enable(int fd); int perf_event_disable(int fd); int perf_event_reset(int fd); +#if !defined(__GLIBC_PREREQ) || !__GLIBC_PREREQ(2, 30) +#include +#include + +static inline pid_t gettid(void) +{ + return syscall(SYS_gettid); +} +#endif + static inline bool have_hwcap(unsigned long ftr) { return ((unsigned long)get_auxv_entry(AT_HWCAP) & ftr) == ftr; -- GitLab From c27f2fd1705a7e19ef2dc2b986c0d1cde3c3dbe7 Mon Sep 17 00:00:00 2001 From: Sandipan Das Date: Mon, 27 Jul 2020 09:30:40 +0530 Subject: [PATCH 1067/1476] selftests/powerpc: Add test for pkey siginfo verification Commit c46241a370a61 ("powerpc/pkeys: Check vma before returning key fault error to the user") fixes a bug which causes the kernel to set the wrong pkey in siginfo when a pkey fault occurs after two competing threads that have allocated different pkeys, one fully permissive and the other restrictive, attempt to protect a common page at the same time. This adds a test to detect the bug. Signed-off-by: Sandipan Das Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/ce40b6ee270bda52e8f4088578ed2faf7d1d509a.1595821792.git.sandipan@linux.ibm.com --- tools/testing/selftests/powerpc/mm/.gitignore | 1 + tools/testing/selftests/powerpc/mm/Makefile | 5 +- .../selftests/powerpc/mm/pkey_siginfo.c | 333 ++++++++++++++++++ 3 files changed, 338 insertions(+), 1 deletion(-) create mode 100644 tools/testing/selftests/powerpc/mm/pkey_siginfo.c diff --git a/tools/testing/selftests/powerpc/mm/.gitignore b/tools/testing/selftests/powerpc/mm/.gitignore index 8d041f508a517..0323892a80322 100644 --- a/tools/testing/selftests/powerpc/mm/.gitignore +++ b/tools/testing/selftests/powerpc/mm/.gitignore @@ -8,3 +8,4 @@ large_vm_fork_separation bad_accesses tlbie_test pkey_exec_prot +pkey_siginfo diff --git a/tools/testing/selftests/powerpc/mm/Makefile b/tools/testing/selftests/powerpc/mm/Makefile index 5a86d59441dcb..740f7854548ee 100644 --- a/tools/testing/selftests/powerpc/mm/Makefile +++ b/tools/testing/selftests/powerpc/mm/Makefile @@ -3,7 +3,8 @@ noarg: $(MAKE) -C ../ TEST_GEN_PROGS := hugetlb_vs_thp_test subpage_prot segv_errors wild_bctr \ - large_vm_fork_separation bad_accesses pkey_exec_prot + large_vm_fork_separation bad_accesses pkey_exec_prot \ + pkey_siginfo TEST_GEN_PROGS_EXTENDED := tlbie_test TEST_GEN_FILES := tempfile @@ -16,8 +17,10 @@ $(OUTPUT)/wild_bctr: CFLAGS += -m64 $(OUTPUT)/large_vm_fork_separation: CFLAGS += -m64 $(OUTPUT)/bad_accesses: CFLAGS += -m64 $(OUTPUT)/pkey_exec_prot: CFLAGS += -m64 +$(OUTPUT)/pkey_siginfo: CFLAGS += -m64 $(OUTPUT)/tempfile: dd if=/dev/zero of=$@ bs=64k count=1 $(OUTPUT)/tlbie_test: LDLIBS += -lpthread +$(OUTPUT)/pkey_siginfo: LDLIBS += -lpthread diff --git a/tools/testing/selftests/powerpc/mm/pkey_siginfo.c b/tools/testing/selftests/powerpc/mm/pkey_siginfo.c new file mode 100644 index 0000000000000..4f815d7c12145 --- /dev/null +++ b/tools/testing/selftests/powerpc/mm/pkey_siginfo.c @@ -0,0 +1,333 @@ +// SPDX-License-Identifier: GPL-2.0 + +/* + * Copyright 2020, Sandipan Das, IBM Corp. + * + * Test if the signal information reports the correct memory protection + * key upon getting a key access violation fault for a page that was + * attempted to be protected by two different keys from two competing + * threads at the same time. + */ + +#define _GNU_SOURCE +#include +#include +#include +#include + +#include +#include +#include + +#include "pkeys.h" + +#define PPC_INST_NOP 0x60000000 +#define PPC_INST_BLR 0x4e800020 +#define PROT_RWX (PROT_READ | PROT_WRITE | PROT_EXEC) + +#define NUM_ITERATIONS 1000000 + +static volatile sig_atomic_t perm_pkey, rest_pkey; +static volatile sig_atomic_t rights, fault_count; +static volatile unsigned int *volatile fault_addr; +static pthread_barrier_t iteration_barrier; + +static void segv_handler(int signum, siginfo_t *sinfo, void *ctx) +{ + void *pgstart; + size_t pgsize; + int pkey; + + pkey = siginfo_pkey(sinfo); + + /* Check if this fault originated from a pkey access violation */ + if (sinfo->si_code != SEGV_PKUERR) { + sigsafe_err("got a fault for an unexpected reason\n"); + _exit(1); + } + + /* Check if this fault originated from the expected address */ + if (sinfo->si_addr != (void *) fault_addr) { + sigsafe_err("got a fault for an unexpected address\n"); + _exit(1); + } + + /* Check if this fault originated from the restrictive pkey */ + if (pkey != rest_pkey) { + sigsafe_err("got a fault for an unexpected pkey\n"); + _exit(1); + } + + /* Check if too many faults have occurred for the same iteration */ + if (fault_count > 0) { + sigsafe_err("got too many faults for the same address\n"); + _exit(1); + } + + pgsize = getpagesize(); + pgstart = (void *) ((unsigned long) fault_addr & ~(pgsize - 1)); + + /* + * If the current fault occurred due to lack of execute rights, + * reassociate the page with the exec-only pkey since execute + * rights cannot be changed directly for the faulting pkey as + * IAMR is inaccessible from userspace. + * + * Otherwise, if the current fault occurred due to lack of + * read-write rights, change the AMR permission bits for the + * pkey. + * + * This will let the test continue. + */ + if (rights == PKEY_DISABLE_EXECUTE && + mprotect(pgstart, pgsize, PROT_EXEC)) + _exit(1); + else + pkey_set_rights(pkey, 0); + + fault_count++; +} + +struct region { + unsigned long rights; + unsigned int *base; + size_t size; +}; + +static void *protect(void *p) +{ + unsigned long rights; + unsigned int *base; + size_t size; + int tid, i; + + tid = gettid(); + base = ((struct region *) p)->base; + size = ((struct region *) p)->size; + FAIL_IF_EXIT(!base); + + /* No read, write and execute restrictions */ + rights = 0; + + printf("tid %d, pkey permissions are %s\n", tid, pkey_rights(rights)); + + /* Allocate the permissive pkey */ + perm_pkey = sys_pkey_alloc(0, rights); + FAIL_IF_EXIT(perm_pkey < 0); + + /* + * Repeatedly try to protect the common region with a permissive + * pkey + */ + for (i = 0; i < NUM_ITERATIONS; i++) { + /* + * Wait until the other thread has finished allocating the + * restrictive pkey or until the next iteration has begun + */ + pthread_barrier_wait(&iteration_barrier); + + /* Try to associate the permissive pkey with the region */ + FAIL_IF_EXIT(sys_pkey_mprotect(base, size, PROT_RWX, + perm_pkey)); + } + + /* Free the permissive pkey */ + sys_pkey_free(perm_pkey); + + return NULL; +} + +static void *protect_access(void *p) +{ + size_t size, numinsns; + unsigned int *base; + int tid, i; + + tid = gettid(); + base = ((struct region *) p)->base; + size = ((struct region *) p)->size; + rights = ((struct region *) p)->rights; + numinsns = size / sizeof(base[0]); + FAIL_IF_EXIT(!base); + + /* Allocate the restrictive pkey */ + rest_pkey = sys_pkey_alloc(0, rights); + FAIL_IF_EXIT(rest_pkey < 0); + + printf("tid %d, pkey permissions are %s\n", tid, pkey_rights(rights)); + printf("tid %d, %s randomly in range [%p, %p]\n", tid, + (rights == PKEY_DISABLE_EXECUTE) ? "execute" : + (rights == PKEY_DISABLE_WRITE) ? "write" : "read", + base, base + numinsns); + + /* + * Repeatedly try to protect the common region with a restrictive + * pkey and read, write or execute from it + */ + for (i = 0; i < NUM_ITERATIONS; i++) { + /* + * Wait until the other thread has finished allocating the + * permissive pkey or until the next iteration has begun + */ + pthread_barrier_wait(&iteration_barrier); + + /* Try to associate the restrictive pkey with the region */ + FAIL_IF_EXIT(sys_pkey_mprotect(base, size, PROT_RWX, + rest_pkey)); + + /* Choose a random instruction word address from the region */ + fault_addr = base + (rand() % numinsns); + fault_count = 0; + + switch (rights) { + /* Read protection test */ + case PKEY_DISABLE_ACCESS: + /* + * Read an instruction word from the region and + * verify if it has not been overwritten to + * something unexpected + */ + FAIL_IF_EXIT(*fault_addr != PPC_INST_NOP && + *fault_addr != PPC_INST_BLR); + break; + + /* Write protection test */ + case PKEY_DISABLE_WRITE: + /* + * Write an instruction word to the region and + * verify if the overwrite has succeeded + */ + *fault_addr = PPC_INST_BLR; + FAIL_IF_EXIT(*fault_addr != PPC_INST_BLR); + break; + + /* Execute protection test */ + case PKEY_DISABLE_EXECUTE: + /* Jump to the region and execute instructions */ + asm volatile( + "mtctr %0; bctrl" + : : "r"(fault_addr) : "ctr", "lr"); + break; + } + + /* + * Restore the restrictions originally imposed by the + * restrictive pkey as the signal handler would have + * cleared out the corresponding AMR bits + */ + pkey_set_rights(rest_pkey, rights); + } + + /* Free restrictive pkey */ + sys_pkey_free(rest_pkey); + + return NULL; +} + +static void reset_pkeys(unsigned long rights) +{ + int pkeys[NR_PKEYS], i; + + /* Exhaustively allocate all available pkeys */ + for (i = 0; i < NR_PKEYS; i++) + pkeys[i] = sys_pkey_alloc(0, rights); + + /* Free all allocated pkeys */ + for (i = 0; i < NR_PKEYS; i++) + sys_pkey_free(pkeys[i]); +} + +static int test(void) +{ + pthread_t prot_thread, pacc_thread; + struct sigaction act; + pthread_attr_t attr; + size_t numinsns; + struct region r; + int ret, i; + + srand(time(NULL)); + ret = pkeys_unsupported(); + if (ret) + return ret; + + /* Allocate the region */ + r.size = getpagesize(); + r.base = mmap(NULL, r.size, PROT_RWX, + MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + FAIL_IF(r.base == MAP_FAILED); + + /* + * Fill the region with no-ops with a branch at the end + * for returning to the caller + */ + numinsns = r.size / sizeof(r.base[0]); + for (i = 0; i < numinsns - 1; i++) + r.base[i] = PPC_INST_NOP; + r.base[i] = PPC_INST_BLR; + + /* Setup SIGSEGV handler */ + act.sa_handler = 0; + act.sa_sigaction = segv_handler; + FAIL_IF(sigprocmask(SIG_SETMASK, 0, &act.sa_mask) != 0); + act.sa_flags = SA_SIGINFO; + act.sa_restorer = 0; + FAIL_IF(sigaction(SIGSEGV, &act, NULL) != 0); + + /* + * For these tests, the parent process should clear all bits of + * AMR and IAMR, i.e. impose no restrictions, for all available + * pkeys. This will be the base for the initial AMR and IAMR + * values for all the test thread pairs. + * + * If the AMR and IAMR bits of all available pkeys are cleared + * before running the tests and a fault is generated when + * attempting to read, write or execute instructions from a + * pkey protected region, the pkey responsible for this must be + * the one from the protect-and-access thread since the other + * one is fully permissive. Despite that, if the pkey reported + * by siginfo is not the restrictive pkey, then there must be a + * kernel bug. + */ + reset_pkeys(0); + + /* Setup barrier for protect and protect-and-access threads */ + FAIL_IF(pthread_attr_init(&attr) != 0); + FAIL_IF(pthread_barrier_init(&iteration_barrier, NULL, 2) != 0); + + /* Setup and start protect and protect-and-read threads */ + puts("starting thread pair (protect, protect-and-read)"); + r.rights = PKEY_DISABLE_ACCESS; + FAIL_IF(pthread_create(&prot_thread, &attr, &protect, &r) != 0); + FAIL_IF(pthread_create(&pacc_thread, &attr, &protect_access, &r) != 0); + FAIL_IF(pthread_join(prot_thread, NULL) != 0); + FAIL_IF(pthread_join(pacc_thread, NULL) != 0); + + /* Setup and start protect and protect-and-write threads */ + puts("starting thread pair (protect, protect-and-write)"); + r.rights = PKEY_DISABLE_WRITE; + FAIL_IF(pthread_create(&prot_thread, &attr, &protect, &r) != 0); + FAIL_IF(pthread_create(&pacc_thread, &attr, &protect_access, &r) != 0); + FAIL_IF(pthread_join(prot_thread, NULL) != 0); + FAIL_IF(pthread_join(pacc_thread, NULL) != 0); + + /* Setup and start protect and protect-and-execute threads */ + puts("starting thread pair (protect, protect-and-execute)"); + r.rights = PKEY_DISABLE_EXECUTE; + FAIL_IF(pthread_create(&prot_thread, &attr, &protect, &r) != 0); + FAIL_IF(pthread_create(&pacc_thread, &attr, &protect_access, &r) != 0); + FAIL_IF(pthread_join(prot_thread, NULL) != 0); + FAIL_IF(pthread_join(pacc_thread, NULL) != 0); + + /* Cleanup */ + FAIL_IF(pthread_attr_destroy(&attr) != 0); + FAIL_IF(pthread_barrier_destroy(&iteration_barrier) != 0); + munmap(r.base, r.size); + + return 0; +} + +int main(void) +{ + test_harness(test, "pkey_siginfo"); +} -- GitLab From 5f8cf6475828b600ff6d000e580c961ac839cc61 Mon Sep 17 00:00:00 2001 From: Oliver O'Halloran Date: Mon, 27 Jul 2020 11:01:27 +1000 Subject: [PATCH 1068/1476] selftests/powerpc: Squash spurious errors due to device removal For drivers that don't have the error handling callbacks we implement recovery by removing the device and re-probing it. This causes the sysfs directory for the PCI device to be removed which causes the following spurious error to be printed when checking the PE state: Breaking 0005:03:00.0... ./eeh-basic.sh: line 13: can't open /sys/bus/pci/devices/0005:03:00.0/eeh_pe_state: no such file 0005:03:00.0, waited 0/60 0005:03:00.0, waited 1/60 0005:03:00.0, waited 2/60 0005:03:00.0, waited 3/60 0005:03:00.0, waited 4/60 0005:03:00.0, waited 5/60 0005:03:00.0, waited 6/60 0005:03:00.0, waited 7/60 0005:03:00.0, Recovered after 8 seconds We currently try to avoid this by checking if the PE state file exists before reading from it. This is however inherently racy so re-work the state checking so that we only read from the file once, and we squash any errors that occur while reading. Fixes: 85d86c8aa52e ("selftests/powerpc: Add basic EEH selftest") Signed-off-by: Oliver O'Halloran Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20200727010127.23698-1-oohall@gmail.com --- tools/testing/selftests/powerpc/eeh/eeh-functions.sh | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/tools/testing/selftests/powerpc/eeh/eeh-functions.sh b/tools/testing/selftests/powerpc/eeh/eeh-functions.sh index f52ed92b53e74..00dc32c0ed75c 100755 --- a/tools/testing/selftests/powerpc/eeh/eeh-functions.sh +++ b/tools/testing/selftests/powerpc/eeh/eeh-functions.sh @@ -5,12 +5,17 @@ pe_ok() { local dev="$1" local path="/sys/bus/pci/devices/$dev/eeh_pe_state" - if ! [ -e "$path" ] ; then + # if a driver doesn't support the error handling callbacks then the + # device is recovered by removing and re-probing it. This causes the + # sysfs directory to disappear so read the PE state once and squash + # any potential error messages + local eeh_state="$(cat $path 2>/dev/null)" + if [ -z "$eeh_state" ]; then return 1; fi - local fw_state="$(cut -d' ' -f1 < $path)" - local sw_state="$(cut -d' ' -f2 < $path)" + local fw_state="$(echo $eeh_state | cut -d' ' -f1)" + local sw_state="$(echo $eeh_state | cut -d' ' -f2)" # If EEH_PE_ISOLATED or EEH_PE_RECOVERING are set then the PE is in an # error state or being recovered. Either way, not ok. -- GitLab From c9938a9dac95be7650218cdd8e9d1f882e7b5691 Mon Sep 17 00:00:00 2001 From: Michael Ellerman Date: Fri, 24 Jul 2020 19:25:24 +1000 Subject: [PATCH 1069/1476] selftests/powerpc: Add test of stack expansion logic We have custom stack expansion checks that it turns out are extremely badly tested and contain bugs, surprise. So add some tests that exercise the code and capture the current boundary conditions. The signal test currently fails on 64-bit kernels because the 2048 byte allowance for the signal frame is too small, we will fix that in a subsequent patch. Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20200724092528.1578671-1-mpe@ellerman.id.au --- tools/testing/selftests/powerpc/mm/.gitignore | 2 + tools/testing/selftests/powerpc/mm/Makefile | 8 +- .../powerpc/mm/stack_expansion_ldst.c | 233 ++++++++++++++++++ .../powerpc/mm/stack_expansion_signal.c | 118 +++++++++ tools/testing/selftests/powerpc/pmu/lib.h | 1 + 5 files changed, 361 insertions(+), 1 deletion(-) create mode 100644 tools/testing/selftests/powerpc/mm/stack_expansion_ldst.c create mode 100644 tools/testing/selftests/powerpc/mm/stack_expansion_signal.c diff --git a/tools/testing/selftests/powerpc/mm/.gitignore b/tools/testing/selftests/powerpc/mm/.gitignore index 0323892a80322..91c775c23c660 100644 --- a/tools/testing/selftests/powerpc/mm/.gitignore +++ b/tools/testing/selftests/powerpc/mm/.gitignore @@ -9,3 +9,5 @@ bad_accesses tlbie_test pkey_exec_prot pkey_siginfo +stack_expansion_ldst +stack_expansion_signal diff --git a/tools/testing/selftests/powerpc/mm/Makefile b/tools/testing/selftests/powerpc/mm/Makefile index 740f7854548ee..250ce172e0dac 100644 --- a/tools/testing/selftests/powerpc/mm/Makefile +++ b/tools/testing/selftests/powerpc/mm/Makefile @@ -4,7 +4,8 @@ noarg: TEST_GEN_PROGS := hugetlb_vs_thp_test subpage_prot segv_errors wild_bctr \ large_vm_fork_separation bad_accesses pkey_exec_prot \ - pkey_siginfo + pkey_siginfo stack_expansion_signal stack_expansion_ldst + TEST_GEN_PROGS_EXTENDED := tlbie_test TEST_GEN_FILES := tempfile @@ -19,6 +20,11 @@ $(OUTPUT)/bad_accesses: CFLAGS += -m64 $(OUTPUT)/pkey_exec_prot: CFLAGS += -m64 $(OUTPUT)/pkey_siginfo: CFLAGS += -m64 +$(OUTPUT)/stack_expansion_signal: ../utils.c ../pmu/lib.c + +$(OUTPUT)/stack_expansion_ldst: CFLAGS += -fno-stack-protector +$(OUTPUT)/stack_expansion_ldst: ../utils.c + $(OUTPUT)/tempfile: dd if=/dev/zero of=$@ bs=64k count=1 diff --git a/tools/testing/selftests/powerpc/mm/stack_expansion_ldst.c b/tools/testing/selftests/powerpc/mm/stack_expansion_ldst.c new file mode 100644 index 0000000000000..0587e11437f5b --- /dev/null +++ b/tools/testing/selftests/powerpc/mm/stack_expansion_ldst.c @@ -0,0 +1,233 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Test that loads/stores expand the stack segment, or trigger a SEGV, in + * various conditions. + * + * Based on test code by Tom Lane. + */ + +#undef NDEBUG +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define _KB (1024) +#define _MB (1024 * 1024) + +volatile char *stack_top_ptr; +volatile unsigned long stack_top_sp; +volatile char c; + +enum access_type { + LOAD, + STORE, +}; + +/* + * Consume stack until the stack pointer is below @target_sp, then do an access + * (load or store) at offset @delta from either the base of the stack or the + * current stack pointer. + */ +__attribute__ ((noinline)) +int consume_stack(unsigned long target_sp, unsigned long stack_high, int delta, enum access_type type) +{ + unsigned long target; + char stack_cur; + + if ((unsigned long)&stack_cur > target_sp) + return consume_stack(target_sp, stack_high, delta, type); + else { + // We don't really need this, but without it GCC might not + // generate a recursive call above. + stack_top_ptr = &stack_cur; + +#ifdef __powerpc__ + asm volatile ("mr %[sp], %%r1" : [sp] "=r" (stack_top_sp)); +#else + asm volatile ("mov %%rsp, %[sp]" : [sp] "=r" (stack_top_sp)); +#endif + + // Kludge, delta < 0 indicates relative to SP + if (delta < 0) + target = stack_top_sp + delta; + else + target = stack_high - delta + 1; + + volatile char *p = (char *)target; + + if (type == STORE) + *p = c; + else + c = *p; + + // Do something to prevent the stack frame being popped prior to + // our access above. + getpid(); + } + + return 0; +} + +static int search_proc_maps(char *needle, unsigned long *low, unsigned long *high) +{ + unsigned long start, end; + static char buf[4096]; + char name[128]; + FILE *f; + int rc; + + f = fopen("/proc/self/maps", "r"); + if (!f) { + perror("fopen"); + return -1; + } + + while (fgets(buf, sizeof(buf), f)) { + rc = sscanf(buf, "%lx-%lx %*c%*c%*c%*c %*x %*d:%*d %*d %127s\n", + &start, &end, name); + if (rc == 2) + continue; + + if (rc != 3) { + printf("sscanf errored\n"); + rc = -1; + break; + } + + if (strstr(name, needle)) { + *low = start; + *high = end - 1; + rc = 0; + break; + } + } + + fclose(f); + + return rc; +} + +int child(unsigned int stack_used, int delta, enum access_type type) +{ + unsigned long low, stack_high; + + assert(search_proc_maps("[stack]", &low, &stack_high) == 0); + + assert(consume_stack(stack_high - stack_used, stack_high, delta, type) == 0); + + printf("Access OK: %s delta %-7d used size 0x%06x stack high 0x%lx top_ptr %p top sp 0x%lx actual used 0x%lx\n", + type == LOAD ? "load" : "store", delta, stack_used, stack_high, + stack_top_ptr, stack_top_sp, stack_high - stack_top_sp + 1); + + return 0; +} + +static int test_one(unsigned int stack_used, int delta, enum access_type type) +{ + pid_t pid; + int rc; + + pid = fork(); + if (pid == 0) + exit(child(stack_used, delta, type)); + + assert(waitpid(pid, &rc, 0) != -1); + + if (WIFEXITED(rc) && WEXITSTATUS(rc) == 0) + return 0; + + // We don't expect a non-zero exit that's not a signal + assert(!WIFEXITED(rc)); + + printf("Faulted: %s delta %-7d used size 0x%06x signal %d\n", + type == LOAD ? "load" : "store", delta, stack_used, + WTERMSIG(rc)); + + return 1; +} + +// This is fairly arbitrary but is well below any of the targets below, +// so that the delta between the stack pointer and the target is large. +#define DEFAULT_SIZE (32 * _KB) + +static void test_one_type(enum access_type type, unsigned long page_size, unsigned long rlim_cur) +{ + assert(test_one(DEFAULT_SIZE, 512 * _KB, type) == 0); + + // powerpc has a special case to allow up to 1MB + assert(test_one(DEFAULT_SIZE, 1 * _MB, type) == 0); + +#ifdef __powerpc__ + // This fails on powerpc because it's > 1MB and is not a stdu & + // not close to r1 + assert(test_one(DEFAULT_SIZE, 1 * _MB + 8, type) != 0); +#else + assert(test_one(DEFAULT_SIZE, 1 * _MB + 8, type) == 0); +#endif + +#ifdef __powerpc__ + // Accessing way past the stack pointer is not allowed on powerpc + assert(test_one(DEFAULT_SIZE, rlim_cur, type) != 0); +#else + // We should be able to access anywhere within the rlimit + assert(test_one(DEFAULT_SIZE, rlim_cur, type) == 0); +#endif + + // But if we go past the rlimit it should fail + assert(test_one(DEFAULT_SIZE, rlim_cur + 1, type) != 0); + + // Above 1MB powerpc only allows accesses within 2048 bytes of + // r1 for accesses that aren't stdu + assert(test_one(1 * _MB + page_size - 128, -2048, type) == 0); +#ifdef __powerpc__ + assert(test_one(1 * _MB + page_size - 128, -2049, type) != 0); +#else + assert(test_one(1 * _MB + page_size - 128, -2049, type) == 0); +#endif + + // By consuming 2MB of stack we test the stdu case + assert(test_one(2 * _MB + page_size - 128, -2048, type) == 0); +} + +static int test(void) +{ + unsigned long page_size; + struct rlimit rlimit; + + page_size = getpagesize(); + getrlimit(RLIMIT_STACK, &rlimit); + printf("Stack rlimit is 0x%lx\n", rlimit.rlim_cur); + + printf("Testing loads ...\n"); + test_one_type(LOAD, page_size, rlimit.rlim_cur); + printf("Testing stores ...\n"); + test_one_type(STORE, page_size, rlimit.rlim_cur); + + printf("All OK\n"); + + return 0; +} + +#ifdef __powerpc__ +#include "utils.h" + +int main(void) +{ + return test_harness(test, "stack_expansion_ldst"); +} +#else +int main(void) +{ + return test(); +} +#endif diff --git a/tools/testing/selftests/powerpc/mm/stack_expansion_signal.c b/tools/testing/selftests/powerpc/mm/stack_expansion_signal.c new file mode 100644 index 0000000000000..c8b32a29e2747 --- /dev/null +++ b/tools/testing/selftests/powerpc/mm/stack_expansion_signal.c @@ -0,0 +1,118 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Test that signal delivery is able to expand the stack segment without + * triggering a SEGV. + * + * Based on test code by Tom Lane. + */ + +#include +#include +#include +#include +#include +#include + +#include "../pmu/lib.h" +#include "utils.h" + +#define _KB (1024) +#define _MB (1024 * 1024) + +static char *stack_base_ptr; +static char *stack_top_ptr; + +static volatile sig_atomic_t sig_occurred = 0; + +static void sigusr1_handler(int signal_arg) +{ + sig_occurred = 1; +} + +static int consume_stack(unsigned int stack_size, union pipe write_pipe) +{ + char stack_cur; + + if ((stack_base_ptr - &stack_cur) < stack_size) + return consume_stack(stack_size, write_pipe); + else { + stack_top_ptr = &stack_cur; + + FAIL_IF(notify_parent(write_pipe)); + + while (!sig_occurred) + barrier(); + } + + return 0; +} + +static int child(unsigned int stack_size, union pipe write_pipe) +{ + struct sigaction act; + char stack_base; + + act.sa_handler = sigusr1_handler; + sigemptyset(&act.sa_mask); + act.sa_flags = 0; + if (sigaction(SIGUSR1, &act, NULL) < 0) + err(1, "sigaction"); + + stack_base_ptr = (char *) (((size_t) &stack_base + 65535) & ~65535UL); + + FAIL_IF(consume_stack(stack_size, write_pipe)); + + printf("size 0x%06x: OK, stack base %p top %p (%zx used)\n", + stack_size, stack_base_ptr, stack_top_ptr, + stack_base_ptr - stack_top_ptr); + + return 0; +} + +static int test_one_size(unsigned int stack_size) +{ + union pipe read_pipe, write_pipe; + pid_t pid; + + FAIL_IF(pipe(read_pipe.fds) == -1); + FAIL_IF(pipe(write_pipe.fds) == -1); + + pid = fork(); + if (pid == 0) { + close(read_pipe.read_fd); + close(write_pipe.write_fd); + exit(child(stack_size, read_pipe)); + } + + close(read_pipe.write_fd); + close(write_pipe.read_fd); + FAIL_IF(sync_with_child(read_pipe, write_pipe)); + + kill(pid, SIGUSR1); + + FAIL_IF(wait_for_child(pid)); + + close(read_pipe.read_fd); + close(write_pipe.write_fd); + + return 0; +} + +int test(void) +{ + unsigned int i, size; + + // Test with used stack from 1MB - 64K to 1MB + 64K + // Increment by 64 to get more coverage of odd sizes + for (i = 0; i < (128 * _KB); i += 64) { + size = i + (1 * _MB) - (64 * _KB); + FAIL_IF(test_one_size(size)); + } + + return 0; +} + +int main(void) +{ + return test_harness(test, "stack_expansion_signal"); +} diff --git a/tools/testing/selftests/powerpc/pmu/lib.h b/tools/testing/selftests/powerpc/pmu/lib.h index fa12e7d0b4d39..bf1bec013bbb4 100644 --- a/tools/testing/selftests/powerpc/pmu/lib.h +++ b/tools/testing/selftests/powerpc/pmu/lib.h @@ -6,6 +6,7 @@ #ifndef __SELFTESTS_POWERPC_PMU_LIB_H #define __SELFTESTS_POWERPC_PMU_LIB_H +#include #include #include #include -- GitLab From 63dee5df43a31f3844efabc58972f0a206ca4534 Mon Sep 17 00:00:00 2001 From: Michael Ellerman Date: Fri, 24 Jul 2020 19:25:25 +1000 Subject: [PATCH 1070/1476] powerpc: Allow 4224 bytes of stack expansion for the signal frame We have powerpc specific logic in our page fault handling to decide if an access to an unmapped address below the stack pointer should expand the stack VMA. The code was originally added in 2004 "ported from 2.4". The rough logic is that the stack is allowed to grow to 1MB with no extra checking. Over 1MB the access must be within 2048 bytes of the stack pointer, or be from a user instruction that updates the stack pointer. The 2048 byte allowance below the stack pointer is there to cover the 288 byte "red zone" as well as the "about 1.5kB" needed by the signal delivery code. Unfortunately since then the signal frame has expanded, and is now 4224 bytes on 64-bit kernels with transactional memory enabled. This means if a process has consumed more than 1MB of stack, and its stack pointer lies less than 4224 bytes from the next page boundary, signal delivery will fault when trying to expand the stack and the process will see a SEGV. The total size of the signal frame is the size of struct rt_sigframe (which includes the red zone) plus __SIGNAL_FRAMESIZE (128 bytes on 64-bit). The 2048 byte allowance was correct until 2008 as the signal frame was: struct rt_sigframe { struct ucontext uc; /* 0 1440 */ /* --- cacheline 11 boundary (1408 bytes) was 32 bytes ago --- */ long unsigned int _unused[2]; /* 1440 16 */ unsigned int tramp[6]; /* 1456 24 */ struct siginfo * pinfo; /* 1480 8 */ void * puc; /* 1488 8 */ struct siginfo info; /* 1496 128 */ /* --- cacheline 12 boundary (1536 bytes) was 88 bytes ago --- */ char abigap[288]; /* 1624 288 */ /* size: 1920, cachelines: 15, members: 7 */ /* padding: 8 */ }; 1920 + 128 = 2048 Then in commit ce48b2100785 ("powerpc: Add VSX context save/restore, ptrace and signal support") (Jul 2008) the signal frame expanded to 2304 bytes: struct rt_sigframe { struct ucontext uc; /* 0 1696 */ <-- /* --- cacheline 13 boundary (1664 bytes) was 32 bytes ago --- */ long unsigned int _unused[2]; /* 1696 16 */ unsigned int tramp[6]; /* 1712 24 */ struct siginfo * pinfo; /* 1736 8 */ void * puc; /* 1744 8 */ struct siginfo info; /* 1752 128 */ /* --- cacheline 14 boundary (1792 bytes) was 88 bytes ago --- */ char abigap[288]; /* 1880 288 */ /* size: 2176, cachelines: 17, members: 7 */ /* padding: 8 */ }; 2176 + 128 = 2304 At this point we should have been exposed to the bug, though as far as I know it was never reported. I no longer have a system old enough to easily test on. Then in 2010 commit 320b2b8de126 ("mm: keep a guard page below a grow-down stack segment") caused our stack expansion code to never trigger, as there was always a VMA found for a write up to PAGE_SIZE below r1. That meant the bug was hidden as we continued to expand the signal frame in commit 2b0a576d15e0 ("powerpc: Add new transactional memory state to the signal context") (Feb 2013): struct rt_sigframe { struct ucontext uc; /* 0 1696 */ /* --- cacheline 13 boundary (1664 bytes) was 32 bytes ago --- */ struct ucontext uc_transact; /* 1696 1696 */ <-- /* --- cacheline 26 boundary (3328 bytes) was 64 bytes ago --- */ long unsigned int _unused[2]; /* 3392 16 */ unsigned int tramp[6]; /* 3408 24 */ struct siginfo * pinfo; /* 3432 8 */ void * puc; /* 3440 8 */ struct siginfo info; /* 3448 128 */ /* --- cacheline 27 boundary (3456 bytes) was 120 bytes ago --- */ char abigap[288]; /* 3576 288 */ /* size: 3872, cachelines: 31, members: 8 */ /* padding: 8 */ /* last cacheline: 32 bytes */ }; 3872 + 128 = 4000 And commit 573ebfa6601f ("powerpc: Increase stack redzone for 64-bit userspace to 512 bytes") (Feb 2014): struct rt_sigframe { struct ucontext uc; /* 0 1696 */ /* --- cacheline 13 boundary (1664 bytes) was 32 bytes ago --- */ struct ucontext uc_transact; /* 1696 1696 */ /* --- cacheline 26 boundary (3328 bytes) was 64 bytes ago --- */ long unsigned int _unused[2]; /* 3392 16 */ unsigned int tramp[6]; /* 3408 24 */ struct siginfo * pinfo; /* 3432 8 */ void * puc; /* 3440 8 */ struct siginfo info; /* 3448 128 */ /* --- cacheline 27 boundary (3456 bytes) was 120 bytes ago --- */ char abigap[512]; /* 3576 512 */ <-- /* size: 4096, cachelines: 32, members: 8 */ /* padding: 8 */ }; 4096 + 128 = 4224 Then finally in 2017, commit 1be7107fbe18 ("mm: larger stack guard gap, between vmas") exposed us to the existing bug, because it changed the stack VMA to be the correct/real size, meaning our stack expansion code is now triggered. Fix it by increasing the allowance to 4224 bytes. Hard-coding 4224 is obviously unsafe against future expansions of the signal frame in the same way as the existing code. We can't easily use sizeof() because the signal frame structure is not in a header. We will either fix that, or rip out all the custom stack expansion checking logic entirely. Fixes: ce48b2100785 ("powerpc: Add VSX context save/restore, ptrace and signal support") Cc: stable@vger.kernel.org # v2.6.27+ Reported-by: Tom Lane Tested-by: Daniel Axtens Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20200724092528.1578671-2-mpe@ellerman.id.au --- arch/powerpc/mm/fault.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/arch/powerpc/mm/fault.c b/arch/powerpc/mm/fault.c index 641fc5f3d7dd9..3ebb1792e6367 100644 --- a/arch/powerpc/mm/fault.c +++ b/arch/powerpc/mm/fault.c @@ -267,6 +267,9 @@ static bool bad_kernel_fault(struct pt_regs *regs, unsigned long error_code, return false; } +// This comes from 64-bit struct rt_sigframe + __SIGNAL_FRAMESIZE +#define SIGFRAME_MAX_SIZE (4096 + 128) + static bool bad_stack_expansion(struct pt_regs *regs, unsigned long address, struct vm_area_struct *vma, unsigned int flags, bool *must_retry) @@ -274,7 +277,7 @@ static bool bad_stack_expansion(struct pt_regs *regs, unsigned long address, /* * N.B. The POWER/Open ABI allows programs to access up to * 288 bytes below the stack pointer. - * The kernel signal delivery code writes up to about 1.5kB + * The kernel signal delivery code writes a bit over 4KB * below the stack pointer (r1) before decrementing it. * The exec code can write slightly over 640kB to the stack * before setting the user r1. Thus we allow the stack to @@ -299,7 +302,7 @@ static bool bad_stack_expansion(struct pt_regs *regs, unsigned long address, * between the last mapped region and the stack will * expand the stack rather than segfaulting. */ - if (address + 2048 >= uregs->gpr[1]) + if (address + SIGFRAME_MAX_SIZE >= uregs->gpr[1]) return false; if ((flags & FAULT_FLAG_WRITE) && (flags & FAULT_FLAG_USER) && -- GitLab From 9ee571d84bf8cfdd587a1acbf3490ca90fc40c9d Mon Sep 17 00:00:00 2001 From: Michael Ellerman Date: Fri, 24 Jul 2020 19:25:26 +1000 Subject: [PATCH 1071/1476] selftests/powerpc: Update the stack expansion test Update the stack expansion load/store test to take into account the new allowance of 4224 bytes below the stack pointer. Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20200724092528.1578671-3-mpe@ellerman.id.au --- .../selftests/powerpc/mm/stack_expansion_ldst.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/tools/testing/selftests/powerpc/mm/stack_expansion_ldst.c b/tools/testing/selftests/powerpc/mm/stack_expansion_ldst.c index 0587e11437f5b..8dbfb51acf0f9 100644 --- a/tools/testing/selftests/powerpc/mm/stack_expansion_ldst.c +++ b/tools/testing/selftests/powerpc/mm/stack_expansion_ldst.c @@ -186,17 +186,17 @@ static void test_one_type(enum access_type type, unsigned long page_size, unsign // But if we go past the rlimit it should fail assert(test_one(DEFAULT_SIZE, rlim_cur + 1, type) != 0); - // Above 1MB powerpc only allows accesses within 2048 bytes of + // Above 1MB powerpc only allows accesses within 4224 bytes of // r1 for accesses that aren't stdu - assert(test_one(1 * _MB + page_size - 128, -2048, type) == 0); + assert(test_one(1 * _MB + page_size - 128, -4224, type) == 0); #ifdef __powerpc__ - assert(test_one(1 * _MB + page_size - 128, -2049, type) != 0); + assert(test_one(1 * _MB + page_size - 128, -4225, type) != 0); #else - assert(test_one(1 * _MB + page_size - 128, -2049, type) == 0); + assert(test_one(1 * _MB + page_size - 128, -4225, type) == 0); #endif // By consuming 2MB of stack we test the stdu case - assert(test_one(2 * _MB + page_size - 128, -2048, type) == 0); + assert(test_one(2 * _MB + page_size - 128, -4224, type) == 0); } static int test(void) -- GitLab From 773b3e53df5b84e73bf64998e4019f50a6662ad1 Mon Sep 17 00:00:00 2001 From: Michael Ellerman Date: Fri, 24 Jul 2020 19:25:27 +1000 Subject: [PATCH 1072/1476] powerpc/mm: Remove custom stack expansion checking We have powerpc specific logic in our page fault handling to decide if an access to an unmapped address below the stack pointer should expand the stack VMA. The logic aims to prevent userspace from doing bad accesses below the stack pointer. However as long as the stack is < 1MB in size, we allow all accesses without further checks. Adding some debug I see that I can do a full kernel build and LTP run, and not a single process has used more than 1MB of stack. So for the majority of processes the logic never even fires. We also recently found a nasty bug in this code which could cause userspace programs to be killed during signal delivery. It went unnoticed presumably because most processes use < 1MB of stack. The generic mm code has also grown support for stack guard pages since this code was originally written, so the most heinous case of the stack expanding into other mappings is now handled for us. Finally although some other arches have special logic in this path, from what I can tell none of x86, arm64, arm and s390 impose any extra checks other than those in expand_stack(). So drop our complicated logic and like other architectures just let the stack expand as long as its within the rlimit. Signed-off-by: Michael Ellerman Tested-by: Daniel Axtens Link: https://lore.kernel.org/r/20200724092528.1578671-4-mpe@ellerman.id.au --- arch/powerpc/mm/fault.c | 109 ++-------------------------------------- 1 file changed, 5 insertions(+), 104 deletions(-) diff --git a/arch/powerpc/mm/fault.c b/arch/powerpc/mm/fault.c index 3ebb1792e6367..925a7231abb33 100644 --- a/arch/powerpc/mm/fault.c +++ b/arch/powerpc/mm/fault.c @@ -42,39 +42,7 @@ #include #include -/* - * Check whether the instruction inst is a store using - * an update addressing form which will update r1. - */ -static bool store_updates_sp(struct ppc_inst inst) -{ - /* check for 1 in the rA field */ - if (((ppc_inst_val(inst) >> 16) & 0x1f) != 1) - return false; - /* check major opcode */ - switch (ppc_inst_primary_opcode(inst)) { - case OP_STWU: - case OP_STBU: - case OP_STHU: - case OP_STFSU: - case OP_STFDU: - return true; - case OP_STD: /* std or stdu */ - return (ppc_inst_val(inst) & 3) == 1; - case OP_31: - /* check minor opcode */ - switch ((ppc_inst_val(inst) >> 1) & 0x3ff) { - case OP_31_XOP_STDUX: - case OP_31_XOP_STWUX: - case OP_31_XOP_STBUX: - case OP_31_XOP_STHUX: - case OP_31_XOP_STFSUX: - case OP_31_XOP_STFDUX: - return true; - } - } - return false; -} + /* * do_page_fault error handling helpers */ @@ -267,57 +235,6 @@ static bool bad_kernel_fault(struct pt_regs *regs, unsigned long error_code, return false; } -// This comes from 64-bit struct rt_sigframe + __SIGNAL_FRAMESIZE -#define SIGFRAME_MAX_SIZE (4096 + 128) - -static bool bad_stack_expansion(struct pt_regs *regs, unsigned long address, - struct vm_area_struct *vma, unsigned int flags, - bool *must_retry) -{ - /* - * N.B. The POWER/Open ABI allows programs to access up to - * 288 bytes below the stack pointer. - * The kernel signal delivery code writes a bit over 4KB - * below the stack pointer (r1) before decrementing it. - * The exec code can write slightly over 640kB to the stack - * before setting the user r1. Thus we allow the stack to - * expand to 1MB without further checks. - */ - if (address + 0x100000 < vma->vm_end) { - struct ppc_inst __user *nip = (struct ppc_inst __user *)regs->nip; - /* get user regs even if this fault is in kernel mode */ - struct pt_regs *uregs = current->thread.regs; - if (uregs == NULL) - return true; - - /* - * A user-mode access to an address a long way below - * the stack pointer is only valid if the instruction - * is one which would update the stack pointer to the - * address accessed if the instruction completed, - * i.e. either stwu rs,n(r1) or stwux rs,r1,rb - * (or the byte, halfword, float or double forms). - * - * If we don't check this then any write to the area - * between the last mapped region and the stack will - * expand the stack rather than segfaulting. - */ - if (address + SIGFRAME_MAX_SIZE >= uregs->gpr[1]) - return false; - - if ((flags & FAULT_FLAG_WRITE) && (flags & FAULT_FLAG_USER) && - access_ok(nip, sizeof(*nip))) { - struct ppc_inst inst; - - if (!probe_user_read_inst(&inst, nip)) - return !store_updates_sp(inst); - *must_retry = true; - } - return true; - } - return false; -} - #ifdef CONFIG_PPC_MEM_KEYS static bool access_pkey_error(bool is_write, bool is_exec, bool is_pkey, struct vm_area_struct *vma) @@ -483,7 +400,6 @@ static int __do_page_fault(struct pt_regs *regs, unsigned long address, int is_user = user_mode(regs); int is_write = page_fault_is_write(error_code); vm_fault_t fault, major = 0; - bool must_retry = false; bool kprobe_fault = kprobe_page_fault(regs, 11); if (unlikely(debugger_fault_handler(regs) || kprobe_fault)) @@ -572,30 +488,15 @@ retry: vma = find_vma(mm, address); if (unlikely(!vma)) return bad_area(regs, address); - if (likely(vma->vm_start <= address)) - goto good_area; - if (unlikely(!(vma->vm_flags & VM_GROWSDOWN))) - return bad_area(regs, address); - /* The stack is being expanded, check if it's valid */ - if (unlikely(bad_stack_expansion(regs, address, vma, flags, - &must_retry))) { - if (!must_retry) + if (unlikely(vma->vm_start > address)) { + if (unlikely(!(vma->vm_flags & VM_GROWSDOWN))) return bad_area(regs, address); - mmap_read_unlock(mm); - if (fault_in_pages_readable((const char __user *)regs->nip, - sizeof(unsigned int))) - return bad_area_nosemaphore(regs, address); - goto retry; + if (unlikely(expand_stack(vma, address))) + return bad_area(regs, address); } - /* Try to expand it */ - if (unlikely(expand_stack(vma, address))) - return bad_area(regs, address); - -good_area: - #ifdef CONFIG_PPC_MEM_KEYS if (unlikely(access_pkey_error(is_write, is_exec, (error_code & DSISR_KEYFAULT), vma))) -- GitLab From 73da08f6966b81feb429af4fb3229da4cf21d6d9 Mon Sep 17 00:00:00 2001 From: Michael Ellerman Date: Fri, 24 Jul 2020 19:25:28 +1000 Subject: [PATCH 1073/1476] selftests/powerpc: Remove powerpc special cases from stack expansion test Now that the powerpc code behaves the same as other architectures we can drop the special cases we had. Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20200724092528.1578671-5-mpe@ellerman.id.au --- .../powerpc/mm/stack_expansion_ldst.c | 41 +++---------------- 1 file changed, 5 insertions(+), 36 deletions(-) diff --git a/tools/testing/selftests/powerpc/mm/stack_expansion_ldst.c b/tools/testing/selftests/powerpc/mm/stack_expansion_ldst.c index 8dbfb51acf0f9..ed9143990888d 100644 --- a/tools/testing/selftests/powerpc/mm/stack_expansion_ldst.c +++ b/tools/testing/selftests/powerpc/mm/stack_expansion_ldst.c @@ -56,13 +56,7 @@ int consume_stack(unsigned long target_sp, unsigned long stack_high, int delta, #else asm volatile ("mov %%rsp, %[sp]" : [sp] "=r" (stack_top_sp)); #endif - - // Kludge, delta < 0 indicates relative to SP - if (delta < 0) - target = stack_top_sp + delta; - else - target = stack_high - delta + 1; - + target = stack_high - delta + 1; volatile char *p = (char *)target; if (type == STORE) @@ -162,41 +156,16 @@ static int test_one(unsigned int stack_used, int delta, enum access_type type) static void test_one_type(enum access_type type, unsigned long page_size, unsigned long rlim_cur) { - assert(test_one(DEFAULT_SIZE, 512 * _KB, type) == 0); + unsigned long delta; - // powerpc has a special case to allow up to 1MB - assert(test_one(DEFAULT_SIZE, 1 * _MB, type) == 0); - -#ifdef __powerpc__ - // This fails on powerpc because it's > 1MB and is not a stdu & - // not close to r1 - assert(test_one(DEFAULT_SIZE, 1 * _MB + 8, type) != 0); -#else - assert(test_one(DEFAULT_SIZE, 1 * _MB + 8, type) == 0); -#endif - -#ifdef __powerpc__ - // Accessing way past the stack pointer is not allowed on powerpc - assert(test_one(DEFAULT_SIZE, rlim_cur, type) != 0); -#else // We should be able to access anywhere within the rlimit + for (delta = page_size; delta <= rlim_cur; delta += page_size) + assert(test_one(DEFAULT_SIZE, delta, type) == 0); + assert(test_one(DEFAULT_SIZE, rlim_cur, type) == 0); -#endif // But if we go past the rlimit it should fail assert(test_one(DEFAULT_SIZE, rlim_cur + 1, type) != 0); - - // Above 1MB powerpc only allows accesses within 4224 bytes of - // r1 for accesses that aren't stdu - assert(test_one(1 * _MB + page_size - 128, -4224, type) == 0); -#ifdef __powerpc__ - assert(test_one(1 * _MB + page_size - 128, -4225, type) != 0); -#else - assert(test_one(1 * _MB + page_size - 128, -4225, type) == 0); -#endif - - // By consuming 2MB of stack we test the stdu case - assert(test_one(2 * _MB + page_size - 128, -4224, type) == 0); } static int test(void) -- GitLab From 55548a86ebde2b3691b6a84baef1b02933408994 Mon Sep 17 00:00:00 2001 From: Bharata B Rao Date: Mon, 27 Jul 2020 15:27:04 +0530 Subject: [PATCH 1074/1476] powerpc/mm: Limit resize_hpt_for_hotplug() call to hash guests only During memory hotplug and unplug, resize_hpt_for_hotplug() gets called for both hash and radix guests but it should be called only for hash guests. Though the call does nothing in the radix guest case, it is cleaner to push this call into hash specific memory hotplug routines. Reported-by: Nathan Lynch Signed-off-by: Bharata B Rao Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20200727095704.1432916-1-bharata@linux.ibm.com --- arch/powerpc/include/asm/sparsemem.h | 6 ------ arch/powerpc/mm/book3s64/hash_utils.c | 8 +++++++- arch/powerpc/mm/mem.c | 5 ----- 3 files changed, 7 insertions(+), 12 deletions(-) diff --git a/arch/powerpc/include/asm/sparsemem.h b/arch/powerpc/include/asm/sparsemem.h index c89b32443cff3..1e6fa371cc387 100644 --- a/arch/powerpc/include/asm/sparsemem.h +++ b/arch/powerpc/include/asm/sparsemem.h @@ -17,12 +17,6 @@ extern int create_section_mapping(unsigned long start, unsigned long end, int nid, pgprot_t prot); extern int remove_section_mapping(unsigned long start, unsigned long end); -#ifdef CONFIG_PPC_BOOK3S_64 -extern int resize_hpt_for_hotplug(unsigned long new_mem_size); -#else -static inline int resize_hpt_for_hotplug(unsigned long new_mem_size) { return 0; } -#endif - #ifdef CONFIG_NUMA extern int hot_add_scn_to_nid(unsigned long scn_addr); #else diff --git a/arch/powerpc/mm/book3s64/hash_utils.c b/arch/powerpc/mm/book3s64/hash_utils.c index 9fdabea049907..30a4a91d99877 100644 --- a/arch/powerpc/mm/book3s64/hash_utils.c +++ b/arch/powerpc/mm/book3s64/hash_utils.c @@ -785,7 +785,7 @@ static unsigned long __init htab_get_table_size(void) } #ifdef CONFIG_MEMORY_HOTPLUG -int resize_hpt_for_hotplug(unsigned long new_mem_size) +static int resize_hpt_for_hotplug(unsigned long new_mem_size) { unsigned target_hpt_shift; @@ -819,6 +819,8 @@ int hash__create_section_mapping(unsigned long start, unsigned long end, return -1; } + resize_hpt_for_hotplug(memblock_phys_mem_size()); + rc = htab_bolt_mapping(start, end, __pa(start), pgprot_val(prot), mmu_linear_psize, mmu_kernel_ssize); @@ -836,6 +838,10 @@ int hash__remove_section_mapping(unsigned long start, unsigned long end) int rc = htab_remove_mapping(start, end, mmu_linear_psize, mmu_kernel_ssize); WARN_ON(rc < 0); + + if (resize_hpt_for_hotplug(memblock_phys_mem_size()) == -ENOSPC) + pr_warn("Hash collision while resizing HPT\n"); + return rc; } #endif /* CONFIG_MEMORY_HOTPLUG */ diff --git a/arch/powerpc/mm/mem.c b/arch/powerpc/mm/mem.c index c2c11eb8dcfca..9dafc636588f0 100644 --- a/arch/powerpc/mm/mem.c +++ b/arch/powerpc/mm/mem.c @@ -127,8 +127,6 @@ int __ref arch_add_memory(int nid, u64 start, u64 size, unsigned long nr_pages = size >> PAGE_SHIFT; int rc; - resize_hpt_for_hotplug(memblock_phys_mem_size()); - start = (unsigned long)__va(start); rc = create_section_mapping(start, start + size, nid, params->pgprot); @@ -161,9 +159,6 @@ void __ref arch_remove_memory(int nid, u64 start, u64 size, * hit that section of memory */ vm_unmap_aliases(); - - if (resize_hpt_for_hotplug(memblock_phys_mem_size()) == -ENOSPC) - pr_warn("Hash collision while resizing HPT\n"); } #endif -- GitLab From fbb44c9a08ef994109947c5439e649b18ad509ac Mon Sep 17 00:00:00 2001 From: Michael Ellerman Date: Fri, 24 Jul 2020 23:17:20 +1000 Subject: [PATCH 1075/1476] powerpc/configs: Drop old symbols from ppc6xx_defconfig ppc6xx_defconfig refers to quite a few symbols that no longer exist, as reported by scripts/checkkconfigsymbols.py, remove them. Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20200724131728.1643966-1-mpe@ellerman.id.au --- arch/powerpc/configs/ppc6xx_defconfig | 39 --------------------------- 1 file changed, 39 deletions(-) diff --git a/arch/powerpc/configs/ppc6xx_defconfig b/arch/powerpc/configs/ppc6xx_defconfig index feb5d47d8d1e0..5e6f92ba3210e 100644 --- a/arch/powerpc/configs/ppc6xx_defconfig +++ b/arch/powerpc/configs/ppc6xx_defconfig @@ -53,7 +53,6 @@ CONFIG_MPC836x_MDS=y CONFIG_MPC836x_RDK=y CONFIG_MPC837x_MDS=y CONFIG_MPC837x_RDB=y -CONFIG_SBC834x=y CONFIG_ASP834x=y CONFIG_PPC_86xx=y CONFIG_MPC8641_HPCN=y @@ -187,7 +186,6 @@ CONFIG_NETFILTER_XT_MATCH_STRING=m CONFIG_NETFILTER_XT_MATCH_TCPMSS=m CONFIG_NETFILTER_XT_MATCH_TIME=m CONFIG_NETFILTER_XT_MATCH_U32=m -CONFIG_NF_CONNTRACK_IPV4=m CONFIG_IP_NF_IPTABLES=m CONFIG_IP_NF_MATCH_AH=m CONFIG_IP_NF_MATCH_ECN=m @@ -203,7 +201,6 @@ CONFIG_IP_NF_SECURITY=m CONFIG_IP_NF_ARPTABLES=m CONFIG_IP_NF_ARPFILTER=m CONFIG_IP_NF_ARP_MANGLE=m -CONFIG_NF_CONNTRACK_IPV6=m CONFIG_IP6_NF_IPTABLES=m CONFIG_IP6_NF_MATCH_AH=m CONFIG_IP6_NF_MATCH_EUI64=m @@ -241,7 +238,6 @@ CONFIG_BRIDGE_EBT_SNAT=m CONFIG_BRIDGE_EBT_LOG=m CONFIG_BRIDGE_EBT_NFLOG=m CONFIG_IP_DCCP=m -CONFIG_NET_DCCPPROBE=m CONFIG_TIPC=m CONFIG_ATM=m CONFIG_ATM_CLIP=m @@ -251,7 +247,6 @@ CONFIG_BRIDGE=m CONFIG_VLAN_8021Q=m CONFIG_DECNET=m CONFIG_DECNET_ROUTER=y -CONFIG_IPX=m CONFIG_ATALK=m CONFIG_DEV_APPLETALK=m CONFIG_IPDDP=m @@ -297,26 +292,6 @@ CONFIG_NET_ACT_NAT=m CONFIG_NET_ACT_PEDIT=m CONFIG_NET_ACT_SIMP=m CONFIG_NET_ACT_SKBEDIT=m -CONFIG_IRDA=m -CONFIG_IRLAN=m -CONFIG_IRNET=m -CONFIG_IRCOMM=m -CONFIG_IRDA_CACHE_LAST_LSAP=y -CONFIG_IRDA_FAST_RR=y -CONFIG_IRTTY_SIR=m -CONFIG_KINGSUN_DONGLE=m -CONFIG_KSDAZZLE_DONGLE=m -CONFIG_KS959_DONGLE=m -CONFIG_USB_IRDA=m -CONFIG_SIGMATEL_FIR=m -CONFIG_NSC_FIR=m -CONFIG_WINBOND_FIR=m -CONFIG_TOSHIBA_FIR=m -CONFIG_SMC_IRCC_FIR=m -CONFIG_ALI_FIR=m -CONFIG_VLSI_FIR=m -CONFIG_VIA_FIR=m -CONFIG_MCS_FIR=m CONFIG_BT=m CONFIG_BT_RFCOMM=m CONFIG_BT_RFCOMM_TTY=y @@ -332,7 +307,6 @@ CONFIG_BT_HCIBFUSB=m CONFIG_BT_HCIDTL1=m CONFIG_BT_HCIBT3C=m CONFIG_BT_HCIBLUECARD=m -CONFIG_BT_HCIBTUART=m CONFIG_BT_HCIVHCI=m CONFIG_CFG80211=m CONFIG_MAC80211=m @@ -366,7 +340,6 @@ CONFIG_EEPROM_93CX6=m CONFIG_RAID_ATTRS=m CONFIG_BLK_DEV_SD=y CONFIG_CHR_DEV_ST=m -CONFIG_CHR_DEV_OSST=m CONFIG_BLK_DEV_SR=m CONFIG_CHR_DEV_SG=y CONFIG_CHR_DEV_SCH=m @@ -663,7 +636,6 @@ CONFIG_I2C_MPC=m CONFIG_I2C_PCA_PLATFORM=m CONFIG_I2C_SIMTEC=m CONFIG_I2C_PARPORT=m -CONFIG_I2C_PARPORT_LIGHT=m CONFIG_I2C_TINY_USB=m CONFIG_I2C_PCA_ISA=m CONFIG_I2C_STUB=m @@ -676,7 +648,6 @@ CONFIG_W1_SLAVE_THERM=m CONFIG_W1_SLAVE_SMEM=m CONFIG_W1_SLAVE_DS2433=m CONFIG_W1_SLAVE_DS2433_CRC=y -CONFIG_W1_SLAVE_DS2760=m CONFIG_APM_POWER=m CONFIG_BATTERY_PMU=m CONFIG_HWMON=m @@ -1065,15 +1036,6 @@ CONFIG_CIFS_UPCALL=y CONFIG_CIFS_XATTR=y CONFIG_CIFS_POSIX=y CONFIG_CIFS_DFS_UPCALL=y -CONFIG_NCP_FS=m -CONFIG_NCPFS_PACKET_SIGNING=y -CONFIG_NCPFS_IOCTL_LOCKING=y -CONFIG_NCPFS_STRONG=y -CONFIG_NCPFS_NFS_NS=y -CONFIG_NCPFS_OS2_NS=y -CONFIG_NCPFS_SMALLDOS=y -CONFIG_NCPFS_NLS=y -CONFIG_NCPFS_EXTRAS=y CONFIG_CODA_FS=m CONFIG_9P_FS=m CONFIG_NLS_DEFAULT="utf8" @@ -1117,7 +1079,6 @@ CONFIG_NLS_KOI8_U=m CONFIG_DEBUG_INFO=y CONFIG_UNUSED_SYMBOLS=y CONFIG_HEADERS_INSTALL=y -CONFIG_HEADERS_CHECK=y CONFIG_MAGIC_SYSRQ=y CONFIG_DEBUG_KERNEL=y CONFIG_DEBUG_OBJECTS=y -- GitLab From 0fcce25b7743d634cc1ddce83382f51333933f76 Mon Sep 17 00:00:00 2001 From: Michael Ellerman Date: Fri, 24 Jul 2020 23:17:21 +1000 Subject: [PATCH 1076/1476] powerpc/configs: Remove dead symbols Remove references to symbols that no longer exist as reported by scripts/checkkconfigsymbols.py. Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20200724131728.1643966-2-mpe@ellerman.id.au --- arch/powerpc/configs/44x/akebono_defconfig | 1 - arch/powerpc/configs/85xx/xes_mpc85xx_defconfig | 3 --- arch/powerpc/configs/86xx-hw.config | 2 -- arch/powerpc/configs/fsl-emb-nonhw.config | 1 - arch/powerpc/configs/g5_defconfig | 1 - arch/powerpc/configs/linkstation_defconfig | 1 - arch/powerpc/configs/mpc512x_defconfig | 1 - arch/powerpc/configs/mpc83xx_defconfig | 1 - arch/powerpc/configs/mvme5100_defconfig | 1 - arch/powerpc/configs/pasemi_defconfig | 1 - arch/powerpc/configs/pmac32_defconfig | 8 -------- arch/powerpc/configs/powernv_defconfig | 1 - arch/powerpc/configs/ppc40x_defconfig | 3 --- arch/powerpc/configs/ppc64_defconfig | 1 - arch/powerpc/configs/pseries_defconfig | 1 - 15 files changed, 27 deletions(-) diff --git a/arch/powerpc/configs/44x/akebono_defconfig b/arch/powerpc/configs/44x/akebono_defconfig index 60d5fa2c3b930..3894ba8f8ffc5 100644 --- a/arch/powerpc/configs/44x/akebono_defconfig +++ b/arch/powerpc/configs/44x/akebono_defconfig @@ -56,7 +56,6 @@ CONFIG_BLK_DEV_SD=y # CONFIG_NET_VENDOR_DEC is not set # CONFIG_NET_VENDOR_DLINK is not set # CONFIG_NET_VENDOR_EMULEX is not set -# CONFIG_NET_VENDOR_EXAR is not set CONFIG_IBM_EMAC=y # CONFIG_NET_VENDOR_MARVELL is not set # CONFIG_NET_VENDOR_MELLANOX is not set diff --git a/arch/powerpc/configs/85xx/xes_mpc85xx_defconfig b/arch/powerpc/configs/85xx/xes_mpc85xx_defconfig index d50aca6087367..3a6381aa9fdc6 100644 --- a/arch/powerpc/configs/85xx/xes_mpc85xx_defconfig +++ b/arch/powerpc/configs/85xx/xes_mpc85xx_defconfig @@ -51,9 +51,6 @@ CONFIG_NET_IPIP=y CONFIG_IP_MROUTE=y CONFIG_IP_PIMSM_V1=y CONFIG_IP_PIMSM_V2=y -# CONFIG_INET_XFRM_MODE_TRANSPORT is not set -# CONFIG_INET_XFRM_MODE_TUNNEL is not set -# CONFIG_INET_XFRM_MODE_BEET is not set CONFIG_MTD=y CONFIG_MTD_REDBOOT_PARTS=y CONFIG_MTD_CMDLINE_PARTS=y diff --git a/arch/powerpc/configs/86xx-hw.config b/arch/powerpc/configs/86xx-hw.config index 151164cf8cb3f..0cb24b33c88e9 100644 --- a/arch/powerpc/configs/86xx-hw.config +++ b/arch/powerpc/configs/86xx-hw.config @@ -32,8 +32,6 @@ CONFIG_HW_RANDOM=y CONFIG_HZ_1000=y CONFIG_I2C_MPC=y CONFIG_I2C=y -# CONFIG_INET_XFRM_MODE_TRANSPORT is not set -# CONFIG_INET_XFRM_MODE_TUNNEL is not set CONFIG_INPUT_FF_MEMLESS=m # CONFIG_INPUT_KEYBOARD is not set # CONFIG_INPUT_MOUSEDEV is not set diff --git a/arch/powerpc/configs/fsl-emb-nonhw.config b/arch/powerpc/configs/fsl-emb-nonhw.config index 3c7dad19a691a..df37efed0aec3 100644 --- a/arch/powerpc/configs/fsl-emb-nonhw.config +++ b/arch/powerpc/configs/fsl-emb-nonhw.config @@ -56,7 +56,6 @@ CONFIG_IKCONFIG=y CONFIG_INET_AH=y CONFIG_INET_ESP=y CONFIG_INET_IPCOMP=y -# CONFIG_INET_XFRM_MODE_BEET is not set CONFIG_INET=y CONFIG_IP_ADVANCED_ROUTER=y CONFIG_IP_MROUTE=y diff --git a/arch/powerpc/configs/g5_defconfig b/arch/powerpc/configs/g5_defconfig index a68c7f3af10ed..1c674c4c1d860 100644 --- a/arch/powerpc/configs/g5_defconfig +++ b/arch/powerpc/configs/g5_defconfig @@ -51,7 +51,6 @@ CONFIG_NF_CONNTRACK_FTP=m CONFIG_NF_CONNTRACK_IRC=m CONFIG_NF_CONNTRACK_TFTP=m CONFIG_NF_CT_NETLINK=m -CONFIG_NF_CONNTRACK_IPV4=m CONFIG_DEVTMPFS=y CONFIG_DEVTMPFS_MOUNT=y CONFIG_BLK_DEV_LOOP=y diff --git a/arch/powerpc/configs/linkstation_defconfig b/arch/powerpc/configs/linkstation_defconfig index ea59f3d146df1..d4be64f190ffb 100644 --- a/arch/powerpc/configs/linkstation_defconfig +++ b/arch/powerpc/configs/linkstation_defconfig @@ -37,7 +37,6 @@ CONFIG_NF_CONNTRACK_TFTP=m CONFIG_NETFILTER_XT_MATCH_MAC=m CONFIG_NETFILTER_XT_MATCH_PKTTYPE=m CONFIG_NETFILTER_XT_MATCH_STATE=m -CONFIG_NF_CONNTRACK_IPV4=m CONFIG_IP_NF_IPTABLES=m CONFIG_IP_NF_FILTER=m CONFIG_IP_NF_TARGET_REJECT=m diff --git a/arch/powerpc/configs/mpc512x_defconfig b/arch/powerpc/configs/mpc512x_defconfig index e39346b3dc3bb..e75d3f3060c9e 100644 --- a/arch/powerpc/configs/mpc512x_defconfig +++ b/arch/powerpc/configs/mpc512x_defconfig @@ -47,7 +47,6 @@ CONFIG_MTD_UBI=y CONFIG_BLK_DEV_RAM=y CONFIG_BLK_DEV_RAM_COUNT=1 CONFIG_BLK_DEV_RAM_SIZE=8192 -CONFIG_BLK_DEV_RAM_DAX=y CONFIG_EEPROM_AT24=y CONFIG_EEPROM_AT25=y CONFIG_SCSI=y diff --git a/arch/powerpc/configs/mpc83xx_defconfig b/arch/powerpc/configs/mpc83xx_defconfig index be125729635c7..95d43f8a38694 100644 --- a/arch/powerpc/configs/mpc83xx_defconfig +++ b/arch/powerpc/configs/mpc83xx_defconfig @@ -19,7 +19,6 @@ CONFIG_MPC836x_MDS=y CONFIG_MPC836x_RDK=y CONFIG_MPC837x_MDS=y CONFIG_MPC837x_RDB=y -CONFIG_SBC834x=y CONFIG_ASP834x=y CONFIG_QE_GPIO=y CONFIG_MATH_EMULATION=y diff --git a/arch/powerpc/configs/mvme5100_defconfig b/arch/powerpc/configs/mvme5100_defconfig index 3d53d69ed36c2..1fed6be95d538 100644 --- a/arch/powerpc/configs/mvme5100_defconfig +++ b/arch/powerpc/configs/mvme5100_defconfig @@ -45,7 +45,6 @@ CONFIG_NF_CONNTRACK_TFTP=m CONFIG_NETFILTER_XT_MATCH_MAC=m CONFIG_NETFILTER_XT_MATCH_PKTTYPE=m CONFIG_NETFILTER_XT_MATCH_STATE=m -CONFIG_NF_CONNTRACK_IPV4=m CONFIG_IP_NF_IPTABLES=m CONFIG_IP_NF_FILTER=m CONFIG_IP_NF_TARGET_REJECT=m diff --git a/arch/powerpc/configs/pasemi_defconfig b/arch/powerpc/configs/pasemi_defconfig index 08b7f4cef2434..af9af03059e45 100644 --- a/arch/powerpc/configs/pasemi_defconfig +++ b/arch/powerpc/configs/pasemi_defconfig @@ -58,7 +58,6 @@ CONFIG_BLK_DEV_RAM_SIZE=16384 CONFIG_EEPROM_LEGACY=y CONFIG_BLK_DEV_SD=y CONFIG_CHR_DEV_ST=y -CONFIG_CHR_DEV_OSST=y CONFIG_BLK_DEV_SR=y CONFIG_CHR_DEV_SG=y CONFIG_CHR_DEV_SCH=y diff --git a/arch/powerpc/configs/pmac32_defconfig b/arch/powerpc/configs/pmac32_defconfig index 05e325ca3fbd1..665a8d7cded09 100644 --- a/arch/powerpc/configs/pmac32_defconfig +++ b/arch/powerpc/configs/pmac32_defconfig @@ -75,7 +75,6 @@ CONFIG_NETFILTER_XT_MATCH_STRING=m CONFIG_NETFILTER_XT_MATCH_TCPMSS=m CONFIG_NETFILTER_XT_MATCH_TIME=m CONFIG_NETFILTER_XT_MATCH_U32=m -CONFIG_NF_CONNTRACK_IPV4=m CONFIG_IP_NF_IPTABLES=m CONFIG_IP_NF_MATCH_AH=m CONFIG_IP_NF_MATCH_ECN=m @@ -90,13 +89,6 @@ CONFIG_IP_NF_ARPTABLES=m CONFIG_IP_NF_ARPFILTER=m CONFIG_IP_NF_ARP_MANGLE=m CONFIG_IP_DCCP=m -CONFIG_IRDA=m -CONFIG_IRLAN=m -CONFIG_IRNET=m -CONFIG_IRCOMM=m -CONFIG_IRDA_CACHE_LAST_LSAP=y -CONFIG_IRDA_FAST_RR=y -CONFIG_IRTTY_SIR=m CONFIG_BT=m CONFIG_BT_RFCOMM=m CONFIG_BT_RFCOMM_TTY=y diff --git a/arch/powerpc/configs/powernv_defconfig b/arch/powerpc/configs/powernv_defconfig index afc0dd73a1e64..cf30fc24413be 100644 --- a/arch/powerpc/configs/powernv_defconfig +++ b/arch/powerpc/configs/powernv_defconfig @@ -245,7 +245,6 @@ CONFIG_INFINIBAND=m CONFIG_INFINIBAND_USER_MAD=m CONFIG_INFINIBAND_USER_ACCESS=m CONFIG_INFINIBAND_MTHCA=m -CONFIG_INFINIBAND_CXGB3=m CONFIG_INFINIBAND_CXGB4=m CONFIG_MLX4_INFINIBAND=m CONFIG_INFINIBAND_IPOIB=m diff --git a/arch/powerpc/configs/ppc40x_defconfig b/arch/powerpc/configs/ppc40x_defconfig index 25f6c91e843a1..7e48693775f4c 100644 --- a/arch/powerpc/configs/ppc40x_defconfig +++ b/arch/powerpc/configs/ppc40x_defconfig @@ -20,9 +20,6 @@ CONFIG_INET=y CONFIG_IP_PNP=y CONFIG_IP_PNP_DHCP=y CONFIG_IP_PNP_BOOTP=y -# CONFIG_INET_XFRM_MODE_TRANSPORT is not set -# CONFIG_INET_XFRM_MODE_TUNNEL is not set -# CONFIG_INET_XFRM_MODE_BEET is not set CONFIG_CONNECTOR=y CONFIG_MTD=y CONFIG_MTD_CMDLINE_PARTS=y diff --git a/arch/powerpc/configs/ppc64_defconfig b/arch/powerpc/configs/ppc64_defconfig index 8d7e3e98856df..48759656a067b 100644 --- a/arch/powerpc/configs/ppc64_defconfig +++ b/arch/powerpc/configs/ppc64_defconfig @@ -268,7 +268,6 @@ CONFIG_INFINIBAND=m CONFIG_INFINIBAND_USER_MAD=m CONFIG_INFINIBAND_USER_ACCESS=m CONFIG_INFINIBAND_MTHCA=m -CONFIG_INFINIBAND_CXGB3=m CONFIG_INFINIBAND_CXGB4=m CONFIG_MLX4_INFINIBAND=m CONFIG_INFINIBAND_IPOIB=m diff --git a/arch/powerpc/configs/pseries_defconfig b/arch/powerpc/configs/pseries_defconfig index 894e8d85fb487..efd5398928d5a 100644 --- a/arch/powerpc/configs/pseries_defconfig +++ b/arch/powerpc/configs/pseries_defconfig @@ -223,7 +223,6 @@ CONFIG_INFINIBAND=m CONFIG_INFINIBAND_USER_MAD=m CONFIG_INFINIBAND_USER_ACCESS=m CONFIG_INFINIBAND_MTHCA=m -CONFIG_INFINIBAND_CXGB3=m CONFIG_INFINIBAND_CXGB4=m CONFIG_MLX4_INFINIBAND=m CONFIG_INFINIBAND_IPOIB=m -- GitLab From 8cdcde5f76a42d53a50d1fc9e1fbfc9b90102323 Mon Sep 17 00:00:00 2001 From: Michael Ellerman Date: Fri, 24 Jul 2020 23:17:22 +1000 Subject: [PATCH 1077/1476] powerpc/52xx: Fix comment about CONFIG_BDI* There's a comment in lite5200_sleep.S that refers to "CONFIG_BDI*". This confuses scripts/checkkconfigsymbols.py, which thinks it should be able to find CONFIG_BDI. Change the comment to refer to CONFIG_BDI_SWITCH which is presumably roughly what it was referring to. AFAICS there never has been a CONFIG_BDI. Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20200724131728.1643966-3-mpe@ellerman.id.au --- arch/powerpc/platforms/52xx/lite5200_sleep.S | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/powerpc/platforms/52xx/lite5200_sleep.S b/arch/powerpc/platforms/52xx/lite5200_sleep.S index 70083649c9ea1..11475c58ea431 100644 --- a/arch/powerpc/platforms/52xx/lite5200_sleep.S +++ b/arch/powerpc/platforms/52xx/lite5200_sleep.S @@ -56,7 +56,7 @@ lite5200_low_power: /* * save stuff BDI overwrites * 0xf0 (0xe0->0x100 gets overwritten when BDI connected; - * even when CONFIG_BDI* is disabled and MMU XLAT commented; heisenbug?)) + * even when CONFIG_BDI_SWITCH is disabled and MMU XLAT commented; heisenbug?)) * WARNING: self-refresh doesn't seem to work when BDI2000 is connected, * possibly because BDI sets SDRAM registers before wakeup code does */ -- GitLab From 07e571ea59eef518730f983f4203651ea413f2cf Mon Sep 17 00:00:00 2001 From: Michael Ellerman Date: Fri, 24 Jul 2020 23:17:23 +1000 Subject: [PATCH 1078/1476] powerpc/64e: Drop dead BOOK3E_MMU_TLB_STATS code This code was merged 11 years ago in commit 13363ab9b9d0 ("powerpc: Add definitions used by exception handling on 64-bit Book3E") but was never able to be built because CONFIG_BOOK3E_MMU_TLB_STATS never existed. Remove it. Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20200724131728.1643966-4-mpe@ellerman.id.au --- arch/powerpc/include/asm/exception-64e.h | 53 +----------------------- arch/powerpc/mm/nohash/tlb_low_64e.S | 47 ++------------------- 2 files changed, 4 insertions(+), 96 deletions(-) diff --git a/arch/powerpc/include/asm/exception-64e.h b/arch/powerpc/include/asm/exception-64e.h index 72b6657acd2de..40cdcb2fb0572 100644 --- a/arch/powerpc/include/asm/exception-64e.h +++ b/arch/powerpc/include/asm/exception-64e.h @@ -66,14 +66,7 @@ #define EX_TLB_SRR0 (10 * 8) #define EX_TLB_SRR1 (11 * 8) #define EX_TLB_R7 (12 * 8) -#ifdef CONFIG_BOOK3E_MMU_TLB_STATS -#define EX_TLB_R8 (13 * 8) -#define EX_TLB_R9 (14 * 8) -#define EX_TLB_LR (15 * 8) -#define EX_TLB_SIZE (16 * 8) -#else #define EX_TLB_SIZE (13 * 8) -#endif #define START_EXCEPTION(label) \ .globl exc_##label##_book3e; \ @@ -110,8 +103,7 @@ exc_##label##_book3e: std r11,EX_TLB_R12(r12); \ mtspr SPRN_SPRG_TLB_EXFRAME,r14; \ std r15,EX_TLB_SRR1(r12); \ - std r16,EX_TLB_SRR0(r12); \ - TLB_MISS_PROLOG_STATS + std r16,EX_TLB_SRR0(r12); /* And these are the matching epilogs that restores things * @@ -143,7 +135,6 @@ exc_##label##_book3e: mtspr SPRN_SRR0,r15; \ ld r15,EX_TLB_R15(r12); \ mtspr SPRN_SRR1,r16; \ - TLB_MISS_RESTORE_STATS \ ld r16,EX_TLB_R16(r12); \ ld r12,EX_TLB_R12(r12); \ @@ -158,48 +149,6 @@ exc_##label##_book3e: addi r11,r13,PACA_EXTLB; \ TLB_MISS_RESTORE(r11) -#ifdef CONFIG_BOOK3E_MMU_TLB_STATS -#define TLB_MISS_PROLOG_STATS \ - mflr r10; \ - std r8,EX_TLB_R8(r12); \ - std r9,EX_TLB_R9(r12); \ - std r10,EX_TLB_LR(r12); -#define TLB_MISS_RESTORE_STATS \ - ld r16,EX_TLB_LR(r12); \ - ld r9,EX_TLB_R9(r12); \ - ld r8,EX_TLB_R8(r12); \ - mtlr r16; -#define TLB_MISS_STATS_D(name) \ - addi r9,r13,MMSTAT_DSTATS+name; \ - bl tlb_stat_inc; -#define TLB_MISS_STATS_I(name) \ - addi r9,r13,MMSTAT_ISTATS+name; \ - bl tlb_stat_inc; -#define TLB_MISS_STATS_X(name) \ - ld r8,PACA_EXTLB+EX_TLB_ESR(r13); \ - cmpdi cr2,r8,-1; \ - beq cr2,61f; \ - addi r9,r13,MMSTAT_DSTATS+name; \ - b 62f; \ -61: addi r9,r13,MMSTAT_ISTATS+name; \ -62: bl tlb_stat_inc; -#define TLB_MISS_STATS_SAVE_INFO \ - std r14,EX_TLB_ESR(r12); /* save ESR */ -#define TLB_MISS_STATS_SAVE_INFO_BOLTED \ - std r14,PACA_EXTLB+EX_TLB_ESR(r13); /* save ESR */ -#else -#define TLB_MISS_PROLOG_STATS -#define TLB_MISS_RESTORE_STATS -#define TLB_MISS_PROLOG_STATS_BOLTED -#define TLB_MISS_RESTORE_STATS_BOLTED -#define TLB_MISS_STATS_D(name) -#define TLB_MISS_STATS_I(name) -#define TLB_MISS_STATS_X(name) -#define TLB_MISS_STATS_Y(name) -#define TLB_MISS_STATS_SAVE_INFO -#define TLB_MISS_STATS_SAVE_INFO_BOLTED -#endif - #define SET_IVOR(vector_number, vector_offset) \ LOAD_REG_ADDR(r3,interrupt_base_book3e);\ ori r3,r3,vector_offset@l; \ diff --git a/arch/powerpc/mm/nohash/tlb_low_64e.S b/arch/powerpc/mm/nohash/tlb_low_64e.S index d5e2704d00963..bf24451f3e71f 100644 --- a/arch/powerpc/mm/nohash/tlb_low_64e.S +++ b/arch/powerpc/mm/nohash/tlb_low_64e.S @@ -71,7 +71,6 @@ START_BTB_FLUSH_SECTION END_BTB_FLUSH_SECTION std r7,EX_TLB_R7(r12) #endif - TLB_MISS_PROLOG_STATS .endm .macro tlb_epilog_bolted @@ -85,7 +84,6 @@ END_BTB_FLUSH_SECTION mtcr r14 ld r14,EX_TLB_R14(r12) ld r15,EX_TLB_R15(r12) - TLB_MISS_RESTORE_STATS ld r16,EX_TLB_R16(r12) mfspr r12,SPRN_SPRG_GEN_SCRATCH .endm @@ -128,7 +126,6 @@ END_BTB_FLUSH_SECTION ori r10,r10,_PAGE_PRESENT oris r11,r10,_PAGE_ACCESSED@h - TLB_MISS_STATS_SAVE_INFO_BOLTED bne tlb_miss_kernel_bolted tlb_miss_common_bolted: @@ -209,7 +206,6 @@ ALT_MMU_FTR_SECTION_END_IFSET(MMU_FTR_USE_TLBRSRV) tlbwe tlb_miss_done_bolted: - TLB_MISS_STATS_X(MMSTAT_TLB_MISS_NORM_OK) tlb_epilog_bolted rfi @@ -229,11 +225,9 @@ tlb_miss_fault_bolted: andi. r10,r11,_PAGE_EXEC|_PAGE_BAP_SX bne itlb_miss_fault_bolted dtlb_miss_fault_bolted: - TLB_MISS_STATS_D(MMSTAT_TLB_MISS_NORM_FAULT) tlb_epilog_bolted b exc_data_storage_book3e itlb_miss_fault_bolted: - TLB_MISS_STATS_I(MMSTAT_TLB_MISS_NORM_FAULT) tlb_epilog_bolted b exc_instruction_storage_book3e @@ -243,7 +237,6 @@ itlb_miss_fault_bolted: rldicl. r10,r16,64-PGTABLE_EADDR_SIZE,PGTABLE_EADDR_SIZE+4 srdi r15,r16,60 /* get region */ - TLB_MISS_STATS_SAVE_INFO_BOLTED bne- itlb_miss_fault_bolted li r11,_PAGE_PRESENT|_PAGE_EXEC /* Base perm */ @@ -276,7 +269,6 @@ itlb_miss_fault_bolted: srdi. r15,r16,60 /* get region */ ori r16,r16,1 - TLB_MISS_STATS_SAVE_INFO_BOLTED bne tlb_miss_kernel_e6500 /* user/kernel test */ b tlb_miss_common_e6500 @@ -288,7 +280,6 @@ itlb_miss_fault_bolted: srdi. r15,r16,60 /* get region */ rldicr r16,r16,0,62 - TLB_MISS_STATS_SAVE_INFO_BOLTED bne tlb_miss_kernel_e6500 /* user vs kernel check */ /* @@ -460,7 +451,6 @@ END_FTR_SECTION_IFSET(CPU_FTR_SMT) .endm tlb_unlock_e6500 - TLB_MISS_STATS_X(MMSTAT_TLB_MISS_NORM_OK) tlb_epilog_bolted rfi @@ -519,11 +509,9 @@ tlb_miss_fault_e6500: andi. r16,r16,1 bne itlb_miss_fault_e6500 dtlb_miss_fault_e6500: - TLB_MISS_STATS_D(MMSTAT_TLB_MISS_NORM_FAULT) tlb_epilog_bolted b exc_data_storage_book3e itlb_miss_fault_e6500: - TLB_MISS_STATS_I(MMSTAT_TLB_MISS_NORM_FAULT) tlb_epilog_bolted b exc_instruction_storage_book3e #endif /* CONFIG_PPC_FSL_BOOK3E */ @@ -548,7 +536,6 @@ itlb_miss_fault_e6500: mfspr r16,SPRN_DEAR /* get faulting address */ srdi r15,r16,60 /* get region */ cmpldi cr0,r15,0xc /* linear mapping ? */ - TLB_MISS_STATS_SAVE_INFO beq tlb_load_linear /* yes -> go to linear map load */ /* The page tables are mapped virtually linear. At this point, though, @@ -600,7 +587,6 @@ itlb_miss_fault_e6500: /* We got a crappy address, just fault with whatever DEAR and ESR * are here */ - TLB_MISS_STATS_D(MMSTAT_TLB_MISS_NORM_FAULT) TLB_MISS_EPILOG_ERROR b exc_data_storage_book3e @@ -624,7 +610,6 @@ itlb_miss_fault_e6500: */ srdi r15,r16,60 /* get region */ cmpldi cr0,r15,0xc /* linear mapping ? */ - TLB_MISS_STATS_SAVE_INFO beq tlb_load_linear /* yes -> go to linear map load */ /* We do the user/kernel test for the PID here along with the RW test @@ -646,7 +631,6 @@ itlb_miss_fault_e6500: beq+ normal_tlb_miss /* We got a crappy address, just fault */ - TLB_MISS_STATS_I(MMSTAT_TLB_MISS_NORM_FAULT) TLB_MISS_EPILOG_ERROR b exc_instruction_storage_book3e @@ -745,7 +729,6 @@ normal_tlb_miss_done: * level 0 and just going back to userland. They are only needed * if you are going to take an access fault */ - TLB_MISS_STATS_X(MMSTAT_TLB_MISS_NORM_OK) TLB_MISS_EPILOG_SUCCESS rfi @@ -757,11 +740,9 @@ normal_tlb_miss_access_fault: ld r15,EX_TLB_ESR(r12) mtspr SPRN_DEAR,r14 mtspr SPRN_ESR,r15 - TLB_MISS_STATS_D(MMSTAT_TLB_MISS_NORM_FAULT) TLB_MISS_EPILOG_ERROR b exc_data_storage_book3e -1: TLB_MISS_STATS_I(MMSTAT_TLB_MISS_NORM_FAULT) - TLB_MISS_EPILOG_ERROR +1: TLB_MISS_EPILOG_ERROR b exc_instruction_storage_book3e @@ -899,7 +880,6 @@ virt_page_table_tlb_miss_done: 1: END_MMU_FTR_SECTION_IFSET(MMU_FTR_USE_TLBRSRV) /* Return to caller, normal case */ - TLB_MISS_STATS_X(MMSTAT_TLB_MISS_PT_OK); TLB_MISS_EPILOG_SUCCESS rfi @@ -935,18 +915,15 @@ virt_page_table_tlb_miss_fault: beq 1f mtspr SPRN_DEAR,r15 mtspr SPRN_ESR,r16 - TLB_MISS_STATS_D(MMSTAT_TLB_MISS_PT_FAULT); TLB_MISS_EPILOG_ERROR b exc_data_storage_book3e -1: TLB_MISS_STATS_I(MMSTAT_TLB_MISS_PT_FAULT); - TLB_MISS_EPILOG_ERROR +1: TLB_MISS_EPILOG_ERROR b exc_instruction_storage_book3e virt_page_table_tlb_miss_whacko_fault: /* The linear fault will restart everything so ESR and DEAR will * not have been clobbered, let's just fault with what we have */ - TLB_MISS_STATS_X(MMSTAT_TLB_MISS_PT_FAULT); TLB_MISS_EPILOG_ERROR b exc_data_storage_book3e @@ -971,7 +948,6 @@ virt_page_table_tlb_miss_whacko_fault: mfspr r16,SPRN_DEAR /* get faulting address */ srdi r11,r16,60 /* get region */ cmpldi cr0,r11,0xc /* linear mapping ? */ - TLB_MISS_STATS_SAVE_INFO beq tlb_load_linear /* yes -> go to linear map load */ /* We do the user/kernel test for the PID here along with the RW test @@ -991,7 +967,6 @@ virt_page_table_tlb_miss_whacko_fault: /* We got a crappy address, just fault with whatever DEAR and ESR * are here */ - TLB_MISS_STATS_D(MMSTAT_TLB_MISS_NORM_FAULT) TLB_MISS_EPILOG_ERROR b exc_data_storage_book3e @@ -1015,7 +990,6 @@ virt_page_table_tlb_miss_whacko_fault: */ srdi r11,r16,60 /* get region */ cmpldi cr0,r11,0xc /* linear mapping ? */ - TLB_MISS_STATS_SAVE_INFO beq tlb_load_linear /* yes -> go to linear map load */ /* We do the user/kernel test for the PID here along with the RW test @@ -1033,7 +1007,6 @@ virt_page_table_tlb_miss_whacko_fault: beq+ htw_tlb_miss /* We got a crappy address, just fault */ - TLB_MISS_STATS_I(MMSTAT_TLB_MISS_NORM_FAULT) TLB_MISS_EPILOG_ERROR b exc_instruction_storage_book3e @@ -1130,7 +1103,6 @@ htw_tlb_miss_done: * level 0 and just going back to userland. They are only needed * if you are going to take an access fault */ - TLB_MISS_STATS_X(MMSTAT_TLB_MISS_PT_OK) TLB_MISS_EPILOG_SUCCESS rfi @@ -1142,11 +1114,9 @@ htw_tlb_miss_fault: beq 1f mtspr SPRN_DEAR,r16 mtspr SPRN_ESR,r14 - TLB_MISS_STATS_D(MMSTAT_TLB_MISS_PT_FAULT) TLB_MISS_EPILOG_ERROR b exc_data_storage_book3e -1: TLB_MISS_STATS_I(MMSTAT_TLB_MISS_PT_FAULT) - TLB_MISS_EPILOG_ERROR +1: TLB_MISS_EPILOG_ERROR b exc_instruction_storage_book3e /* @@ -1221,7 +1191,6 @@ tlb_load_linear_done: * We do that because we can't resume a fault within a TLB * miss handler, due to MAS and TLB reservation being clobbered. */ - TLB_MISS_STATS_X(MMSTAT_TLB_MISS_LINEAR) TLB_MISS_EPILOG_ERROR rfi @@ -1233,13 +1202,3 @@ tlb_load_linear_fault: b exc_data_storage_book3e 1: TLB_MISS_EPILOG_ERROR_SPECIAL b exc_instruction_storage_book3e - - -#ifdef CONFIG_BOOK3E_MMU_TLB_STATS -.tlb_stat_inc: -1: ldarx r8,0,r9 - addi r8,r8,1 - stdcx. r8,0,r9 - bne- 1b - blr -#endif -- GitLab From df4d4ef22446b3a789a4efd74d34f2ec1e24deb2 Mon Sep 17 00:00:00 2001 From: Michael Ellerman Date: Fri, 24 Jul 2020 23:17:24 +1000 Subject: [PATCH 1079/1476] powerpc/32s: Fix CONFIG_BOOK3S_601 uses We have two uses of CONFIG_BOOK3S_601, which doesn't exist. Fix them to use CONFIG_PPC_BOOK3S_601 which is the correct symbol. Fixes: 12c3f1fd87bf ("powerpc/32s: get rid of CPU_FTR_601 feature") Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20200724131728.1643966-5-mpe@ellerman.id.au --- arch/powerpc/include/asm/ptrace.h | 2 +- arch/powerpc/include/asm/timex.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/powerpc/include/asm/ptrace.h b/arch/powerpc/include/asm/ptrace.h index f194339cef3ba..155a197c0aa10 100644 --- a/arch/powerpc/include/asm/ptrace.h +++ b/arch/powerpc/include/asm/ptrace.h @@ -243,7 +243,7 @@ static inline void set_trap_norestart(struct pt_regs *regs) } #define arch_has_single_step() (1) -#ifndef CONFIG_BOOK3S_601 +#ifndef CONFIG_PPC_BOOK3S_601 #define arch_has_block_step() (true) #else #define arch_has_block_step() (false) diff --git a/arch/powerpc/include/asm/timex.h b/arch/powerpc/include/asm/timex.h index d2d2c4bd84358..6047402b0a4db 100644 --- a/arch/powerpc/include/asm/timex.h +++ b/arch/powerpc/include/asm/timex.h @@ -17,7 +17,7 @@ typedef unsigned long cycles_t; static inline cycles_t get_cycles(void) { - if (IS_ENABLED(CONFIG_BOOK3S_601)) + if (IS_ENABLED(CONFIG_PPC_BOOK3S_601)) return 0; return mftb(); -- GitLab From 69eeff022433b54390a359c629f6457d7d1a8e94 Mon Sep 17 00:00:00 2001 From: Michael Ellerman Date: Fri, 24 Jul 2020 23:17:25 +1000 Subject: [PATCH 1080/1476] powerpc/32s: Remove TAUException wart in traps.c All 32 and 64-bit builds that don't have CONFIG_TAU_INT enabled (all of them), get a definition of TAUException() in traps.c. On 64-bit it's completely useless, and just wastes ~120 bytes of text. On 32-bit it allows the kernel to link because head_32.S calls it unconditionally. Instead follow the example of altivec_assist_exception(), and if CONFIG_TAU_INT is not enabled just point it at unknown_exception using the preprocessor. Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20200724131728.1643966-6-mpe@ellerman.id.au --- arch/powerpc/kernel/head_32.S | 4 ++++ arch/powerpc/kernel/traps.c | 8 -------- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/arch/powerpc/kernel/head_32.S b/arch/powerpc/kernel/head_32.S index bbef6ce8322bc..f3ab94d73936d 100644 --- a/arch/powerpc/kernel/head_32.S +++ b/arch/powerpc/kernel/head_32.S @@ -671,6 +671,10 @@ END_MMU_FTR_SECTION_IFSET(MMU_FTR_NEED_DTLB_SW_LRU) #ifndef CONFIG_ALTIVEC #define altivec_assist_exception unknown_exception +#endif + +#ifndef CONFIG_TAU_INT +#define TAUException unknown_exception #endif EXCEPTION(0x1300, Trap_13, instruction_breakpoint_exception, EXC_XFER_STD) diff --git a/arch/powerpc/kernel/traps.c b/arch/powerpc/kernel/traps.c index 97413a385720c..d1ebe152f2107 100644 --- a/arch/powerpc/kernel/traps.c +++ b/arch/powerpc/kernel/traps.c @@ -2060,14 +2060,6 @@ void DebugException(struct pt_regs *regs, unsigned long debug_status) NOKPROBE_SYMBOL(DebugException); #endif /* CONFIG_PPC_ADV_DEBUG_REGS */ -#if !defined(CONFIG_TAU_INT) -void TAUException(struct pt_regs *regs) -{ - printk("TAU trap at PC: %lx, MSR: %lx, vector=%lx %s\n", - regs->nip, regs->msr, regs->trap, print_tainted()); -} -#endif /* CONFIG_INT_TAU */ - #ifdef CONFIG_ALTIVEC void altivec_assist_exception(struct pt_regs *regs) { -- GitLab From e5eff89657e72a9050d95fde146b54c7dc165981 Mon Sep 17 00:00:00 2001 From: Michael Ellerman Date: Fri, 24 Jul 2020 23:17:26 +1000 Subject: [PATCH 1081/1476] powerpc/boot: Fix CONFIG_PPC_MPC52XX references Commit 866bfc75f40e ("powerpc: conditionally compile platform-specific serial drivers") made some code depend on CONFIG_PPC_MPC52XX, which doesn't exist. Fix it to use CONFIG_PPC_MPC52xx. Fixes: 866bfc75f40e ("powerpc: conditionally compile platform-specific serial drivers") Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20200724131728.1643966-7-mpe@ellerman.id.au --- arch/powerpc/boot/Makefile | 2 +- arch/powerpc/boot/serial.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/powerpc/boot/Makefile b/arch/powerpc/boot/Makefile index 4d43cb59b4a40..44af715433802 100644 --- a/arch/powerpc/boot/Makefile +++ b/arch/powerpc/boot/Makefile @@ -117,7 +117,7 @@ src-wlib-y := string.S crt0.S stdio.c decompress.c main.c \ elf_util.c $(zlib-y) devtree.c stdlib.c \ oflib.c ofconsole.c cuboot.c -src-wlib-$(CONFIG_PPC_MPC52XX) += mpc52xx-psc.c +src-wlib-$(CONFIG_PPC_MPC52xx) += mpc52xx-psc.c src-wlib-$(CONFIG_PPC64_BOOT_WRAPPER) += opal-calls.S opal.c ifndef CONFIG_PPC64_BOOT_WRAPPER src-wlib-y += crtsavres.S diff --git a/arch/powerpc/boot/serial.c b/arch/powerpc/boot/serial.c index 0bfa7e87e5460..9a19e5905485c 100644 --- a/arch/powerpc/boot/serial.c +++ b/arch/powerpc/boot/serial.c @@ -128,7 +128,7 @@ int serial_console_init(void) dt_is_compatible(devp, "fsl,cpm2-smc-uart")) rc = cpm_console_init(devp, &serial_cd); #endif -#ifdef CONFIG_PPC_MPC52XX +#ifdef CONFIG_PPC_MPC52xx else if (dt_is_compatible(devp, "fsl,mpc5200-psc-uart")) rc = mpc5200_psc_console_init(devp, &serial_cd); #endif -- GitLab From 157dad8678ad910ef7579c3f8ba93cc2940b014b Mon Sep 17 00:00:00 2001 From: Michael Ellerman Date: Fri, 24 Jul 2020 23:17:27 +1000 Subject: [PATCH 1082/1476] powerpc/kvm: Use correct CONFIG symbol in comment This comment refers to the non-existent CONFIG_PPC_BOOK3S_XX, which confuses scripts/checkkconfigsymbols.py. Change it to use the correct symbol. Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20200724131728.1643966-8-mpe@ellerman.id.au --- arch/powerpc/kvm/book3s_interrupts.S | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/powerpc/kvm/book3s_interrupts.S b/arch/powerpc/kvm/book3s_interrupts.S index f7ad99d972ce3..607a9b99c3344 100644 --- a/arch/powerpc/kvm/book3s_interrupts.S +++ b/arch/powerpc/kvm/book3s_interrupts.S @@ -26,7 +26,7 @@ #define FUNC(name) name #define GET_SHADOW_VCPU(reg) lwz reg, (THREAD + THREAD_KVM_SVCPU)(r2) -#endif /* CONFIG_PPC_BOOK3S_XX */ +#endif /* CONFIG_PPC_BOOK3S_64 */ #define VCPU_LOAD_NVGPRS(vcpu) \ PPC_LL r14, VCPU_GPR(R14)(vcpu); \ -- GitLab From ee36d867b2fefeb6fb6661b27e62e29c9ca5e7e5 Mon Sep 17 00:00:00 2001 From: Michael Ellerman Date: Fri, 24 Jul 2020 23:17:28 +1000 Subject: [PATCH 1083/1476] powerpc: Drop old comment about CONFIG_POWER There's a comment in time.h referring to CONFIG_POWER, which doesn't exist. That confuses scripts/checkkconfigsymbols.py. Presumably the comment was referring to a CONFIG_POWER vs CONFIG_PPC, in which case for CONFIG_POWER we would #define __USE_RTC to 1. But instead we have CONFIG_PPC_BOOK3S_601, and these days we have IS_ENABLED(). So the comment is no longer relevant, drop it. Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20200724131728.1643966-9-mpe@ellerman.id.au --- arch/powerpc/include/asm/time.h | 1 - 1 file changed, 1 deletion(-) diff --git a/arch/powerpc/include/asm/time.h b/arch/powerpc/include/asm/time.h index b287cfc2dd85f..cb326720a8a15 100644 --- a/arch/powerpc/include/asm/time.h +++ b/arch/powerpc/include/asm/time.h @@ -39,7 +39,6 @@ struct div_result { }; /* Accessor functions for the timebase (RTC on 601) registers. */ -/* If one day CONFIG_POWER is added just define __USE_RTC as 1 */ #define __USE_RTC() (IS_ENABLED(CONFIG_PPC_BOOK3S_601)) #ifdef CONFIG_PPC64 -- GitLab From 81a413259a224f0d1783c41a74f18864d4f3d67e Mon Sep 17 00:00:00 2001 From: Balamuruhan S Date: Mon, 30 Mar 2020 13:29:54 +0530 Subject: [PATCH 1084/1476] powerpc/xmon: Use `dcbf` inplace of `dcbi` instruction for 64bit Book3S Data Cache Block Invalidate (dcbi) instruction implemented back in PowerPC architecture version 2.03. But as per Power Processor Users Manual it is obsolete and not supported by POWER8/POWER9 core. Attempt to use of this illegal instruction results in a hypervisor emulation assistance interrupt. So, ifdef it out the option `i` in xmon for 64bit Book3S. 0:mon> fi cpu 0x0: Vector: 700 (Program Check) at [c000000003be74a0] pc: c000000000102030: cacheflush+0x180/0x1a0 lr: c000000000101f3c: cacheflush+0x8c/0x1a0 sp: c000000003be7730 msr: 8000000000081033 current = 0xc0000000035e5c00 paca = 0xc000000001910000 irqmask: 0x03 irq_happened: 0x01 pid = 1025, comm = bash Linux version 5.6.0-rc5-g5aa19adac (root@ltc-wspoon6) (gcc version 7.4.0 (Ubuntu 7.4.0-1ubuntu1~18.04.1)) #1 SMP Tue Mar 10 04:38:41 CDT 2020 cpu 0x0: Exception 700 (Program Check) in xmon, returning to main loop [c000000003be7c50] c00000000084abb0 __handle_sysrq+0xf0/0x2a0 [c000000003be7d00] c00000000084b3c0 write_sysrq_trigger+0xb0/0xe0 [c000000003be7d30] c0000000004d1edc proc_reg_write+0x8c/0x130 [c000000003be7d60] c00000000040dc7c __vfs_write+0x3c/0x70 [c000000003be7d80] c000000000410e70 vfs_write+0xd0/0x210 [c000000003be7dd0] c00000000041126c ksys_write+0xdc/0x130 [c000000003be7e20] c00000000000b9d0 system_call+0x5c/0x68 --- Exception: c01 (System Call) at 00007fffa345e420 SP (7ffff0b08ab0) is in userspace Signed-off-by: Balamuruhan S Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20200330075954.538773-1-bala24@linux.ibm.com --- arch/powerpc/xmon/xmon.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/powerpc/xmon/xmon.c b/arch/powerpc/xmon/xmon.c index 8fb1f857c11c6..4923347b884be 100644 --- a/arch/powerpc/xmon/xmon.c +++ b/arch/powerpc/xmon/xmon.c @@ -1869,7 +1869,7 @@ static void cacheflush(void) catch_memory_errors = 1; sync(); - if (cmd != 'i') { + if (cmd != 'i' || IS_ENABLED(CONFIG_PPC_BOOK3S_64)) { for (; nflush > 0; --nflush, adrs += L1_CACHE_BYTES) cflush((void *) adrs); } else { -- GitLab From ef26b76d1af61b90eb0dd3da58ad4f97d8e028f8 Mon Sep 17 00:00:00 2001 From: "Aneesh Kumar K.V" Date: Mon, 13 Jul 2020 20:37:48 +0530 Subject: [PATCH 1085/1476] powerpc/hugetlb/cma: Allocate gigantic hugetlb pages using CMA commit: cf11e85fc08c ("mm: hugetlb: optionally allocate gigantic hugepages using cma") added support for allocating gigantic hugepages using CMA. This patch enables the same for powerpc Signed-off-by: Aneesh Kumar K.V Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20200713150749.25245-1-aneesh.kumar@linux.ibm.com --- arch/powerpc/include/asm/hugetlb.h | 7 +++++++ arch/powerpc/kernel/setup-common.c | 3 +++ arch/powerpc/mm/hugetlbpage.c | 18 ++++++++++++++++++ 3 files changed, 28 insertions(+) diff --git a/arch/powerpc/include/asm/hugetlb.h b/arch/powerpc/include/asm/hugetlb.h index 551a9d4d3958e..013165e626186 100644 --- a/arch/powerpc/include/asm/hugetlb.h +++ b/arch/powerpc/include/asm/hugetlb.h @@ -57,6 +57,7 @@ int huge_ptep_set_access_flags(struct vm_area_struct *vma, unsigned long addr, pte_t *ptep, pte_t pte, int dirty); +void gigantic_hugetlb_cma_reserve(void) __init; #include #else /* ! CONFIG_HUGETLB_PAGE */ @@ -71,6 +72,12 @@ static inline pte_t *hugepte_offset(hugepd_t hpd, unsigned long addr, { return NULL; } + + +static inline void __init gigantic_hugetlb_cma_reserve(void) +{ +} + #endif /* CONFIG_HUGETLB_PAGE */ #endif /* _ASM_POWERPC_HUGETLB_H */ diff --git a/arch/powerpc/kernel/setup-common.c b/arch/powerpc/kernel/setup-common.c index 9d3faac53295e..b198b0ff25bcf 100644 --- a/arch/powerpc/kernel/setup-common.c +++ b/arch/powerpc/kernel/setup-common.c @@ -928,6 +928,9 @@ void __init setup_arch(char **cmdline_p) /* Reserve large chunks of memory for use by CMA for KVM. */ kvm_cma_reserve(); + /* Reserve large chunks of memory for us by CMA for hugetlb */ + gigantic_hugetlb_cma_reserve(); + klp_init_thread_info(&init_task); init_mm.start_code = (unsigned long)_stext; diff --git a/arch/powerpc/mm/hugetlbpage.c b/arch/powerpc/mm/hugetlbpage.c index e9bfbccd975d5..26292544630fb 100644 --- a/arch/powerpc/mm/hugetlbpage.c +++ b/arch/powerpc/mm/hugetlbpage.c @@ -684,3 +684,21 @@ void flush_dcache_icache_hugepage(struct page *page) } } } + +void __init gigantic_hugetlb_cma_reserve(void) +{ + unsigned long order = 0; + + if (radix_enabled()) + order = PUD_SHIFT - PAGE_SHIFT; + else if (!firmware_has_feature(FW_FEATURE_LPAR) && mmu_psize_defs[MMU_PAGE_16G].shift) + /* + * For pseries we do use ibm,expected#pages for reserving 16G pages. + */ + order = mmu_psize_to_shift(MMU_PAGE_16G) - PAGE_SHIFT; + + if (order) { + VM_WARN_ON(order < MAX_ORDER); + hugetlb_cma_reserve(order); + } +} -- GitLab From a5a8b258da7861009240b57687dfef47af91b406 Mon Sep 17 00:00:00 2001 From: "Aneesh Kumar K.V" Date: Mon, 13 Jul 2020 20:37:49 +0530 Subject: [PATCH 1086/1476] powerpc/kvm/cma: Improve kernel log during boot Current kernel gives: [ 0.000000] cma: Reserved 26224 MiB at 0x0000007959000000 [ 0.000000] hugetlb_cma: reserve 65536 MiB, up to 16384 MiB per node [ 0.000000] cma: Reserved 16384 MiB at 0x0000001800000000 With the fix [ 0.000000] kvm_cma_reserve: reserving 26214 MiB for global area [ 0.000000] cma: Reserved 26224 MiB at 0x0000007959000000 [ 0.000000] hugetlb_cma: reserve 65536 MiB, up to 16384 MiB per node [ 0.000000] cma: Reserved 16384 MiB at 0x0000001800000000 Signed-off-by: Aneesh Kumar K.V Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20200713150749.25245-2-aneesh.kumar@linux.ibm.com --- arch/powerpc/kvm/book3s_hv_builtin.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/powerpc/kvm/book3s_hv_builtin.c b/arch/powerpc/kvm/book3s_hv_builtin.c index 7cd3cf3d366b7..073617ce83e0d 100644 --- a/arch/powerpc/kvm/book3s_hv_builtin.c +++ b/arch/powerpc/kvm/book3s_hv_builtin.c @@ -113,7 +113,7 @@ void __init kvm_cma_reserve(void) selected_size = (selected_size * kvm_cma_resv_ratio / 100) << PAGE_SHIFT; if (selected_size) { - pr_debug("%s: reserving %ld MiB for global area\n", __func__, + pr_info("%s: reserving %ld MiB for global area\n", __func__, (unsigned long)selected_size / SZ_1M); align_size = HPT_ALIGN_PAGES << PAGE_SHIFT; cma_declare_contiguous(0, selected_size, 0, align_size, -- GitLab From bf6b7661f41615c0815fce0a3f27acb5fc005470 Mon Sep 17 00:00:00 2001 From: "Aneesh Kumar K.V" Date: Mon, 27 Jul 2020 14:29:08 +0530 Subject: [PATCH 1087/1476] powerpc/book3s64/radix: Add kernel command line option to disable radix GTSE This adds a kernel command line option that can be used to disable GTSE support. Disabling GTSE implies kernel will make hcalls to invalidate TLB entries. This was done so that we can do VM migration between configs that enable/disable GTSE support via hypervisor. To migrate a VM from a system that supports GTSE to a system that doesn't, we can boot the guest with radix_hcall_invalidate=on, thereby forcing the guest to use hcalls for TLB invalidates. The check for hcall availability is done in pSeries_setup_arch so that the panic message appears on the console. This should only happen on a hypervisor that doesn't force the guest to hash translation even though it can't handle the radix GTSE=0 request via CAS. With radix_hcall_invalidate=on if the hypervisor doesn't support hcall_rpt_invalidate hcall it should force the LPAR to hash translation. Signed-off-by: Aneesh Kumar K.V Tested-by: Bharata B Rao Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20200727085908.420806-1-aneesh.kumar@linux.ibm.com --- Documentation/admin-guide/kernel-parameters.txt | 4 ++++ arch/powerpc/include/asm/firmware.h | 4 +++- arch/powerpc/kernel/prom_init.c | 13 +++++++++---- arch/powerpc/mm/init_64.c | 1 - arch/powerpc/platforms/pseries/firmware.c | 1 + arch/powerpc/platforms/pseries/setup.c | 5 +++++ 6 files changed, 22 insertions(+), 6 deletions(-) diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt index fb95fad81c79a..3ab61cd0f89c7 100644 --- a/Documentation/admin-guide/kernel-parameters.txt +++ b/Documentation/admin-guide/kernel-parameters.txt @@ -896,6 +896,10 @@ disable_radix [PPC] Disable RADIX MMU mode on POWER9 + radix_hcall_invalidate=on [PPC/PSERIES] + Disable RADIX GTSE feature and use hcall for TLB + invalidate. + disable_tlbie [PPC] Disable TLBIE instruction. Currently does not work with KVM, with HASH MMU, or with coherent accelerators. diff --git a/arch/powerpc/include/asm/firmware.h b/arch/powerpc/include/asm/firmware.h index f67efbaba17f5..0b295bdb201e8 100644 --- a/arch/powerpc/include/asm/firmware.h +++ b/arch/powerpc/include/asm/firmware.h @@ -52,6 +52,7 @@ #define FW_FEATURE_PAPR_SCM ASM_CONST(0x0000002000000000) #define FW_FEATURE_ULTRAVISOR ASM_CONST(0x0000004000000000) #define FW_FEATURE_STUFF_TCE ASM_CONST(0x0000008000000000) +#define FW_FEATURE_RPT_INVALIDATE ASM_CONST(0x0000010000000000) #ifndef __ASSEMBLY__ @@ -71,7 +72,8 @@ enum { FW_FEATURE_TYPE1_AFFINITY | FW_FEATURE_PRRN | FW_FEATURE_HPT_RESIZE | FW_FEATURE_DRMEM_V2 | FW_FEATURE_DRC_INFO | FW_FEATURE_BLOCK_REMOVE | - FW_FEATURE_PAPR_SCM | FW_FEATURE_ULTRAVISOR, + FW_FEATURE_PAPR_SCM | FW_FEATURE_ULTRAVISOR | + FW_FEATURE_RPT_INVALIDATE, FW_FEATURE_PSERIES_ALWAYS = 0, FW_FEATURE_POWERNV_POSSIBLE = FW_FEATURE_OPAL | FW_FEATURE_ULTRAVISOR, FW_FEATURE_POWERNV_ALWAYS = 0, diff --git a/arch/powerpc/kernel/prom_init.c b/arch/powerpc/kernel/prom_init.c index cbc605cfdec06..f279a1f58fa72 100644 --- a/arch/powerpc/kernel/prom_init.c +++ b/arch/powerpc/kernel/prom_init.c @@ -169,6 +169,7 @@ static unsigned long __prombss prom_tce_alloc_end; #ifdef CONFIG_PPC_PSERIES static bool __prombss prom_radix_disable; +static bool __prombss prom_radix_gtse_disable; static bool __prombss prom_xive_disable; #endif @@ -823,6 +824,12 @@ static void __init early_cmdline_parse(void) if (prom_radix_disable) prom_debug("Radix disabled from cmdline\n"); + opt = prom_strstr(prom_cmd_line, "radix_hcall_invalidate=on"); + if (opt) { + prom_radix_gtse_disable = true; + prom_debug("Radix GTSE disabled from cmdline\n"); + } + opt = prom_strstr(prom_cmd_line, "xive=off"); if (opt) { prom_xive_disable = true; @@ -1285,10 +1292,8 @@ static void __init prom_parse_platform_support(u8 index, u8 val, prom_parse_mmu_model(val & OV5_FEAT(OV5_MMU_SUPPORT), support); break; case OV5_INDX(OV5_RADIX_GTSE): /* Radix Extensions */ - if (val & OV5_FEAT(OV5_RADIX_GTSE)) { - prom_debug("Radix - GTSE supported\n"); - support->radix_gtse = true; - } + if (val & OV5_FEAT(OV5_RADIX_GTSE)) + support->radix_gtse = !prom_radix_gtse_disable; break; case OV5_INDX(OV5_XIVE_SUPPORT): /* Interrupt mode */ prom_parse_xive_model(val & OV5_FEAT(OV5_XIVE_SUPPORT), diff --git a/arch/powerpc/mm/init_64.c b/arch/powerpc/mm/init_64.c index 152aa0200cef7..4ae5fc0ceb309 100644 --- a/arch/powerpc/mm/init_64.c +++ b/arch/powerpc/mm/init_64.c @@ -406,7 +406,6 @@ static void __init early_check_vec5(void) } if (!(vec5[OV5_INDX(OV5_RADIX_GTSE)] & OV5_FEAT(OV5_RADIX_GTSE))) { - pr_warn("WARNING: Hypervisor doesn't support RADIX with GTSE\n"); cur_cpu_spec->mmu_features &= ~MMU_FTR_GTSE; } else cur_cpu_spec->mmu_features |= MMU_FTR_GTSE; diff --git a/arch/powerpc/platforms/pseries/firmware.c b/arch/powerpc/platforms/pseries/firmware.c index 3e49cc23a97a3..4c7b7f5a2ebca 100644 --- a/arch/powerpc/platforms/pseries/firmware.c +++ b/arch/powerpc/platforms/pseries/firmware.c @@ -65,6 +65,7 @@ hypertas_fw_features_table[] = { {FW_FEATURE_HPT_RESIZE, "hcall-hpt-resize"}, {FW_FEATURE_BLOCK_REMOVE, "hcall-block-remove"}, {FW_FEATURE_PAPR_SCM, "hcall-scm"}, + {FW_FEATURE_RPT_INVALIDATE, "hcall-rpt-invalidate"}, }; /* Build up the firmware features bitmask using the contents of diff --git a/arch/powerpc/platforms/pseries/setup.c b/arch/powerpc/platforms/pseries/setup.c index e29c9bf0a3b98..b1a8c72a8c121 100644 --- a/arch/powerpc/platforms/pseries/setup.c +++ b/arch/powerpc/platforms/pseries/setup.c @@ -747,6 +747,11 @@ static void __init pSeries_setup_arch(void) smp_init_pseries(); + if (radix_enabled() && !mmu_has_feature(MMU_FTR_GTSE)) + if (!firmware_has_feature(FW_FEATURE_RPT_INVALIDATE)) + panic("BUG: Radix support requires either GTSE or RPT_INVALIDATE\n"); + + /* openpic global configuration register (64-bit format). */ /* openpic Interrupt Source Unit pointer (64-bit format). */ /* python0 facility area (mmio) (64-bit format) REAL address. */ -- GitLab From 5e66a0cb5fbdc76f9ad86a1e8f43256dbad29ef7 Mon Sep 17 00:00:00 2001 From: "Gustavo A. R. Silva" Date: Mon, 27 Jul 2020 17:42:01 -0500 Subject: [PATCH 1088/1476] powerpc: Use fallthrough pseudo-keyword Replace the existing /* fall through */ comments and its variants with the new pseudo-keyword macro fallthrough[1]. Also, remove unnecessary fall-through markings when it is the case. [1] https://www.kernel.org/doc/html/v5.7/process/deprecated.html?highlight=fallthrough#implicit-switch-case-fall-through Signed-off-by: Gustavo A. R. Silva Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20200727224201.GA10133@embeddedor --- arch/powerpc/kernel/align.c | 8 ++++---- arch/powerpc/platforms/powermac/feature.c | 2 +- arch/powerpc/platforms/powernv/opal-async.c | 2 +- arch/powerpc/platforms/pseries/hvcserver.c | 2 +- arch/powerpc/xmon/xmon.c | 2 +- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/arch/powerpc/kernel/align.c b/arch/powerpc/kernel/align.c index 1f1ce8b86d5b1..c7797eb958c73 100644 --- a/arch/powerpc/kernel/align.c +++ b/arch/powerpc/kernel/align.c @@ -178,11 +178,11 @@ static int emulate_spe(struct pt_regs *regs, unsigned int reg, ret |= __get_user_inatomic(temp.v[1], p++); ret |= __get_user_inatomic(temp.v[2], p++); ret |= __get_user_inatomic(temp.v[3], p++); - /* fall through */ + fallthrough; case 4: ret |= __get_user_inatomic(temp.v[4], p++); ret |= __get_user_inatomic(temp.v[5], p++); - /* fall through */ + fallthrough; case 2: ret |= __get_user_inatomic(temp.v[6], p++); ret |= __get_user_inatomic(temp.v[7], p++); @@ -263,11 +263,11 @@ static int emulate_spe(struct pt_regs *regs, unsigned int reg, ret |= __put_user_inatomic(data.v[1], p++); ret |= __put_user_inatomic(data.v[2], p++); ret |= __put_user_inatomic(data.v[3], p++); - /* fall through */ + fallthrough; case 4: ret |= __put_user_inatomic(data.v[4], p++); ret |= __put_user_inatomic(data.v[5], p++); - /* fall through */ + fallthrough; case 2: ret |= __put_user_inatomic(data.v[6], p++); ret |= __put_user_inatomic(data.v[7], p++); diff --git a/arch/powerpc/platforms/powermac/feature.c b/arch/powerpc/platforms/powermac/feature.c index 181caa3f67173..5c77b9a24c0e8 100644 --- a/arch/powerpc/platforms/powermac/feature.c +++ b/arch/powerpc/platforms/powermac/feature.c @@ -1465,7 +1465,7 @@ static long g5_i2s_enable(struct device_node *node, long param, long value) case 2: if (macio->type == macio_shasta) break; - /* fall through */ + fallthrough; default: return -ENODEV; } diff --git a/arch/powerpc/platforms/powernv/opal-async.c b/arch/powerpc/platforms/powernv/opal-async.c index 1656e8965d6bb..c094fdf5825c9 100644 --- a/arch/powerpc/platforms/powernv/opal-async.c +++ b/arch/powerpc/platforms/powernv/opal-async.c @@ -104,7 +104,7 @@ static int __opal_async_release_token(int token) */ case ASYNC_TOKEN_DISPATCHED: opal_async_tokens[token].state = ASYNC_TOKEN_ABANDONED; - /* Fall through */ + fallthrough; default: rc = 1; } diff --git a/arch/powerpc/platforms/pseries/hvcserver.c b/arch/powerpc/platforms/pseries/hvcserver.c index 267139b13530e..96e18d3b2fcf5 100644 --- a/arch/powerpc/platforms/pseries/hvcserver.c +++ b/arch/powerpc/platforms/pseries/hvcserver.c @@ -45,7 +45,7 @@ static int hvcs_convert(long to_convert) case H_LONG_BUSY_ORDER_10_SEC: case H_LONG_BUSY_ORDER_100_SEC: return -EBUSY; - case H_FUNCTION: /* fall through */ + case H_FUNCTION: default: return -EPERM; } diff --git a/arch/powerpc/xmon/xmon.c b/arch/powerpc/xmon/xmon.c index 4923347b884be..df7bca00f5ec9 100644 --- a/arch/powerpc/xmon/xmon.c +++ b/arch/powerpc/xmon/xmon.c @@ -4278,7 +4278,7 @@ static int do_spu_cmd(void) subcmd = inchar(); if (isxdigit(subcmd) || subcmd == '\n') termch = subcmd; - /* fall through */ + fallthrough; case 'f': scanhex(&num); if (num >= XMON_NUM_SPUS || !spu_info[num].spu) { -- GitLab From f2af201002a8bc22500c04cc474ea480bf361351 Mon Sep 17 00:00:00 2001 From: Nicholas Piggin Date: Tue, 3 Mar 2020 11:27:48 +1000 Subject: [PATCH 1089/1476] powerpc/build: vdso linker warning for orphan sections Signed-off-by: Nicholas Piggin Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20200303012748.4190929-1-npiggin@gmail.com --- arch/powerpc/kernel/vdso32/Makefile | 2 +- arch/powerpc/kernel/vdso32/vdso32.lds.S | 1 + arch/powerpc/kernel/vdso64/Makefile | 2 +- arch/powerpc/kernel/vdso64/vdso64.lds.S | 3 ++- 4 files changed, 5 insertions(+), 3 deletions(-) diff --git a/arch/powerpc/kernel/vdso32/Makefile b/arch/powerpc/kernel/vdso32/Makefile index e147bbdc12cdf..87ab1152d5ce5 100644 --- a/arch/powerpc/kernel/vdso32/Makefile +++ b/arch/powerpc/kernel/vdso32/Makefile @@ -50,7 +50,7 @@ $(obj-vdso32): %.o: %.S FORCE # actual build commands quiet_cmd_vdso32ld = VDSO32L $@ - cmd_vdso32ld = $(VDSOCC) $(c_flags) $(CC32FLAGS) -o $@ -Wl,-T$(filter %.lds,$^) $(filter %.o,$^) + cmd_vdso32ld = $(VDSOCC) $(c_flags) $(CC32FLAGS) -o $@ $(call cc-ldoption, -Wl$(comma)--orphan-handling=warn) -Wl,-T$(filter %.lds,$^) $(filter %.o,$^) quiet_cmd_vdso32as = VDSO32A $@ cmd_vdso32as = $(VDSOCC) $(a_flags) $(CC32FLAGS) -c -o $@ $< diff --git a/arch/powerpc/kernel/vdso32/vdso32.lds.S b/arch/powerpc/kernel/vdso32/vdso32.lds.S index 5206c2eb2a1de..4c985467a6680 100644 --- a/arch/powerpc/kernel/vdso32/vdso32.lds.S +++ b/arch/powerpc/kernel/vdso32/vdso32.lds.S @@ -111,6 +111,7 @@ SECTIONS *(.note.GNU-stack) *(.data .data.* .gnu.linkonce.d.* .sdata*) *(.bss .sbss .dynbss .dynsbss) + *(.glink .iplt .plt .rela*) } } diff --git a/arch/powerpc/kernel/vdso64/Makefile b/arch/powerpc/kernel/vdso64/Makefile index 32ebb3522ea19..38c317f251417 100644 --- a/arch/powerpc/kernel/vdso64/Makefile +++ b/arch/powerpc/kernel/vdso64/Makefile @@ -34,7 +34,7 @@ $(obj)/%.so: $(obj)/%.so.dbg FORCE # actual build commands quiet_cmd_vdso64ld = VDSO64L $@ - cmd_vdso64ld = $(CC) $(c_flags) -o $@ -Wl,-T$(filter %.lds,$^) $(filter %.o,$^) + cmd_vdso64ld = $(CC) $(c_flags) -o $@ -Wl,-T$(filter %.lds,$^) $(filter %.o,$^) $(call cc-ldoption, -Wl$(comma)--orphan-handling=warn) # install commands for the unstripped file quiet_cmd_vdso_install = INSTALL $@ diff --git a/arch/powerpc/kernel/vdso64/vdso64.lds.S b/arch/powerpc/kernel/vdso64/vdso64.lds.S index 256fb97202987..4e3a8d4ee6140 100644 --- a/arch/powerpc/kernel/vdso64/vdso64.lds.S +++ b/arch/powerpc/kernel/vdso64/vdso64.lds.S @@ -30,7 +30,7 @@ SECTIONS . = ALIGN(16); .text : { *(.text .stub .text.* .gnu.linkonce.t.* __ftr_alt_*) - *(.sfpr .glink) + *(.sfpr) } :text PROVIDE(__etext = .); PROVIDE(_etext = .); @@ -111,6 +111,7 @@ SECTIONS *(.branch_lt) *(.data .data.* .gnu.linkonce.d.* .sdata*) *(.bss .sbss .dynbss .dynsbss) + *(.glink .iplt .plt .rela*) } } -- GitLab From b6ac59d39a348af29477d7bfdc3ba23526e3f4ea Mon Sep 17 00:00:00 2001 From: Qinglang Miao Date: Tue, 28 Jul 2020 10:28:07 +0800 Subject: [PATCH 1090/1476] powerpc: use for_each_child_of_node() macro Use for_each_child_of_node() macro instead of open coding it. Signed-off-by: Qinglang Miao Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20200728022807.87815-1-miaoqinglang@huawei.com --- arch/powerpc/platforms/pasemi/misc.c | 3 +-- arch/powerpc/platforms/powermac/low_i2c.c | 6 ++---- arch/powerpc/platforms/powermac/pfunc_base.c | 4 ++-- arch/powerpc/platforms/powermac/udbg_scc.c | 2 +- 4 files changed, 6 insertions(+), 9 deletions(-) diff --git a/arch/powerpc/platforms/pasemi/misc.c b/arch/powerpc/platforms/pasemi/misc.c index 1cd4ca14b08d8..1bf65d02d3ba3 100644 --- a/arch/powerpc/platforms/pasemi/misc.c +++ b/arch/powerpc/platforms/pasemi/misc.c @@ -56,8 +56,7 @@ static int __init pasemi_register_i2c_devices(void) if (!adap_node) continue; - node = NULL; - while ((node = of_get_next_child(adap_node, node))) { + for_each_child_of_node(adap_node, node) { struct i2c_board_info info = {}; const u32 *addr; int len; diff --git a/arch/powerpc/platforms/powermac/low_i2c.c b/arch/powerpc/platforms/powermac/low_i2c.c index bf4be4b53b44d..f77a59b5c2e1a 100644 --- a/arch/powerpc/platforms/powermac/low_i2c.c +++ b/arch/powerpc/platforms/powermac/low_i2c.c @@ -629,8 +629,7 @@ static void __init kw_i2c_probe(void) for (i = 0; i < chans; i++) kw_i2c_add(host, np, np, i); } else { - for (child = NULL; - (child = of_get_next_child(np, child)) != NULL;) { + for_each_child_of_node(np, child) { const u32 *reg = of_get_property(child, "reg", NULL); if (reg == NULL) @@ -1193,8 +1192,7 @@ static void pmac_i2c_devscan(void (*callback)(struct device_node *dev, * platform function instance */ list_for_each_entry(bus, &pmac_i2c_busses, link) { - for (np = NULL; - (np = of_get_next_child(bus->busnode, np)) != NULL;) { + for_each_child_of_node(bus->busnode, np) { struct whitelist_ent *p; /* If multibus, check if device is on that bus */ if (bus->flags & pmac_i2c_multibus) diff --git a/arch/powerpc/platforms/powermac/pfunc_base.c b/arch/powerpc/platforms/powermac/pfunc_base.c index 62311e84a4232..f5422506d4b08 100644 --- a/arch/powerpc/platforms/powermac/pfunc_base.c +++ b/arch/powerpc/platforms/powermac/pfunc_base.c @@ -114,7 +114,7 @@ static void macio_gpio_init_one(struct macio_chip *macio) * Ok, got one, we dont need anything special to track them down, so * we just create them all */ - for (gp = NULL; (gp = of_get_next_child(gparent, gp)) != NULL;) { + for_each_child_of_node(gparent, gp) { const u32 *reg = of_get_property(gp, "reg", NULL); unsigned long offset; if (reg == NULL) @@ -133,7 +133,7 @@ static void macio_gpio_init_one(struct macio_chip *macio) macio->of_node); /* And now we run all the init ones */ - for (gp = NULL; (gp = of_get_next_child(gparent, gp)) != NULL;) + for_each_child_of_node(gparent, gp) pmf_do_functions(gp, NULL, 0, PMF_FLAGS_ON_INIT, NULL); /* Note: We do not at this point implement the "at sleep" or "at wake" diff --git a/arch/powerpc/platforms/powermac/udbg_scc.c b/arch/powerpc/platforms/powermac/udbg_scc.c index 6b61a18e8db33..f286bdfe8346e 100644 --- a/arch/powerpc/platforms/powermac/udbg_scc.c +++ b/arch/powerpc/platforms/powermac/udbg_scc.c @@ -80,7 +80,7 @@ void udbg_scc_init(int force_scc) path = of_get_property(of_chosen, "linux,stdout-path", NULL); if (path != NULL) stdout = of_find_node_by_path(path); - for (ch = NULL; (ch = of_get_next_child(escc, ch)) != NULL;) { + for_each_child_of_node(escc, ch) { if (ch == stdout) ch_def = of_node_get(ch); if (of_node_name_eq(ch, "ch-a")) -- GitLab From cf1ae052e073c7ef6cf1a783a6427f7228253bd3 Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Tue, 28 Jul 2020 01:11:12 +0800 Subject: [PATCH 1091/1476] powerpc/powernv/sriov: Remove unused but set variable 'phb' Gcc report warning as follows: arch/powerpc/platforms/powernv/pci-sriov.c:602:25: warning: variable 'phb' set but not used [-Wunused-but-set-variable] 602 | struct pnv_phb *phb; | ^~~ This variable is not used, so this commit removing it. Reported-by: Hulk Robot Signed-off-by: Wei Yongjun Acked-by: Oliver O'Halloran Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20200727171112.2781-1-weiyongjun1@huawei.com --- arch/powerpc/platforms/powernv/pci-sriov.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/arch/powerpc/platforms/powernv/pci-sriov.c b/arch/powerpc/platforms/powernv/pci-sriov.c index 8404d8c3901de..7894745fd4f85 100644 --- a/arch/powerpc/platforms/powernv/pci-sriov.c +++ b/arch/powerpc/platforms/powernv/pci-sriov.c @@ -599,10 +599,8 @@ static int pnv_pci_vf_resource_shift(struct pci_dev *dev, int offset) static void pnv_pci_sriov_disable(struct pci_dev *pdev) { u16 num_vfs, base_pe; - struct pnv_phb *phb; struct pnv_iov_data *iov; - phb = pci_bus_to_pnvhb(pdev->bus); iov = pnv_iov_get(pdev); num_vfs = iov->num_vfs; base_pe = iov->vf_pe_arr[0].pe_number; -- GitLab From 854eb5022be04f81e318765f089f41a57c8e5d83 Mon Sep 17 00:00:00 2001 From: Harish Date: Tue, 9 Jun 2020 13:44:23 +0530 Subject: [PATCH 1092/1476] selftests/powerpc: Fix CPU affinity for child process On systems with large number of cpus, test fails trying to set affinity by calling sched_setaffinity() with smaller size for affinity mask. This patch fixes it by making sure that the size of allocated affinity mask is dependent on the number of CPUs as reported by get_nprocs(). Fixes: 00b7ec5c9cf3 ("selftests/powerpc: Import Anton's context_switch2 benchmark") Reported-by: Shirisha Ganta Signed-off-by: Sandipan Das Signed-off-by: Harish Reviewed-by: Kamalesh Babulal Reviewed-by: Satheesh Rajendran Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20200609081423.529664-1-harish@linux.ibm.com --- .../powerpc/benchmarks/context_switch.c | 21 ++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/tools/testing/selftests/powerpc/benchmarks/context_switch.c b/tools/testing/selftests/powerpc/benchmarks/context_switch.c index a2e8c9da7fa53..d50cc05df4952 100644 --- a/tools/testing/selftests/powerpc/benchmarks/context_switch.c +++ b/tools/testing/selftests/powerpc/benchmarks/context_switch.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -104,8 +105,9 @@ static void start_thread_on(void *(*fn)(void *), void *arg, unsigned long cpu) static void start_process_on(void *(*fn)(void *), void *arg, unsigned long cpu) { - int pid; - cpu_set_t cpuset; + int pid, ncpus; + cpu_set_t *cpuset; + size_t size; pid = fork(); if (pid == -1) { @@ -116,14 +118,23 @@ static void start_process_on(void *(*fn)(void *), void *arg, unsigned long cpu) if (pid) return; - CPU_ZERO(&cpuset); - CPU_SET(cpu, &cpuset); + ncpus = get_nprocs(); + size = CPU_ALLOC_SIZE(ncpus); + cpuset = CPU_ALLOC(ncpus); + if (!cpuset) { + perror("malloc"); + exit(1); + } + CPU_ZERO_S(size, cpuset); + CPU_SET_S(cpu, size, cpuset); - if (sched_setaffinity(0, sizeof(cpuset), &cpuset)) { + if (sched_setaffinity(0, size, cpuset)) { perror("sched_setaffinity"); + CPU_FREE(cpuset); exit(1); } + CPU_FREE(cpuset); fn(arg); exit(0); -- GitLab From 8902c6f96364d1117236948d6c7b9178f428529c Mon Sep 17 00:00:00 2001 From: Balamuruhan S Date: Tue, 28 Jul 2020 18:33:06 +0530 Subject: [PATCH 1093/1476] powerpc/ppc-opcode: Add divde and divdeu opcodes Include instruction opcodes for divde and divdeu as macros. Signed-off-by: Balamuruhan S Reviewed-by: Sandipan Das Acked-by: Naveen N. Rao Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20200728130308.1790982-2-bala24@linux.ibm.com --- arch/powerpc/include/asm/ppc-opcode.h | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/arch/powerpc/include/asm/ppc-opcode.h b/arch/powerpc/include/asm/ppc-opcode.h index 4c0bdafb6a7b9..a6e3700c4566a 100644 --- a/arch/powerpc/include/asm/ppc-opcode.h +++ b/arch/powerpc/include/asm/ppc-opcode.h @@ -466,6 +466,10 @@ #define PPC_RAW_MULI(d, a, i) (0x1c000000 | ___PPC_RT(d) | ___PPC_RA(a) | IMM_L(i)) #define PPC_RAW_DIVWU(d, a, b) (0x7c000396 | ___PPC_RT(d) | ___PPC_RA(a) | ___PPC_RB(b)) #define PPC_RAW_DIVDU(d, a, b) (0x7c000392 | ___PPC_RT(d) | ___PPC_RA(a) | ___PPC_RB(b)) +#define PPC_RAW_DIVDE(t, a, b) (0x7c000352 | ___PPC_RT(t) | ___PPC_RA(a) | ___PPC_RB(b)) +#define PPC_RAW_DIVDE_DOT(t, a, b) (0x7c000352 | ___PPC_RT(t) | ___PPC_RA(a) | ___PPC_RB(b) | 0x1) +#define PPC_RAW_DIVDEU(t, a, b) (0x7c000312 | ___PPC_RT(t) | ___PPC_RA(a) | ___PPC_RB(b)) +#define PPC_RAW_DIVDEU_DOT(t, a, b) (0x7c000312 | ___PPC_RT(t) | ___PPC_RA(a) | ___PPC_RB(b) | 0x1) #define PPC_RAW_AND(d, a, b) (0x7c000038 | ___PPC_RA(d) | ___PPC_RS(a) | ___PPC_RB(b)) #define PPC_RAW_ANDI(d, a, i) (0x70000000 | ___PPC_RA(d) | ___PPC_RS(a) | IMM_L(i)) #define PPC_RAW_AND_DOT(d, a, b) (0x7c000039 | ___PPC_RA(d) | ___PPC_RS(a) | ___PPC_RB(b)) @@ -510,6 +514,8 @@ #define PPC_DARN(t, l) stringify_in_c(.long PPC_RAW_DARN(t, l)) #define PPC_DCBAL(a, b) stringify_in_c(.long PPC_RAW_DCBAL(a, b)) #define PPC_DCBZL(a, b) stringify_in_c(.long PPC_RAW_DCBZL(a, b)) +#define PPC_DIVDE(t, a, b) stringify_in_c(.long PPC_RAW_DIVDE(t, a, b)) +#define PPC_DIVDEU(t, a, b) stringify_in_c(.long PPC_RAW_DIVDEU(t, a, b)) #define PPC_LQARX(t, a, b, eh) stringify_in_c(.long PPC_RAW_LQARX(t, a, b, eh)) #define PPC_LDARX(t, a, b, eh) stringify_in_c(.long PPC_RAW_LDARX(t, a, b, eh)) #define PPC_LWARX(t, a, b, eh) stringify_in_c(.long PPC_RAW_LWARX(t, a, b, eh)) -- GitLab From 151c32bf5ebdd41114267717dc4b53d2632cbd30 Mon Sep 17 00:00:00 2001 From: Balamuruhan S Date: Tue, 28 Jul 2020 18:33:07 +0530 Subject: [PATCH 1094/1476] powerpc/sstep: Add support for divde[.] and divdeu[.] instructions This patch adds emulation support for divde, divdeu instructions, - Divide Doubleword Extended (divde[.]) - Divide Doubleword Extended Unsigned (divdeu[.]) Signed-off-by: Balamuruhan S Reviewed-by: Sandipan Das Acked-by: Naveen N. Rao Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20200728130308.1790982-3-bala24@linux.ibm.com --- arch/powerpc/lib/sstep.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/arch/powerpc/lib/sstep.c b/arch/powerpc/lib/sstep.c index c58ea9e787cb6..caee8cc77e195 100644 --- a/arch/powerpc/lib/sstep.c +++ b/arch/powerpc/lib/sstep.c @@ -1806,7 +1806,18 @@ int analyse_instr(struct instruction_op *op, const struct pt_regs *regs, op->val = (int) regs->gpr[ra] / (int) regs->gpr[rb]; goto arith_done; - +#ifdef __powerpc64__ + case 425: /* divde[.] */ + asm volatile(PPC_DIVDE(%0, %1, %2) : + "=r" (op->val) : "r" (regs->gpr[ra]), + "r" (regs->gpr[rb])); + goto arith_done; + case 393: /* divdeu[.] */ + asm volatile(PPC_DIVDEU(%0, %1, %2) : + "=r" (op->val) : "r" (regs->gpr[ra]), + "r" (regs->gpr[rb])); + goto arith_done; +#endif case 755: /* darn */ if (!cpu_has_feature(CPU_FTR_ARCH_300)) return -1; -- GitLab From b859c95cf4b936b5e8019e7ab68ee2740e609ffd Mon Sep 17 00:00:00 2001 From: Balamuruhan S Date: Tue, 28 Jul 2020 18:33:08 +0530 Subject: [PATCH 1095/1476] powerpc/test_emulate_step: Add testcases for divde[.] and divdeu[.] instructions Add testcases for divde, divde., divdeu, divdeu. emulated instructions to cover few scenarios, - with same dividend and divisor to have undefine RT for divdeu[.] - with divide by zero to have undefine RT for both divde[.] and divdeu[.] - with negative dividend to cover -|divisor| < r <= 0 if the dividend is negative for divde[.] - normal case with proper dividend and divisor for both divde[.] and divdeu[.] Signed-off-by: Balamuruhan S Reviewed-by: Sandipan Das Acked-by: Naveen N. Rao Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20200728130308.1790982-4-bala24@linux.ibm.com --- arch/powerpc/lib/test_emulate_step.c | 156 +++++++++++++++++++++++++++ 1 file changed, 156 insertions(+) diff --git a/arch/powerpc/lib/test_emulate_step.c b/arch/powerpc/lib/test_emulate_step.c index d242e9f72e0c7..0a201b7714772 100644 --- a/arch/powerpc/lib/test_emulate_step.c +++ b/arch/powerpc/lib/test_emulate_step.c @@ -1019,6 +1019,162 @@ static struct compute_test compute_tests[] = { } } }, + { + .mnemonic = "divde", + .subtests = { + { + .descr = "RA = LONG_MIN, RB = LONG_MIN", + .instr = ppc_inst(PPC_RAW_DIVDE(20, 21, 22)), + .regs = { + .gpr[21] = LONG_MIN, + .gpr[22] = LONG_MIN, + } + }, + { + .descr = "RA = 1L, RB = 0", + .instr = ppc_inst(PPC_RAW_DIVDE(20, 21, 22)), + .flags = IGNORE_GPR(20), + .regs = { + .gpr[21] = 1L, + .gpr[22] = 0, + } + }, + { + .descr = "RA = LONG_MIN, RB = LONG_MAX", + .instr = ppc_inst(PPC_RAW_DIVDE(20, 21, 22)), + .regs = { + .gpr[21] = LONG_MIN, + .gpr[22] = LONG_MAX, + } + } + } + }, + { + .mnemonic = "divde.", + .subtests = { + { + .descr = "RA = LONG_MIN, RB = LONG_MIN", + .instr = ppc_inst(PPC_RAW_DIVDE_DOT(20, 21, 22)), + .regs = { + .gpr[21] = LONG_MIN, + .gpr[22] = LONG_MIN, + } + }, + { + .descr = "RA = 1L, RB = 0", + .instr = ppc_inst(PPC_RAW_DIVDE_DOT(20, 21, 22)), + .flags = IGNORE_GPR(20), + .regs = { + .gpr[21] = 1L, + .gpr[22] = 0, + } + }, + { + .descr = "RA = LONG_MIN, RB = LONG_MAX", + .instr = ppc_inst(PPC_RAW_DIVDE_DOT(20, 21, 22)), + .regs = { + .gpr[21] = LONG_MIN, + .gpr[22] = LONG_MAX, + } + } + } + }, + { + .mnemonic = "divdeu", + .subtests = { + { + .descr = "RA = LONG_MIN, RB = LONG_MIN", + .instr = ppc_inst(PPC_RAW_DIVDEU(20, 21, 22)), + .flags = IGNORE_GPR(20), + .regs = { + .gpr[21] = LONG_MIN, + .gpr[22] = LONG_MIN, + } + }, + { + .descr = "RA = 1L, RB = 0", + .instr = ppc_inst(PPC_RAW_DIVDEU(20, 21, 22)), + .flags = IGNORE_GPR(20), + .regs = { + .gpr[21] = 1L, + .gpr[22] = 0, + } + }, + { + .descr = "RA = LONG_MIN, RB = LONG_MAX", + .instr = ppc_inst(PPC_RAW_DIVDEU(20, 21, 22)), + .regs = { + .gpr[21] = LONG_MIN, + .gpr[22] = LONG_MAX, + } + }, + { + .descr = "RA = LONG_MAX - 1, RB = LONG_MAX", + .instr = ppc_inst(PPC_RAW_DIVDEU(20, 21, 22)), + .regs = { + .gpr[21] = LONG_MAX - 1, + .gpr[22] = LONG_MAX, + } + }, + { + .descr = "RA = LONG_MIN + 1, RB = LONG_MIN", + .instr = ppc_inst(PPC_RAW_DIVDEU(20, 21, 22)), + .flags = IGNORE_GPR(20), + .regs = { + .gpr[21] = LONG_MIN + 1, + .gpr[22] = LONG_MIN, + } + } + } + }, + { + .mnemonic = "divdeu.", + .subtests = { + { + .descr = "RA = LONG_MIN, RB = LONG_MIN", + .instr = ppc_inst(PPC_RAW_DIVDEU_DOT(20, 21, 22)), + .flags = IGNORE_GPR(20), + .regs = { + .gpr[21] = LONG_MIN, + .gpr[22] = LONG_MIN, + } + }, + { + .descr = "RA = 1L, RB = 0", + .instr = ppc_inst(PPC_RAW_DIVDEU_DOT(20, 21, 22)), + .flags = IGNORE_GPR(20), + .regs = { + .gpr[21] = 1L, + .gpr[22] = 0, + } + }, + { + .descr = "RA = LONG_MIN, RB = LONG_MAX", + .instr = ppc_inst(PPC_RAW_DIVDEU_DOT(20, 21, 22)), + .regs = { + .gpr[21] = LONG_MIN, + .gpr[22] = LONG_MAX, + } + }, + { + .descr = "RA = LONG_MAX - 1, RB = LONG_MAX", + .instr = ppc_inst(PPC_RAW_DIVDEU_DOT(20, 21, 22)), + .regs = { + .gpr[21] = LONG_MAX - 1, + .gpr[22] = LONG_MAX, + } + }, + { + .descr = "RA = LONG_MIN + 1, RB = LONG_MIN", + .instr = ppc_inst(PPC_RAW_DIVDEU_DOT(20, 21, 22)), + .flags = IGNORE_GPR(20), + .regs = { + .gpr[21] = LONG_MIN + 1, + .gpr[22] = LONG_MIN, + } + } + } + }, { .mnemonic = "paddi", .cpu_feature = CPU_FTR_ARCH_31, -- GitLab From f3054ffd71b5afd44832b2207e6e90267e1cd2d1 Mon Sep 17 00:00:00 2001 From: Thadeu Lima de Souza Cascardo Date: Tue, 28 Jul 2020 12:50:39 -0300 Subject: [PATCH 1096/1476] selftests/powerpc: Return skip code for spectre_v2 When running under older versions of qemu of under newer versions with old machine types, some security features will not be reported to the guest. This will lead the guest OS to consider itself Vulnerable to spectre_v2. So, spectre_v2 test fails in such cases when the host is mitigated and miss predictions cannot be detected as expected by the test. Make it return the skip code instead, for this particular case. We don't want to miss the case when the test fails and the system reports as mitigated or not affected. But it is not a problem to miss failures when the system reports as Vulnerable. Signed-off-by: Thadeu Lima de Souza Cascardo Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20200728155039.401445-1-cascardo@canonical.com --- tools/testing/selftests/powerpc/security/spectre_v2.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/tools/testing/selftests/powerpc/security/spectre_v2.c b/tools/testing/selftests/powerpc/security/spectre_v2.c index 8c6b982af2a89..c8d82b7841023 100644 --- a/tools/testing/selftests/powerpc/security/spectre_v2.c +++ b/tools/testing/selftests/powerpc/security/spectre_v2.c @@ -183,6 +183,16 @@ int spectre_v2_test(void) if (miss_percent > 15) { printf("Branch misses > 15%% unexpected in this configuration!\n"); printf("Possible mis-match between reported & actual mitigation\n"); + /* + * Such a mismatch may be caused by a guest system + * reporting as vulnerable when the host is mitigated. + * Return skip code to avoid detecting this as an error. + * We are not vulnerable and reporting otherwise, so + * missing such a mismatch is safe. + */ + if (state == VULNERABLE) + return 4; + return 1; } break; -- GitLab From c75d42e4c768c403f259f6c7f6217c850cf11be9 Mon Sep 17 00:00:00 2001 From: Alastair D'Silva Date: Wed, 15 Apr 2020 11:23:42 +1000 Subject: [PATCH 1097/1476] ocxl: Remove unnecessary externs Function declarations don't need externs, remove the existing ones so they are consistent with newer code Signed-off-by: Alastair D'Silva Acked-by: Andrew Donnellan Acked-by: Frederic Barrat Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20200415012343.919255-2-alastair@d-silva.org --- arch/powerpc/include/asm/pnv-ocxl.h | 40 ++++++++++++++--------------- include/misc/ocxl.h | 6 ++--- 2 files changed, 22 insertions(+), 24 deletions(-) diff --git a/arch/powerpc/include/asm/pnv-ocxl.h b/arch/powerpc/include/asm/pnv-ocxl.h index 7de82647e7610..ee79d2cd9fb64 100644 --- a/arch/powerpc/include/asm/pnv-ocxl.h +++ b/arch/powerpc/include/asm/pnv-ocxl.h @@ -9,28 +9,26 @@ #define PNV_OCXL_TL_BITS_PER_RATE 4 #define PNV_OCXL_TL_RATE_BUF_SIZE ((PNV_OCXL_TL_MAX_TEMPLATE+1) * PNV_OCXL_TL_BITS_PER_RATE / 8) -extern int pnv_ocxl_get_actag(struct pci_dev *dev, u16 *base, u16 *enabled, - u16 *supported); -extern int pnv_ocxl_get_pasid_count(struct pci_dev *dev, int *count); +int pnv_ocxl_get_actag(struct pci_dev *dev, u16 *base, u16 *enabled, u16 *supported); +int pnv_ocxl_get_pasid_count(struct pci_dev *dev, int *count); -extern int pnv_ocxl_get_tl_cap(struct pci_dev *dev, long *cap, +int pnv_ocxl_get_tl_cap(struct pci_dev *dev, long *cap, char *rate_buf, int rate_buf_size); -extern int pnv_ocxl_set_tl_conf(struct pci_dev *dev, long cap, - uint64_t rate_buf_phys, int rate_buf_size); - -extern int pnv_ocxl_get_xsl_irq(struct pci_dev *dev, int *hwirq); -extern void pnv_ocxl_unmap_xsl_regs(void __iomem *dsisr, void __iomem *dar, - void __iomem *tfc, void __iomem *pe_handle); -extern int pnv_ocxl_map_xsl_regs(struct pci_dev *dev, void __iomem **dsisr, - void __iomem **dar, void __iomem **tfc, - void __iomem **pe_handle); - -extern int pnv_ocxl_spa_setup(struct pci_dev *dev, void *spa_mem, int PE_mask, - void **platform_data); -extern void pnv_ocxl_spa_release(void *platform_data); -extern int pnv_ocxl_spa_remove_pe_from_cache(void *platform_data, int pe_handle); - -extern int pnv_ocxl_alloc_xive_irq(u32 *irq, u64 *trigger_addr); -extern void pnv_ocxl_free_xive_irq(u32 irq); +int pnv_ocxl_set_tl_conf(struct pci_dev *dev, long cap, + uint64_t rate_buf_phys, int rate_buf_size); + +int pnv_ocxl_get_xsl_irq(struct pci_dev *dev, int *hwirq); +void pnv_ocxl_unmap_xsl_regs(void __iomem *dsisr, void __iomem *dar, + void __iomem *tfc, void __iomem *pe_handle); +int pnv_ocxl_map_xsl_regs(struct pci_dev *dev, void __iomem **dsisr, + void __iomem **dar, void __iomem **tfc, + void __iomem **pe_handle); + +int pnv_ocxl_spa_setup(struct pci_dev *dev, void *spa_mem, int PE_mask, void **platform_data); +void pnv_ocxl_spa_release(void *platform_data); +int pnv_ocxl_spa_remove_pe_from_cache(void *platform_data, int pe_handle); + +int pnv_ocxl_alloc_xive_irq(u32 *irq, u64 *trigger_addr); +void pnv_ocxl_free_xive_irq(u32 irq); #endif /* _ASM_PNV_OCXL_H */ diff --git a/include/misc/ocxl.h b/include/misc/ocxl.h index 06dd5839e4386..0a762e3874186 100644 --- a/include/misc/ocxl.h +++ b/include/misc/ocxl.h @@ -173,7 +173,7 @@ int ocxl_context_detach(struct ocxl_context *ctx); * * Returns 0 on success, negative on failure */ -extern int ocxl_afu_irq_alloc(struct ocxl_context *ctx, int *irq_id); +int ocxl_afu_irq_alloc(struct ocxl_context *ctx, int *irq_id); /** * Frees an IRQ associated with an AFU context @@ -182,7 +182,7 @@ extern int ocxl_afu_irq_alloc(struct ocxl_context *ctx, int *irq_id); * * Returns 0 on success, negative on failure */ -extern int ocxl_afu_irq_free(struct ocxl_context *ctx, int irq_id); +int ocxl_afu_irq_free(struct ocxl_context *ctx, int irq_id); /** * Gets the address of the trigger page for an IRQ @@ -193,7 +193,7 @@ extern int ocxl_afu_irq_free(struct ocxl_context *ctx, int irq_id); * * returns the trigger page address, or 0 if the IRQ is not valid */ -extern u64 ocxl_afu_irq_get_addr(struct ocxl_context *ctx, int irq_id); +u64 ocxl_afu_irq_get_addr(struct ocxl_context *ctx, int irq_id); /** * Provide a callback to be called when an IRQ is triggered -- GitLab From 3591538a31af37cf6a2d83f1da99e651a822af8b Mon Sep 17 00:00:00 2001 From: Alastair D'Silva Date: Wed, 15 Apr 2020 11:23:43 +1000 Subject: [PATCH 1098/1476] ocxl: Address kernel doc errors & warnings This patch addresses warnings and errors from the kernel doc scripts for the OpenCAPI driver. It also makes minor tweaks to make the docs more consistent. Signed-off-by: Alastair D'Silva Acked-by: Andrew Donnellan Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20200415012343.919255-3-alastair@d-silva.org --- drivers/misc/ocxl/config.c | 24 ++++---- drivers/misc/ocxl/ocxl_internal.h | 9 +-- include/misc/ocxl.h | 96 ++++++++++++------------------- 3 files changed, 55 insertions(+), 74 deletions(-) diff --git a/drivers/misc/ocxl/config.c b/drivers/misc/ocxl/config.c index 42f7a1298775b..4d490b92d951f 100644 --- a/drivers/misc/ocxl/config.c +++ b/drivers/misc/ocxl/config.c @@ -344,16 +344,16 @@ static int read_afu_info(struct pci_dev *dev, struct ocxl_fn_config *fn, } /** - * Read the template version from the AFU - * dev: the device for the AFU - * fn: the AFU offsets - * len: outputs the template length - * version: outputs the major<<8,minor version + * read_template_version() - Read the template version from the AFU + * @dev: the device for the AFU + * @fn: the AFU offsets + * @len: outputs the template length + * @version: outputs the major<<8,minor version * * Returns 0 on success, negative on failure */ static int read_template_version(struct pci_dev *dev, struct ocxl_fn_config *fn, - u16 *len, u16 *version) + u16 *len, u16 *version) { u32 val32; u8 major, minor; @@ -547,16 +547,16 @@ static int validate_afu(struct pci_dev *dev, struct ocxl_afu_config *afu) } /** - * Populate AFU metadata regarding LPC memory - * dev: the device for the AFU - * fn: the AFU offsets - * afu: the AFU struct to populate the LPC metadata into + * read_afu_lpc_memory_info() - Populate AFU metadata regarding LPC memory + * @dev: the device for the AFU + * @fn: the AFU offsets + * @afu: the AFU struct to populate the LPC metadata into * * Returns 0 on success, negative on failure */ static int read_afu_lpc_memory_info(struct pci_dev *dev, - struct ocxl_fn_config *fn, - struct ocxl_afu_config *afu) + struct ocxl_fn_config *fn, + struct ocxl_afu_config *afu) { int rc; u32 val32; diff --git a/drivers/misc/ocxl/ocxl_internal.h b/drivers/misc/ocxl/ocxl_internal.h index af9a84aeee6f9..0bad0a123af62 100644 --- a/drivers/misc/ocxl/ocxl_internal.h +++ b/drivers/misc/ocxl/ocxl_internal.h @@ -128,11 +128,12 @@ int ocxl_config_check_afu_index(struct pci_dev *dev, struct ocxl_fn_config *fn, int afu_idx); /** - * Update values within a Process Element + * ocxl_link_update_pe() - Update values within a Process Element + * @link_handle: the link handle associated with the process element + * @pasid: the PASID for the AFU context + * @tid: the new thread id for the process element * - * link_handle: the link handle associated with the process element - * pasid: the PASID for the AFU context - * tid: the new thread id for the process element + * Returns 0 on success */ int ocxl_link_update_pe(void *link_handle, int pasid, __u16 tid); diff --git a/include/misc/ocxl.h b/include/misc/ocxl.h index 0a762e3874186..357ef1aadbc00 100644 --- a/include/misc/ocxl.h +++ b/include/misc/ocxl.h @@ -62,8 +62,7 @@ struct ocxl_context; // Device detection & initialisation /** - * Open an OpenCAPI function on an OpenCAPI device - * + * ocxl_function_open() - Open an OpenCAPI function on an OpenCAPI device * @dev: The PCI device that contains the function * * Returns an opaque pointer to the function, or an error pointer (check with IS_ERR) @@ -71,8 +70,7 @@ struct ocxl_context; struct ocxl_fn *ocxl_function_open(struct pci_dev *dev); /** - * Get the list of AFUs associated with a PCI function device - * + * ocxl_function_afu_list() - Get the list of AFUs associated with a PCI function device * Returns a list of struct ocxl_afu * * * @fn: The OpenCAPI function containing the AFUs @@ -80,8 +78,7 @@ struct ocxl_fn *ocxl_function_open(struct pci_dev *dev); struct list_head *ocxl_function_afu_list(struct ocxl_fn *fn); /** - * Fetch an AFU instance from an OpenCAPI function - * + * ocxl_function_fetch_afu() - Fetch an AFU instance from an OpenCAPI function * @fn: The OpenCAPI function to get the AFU from * @afu_idx: The index of the AFU to get * @@ -92,23 +89,20 @@ struct list_head *ocxl_function_afu_list(struct ocxl_fn *fn); struct ocxl_afu *ocxl_function_fetch_afu(struct ocxl_fn *fn, u8 afu_idx); /** - * Take a reference to an AFU - * + * ocxl_afu_get() - Take a reference to an AFU * @afu: The AFU to increment the reference count on */ void ocxl_afu_get(struct ocxl_afu *afu); /** - * Release a reference to an AFU - * + * ocxl_afu_put() - Release a reference to an AFU * @afu: The AFU to decrement the reference count on */ void ocxl_afu_put(struct ocxl_afu *afu); /** - * Get the configuration information for an OpenCAPI function - * + * ocxl_function_config() - Get the configuration information for an OpenCAPI function * @fn: The OpenCAPI function to get the config for * * Returns the function config, or NULL on error @@ -116,8 +110,7 @@ void ocxl_afu_put(struct ocxl_afu *afu); const struct ocxl_fn_config *ocxl_function_config(struct ocxl_fn *fn); /** - * Close an OpenCAPI function - * + * ocxl_function_close() - Close an OpenCAPI function * This will free any AFUs previously retrieved from the function, and * detach and associated contexts. The contexts must by freed by the caller. * @@ -129,8 +122,7 @@ void ocxl_function_close(struct ocxl_fn *fn); // Context allocation /** - * Allocate an OpenCAPI context - * + * ocxl_context_alloc() - Allocate an OpenCAPI context * @context: The OpenCAPI context to allocate, must be freed with ocxl_context_free * @afu: The AFU the context belongs to * @mapping: The mapping to unmap when the context is closed (may be NULL) @@ -139,14 +131,13 @@ int ocxl_context_alloc(struct ocxl_context **context, struct ocxl_afu *afu, struct address_space *mapping); /** - * Free an OpenCAPI context - * + * ocxl_context_free() - Free an OpenCAPI context * @ctx: The OpenCAPI context to free */ void ocxl_context_free(struct ocxl_context *ctx); /** - * Grant access to an MM to an OpenCAPI context + * ocxl_context_attach() - Grant access to an MM to an OpenCAPI context * @ctx: The OpenCAPI context to attach * @amr: The value of the AMR register to restrict access * @mm: The mm to attach to the context @@ -157,7 +148,7 @@ int ocxl_context_attach(struct ocxl_context *ctx, u64 amr, struct mm_struct *mm); /** - * Detach an MM from an OpenCAPI context + * ocxl_context_detach() - Detach an MM from an OpenCAPI context * @ctx: The OpenCAPI context to attach * * Returns 0 on success, negative on failure @@ -167,7 +158,7 @@ int ocxl_context_detach(struct ocxl_context *ctx); // AFU IRQs /** - * Allocate an IRQ associated with an AFU context + * ocxl_afu_irq_alloc() - Allocate an IRQ associated with an AFU context * @ctx: the AFU context * @irq_id: out, the IRQ ID * @@ -176,7 +167,7 @@ int ocxl_context_detach(struct ocxl_context *ctx); int ocxl_afu_irq_alloc(struct ocxl_context *ctx, int *irq_id); /** - * Frees an IRQ associated with an AFU context + * ocxl_afu_irq_free() - Frees an IRQ associated with an AFU context * @ctx: the AFU context * @irq_id: the IRQ ID * @@ -185,7 +176,7 @@ int ocxl_afu_irq_alloc(struct ocxl_context *ctx, int *irq_id); int ocxl_afu_irq_free(struct ocxl_context *ctx, int irq_id); /** - * Gets the address of the trigger page for an IRQ + * ocxl_afu_irq_get_addr() - Gets the address of the trigger page for an IRQ * This can then be provided to an AFU which will write to that * page to trigger the IRQ. * @ctx: The AFU context that the IRQ is associated with @@ -196,7 +187,7 @@ int ocxl_afu_irq_free(struct ocxl_context *ctx, int irq_id); u64 ocxl_afu_irq_get_addr(struct ocxl_context *ctx, int irq_id); /** - * Provide a callback to be called when an IRQ is triggered + * ocxl_irq_set_handler() - Provide a callback to be called when an IRQ is triggered * @ctx: The AFU context that the IRQ is associated with * @irq_id: The IRQ ID * @handler: the callback to be called when the IRQ is triggered @@ -213,8 +204,7 @@ int ocxl_irq_set_handler(struct ocxl_context *ctx, int irq_id, // AFU Metadata /** - * Get a pointer to the config for an AFU - * + * ocxl_afu_config() - Get a pointer to the config for an AFU * @afu: a pointer to the AFU to get the config for * * Returns a pointer to the AFU config @@ -222,27 +212,24 @@ int ocxl_irq_set_handler(struct ocxl_context *ctx, int irq_id, struct ocxl_afu_config *ocxl_afu_config(struct ocxl_afu *afu); /** - * Assign opaque hardware specific information to an OpenCAPI AFU. - * - * @dev: The PCI device associated with the OpenCAPI device + * ocxl_afu_set_private() - Assign opaque hardware specific information to an OpenCAPI AFU. + * @afu: The OpenCAPI AFU * @private: the opaque hardware specific information to assign to the driver */ void ocxl_afu_set_private(struct ocxl_afu *afu, void *private); /** - * Fetch the hardware specific information associated with an external OpenCAPI - * AFU. This may be consumed by an external OpenCAPI driver. - * - * @afu: The AFU + * ocxl_afu_get_private() - Fetch the hardware specific information associated with + * an external OpenCAPI AFU. This may be consumed by an external OpenCAPI driver. + * @afu: The OpenCAPI AFU * * Returns the opaque pointer associated with the device, or NULL if not set */ -void *ocxl_afu_get_private(struct ocxl_afu *dev); +void *ocxl_afu_get_private(struct ocxl_afu *afu); // Global MMIO /** - * Read a 32 bit value from global MMIO - * + * ocxl_global_mmio_read32() - Read a 32 bit value from global MMIO * @afu: The AFU * @offset: The Offset from the start of MMIO * @endian: the endianness that the MMIO data is in @@ -251,11 +238,10 @@ void *ocxl_afu_get_private(struct ocxl_afu *dev); * Returns 0 for success, negative on error */ int ocxl_global_mmio_read32(struct ocxl_afu *afu, size_t offset, - enum ocxl_endian endian, u32 *val); + enum ocxl_endian endian, u32 *val); /** - * Read a 64 bit value from global MMIO - * + * ocxl_global_mmio_read64() - Read a 64 bit value from global MMIO * @afu: The AFU * @offset: The Offset from the start of MMIO * @endian: the endianness that the MMIO data is in @@ -264,11 +250,10 @@ int ocxl_global_mmio_read32(struct ocxl_afu *afu, size_t offset, * Returns 0 for success, negative on error */ int ocxl_global_mmio_read64(struct ocxl_afu *afu, size_t offset, - enum ocxl_endian endian, u64 *val); + enum ocxl_endian endian, u64 *val); /** - * Write a 32 bit value to global MMIO - * + * ocxl_global_mmio_write32() - Write a 32 bit value to global MMIO * @afu: The AFU * @offset: The Offset from the start of MMIO * @endian: the endianness that the MMIO data is in @@ -277,11 +262,10 @@ int ocxl_global_mmio_read64(struct ocxl_afu *afu, size_t offset, * Returns 0 for success, negative on error */ int ocxl_global_mmio_write32(struct ocxl_afu *afu, size_t offset, - enum ocxl_endian endian, u32 val); + enum ocxl_endian endian, u32 val); /** - * Write a 64 bit value to global MMIO - * + * ocxl_global_mmio_write64() - Write a 64 bit value to global MMIO * @afu: The AFU * @offset: The Offset from the start of MMIO * @endian: the endianness that the MMIO data is in @@ -290,11 +274,10 @@ int ocxl_global_mmio_write32(struct ocxl_afu *afu, size_t offset, * Returns 0 for success, negative on error */ int ocxl_global_mmio_write64(struct ocxl_afu *afu, size_t offset, - enum ocxl_endian endian, u64 val); + enum ocxl_endian endian, u64 val); /** - * Set bits in a 32 bit global MMIO register - * + * ocxl_global_mmio_set32() - Set bits in a 32 bit global MMIO register * @afu: The AFU * @offset: The Offset from the start of MMIO * @endian: the endianness that the MMIO data is in @@ -303,11 +286,10 @@ int ocxl_global_mmio_write64(struct ocxl_afu *afu, size_t offset, * Returns 0 for success, negative on error */ int ocxl_global_mmio_set32(struct ocxl_afu *afu, size_t offset, - enum ocxl_endian endian, u32 mask); + enum ocxl_endian endian, u32 mask); /** - * Set bits in a 64 bit global MMIO register - * + * ocxl_global_mmio_set64() - Set bits in a 64 bit global MMIO register * @afu: The AFU * @offset: The Offset from the start of MMIO * @endian: the endianness that the MMIO data is in @@ -316,11 +298,10 @@ int ocxl_global_mmio_set32(struct ocxl_afu *afu, size_t offset, * Returns 0 for success, negative on error */ int ocxl_global_mmio_set64(struct ocxl_afu *afu, size_t offset, - enum ocxl_endian endian, u64 mask); + enum ocxl_endian endian, u64 mask); /** - * Set bits in a 32 bit global MMIO register - * + * ocxl_global_mmio_clear32() - Set bits in a 32 bit global MMIO register * @afu: The AFU * @offset: The Offset from the start of MMIO * @endian: the endianness that the MMIO data is in @@ -329,11 +310,10 @@ int ocxl_global_mmio_set64(struct ocxl_afu *afu, size_t offset, * Returns 0 for success, negative on error */ int ocxl_global_mmio_clear32(struct ocxl_afu *afu, size_t offset, - enum ocxl_endian endian, u32 mask); + enum ocxl_endian endian, u32 mask); /** - * Set bits in a 64 bit global MMIO register - * + * ocxl_global_mmio_clear64() - Set bits in a 64 bit global MMIO register * @afu: The AFU * @offset: The Offset from the start of MMIO * @endian: the endianness that the MMIO data is in @@ -342,7 +322,7 @@ int ocxl_global_mmio_clear32(struct ocxl_afu *afu, size_t offset, * Returns 0 for success, negative on error */ int ocxl_global_mmio_clear64(struct ocxl_afu *afu, size_t offset, - enum ocxl_endian endian, u64 mask); + enum ocxl_endian endian, u64 mask); // Functions left here are for compatibility with the cxlflash driver -- GitLab From d3c61954fc1827df571e235b9a98e10108ef5c3d Mon Sep 17 00:00:00 2001 From: David Lamparter Date: Fri, 21 Sep 2018 01:04:22 +0200 Subject: [PATCH 1099/1476] powerpc/fsl/dts: add missing P4080DS I2C devices This just adds the zl2006 voltage regulators / power monitors and the onboard I2C eeproms. The ICS9FG108 clock chip doesn't seem to have a driver, so it is left in the DTS as a comment. And for good measure, the SPD eeproms are tagged as such. Signed-off-by: David Lamparter Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20180920230422.GK487685@eidolon.nox.tf --- arch/powerpc/boot/dts/fsl/p4080ds.dts | 43 +++++++++++++++++++++++++-- 1 file changed, 41 insertions(+), 2 deletions(-) diff --git a/arch/powerpc/boot/dts/fsl/p4080ds.dts b/arch/powerpc/boot/dts/fsl/p4080ds.dts index 65e20152e22f9..969b32c4f2d50 100644 --- a/arch/powerpc/boot/dts/fsl/p4080ds.dts +++ b/arch/powerpc/boot/dts/fsl/p4080ds.dts @@ -125,11 +125,11 @@ i2c@118100 { eeprom@51 { - compatible = "atmel,24c256"; + compatible = "atmel,spd"; reg = <0x51>; }; eeprom@52 { - compatible = "atmel,24c256"; + compatible = "atmel,spd"; reg = <0x52>; }; rtc@68 { @@ -143,6 +143,45 @@ }; }; + i2c@118000 { + zl2006@21 { + compatible = "zl2006"; + reg = <0x21>; + }; + zl2006@22 { + compatible = "zl2006"; + reg = <0x22>; + }; + zl2006@23 { + compatible = "zl2006"; + reg = <0x23>; + }; + zl2006@24 { + compatible = "zl2006"; + reg = <0x24>; + }; + eeprom@50 { + compatible = "atmel,24c64"; + reg = <0x50>; + }; + eeprom@55 { + compatible = "atmel,24c64"; + reg = <0x55>; + }; + eeprom@56 { + compatible = "atmel,24c64"; + reg = <0x56>; + }; + eeprom@57 { + compatible = "atmel,24c02"; + reg = <0x57>; + }; + }; + + i2c@119100 { + /* 0x6E: ICS9FG108 */ + }; + usb0: usb@210000 { phy_type = "ulpi"; }; -- GitLab From ada68a66b72687e6b74e35c42efd1783e84b01fd Mon Sep 17 00:00:00 2001 From: Mahesh Salgaonkar Date: Tue, 23 Jun 2020 15:57:50 +0530 Subject: [PATCH 1100/1476] powerpc/64s: Move HMI IRQ stat from percpu variable to paca. With the proposed change in percpu bootmem allocator to use page mapping [1], the percpu first chunk memory area can come from vmalloc ranges. This makes the HMI (Hypervisor Maintenance Interrupt) handler crash the kernel whenever percpu variable is accessed in real mode. This patch fixes this issue by moving the HMI IRQ stat inside paca for safe access in realmode. [1] https://lore.kernel.org/linuxppc-dev/20200608070904.387440-1-aneesh.kumar@linux.ibm.com/ Suggested-by: Aneesh Kumar K.V Signed-off-by: Mahesh Salgaonkar Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/159290806973.3642154.5244613424529764050.stgit@jupiter --- arch/powerpc/include/asm/hardirq.h | 1 - arch/powerpc/include/asm/paca.h | 1 + arch/powerpc/kernel/irq.c | 9 ++++++--- arch/powerpc/kernel/mce.c | 2 +- arch/powerpc/kvm/book3s_hv_ras.c | 2 +- 5 files changed, 9 insertions(+), 6 deletions(-) diff --git a/arch/powerpc/include/asm/hardirq.h b/arch/powerpc/include/asm/hardirq.h index f1e9067bd5ac1..f133b5930ae15 100644 --- a/arch/powerpc/include/asm/hardirq.h +++ b/arch/powerpc/include/asm/hardirq.h @@ -13,7 +13,6 @@ typedef struct { unsigned int pmu_irqs; unsigned int mce_exceptions; unsigned int spurious_irqs; - unsigned int hmi_exceptions; unsigned int sreset_irqs; #ifdef CONFIG_PPC_WATCHDOG unsigned int soft_nmi_irqs; diff --git a/arch/powerpc/include/asm/paca.h b/arch/powerpc/include/asm/paca.h index 45a839a7c6cfa..cc07c399306e5 100644 --- a/arch/powerpc/include/asm/paca.h +++ b/arch/powerpc/include/asm/paca.h @@ -225,6 +225,7 @@ struct paca_struct { u16 in_mce; u8 hmi_event_available; /* HMI event is available */ u8 hmi_p9_special_emu; /* HMI P9 special emulation */ + u32 hmi_irqs; /* HMI irq stat */ #endif u8 ftrace_enabled; /* Hard disable ftrace */ diff --git a/arch/powerpc/kernel/irq.c b/arch/powerpc/kernel/irq.c index 05b1cc0e009e4..bf21ebd361900 100644 --- a/arch/powerpc/kernel/irq.c +++ b/arch/powerpc/kernel/irq.c @@ -621,13 +621,14 @@ int arch_show_interrupts(struct seq_file *p, int prec) seq_printf(p, "%10u ", per_cpu(irq_stat, j).mce_exceptions); seq_printf(p, " Machine check exceptions\n"); +#ifdef CONFIG_PPC_BOOK3S_64 if (cpu_has_feature(CPU_FTR_HVMODE)) { seq_printf(p, "%*s: ", prec, "HMI"); for_each_online_cpu(j) - seq_printf(p, "%10u ", - per_cpu(irq_stat, j).hmi_exceptions); + seq_printf(p, "%10u ", paca_ptrs[j]->hmi_irqs); seq_printf(p, " Hypervisor Maintenance Interrupts\n"); } +#endif seq_printf(p, "%*s: ", prec, "NMI"); for_each_online_cpu(j) @@ -665,7 +666,9 @@ u64 arch_irq_stat_cpu(unsigned int cpu) sum += per_cpu(irq_stat, cpu).mce_exceptions; sum += per_cpu(irq_stat, cpu).spurious_irqs; sum += per_cpu(irq_stat, cpu).timer_irqs_others; - sum += per_cpu(irq_stat, cpu).hmi_exceptions; +#ifdef CONFIG_PPC_BOOK3S_64 + sum += paca_ptrs[cpu]->hmi_irqs; +#endif sum += per_cpu(irq_stat, cpu).sreset_irqs; #ifdef CONFIG_PPC_WATCHDOG sum += per_cpu(irq_stat, cpu).soft_nmi_irqs; diff --git a/arch/powerpc/kernel/mce.c b/arch/powerpc/kernel/mce.c index c2fa8ee63720e..ada59f6c4298f 100644 --- a/arch/powerpc/kernel/mce.c +++ b/arch/powerpc/kernel/mce.c @@ -727,7 +727,7 @@ long hmi_exception_realmode(struct pt_regs *regs) { int ret; - __this_cpu_inc(irq_stat.hmi_exceptions); + local_paca->hmi_irqs++; ret = hmi_handle_debugtrig(regs); if (ret >= 0) diff --git a/arch/powerpc/kvm/book3s_hv_ras.c b/arch/powerpc/kvm/book3s_hv_ras.c index 79f7d07ef674c..6028628ea3acf 100644 --- a/arch/powerpc/kvm/book3s_hv_ras.c +++ b/arch/powerpc/kvm/book3s_hv_ras.c @@ -244,7 +244,7 @@ long kvmppc_realmode_hmi_handler(void) { bool resync_req; - __this_cpu_inc(irq_stat.hmi_exceptions); + local_paca->hmi_irqs++; if (hmi_handle_debugtrig(NULL) >= 0) return 1; -- GitLab From fdaa7ce2016ccd09a538b05bace5f4479662ddcb Mon Sep 17 00:00:00 2001 From: Anton Blanchard Date: Wed, 29 Jul 2020 14:08:28 +1000 Subject: [PATCH 1101/1476] powerpc/configs: Add BLK_DEV_NVME to pseries_defconfig I've forgotten to manually enable NVME when building pseries kernels for machines with NVME adapters. Since it's a reasonably common configuration, enable it by default. Signed-off-by: Anton Blanchard Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20200729040828.2312966-1-anton@ozlabs.org --- arch/powerpc/configs/pseries_defconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/powerpc/configs/pseries_defconfig b/arch/powerpc/configs/pseries_defconfig index efd5398928d5a..d5dece981c022 100644 --- a/arch/powerpc/configs/pseries_defconfig +++ b/arch/powerpc/configs/pseries_defconfig @@ -93,6 +93,7 @@ CONFIG_BLK_DEV_NBD=m CONFIG_BLK_DEV_RAM=y CONFIG_BLK_DEV_RAM_SIZE=65536 CONFIG_VIRTIO_BLK=m +CONFIG_BLK_DEV_NVME=y CONFIG_BLK_DEV_SD=y CONFIG_CHR_DEV_ST=m CONFIG_BLK_DEV_SR=y -- GitLab From f891f19736bdf404845f97d8038054be37160ea8 Mon Sep 17 00:00:00 2001 From: Hari Bathini Date: Wed, 29 Jul 2020 17:09:19 +0530 Subject: [PATCH 1102/1476] kexec_file: Allow archs to handle special regions while locating memory hole Some architectures may have special memory regions, within the given memory range, which can't be used for the buffer in a kexec segment. Implement weak arch_kexec_locate_mem_hole() definition which arch code may override, to take care of special regions, while trying to locate a memory hole. Also, add the missing declarations for arch overridable functions and and drop the __weak descriptors in the declarations to avoid non-weak definitions from becoming weak. Signed-off-by: Hari Bathini Tested-by: Pingfan Liu Reviewed-by: Thiago Jung Bauermann Acked-by: Dave Young Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/159602273603.575379.17665852963340380839.stgit@hbathini --- include/linux/kexec.h | 29 ++++++++++++++++++----------- kernel/kexec_file.c | 16 ++++++++++++++-- 2 files changed, 32 insertions(+), 13 deletions(-) diff --git a/include/linux/kexec.h b/include/linux/kexec.h index ea67910ae6b72..9e93bef529680 100644 --- a/include/linux/kexec.h +++ b/include/linux/kexec.h @@ -183,17 +183,24 @@ int kexec_purgatory_get_set_symbol(struct kimage *image, const char *name, bool get_value); void *kexec_purgatory_get_symbol_addr(struct kimage *image, const char *name); -int __weak arch_kexec_kernel_image_probe(struct kimage *image, void *buf, - unsigned long buf_len); -void * __weak arch_kexec_kernel_image_load(struct kimage *image); -int __weak arch_kexec_apply_relocations_add(struct purgatory_info *pi, - Elf_Shdr *section, - const Elf_Shdr *relsec, - const Elf_Shdr *symtab); -int __weak arch_kexec_apply_relocations(struct purgatory_info *pi, - Elf_Shdr *section, - const Elf_Shdr *relsec, - const Elf_Shdr *symtab); +/* Architectures may override the below functions */ +int arch_kexec_kernel_image_probe(struct kimage *image, void *buf, + unsigned long buf_len); +void *arch_kexec_kernel_image_load(struct kimage *image); +int arch_kexec_apply_relocations_add(struct purgatory_info *pi, + Elf_Shdr *section, + const Elf_Shdr *relsec, + const Elf_Shdr *symtab); +int arch_kexec_apply_relocations(struct purgatory_info *pi, + Elf_Shdr *section, + const Elf_Shdr *relsec, + const Elf_Shdr *symtab); +int arch_kimage_file_post_load_cleanup(struct kimage *image); +#ifdef CONFIG_KEXEC_SIG +int arch_kexec_kernel_verify_sig(struct kimage *image, void *buf, + unsigned long buf_len); +#endif +int arch_kexec_locate_mem_hole(struct kexec_buf *kbuf); extern int kexec_add_buffer(struct kexec_buf *kbuf); int kexec_locate_mem_hole(struct kexec_buf *kbuf); diff --git a/kernel/kexec_file.c b/kernel/kexec_file.c index bb05fd52de850..eb42d2efa16af 100644 --- a/kernel/kexec_file.c +++ b/kernel/kexec_file.c @@ -657,6 +657,19 @@ int kexec_locate_mem_hole(struct kexec_buf *kbuf) return ret == 1 ? 0 : -EADDRNOTAVAIL; } +/** + * arch_kexec_locate_mem_hole - Find free memory to place the segments. + * @kbuf: Parameters for the memory search. + * + * On success, kbuf->mem will have the start address of the memory region found. + * + * Return: 0 on success, negative errno on error. + */ +int __weak arch_kexec_locate_mem_hole(struct kexec_buf *kbuf) +{ + return kexec_locate_mem_hole(kbuf); +} + /** * kexec_add_buffer - place a buffer in a kexec segment * @kbuf: Buffer contents and memory parameters. @@ -669,7 +682,6 @@ int kexec_locate_mem_hole(struct kexec_buf *kbuf) */ int kexec_add_buffer(struct kexec_buf *kbuf) { - struct kexec_segment *ksegment; int ret; @@ -697,7 +709,7 @@ int kexec_add_buffer(struct kexec_buf *kbuf) kbuf->buf_align = max(kbuf->buf_align, PAGE_SIZE); /* Walk the RAM ranges and allocate a suitable range for the buffer */ - ret = kexec_locate_mem_hole(kbuf); + ret = arch_kexec_locate_mem_hole(kbuf); if (ret) return ret; -- GitLab From 19031275a5881233b4fc31b7dee68bf0b0758bbc Mon Sep 17 00:00:00 2001 From: Hari Bathini Date: Wed, 29 Jul 2020 17:09:41 +0530 Subject: [PATCH 1103/1476] powerpc/kexec_file: Mark PPC64 specific code Some of the kexec_file_load code isn't PPC64 specific. Move PPC64 specific code from kexec/file_load.c to kexec/file_load_64.c. Also, rename purgatory/trampoline.S to purgatory/trampoline_64.S in the same spirit. No functional changes. Signed-off-by: Hari Bathini Tested-by: Pingfan Liu Reviewed-by: Laurent Dufour Reviewed-by: Thiago Jung Bauermann Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/159602276920.575379.10390965946438306388.stgit@hbathini --- arch/powerpc/include/asm/kexec.h | 9 ++ arch/powerpc/kexec/Makefile | 2 +- arch/powerpc/kexec/elf_64.c | 7 +- arch/powerpc/kexec/file_load.c | 19 +--- arch/powerpc/kexec/file_load_64.c | 87 +++++++++++++++++++ arch/powerpc/purgatory/Makefile | 4 +- .../{trampoline.S => trampoline_64.S} | 0 7 files changed, 105 insertions(+), 23 deletions(-) create mode 100644 arch/powerpc/kexec/file_load_64.c rename arch/powerpc/purgatory/{trampoline.S => trampoline_64.S} (100%) diff --git a/arch/powerpc/include/asm/kexec.h b/arch/powerpc/include/asm/kexec.h index 89f7e3462292c..9b3feef55aaad 100644 --- a/arch/powerpc/include/asm/kexec.h +++ b/arch/powerpc/include/asm/kexec.h @@ -116,6 +116,15 @@ int setup_new_fdt(const struct kimage *image, void *fdt, unsigned long initrd_load_addr, unsigned long initrd_len, const char *cmdline); int delete_fdt_mem_rsv(void *fdt, unsigned long start, unsigned long size); + +#ifdef CONFIG_PPC64 +int setup_purgatory_ppc64(struct kimage *image, const void *slave_code, + const void *fdt, unsigned long kernel_load_addr, + unsigned long fdt_load_addr); +int setup_new_fdt_ppc64(const struct kimage *image, void *fdt, + unsigned long initrd_load_addr, + unsigned long initrd_len, const char *cmdline); +#endif /* CONFIG_PPC64 */ #endif /* CONFIG_KEXEC_FILE */ #else /* !CONFIG_KEXEC_CORE */ diff --git a/arch/powerpc/kexec/Makefile b/arch/powerpc/kexec/Makefile index 86380c69f5ce2..67c3553294571 100644 --- a/arch/powerpc/kexec/Makefile +++ b/arch/powerpc/kexec/Makefile @@ -7,7 +7,7 @@ obj-y += core.o crash.o core_$(BITS).o obj-$(CONFIG_PPC32) += relocate_32.o -obj-$(CONFIG_KEXEC_FILE) += file_load.o elf_$(BITS).o +obj-$(CONFIG_KEXEC_FILE) += file_load.o file_load_$(BITS).o elf_$(BITS).o ifdef CONFIG_HAVE_IMA_KEXEC ifdef CONFIG_IMA diff --git a/arch/powerpc/kexec/elf_64.c b/arch/powerpc/kexec/elf_64.c index 3072fd6dbe94a..23ad04ccaf8e8 100644 --- a/arch/powerpc/kexec/elf_64.c +++ b/arch/powerpc/kexec/elf_64.c @@ -88,7 +88,8 @@ static void *elf64_load(struct kimage *image, char *kernel_buf, goto out; } - ret = setup_new_fdt(image, fdt, initrd_load_addr, initrd_len, cmdline); + ret = setup_new_fdt_ppc64(image, fdt, initrd_load_addr, + initrd_len, cmdline); if (ret) goto out; @@ -107,8 +108,8 @@ static void *elf64_load(struct kimage *image, char *kernel_buf, pr_debug("Loaded device tree at 0x%lx\n", fdt_load_addr); slave_code = elf_info.buffer + elf_info.proghdrs[0].p_offset; - ret = setup_purgatory(image, slave_code, fdt, kernel_load_addr, - fdt_load_addr); + ret = setup_purgatory_ppc64(image, slave_code, fdt, kernel_load_addr, + fdt_load_addr); if (ret) pr_err("Error setting up the purgatory.\n"); diff --git a/arch/powerpc/kexec/file_load.c b/arch/powerpc/kexec/file_load.c index 143c91724617e..38439aba27d7e 100644 --- a/arch/powerpc/kexec/file_load.c +++ b/arch/powerpc/kexec/file_load.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only /* - * ppc64 code to implement the kexec_file_load syscall + * powerpc code to implement the kexec_file_load syscall * * Copyright (C) 2004 Adam Litke (agl@us.ibm.com) * Copyright (C) 2004 IBM Corp. @@ -20,22 +20,7 @@ #include #include -#define SLAVE_CODE_SIZE 256 - -const struct kexec_file_ops * const kexec_file_loaders[] = { - &kexec_elf64_ops, - NULL -}; - -int arch_kexec_kernel_image_probe(struct kimage *image, void *buf, - unsigned long buf_len) -{ - /* We don't support crash kernels yet. */ - if (image->type == KEXEC_TYPE_CRASH) - return -EOPNOTSUPP; - - return kexec_image_probe_default(image, buf, buf_len); -} +#define SLAVE_CODE_SIZE 256 /* First 0x100 bytes */ /** * setup_purgatory - initialize the purgatory's global variables diff --git a/arch/powerpc/kexec/file_load_64.c b/arch/powerpc/kexec/file_load_64.c new file mode 100644 index 0000000000000..3e9ac5f216b09 --- /dev/null +++ b/arch/powerpc/kexec/file_load_64.c @@ -0,0 +1,87 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * ppc64 code to implement the kexec_file_load syscall + * + * Copyright (C) 2004 Adam Litke (agl@us.ibm.com) + * Copyright (C) 2004 IBM Corp. + * Copyright (C) 2004,2005 Milton D Miller II, IBM Corporation + * Copyright (C) 2005 R Sharada (sharada@in.ibm.com) + * Copyright (C) 2006 Mohan Kumar M (mohan@in.ibm.com) + * Copyright (C) 2020 IBM Corporation + * + * Based on kexec-tools' kexec-ppc64.c, kexec-elf-rel-ppc64.c, fs2dt.c. + * Heavily modified for the kernel by + * Hari Bathini, IBM Corporation. + */ + +#include +#include +#include + +const struct kexec_file_ops * const kexec_file_loaders[] = { + &kexec_elf64_ops, + NULL +}; + +/** + * setup_purgatory_ppc64 - initialize PPC64 specific purgatory's global + * variables and call setup_purgatory() to initialize + * common global variable. + * @image: kexec image. + * @slave_code: Slave code for the purgatory. + * @fdt: Flattened device tree for the next kernel. + * @kernel_load_addr: Address where the kernel is loaded. + * @fdt_load_addr: Address where the flattened device tree is loaded. + * + * Returns 0 on success, negative errno on error. + */ +int setup_purgatory_ppc64(struct kimage *image, const void *slave_code, + const void *fdt, unsigned long kernel_load_addr, + unsigned long fdt_load_addr) +{ + int ret; + + ret = setup_purgatory(image, slave_code, fdt, kernel_load_addr, + fdt_load_addr); + if (ret) + pr_err("Failed to setup purgatory symbols"); + return ret; +} + +/** + * setup_new_fdt_ppc64 - Update the flattend device-tree of the kernel + * being loaded. + * @image: kexec image being loaded. + * @fdt: Flattened device tree for the next kernel. + * @initrd_load_addr: Address where the next initrd will be loaded. + * @initrd_len: Size of the next initrd, or 0 if there will be none. + * @cmdline: Command line for the next kernel, or NULL if there will + * be none. + * + * Returns 0 on success, negative errno on error. + */ +int setup_new_fdt_ppc64(const struct kimage *image, void *fdt, + unsigned long initrd_load_addr, + unsigned long initrd_len, const char *cmdline) +{ + return setup_new_fdt(image, fdt, initrd_load_addr, initrd_len, cmdline); +} + +/** + * arch_kexec_kernel_image_probe - Does additional handling needed to setup + * kexec segments. + * @image: kexec image being loaded. + * @buf: Buffer pointing to elf data. + * @buf_len: Length of the buffer. + * + * Returns 0 on success, negative errno on error. + */ +int arch_kexec_kernel_image_probe(struct kimage *image, void *buf, + unsigned long buf_len) +{ + /* We don't support crash kernels yet. */ + if (image->type == KEXEC_TYPE_CRASH) + return -EOPNOTSUPP; + + return kexec_image_probe_default(image, buf, buf_len); +} diff --git a/arch/powerpc/purgatory/Makefile b/arch/powerpc/purgatory/Makefile index 7c6d8b14f4403..348f595810523 100644 --- a/arch/powerpc/purgatory/Makefile +++ b/arch/powerpc/purgatory/Makefile @@ -2,11 +2,11 @@ KASAN_SANITIZE := n -targets += trampoline.o purgatory.ro kexec-purgatory.c +targets += trampoline_$(BITS).o purgatory.ro kexec-purgatory.c LDFLAGS_purgatory.ro := -e purgatory_start -r --no-undefined -$(obj)/purgatory.ro: $(obj)/trampoline.o FORCE +$(obj)/purgatory.ro: $(obj)/trampoline_$(BITS).o FORCE $(call if_changed,ld) quiet_cmd_bin2c = BIN2C $@ diff --git a/arch/powerpc/purgatory/trampoline.S b/arch/powerpc/purgatory/trampoline_64.S similarity index 100% rename from arch/powerpc/purgatory/trampoline.S rename to arch/powerpc/purgatory/trampoline_64.S -- GitLab From 180adfc532a83c1d74146449f7385f767d4b8059 Mon Sep 17 00:00:00 2001 From: Hari Bathini Date: Wed, 29 Jul 2020 17:10:00 +0530 Subject: [PATCH 1104/1476] powerpc/kexec_file: Add helper functions for getting memory ranges In kexec case, the kernel to be loaded uses the same memory layout as the running kernel. So, passing on the DT of the running kernel would be good enough. But in case of kdump, different memory ranges are needed to manage loading the kdump kernel, booting into it and exporting the elfcore of the crashing kernel. The ranges are exclude memory ranges, usable memory ranges, reserved memory ranges and crash memory ranges. Exclude memory ranges specify the list of memory ranges to avoid while loading kdump segments. Usable memory ranges list the memory ranges that could be used for booting kdump kernel. Reserved memory ranges list the memory regions for the loading kernel's reserve map. Crash memory ranges list the memory ranges to be exported as the crashing kernel's elfcore. Add helper functions for setting up the above mentioned memory ranges. This helpers facilitate in understanding the subsequent changes better and make it easy to setup the different memory ranges listed above, as and when appropriate. Signed-off-by: Hari Bathini Tested-by: Pingfan Liu Reviewed-by: Thiago Jung Bauermann Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/159602279194.575379.8526552316948643550.stgit@hbathini --- arch/powerpc/include/asm/kexec_ranges.h | 11 ++ arch/powerpc/kexec/Makefile | 2 +- arch/powerpc/kexec/ranges.c | 235 ++++++++++++++++++++++++ 3 files changed, 247 insertions(+), 1 deletion(-) create mode 100644 arch/powerpc/include/asm/kexec_ranges.h create mode 100644 arch/powerpc/kexec/ranges.c diff --git a/arch/powerpc/include/asm/kexec_ranges.h b/arch/powerpc/include/asm/kexec_ranges.h new file mode 100644 index 0000000000000..35ae31a7a4de0 --- /dev/null +++ b/arch/powerpc/include/asm/kexec_ranges.h @@ -0,0 +1,11 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +#ifndef _ASM_POWERPC_KEXEC_RANGES_H +#define _ASM_POWERPC_KEXEC_RANGES_H + +#define MEM_RANGE_CHUNK_SZ 2048 /* Memory ranges size chunk */ + +void sort_memory_ranges(struct crash_mem *mrngs, bool merge); +struct crash_mem *realloc_mem_ranges(struct crash_mem **mem_ranges); +int add_mem_range(struct crash_mem **mem_ranges, u64 base, u64 size); + +#endif /* _ASM_POWERPC_KEXEC_RANGES_H */ diff --git a/arch/powerpc/kexec/Makefile b/arch/powerpc/kexec/Makefile index 67c3553294571..4aff6846c7726 100644 --- a/arch/powerpc/kexec/Makefile +++ b/arch/powerpc/kexec/Makefile @@ -7,7 +7,7 @@ obj-y += core.o crash.o core_$(BITS).o obj-$(CONFIG_PPC32) += relocate_32.o -obj-$(CONFIG_KEXEC_FILE) += file_load.o file_load_$(BITS).o elf_$(BITS).o +obj-$(CONFIG_KEXEC_FILE) += file_load.o ranges.o file_load_$(BITS).o elf_$(BITS).o ifdef CONFIG_HAVE_IMA_KEXEC ifdef CONFIG_IMA diff --git a/arch/powerpc/kexec/ranges.c b/arch/powerpc/kexec/ranges.c new file mode 100644 index 0000000000000..dc3ce036f4169 --- /dev/null +++ b/arch/powerpc/kexec/ranges.c @@ -0,0 +1,235 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * powerpc code to implement the kexec_file_load syscall + * + * Copyright (C) 2004 Adam Litke (agl@us.ibm.com) + * Copyright (C) 2004 IBM Corp. + * Copyright (C) 2004,2005 Milton D Miller II, IBM Corporation + * Copyright (C) 2005 R Sharada (sharada@in.ibm.com) + * Copyright (C) 2006 Mohan Kumar M (mohan@in.ibm.com) + * Copyright (C) 2020 IBM Corporation + * + * Based on kexec-tools' kexec-ppc64.c, fs2dt.c. + * Heavily modified for the kernel by + * Hari Bathini, IBM Corporation. + */ + +#define pr_fmt(fmt) "kexec ranges: " fmt + +#include +#include +#include +#include +#include +#include + +/** + * get_max_nr_ranges - Get the max no. of ranges crash_mem structure + * could hold, given the size allocated for it. + * @size: Allocation size of crash_mem structure. + * + * Returns the maximum no. of ranges. + */ +static inline unsigned int get_max_nr_ranges(size_t size) +{ + return ((size - sizeof(struct crash_mem)) / + sizeof(struct crash_mem_range)); +} + +/** + * get_mem_rngs_size - Get the allocated size of mem_rngs based on + * max_nr_ranges and chunk size. + * @mem_rngs: Memory ranges. + * + * Returns the maximum size of @mem_rngs. + */ +static inline size_t get_mem_rngs_size(struct crash_mem *mem_rngs) +{ + size_t size; + + if (!mem_rngs) + return 0; + + size = (sizeof(struct crash_mem) + + (mem_rngs->max_nr_ranges * sizeof(struct crash_mem_range))); + + /* + * Memory is allocated in size multiple of MEM_RANGE_CHUNK_SZ. + * So, align to get the actual length. + */ + return ALIGN(size, MEM_RANGE_CHUNK_SZ); +} + +/** + * __add_mem_range - add a memory range to memory ranges list. + * @mem_ranges: Range list to add the memory range to. + * @base: Base address of the range to add. + * @size: Size of the memory range to add. + * + * (Re)allocates memory, if needed. + * + * Returns 0 on success, negative errno on error. + */ +static int __add_mem_range(struct crash_mem **mem_ranges, u64 base, u64 size) +{ + struct crash_mem *mem_rngs = *mem_ranges; + + if (!mem_rngs || (mem_rngs->nr_ranges == mem_rngs->max_nr_ranges)) { + mem_rngs = realloc_mem_ranges(mem_ranges); + if (!mem_rngs) + return -ENOMEM; + } + + mem_rngs->ranges[mem_rngs->nr_ranges].start = base; + mem_rngs->ranges[mem_rngs->nr_ranges].end = base + size - 1; + pr_debug("Added memory range [%#016llx - %#016llx] at index %d\n", + base, base + size - 1, mem_rngs->nr_ranges); + mem_rngs->nr_ranges++; + return 0; +} + +/** + * __merge_memory_ranges - Merges the given memory ranges list. + * @mem_rngs: Range list to merge. + * + * Assumes a sorted range list. + * + * Returns nothing. + */ +static void __merge_memory_ranges(struct crash_mem *mem_rngs) +{ + struct crash_mem_range *ranges; + int i, idx; + + if (!mem_rngs) + return; + + idx = 0; + ranges = &(mem_rngs->ranges[0]); + for (i = 1; i < mem_rngs->nr_ranges; i++) { + if (ranges[i].start <= (ranges[i-1].end + 1)) + ranges[idx].end = ranges[i].end; + else { + idx++; + if (i == idx) + continue; + + ranges[idx] = ranges[i]; + } + } + mem_rngs->nr_ranges = idx + 1; +} + +/* cmp_func_t callback to sort ranges with sort() */ +static int rngcmp(const void *_x, const void *_y) +{ + const struct crash_mem_range *x = _x, *y = _y; + + if (x->start > y->start) + return 1; + if (x->start < y->start) + return -1; + return 0; +} + +/** + * sort_memory_ranges - Sorts the given memory ranges list. + * @mem_rngs: Range list to sort. + * @merge: If true, merge the list after sorting. + * + * Returns nothing. + */ +void sort_memory_ranges(struct crash_mem *mem_rngs, bool merge) +{ + int i; + + if (!mem_rngs) + return; + + /* Sort the ranges in-place */ + sort(&(mem_rngs->ranges[0]), mem_rngs->nr_ranges, + sizeof(mem_rngs->ranges[0]), rngcmp, NULL); + + if (merge) + __merge_memory_ranges(mem_rngs); + + /* For debugging purpose */ + pr_debug("Memory ranges:\n"); + for (i = 0; i < mem_rngs->nr_ranges; i++) { + pr_debug("\t[%03d][%#016llx - %#016llx]\n", i, + mem_rngs->ranges[i].start, + mem_rngs->ranges[i].end); + } +} + +/** + * realloc_mem_ranges - reallocate mem_ranges with size incremented + * by MEM_RANGE_CHUNK_SZ. Frees up the old memory, + * if memory allocation fails. + * @mem_ranges: Memory ranges to reallocate. + * + * Returns pointer to reallocated memory on success, NULL otherwise. + */ +struct crash_mem *realloc_mem_ranges(struct crash_mem **mem_ranges) +{ + struct crash_mem *mem_rngs = *mem_ranges; + unsigned int nr_ranges; + size_t size; + + size = get_mem_rngs_size(mem_rngs); + nr_ranges = mem_rngs ? mem_rngs->nr_ranges : 0; + + size += MEM_RANGE_CHUNK_SZ; + mem_rngs = krealloc(*mem_ranges, size, GFP_KERNEL); + if (!mem_rngs) { + kfree(*mem_ranges); + *mem_ranges = NULL; + return NULL; + } + + mem_rngs->nr_ranges = nr_ranges; + mem_rngs->max_nr_ranges = get_max_nr_ranges(size); + *mem_ranges = mem_rngs; + + return mem_rngs; +} + +/** + * add_mem_range - Updates existing memory range, if there is an overlap. + * Else, adds a new memory range. + * @mem_ranges: Range list to add the memory range to. + * @base: Base address of the range to add. + * @size: Size of the memory range to add. + * + * (Re)allocates memory, if needed. + * + * Returns 0 on success, negative errno on error. + */ +int add_mem_range(struct crash_mem **mem_ranges, u64 base, u64 size) +{ + struct crash_mem *mem_rngs = *mem_ranges; + u64 mstart, mend, end; + unsigned int i; + + if (!size) + return 0; + + end = base + size - 1; + + if (!mem_rngs || !(mem_rngs->nr_ranges)) + return __add_mem_range(mem_ranges, base, size); + + for (i = 0; i < mem_rngs->nr_ranges; i++) { + mstart = mem_rngs->ranges[i].start; + mend = mem_rngs->ranges[i].end; + if (base < mend && end > mstart) { + if (base < mstart) + mem_rngs->ranges[i].start = base; + if (end > mend) + mem_rngs->ranges[i].end = end; + return 0; + } + } + + return __add_mem_range(mem_ranges, base, size); +} -- GitLab From b8e55a3e5c208862eacded5aad822184f89f85d9 Mon Sep 17 00:00:00 2001 From: Hari Bathini Date: Wed, 29 Jul 2020 17:10:16 +0530 Subject: [PATCH 1105/1476] powerpc/kexec_file: Avoid stomping memory used by special regions crashkernel region could have an overlap with special memory regions like OPAL, RTAS, TCE table & such. These regions are referred to as excluded memory ranges. Setup these ranges during image probe in order to avoid them while finding the buffer for different kdump segments. Override arch_kexec_locate_mem_hole() to locate a memory hole taking these ranges into account. Signed-off-by: Hari Bathini Reviewed-by: Thiago Jung Bauermann Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/159602281047.575379.6636807148335160795.stgit@hbathini --- arch/powerpc/include/asm/kexec.h | 7 +- arch/powerpc/include/asm/kexec_ranges.h | 14 + arch/powerpc/kexec/elf_64.c | 8 + arch/powerpc/kexec/file_load_64.c | 337 +++++++++++++++++++++++- arch/powerpc/kexec/ranges.c | 177 +++++++++++++ 5 files changed, 539 insertions(+), 4 deletions(-) diff --git a/arch/powerpc/include/asm/kexec.h b/arch/powerpc/include/asm/kexec.h index 9b3feef55aaad..639f31e13593a 100644 --- a/arch/powerpc/include/asm/kexec.h +++ b/arch/powerpc/include/asm/kexec.h @@ -100,14 +100,16 @@ void relocate_new_kernel(unsigned long indirection_page, unsigned long reboot_co #ifdef CONFIG_KEXEC_FILE extern const struct kexec_file_ops kexec_elf64_ops; -#ifdef CONFIG_IMA_KEXEC #define ARCH_HAS_KIMAGE_ARCH struct kimage_arch { + struct crash_mem *exclude_ranges; + +#ifdef CONFIG_IMA_KEXEC phys_addr_t ima_buffer_addr; size_t ima_buffer_size; -}; #endif +}; int setup_purgatory(struct kimage *image, const void *slave_code, const void *fdt, unsigned long kernel_load_addr, @@ -125,6 +127,7 @@ int setup_new_fdt_ppc64(const struct kimage *image, void *fdt, unsigned long initrd_load_addr, unsigned long initrd_len, const char *cmdline); #endif /* CONFIG_PPC64 */ + #endif /* CONFIG_KEXEC_FILE */ #else /* !CONFIG_KEXEC_CORE */ diff --git a/arch/powerpc/include/asm/kexec_ranges.h b/arch/powerpc/include/asm/kexec_ranges.h index 35ae31a7a4de0..7a90000f8d152 100644 --- a/arch/powerpc/include/asm/kexec_ranges.h +++ b/arch/powerpc/include/asm/kexec_ranges.h @@ -7,5 +7,19 @@ void sort_memory_ranges(struct crash_mem *mrngs, bool merge); struct crash_mem *realloc_mem_ranges(struct crash_mem **mem_ranges); int add_mem_range(struct crash_mem **mem_ranges, u64 base, u64 size); +int add_tce_mem_ranges(struct crash_mem **mem_ranges); +int add_initrd_mem_range(struct crash_mem **mem_ranges); +#ifdef CONFIG_PPC_BOOK3S_64 +int add_htab_mem_range(struct crash_mem **mem_ranges); +#else +static inline int add_htab_mem_range(struct crash_mem **mem_ranges) +{ + return 0; +} +#endif +int add_kernel_mem_range(struct crash_mem **mem_ranges); +int add_rtas_mem_range(struct crash_mem **mem_ranges); +int add_opal_mem_range(struct crash_mem **mem_ranges); +int add_reserved_mem_ranges(struct crash_mem **mem_ranges); #endif /* _ASM_POWERPC_KEXEC_RANGES_H */ diff --git a/arch/powerpc/kexec/elf_64.c b/arch/powerpc/kexec/elf_64.c index 23ad04ccaf8e8..64c15a5a280b3 100644 --- a/arch/powerpc/kexec/elf_64.c +++ b/arch/powerpc/kexec/elf_64.c @@ -46,6 +46,14 @@ static void *elf64_load(struct kimage *image, char *kernel_buf, if (ret) goto out; + if (image->type == KEXEC_TYPE_CRASH) { + /* min & max buffer values for kdump case */ + kbuf.buf_min = pbuf.buf_min = crashk_res.start; + kbuf.buf_max = pbuf.buf_max = + ((crashk_res.end < ppc64_rma_size) ? + crashk_res.end : (ppc64_rma_size - 1)); + } + ret = kexec_elf_load(image, &ehdr, &elf_info, &kbuf, &kernel_load_addr); if (ret) goto out; diff --git a/arch/powerpc/kexec/file_load_64.c b/arch/powerpc/kexec/file_load_64.c index 3e9ac5f216b09..d09c7724efa84 100644 --- a/arch/powerpc/kexec/file_load_64.c +++ b/arch/powerpc/kexec/file_load_64.c @@ -17,12 +17,262 @@ #include #include #include +#include +#include const struct kexec_file_ops * const kexec_file_loaders[] = { &kexec_elf64_ops, NULL }; +/** + * get_exclude_memory_ranges - Get exclude memory ranges. This list includes + * regions like opal/rtas, tce-table, initrd, + * kernel, htab which should be avoided while + * setting up kexec load segments. + * @mem_ranges: Range list to add the memory ranges to. + * + * Returns 0 on success, negative errno on error. + */ +static int get_exclude_memory_ranges(struct crash_mem **mem_ranges) +{ + int ret; + + ret = add_tce_mem_ranges(mem_ranges); + if (ret) + goto out; + + ret = add_initrd_mem_range(mem_ranges); + if (ret) + goto out; + + ret = add_htab_mem_range(mem_ranges); + if (ret) + goto out; + + ret = add_kernel_mem_range(mem_ranges); + if (ret) + goto out; + + ret = add_rtas_mem_range(mem_ranges); + if (ret) + goto out; + + ret = add_opal_mem_range(mem_ranges); + if (ret) + goto out; + + ret = add_reserved_mem_ranges(mem_ranges); + if (ret) + goto out; + + /* exclude memory ranges should be sorted for easy lookup */ + sort_memory_ranges(*mem_ranges, true); +out: + if (ret) + pr_err("Failed to setup exclude memory ranges\n"); + return ret; +} + +/** + * __locate_mem_hole_top_down - Looks top down for a large enough memory hole + * in the memory regions between buf_min & buf_max + * for the buffer. If found, sets kbuf->mem. + * @kbuf: Buffer contents and memory parameters. + * @buf_min: Minimum address for the buffer. + * @buf_max: Maximum address for the buffer. + * + * Returns 0 on success, negative errno on error. + */ +static int __locate_mem_hole_top_down(struct kexec_buf *kbuf, + u64 buf_min, u64 buf_max) +{ + int ret = -EADDRNOTAVAIL; + phys_addr_t start, end; + u64 i; + + for_each_mem_range_rev(i, &memblock.memory, NULL, NUMA_NO_NODE, + MEMBLOCK_NONE, &start, &end, NULL) { + /* + * memblock uses [start, end) convention while it is + * [start, end] here. Fix the off-by-one to have the + * same convention. + */ + end -= 1; + + if (start > buf_max) + continue; + + /* Memory hole not found */ + if (end < buf_min) + break; + + /* Adjust memory region based on the given range */ + if (start < buf_min) + start = buf_min; + if (end > buf_max) + end = buf_max; + + start = ALIGN(start, kbuf->buf_align); + if (start < end && (end - start + 1) >= kbuf->memsz) { + /* Suitable memory range found. Set kbuf->mem */ + kbuf->mem = ALIGN_DOWN(end - kbuf->memsz + 1, + kbuf->buf_align); + ret = 0; + break; + } + } + + return ret; +} + +/** + * locate_mem_hole_top_down_ppc64 - Skip special memory regions to find a + * suitable buffer with top down approach. + * @kbuf: Buffer contents and memory parameters. + * @buf_min: Minimum address for the buffer. + * @buf_max: Maximum address for the buffer. + * @emem: Exclude memory ranges. + * + * Returns 0 on success, negative errno on error. + */ +static int locate_mem_hole_top_down_ppc64(struct kexec_buf *kbuf, + u64 buf_min, u64 buf_max, + const struct crash_mem *emem) +{ + int i, ret = 0, err = -EADDRNOTAVAIL; + u64 start, end, tmin, tmax; + + tmax = buf_max; + for (i = (emem->nr_ranges - 1); i >= 0; i--) { + start = emem->ranges[i].start; + end = emem->ranges[i].end; + + if (start > tmax) + continue; + + if (end < tmax) { + tmin = (end < buf_min ? buf_min : end + 1); + ret = __locate_mem_hole_top_down(kbuf, tmin, tmax); + if (!ret) + return 0; + } + + tmax = start - 1; + + if (tmax < buf_min) { + ret = err; + break; + } + ret = 0; + } + + if (!ret) { + tmin = buf_min; + ret = __locate_mem_hole_top_down(kbuf, tmin, tmax); + } + return ret; +} + +/** + * __locate_mem_hole_bottom_up - Looks bottom up for a large enough memory hole + * in the memory regions between buf_min & buf_max + * for the buffer. If found, sets kbuf->mem. + * @kbuf: Buffer contents and memory parameters. + * @buf_min: Minimum address for the buffer. + * @buf_max: Maximum address for the buffer. + * + * Returns 0 on success, negative errno on error. + */ +static int __locate_mem_hole_bottom_up(struct kexec_buf *kbuf, + u64 buf_min, u64 buf_max) +{ + int ret = -EADDRNOTAVAIL; + phys_addr_t start, end; + u64 i; + + for_each_mem_range(i, &memblock.memory, NULL, NUMA_NO_NODE, + MEMBLOCK_NONE, &start, &end, NULL) { + /* + * memblock uses [start, end) convention while it is + * [start, end] here. Fix the off-by-one to have the + * same convention. + */ + end -= 1; + + if (end < buf_min) + continue; + + /* Memory hole not found */ + if (start > buf_max) + break; + + /* Adjust memory region based on the given range */ + if (start < buf_min) + start = buf_min; + if (end > buf_max) + end = buf_max; + + start = ALIGN(start, kbuf->buf_align); + if (start < end && (end - start + 1) >= kbuf->memsz) { + /* Suitable memory range found. Set kbuf->mem */ + kbuf->mem = start; + ret = 0; + break; + } + } + + return ret; +} + +/** + * locate_mem_hole_bottom_up_ppc64 - Skip special memory regions to find a + * suitable buffer with bottom up approach. + * @kbuf: Buffer contents and memory parameters. + * @buf_min: Minimum address for the buffer. + * @buf_max: Maximum address for the buffer. + * @emem: Exclude memory ranges. + * + * Returns 0 on success, negative errno on error. + */ +static int locate_mem_hole_bottom_up_ppc64(struct kexec_buf *kbuf, + u64 buf_min, u64 buf_max, + const struct crash_mem *emem) +{ + int i, ret = 0, err = -EADDRNOTAVAIL; + u64 start, end, tmin, tmax; + + tmin = buf_min; + for (i = 0; i < emem->nr_ranges; i++) { + start = emem->ranges[i].start; + end = emem->ranges[i].end; + + if (end < tmin) + continue; + + if (start > tmin) { + tmax = (start > buf_max ? buf_max : start - 1); + ret = __locate_mem_hole_bottom_up(kbuf, tmin, tmax); + if (!ret) + return 0; + } + + tmin = end + 1; + + if (tmin > buf_max) { + ret = err; + break; + } + ret = 0; + } + + if (!ret) { + tmax = buf_max; + ret = __locate_mem_hole_bottom_up(kbuf, tmin, tmax); + } + return ret; +} + /** * setup_purgatory_ppc64 - initialize PPC64 specific purgatory's global * variables and call setup_purgatory() to initialize @@ -67,6 +317,67 @@ int setup_new_fdt_ppc64(const struct kimage *image, void *fdt, return setup_new_fdt(image, fdt, initrd_load_addr, initrd_len, cmdline); } +/** + * arch_kexec_locate_mem_hole - Skip special memory regions like rtas, opal, + * tce-table, reserved-ranges & such (exclude + * memory ranges) as they can't be used for kexec + * segment buffer. Sets kbuf->mem when a suitable + * memory hole is found. + * @kbuf: Buffer contents and memory parameters. + * + * Assumes minimum of PAGE_SIZE alignment for kbuf->memsz & kbuf->buf_align. + * + * Returns 0 on success, negative errno on error. + */ +int arch_kexec_locate_mem_hole(struct kexec_buf *kbuf) +{ + struct crash_mem **emem; + u64 buf_min, buf_max; + int ret; + + /* + * Use the generic kexec_locate_mem_hole for regular + * kexec_file_load syscall + */ + if (kbuf->image->type != KEXEC_TYPE_CRASH) + return kexec_locate_mem_hole(kbuf); + + /* Look up the exclude ranges list while locating the memory hole */ + emem = &(kbuf->image->arch.exclude_ranges); + if (!(*emem) || ((*emem)->nr_ranges == 0)) { + pr_warn("No exclude range list. Using the default locate mem hole method\n"); + return kexec_locate_mem_hole(kbuf); + } + + /* Segments for kdump kernel should be within crashkernel region */ + buf_min = (kbuf->buf_min < crashk_res.start ? + crashk_res.start : kbuf->buf_min); + buf_max = (kbuf->buf_max > crashk_res.end ? + crashk_res.end : kbuf->buf_max); + + if (buf_min > buf_max) { + pr_err("Invalid buffer min and/or max values\n"); + return -EINVAL; + } + + if (kbuf->top_down) + ret = locate_mem_hole_top_down_ppc64(kbuf, buf_min, buf_max, + *emem); + else + ret = locate_mem_hole_bottom_up_ppc64(kbuf, buf_min, buf_max, + *emem); + + /* Add the buffer allocated to the exclude list for the next lookup */ + if (!ret) { + add_mem_range(emem, kbuf->mem, kbuf->memsz); + sort_memory_ranges(*emem, true); + } else { + pr_err("Failed to locate memory buffer of size %lu\n", + kbuf->memsz); + } + return ret; +} + /** * arch_kexec_kernel_image_probe - Does additional handling needed to setup * kexec segments. @@ -79,9 +390,31 @@ int setup_new_fdt_ppc64(const struct kimage *image, void *fdt, int arch_kexec_kernel_image_probe(struct kimage *image, void *buf, unsigned long buf_len) { - /* We don't support crash kernels yet. */ - if (image->type == KEXEC_TYPE_CRASH) + if (image->type == KEXEC_TYPE_CRASH) { + int ret; + + /* Get exclude memory ranges needed for setting up kdump segments */ + ret = get_exclude_memory_ranges(&(image->arch.exclude_ranges)); + if (ret) + pr_err("Failed to setup exclude memory ranges for buffer lookup\n"); + /* Return this until all changes for panic kernel are in */ return -EOPNOTSUPP; + } return kexec_image_probe_default(image, buf, buf_len); } + +/** + * arch_kimage_file_post_load_cleanup - Frees up all the allocations done + * while loading the image. + * @image: kexec image being loaded. + * + * Returns 0 on success, negative errno on error. + */ +int arch_kimage_file_post_load_cleanup(struct kimage *image) +{ + kfree(image->arch.exclude_ranges); + image->arch.exclude_ranges = NULL; + + return kexec_image_post_load_cleanup_default(image); +} diff --git a/arch/powerpc/kexec/ranges.c b/arch/powerpc/kexec/ranges.c index dc3ce036f4169..6b81c852feaba 100644 --- a/arch/powerpc/kexec/ranges.c +++ b/arch/powerpc/kexec/ranges.c @@ -233,3 +233,180 @@ int add_mem_range(struct crash_mem **mem_ranges, u64 base, u64 size) return __add_mem_range(mem_ranges, base, size); } + +/** + * add_tce_mem_ranges - Adds tce-table range to the given memory ranges list. + * @mem_ranges: Range list to add the memory range(s) to. + * + * Returns 0 on success, negative errno on error. + */ +int add_tce_mem_ranges(struct crash_mem **mem_ranges) +{ + struct device_node *dn = NULL; + int ret = 0; + + for_each_node_by_type(dn, "pci") { + u64 base; + u32 size; + + ret = of_property_read_u64(dn, "linux,tce-base", &base); + ret |= of_property_read_u32(dn, "linux,tce-size", &size); + if (ret) { + /* + * It is ok to have pci nodes without tce. So, ignore + * property does not exist error. + */ + if (ret == -EINVAL) { + ret = 0; + continue; + } + break; + } + + ret = add_mem_range(mem_ranges, base, size); + if (ret) + break; + } + + of_node_put(dn); + return ret; +} + +/** + * add_initrd_mem_range - Adds initrd range to the given memory ranges list, + * if the initrd was retained. + * @mem_ranges: Range list to add the memory range to. + * + * Returns 0 on success, negative errno on error. + */ +int add_initrd_mem_range(struct crash_mem **mem_ranges) +{ + u64 base, end; + int ret; + + /* This range means something, only if initrd was retained */ + if (!strstr(saved_command_line, "retain_initrd")) + return 0; + + ret = of_property_read_u64(of_chosen, "linux,initrd-start", &base); + ret |= of_property_read_u64(of_chosen, "linux,initrd-end", &end); + if (!ret) + ret = add_mem_range(mem_ranges, base, end - base + 1); + + return ret; +} + +#ifdef CONFIG_PPC_BOOK3S_64 +/** + * add_htab_mem_range - Adds htab range to the given memory ranges list, + * if it exists + * @mem_ranges: Range list to add the memory range to. + * + * Returns 0 on success, negative errno on error. + */ +int add_htab_mem_range(struct crash_mem **mem_ranges) +{ + if (!htab_address) + return 0; + + return add_mem_range(mem_ranges, __pa(htab_address), htab_size_bytes); +} +#endif + +/** + * add_kernel_mem_range - Adds kernel text region to the given + * memory ranges list. + * @mem_ranges: Range list to add the memory range to. + * + * Returns 0 on success, negative errno on error. + */ +int add_kernel_mem_range(struct crash_mem **mem_ranges) +{ + return add_mem_range(mem_ranges, 0, __pa(_end)); +} + +/** + * add_rtas_mem_range - Adds RTAS region to the given memory ranges list. + * @mem_ranges: Range list to add the memory range to. + * + * Returns 0 on success, negative errno on error. + */ +int add_rtas_mem_range(struct crash_mem **mem_ranges) +{ + struct device_node *dn; + u32 base, size; + int ret = 0; + + dn = of_find_node_by_path("/rtas"); + if (!dn) + return 0; + + ret = of_property_read_u32(dn, "linux,rtas-base", &base); + ret |= of_property_read_u32(dn, "rtas-size", &size); + if (!ret) + ret = add_mem_range(mem_ranges, base, size); + + of_node_put(dn); + return ret; +} + +/** + * add_opal_mem_range - Adds OPAL region to the given memory ranges list. + * @mem_ranges: Range list to add the memory range to. + * + * Returns 0 on success, negative errno on error. + */ +int add_opal_mem_range(struct crash_mem **mem_ranges) +{ + struct device_node *dn; + u64 base, size; + int ret; + + dn = of_find_node_by_path("/ibm,opal"); + if (!dn) + return 0; + + ret = of_property_read_u64(dn, "opal-base-address", &base); + ret |= of_property_read_u64(dn, "opal-runtime-size", &size); + if (!ret) + ret = add_mem_range(mem_ranges, base, size); + + of_node_put(dn); + return ret; +} + +/** + * add_reserved_mem_ranges - Adds "/reserved-ranges" regions exported by f/w + * to the given memory ranges list. + * @mem_ranges: Range list to add the memory ranges to. + * + * Returns 0 on success, negative errno on error. + */ +int add_reserved_mem_ranges(struct crash_mem **mem_ranges) +{ + int n_mem_addr_cells, n_mem_size_cells, i, len, cells, ret = 0; + const __be32 *prop; + + prop = of_get_property(of_root, "reserved-ranges", &len); + if (!prop) + return 0; + + n_mem_addr_cells = of_n_addr_cells(of_root); + n_mem_size_cells = of_n_size_cells(of_root); + cells = n_mem_addr_cells + n_mem_size_cells; + + /* Each reserved range is an (address,size) pair */ + for (i = 0; i < (len / (sizeof(u32) * cells)); i++) { + u64 base, size; + + base = of_read_number(prop + (i * cells), n_mem_addr_cells); + size = of_read_number(prop + (i * cells) + n_mem_addr_cells, + n_mem_size_cells); + + ret = add_mem_range(mem_ranges, base, size); + if (ret) + break; + } + + return ret; +} -- GitLab From adfefc609e55edc5dce18a68d1526af6d70aaf86 Mon Sep 17 00:00:00 2001 From: Hari Bathini Date: Wed, 29 Jul 2020 17:10:32 +0530 Subject: [PATCH 1106/1476] powerpc/drmem: Make LMB walk a bit more flexible Currently, numa & prom are the only users of drmem LMB walk code. Loading kdump with kexec_file also needs to walk the drmem LMBs to setup the usable memory ranges for kdump kernel. But there are couple of issues in using the code as is. One, walk_drmem_lmb() code is built into the .init section currently, while kexec_file needs it later. Two, there is no scope to pass data to the callback function for processing and/or erroring out on certain conditions. Fix that by, moving drmem LMB walk code out of .init section, adding scope to pass data to the callback function and bailing out when an error is encountered in the callback function. Signed-off-by: Hari Bathini Tested-by: Pingfan Liu Reviewed-by: Thiago Jung Bauermann Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/159602282727.575379.3979857013827701828.stgit@hbathini --- arch/powerpc/include/asm/drmem.h | 9 ++-- arch/powerpc/kernel/prom.c | 13 +++-- arch/powerpc/mm/drmem.c | 87 +++++++++++++++++++++----------- arch/powerpc/mm/numa.c | 13 +++-- 4 files changed, 78 insertions(+), 44 deletions(-) diff --git a/arch/powerpc/include/asm/drmem.h b/arch/powerpc/include/asm/drmem.h index 414d209f45bbe..17ccc6474ab6f 100644 --- a/arch/powerpc/include/asm/drmem.h +++ b/arch/powerpc/include/asm/drmem.h @@ -90,13 +90,14 @@ static inline bool drmem_lmb_reserved(struct drmem_lmb *lmb) } u64 drmem_lmb_memory_max(void); -void __init walk_drmem_lmbs(struct device_node *dn, - void (*func)(struct drmem_lmb *, const __be32 **)); +int walk_drmem_lmbs(struct device_node *dn, void *data, + int (*func)(struct drmem_lmb *, const __be32 **, void *)); int drmem_update_dt(void); #ifdef CONFIG_PPC_PSERIES -void __init walk_drmem_lmbs_early(unsigned long node, - void (*func)(struct drmem_lmb *, const __be32 **)); +int __init +walk_drmem_lmbs_early(unsigned long node, void *data, + int (*func)(struct drmem_lmb *, const __be32 **, void *)); #endif static inline void invalidate_lmb_associativity_index(struct drmem_lmb *lmb) diff --git a/arch/powerpc/kernel/prom.c b/arch/powerpc/kernel/prom.c index 5a05c5e993fd0..d8a2fb87ba0c4 100644 --- a/arch/powerpc/kernel/prom.c +++ b/arch/powerpc/kernel/prom.c @@ -470,8 +470,9 @@ static bool validate_mem_limit(u64 base, u64 *size) * This contains a list of memory blocks along with NUMA affinity * information. */ -static void __init early_init_drmem_lmb(struct drmem_lmb *lmb, - const __be32 **usm) +static int __init early_init_drmem_lmb(struct drmem_lmb *lmb, + const __be32 **usm, + void *data) { u64 base, size; int is_kexec_kdump = 0, rngs; @@ -486,7 +487,7 @@ static void __init early_init_drmem_lmb(struct drmem_lmb *lmb, */ if ((lmb->flags & DRCONF_MEM_RESERVED) || !(lmb->flags & DRCONF_MEM_ASSIGNED)) - return; + return 0; if (*usm) is_kexec_kdump = 1; @@ -501,7 +502,7 @@ static void __init early_init_drmem_lmb(struct drmem_lmb *lmb, */ rngs = dt_mem_next_cell(dt_root_size_cells, usm); if (!rngs) /* there are no (base, size) duple */ - return; + return 0; } do { @@ -526,6 +527,8 @@ static void __init early_init_drmem_lmb(struct drmem_lmb *lmb, if (lmb->flags & DRCONF_MEM_HOTREMOVABLE) memblock_mark_hotplug(base, size); } while (--rngs); + + return 0; } #endif /* CONFIG_PPC_PSERIES */ @@ -536,7 +539,7 @@ static int __init early_init_dt_scan_memory_ppc(unsigned long node, #ifdef CONFIG_PPC_PSERIES if (depth == 1 && strcmp(uname, "ibm,dynamic-reconfiguration-memory") == 0) { - walk_drmem_lmbs_early(node, early_init_drmem_lmb); + walk_drmem_lmbs_early(node, NULL, early_init_drmem_lmb); return 0; } #endif diff --git a/arch/powerpc/mm/drmem.c b/arch/powerpc/mm/drmem.c index 59327cefbc6a6..b2eeea39684ca 100644 --- a/arch/powerpc/mm/drmem.c +++ b/arch/powerpc/mm/drmem.c @@ -14,6 +14,8 @@ #include #include +static int n_root_addr_cells, n_root_size_cells; + static struct drmem_lmb_info __drmem_info; struct drmem_lmb_info *drmem_info = &__drmem_info; @@ -189,12 +191,13 @@ int drmem_update_dt(void) return rc; } -static void __init read_drconf_v1_cell(struct drmem_lmb *lmb, +static void read_drconf_v1_cell(struct drmem_lmb *lmb, const __be32 **prop) { const __be32 *p = *prop; - lmb->base_addr = dt_mem_next_cell(dt_root_addr_cells, &p); + lmb->base_addr = of_read_number(p, n_root_addr_cells); + p += n_root_addr_cells; lmb->drc_index = of_read_number(p++, 1); p++; /* skip reserved field */ @@ -205,29 +208,33 @@ static void __init read_drconf_v1_cell(struct drmem_lmb *lmb, *prop = p; } -static void __init __walk_drmem_v1_lmbs(const __be32 *prop, const __be32 *usm, - void (*func)(struct drmem_lmb *, const __be32 **)) +static int +__walk_drmem_v1_lmbs(const __be32 *prop, const __be32 *usm, void *data, + int (*func)(struct drmem_lmb *, const __be32 **, void *)) { struct drmem_lmb lmb; u32 i, n_lmbs; + int ret = 0; n_lmbs = of_read_number(prop++, 1); - if (n_lmbs == 0) - return; - for (i = 0; i < n_lmbs; i++) { read_drconf_v1_cell(&lmb, &prop); - func(&lmb, &usm); + ret = func(&lmb, &usm, data); + if (ret) + break; } + + return ret; } -static void __init read_drconf_v2_cell(struct of_drconf_cell_v2 *dr_cell, +static void read_drconf_v2_cell(struct of_drconf_cell_v2 *dr_cell, const __be32 **prop) { const __be32 *p = *prop; dr_cell->seq_lmbs = of_read_number(p++, 1); - dr_cell->base_addr = dt_mem_next_cell(dt_root_addr_cells, &p); + dr_cell->base_addr = of_read_number(p, n_root_addr_cells); + p += n_root_addr_cells; dr_cell->drc_index = of_read_number(p++, 1); dr_cell->aa_index = of_read_number(p++, 1); dr_cell->flags = of_read_number(p++, 1); @@ -235,17 +242,16 @@ static void __init read_drconf_v2_cell(struct of_drconf_cell_v2 *dr_cell, *prop = p; } -static void __init __walk_drmem_v2_lmbs(const __be32 *prop, const __be32 *usm, - void (*func)(struct drmem_lmb *, const __be32 **)) +static int +__walk_drmem_v2_lmbs(const __be32 *prop, const __be32 *usm, void *data, + int (*func)(struct drmem_lmb *, const __be32 **, void *)) { struct of_drconf_cell_v2 dr_cell; struct drmem_lmb lmb; u32 i, j, lmb_sets; + int ret = 0; lmb_sets = of_read_number(prop++, 1); - if (lmb_sets == 0) - return; - for (i = 0; i < lmb_sets; i++) { read_drconf_v2_cell(&dr_cell, &prop); @@ -259,21 +265,29 @@ static void __init __walk_drmem_v2_lmbs(const __be32 *prop, const __be32 *usm, lmb.aa_index = dr_cell.aa_index; lmb.flags = dr_cell.flags; - func(&lmb, &usm); + ret = func(&lmb, &usm, data); + if (ret) + break; } } + + return ret; } #ifdef CONFIG_PPC_PSERIES -void __init walk_drmem_lmbs_early(unsigned long node, - void (*func)(struct drmem_lmb *, const __be32 **)) +int __init walk_drmem_lmbs_early(unsigned long node, void *data, + int (*func)(struct drmem_lmb *, const __be32 **, void *)) { const __be32 *prop, *usm; - int len; + int len, ret = -ENODEV; prop = of_get_flat_dt_prop(node, "ibm,lmb-size", &len); if (!prop || len < dt_root_size_cells * sizeof(__be32)) - return; + return ret; + + /* Get the address & size cells */ + n_root_addr_cells = dt_root_addr_cells; + n_root_size_cells = dt_root_size_cells; drmem_info->lmb_size = dt_mem_next_cell(dt_root_size_cells, &prop); @@ -281,20 +295,21 @@ void __init walk_drmem_lmbs_early(unsigned long node, prop = of_get_flat_dt_prop(node, "ibm,dynamic-memory", &len); if (prop) { - __walk_drmem_v1_lmbs(prop, usm, func); + ret = __walk_drmem_v1_lmbs(prop, usm, data, func); } else { prop = of_get_flat_dt_prop(node, "ibm,dynamic-memory-v2", &len); if (prop) - __walk_drmem_v2_lmbs(prop, usm, func); + ret = __walk_drmem_v2_lmbs(prop, usm, data, func); } memblock_dump_all(); + return ret; } #endif -static int __init init_drmem_lmb_size(struct device_node *dn) +static int init_drmem_lmb_size(struct device_node *dn) { const __be32 *prop; int len; @@ -303,12 +318,12 @@ static int __init init_drmem_lmb_size(struct device_node *dn) return 0; prop = of_get_property(dn, "ibm,lmb-size", &len); - if (!prop || len < dt_root_size_cells * sizeof(__be32)) { + if (!prop || len < n_root_size_cells * sizeof(__be32)) { pr_info("Could not determine LMB size\n"); return -1; } - drmem_info->lmb_size = dt_mem_next_cell(dt_root_size_cells, &prop); + drmem_info->lmb_size = of_read_number(prop, n_root_size_cells); return 0; } @@ -329,24 +344,36 @@ static const __be32 *of_get_usable_memory(struct device_node *dn) return prop; } -void __init walk_drmem_lmbs(struct device_node *dn, - void (*func)(struct drmem_lmb *, const __be32 **)) +int walk_drmem_lmbs(struct device_node *dn, void *data, + int (*func)(struct drmem_lmb *, const __be32 **, void *)) { const __be32 *prop, *usm; + int ret = -ENODEV; + + if (!of_root) + return ret; + + /* Get the address & size cells */ + of_node_get(of_root); + n_root_addr_cells = of_n_addr_cells(of_root); + n_root_size_cells = of_n_size_cells(of_root); + of_node_put(of_root); if (init_drmem_lmb_size(dn)) - return; + return ret; usm = of_get_usable_memory(dn); prop = of_get_property(dn, "ibm,dynamic-memory", NULL); if (prop) { - __walk_drmem_v1_lmbs(prop, usm, func); + ret = __walk_drmem_v1_lmbs(prop, usm, data, func); } else { prop = of_get_property(dn, "ibm,dynamic-memory-v2", NULL); if (prop) - __walk_drmem_v2_lmbs(prop, usm, func); + ret = __walk_drmem_v2_lmbs(prop, usm, data, func); } + + return ret; } static void __init init_drmem_v1_lmbs(const __be32 *prop) diff --git a/arch/powerpc/mm/numa.c b/arch/powerpc/mm/numa.c index 92938393fec6d..058fee9a08358 100644 --- a/arch/powerpc/mm/numa.c +++ b/arch/powerpc/mm/numa.c @@ -645,8 +645,9 @@ static inline int __init read_usm_ranges(const __be32 **usm) * Extract NUMA information from the ibm,dynamic-reconfiguration-memory * node. This assumes n_mem_{addr,size}_cells have been set. */ -static void __init numa_setup_drmem_lmb(struct drmem_lmb *lmb, - const __be32 **usm) +static int __init numa_setup_drmem_lmb(struct drmem_lmb *lmb, + const __be32 **usm, + void *data) { unsigned int ranges, is_kexec_kdump = 0; unsigned long base, size, sz; @@ -658,7 +659,7 @@ static void __init numa_setup_drmem_lmb(struct drmem_lmb *lmb, */ if ((lmb->flags & DRCONF_MEM_RESERVED) || !(lmb->flags & DRCONF_MEM_ASSIGNED)) - return; + return 0; if (*usm) is_kexec_kdump = 1; @@ -670,7 +671,7 @@ static void __init numa_setup_drmem_lmb(struct drmem_lmb *lmb, if (is_kexec_kdump) { ranges = read_usm_ranges(usm); if (!ranges) /* there are no (base, size) duple */ - return; + return 0; } do { @@ -687,6 +688,8 @@ static void __init numa_setup_drmem_lmb(struct drmem_lmb *lmb, if (sz) memblock_set_node(base, sz, &memblock.memory, nid); } while (--ranges); + + return 0; } static int __init parse_numa_properties(void) @@ -788,7 +791,7 @@ new_range: */ memory = of_find_node_by_path("/ibm,dynamic-reconfiguration-memory"); if (memory) { - walk_drmem_lmbs(memory, numa_setup_drmem_lmb); + walk_drmem_lmbs(memory, NULL, numa_setup_drmem_lmb); of_node_put(memory); } -- GitLab From 7c64e21a1c5a5bcd651d895b8faa68e9cdcc433d Mon Sep 17 00:00:00 2001 From: Hari Bathini Date: Wed, 29 Jul 2020 17:12:16 +0530 Subject: [PATCH 1107/1476] powerpc/kexec_file: Restrict memory usage of kdump kernel Kdump kernel, used for capturing the kernel core image, is supposed to use only specific memory regions to avoid corrupting the image to be captured. The regions are crashkernel range - the memory reserved explicitly for kdump kernel, memory used for the tce-table, the OPAL region and RTAS region as applicable. Restrict kdump kernel memory to use only these regions by setting up usable-memory DT property. Also, tell the kdump kernel to run at the loaded address by setting the magic word at 0x5c. Signed-off-by: Hari Bathini Tested-by: Pingfan Liu Reviewed-by: Thiago Jung Bauermann Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/159602284284.575379.6962016255404325493.stgit@hbathini --- arch/powerpc/kexec/file_load_64.c | 386 +++++++++++++++++++++++++++++- 1 file changed, 385 insertions(+), 1 deletion(-) diff --git a/arch/powerpc/kexec/file_load_64.c b/arch/powerpc/kexec/file_load_64.c index d09c7724efa84..f946608747657 100644 --- a/arch/powerpc/kexec/file_load_64.c +++ b/arch/powerpc/kexec/file_load_64.c @@ -17,9 +17,23 @@ #include #include #include +#include #include +#include +#include #include +struct umem_info { + u64 *buf; /* data buffer for usable-memory property */ + u32 size; /* size allocated for the data buffer */ + u32 max_entries; /* maximum no. of entries */ + u32 idx; /* index of current entry */ + + /* usable memory ranges to look up */ + unsigned int nr_ranges; + const struct crash_mem_range *ranges; +}; + const struct kexec_file_ops * const kexec_file_loaders[] = { &kexec_elf64_ops, NULL @@ -74,6 +88,44 @@ out: return ret; } +/** + * get_usable_memory_ranges - Get usable memory ranges. This list includes + * regions like crashkernel, opal/rtas & tce-table, + * that kdump kernel could use. + * @mem_ranges: Range list to add the memory ranges to. + * + * Returns 0 on success, negative errno on error. + */ +static int get_usable_memory_ranges(struct crash_mem **mem_ranges) +{ + int ret; + + /* + * Early boot failure observed on guests when low memory (first memory + * block?) is not added to usable memory. So, add [0, crashk_res.end] + * instead of [crashk_res.start, crashk_res.end] to workaround it. + * Also, crashed kernel's memory must be added to reserve map to + * avoid kdump kernel from using it. + */ + ret = add_mem_range(mem_ranges, 0, crashk_res.end + 1); + if (ret) + goto out; + + ret = add_rtas_mem_range(mem_ranges); + if (ret) + goto out; + + ret = add_opal_mem_range(mem_ranges); + if (ret) + goto out; + + ret = add_tce_mem_ranges(mem_ranges); +out: + if (ret) + pr_err("Failed to setup usable memory ranges\n"); + return ret; +} + /** * __locate_mem_hole_top_down - Looks top down for a large enough memory hole * in the memory regions between buf_min & buf_max @@ -273,6 +325,286 @@ static int locate_mem_hole_bottom_up_ppc64(struct kexec_buf *kbuf, return ret; } +/** + * check_realloc_usable_mem - Reallocate buffer if it can't accommodate entries + * @um_info: Usable memory buffer and ranges info. + * @cnt: No. of entries to accommodate. + * + * Frees up the old buffer if memory reallocation fails. + * + * Returns buffer on success, NULL on error. + */ +static u64 *check_realloc_usable_mem(struct umem_info *um_info, int cnt) +{ + u32 new_size; + u64 *tbuf; + + if ((um_info->idx + cnt) <= um_info->max_entries) + return um_info->buf; + + new_size = um_info->size + MEM_RANGE_CHUNK_SZ; + tbuf = krealloc(um_info->buf, new_size, GFP_KERNEL); + if (tbuf) { + um_info->buf = tbuf; + um_info->size = new_size; + um_info->max_entries = (um_info->size / sizeof(u64)); + } + + return tbuf; +} + +/** + * add_usable_mem - Add the usable memory ranges within the given memory range + * to the buffer + * @um_info: Usable memory buffer and ranges info. + * @base: Base address of memory range to look for. + * @end: End address of memory range to look for. + * + * Returns 0 on success, negative errno on error. + */ +static int add_usable_mem(struct umem_info *um_info, u64 base, u64 end) +{ + u64 loc_base, loc_end; + bool add; + int i; + + for (i = 0; i < um_info->nr_ranges; i++) { + add = false; + loc_base = um_info->ranges[i].start; + loc_end = um_info->ranges[i].end; + if (loc_base >= base && loc_end <= end) + add = true; + else if (base < loc_end && end > loc_base) { + if (loc_base < base) + loc_base = base; + if (loc_end > end) + loc_end = end; + add = true; + } + + if (add) { + if (!check_realloc_usable_mem(um_info, 2)) + return -ENOMEM; + + um_info->buf[um_info->idx++] = cpu_to_be64(loc_base); + um_info->buf[um_info->idx++] = + cpu_to_be64(loc_end - loc_base + 1); + } + } + + return 0; +} + +/** + * kdump_setup_usable_lmb - This is a callback function that gets called by + * walk_drmem_lmbs for every LMB to set its + * usable memory ranges. + * @lmb: LMB info. + * @usm: linux,drconf-usable-memory property value. + * @data: Pointer to usable memory buffer and ranges info. + * + * Returns 0 on success, negative errno on error. + */ +static int kdump_setup_usable_lmb(struct drmem_lmb *lmb, const __be32 **usm, + void *data) +{ + struct umem_info *um_info; + int tmp_idx, ret; + u64 base, end; + + /* + * kdump load isn't supported on kernels already booted with + * linux,drconf-usable-memory property. + */ + if (*usm) { + pr_err("linux,drconf-usable-memory property already exists!"); + return -EINVAL; + } + + um_info = data; + tmp_idx = um_info->idx; + if (!check_realloc_usable_mem(um_info, 1)) + return -ENOMEM; + + um_info->idx++; + base = lmb->base_addr; + end = base + drmem_lmb_size() - 1; + ret = add_usable_mem(um_info, base, end); + if (!ret) { + /* + * Update the no. of ranges added. Two entries (base & size) + * for every range added. + */ + um_info->buf[tmp_idx] = + cpu_to_be64((um_info->idx - tmp_idx - 1) / 2); + } + + return ret; +} + +#define NODE_PATH_LEN 256 +/** + * add_usable_mem_property - Add usable memory property for the given + * memory node. + * @fdt: Flattened device tree for the kdump kernel. + * @dn: Memory node. + * @um_info: Usable memory buffer and ranges info. + * + * Returns 0 on success, negative errno on error. + */ +static int add_usable_mem_property(void *fdt, struct device_node *dn, + struct umem_info *um_info) +{ + int n_mem_addr_cells, n_mem_size_cells, node; + char path[NODE_PATH_LEN]; + int i, len, ranges, ret; + const __be32 *prop; + u64 base, end; + + of_node_get(dn); + + if (snprintf(path, NODE_PATH_LEN, "%pOF", dn) > (NODE_PATH_LEN - 1)) { + pr_err("Buffer (%d) too small for memory node: %pOF\n", + NODE_PATH_LEN, dn); + return -EOVERFLOW; + } + pr_debug("Memory node path: %s\n", path); + + /* Now that we know the path, find its offset in kdump kernel's fdt */ + node = fdt_path_offset(fdt, path); + if (node < 0) { + pr_err("Malformed device tree: error reading %s\n", path); + ret = -EINVAL; + goto out; + } + + /* Get the address & size cells */ + n_mem_addr_cells = of_n_addr_cells(dn); + n_mem_size_cells = of_n_size_cells(dn); + pr_debug("address cells: %d, size cells: %d\n", n_mem_addr_cells, + n_mem_size_cells); + + um_info->idx = 0; + if (!check_realloc_usable_mem(um_info, 2)) { + ret = -ENOMEM; + goto out; + } + + prop = of_get_property(dn, "reg", &len); + if (!prop || len <= 0) { + ret = 0; + goto out; + } + + /* + * "reg" property represents sequence of (addr,size) tuples + * each representing a memory range. + */ + ranges = (len >> 2) / (n_mem_addr_cells + n_mem_size_cells); + + for (i = 0; i < ranges; i++) { + base = of_read_number(prop, n_mem_addr_cells); + prop += n_mem_addr_cells; + end = base + of_read_number(prop, n_mem_size_cells) - 1; + prop += n_mem_size_cells; + + ret = add_usable_mem(um_info, base, end); + if (ret) + goto out; + } + + /* + * No kdump kernel usable memory found in this memory node. + * Write (0,0) tuple in linux,usable-memory property for + * this region to be ignored. + */ + if (um_info->idx == 0) { + um_info->buf[0] = 0; + um_info->buf[1] = 0; + um_info->idx = 2; + } + + ret = fdt_setprop(fdt, node, "linux,usable-memory", um_info->buf, + (um_info->idx * sizeof(u64))); + +out: + of_node_put(dn); + return ret; +} + + +/** + * update_usable_mem_fdt - Updates kdump kernel's fdt with linux,usable-memory + * and linux,drconf-usable-memory DT properties as + * appropriate to restrict its memory usage. + * @fdt: Flattened device tree for the kdump kernel. + * @usable_mem: Usable memory ranges for kdump kernel. + * + * Returns 0 on success, negative errno on error. + */ +static int update_usable_mem_fdt(void *fdt, struct crash_mem *usable_mem) +{ + struct umem_info um_info; + struct device_node *dn; + int node, ret = 0; + + if (!usable_mem) { + pr_err("Usable memory ranges for kdump kernel not found\n"); + return -ENOENT; + } + + node = fdt_path_offset(fdt, "/ibm,dynamic-reconfiguration-memory"); + if (node == -FDT_ERR_NOTFOUND) + pr_debug("No dynamic reconfiguration memory found\n"); + else if (node < 0) { + pr_err("Malformed device tree: error reading /ibm,dynamic-reconfiguration-memory.\n"); + return -EINVAL; + } + + um_info.buf = NULL; + um_info.size = 0; + um_info.max_entries = 0; + um_info.idx = 0; + /* Memory ranges to look up */ + um_info.ranges = &(usable_mem->ranges[0]); + um_info.nr_ranges = usable_mem->nr_ranges; + + dn = of_find_node_by_path("/ibm,dynamic-reconfiguration-memory"); + if (dn) { + ret = walk_drmem_lmbs(dn, &um_info, kdump_setup_usable_lmb); + of_node_put(dn); + + if (ret) { + pr_err("Could not setup linux,drconf-usable-memory property for kdump\n"); + goto out; + } + + ret = fdt_setprop(fdt, node, "linux,drconf-usable-memory", + um_info.buf, (um_info.idx * sizeof(u64))); + if (ret) { + pr_err("Failed to update fdt with linux,drconf-usable-memory property"); + goto out; + } + } + + /* + * Walk through each memory node and set linux,usable-memory property + * for the corresponding node in kdump kernel's fdt. + */ + for_each_node_by_type(dn, "memory") { + ret = add_usable_mem_property(fdt, dn, &um_info); + if (ret) { + pr_err("Failed to set linux,usable-memory property for %s node", + dn->full_name); + goto out; + } + } + +out: + kfree(um_info.buf); + return ret; +} + /** * setup_purgatory_ppc64 - initialize PPC64 specific purgatory's global * variables and call setup_purgatory() to initialize @@ -293,6 +625,25 @@ int setup_purgatory_ppc64(struct kimage *image, const void *slave_code, ret = setup_purgatory(image, slave_code, fdt, kernel_load_addr, fdt_load_addr); + if (ret) + goto out; + + if (image->type == KEXEC_TYPE_CRASH) { + u32 my_run_at_load = 1; + + /* + * Tell relocatable kernel to run at load address + * via the word meant for that at 0x5c. + */ + ret = kexec_purgatory_get_set_symbol(image, "run_at_load", + &my_run_at_load, + sizeof(my_run_at_load), + false); + if (ret) + goto out; + } + +out: if (ret) pr_err("Failed to setup purgatory symbols"); return ret; @@ -314,7 +665,40 @@ int setup_new_fdt_ppc64(const struct kimage *image, void *fdt, unsigned long initrd_load_addr, unsigned long initrd_len, const char *cmdline) { - return setup_new_fdt(image, fdt, initrd_load_addr, initrd_len, cmdline); + struct crash_mem *umem = NULL; + int ret; + + ret = setup_new_fdt(image, fdt, initrd_load_addr, initrd_len, cmdline); + if (ret) + goto out; + + /* + * Restrict memory usage for kdump kernel by setting up + * usable memory ranges. + */ + if (image->type == KEXEC_TYPE_CRASH) { + ret = get_usable_memory_ranges(&umem); + if (ret) + goto out; + + ret = update_usable_mem_fdt(fdt, umem); + if (ret) { + pr_err("Error setting up usable-memory property for kdump kernel\n"); + goto out; + } + + /* Ensure we don't touch crashed kernel's memory */ + ret = fdt_add_mem_rsv(fdt, 0, crashk_res.start); + if (ret) { + pr_err("Error reserving crash memory: %s\n", + fdt_strerror(ret)); + goto out; + } + } + +out: + kfree(umem); + return ret; } /** -- GitLab From 1a1cf93c200581c72a3cd521e1e0a1a3b5d0077d Mon Sep 17 00:00:00 2001 From: Hari Bathini Date: Wed, 29 Jul 2020 17:12:58 +0530 Subject: [PATCH 1108/1476] powerpc/kexec_file: Setup backup region for kdump kernel Though kdump kernel boots from loaded address, the first 64KB of it is copied down to real 0. So, setup a backup region and let purgatory copy the first 64KB of crashed kernel into this backup region before booting into kdump kernel. Update reserve map with backup region and crashed kernel's memory to avoid kdump kernel from accidentially using that memory. Signed-off-by: Hari Bathini Reviewed-by: Thiago Jung Bauermann Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/159602294718.575379.16216507537038008623.stgit@hbathini --- arch/powerpc/include/asm/crashdump-ppc64.h | 19 +++++ arch/powerpc/include/asm/kexec.h | 7 ++ arch/powerpc/kexec/elf_64.c | 9 +++ arch/powerpc/kexec/file_load_64.c | 93 +++++++++++++++++++++- arch/powerpc/purgatory/trampoline_64.S | 38 ++++++++- 5 files changed, 159 insertions(+), 7 deletions(-) create mode 100644 arch/powerpc/include/asm/crashdump-ppc64.h diff --git a/arch/powerpc/include/asm/crashdump-ppc64.h b/arch/powerpc/include/asm/crashdump-ppc64.h new file mode 100644 index 0000000000000..68d9717cc5ee9 --- /dev/null +++ b/arch/powerpc/include/asm/crashdump-ppc64.h @@ -0,0 +1,19 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +#ifndef _ASM_POWERPC_CRASHDUMP_PPC64_H +#define _ASM_POWERPC_CRASHDUMP_PPC64_H + +/* + * Backup region - first 64KB of System RAM + * + * If ever the below macros are to be changed, please be judicious. + * The implicit assumptions are: + * - start, end & size are less than UINT32_MAX. + * - start & size are at least 8 byte aligned. + * + * For implementation details: arch/powerpc/purgatory/trampoline_64.S + */ +#define BACKUP_SRC_START 0 +#define BACKUP_SRC_END 0xffff +#define BACKUP_SRC_SIZE (BACKUP_SRC_END - BACKUP_SRC_START + 1) + +#endif /* __ASM_POWERPC_CRASHDUMP_PPC64_H */ diff --git a/arch/powerpc/include/asm/kexec.h b/arch/powerpc/include/asm/kexec.h index 639f31e13593a..807bb58ed8faf 100644 --- a/arch/powerpc/include/asm/kexec.h +++ b/arch/powerpc/include/asm/kexec.h @@ -105,6 +105,9 @@ extern const struct kexec_file_ops kexec_elf64_ops; struct kimage_arch { struct crash_mem *exclude_ranges; + unsigned long backup_start; + void *backup_buf; + #ifdef CONFIG_IMA_KEXEC phys_addr_t ima_buffer_addr; size_t ima_buffer_size; @@ -120,6 +123,10 @@ int setup_new_fdt(const struct kimage *image, void *fdt, int delete_fdt_mem_rsv(void *fdt, unsigned long start, unsigned long size); #ifdef CONFIG_PPC64 +struct kexec_buf; + +int load_crashdump_segments_ppc64(struct kimage *image, + struct kexec_buf *kbuf); int setup_purgatory_ppc64(struct kimage *image, const void *slave_code, const void *fdt, unsigned long kernel_load_addr, unsigned long fdt_load_addr); diff --git a/arch/powerpc/kexec/elf_64.c b/arch/powerpc/kexec/elf_64.c index 64c15a5a280b3..76e2fc7e6dc30 100644 --- a/arch/powerpc/kexec/elf_64.c +++ b/arch/powerpc/kexec/elf_64.c @@ -68,6 +68,15 @@ static void *elf64_load(struct kimage *image, char *kernel_buf, pr_debug("Loaded purgatory at 0x%lx\n", pbuf.mem); + /* Load additional segments needed for panic kernel */ + if (image->type == KEXEC_TYPE_CRASH) { + ret = load_crashdump_segments_ppc64(image, &kbuf); + if (ret) { + pr_err("Failed to load kdump kernel segments\n"); + goto out; + } + } + if (initrd != NULL) { kbuf.buffer = initrd; kbuf.bufsz = kbuf.memsz = initrd_len; diff --git a/arch/powerpc/kexec/file_load_64.c b/arch/powerpc/kexec/file_load_64.c index f946608747657..a81bffb72cc53 100644 --- a/arch/powerpc/kexec/file_load_64.c +++ b/arch/powerpc/kexec/file_load_64.c @@ -20,8 +20,10 @@ #include #include #include +#include #include #include +#include struct umem_info { u64 *buf; /* data buffer for usable-memory property */ @@ -605,6 +607,70 @@ out: return ret; } +/** + * load_backup_segment - Locate a memory hole to place the backup region. + * @image: Kexec image. + * @kbuf: Buffer contents and memory parameters. + * + * Returns 0 on success, negative errno on error. + */ +static int load_backup_segment(struct kimage *image, struct kexec_buf *kbuf) +{ + void *buf; + int ret; + + /* + * Setup a source buffer for backup segment. + * + * A source buffer has no meaning for backup region as data will + * be copied from backup source, after crash, in the purgatory. + * But as load segment code doesn't recognize such segments, + * setup a dummy source buffer to keep it happy for now. + */ + buf = vzalloc(BACKUP_SRC_SIZE); + if (!buf) + return -ENOMEM; + + kbuf->buffer = buf; + kbuf->mem = KEXEC_BUF_MEM_UNKNOWN; + kbuf->bufsz = kbuf->memsz = BACKUP_SRC_SIZE; + kbuf->top_down = false; + + ret = kexec_add_buffer(kbuf); + if (ret) { + vfree(buf); + return ret; + } + + image->arch.backup_buf = buf; + image->arch.backup_start = kbuf->mem; + return 0; +} + +/** + * load_crashdump_segments_ppc64 - Initialize the additional segements needed + * to load kdump kernel. + * @image: Kexec image. + * @kbuf: Buffer contents and memory parameters. + * + * Returns 0 on success, negative errno on error. + */ +int load_crashdump_segments_ppc64(struct kimage *image, + struct kexec_buf *kbuf) +{ + int ret; + + /* Load backup segment - first 64K bytes of the crashing kernel */ + ret = load_backup_segment(image, kbuf); + if (ret) { + pr_err("Failed to load backup segment\n"); + return ret; + } + pr_debug("Loaded the backup region at 0x%lx\n", kbuf->mem); + + return 0; +} + /** * setup_purgatory_ppc64 - initialize PPC64 specific purgatory's global * variables and call setup_purgatory() to initialize @@ -643,6 +709,11 @@ int setup_purgatory_ppc64(struct kimage *image, const void *slave_code, goto out; } + /* Tell purgatory where to look for backup region */ + ret = kexec_purgatory_get_set_symbol(image, "backup_start", + &image->arch.backup_start, + sizeof(image->arch.backup_start), + false); out: if (ret) pr_err("Failed to setup purgatory symbols"); @@ -674,7 +745,7 @@ int setup_new_fdt_ppc64(const struct kimage *image, void *fdt, /* * Restrict memory usage for kdump kernel by setting up - * usable memory ranges. + * usable memory ranges and memory reserve map. */ if (image->type == KEXEC_TYPE_CRASH) { ret = get_usable_memory_ranges(&umem); @@ -687,13 +758,26 @@ int setup_new_fdt_ppc64(const struct kimage *image, void *fdt, goto out; } - /* Ensure we don't touch crashed kernel's memory */ - ret = fdt_add_mem_rsv(fdt, 0, crashk_res.start); + /* + * Ensure we don't touch crashed kernel's memory except the + * first 64K of RAM, which will be backed up. + */ + ret = fdt_add_mem_rsv(fdt, BACKUP_SRC_END + 1, + crashk_res.start - BACKUP_SRC_SIZE); if (ret) { pr_err("Error reserving crash memory: %s\n", fdt_strerror(ret)); goto out; } + + /* Ensure backup region is not used by kdump/capture kernel */ + ret = fdt_add_mem_rsv(fdt, image->arch.backup_start, + BACKUP_SRC_SIZE); + if (ret) { + pr_err("Error reserving memory for backup: %s\n", + fdt_strerror(ret)); + goto out; + } } out: @@ -800,5 +884,8 @@ int arch_kimage_file_post_load_cleanup(struct kimage *image) kfree(image->arch.exclude_ranges); image->arch.exclude_ranges = NULL; + vfree(image->arch.backup_buf); + image->arch.backup_buf = NULL; + return kexec_image_post_load_cleanup_default(image); } diff --git a/arch/powerpc/purgatory/trampoline_64.S b/arch/powerpc/purgatory/trampoline_64.S index a5a83c3f53e6b..b567da56dde10 100644 --- a/arch/powerpc/purgatory/trampoline_64.S +++ b/arch/powerpc/purgatory/trampoline_64.S @@ -10,6 +10,7 @@ */ #include +#include .machine ppc64 .balign 256 @@ -43,14 +44,39 @@ master: mr %r17,%r3 /* save cpu id to r17 */ mr %r15,%r4 /* save physical address in reg15 */ + /* Work out where we're running */ + bcl 20, 31, 0f +0: mflr %r18 + + /* + * Copy BACKUP_SRC_SIZE bytes from BACKUP_SRC_START to + * backup_start 8 bytes at a time. + * + * Use r3 = dest, r4 = src, r5 = size, r6 = count + */ + ld %r3, (backup_start - 0b)(%r18) + cmpdi %cr0, %r3, 0 + beq .Lskip_copy /* skip if there is no backup region */ + lis %r5, BACKUP_SRC_SIZE@h + ori %r5, %r5, BACKUP_SRC_SIZE@l + cmpdi %cr0, %r5, 0 + beq .Lskip_copy /* skip if copy size is zero */ + lis %r4, BACKUP_SRC_START@h + ori %r4, %r4, BACKUP_SRC_START@l + li %r6, 0 +.Lcopy_loop: + ldx %r0, %r6, %r4 + stdx %r0, %r6, %r3 + addi %r6, %r6, 8 + cmpld %cr0, %r6, %r5 + blt .Lcopy_loop + +.Lskip_copy: or %r3,%r3,%r3 /* ok now to high priority, lets boot */ lis %r6,0x1 mtctr %r6 /* delay a bit for slaves to catch up */ bdnz . /* before we overwrite 0-100 again */ - bl 0f /* Work out where we're running */ -0: mflr %r18 - /* load device-tree address */ ld %r3, (dt_offset - 0b)(%r18) mr %r16,%r3 /* save dt address in reg16 */ @@ -89,7 +115,6 @@ master: rfid /* update MSR and start kernel */ - .balign 8 .globl kernel kernel: @@ -102,6 +127,11 @@ dt_offset: .8byte 0x0 .size dt_offset, . - dt_offset + .balign 8 + .globl backup_start +backup_start: + .8byte 0x0 + .size backup_start, . - backup_start .data .balign 8 -- GitLab From cb350c1f1f867db16725f1bb06be033ece19e998 Mon Sep 17 00:00:00 2001 From: Hari Bathini Date: Wed, 29 Jul 2020 17:13:14 +0530 Subject: [PATCH 1109/1476] powerpc/kexec_file: Prepare elfcore header for crashing kernel Prepare elf headers for the crashing kernel's core file using crash_prepare_elf64_headers() and pass on this info to kdump kernel by updating its command line with elfcorehdr parameter. Also, add elfcorehdr location to reserve map to avoid it from being stomped on while booting. Signed-off-by: Hari Bathini Tested-by: Pingfan Liu Reviewed-by: Thiago Jung Bauermann [mpe: Ensure cmdline is nul terminated] Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/159602298855.575379.15819225623219909517.stgit@hbathini --- arch/powerpc/include/asm/kexec.h | 6 ++ arch/powerpc/kexec/elf_64.c | 12 +++ arch/powerpc/kexec/file_load.c | 51 +++++++++ arch/powerpc/kexec/file_load_64.c | 165 ++++++++++++++++++++++++++++++ 4 files changed, 234 insertions(+) diff --git a/arch/powerpc/include/asm/kexec.h b/arch/powerpc/include/asm/kexec.h index 807bb58ed8faf..55d6ede30c19a 100644 --- a/arch/powerpc/include/asm/kexec.h +++ b/arch/powerpc/include/asm/kexec.h @@ -108,12 +108,18 @@ struct kimage_arch { unsigned long backup_start; void *backup_buf; + unsigned long elfcorehdr_addr; + unsigned long elf_headers_sz; + void *elf_headers; + #ifdef CONFIG_IMA_KEXEC phys_addr_t ima_buffer_addr; size_t ima_buffer_size; #endif }; +char *setup_kdump_cmdline(struct kimage *image, char *cmdline, + unsigned long cmdline_len); int setup_purgatory(struct kimage *image, const void *slave_code, const void *fdt, unsigned long kernel_load_addr, unsigned long fdt_load_addr); diff --git a/arch/powerpc/kexec/elf_64.c b/arch/powerpc/kexec/elf_64.c index 76e2fc7e6dc30..d0e459bb2f05a 100644 --- a/arch/powerpc/kexec/elf_64.c +++ b/arch/powerpc/kexec/elf_64.c @@ -35,6 +35,7 @@ static void *elf64_load(struct kimage *image, char *kernel_buf, void *fdt; const void *slave_code; struct elfhdr ehdr; + char *modified_cmdline = NULL; struct kexec_elf_info elf_info; struct kexec_buf kbuf = { .image = image, .buf_min = 0, .buf_max = ppc64_rma_size }; @@ -75,6 +76,16 @@ static void *elf64_load(struct kimage *image, char *kernel_buf, pr_err("Failed to load kdump kernel segments\n"); goto out; } + + /* Setup cmdline for kdump kernel case */ + modified_cmdline = setup_kdump_cmdline(image, cmdline, + cmdline_len); + if (!modified_cmdline) { + pr_err("Setting up cmdline for kdump kernel failed\n"); + ret = -EINVAL; + goto out; + } + cmdline = modified_cmdline; } if (initrd != NULL) { @@ -131,6 +142,7 @@ static void *elf64_load(struct kimage *image, char *kernel_buf, pr_err("Error setting up the purgatory.\n"); out: + kfree(modified_cmdline); kexec_free_elf_info(&elf_info); /* Make kimage_file_post_load_cleanup free the fdt buffer for us. */ diff --git a/arch/powerpc/kexec/file_load.c b/arch/powerpc/kexec/file_load.c index 38439aba27d7e..9a232bc36c8f9 100644 --- a/arch/powerpc/kexec/file_load.c +++ b/arch/powerpc/kexec/file_load.c @@ -18,10 +18,47 @@ #include #include #include +#include #include #define SLAVE_CODE_SIZE 256 /* First 0x100 bytes */ +/** + * setup_kdump_cmdline - Prepend "elfcorehdr= " to command line + * of kdump kernel for exporting the core. + * @image: Kexec image + * @cmdline: Command line parameters to update. + * @cmdline_len: Length of the cmdline parameters. + * + * kdump segment must be setup before calling this function. + * + * Returns new cmdline buffer for kdump kernel on success, NULL otherwise. + */ +char *setup_kdump_cmdline(struct kimage *image, char *cmdline, + unsigned long cmdline_len) +{ + int elfcorehdr_strlen; + char *cmdline_ptr; + + cmdline_ptr = kzalloc(COMMAND_LINE_SIZE, GFP_KERNEL); + if (!cmdline_ptr) + return NULL; + + elfcorehdr_strlen = sprintf(cmdline_ptr, "elfcorehdr=0x%lx ", + image->arch.elfcorehdr_addr); + + if (elfcorehdr_strlen + cmdline_len > COMMAND_LINE_SIZE) { + pr_err("Appending elfcorehdr= exceeds cmdline size\n"); + kfree(cmdline_ptr); + return NULL; + } + + memcpy(cmdline_ptr + elfcorehdr_strlen, cmdline, cmdline_len); + // Ensure it's nul terminated + cmdline_ptr[COMMAND_LINE_SIZE - 1] = '\0'; + return cmdline_ptr; +} + /** * setup_purgatory - initialize the purgatory's global variables * @image: kexec image. @@ -221,6 +258,20 @@ int setup_new_fdt(const struct kimage *image, void *fdt, } } + if (image->type == KEXEC_TYPE_CRASH) { + /* + * Avoid elfcorehdr from being stomped on in kdump kernel by + * setting up memory reserve map. + */ + ret = fdt_add_mem_rsv(fdt, image->arch.elfcorehdr_addr, + image->arch.elf_headers_sz); + if (ret) { + pr_err("Error reserving elfcorehdr memory: %s\n", + fdt_strerror(ret)); + goto err; + } + } + ret = setup_ima_buffer(image, fdt, chosen_node); if (ret) { pr_err("Error setting up the new device tree.\n"); diff --git a/arch/powerpc/kexec/file_load_64.c b/arch/powerpc/kexec/file_load_64.c index a81bffb72cc53..0d280d097cd6f 100644 --- a/arch/powerpc/kexec/file_load_64.c +++ b/arch/powerpc/kexec/file_load_64.c @@ -128,6 +128,83 @@ out: return ret; } +/** + * get_crash_memory_ranges - Get crash memory ranges. This list includes + * first/crashing kernel's memory regions that + * would be exported via an elfcore. + * @mem_ranges: Range list to add the memory ranges to. + * + * Returns 0 on success, negative errno on error. + */ +static int get_crash_memory_ranges(struct crash_mem **mem_ranges) +{ + struct memblock_region *reg; + struct crash_mem *tmem; + int ret; + + for_each_memblock(memory, reg) { + u64 base, size; + + base = (u64)reg->base; + size = (u64)reg->size; + + /* Skip backup memory region, which needs a separate entry */ + if (base == BACKUP_SRC_START) { + if (size > BACKUP_SRC_SIZE) { + base = BACKUP_SRC_END + 1; + size -= BACKUP_SRC_SIZE; + } else + continue; + } + + ret = add_mem_range(mem_ranges, base, size); + if (ret) + goto out; + + /* Try merging adjacent ranges before reallocation attempt */ + if ((*mem_ranges)->nr_ranges == (*mem_ranges)->max_nr_ranges) + sort_memory_ranges(*mem_ranges, true); + } + + /* Reallocate memory ranges if there is no space to split ranges */ + tmem = *mem_ranges; + if (tmem && (tmem->nr_ranges == tmem->max_nr_ranges)) { + tmem = realloc_mem_ranges(mem_ranges); + if (!tmem) + goto out; + } + + /* Exclude crashkernel region */ + ret = crash_exclude_mem_range(tmem, crashk_res.start, crashk_res.end); + if (ret) + goto out; + + /* + * FIXME: For now, stay in parity with kexec-tools but if RTAS/OPAL + * regions are exported to save their context at the time of + * crash, they should actually be backed up just like the + * first 64K bytes of memory. + */ + ret = add_rtas_mem_range(mem_ranges); + if (ret) + goto out; + + ret = add_opal_mem_range(mem_ranges); + if (ret) + goto out; + + /* create a separate program header for the backup region */ + ret = add_mem_range(mem_ranges, BACKUP_SRC_START, BACKUP_SRC_SIZE); + if (ret) + goto out; + + sort_memory_ranges(*mem_ranges, false); +out: + if (ret) + pr_err("Failed to setup crash memory ranges\n"); + return ret; +} + /** * __locate_mem_hole_top_down - Looks top down for a large enough memory hole * in the memory regions between buf_min & buf_max @@ -647,6 +724,81 @@ static int load_backup_segment(struct kimage *image, struct kexec_buf *kbuf) return 0; } +/** + * update_backup_region_phdr - Update backup region's offset for the core to + * export the region appropriately. + * @image: Kexec image. + * @ehdr: ELF core header. + * + * Assumes an exclusive program header is setup for the backup region + * in the ELF headers + * + * Returns nothing. + */ +static void update_backup_region_phdr(struct kimage *image, Elf64_Ehdr *ehdr) +{ + Elf64_Phdr *phdr; + unsigned int i; + + phdr = (Elf64_Phdr *)(ehdr + 1); + for (i = 0; i < ehdr->e_phnum; i++) { + if (phdr->p_paddr == BACKUP_SRC_START) { + phdr->p_offset = image->arch.backup_start; + pr_debug("Backup region offset updated to 0x%lx\n", + image->arch.backup_start); + return; + } + } +} + +/** + * load_elfcorehdr_segment - Setup crash memory ranges and initialize elfcorehdr + * segment needed to load kdump kernel. + * @image: Kexec image. + * @kbuf: Buffer contents and memory parameters. + * + * Returns 0 on success, negative errno on error. + */ +static int load_elfcorehdr_segment(struct kimage *image, struct kexec_buf *kbuf) +{ + struct crash_mem *cmem = NULL; + unsigned long headers_sz; + void *headers = NULL; + int ret; + + ret = get_crash_memory_ranges(&cmem); + if (ret) + goto out; + + /* Setup elfcorehdr segment */ + ret = crash_prepare_elf64_headers(cmem, false, &headers, &headers_sz); + if (ret) { + pr_err("Failed to prepare elf headers for the core\n"); + goto out; + } + + /* Fix the offset for backup region in the ELF header */ + update_backup_region_phdr(image, headers); + + kbuf->buffer = headers; + kbuf->mem = KEXEC_BUF_MEM_UNKNOWN; + kbuf->bufsz = kbuf->memsz = headers_sz; + kbuf->top_down = false; + + ret = kexec_add_buffer(kbuf); + if (ret) { + vfree(headers); + goto out; + } + + image->arch.elfcorehdr_addr = kbuf->mem; + image->arch.elf_headers_sz = headers_sz; + image->arch.elf_headers = headers; +out: + kfree(cmem); + return ret; +} + /** * load_crashdump_segments_ppc64 - Initialize the additional segements needed * to load kdump kernel. @@ -668,6 +820,15 @@ int load_crashdump_segments_ppc64(struct kimage *image, } pr_debug("Loaded the backup region at 0x%lx\n", kbuf->mem); + /* Load elfcorehdr segment - to export crashing kernel's vmcore */ + ret = load_elfcorehdr_segment(image, kbuf); + if (ret) { + pr_err("Failed to load elfcorehdr segment\n"); + return ret; + } + pr_debug("Loaded elf core header at 0x%lx, bufsz=0x%lx memsz=0x%lx\n", + image->arch.elfcorehdr_addr, kbuf->bufsz, kbuf->memsz); + return 0; } @@ -887,5 +1048,9 @@ int arch_kimage_file_post_load_cleanup(struct kimage *image) vfree(image->arch.backup_buf); image->arch.backup_buf = NULL; + vfree(image->arch.elf_headers); + image->arch.elf_headers = NULL; + image->arch.elf_headers_sz = 0; + return kexec_image_post_load_cleanup_default(image); } -- GitLab From 6ecd0163d36049b5f2435a8658f1320c9f3f2924 Mon Sep 17 00:00:00 2001 From: Hari Bathini Date: Wed, 29 Jul 2020 17:13:33 +0530 Subject: [PATCH 1110/1476] powerpc/kexec_file: Add appropriate regions for memory reserve map While initrd, elfcorehdr and backup regions are already added to the reserve map, there are a few missing regions that need to be added to the memory reserve map. Add them here. And now that all the changes to load panic kernel are in place, claim likewise. Signed-off-by: Hari Bathini Tested-by: Pingfan Liu Reviewed-by: Thiago Jung Bauermann Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/159602300473.575379.4218568032039284448.stgit@hbathini --- arch/powerpc/kexec/file_load_64.c | 58 ++++++++++++++++++++++++++++--- 1 file changed, 53 insertions(+), 5 deletions(-) diff --git a/arch/powerpc/kexec/file_load_64.c b/arch/powerpc/kexec/file_load_64.c index 0d280d097cd6f..f13c5b8399e14 100644 --- a/arch/powerpc/kexec/file_load_64.c +++ b/arch/powerpc/kexec/file_load_64.c @@ -205,6 +205,34 @@ out: return ret; } +/** + * get_reserved_memory_ranges - Get reserve memory ranges. This list includes + * memory regions that should be added to the + * memory reserve map to ensure the region is + * protected from any mischief. + * @mem_ranges: Range list to add the memory ranges to. + * + * Returns 0 on success, negative errno on error. + */ +static int get_reserved_memory_ranges(struct crash_mem **mem_ranges) +{ + int ret; + + ret = add_rtas_mem_range(mem_ranges); + if (ret) + goto out; + + ret = add_tce_mem_ranges(mem_ranges); + if (ret) + goto out; + + ret = add_reserved_mem_ranges(mem_ranges); +out: + if (ret) + pr_err("Failed to setup reserved memory ranges\n"); + return ret; +} + /** * __locate_mem_hole_top_down - Looks top down for a large enough memory hole * in the memory regions between buf_min & buf_max @@ -897,8 +925,8 @@ int setup_new_fdt_ppc64(const struct kimage *image, void *fdt, unsigned long initrd_load_addr, unsigned long initrd_len, const char *cmdline) { - struct crash_mem *umem = NULL; - int ret; + struct crash_mem *umem = NULL, *rmem = NULL; + int i, nr_ranges, ret; ret = setup_new_fdt(image, fdt, initrd_load_addr, initrd_len, cmdline); if (ret) @@ -941,7 +969,27 @@ int setup_new_fdt_ppc64(const struct kimage *image, void *fdt, } } + /* Update memory reserve map */ + ret = get_reserved_memory_ranges(&rmem); + if (ret) + goto out; + + nr_ranges = rmem ? rmem->nr_ranges : 0; + for (i = 0; i < nr_ranges; i++) { + u64 base, size; + + base = rmem->ranges[i].start; + size = rmem->ranges[i].end - base + 1; + ret = fdt_add_mem_rsv(fdt, base, size); + if (ret) { + pr_err("Error updating memory reserve map: %s\n", + fdt_strerror(ret)); + goto out; + } + } + out: + kfree(rmem); kfree(umem); return ret; } @@ -1024,10 +1072,10 @@ int arch_kexec_kernel_image_probe(struct kimage *image, void *buf, /* Get exclude memory ranges needed for setting up kdump segments */ ret = get_exclude_memory_ranges(&(image->arch.exclude_ranges)); - if (ret) + if (ret) { pr_err("Failed to setup exclude memory ranges for buffer lookup\n"); - /* Return this until all changes for panic kernel are in */ - return -EOPNOTSUPP; + return ret; + } } return kexec_image_probe_default(image, buf, buf_len); -- GitLab From b5667d13be8d0928a02b46e0c6f7ab891d32f697 Mon Sep 17 00:00:00 2001 From: Hari Bathini Date: Wed, 29 Jul 2020 17:13:49 +0530 Subject: [PATCH 1111/1476] powerpc/kexec_file: Fix kexec load failure with lack of memory hole The kexec purgatory has to run in real mode. Only the first memory block maybe accessible in real mode. And, unlike the case with panic kernel, no memory is set aside for regular kexec load. Another thing to note is, the memory for crashkernel is reserved at an offset of 128MB. So, when crashkernel memory is reserved, the memory ranges to load kexec segments shrink further as the generic code only looks for memblock free memory ranges and in all likelihood only a tiny bit of memory from 0 to 128MB would be available to load kexec segments. With kdump being used by default in general, kexec file load is likely to fail almost always. This can be fixed by changing the memory hole lookup logic for regular kexec to use the same method as kdump. This would mean that most kexec segments will overlap with crashkernel memory region. That should still be ok as the pages, whose destination address isn't available while loading, are placed in an intermediate location till a flush to the actual destination address happens during kexec boot sequence. Signed-off-by: Hari Bathini Tested-by: Pingfan Liu Reviewed-by: Thiago Jung Bauermann Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/159602302326.575379.14038896654942043093.stgit@hbathini --- arch/powerpc/kexec/file_load_64.c | 33 +++++++++++++------------------ 1 file changed, 14 insertions(+), 19 deletions(-) diff --git a/arch/powerpc/kexec/file_load_64.c b/arch/powerpc/kexec/file_load_64.c index f13c5b8399e14..c6a37ad5a0a4c 100644 --- a/arch/powerpc/kexec/file_load_64.c +++ b/arch/powerpc/kexec/file_load_64.c @@ -1012,13 +1012,6 @@ int arch_kexec_locate_mem_hole(struct kexec_buf *kbuf) u64 buf_min, buf_max; int ret; - /* - * Use the generic kexec_locate_mem_hole for regular - * kexec_file_load syscall - */ - if (kbuf->image->type != KEXEC_TYPE_CRASH) - return kexec_locate_mem_hole(kbuf); - /* Look up the exclude ranges list while locating the memory hole */ emem = &(kbuf->image->arch.exclude_ranges); if (!(*emem) || ((*emem)->nr_ranges == 0)) { @@ -1026,11 +1019,15 @@ int arch_kexec_locate_mem_hole(struct kexec_buf *kbuf) return kexec_locate_mem_hole(kbuf); } + buf_min = kbuf->buf_min; + buf_max = kbuf->buf_max; /* Segments for kdump kernel should be within crashkernel region */ - buf_min = (kbuf->buf_min < crashk_res.start ? - crashk_res.start : kbuf->buf_min); - buf_max = (kbuf->buf_max > crashk_res.end ? - crashk_res.end : kbuf->buf_max); + if (kbuf->image->type == KEXEC_TYPE_CRASH) { + buf_min = (buf_min < crashk_res.start ? + crashk_res.start : buf_min); + buf_max = (buf_max > crashk_res.end ? + crashk_res.end : buf_max); + } if (buf_min > buf_max) { pr_err("Invalid buffer min and/or max values\n"); @@ -1067,15 +1064,13 @@ int arch_kexec_locate_mem_hole(struct kexec_buf *kbuf) int arch_kexec_kernel_image_probe(struct kimage *image, void *buf, unsigned long buf_len) { - if (image->type == KEXEC_TYPE_CRASH) { - int ret; + int ret; - /* Get exclude memory ranges needed for setting up kdump segments */ - ret = get_exclude_memory_ranges(&(image->arch.exclude_ranges)); - if (ret) { - pr_err("Failed to setup exclude memory ranges for buffer lookup\n"); - return ret; - } + /* Get exclude memory ranges needed for setting up kexec segments */ + ret = get_exclude_memory_ranges(&(image->arch.exclude_ranges)); + if (ret) { + pr_err("Failed to setup exclude memory ranges for buffer lookup\n"); + return ret; } return kexec_image_probe_default(image, buf, buf_len); -- GitLab From 2e6bd221d96fcfd9bd1eed5cd9c008e7959daed7 Mon Sep 17 00:00:00 2001 From: Hari Bathini Date: Wed, 29 Jul 2020 17:14:06 +0530 Subject: [PATCH 1112/1476] powerpc/kexec_file: Enable early kernel OPAL calls Kernels built with CONFIG_PPC_EARLY_DEBUG_OPAL enabled expects r8 & r9 to be filled with OPAL base & entry addresses respectively. Setting these registers allows the kernel to perform OPAL calls before the device tree is parsed. Signed-off-by: Hari Bathini Reviewed-by: Thiago Jung Bauermann Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/159602303975.575379.5032301944162937479.stgit@hbathini --- arch/powerpc/kexec/file_load_64.c | 20 ++++++++++++++++++++ arch/powerpc/purgatory/trampoline_64.S | 16 ++++++++++++++++ 2 files changed, 36 insertions(+) diff --git a/arch/powerpc/kexec/file_load_64.c b/arch/powerpc/kexec/file_load_64.c index c6a37ad5a0a4c..53bb71e3a2e1c 100644 --- a/arch/powerpc/kexec/file_load_64.c +++ b/arch/powerpc/kexec/file_load_64.c @@ -876,6 +876,7 @@ int setup_purgatory_ppc64(struct kimage *image, const void *slave_code, const void *fdt, unsigned long kernel_load_addr, unsigned long fdt_load_addr) { + struct device_node *dn = NULL; int ret; ret = setup_purgatory(image, slave_code, fdt, kernel_load_addr, @@ -903,9 +904,28 @@ int setup_purgatory_ppc64(struct kimage *image, const void *slave_code, &image->arch.backup_start, sizeof(image->arch.backup_start), false); + if (ret) + goto out; + + /* Setup OPAL base & entry values */ + dn = of_find_node_by_path("/ibm,opal"); + if (dn) { + u64 val; + + of_property_read_u64(dn, "opal-base-address", &val); + ret = kexec_purgatory_get_set_symbol(image, "opal_base", &val, + sizeof(val), false); + if (ret) + goto out; + + of_property_read_u64(dn, "opal-entry-address", &val); + ret = kexec_purgatory_get_set_symbol(image, "opal_entry", &val, + sizeof(val), false); + } out: if (ret) pr_err("Failed to setup purgatory symbols"); + of_node_put(dn); return ret; } diff --git a/arch/powerpc/purgatory/trampoline_64.S b/arch/powerpc/purgatory/trampoline_64.S index b567da56dde10..d956b8a35fd13 100644 --- a/arch/powerpc/purgatory/trampoline_64.S +++ b/arch/powerpc/purgatory/trampoline_64.S @@ -87,6 +87,10 @@ master: li %r4,28 STWX_BE %r17,%r3,%r4 /* Store my cpu as __be32 at byte 28 */ 1: + /* Load opal base and entry values in r8 & r9 respectively */ + ld %r8,(opal_base - 0b)(%r18) + ld %r9,(opal_entry - 0b)(%r18) + /* load the kernel address */ ld %r4,(kernel - 0b)(%r18) @@ -133,6 +137,18 @@ backup_start: .8byte 0x0 .size backup_start, . - backup_start + .balign 8 + .globl opal_base +opal_base: + .8byte 0x0 + .size opal_base, . - opal_base + + .balign 8 + .globl opal_entry +opal_entry: + .8byte 0x0 + .size opal_entry, . - opal_entry + .data .balign 8 .globl purgatory_sha256_digest -- GitLab From 471dec8023d14e2b6e817b811a37b65e3cc0661a Mon Sep 17 00:00:00 2001 From: Dan Murphy Date: Tue, 28 Jul 2020 15:08:12 -0500 Subject: [PATCH 1113/1476] dt-bindings: power: Convert battery.txt to battery.yaml Convert the battery.txt file to yaml and fix up the examples. Signed-off-by: Dan Murphy Reviewed-by: Rob Herring [Fixed the regex for ocv-capacity-table and the DT example for it] Signed-off-by: Sebastian Reichel --- .../bindings/power/supply/battery.txt | 86 +---------- .../bindings/power/supply/battery.yaml | 144 ++++++++++++++++++ 2 files changed, 145 insertions(+), 85 deletions(-) create mode 100644 Documentation/devicetree/bindings/power/supply/battery.yaml diff --git a/Documentation/devicetree/bindings/power/supply/battery.txt b/Documentation/devicetree/bindings/power/supply/battery.txt index 5e29595edd74a..a9f80cc490680 100644 --- a/Documentation/devicetree/bindings/power/supply/battery.txt +++ b/Documentation/devicetree/bindings/power/supply/battery.txt @@ -1,87 +1,3 @@ -Battery Characteristics - -The devicetree battery node provides static battery characteristics. -In smart batteries, these are typically stored in non-volatile memory -on a fuel gauge chip. The battery node should be used where there is -no appropriate non-volatile memory, or it is unprogrammed/incorrect. - -Upstream dts files should not include battery nodes, unless the battery -represented cannot easily be replaced in the system by one of a -different type. This prevents unpredictable, potentially harmful, -behavior should a replacement that changes the battery type occur -without a corresponding update to the dtb. +The contents of this file has been moved to battery.yaml Please note that not all charger drivers respect all of the properties. - -Required Properties: - - compatible: Must be "simple-battery" - -Optional Properties: - - over-voltage-threshold-microvolt: battery over-voltage limit - - re-charge-voltage-microvolt: limit to automatically start charging again - - voltage-min-design-microvolt: drained battery voltage - - voltage-max-design-microvolt: fully charged battery voltage - - energy-full-design-microwatt-hours: battery design energy - - charge-full-design-microamp-hours: battery design capacity - - trickle-charge-current-microamp: current for trickle-charge phase - - precharge-current-microamp: current for pre-charge phase - - precharge-upper-limit-microvolt: limit when to change to constant charging - - charge-term-current-microamp: current for charge termination phase - - constant-charge-current-max-microamp: maximum constant input current - - constant-charge-voltage-max-microvolt: maximum constant input voltage - - factory-internal-resistance-micro-ohms: battery factory internal resistance - - ocv-capacity-table-0: An array providing the open circuit voltage (OCV) - of the battery and corresponding battery capacity percent, which is used - to look up battery capacity according to current OCV value. And the open - circuit voltage unit is microvolt. - - ocv-capacity-table-1: Same as ocv-capacity-table-0 - ...... - - ocv-capacity-table-n: Same as ocv-capacity-table-0 - - ocv-capacity-celsius: An array containing the temperature in degree Celsius, - for each of the battery capacity lookup table. The first temperature value - specifies the OCV table 0, and the second temperature value specifies the - OCV table 1, and so on. - - resistance-temp-table: An array providing the temperature in degree Celsius - and corresponding battery internal resistance percent, which is used to look - up the resistance percent according to current temperature to get a accurate - batterty internal resistance in different temperatures. - -Battery properties are named, where possible, for the corresponding -elements in enum power_supply_property, defined in -https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/include/linux/power_supply.h - -Batteries must be referenced by chargers and/or fuel-gauges -using a phandle. The phandle's property should be named -"monitored-battery". - -Example: - - bat: battery { - compatible = "simple-battery"; - voltage-min-design-microvolt = <3200000>; - voltage-max-design-microvolt = <4200000>; - energy-full-design-microwatt-hours = <5290000>; - charge-full-design-microamp-hours = <1430000>; - precharge-current-microamp = <256000>; - charge-term-current-microamp = <128000>; - constant-charge-current-max-microamp = <900000>; - constant-charge-voltage-max-microvolt = <4200000>; - factory-internal-resistance-micro-ohms = <250000>; - ocv-capacity-celsius = <(-10) 0 10>; - ocv-capacity-table-0 = <4185000 100>, <4113000 95>, <4066000 90>, ...; - ocv-capacity-table-1 = <4200000 100>, <4185000 95>, <4113000 90>, ...; - ocv-capacity-table-2 = <4250000 100>, <4200000 95>, <4185000 90>, ...; - resistance-temp-table = <20 100>, <10 90>, <0 80>, <(-10) 60>; - }; - - charger: charger@11 { - .... - monitored-battery = <&bat>; - ... - }; - - fuel_gauge: fuel-gauge@22 { - .... - monitored-battery = <&bat>; - ... - }; diff --git a/Documentation/devicetree/bindings/power/supply/battery.yaml b/Documentation/devicetree/bindings/power/supply/battery.yaml new file mode 100644 index 0000000000000..932b736ce5c01 --- /dev/null +++ b/Documentation/devicetree/bindings/power/supply/battery.yaml @@ -0,0 +1,144 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/power/supply/battery.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Battery Characteristics + +maintainers: + - Sebastian Reichel + +description: | + The devicetree battery node provides static battery characteristics. + In smart batteries, these are typically stored in non-volatile memory + on a fuel gauge chip. The battery node should be used where there is + no appropriate non-volatile memory, or it is unprogrammed/incorrect. + + Upstream dts files should not include battery nodes, unless the battery + represented cannot easily be replaced in the system by one of a + different type. This prevents unpredictable, potentially harmful, + behavior should a replacement that changes the battery type occur + without a corresponding update to the dtb. + + Battery properties are named, where possible, for the corresponding elements + in enum power_supply_property, defined in include/linux/power_supply.h + + Batteries must be referenced by chargers and/or fuel-gauges using a phandle. + The phandle's property should be named "monitored-battery". + +properties: + compatible: + const: simple-battery + + over-voltage-threshold-microvolt: + description: battery over-voltage limit + + re-charge-voltage-microvolt: + description: limit to automatically start charging again + + voltage-min-design-microvolt: + description: drained battery voltage + + voltage-max-design-microvolt: + description: fully charged battery voltage + + energy-full-design-microwatt-hours: + description: battery design energy + + charge-full-design-microamp-hours: + description: battery design capacity + + trickle-charge-current-microamp: + description: current for trickle-charge phase + + precharge-current-microamp: + description: current for pre-charge phase + + precharge-upper-limit-microvolt: + description: limit when to change to constant charging + + charge-term-current-microamp: + description: current for charge termination phase + + constant-charge-current-max-microamp: + description: maximum constant input current + + constant-charge-voltage-max-microvolt: + description: maximum constant input voltage + + factory-internal-resistance-micro-ohms: + description: battery factory internal resistance + + resistance-temp-table: + description: | + An array providing the temperature in degree Celsius + and corresponding battery internal resistance percent, which is used to + look up the resistance percent according to current temperature to get an + accurate batterty internal resistance in different temperatures. + + ocv-capacity-celsius: + description: | + An array containing the temperature in degree Celsius, + for each of the battery capacity lookup table. + +required: + - compatible + +patternProperties: + '^ocv-capacity-table-[0-9]+$': + $ref: /schemas/types.yaml#/definitions/uint32-matrix + description: | + An array providing the open circuit voltage (OCV) + of the battery and corresponding battery capacity percent, which is used + to look up battery capacity according to current OCV value. And the open + circuit voltage unit is microvolt. + maxItems: 100 + items: + items: + - description: open circuit voltage (OCV) in microvolts + - description: battery capacity percent + maximum: 100 + +additionalProperties: false + +examples: + - | + power { + #address-cells = <1>; + #size-cells = <0>; + + battery: battery { + compatible = "simple-battery"; + over-voltage-threshold-microvolt = <4500000>; + re-charge-voltage-microvolt = <250000>; + voltage-min-design-microvolt = <3200000>; + voltage-max-design-microvolt = <4200000>; + energy-full-design-microwatt-hours = <5290000>; + charge-full-design-microamp-hours = <1430000>; + precharge-current-microamp = <256000>; + precharge-upper-limit-microvolt = <2500000>; + charge-term-current-microamp = <128000>; + constant-charge-current-max-microamp = <900000>; + constant-charge-voltage-max-microvolt = <4200000>; + factory-internal-resistance-micro-ohms = <250000>; + ocv-capacity-celsius = <(-10) 0 10>; + /* table for -10 degree Celsius */ + ocv-capacity-table-0 = <4185000 100>, <4113000 95>, <4066000 90>; + /* table for 0 degree Celsius */ + ocv-capacity-table-1 = <4200000 100>, <4185000 95>, <4113000 90>; + /* table for 10 degree Celsius */ + ocv-capacity-table-2 = <4250000 100>, <4200000 95>, <4185000 90>; + resistance-temp-table = <20 100>, <10 90>, <0 80>, <(-10) 60>; + }; + + charger@11 { + reg = <0x11>; + monitored-battery = <&battery>; + }; + + fuel-gauge@22 { + reg = <0x22>; + monitored-battery = <&battery>; + }; + }; -- GitLab From 362fd2ecc3f118b09a56a54752c5310ebb9f7c54 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Wed, 29 Jul 2020 09:43:47 +0200 Subject: [PATCH 1114/1476] power: supply: cpcap-battery: Fix kerneldoc of cpcap_battery_read_accumulated() Fix W=1 compile warnings (invalid kerneldoc): drivers/power/supply/cpcap-battery.c:292: warning: Function parameter or member 'ccd' not described in 'cpcap_battery_read_accumulated' drivers/power/supply/cpcap-battery.c:292: warning: Excess function parameter 'regs' description in 'cpcap_battery_read_accumulated' Signed-off-by: Krzysztof Kozlowski Signed-off-by: Sebastian Reichel --- drivers/power/supply/cpcap-battery.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/power/supply/cpcap-battery.c b/drivers/power/supply/cpcap-battery.c index 6e9392901b0a2..90eba364664b8 100644 --- a/drivers/power/supply/cpcap-battery.c +++ b/drivers/power/supply/cpcap-battery.c @@ -274,7 +274,7 @@ static int cpcap_battery_cc_to_ua(struct cpcap_battery_ddata *ddata, /** * cpcap_battery_read_accumulated - reads cpcap coulomb counter * @ddata: device driver data - * @regs: coulomb counter values + * @ccd: coulomb counter values * * Based on Motorola mapphone kernel function data_read_regs(). * Looking at the registers, the coulomb counter seems similar to -- GitLab From 152ee3d1afbee08c88dc37048ee26a3edacbef90 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Wed, 29 Jul 2020 09:43:48 +0200 Subject: [PATCH 1115/1476] power: supply: Fix kerneldoc of power_supply_temp2resist_simple() Fix W=1 compile warnings (invalid kerneldoc): drivers/power/supply/power_supply_core.c:747: warning: Function parameter or member 'temp' not described in 'power_supply_temp2resist_simple' drivers/power/supply/power_supply_core.c:747: warning: Excess function parameter 'ocv' description in 'power_supply_temp2resist_simple' Signed-off-by: Krzysztof Kozlowski Signed-off-by: Sebastian Reichel --- drivers/power/supply/power_supply_core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/power/supply/power_supply_core.c b/drivers/power/supply/power_supply_core.c index 02b37fe6061c0..595e030034d41 100644 --- a/drivers/power/supply/power_supply_core.c +++ b/drivers/power/supply/power_supply_core.c @@ -733,7 +733,7 @@ EXPORT_SYMBOL_GPL(power_supply_put_battery_info); * percent * @table: Pointer to battery resistance temperature table * @table_len: The table length - * @ocv: Current temperature + * @temp: Current temperature * * This helper function is used to look up battery internal resistance percent * according to current temperature value from the resistance temperature table, -- GitLab From c365ee561ee343b587df9f5ba191c1a090d4f43e Mon Sep 17 00:00:00 2001 From: LH Lin Date: Wed, 29 Jul 2020 20:14:00 +0800 Subject: [PATCH 1116/1476] power: supply: test_power: Fix battery_current initial value Since default battery_status is POWER_SUPPLY_STATUS_DISCHARGING, we should change default battery_current to a negative value. Signed-off-by: LH Lin Signed-off-by: Sebastian Reichel --- drivers/power/supply/test_power.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/power/supply/test_power.c b/drivers/power/supply/test_power.c index b3c05ff05783f..04acd76bbaa12 100644 --- a/drivers/power/supply/test_power.c +++ b/drivers/power/supply/test_power.c @@ -34,7 +34,7 @@ static int battery_technology = POWER_SUPPLY_TECHNOLOGY_LION; static int battery_capacity = 50; static int battery_voltage = 3300; static int battery_charge_counter = -1000; -static int battery_current = 1600; +static int battery_current = -1600; static bool module_initialized; -- GitLab From 2167c40657981f8a3d1ff1f04d4bda6b4a64f8e2 Mon Sep 17 00:00:00 2001 From: Heiner Kallweit Date: Tue, 28 Jul 2020 23:03:41 +0200 Subject: [PATCH 1117/1476] PCI: Remove unused pci_lost_interrupt() 388c8c16abaf ("PCI: add routines for debugging and handling lost interrupts") added pci_lost_interrupt() that apparently never has had a single user. Remove it. Link: https://lore.kernel.org/r/e328d059-3068-6a40-28df-f81f616d15a0@gmail.com Signed-off-by: Heiner Kallweit Signed-off-by: Bjorn Helgaas --- drivers/pci/irq.c | 50 --------------------------------------------- include/linux/pci.h | 7 ------- 2 files changed, 57 deletions(-) diff --git a/drivers/pci/irq.c b/drivers/pci/irq.c index a1de501a27299..12ecd0aaa28d6 100644 --- a/drivers/pci/irq.c +++ b/drivers/pci/irq.c @@ -6,61 +6,11 @@ * Copyright (C) 2017 Christoph Hellwig. */ -#include #include #include #include #include -static void pci_note_irq_problem(struct pci_dev *pdev, const char *reason) -{ - struct pci_dev *parent = to_pci_dev(pdev->dev.parent); - - pci_err(pdev, "Potentially misrouted IRQ (Bridge %s %04x:%04x)\n", - dev_name(&parent->dev), parent->vendor, parent->device); - pci_err(pdev, "%s\n", reason); - pci_err(pdev, "Please report to linux-kernel@vger.kernel.org\n"); - WARN_ON(1); -} - -/** - * pci_lost_interrupt - reports a lost PCI interrupt - * @pdev: device whose interrupt is lost - * - * The primary function of this routine is to report a lost interrupt - * in a standard way which users can recognise (instead of blaming the - * driver). - * - * Returns: - * a suggestion for fixing it (although the driver is not required to - * act on this). - */ -enum pci_lost_interrupt_reason pci_lost_interrupt(struct pci_dev *pdev) -{ - if (pdev->msi_enabled || pdev->msix_enabled) { - enum pci_lost_interrupt_reason ret; - - if (pdev->msix_enabled) { - pci_note_irq_problem(pdev, "MSIX routing failure"); - ret = PCI_LOST_IRQ_DISABLE_MSIX; - } else { - pci_note_irq_problem(pdev, "MSI routing failure"); - ret = PCI_LOST_IRQ_DISABLE_MSI; - } - return ret; - } -#ifdef CONFIG_ACPI - if (!(acpi_disabled || acpi_noirq)) { - pci_note_irq_problem(pdev, "Potential ACPI misrouting please reboot with acpi=noirq"); - /* currently no way to fix acpi on the fly */ - return PCI_LOST_IRQ_DISABLE_ACPI; - } -#endif - pci_note_irq_problem(pdev, "unknown cause (not MSI or ACPI)"); - return PCI_LOST_IRQ_NO_INFORMATION; -} -EXPORT_SYMBOL(pci_lost_interrupt); - /** * pci_request_irq - allocate an interrupt line for a PCI device * @dev: PCI device to operate on diff --git a/include/linux/pci.h b/include/linux/pci.h index c79d83304e529..2dcd67f509a8c 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -1053,13 +1053,6 @@ void pci_sort_breadthfirst(void); /* Generic PCI functions exported to card drivers */ -enum pci_lost_interrupt_reason { - PCI_LOST_IRQ_NO_INFORMATION = 0, - PCI_LOST_IRQ_DISABLE_MSI, - PCI_LOST_IRQ_DISABLE_MSIX, - PCI_LOST_IRQ_DISABLE_ACPI, -}; -enum pci_lost_interrupt_reason pci_lost_interrupt(struct pci_dev *dev); int pci_find_capability(struct pci_dev *dev, int cap); int pci_find_next_capability(struct pci_dev *dev, u8 pos, int cap); int pci_find_ext_capability(struct pci_dev *dev, int cap); -- GitLab From 45beb31d3afb651bb5c41897e46bd4fa9980c51c Mon Sep 17 00:00:00 2001 From: Kai-Heng Feng Date: Tue, 28 Jul 2020 18:45:53 +0800 Subject: [PATCH 1118/1476] PCI: Mark AMD Navi10 GPU rev 0x00 ATS as broken We are seeing AMD Radeon Pro W5700 doesn't work when IOMMU is enabled: iommu ivhd0: AMD-Vi: Event logged [IOTLB_INV_TIMEOUT device=63:00.0 address=0x42b5b01a0] iommu ivhd0: AMD-Vi: Event logged [IOTLB_INV_TIMEOUT device=63:00.0 address=0x42b5b01c0] The error also makes graphics driver fail to probe the device. It appears to be the same issue as commit 5e89cd303e3a ("PCI: Mark AMD Navi14 GPU rev 0xc5 ATS as broken") addresses, and indeed the same ATS quirk can workaround the issue. See-also: 5e89cd303e3a ("PCI: Mark AMD Navi14 GPU rev 0xc5 ATS as broken") See-also: d28ca864c493 ("PCI: Mark AMD Stoney Radeon R7 GPU ATS as broken") See-also: 9b44b0b09dec ("PCI: Mark AMD Stoney GPU ATS as broken") Bugzilla: https://bugzilla.kernel.org/show_bug.cgi?id=208725 Link: https://lore.kernel.org/r/20200728104554.28927-1-kai.heng.feng@canonical.com Signed-off-by: Kai-Heng Feng Signed-off-by: Bjorn Helgaas Acked-by: Alex Deucher Cc: stable@vger.kernel.org --- drivers/pci/quirks.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c index 812bfc32ecb82..052efeb9f0532 100644 --- a/drivers/pci/quirks.c +++ b/drivers/pci/quirks.c @@ -5192,7 +5192,8 @@ DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_SERVERWORKS, 0x0422, quirk_no_ext_tags); */ static void quirk_amd_harvest_no_ats(struct pci_dev *pdev) { - if (pdev->device == 0x7340 && pdev->revision != 0xc5) + if ((pdev->device == 0x7312 && pdev->revision != 0x00) || + (pdev->device == 0x7340 && pdev->revision != 0xc5)) return; pci_info(pdev, "disabling ATS\n"); @@ -5203,6 +5204,8 @@ static void quirk_amd_harvest_no_ats(struct pci_dev *pdev) DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_ATI, 0x98e4, quirk_amd_harvest_no_ats); /* AMD Iceland dGPU */ DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_ATI, 0x6900, quirk_amd_harvest_no_ats); +/* AMD Navi10 dGPU */ +DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_ATI, 0x7312, quirk_amd_harvest_no_ats); /* AMD Navi14 dGPU */ DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_ATI, 0x7340, quirk_amd_harvest_no_ats); #endif /* CONFIG_PCI_ATS */ -- GitLab From 36d1b6997dd6a4f3be194dde6553c3dcc2f7f07a Mon Sep 17 00:00:00 2001 From: Dan Murphy Date: Wed, 29 Jul 2020 13:31:42 -0500 Subject: [PATCH 1119/1476] dt-bindings: power: Add BQ27Z561 compatible Add the Texas Instruments bq27z561 battery monitor to the bq27xxx binding. Acked-by: Rob Herring Signed-off-by: Dan Murphy Signed-off-by: Sebastian Reichel --- Documentation/devicetree/bindings/power/supply/bq27xxx.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/power/supply/bq27xxx.yaml b/Documentation/devicetree/bindings/power/supply/bq27xxx.yaml index 03d1020a2e470..0aa33590ac24a 100644 --- a/Documentation/devicetree/bindings/power/supply/bq27xxx.yaml +++ b/Documentation/devicetree/bindings/power/supply/bq27xxx.yaml @@ -49,6 +49,7 @@ properties: - ti,bq27426 - ti,bq27441 - ti,bq27621 + - ti,bq27z561 reg: maxItems: 1 -- GitLab From 6f24ff97e3231a5303841c5196a6f460f8485eb4 Mon Sep 17 00:00:00 2001 From: Dan Murphy Date: Wed, 29 Jul 2020 13:31:43 -0500 Subject: [PATCH 1120/1476] power: supply: bq27xxx_battery: Add the BQ27Z561 Battery monitor Add the Texas Instruments BQ27Z561 battery monitor. The register address map is laid out the same as compared to other devices within the file. The battery status register has differing bits to determine if the battery is full, discharging or dead. Signed-off-by: Dan Murphy Signed-off-by: Sebastian Reichel --- drivers/power/supply/bq27xxx_battery.c | 68 +++++++++++++++++++++- drivers/power/supply/bq27xxx_battery_i2c.c | 2 + include/linux/power/bq27xxx_battery.h | 1 + 3 files changed, 70 insertions(+), 1 deletion(-) diff --git a/drivers/power/supply/bq27xxx_battery.c b/drivers/power/supply/bq27xxx_battery.c index acaafed037be8..a05b9a2d112d4 100644 --- a/drivers/power/supply/bq27xxx_battery.c +++ b/drivers/power/supply/bq27xxx_battery.c @@ -43,6 +43,7 @@ * https://www.ti.com/product/bq27411-g1 * https://www.ti.com/product/bq27441-g1 * https://www.ti.com/product/bq27621-g1 + * https://www.ti.com/product/bq27z561 */ #include @@ -79,6 +80,11 @@ #define BQ27000_FLAG_FC BIT(5) #define BQ27000_FLAG_CHGS BIT(7) /* Charge state flag */ +/* BQ27Z561 has different layout for Flags register */ +#define BQ27Z561_FLAG_FDC BIT(4) /* Battery fully discharged */ +#define BQ27Z561_FLAG_FC BIT(5) /* Battery fully charged */ +#define BQ27Z561_FLAG_DIS_CH BIT(6) /* Battery is discharging */ + /* control register params */ #define BQ27XXX_SEALED 0x20 #define BQ27XXX_SET_CFGUPDATE 0x13 @@ -431,12 +437,32 @@ static u8 [BQ27XXX_REG_DCAP] = 0x3c, [BQ27XXX_REG_AP] = 0x18, BQ27XXX_DM_REG_ROWS, - }; + }, #define bq27411_regs bq27421_regs #define bq27425_regs bq27421_regs #define bq27426_regs bq27421_regs #define bq27441_regs bq27421_regs #define bq27621_regs bq27421_regs + bq27z561_regs[BQ27XXX_REG_MAX] = { + [BQ27XXX_REG_CTRL] = 0x00, + [BQ27XXX_REG_TEMP] = 0x06, + [BQ27XXX_REG_INT_TEMP] = INVALID_REG_ADDR, + [BQ27XXX_REG_VOLT] = 0x08, + [BQ27XXX_REG_AI] = 0x14, + [BQ27XXX_REG_FLAGS] = 0x0a, + [BQ27XXX_REG_TTE] = 0x16, + [BQ27XXX_REG_TTF] = 0x18, + [BQ27XXX_REG_TTES] = INVALID_REG_ADDR, + [BQ27XXX_REG_TTECP] = INVALID_REG_ADDR, + [BQ27XXX_REG_NAC] = INVALID_REG_ADDR, + [BQ27XXX_REG_FCC] = 0x12, + [BQ27XXX_REG_CYCT] = 0x2a, + [BQ27XXX_REG_AE] = 0x22, + [BQ27XXX_REG_SOC] = 0x2c, + [BQ27XXX_REG_DCAP] = 0x3c, + [BQ27XXX_REG_AP] = 0x22, + BQ27XXX_DM_REG_ROWS, + }; static enum power_supply_property bq27000_props[] = { POWER_SUPPLY_PROP_STATUS, @@ -672,6 +698,25 @@ static enum power_supply_property bq27421_props[] = { #define bq27441_props bq27421_props #define bq27621_props bq27421_props +static enum power_supply_property bq27z561_props[] = { + POWER_SUPPLY_PROP_STATUS, + POWER_SUPPLY_PROP_PRESENT, + POWER_SUPPLY_PROP_VOLTAGE_NOW, + POWER_SUPPLY_PROP_CURRENT_NOW, + POWER_SUPPLY_PROP_CAPACITY, + POWER_SUPPLY_PROP_CAPACITY_LEVEL, + POWER_SUPPLY_PROP_TEMP, + POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW, + POWER_SUPPLY_PROP_TIME_TO_FULL_NOW, + POWER_SUPPLY_PROP_TECHNOLOGY, + POWER_SUPPLY_PROP_CHARGE_FULL, + POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, + POWER_SUPPLY_PROP_CYCLE_COUNT, + POWER_SUPPLY_PROP_POWER_AVG, + POWER_SUPPLY_PROP_HEALTH, + POWER_SUPPLY_PROP_MANUFACTURER, +}; + struct bq27xxx_dm_reg { u8 subclass_id; u8 offset; @@ -767,11 +812,14 @@ static struct bq27xxx_dm_reg bq27621_dm_regs[] = { #define bq27621_dm_regs 0 #endif +#define bq27z561_dm_regs 0 + #define BQ27XXX_O_ZERO 0x00000001 #define BQ27XXX_O_OTDC 0x00000002 /* has OTC/OTD overtemperature flags */ #define BQ27XXX_O_UTOT 0x00000004 /* has OT overtemperature flag */ #define BQ27XXX_O_CFGUP 0x00000008 #define BQ27XXX_O_RAM 0x00000010 +#define BQ27Z561_O_BITS 0x00000020 #define BQ27XXX_DATA(ref, key, opt) { \ .opts = (opt), \ @@ -816,6 +864,7 @@ static struct { [BQ27426] = BQ27XXX_DATA(bq27426, 0x80008000, BQ27XXX_O_UTOT | BQ27XXX_O_CFGUP | BQ27XXX_O_RAM), [BQ27441] = BQ27XXX_DATA(bq27441, 0x80008000, BQ27XXX_O_UTOT | BQ27XXX_O_CFGUP | BQ27XXX_O_RAM), [BQ27621] = BQ27XXX_DATA(bq27621, 0x80008000, BQ27XXX_O_UTOT | BQ27XXX_O_CFGUP | BQ27XXX_O_RAM), + [BQ27Z561] = BQ27XXX_DATA(bq27z561, 0 , BQ27Z561_O_BITS), }; static DEFINE_MUTEX(bq27xxx_list_lock); @@ -1551,6 +1600,8 @@ static bool bq27xxx_battery_dead(struct bq27xxx_device_info *di, u16 flags) { if (di->opts & BQ27XXX_O_ZERO) return flags & (BQ27000_FLAG_EDV1 | BQ27000_FLAG_EDVF); + else if (di->opts & BQ27Z561_O_BITS) + return flags & BQ27Z561_FLAG_FDC; else return flags & (BQ27XXX_FLAG_SOC1 | BQ27XXX_FLAG_SOCF); } @@ -1595,6 +1646,7 @@ void bq27xxx_battery_update(struct bq27xxx_device_info *di) cache.time_to_empty_avg = bq27xxx_battery_read_time(di, BQ27XXX_REG_TTECP); if (di->regs[BQ27XXX_REG_TTF] != INVALID_REG_ADDR) cache.time_to_full = bq27xxx_battery_read_time(di, BQ27XXX_REG_TTF); + cache.charge_full = bq27xxx_battery_read_fcc(di); cache.capacity = bq27xxx_battery_read_soc(di); if (di->regs[BQ27XXX_REG_AE] != INVALID_REG_ADDR) @@ -1682,6 +1734,13 @@ static int bq27xxx_battery_status(struct bq27xxx_device_info *di, status = POWER_SUPPLY_STATUS_NOT_CHARGING; else status = POWER_SUPPLY_STATUS_DISCHARGING; + } else if (di->opts & BQ27Z561_O_BITS) { + if (di->cache.flags & BQ27Z561_FLAG_FC) + status = POWER_SUPPLY_STATUS_FULL; + else if (di->cache.flags & BQ27Z561_FLAG_DIS_CH) + status = POWER_SUPPLY_STATUS_DISCHARGING; + else + status = POWER_SUPPLY_STATUS_CHARGING; } else { if (di->cache.flags & BQ27XXX_FLAG_FC) status = POWER_SUPPLY_STATUS_FULL; @@ -1710,6 +1769,13 @@ static int bq27xxx_battery_capacity_level(struct bq27xxx_device_info *di, level = POWER_SUPPLY_CAPACITY_LEVEL_CRITICAL; else level = POWER_SUPPLY_CAPACITY_LEVEL_NORMAL; + } else if (di->opts & BQ27Z561_O_BITS) { + if (di->cache.flags & BQ27Z561_FLAG_FC) + level = POWER_SUPPLY_CAPACITY_LEVEL_FULL; + else if (di->cache.flags & BQ27Z561_FLAG_FDC) + level = POWER_SUPPLY_CAPACITY_LEVEL_CRITICAL; + else + level = POWER_SUPPLY_CAPACITY_LEVEL_NORMAL; } else { if (di->cache.flags & BQ27XXX_FLAG_FC) level = POWER_SUPPLY_CAPACITY_LEVEL_FULL; diff --git a/drivers/power/supply/bq27xxx_battery_i2c.c b/drivers/power/supply/bq27xxx_battery_i2c.c index 8e114a7abfc93..15f4e75786ab5 100644 --- a/drivers/power/supply/bq27xxx_battery_i2c.c +++ b/drivers/power/supply/bq27xxx_battery_i2c.c @@ -253,6 +253,7 @@ static const struct i2c_device_id bq27xxx_i2c_id_table[] = { { "bq27426", BQ27426 }, { "bq27441", BQ27441 }, { "bq27621", BQ27621 }, + { "bq27z561", BQ27Z561 }, {}, }; MODULE_DEVICE_TABLE(i2c, bq27xxx_i2c_id_table); @@ -286,6 +287,7 @@ static const struct of_device_id bq27xxx_battery_i2c_of_match_table[] = { { .compatible = "ti,bq27426" }, { .compatible = "ti,bq27441" }, { .compatible = "ti,bq27621" }, + { .compatible = "ti,bq27z561" }, {}, }; MODULE_DEVICE_TABLE(of, bq27xxx_battery_i2c_of_match_table); diff --git a/include/linux/power/bq27xxx_battery.h b/include/linux/power/bq27xxx_battery.h index 507c5e214c424..1f6ea5d5063db 100644 --- a/include/linux/power/bq27xxx_battery.h +++ b/include/linux/power/bq27xxx_battery.h @@ -30,6 +30,7 @@ enum bq27xxx_chip { BQ27426, BQ27441, BQ27621, + BQ27Z561, }; struct bq27xxx_device_info; -- GitLab From 0827425db7a65259db50c188a26aa2ad74a3cc3a Mon Sep 17 00:00:00 2001 From: Dan Murphy Date: Wed, 29 Jul 2020 13:31:44 -0500 Subject: [PATCH 1121/1476] dt-bindings: power: Add BQ28z610 compatible Add the Texas Instruments bq28z610 battery monitor to the bq27xxx binding. Acked-by: Rob Herring Signed-off-by: Dan Murphy Signed-off-by: Sebastian Reichel --- Documentation/devicetree/bindings/power/supply/bq27xxx.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/power/supply/bq27xxx.yaml b/Documentation/devicetree/bindings/power/supply/bq27xxx.yaml index 0aa33590ac24a..82f682705f449 100644 --- a/Documentation/devicetree/bindings/power/supply/bq27xxx.yaml +++ b/Documentation/devicetree/bindings/power/supply/bq27xxx.yaml @@ -50,6 +50,7 @@ properties: - ti,bq27441 - ti,bq27621 - ti,bq27z561 + - ti,bq28z610 reg: maxItems: 1 -- GitLab From 707d678a5c7c5e80d1caac6c6b021171f5ecde58 Mon Sep 17 00:00:00 2001 From: Dan Murphy Date: Wed, 29 Jul 2020 13:31:45 -0500 Subject: [PATCH 1122/1476] power: supply: bq27xxx_battery: Add the BQ28z610 Battery monitor Add the Texas Instruments BQ28z610 battery monitor. The register address map is laid out the same as compared to other devices within the file. The battery status register bits are similar to the bq27z561 but they are different compared to other fuel gauge devices within this file. Signed-off-by: Dan Murphy Signed-off-by: Sebastian Reichel --- drivers/power/supply/bq27xxx_battery.c | 42 ++++++++++++++++++++++ drivers/power/supply/bq27xxx_battery_i2c.c | 2 ++ include/linux/power/bq27xxx_battery.h | 1 + 3 files changed, 45 insertions(+) diff --git a/drivers/power/supply/bq27xxx_battery.c b/drivers/power/supply/bq27xxx_battery.c index a05b9a2d112d4..a123f6e21f08a 100644 --- a/drivers/power/supply/bq27xxx_battery.c +++ b/drivers/power/supply/bq27xxx_battery.c @@ -44,6 +44,7 @@ * https://www.ti.com/product/bq27441-g1 * https://www.ti.com/product/bq27621-g1 * https://www.ti.com/product/bq27z561 + * https://www.ti.com/product/bq28z610 */ #include @@ -462,6 +463,26 @@ static u8 [BQ27XXX_REG_DCAP] = 0x3c, [BQ27XXX_REG_AP] = 0x22, BQ27XXX_DM_REG_ROWS, + }, + bq28z610_regs[BQ27XXX_REG_MAX] = { + [BQ27XXX_REG_CTRL] = 0x00, + [BQ27XXX_REG_TEMP] = 0x06, + [BQ27XXX_REG_INT_TEMP] = INVALID_REG_ADDR, + [BQ27XXX_REG_VOLT] = 0x08, + [BQ27XXX_REG_AI] = 0x14, + [BQ27XXX_REG_FLAGS] = 0x0a, + [BQ27XXX_REG_TTE] = 0x16, + [BQ27XXX_REG_TTF] = 0x18, + [BQ27XXX_REG_TTES] = INVALID_REG_ADDR, + [BQ27XXX_REG_TTECP] = INVALID_REG_ADDR, + [BQ27XXX_REG_NAC] = INVALID_REG_ADDR, + [BQ27XXX_REG_FCC] = 0x12, + [BQ27XXX_REG_CYCT] = 0x2a, + [BQ27XXX_REG_AE] = 0x22, + [BQ27XXX_REG_SOC] = 0x2c, + [BQ27XXX_REG_DCAP] = 0x3c, + [BQ27XXX_REG_AP] = 0x22, + BQ27XXX_DM_REG_ROWS, }; static enum power_supply_property bq27000_props[] = { @@ -717,6 +738,25 @@ static enum power_supply_property bq27z561_props[] = { POWER_SUPPLY_PROP_MANUFACTURER, }; +static enum power_supply_property bq28z610_props[] = { + POWER_SUPPLY_PROP_STATUS, + POWER_SUPPLY_PROP_PRESENT, + POWER_SUPPLY_PROP_VOLTAGE_NOW, + POWER_SUPPLY_PROP_CURRENT_NOW, + POWER_SUPPLY_PROP_CAPACITY, + POWER_SUPPLY_PROP_CAPACITY_LEVEL, + POWER_SUPPLY_PROP_TEMP, + POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW, + POWER_SUPPLY_PROP_TIME_TO_FULL_NOW, + POWER_SUPPLY_PROP_TECHNOLOGY, + POWER_SUPPLY_PROP_CHARGE_FULL, + POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, + POWER_SUPPLY_PROP_CYCLE_COUNT, + POWER_SUPPLY_PROP_POWER_AVG, + POWER_SUPPLY_PROP_HEALTH, + POWER_SUPPLY_PROP_MANUFACTURER, +}; + struct bq27xxx_dm_reg { u8 subclass_id; u8 offset; @@ -813,6 +853,7 @@ static struct bq27xxx_dm_reg bq27621_dm_regs[] = { #endif #define bq27z561_dm_regs 0 +#define bq28z610_dm_regs 0 #define BQ27XXX_O_ZERO 0x00000001 #define BQ27XXX_O_OTDC 0x00000002 /* has OTC/OTD overtemperature flags */ @@ -865,6 +906,7 @@ static struct { [BQ27441] = BQ27XXX_DATA(bq27441, 0x80008000, BQ27XXX_O_UTOT | BQ27XXX_O_CFGUP | BQ27XXX_O_RAM), [BQ27621] = BQ27XXX_DATA(bq27621, 0x80008000, BQ27XXX_O_UTOT | BQ27XXX_O_CFGUP | BQ27XXX_O_RAM), [BQ27Z561] = BQ27XXX_DATA(bq27z561, 0 , BQ27Z561_O_BITS), + [BQ28Z610] = BQ27XXX_DATA(bq28z610, 0 , BQ27Z561_O_BITS), }; static DEFINE_MUTEX(bq27xxx_list_lock); diff --git a/drivers/power/supply/bq27xxx_battery_i2c.c b/drivers/power/supply/bq27xxx_battery_i2c.c index 15f4e75786ab5..ab02456d69e54 100644 --- a/drivers/power/supply/bq27xxx_battery_i2c.c +++ b/drivers/power/supply/bq27xxx_battery_i2c.c @@ -254,6 +254,7 @@ static const struct i2c_device_id bq27xxx_i2c_id_table[] = { { "bq27441", BQ27441 }, { "bq27621", BQ27621 }, { "bq27z561", BQ27Z561 }, + { "bq28z610", BQ28Z610 }, {}, }; MODULE_DEVICE_TABLE(i2c, bq27xxx_i2c_id_table); @@ -288,6 +289,7 @@ static const struct of_device_id bq27xxx_battery_i2c_of_match_table[] = { { .compatible = "ti,bq27441" }, { .compatible = "ti,bq27621" }, { .compatible = "ti,bq27z561" }, + { .compatible = "ti,bq28z610" }, {}, }; MODULE_DEVICE_TABLE(of, bq27xxx_battery_i2c_of_match_table); diff --git a/include/linux/power/bq27xxx_battery.h b/include/linux/power/bq27xxx_battery.h index 1f6ea5d5063db..987d9652aa4e7 100644 --- a/include/linux/power/bq27xxx_battery.h +++ b/include/linux/power/bq27xxx_battery.h @@ -31,6 +31,7 @@ enum bq27xxx_chip { BQ27441, BQ27621, BQ27Z561, + BQ28Z610, }; struct bq27xxx_device_info; -- GitLab From b7360f609f5fa0537667dc1b8036c96bf7e03023 Mon Sep 17 00:00:00 2001 From: Tiezhu Yang Date: Mon, 27 Jul 2020 15:06:55 +0800 Subject: [PATCH 1123/1476] PCI: Announce device after early fixups Announce the device, e.g., pci 0000:00:00.0: [8086:5910] type 00 class 0x060000 after running early fixups, so the log message reflects any device type or class code fixups. [bhelgaas: commit log] Link: https://lore.kernel.org/r/1595833615-8049-1-git-send-email-yangtiezhu@loongson.cn Signed-off-by: Tiezhu Yang Signed-off-by: Bjorn Helgaas --- drivers/pci/probe.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index 8c40c00413e74..494333df46af6 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -1802,9 +1802,6 @@ int pci_setup_device(struct pci_dev *dev) dev->revision = class & 0xff; dev->class = class >> 8; /* upper 3 bytes */ - pci_info(dev, "[%04x:%04x] type %02x class %#08x\n", - dev->vendor, dev->device, dev->hdr_type, dev->class); - if (pci_early_dump) early_dump_pci_device(dev); @@ -1822,6 +1819,9 @@ int pci_setup_device(struct pci_dev *dev) /* Early fixups, before probing the BARs */ pci_fixup_device(pci_fixup_early, dev); + pci_info(dev, "[%04x:%04x] type %02x class %#08x\n", + dev->vendor, dev->device, dev->hdr_type, dev->class); + /* Device class may be changed after fixup */ class = dev->class >> 8; -- GitLab From 090688fa4e448284aaa16136372397d7d10814db Mon Sep 17 00:00:00 2001 From: Hanjun Guo Date: Wed, 22 Jul 2020 17:44:28 +0800 Subject: [PATCH 1124/1476] PCI: Release IVRS table in AMD ACS quirk The acpi_get_table() should be coupled with acpi_put_table() if the mapped table is not used at runtime to release the table mapping. In pci_quirk_amd_sb_acs(), IVRS table is just used for checking AMD IOMMU is supported, not used at runtime, so put the table after using it. Fixes: 15b100dfd1c9 ("PCI: Claim ACS support for AMD southbridge devices") Link: https://lore.kernel.org/r/1595411068-15440-1-git-send-email-guohanjun@huawei.com Signed-off-by: Hanjun Guo Signed-off-by: Bjorn Helgaas --- drivers/pci/quirks.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c index 052efeb9f0532..2456a1950a8a4 100644 --- a/drivers/pci/quirks.c +++ b/drivers/pci/quirks.c @@ -4409,6 +4409,8 @@ static int pci_quirk_amd_sb_acs(struct pci_dev *dev, u16 acs_flags) if (ACPI_FAILURE(status)) return -ENODEV; + acpi_put_table(header); + /* Filter out flags not applicable to multifunction */ acs_flags &= (PCI_ACS_RR | PCI_ACS_CR | PCI_ACS_EC | PCI_ACS_DT); -- GitLab From aff779515a070df7e23da9e86f1096f7d10d647e Mon Sep 17 00:00:00 2001 From: Vladis Dronov Date: Wed, 29 Jul 2020 15:37:41 +0200 Subject: [PATCH 1125/1476] powerpc: fix function annotations to avoid section mismatch warnings with gcc-10 Certain warnings are emitted for powerpc code when building with a gcc-10 toolset: WARNING: modpost: vmlinux.o(.text.unlikely+0x377c): Section mismatch in reference from the function remove_pmd_table() to the function .meminit.text:split_kernel_mapping() The function remove_pmd_table() references the function __meminit split_kernel_mapping(). This is often because remove_pmd_table lacks a __meminit annotation or the annotation of split_kernel_mapping is wrong. Add the appropriate __init and __meminit annotations to make modpost not complain. In all the cases there are just a single callsite from another __init or __meminit function: __meminit remove_pagetable() -> remove_pud_table() -> remove_pmd_table() __init prom_init() -> setup_secure_guest() __init xive_spapr_init() -> xive_spapr_disabled() Signed-off-by: Vladis Dronov Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20200729133741.62789-1-vdronov@redhat.com --- arch/powerpc/kernel/prom_init.c | 4 ++-- arch/powerpc/mm/book3s64/radix_pgtable.c | 4 ++-- arch/powerpc/sysdev/xive/spapr.c | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/arch/powerpc/kernel/prom_init.c b/arch/powerpc/kernel/prom_init.c index f279a1f58fa72..ae7ec99031919 100644 --- a/arch/powerpc/kernel/prom_init.c +++ b/arch/powerpc/kernel/prom_init.c @@ -3270,7 +3270,7 @@ static int enter_secure_mode(unsigned long kbase, unsigned long fdt) /* * Call the Ultravisor to transfer us to secure memory if we have an ESM blob. */ -static void setup_secure_guest(unsigned long kbase, unsigned long fdt) +static void __init setup_secure_guest(unsigned long kbase, unsigned long fdt) { int ret; @@ -3300,7 +3300,7 @@ static void setup_secure_guest(unsigned long kbase, unsigned long fdt) } } #else -static void setup_secure_guest(unsigned long kbase, unsigned long fdt) +static void __init setup_secure_guest(unsigned long kbase, unsigned long fdt) { } #endif /* CONFIG_PPC_SVM */ diff --git a/arch/powerpc/mm/book3s64/radix_pgtable.c b/arch/powerpc/mm/book3s64/radix_pgtable.c index a8f46a6862196..28c784976beda 100644 --- a/arch/powerpc/mm/book3s64/radix_pgtable.c +++ b/arch/powerpc/mm/book3s64/radix_pgtable.c @@ -823,7 +823,7 @@ static void remove_pte_table(pte_t *pte_start, unsigned long addr, } } -static void remove_pmd_table(pmd_t *pmd_start, unsigned long addr, +static void __meminit remove_pmd_table(pmd_t *pmd_start, unsigned long addr, unsigned long end) { unsigned long next; @@ -853,7 +853,7 @@ static void remove_pmd_table(pmd_t *pmd_start, unsigned long addr, } } -static void remove_pud_table(pud_t *pud_start, unsigned long addr, +static void __meminit remove_pud_table(pud_t *pud_start, unsigned long addr, unsigned long end) { unsigned long next; diff --git a/arch/powerpc/sysdev/xive/spapr.c b/arch/powerpc/sysdev/xive/spapr.c index f0551a2be9df4..1e3674d7ea7bc 100644 --- a/arch/powerpc/sysdev/xive/spapr.c +++ b/arch/powerpc/sysdev/xive/spapr.c @@ -768,7 +768,7 @@ static const u8 *get_vec5_feature(unsigned int index) return vec5 + index; } -static bool xive_spapr_disabled(void) +static bool __init xive_spapr_disabled(void) { const u8 *vec5_xive; -- GitLab From e2b3c165f27a6bdb197b0dc86683ed36f61c5527 Mon Sep 17 00:00:00 2001 From: Nathan Lynch Date: Thu, 27 Jun 2019 00:15:34 -0500 Subject: [PATCH 1126/1476] powerpc/cacheinfo: Set pr_fmt() Set pr_fmt() so we get a nice prefix on messages. Signed-off-by: Nathan Lynch Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20190627051537.7298-2-nathanl@linux.ibm.com --- arch/powerpc/kernel/cacheinfo.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/arch/powerpc/kernel/cacheinfo.c b/arch/powerpc/kernel/cacheinfo.c index d8d4552af30ad..751a4ffcd588b 100644 --- a/arch/powerpc/kernel/cacheinfo.c +++ b/arch/powerpc/kernel/cacheinfo.c @@ -7,6 +7,8 @@ * Author: Nathan Lynch */ +#define pr_fmt(fmt) "cacheinfo: " fmt + #include #include #include -- GitLab From 3ae1f39aef08c5c584b0d0ce2186273be3f14d16 Mon Sep 17 00:00:00 2001 From: Sibi Sankar Date: Sat, 6 Jun 2020 03:03:30 +0530 Subject: [PATCH 1127/1476] OPP: Add and export helper to set bandwidth Add and export 'dev_pm_opp_set_bw' to set the bandwidth levels associated with an OPP. Signed-off-by: Sibi Sankar Signed-off-by: Viresh Kumar --- drivers/opp/core.c | 31 +++++++++++++++++++++++++++++++ include/linux/pm_opp.h | 6 ++++++ 2 files changed, 37 insertions(+) diff --git a/drivers/opp/core.c b/drivers/opp/core.c index dfbd3d10410ca..6937bf45f497f 100644 --- a/drivers/opp/core.c +++ b/drivers/opp/core.c @@ -831,6 +831,37 @@ static int _set_required_opps(struct device *dev, return ret; } +/** + * dev_pm_opp_set_bw() - sets bandwidth levels corresponding to an opp + * @dev: device for which we do this operation + * @opp: opp based on which the bandwidth levels are to be configured + * + * This configures the bandwidth to the levels specified by the OPP. However + * if the OPP specified is NULL the bandwidth levels are cleared out. + * + * Return: 0 on success or a negative error value. + */ +int dev_pm_opp_set_bw(struct device *dev, struct dev_pm_opp *opp) +{ + struct opp_table *opp_table; + int ret; + + opp_table = _find_opp_table(dev); + if (IS_ERR(opp_table)) { + dev_err(dev, "%s: device opp table doesn't exist\n", __func__); + return PTR_ERR(opp_table); + } + + if (opp) + ret = _set_opp_bw(opp_table, opp, dev, false); + else + ret = _set_opp_bw(opp_table, NULL, dev, true); + + dev_pm_opp_put_opp_table(opp_table); + return ret; +} +EXPORT_SYMBOL_GPL(dev_pm_opp_set_bw); + /** * dev_pm_opp_set_rate() - Configure new OPP based on frequency * @dev: device for which we do this operation diff --git a/include/linux/pm_opp.h b/include/linux/pm_opp.h index d5c4a329321dd..ae68417c0ae00 100644 --- a/include/linux/pm_opp.h +++ b/include/linux/pm_opp.h @@ -151,6 +151,7 @@ struct opp_table *dev_pm_opp_attach_genpd(struct device *dev, const char **names void dev_pm_opp_detach_genpd(struct opp_table *opp_table); int dev_pm_opp_xlate_performance_state(struct opp_table *src_table, struct opp_table *dst_table, unsigned int pstate); int dev_pm_opp_set_rate(struct device *dev, unsigned long target_freq); +int dev_pm_opp_set_bw(struct device *dev, struct dev_pm_opp *opp); int dev_pm_opp_set_sharing_cpus(struct device *cpu_dev, const struct cpumask *cpumask); int dev_pm_opp_get_sharing_cpus(struct device *cpu_dev, struct cpumask *cpumask); void dev_pm_opp_remove_table(struct device *dev); @@ -342,6 +343,11 @@ static inline int dev_pm_opp_set_rate(struct device *dev, unsigned long target_f return -ENOTSUPP; } +static inline int dev_pm_opp_set_bw(struct device *dev, struct dev_pm_opp *opp) +{ + return -EOPNOTSUPP; +} + static inline int dev_pm_opp_set_sharing_cpus(struct device *cpu_dev, const struct cpumask *cpumask) { return -ENOTSUPP; -- GitLab From 51c843cf77bb52db6df947c4fedcfc62ae3b7b30 Mon Sep 17 00:00:00 2001 From: Sibi Sankar Date: Mon, 22 Jun 2020 13:46:48 +0530 Subject: [PATCH 1128/1476] cpufreq: qcom: Update the bandwidth levels on frequency change Add support to parse optional OPP table attached to the cpu node when the OPP bandwidth values are populated. This allows for scaling of DDR/L3 bandwidth levels with frequency change. Signed-off-by: Sibi Sankar Reviewed-by: Matthias Kaehlcke Signed-off-by: Viresh Kumar --- drivers/cpufreq/qcom-cpufreq-hw.c | 83 ++++++++++++++++++++++++++++++- 1 file changed, 81 insertions(+), 2 deletions(-) diff --git a/drivers/cpufreq/qcom-cpufreq-hw.c b/drivers/cpufreq/qcom-cpufreq-hw.c index fc92a8842e252..aaf98333d37da 100644 --- a/drivers/cpufreq/qcom-cpufreq-hw.c +++ b/drivers/cpufreq/qcom-cpufreq-hw.c @@ -6,6 +6,7 @@ #include #include #include +#include #include #include #include @@ -30,6 +31,48 @@ static unsigned long cpu_hw_rate, xo_rate; static struct platform_device *global_pdev; +static bool icc_scaling_enabled; + +static int qcom_cpufreq_set_bw(struct cpufreq_policy *policy, + unsigned long freq_khz) +{ + unsigned long freq_hz = freq_khz * 1000; + struct dev_pm_opp *opp; + struct device *dev; + int ret; + + dev = get_cpu_device(policy->cpu); + if (!dev) + return -ENODEV; + + opp = dev_pm_opp_find_freq_exact(dev, freq_hz, true); + if (IS_ERR(opp)) + return PTR_ERR(opp); + + ret = dev_pm_opp_set_bw(dev, opp); + dev_pm_opp_put(opp); + return ret; +} + +static int qcom_cpufreq_update_opp(struct device *cpu_dev, + unsigned long freq_khz, + unsigned long volt) +{ + unsigned long freq_hz = freq_khz * 1000; + int ret; + + /* Skip voltage update if the opp table is not available */ + if (!icc_scaling_enabled) + return dev_pm_opp_add(cpu_dev, freq_hz, volt); + + ret = dev_pm_opp_adjust_voltage(cpu_dev, freq_hz, volt, volt, volt); + if (ret) { + dev_err(cpu_dev, "Voltage update failed freq=%ld\n", freq_khz); + return ret; + } + + return dev_pm_opp_enable(cpu_dev, freq_hz); +} static int qcom_cpufreq_hw_target_index(struct cpufreq_policy *policy, unsigned int index) @@ -39,6 +82,9 @@ static int qcom_cpufreq_hw_target_index(struct cpufreq_policy *policy, writel_relaxed(index, perf_state_reg); + if (icc_scaling_enabled) + qcom_cpufreq_set_bw(policy, freq); + arch_set_freq_scale(policy->related_cpus, freq, policy->cpuinfo.max_freq); return 0; @@ -89,11 +135,33 @@ static int qcom_cpufreq_hw_read_lut(struct device *cpu_dev, u32 data, src, lval, i, core_count, prev_freq = 0, freq; u32 volt; struct cpufreq_frequency_table *table; + struct dev_pm_opp *opp; + unsigned long rate; + int ret; table = kcalloc(LUT_MAX_ENTRIES + 1, sizeof(*table), GFP_KERNEL); if (!table) return -ENOMEM; + ret = dev_pm_opp_of_add_table(cpu_dev); + if (!ret) { + /* Disable all opps and cross-validate against LUT later */ + icc_scaling_enabled = true; + for (rate = 0; ; rate++) { + opp = dev_pm_opp_find_freq_ceil(cpu_dev, &rate); + if (IS_ERR(opp)) + break; + + dev_pm_opp_put(opp); + dev_pm_opp_disable(cpu_dev, rate); + } + } else if (ret != -ENODEV) { + dev_err(cpu_dev, "Invalid opp table in device tree\n"); + return ret; + } else { + icc_scaling_enabled = false; + } + for (i = 0; i < LUT_MAX_ENTRIES; i++) { data = readl_relaxed(base + REG_FREQ_LUT + i * LUT_ROW_SIZE); @@ -112,7 +180,7 @@ static int qcom_cpufreq_hw_read_lut(struct device *cpu_dev, if (freq != prev_freq && core_count != LUT_TURBO_IND) { table[i].frequency = freq; - dev_pm_opp_add(cpu_dev, freq * 1000, volt); + qcom_cpufreq_update_opp(cpu_dev, freq, volt); dev_dbg(cpu_dev, "index=%d freq=%d, core_count %d\n", i, freq, core_count); } else if (core_count == LUT_TURBO_IND) { @@ -133,7 +201,7 @@ static int qcom_cpufreq_hw_read_lut(struct device *cpu_dev, if (prev->frequency == CPUFREQ_ENTRY_INVALID) { prev->frequency = prev_freq; prev->flags = CPUFREQ_BOOST_FREQ; - dev_pm_opp_add(cpu_dev, prev_freq * 1000, volt); + qcom_cpufreq_update_opp(cpu_dev, prev_freq, volt); } break; @@ -254,6 +322,7 @@ static int qcom_cpufreq_hw_cpu_exit(struct cpufreq_policy *policy) void __iomem *base = policy->driver_data - REG_PERF_STATE; dev_pm_opp_remove_all_dynamic(cpu_dev); + dev_pm_opp_of_cpumask_remove_table(policy->related_cpus); kfree(policy->freq_table); devm_iounmap(&global_pdev->dev, base); @@ -282,6 +351,7 @@ static struct cpufreq_driver cpufreq_qcom_hw_driver = { static int qcom_cpufreq_hw_driver_probe(struct platform_device *pdev) { + struct device *cpu_dev; struct clk *clk; int ret; @@ -301,6 +371,15 @@ static int qcom_cpufreq_hw_driver_probe(struct platform_device *pdev) global_pdev = pdev; + /* Check for optional interconnect paths on CPU0 */ + cpu_dev = get_cpu_device(0); + if (!cpu_dev) + return -EPROBE_DEFER; + + ret = dev_pm_opp_of_find_icc_paths(cpu_dev, NULL); + if (ret) + return ret; + ret = cpufreq_register_driver(&cpufreq_qcom_hw_driver); if (ret) dev_err(&pdev->dev, "CPUFreq HW driver failed to register\n"); -- GitLab From afdb219bab58dc2935e2c16adae2b2ce2a242386 Mon Sep 17 00:00:00 2001 From: Sibi Sankar Date: Mon, 22 Jun 2020 13:46:49 +0530 Subject: [PATCH 1129/1476] cpufreq: qcom: Disable fast switch when scaling DDR/L3 Disable fast switch when the opp-tables required for scaling DDR/L3 are populated. Signed-off-by: Sibi Sankar Reviewed-by: Matthias Kaehlcke Signed-off-by: Viresh Kumar --- drivers/cpufreq/qcom-cpufreq-hw.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/cpufreq/qcom-cpufreq-hw.c b/drivers/cpufreq/qcom-cpufreq-hw.c index aaf98333d37da..fa68fa8ebd95e 100644 --- a/drivers/cpufreq/qcom-cpufreq-hw.c +++ b/drivers/cpufreq/qcom-cpufreq-hw.c @@ -159,6 +159,7 @@ static int qcom_cpufreq_hw_read_lut(struct device *cpu_dev, dev_err(cpu_dev, "Invalid opp table in device tree\n"); return ret; } else { + policy->fast_switch_possible = true; icc_scaling_enabled = false; } @@ -308,8 +309,6 @@ static int qcom_cpufreq_hw_cpu_init(struct cpufreq_policy *policy) dev_pm_opp_of_register_em(policy->cpus); - policy->fast_switch_possible = true; - return 0; error: devm_iounmap(dev, base); -- GitLab From b75acfb45e4248c0b130a3fca64754ad0bb2753f Mon Sep 17 00:00:00 2001 From: Markus Mayer Date: Thu, 28 May 2020 11:20:12 -0700 Subject: [PATCH 1130/1476] cpufreq: brcmstb-avs-cpufreq: more flexible interface for __issue_avs_command() We are changing how parameters are passed to __issue_avs_command(), so we can pass input *and* output arguments with the same command, rather than just one or the other. Signed-off-by: Markus Mayer Acked-by: Florian Fainelli Signed-off-by: Viresh Kumar --- drivers/cpufreq/brcmstb-avs-cpufreq.c | 30 +++++++++++++-------------- 1 file changed, 14 insertions(+), 16 deletions(-) diff --git a/drivers/cpufreq/brcmstb-avs-cpufreq.c b/drivers/cpufreq/brcmstb-avs-cpufreq.c index 4f86ce2db34fb..c8b754694a5e5 100644 --- a/drivers/cpufreq/brcmstb-avs-cpufreq.c +++ b/drivers/cpufreq/brcmstb-avs-cpufreq.c @@ -195,7 +195,8 @@ static void __iomem *__map_region(const char *name) return ptr; } -static int __issue_avs_command(struct private_data *priv, int cmd, bool is_send, +static int __issue_avs_command(struct private_data *priv, unsigned int cmd, + unsigned int num_in, unsigned int num_out, u32 args[]) { unsigned long time_left = msecs_to_jiffies(AVS_TIMEOUT); @@ -225,11 +226,9 @@ static int __issue_avs_command(struct private_data *priv, int cmd, bool is_send, /* Clear status before we begin. */ writel(AVS_STATUS_CLEAR, base + AVS_MBOX_STATUS); - /* We need to send arguments for this command. */ - if (args && is_send) { - for (i = 0; i < AVS_MAX_CMD_ARGS; i++) - writel(args[i], base + AVS_MBOX_PARAM(i)); - } + /* Provide input parameters */ + for (i = 0; i < num_in; i++) + writel(args[i], base + AVS_MBOX_PARAM(i)); /* Protect from spurious interrupts. */ reinit_completion(&priv->done); @@ -256,11 +255,9 @@ static int __issue_avs_command(struct private_data *priv, int cmd, bool is_send, goto out; } - /* This command returned arguments, so we read them back. */ - if (args && !is_send) { - for (i = 0; i < AVS_MAX_CMD_ARGS; i++) - args[i] = readl(base + AVS_MBOX_PARAM(i)); - } + /* Process returned values */ + for (i = 0; i < num_out; i++) + args[i] = readl(base + AVS_MBOX_PARAM(i)); /* Clear status to tell AVS co-processor we are done. */ writel(AVS_STATUS_CLEAR, base + AVS_MBOX_STATUS); @@ -338,7 +335,7 @@ static int brcm_avs_get_pmap(struct private_data *priv, struct pmap *pmap) u32 args[AVS_MAX_CMD_ARGS]; int ret; - ret = __issue_avs_command(priv, AVS_CMD_GET_PMAP, false, args); + ret = __issue_avs_command(priv, AVS_CMD_GET_PMAP, 0, 4, args); if (ret || !pmap) return ret; @@ -359,7 +356,7 @@ static int brcm_avs_set_pmap(struct private_data *priv, struct pmap *pmap) args[2] = pmap->p2; args[3] = pmap->state; - return __issue_avs_command(priv, AVS_CMD_SET_PMAP, true, args); + return __issue_avs_command(priv, AVS_CMD_SET_PMAP, 4, 0, args); } static int brcm_avs_get_pstate(struct private_data *priv, unsigned int *pstate) @@ -367,7 +364,7 @@ static int brcm_avs_get_pstate(struct private_data *priv, unsigned int *pstate) u32 args[AVS_MAX_CMD_ARGS]; int ret; - ret = __issue_avs_command(priv, AVS_CMD_GET_PSTATE, false, args); + ret = __issue_avs_command(priv, AVS_CMD_GET_PSTATE, 0, 1, args); if (ret) return ret; *pstate = args[0]; @@ -381,7 +378,8 @@ static int brcm_avs_set_pstate(struct private_data *priv, unsigned int pstate) args[0] = pstate; - return __issue_avs_command(priv, AVS_CMD_SET_PSTATE, true, args); + return __issue_avs_command(priv, AVS_CMD_SET_PSTATE, 1, 0, args); + } static u32 brcm_avs_get_voltage(void __iomem *base) @@ -593,7 +591,7 @@ static int brcm_avs_cpufreq_init(struct cpufreq_policy *policy) /* All cores share the same clock and thus the same policy. */ cpumask_setall(policy->cpus); - ret = __issue_avs_command(priv, AVS_CMD_ENABLE, false, NULL); + ret = __issue_avs_command(priv, AVS_CMD_ENABLE, 0, 0, NULL); if (!ret) { unsigned int pstate; -- GitLab From 08535ccdd787a146c1a03808215572af95d8c8c1 Mon Sep 17 00:00:00 2001 From: Florian Fainelli Date: Thu, 28 May 2020 11:20:13 -0700 Subject: [PATCH 1131/1476] cpufreq: brcmstb-avs-cpufreq: Support polling AVS firmware In case the interrupt towards the host is never raised, yet the AVS firmware responds correctly within the alloted time, allow supporting a polling mode. Signed-off-by: Florian Fainelli Signed-off-by: Markus Mayer Signed-off-by: Viresh Kumar --- drivers/cpufreq/brcmstb-avs-cpufreq.c | 47 +++++++++++++++++++-------- 1 file changed, 34 insertions(+), 13 deletions(-) diff --git a/drivers/cpufreq/brcmstb-avs-cpufreq.c b/drivers/cpufreq/brcmstb-avs-cpufreq.c index c8b754694a5e5..79a0538a531af 100644 --- a/drivers/cpufreq/brcmstb-avs-cpufreq.c +++ b/drivers/cpufreq/brcmstb-avs-cpufreq.c @@ -42,6 +42,7 @@ */ #include +#include #include #include #include @@ -178,6 +179,7 @@ struct private_data { struct completion done; struct semaphore sem; struct pmap pmap; + int host_irq; }; static void __iomem *__map_region(const char *name) @@ -195,12 +197,36 @@ static void __iomem *__map_region(const char *name) return ptr; } +static unsigned long wait_for_avs_command(struct private_data *priv, + unsigned long timeout) +{ + unsigned long time_left = 0; + u32 val; + + /* Event driven, wait for the command interrupt */ + if (priv->host_irq >= 0) + return wait_for_completion_timeout(&priv->done, + msecs_to_jiffies(timeout)); + + /* Polling for command completion */ + do { + time_left = timeout; + val = readl(priv->base + AVS_MBOX_STATUS); + if (val) + break; + + usleep_range(1000, 2000); + } while (--timeout); + + return time_left; +} + static int __issue_avs_command(struct private_data *priv, unsigned int cmd, unsigned int num_in, unsigned int num_out, u32 args[]) { - unsigned long time_left = msecs_to_jiffies(AVS_TIMEOUT); void __iomem *base = priv->base; + unsigned long time_left; unsigned int i; int ret; u32 val; @@ -238,7 +264,7 @@ static int __issue_avs_command(struct private_data *priv, unsigned int cmd, writel(AVS_CPU_L2_INT_MASK, priv->avs_intr_base + AVS_CPU_L2_SET0); /* Wait for AVS co-processor to finish processing the command. */ - time_left = wait_for_completion_timeout(&priv->done, time_left); + time_left = wait_for_avs_command(priv, AVS_TIMEOUT); /* * If the AVS status is not in the expected range, it means AVS didn't @@ -509,7 +535,7 @@ static int brcm_avs_prepare_init(struct platform_device *pdev) { struct private_data *priv; struct device *dev; - int host_irq, ret; + int ret; dev = &pdev->dev; priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); @@ -536,19 +562,14 @@ static int brcm_avs_prepare_init(struct platform_device *pdev) goto unmap_base; } - host_irq = platform_get_irq_byname(pdev, BRCM_AVS_HOST_INTR); - if (host_irq < 0) { - dev_err(dev, "Couldn't find interrupt %s -- %d\n", - BRCM_AVS_HOST_INTR, host_irq); - ret = host_irq; - goto unmap_intr_base; - } + priv->host_irq = platform_get_irq_byname(pdev, BRCM_AVS_HOST_INTR); - ret = devm_request_irq(dev, host_irq, irq_handler, IRQF_TRIGGER_RISING, + ret = devm_request_irq(dev, priv->host_irq, irq_handler, + IRQF_TRIGGER_RISING, BRCM_AVS_HOST_INTR, priv); - if (ret) { + if (ret && priv->host_irq >= 0) { dev_err(dev, "IRQ request failed: %s (%d) -- %d\n", - BRCM_AVS_HOST_INTR, host_irq, ret); + BRCM_AVS_HOST_INTR, priv->host_irq, ret); goto unmap_intr_base; } -- GitLab From d48461b2c7c39c89dec598e6ea6e188fdaf418e7 Mon Sep 17 00:00:00 2001 From: Markus Mayer Date: Thu, 28 May 2020 11:20:14 -0700 Subject: [PATCH 1132/1476] cpufreq: brcmstb-avs-cpufreq: send S2_ENTER / S2_EXIT commands to AVS On suspend we send AVS_CMD_S2_ENTER and on resume AVS_CMD_S2_EXIT. These are best effort calls, so we don't check the return code or take any action if either of the calls fails. Signed-off-by: Markus Mayer Acked-by: Florian Fainelli Signed-off-by: Viresh Kumar --- drivers/cpufreq/brcmstb-avs-cpufreq.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/drivers/cpufreq/brcmstb-avs-cpufreq.c b/drivers/cpufreq/brcmstb-avs-cpufreq.c index 79a0538a531af..3e31e5d28b79c 100644 --- a/drivers/cpufreq/brcmstb-avs-cpufreq.c +++ b/drivers/cpufreq/brcmstb-avs-cpufreq.c @@ -506,7 +506,14 @@ static int brcm_avs_suspend(struct cpufreq_policy *policy) * AVS co-processor, not necessarily the P-state we are running at now. * So, we get the current P-state explicitly. */ - return brcm_avs_get_pstate(priv, &priv->pmap.state); + ret = brcm_avs_get_pstate(priv, &priv->pmap.state); + if (ret) + return ret; + + /* This is best effort. Nothing to do if it fails. */ + (void)__issue_avs_command(priv, AVS_CMD_S2_ENTER, 0, 0, NULL); + + return 0; } static int brcm_avs_resume(struct cpufreq_policy *policy) @@ -514,6 +521,9 @@ static int brcm_avs_resume(struct cpufreq_policy *policy) struct private_data *priv = policy->driver_data; int ret; + /* This is best effort. Nothing to do if it fails. */ + (void)__issue_avs_command(priv, AVS_CMD_S2_EXIT, 0, 0, NULL); + ret = brcm_avs_set_pmap(priv, &priv->pmap); if (ret == -EEXIST) { struct platform_device *pdev = cpufreq_get_driver_data(); -- GitLab From 10470dec3decaf5ed3c596f85debd7c42777ae12 Mon Sep 17 00:00:00 2001 From: Ivan Kokshaysky Date: Sat, 20 Jun 2020 17:44:49 +0100 Subject: [PATCH 1133/1476] cpufreq: dt: fix oops on armada37xx Commit 0c868627e617e43a295d8 (cpufreq: dt: Allow platform specific intermediate callbacks) added two function pointers to the struct cpufreq_dt_platform_data. However, armada37xx_cpufreq_driver_init() has this struct (pdata) located on the stack and uses only "suspend" and "resume" fields. So these newly added "get_intermediate" and "target_intermediate" pointers are uninitialized and contain arbitrary non-null values, causing all kinds of trouble. For instance, here is an oops on espressobin after an attempt to change the cpefreq governor: [ 29.174554] Unable to handle kernel execute from non-executable memory at virtual address ffff00003f87bdc0 ... [ 29.269373] pc : 0xffff00003f87bdc0 [ 29.272957] lr : __cpufreq_driver_target+0x138/0x580 ... Fixed by zeroing out pdata before use. Cc: # v5.7+ Signed-off-by: Ivan Kokshaysky Reviewed-by: Andrew Lunn Signed-off-by: Viresh Kumar --- drivers/cpufreq/armada-37xx-cpufreq.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/cpufreq/armada-37xx-cpufreq.c b/drivers/cpufreq/armada-37xx-cpufreq.c index aa0f06dec959c..df1c941260d14 100644 --- a/drivers/cpufreq/armada-37xx-cpufreq.c +++ b/drivers/cpufreq/armada-37xx-cpufreq.c @@ -456,6 +456,7 @@ static int __init armada37xx_cpufreq_driver_init(void) /* Now that everything is setup, enable the DVFS at hardware level */ armada37xx_cpufreq_enable_dvfs(nb_pm_base); + memset(&pdata, 0, sizeof(pdata)); pdata.suspend = armada37xx_cpufreq_suspend; pdata.resume = armada37xx_cpufreq_resume; -- GitLab From d88b0f0edb9041be6feff98e83cd6002e4ab534e Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Tue, 23 Jun 2020 10:38:47 +0530 Subject: [PATCH 1134/1476] cpufreq: cppc: Reorder code and remove apply_hisi_workaround variable With the current approach we have an extra check in the cppc_cpufreq_get_rate() callback, which checks if hisilicon's get rate implementation should be used instead. While it works fine, the approach isn't very straight forward, over that we have an extra check in the routine. Rearrange code and update the cpufreq driver's get() callback pointer directly for the hisilicon case. This gets the extra variable is removed and the extra check isn't required anymore as well. Tested-by: Xiongfeng Wang Signed-off-by: Viresh Kumar --- drivers/cpufreq/cppc_cpufreq.c | 91 ++++++++++++++++------------------ 1 file changed, 42 insertions(+), 49 deletions(-) diff --git a/drivers/cpufreq/cppc_cpufreq.c b/drivers/cpufreq/cppc_cpufreq.c index 257d726a4456b..03a21daddbec0 100644 --- a/drivers/cpufreq/cppc_cpufreq.c +++ b/drivers/cpufreq/cppc_cpufreq.c @@ -45,8 +45,6 @@ struct cppc_workaround_oem_info { u32 oem_revision; }; -static bool apply_hisi_workaround; - static struct cppc_workaround_oem_info wa_info[] = { { .oem_id = "HISI ", @@ -59,50 +57,6 @@ static struct cppc_workaround_oem_info wa_info[] = { } }; -static unsigned int cppc_cpufreq_perf_to_khz(struct cppc_cpudata *cpu, - unsigned int perf); - -/* - * HISI platform does not support delivered performance counter and - * reference performance counter. It can calculate the performance using the - * platform specific mechanism. We reuse the desired performance register to - * store the real performance calculated by the platform. - */ -static unsigned int hisi_cppc_cpufreq_get_rate(unsigned int cpunum) -{ - struct cppc_cpudata *cpudata = all_cpu_data[cpunum]; - u64 desired_perf; - int ret; - - ret = cppc_get_desired_perf(cpunum, &desired_perf); - if (ret < 0) - return -EIO; - - return cppc_cpufreq_perf_to_khz(cpudata, desired_perf); -} - -static void cppc_check_hisi_workaround(void) -{ - struct acpi_table_header *tbl; - acpi_status status = AE_OK; - int i; - - status = acpi_get_table(ACPI_SIG_PCCT, 0, &tbl); - if (ACPI_FAILURE(status) || !tbl) - return; - - for (i = 0; i < ARRAY_SIZE(wa_info); i++) { - if (!memcmp(wa_info[i].oem_id, tbl->oem_id, ACPI_OEM_ID_SIZE) && - !memcmp(wa_info[i].oem_table_id, tbl->oem_table_id, ACPI_OEM_TABLE_ID_SIZE) && - wa_info[i].oem_revision == tbl->oem_revision) { - apply_hisi_workaround = true; - break; - } - } - - acpi_put_table(tbl); -} - /* Callback function used to retrieve the max frequency from DMI */ static void cppc_find_dmi_mhz(const struct dmi_header *dm, void *private) { @@ -402,9 +356,6 @@ static unsigned int cppc_cpufreq_get_rate(unsigned int cpunum) struct cppc_cpudata *cpu = all_cpu_data[cpunum]; int ret; - if (apply_hisi_workaround) - return hisi_cppc_cpufreq_get_rate(cpunum); - ret = cppc_get_perf_ctrs(cpunum, &fb_ctrs_t0); if (ret) return ret; @@ -455,6 +406,48 @@ static struct cpufreq_driver cppc_cpufreq_driver = { .name = "cppc_cpufreq", }; +/* + * HISI platform does not support delivered performance counter and + * reference performance counter. It can calculate the performance using the + * platform specific mechanism. We reuse the desired performance register to + * store the real performance calculated by the platform. + */ +static unsigned int hisi_cppc_cpufreq_get_rate(unsigned int cpunum) +{ + struct cppc_cpudata *cpudata = all_cpu_data[cpunum]; + u64 desired_perf; + int ret; + + ret = cppc_get_desired_perf(cpunum, &desired_perf); + if (ret < 0) + return -EIO; + + return cppc_cpufreq_perf_to_khz(cpudata, desired_perf); +} + +static void cppc_check_hisi_workaround(void) +{ + struct acpi_table_header *tbl; + acpi_status status = AE_OK; + int i; + + status = acpi_get_table(ACPI_SIG_PCCT, 0, &tbl); + if (ACPI_FAILURE(status) || !tbl) + return; + + for (i = 0; i < ARRAY_SIZE(wa_info); i++) { + if (!memcmp(wa_info[i].oem_id, tbl->oem_id, ACPI_OEM_ID_SIZE) && + !memcmp(wa_info[i].oem_table_id, tbl->oem_table_id, ACPI_OEM_TABLE_ID_SIZE) && + wa_info[i].oem_revision == tbl->oem_revision) { + /* Overwrite the get() callback */ + cppc_cpufreq_driver.get = hisi_cppc_cpufreq_get_rate; + break; + } + } + + acpi_put_table(tbl); +} + static int __init cppc_cpufreq_init(void) { int i, ret = 0; -- GitLab From 8c37ad2f523396e15cf002b29f8f796447c71932 Mon Sep 17 00:00:00 2001 From: Sven Auhagen Date: Mon, 22 Jun 2020 14:01:23 +0200 Subject: [PATCH 1135/1476] cpufreq: ap806: fix cpufreq driver needs ap cpu clk The Armada 8K cpufreq driver needs the Armada AP CPU CLK to work. This dependency is currently not satisfied and the ARMADA_AP_CPU_CLK can not be selected independently. Add it to the cpufreq Armada8k driver. Fixes: f525a670533d ("cpufreq: ap806: add cpufreq driver for Armada 8K") Signed-off-by: Sven Auhagen Signed-off-by: Viresh Kumar --- drivers/cpufreq/Kconfig.arm | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/cpufreq/Kconfig.arm b/drivers/cpufreq/Kconfig.arm index c6cbfc8baf724..a967894c4613e 100644 --- a/drivers/cpufreq/Kconfig.arm +++ b/drivers/cpufreq/Kconfig.arm @@ -41,6 +41,7 @@ config ARM_ARMADA_37XX_CPUFREQ config ARM_ARMADA_8K_CPUFREQ tristate "Armada 8K CPUFreq driver" depends on ARCH_MVEBU && CPUFREQ_DT + select ARMADA_AP_CPU_CLK help This enables the CPUFreq driver support for Marvell Armada8k SOCs. -- GitLab From 4264e02d3e1865549952abe2b6d7c61a1e07ca62 Mon Sep 17 00:00:00 2001 From: Xin Hao Date: Wed, 1 Jul 2020 11:26:10 +0800 Subject: [PATCH 1136/1476] cpufreq: CPPC: Reuse caps variable in few routines The 'caps' variable has been defined in cppc_cpufreq_khz_to_perf() and cppc_cpufreq_perf_to_khz() routines, so there is no need to get 'highest_perf' value through 'cpu->caps.highest_perf', we can use 'caps->highest_perf' instead. Signed-off-by: Xin Hao [ Viresh: Updated commit log ] Signed-off-by: Viresh Kumar --- drivers/cpufreq/cppc_cpufreq.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/cpufreq/cppc_cpufreq.c b/drivers/cpufreq/cppc_cpufreq.c index 03a21daddbec0..f29e8d0553a82 100644 --- a/drivers/cpufreq/cppc_cpufreq.c +++ b/drivers/cpufreq/cppc_cpufreq.c @@ -115,7 +115,7 @@ static unsigned int cppc_cpufreq_perf_to_khz(struct cppc_cpudata *cpu, if (!max_khz) max_khz = cppc_get_dmi_max_khz(); mul = max_khz; - div = cpu->perf_caps.highest_perf; + div = caps->highest_perf; } return (u64)perf * mul / div; } @@ -138,7 +138,7 @@ static unsigned int cppc_cpufreq_khz_to_perf(struct cppc_cpudata *cpu, } else { if (!max_khz) max_khz = cppc_get_dmi_max_khz(); - mul = cpu->perf_caps.highest_perf; + mul = caps->highest_perf; div = max_khz; } -- GitLab From 02fd918097f998234c6e9c50c8afad73e7b946e4 Mon Sep 17 00:00:00 2001 From: Jon Hunter Date: Sun, 12 Jul 2020 11:06:45 +0100 Subject: [PATCH 1137/1476] cpufreq: tegra186: Simplify probe return path We always put the reference to BPMP device on exit of the Tegra186 CPUFREQ driver and so there is no need to have separate exit paths for success and failure. Therefore, simplify the probe return path in the Tegra186 CPUFREQ driver by combining the success and failure paths. Signed-off-by: Jon Hunter Signed-off-by: Viresh Kumar --- drivers/cpufreq/tegra186-cpufreq.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/drivers/cpufreq/tegra186-cpufreq.c b/drivers/cpufreq/tegra186-cpufreq.c index 3d2f143748ef8..01e1f58ba4227 100644 --- a/drivers/cpufreq/tegra186-cpufreq.c +++ b/drivers/cpufreq/tegra186-cpufreq.c @@ -223,15 +223,9 @@ static int tegra186_cpufreq_probe(struct platform_device *pdev) } } - tegra_bpmp_put(bpmp); - tegra186_cpufreq_driver.driver_data = data; err = cpufreq_register_driver(&tegra186_cpufreq_driver); - if (err) - return err; - - return 0; put_bpmp: tegra_bpmp_put(bpmp); -- GitLab From 34adbcbd0acc1dfa0d40dc1be1c69ba0df555d1a Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Tue, 14 Jul 2020 15:50:40 +0100 Subject: [PATCH 1138/1476] cpufreq: sti-cpufreq: Fix some formatting and misspelling issues Kerneldoc format for attribute descriptions should be '@.*: '. Fixes the following W=1 kernel build warning(s): drivers/cpufreq/sti-cpufreq.c:49: warning: cannot understand function prototype: 'struct sti_cpufreq_ddata ' Signed-off-by: Lee Jones Cc: Patrice Chotard Cc: Pal Singh Signed-off-by: Viresh Kumar --- drivers/cpufreq/sti-cpufreq.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/cpufreq/sti-cpufreq.c b/drivers/cpufreq/sti-cpufreq.c index 8f16bbb164b84..a5ad96d29adca 100644 --- a/drivers/cpufreq/sti-cpufreq.c +++ b/drivers/cpufreq/sti-cpufreq.c @@ -40,11 +40,11 @@ enum { }; /** - * ST CPUFreq Driver Data + * struct sti_cpufreq_ddata - ST CPUFreq Driver Data * - * @cpu_node CPU's OF node - * @syscfg_eng Engineering Syscon register map - * @regmap Syscon register map + * @cpu: CPU's OF node + * @syscfg_eng: Engineering Syscon register map + * @syscfg: Syscon register map */ static struct sti_cpufreq_ddata { struct device *cpu; -- GitLab From 2782ef34ed230e1b8dbea9ad46e5fc2ca6cfecfb Mon Sep 17 00:00:00 2001 From: Walter Lozano Date: Tue, 14 Jul 2020 15:43:56 -0300 Subject: [PATCH 1139/1476] cpufreq: imx: Select NVMEM_IMX_OCOTP When probing cpufreq for iMX6 the values in the efuse needs to be read which requires NVMEM_IMX_OCOTP. If this option is not enabled, the probe will be deferred forever and cpufreq won't be available. This patch forces the selection of the required configuration option. Signed-off-by: Walter Lozano Signed-off-by: Viresh Kumar --- drivers/cpufreq/Kconfig.arm | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/cpufreq/Kconfig.arm b/drivers/cpufreq/Kconfig.arm index a967894c4613e..06bdbd10fc74d 100644 --- a/drivers/cpufreq/Kconfig.arm +++ b/drivers/cpufreq/Kconfig.arm @@ -94,6 +94,7 @@ config ARM_IMX6Q_CPUFREQ tristate "Freescale i.MX6 cpufreq support" depends on ARCH_MXC depends on REGULATOR_ANATOP + select NVMEM_IMX_OCOTP select PM_OPP help This adds cpufreq driver support for Freescale i.MX6 series SoCs. -- GitLab From 9b25d3f425d25262b22d4c2df682b04785236d1d Mon Sep 17 00:00:00 2001 From: Sumit Gupta Date: Wed, 15 Jul 2020 19:01:23 +0530 Subject: [PATCH 1140/1476] dt-bindings: arm: Add NVIDIA Tegra194 CPU Complex binding Add device-tree binding documentation to represent Tegra194 CPU Complex with compatible string under 'cpus' node. This can be used by drivers like cpufreq which don't have their node or CPU Complex node to bind to. Also, documenting 'nvidia,bpmp' property which points to BPMP device. Signed-off-by: Sumit Gupta Reviewed-by: Rob Herring Signed-off-by: Viresh Kumar --- .../bindings/arm/nvidia,tegra194-ccplex.yaml | 69 +++++++++++++++++++ 1 file changed, 69 insertions(+) create mode 100644 Documentation/devicetree/bindings/arm/nvidia,tegra194-ccplex.yaml diff --git a/Documentation/devicetree/bindings/arm/nvidia,tegra194-ccplex.yaml b/Documentation/devicetree/bindings/arm/nvidia,tegra194-ccplex.yaml new file mode 100644 index 0000000000000..1043e4be4fcae --- /dev/null +++ b/Documentation/devicetree/bindings/arm/nvidia,tegra194-ccplex.yaml @@ -0,0 +1,69 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: "http://devicetree.org/schemas/arm/nvidia,tegra194-ccplex.yaml#" +$schema: "http://devicetree.org/meta-schemas/core.yaml#" + +title: NVIDIA Tegra194 CPU Complex device tree bindings + +maintainers: + - Thierry Reding + - Jonathan Hunter + - Sumit Gupta + +description: |+ + Tegra194 SOC has homogeneous architecture where each cluster has two + symmetric cores. Compatible string in "cpus" node represents the CPU + Complex having all clusters. + +properties: + $nodename: + const: cpus + + compatible: + enum: + - nvidia,tegra194-ccplex + + nvidia,bpmp: + $ref: '/schemas/types.yaml#/definitions/phandle' + description: | + Specifies the bpmp node that needs to be queried to get + operating point data for all CPUs. + +examples: + - | + cpus { + compatible = "nvidia,tegra194-ccplex"; + nvidia,bpmp = <&bpmp>; + #address-cells = <1>; + #size-cells = <0>; + + cpu0_0: cpu@0 { + compatible = "nvidia,tegra194-carmel"; + device_type = "cpu"; + reg = <0x0>; + enable-method = "psci"; + }; + + cpu0_1: cpu@1 { + compatible = "nvidia,tegra194-carmel"; + device_type = "cpu"; + reg = <0x001>; + enable-method = "psci"; + }; + + cpu1_0: cpu@100 { + compatible = "nvidia,tegra194-carmel"; + device_type = "cpu"; + reg = <0x100>; + enable-method = "psci"; + }; + + cpu1_1: cpu@101 { + compatible = "nvidia,tegra194-carmel"; + device_type = "cpu"; + reg = <0x101>; + enable-method = "psci"; + }; + }; +... -- GitLab From df320f89359c0cc22ff552da3ffd07171f7754a6 Mon Sep 17 00:00:00 2001 From: Sumit Gupta Date: Thu, 16 Jul 2020 14:00:01 +0530 Subject: [PATCH 1141/1476] cpufreq: Add Tegra194 cpufreq driver Add support for CPU frequency scaling on Tegra194. The frequency of each core can be adjusted by writing a clock divisor value to a MSR on the core. The range of valid divisors is queried from the BPMP. Signed-off-by: Mikko Perttunen Signed-off-by: Sumit Gupta Signed-off-by: Viresh Kumar --- drivers/cpufreq/Kconfig.arm | 7 + drivers/cpufreq/Makefile | 1 + drivers/cpufreq/tegra194-cpufreq.c | 390 +++++++++++++++++++++++++++++ 3 files changed, 398 insertions(+) create mode 100644 drivers/cpufreq/tegra194-cpufreq.c diff --git a/drivers/cpufreq/Kconfig.arm b/drivers/cpufreq/Kconfig.arm index 06bdbd10fc74d..cb72fb507d574 100644 --- a/drivers/cpufreq/Kconfig.arm +++ b/drivers/cpufreq/Kconfig.arm @@ -316,6 +316,13 @@ config ARM_TEGRA186_CPUFREQ help This adds the CPUFreq driver support for Tegra186 SOCs. +config ARM_TEGRA194_CPUFREQ + tristate "Tegra194 CPUFreq support" + depends on ARCH_TEGRA_194_SOC && TEGRA_BPMP + default y + help + This adds CPU frequency driver support for Tegra194 SOCs. + config ARM_TI_CPUFREQ bool "Texas Instruments CPUFreq support" depends on ARCH_OMAP2PLUS diff --git a/drivers/cpufreq/Makefile b/drivers/cpufreq/Makefile index f6670c4abbb03..66b5563095c40 100644 --- a/drivers/cpufreq/Makefile +++ b/drivers/cpufreq/Makefile @@ -83,6 +83,7 @@ obj-$(CONFIG_ARM_TANGO_CPUFREQ) += tango-cpufreq.o obj-$(CONFIG_ARM_TEGRA20_CPUFREQ) += tegra20-cpufreq.o obj-$(CONFIG_ARM_TEGRA124_CPUFREQ) += tegra124-cpufreq.o obj-$(CONFIG_ARM_TEGRA186_CPUFREQ) += tegra186-cpufreq.o +obj-$(CONFIG_ARM_TEGRA194_CPUFREQ) += tegra194-cpufreq.o obj-$(CONFIG_ARM_TI_CPUFREQ) += ti-cpufreq.o obj-$(CONFIG_ARM_VEXPRESS_SPC_CPUFREQ) += vexpress-spc-cpufreq.o diff --git a/drivers/cpufreq/tegra194-cpufreq.c b/drivers/cpufreq/tegra194-cpufreq.c new file mode 100644 index 0000000000000..bae527e507e01 --- /dev/null +++ b/drivers/cpufreq/tegra194-cpufreq.c @@ -0,0 +1,390 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2020, NVIDIA CORPORATION. All rights reserved + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +#define KHZ 1000 +#define REF_CLK_MHZ 408 /* 408 MHz */ +#define US_DELAY 500 +#define US_DELAY_MIN 2 +#define CPUFREQ_TBL_STEP_HZ (50 * KHZ * KHZ) +#define MAX_CNT ~0U + +/* cpufreq transisition latency */ +#define TEGRA_CPUFREQ_TRANSITION_LATENCY (300 * 1000) /* unit in nanoseconds */ + +enum cluster { + CLUSTER0, + CLUSTER1, + CLUSTER2, + CLUSTER3, + MAX_CLUSTERS, +}; + +struct tegra194_cpufreq_data { + void __iomem *regs; + size_t num_clusters; + struct cpufreq_frequency_table **tables; +}; + +struct tegra_cpu_ctr { + u32 cpu; + u32 delay; + u32 coreclk_cnt, last_coreclk_cnt; + u32 refclk_cnt, last_refclk_cnt; +}; + +struct read_counters_work { + struct work_struct work; + struct tegra_cpu_ctr c; +}; + +static struct workqueue_struct *read_counters_wq; + +static enum cluster get_cpu_cluster(u8 cpu) +{ + return MPIDR_AFFINITY_LEVEL(cpu_logical_map(cpu), 1); +} + +/* + * Read per-core Read-only system register NVFREQ_FEEDBACK_EL1. + * The register provides frequency feedback information to + * determine the average actual frequency a core has run at over + * a period of time. + * [31:0] PLLP counter: Counts at fixed frequency (408 MHz) + * [63:32] Core clock counter: counts on every core clock cycle + * where the core is architecturally clocking + */ +static u64 read_freq_feedback(void) +{ + u64 val = 0; + + asm volatile("mrs %0, s3_0_c15_c0_5" : "=r" (val) : ); + + return val; +} + +static inline u32 map_ndiv_to_freq(struct mrq_cpu_ndiv_limits_response + *nltbl, u16 ndiv) +{ + return nltbl->ref_clk_hz / KHZ * ndiv / (nltbl->pdiv * nltbl->mdiv); +} + +static void tegra_read_counters(struct work_struct *work) +{ + struct read_counters_work *read_counters_work; + struct tegra_cpu_ctr *c; + u64 val; + + /* + * ref_clk_counter(32 bit counter) runs on constant clk, + * pll_p(408MHz). + * It will take = 2 ^ 32 / 408 MHz to overflow ref clk counter + * = 10526880 usec = 10.527 sec to overflow + * + * Like wise core_clk_counter(32 bit counter) runs on core clock. + * It's synchronized to crab_clk (cpu_crab_clk) which runs at + * freq of cluster. Assuming max cluster clock ~2000MHz, + * It will take = 2 ^ 32 / 2000 MHz to overflow core clk counter + * = ~2.147 sec to overflow + */ + read_counters_work = container_of(work, struct read_counters_work, + work); + c = &read_counters_work->c; + + val = read_freq_feedback(); + c->last_refclk_cnt = lower_32_bits(val); + c->last_coreclk_cnt = upper_32_bits(val); + udelay(c->delay); + val = read_freq_feedback(); + c->refclk_cnt = lower_32_bits(val); + c->coreclk_cnt = upper_32_bits(val); +} + +/* + * Return instantaneous cpu speed + * Instantaneous freq is calculated as - + * -Takes sample on every query of getting the freq. + * - Read core and ref clock counters; + * - Delay for X us + * - Read above cycle counters again + * - Calculates freq by subtracting current and previous counters + * divided by the delay time or eqv. of ref_clk_counter in delta time + * - Return Kcycles/second, freq in KHz + * + * delta time period = x sec + * = delta ref_clk_counter / (408 * 10^6) sec + * freq in Hz = cycles/sec + * = (delta cycles / x sec + * = (delta cycles * 408 * 10^6) / delta ref_clk_counter + * in KHz = (delta cycles * 408 * 10^3) / delta ref_clk_counter + * + * @cpu - logical cpu whose freq to be updated + * Returns freq in KHz on success, 0 if cpu is offline + */ +static unsigned int tegra194_get_speed_common(u32 cpu, u32 delay) +{ + struct read_counters_work read_counters_work; + struct tegra_cpu_ctr c; + u32 delta_refcnt; + u32 delta_ccnt; + u32 rate_mhz; + + /* + * udelay() is required to reconstruct cpu frequency over an + * observation window. Using workqueue to call udelay() with + * interrupts enabled. + */ + read_counters_work.c.cpu = cpu; + read_counters_work.c.delay = delay; + INIT_WORK_ONSTACK(&read_counters_work.work, tegra_read_counters); + queue_work_on(cpu, read_counters_wq, &read_counters_work.work); + flush_work(&read_counters_work.work); + c = read_counters_work.c; + + if (c.coreclk_cnt < c.last_coreclk_cnt) + delta_ccnt = c.coreclk_cnt + (MAX_CNT - c.last_coreclk_cnt); + else + delta_ccnt = c.coreclk_cnt - c.last_coreclk_cnt; + if (!delta_ccnt) + return 0; + + /* ref clock is 32 bits */ + if (c.refclk_cnt < c.last_refclk_cnt) + delta_refcnt = c.refclk_cnt + (MAX_CNT - c.last_refclk_cnt); + else + delta_refcnt = c.refclk_cnt - c.last_refclk_cnt; + if (!delta_refcnt) { + pr_debug("cpufreq: %d is idle, delta_refcnt: 0\n", cpu); + return 0; + } + rate_mhz = ((unsigned long)(delta_ccnt * REF_CLK_MHZ)) / delta_refcnt; + + return (rate_mhz * KHZ); /* in KHz */ +} + +static unsigned int tegra194_get_speed(u32 cpu) +{ + return tegra194_get_speed_common(cpu, US_DELAY); +} + +static int tegra194_cpufreq_init(struct cpufreq_policy *policy) +{ + struct tegra194_cpufreq_data *data = cpufreq_get_driver_data(); + int cl = get_cpu_cluster(policy->cpu); + u32 cpu; + + if (cl >= data->num_clusters) + return -EINVAL; + + /* boot freq */ + policy->cur = tegra194_get_speed_common(policy->cpu, US_DELAY_MIN); + + /* set same policy for all cpus in a cluster */ + for (cpu = (cl * 2); cpu < ((cl + 1) * 2); cpu++) + cpumask_set_cpu(cpu, policy->cpus); + + policy->freq_table = data->tables[cl]; + policy->cpuinfo.transition_latency = TEGRA_CPUFREQ_TRANSITION_LATENCY; + + return 0; +} + +static void set_cpu_ndiv(void *data) +{ + struct cpufreq_frequency_table *tbl = data; + u64 ndiv_val = (u64)tbl->driver_data; + + asm volatile("msr s3_0_c15_c0_4, %0" : : "r" (ndiv_val)); +} + +static int tegra194_cpufreq_set_target(struct cpufreq_policy *policy, + unsigned int index) +{ + struct cpufreq_frequency_table *tbl = policy->freq_table + index; + + /* + * Each core writes frequency in per core register. Then both cores + * in a cluster run at same frequency which is the maximum frequency + * request out of the values requested by both cores in that cluster. + */ + on_each_cpu_mask(policy->cpus, set_cpu_ndiv, tbl, true); + + return 0; +} + +static struct cpufreq_driver tegra194_cpufreq_driver = { + .name = "tegra194", + .flags = CPUFREQ_STICKY | CPUFREQ_CONST_LOOPS | + CPUFREQ_NEED_INITIAL_FREQ_CHECK, + .verify = cpufreq_generic_frequency_table_verify, + .target_index = tegra194_cpufreq_set_target, + .get = tegra194_get_speed, + .init = tegra194_cpufreq_init, + .attr = cpufreq_generic_attr, +}; + +static void tegra194_cpufreq_free_resources(void) +{ + destroy_workqueue(read_counters_wq); +} + +static struct cpufreq_frequency_table * +init_freq_table(struct platform_device *pdev, struct tegra_bpmp *bpmp, + unsigned int cluster_id) +{ + struct cpufreq_frequency_table *freq_table; + struct mrq_cpu_ndiv_limits_response resp; + unsigned int num_freqs, ndiv, delta_ndiv; + struct mrq_cpu_ndiv_limits_request req; + struct tegra_bpmp_message msg; + u16 freq_table_step_size; + int err, index; + + memset(&req, 0, sizeof(req)); + req.cluster_id = cluster_id; + + memset(&msg, 0, sizeof(msg)); + msg.mrq = MRQ_CPU_NDIV_LIMITS; + msg.tx.data = &req; + msg.tx.size = sizeof(req); + msg.rx.data = &resp; + msg.rx.size = sizeof(resp); + + err = tegra_bpmp_transfer(bpmp, &msg); + if (err) + return ERR_PTR(err); + + /* + * Make sure frequency table step is a multiple of mdiv to match + * vhint table granularity. + */ + freq_table_step_size = resp.mdiv * + DIV_ROUND_UP(CPUFREQ_TBL_STEP_HZ, resp.ref_clk_hz); + + dev_dbg(&pdev->dev, "cluster %d: frequency table step size: %d\n", + cluster_id, freq_table_step_size); + + delta_ndiv = resp.ndiv_max - resp.ndiv_min; + + if (unlikely(delta_ndiv == 0)) { + num_freqs = 1; + } else { + /* We store both ndiv_min and ndiv_max hence the +1 */ + num_freqs = delta_ndiv / freq_table_step_size + 1; + } + + num_freqs += (delta_ndiv % freq_table_step_size) ? 1 : 0; + + freq_table = devm_kcalloc(&pdev->dev, num_freqs + 1, + sizeof(*freq_table), GFP_KERNEL); + if (!freq_table) + return ERR_PTR(-ENOMEM); + + for (index = 0, ndiv = resp.ndiv_min; + ndiv < resp.ndiv_max; + index++, ndiv += freq_table_step_size) { + freq_table[index].driver_data = ndiv; + freq_table[index].frequency = map_ndiv_to_freq(&resp, ndiv); + } + + freq_table[index].driver_data = resp.ndiv_max; + freq_table[index++].frequency = map_ndiv_to_freq(&resp, resp.ndiv_max); + freq_table[index].frequency = CPUFREQ_TABLE_END; + + return freq_table; +} + +static int tegra194_cpufreq_probe(struct platform_device *pdev) +{ + struct tegra194_cpufreq_data *data; + struct tegra_bpmp *bpmp; + int err, i; + + data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + data->num_clusters = MAX_CLUSTERS; + data->tables = devm_kcalloc(&pdev->dev, data->num_clusters, + sizeof(*data->tables), GFP_KERNEL); + if (!data->tables) + return -ENOMEM; + + platform_set_drvdata(pdev, data); + + bpmp = tegra_bpmp_get(&pdev->dev); + if (IS_ERR(bpmp)) + return PTR_ERR(bpmp); + + read_counters_wq = alloc_workqueue("read_counters_wq", __WQ_LEGACY, 1); + if (!read_counters_wq) { + dev_err(&pdev->dev, "fail to create_workqueue\n"); + err = -EINVAL; + goto put_bpmp; + } + + for (i = 0; i < data->num_clusters; i++) { + data->tables[i] = init_freq_table(pdev, bpmp, i); + if (IS_ERR(data->tables[i])) { + err = PTR_ERR(data->tables[i]); + goto err_free_res; + } + } + + tegra194_cpufreq_driver.driver_data = data; + + err = cpufreq_register_driver(&tegra194_cpufreq_driver); + if (!err) + goto put_bpmp; + +err_free_res: + tegra194_cpufreq_free_resources(); +put_bpmp: + tegra_bpmp_put(bpmp); + return err; +} + +static int tegra194_cpufreq_remove(struct platform_device *pdev) +{ + cpufreq_unregister_driver(&tegra194_cpufreq_driver); + tegra194_cpufreq_free_resources(); + + return 0; +} + +static const struct of_device_id tegra194_cpufreq_of_match[] = { + { .compatible = "nvidia,tegra194-ccplex", }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, tegra194_cpufreq_of_match); + +static struct platform_driver tegra194_ccplex_driver = { + .driver = { + .name = "tegra194-cpufreq", + .of_match_table = tegra194_cpufreq_of_match, + }, + .probe = tegra194_cpufreq_probe, + .remove = tegra194_cpufreq_remove, +}; +module_platform_driver(tegra194_ccplex_driver); + +MODULE_AUTHOR("Mikko Perttunen "); +MODULE_AUTHOR("Sumit Gupta "); +MODULE_DESCRIPTION("NVIDIA Tegra194 cpufreq driver"); +MODULE_LICENSE("GPL v2"); -- GitLab From 292072c38768bb2321cf643b27cdf8fd8282d028 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Thu, 30 Jul 2020 08:59:40 +0530 Subject: [PATCH 1142/1476] cpufreq: cached_resolved_idx can not be negative It is not possible for cached_resolved_idx to be invalid here as the cpufreq core always sets index to a positive value. Change its type to unsigned int and fix qcom usage a bit. Signed-off-by: Viresh Kumar --- drivers/cpufreq/cpufreq.c | 2 +- drivers/cpufreq/qcom-cpufreq-hw.c | 5 +---- include/linux/cpufreq.h | 2 +- 3 files changed, 3 insertions(+), 6 deletions(-) diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c index 0128de3603dfc..053d72e52a313 100644 --- a/drivers/cpufreq/cpufreq.c +++ b/drivers/cpufreq/cpufreq.c @@ -538,7 +538,7 @@ unsigned int cpufreq_driver_resolve_freq(struct cpufreq_policy *policy, policy->cached_target_freq = target_freq; if (cpufreq_driver->target_index) { - int idx; + unsigned int idx; idx = cpufreq_frequency_table_target(policy, target_freq, CPUFREQ_RELATION_L); diff --git a/drivers/cpufreq/qcom-cpufreq-hw.c b/drivers/cpufreq/qcom-cpufreq-hw.c index fa68fa8ebd95e..599818d387175 100644 --- a/drivers/cpufreq/qcom-cpufreq-hw.c +++ b/drivers/cpufreq/qcom-cpufreq-hw.c @@ -112,13 +112,10 @@ static unsigned int qcom_cpufreq_hw_fast_switch(struct cpufreq_policy *policy, unsigned int target_freq) { void __iomem *perf_state_reg = policy->driver_data; - int index; + unsigned int index; unsigned long freq; index = policy->cached_resolved_idx; - if (index < 0) - return 0; - writel_relaxed(index, perf_state_reg); freq = policy->freq_table[index].frequency; diff --git a/include/linux/cpufreq.h b/include/linux/cpufreq.h index 3494f6763597e..540c3ea4eb3c4 100644 --- a/include/linux/cpufreq.h +++ b/include/linux/cpufreq.h @@ -127,7 +127,7 @@ struct cpufreq_policy { /* Cached frequency lookup from cpufreq_driver_resolve_freq. */ unsigned int cached_target_freq; - int cached_resolved_idx; + unsigned int cached_resolved_idx; /* Synchronization for frequency transitions */ bool transition_ongoing; /* Tracks transition status */ -- GitLab From f259eab3ea0e7ed73db91f6358274dd3a9a27d79 Mon Sep 17 00:00:00 2001 From: Valentin Schneider Date: Wed, 22 Jul 2020 10:37:32 +0100 Subject: [PATCH 1143/1476] cpufreq: make schedutil the default for arm and arm64 schedutil is already a hard-requirement for EAS, which has lead to making it default on arm (when CONFIG_BIG_LITTLE), see: commit 8fdcca8e254a ("cpufreq: Select schedutil when using big.LITTLE") One thing worth pointing out is that schedutil isn't only relevant for asymmetric CPU capacity systems; for instance, schedutil is the only governor that honours util-clamp performance requests. Another good example of this is x86 switching to using it by default in: commit a00ec3874e7d ("cpufreq: intel_pstate: Select schedutil as the default governor") Arguably it should be made the default for all architectures, but it seems better to wait for them to also gain frequency invariance powers. Make it the default for arm && arm64 for now. Signed-off-by: Valentin Schneider Signed-off-by: Ionela Voinescu Cc: Catalin Marinas Cc: Will Deacon Cc: Russell King Cc: Rafael J. Wysocki Cc: Viresh Kumar Signed-off-by: Viresh Kumar --- drivers/cpufreq/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/cpufreq/Kconfig b/drivers/cpufreq/Kconfig index e917501325525..2c7171e0b0010 100644 --- a/drivers/cpufreq/Kconfig +++ b/drivers/cpufreq/Kconfig @@ -37,7 +37,7 @@ config CPU_FREQ_STAT choice prompt "Default CPUFreq governor" default CPU_FREQ_DEFAULT_GOV_USERSPACE if ARM_SA1100_CPUFREQ || ARM_SA1110_CPUFREQ - default CPU_FREQ_DEFAULT_GOV_SCHEDUTIL if BIG_LITTLE + default CPU_FREQ_DEFAULT_GOV_SCHEDUTIL if ARM64 || ARM default CPU_FREQ_DEFAULT_GOV_SCHEDUTIL if X86_INTEL_PSTATE && SMP default CPU_FREQ_DEFAULT_GOV_PERFORMANCE help -- GitLab From c8376994c86c4eb02b9a1032cd3a8d44c911d671 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Thu, 4 Jun 2020 10:23:14 +0200 Subject: [PATCH 1144/1476] initrd: remove support for multiple floppies Remove the special handling for multiple floppies in the initrd code. No one should be using floppies for booting these days. (famous last words..) Includes a spelling fix from Colin Ian King . Signed-off-by: Christoph Hellwig Acked-by: Linus Torvalds --- arch/arm/kernel/atags_parse.c | 2 - arch/sh/kernel/setup.c | 2 - arch/sparc/kernel/setup_32.c | 2 - arch/sparc/kernel/setup_64.c | 2 - arch/x86/kernel/setup.c | 2 - include/linux/initrd.h | 6 --- init/do_mounts.c | 69 ++++------------------------------- init/do_mounts.h | 1 - init/do_mounts_rd.c | 20 +++------- 9 files changed, 12 insertions(+), 94 deletions(-) diff --git a/arch/arm/kernel/atags_parse.c b/arch/arm/kernel/atags_parse.c index ce02f92f4ab26..6c12d9fe694e3 100644 --- a/arch/arm/kernel/atags_parse.c +++ b/arch/arm/kernel/atags_parse.c @@ -91,8 +91,6 @@ __tagtable(ATAG_VIDEOTEXT, parse_tag_videotext); static int __init parse_tag_ramdisk(const struct tag *tag) { rd_image_start = tag->u.ramdisk.start; - rd_doload = (tag->u.ramdisk.flags & 1) == 0; - rd_prompt = (tag->u.ramdisk.flags & 2) == 0; if (tag->u.ramdisk.size) rd_size = tag->u.ramdisk.size; diff --git a/arch/sh/kernel/setup.c b/arch/sh/kernel/setup.c index 67f5a3b44c2ef..4144be650d410 100644 --- a/arch/sh/kernel/setup.c +++ b/arch/sh/kernel/setup.c @@ -290,8 +290,6 @@ void __init setup_arch(char **cmdline_p) #ifdef CONFIG_BLK_DEV_RAM rd_image_start = RAMDISK_FLAGS & RAMDISK_IMAGE_START_MASK; - rd_prompt = ((RAMDISK_FLAGS & RAMDISK_PROMPT_FLAG) != 0); - rd_doload = ((RAMDISK_FLAGS & RAMDISK_LOAD_FLAG) != 0); #endif if (!MOUNT_ROOT_RDONLY) diff --git a/arch/sparc/kernel/setup_32.c b/arch/sparc/kernel/setup_32.c index 6d07b85b9e247..eea43a1aef1b9 100644 --- a/arch/sparc/kernel/setup_32.c +++ b/arch/sparc/kernel/setup_32.c @@ -353,8 +353,6 @@ void __init setup_arch(char **cmdline_p) ROOT_DEV = old_decode_dev(root_dev); #ifdef CONFIG_BLK_DEV_RAM rd_image_start = ram_flags & RAMDISK_IMAGE_START_MASK; - rd_prompt = ((ram_flags & RAMDISK_PROMPT_FLAG) != 0); - rd_doload = ((ram_flags & RAMDISK_LOAD_FLAG) != 0); #endif prom_setsync(prom_sync_me); diff --git a/arch/sparc/kernel/setup_64.c b/arch/sparc/kernel/setup_64.c index f765fda871eb6..d87244197d5cb 100644 --- a/arch/sparc/kernel/setup_64.c +++ b/arch/sparc/kernel/setup_64.c @@ -659,8 +659,6 @@ void __init setup_arch(char **cmdline_p) ROOT_DEV = old_decode_dev(root_dev); #ifdef CONFIG_BLK_DEV_RAM rd_image_start = ram_flags & RAMDISK_IMAGE_START_MASK; - rd_prompt = ((ram_flags & RAMDISK_PROMPT_FLAG) != 0); - rd_doload = ((ram_flags & RAMDISK_LOAD_FLAG) != 0); #endif task_thread_info(&init_task)->kregs = &fake_swapper_regs; diff --git a/arch/x86/kernel/setup.c b/arch/x86/kernel/setup.c index a3767e74c758c..b9a68d8e06d8d 100644 --- a/arch/x86/kernel/setup.c +++ b/arch/x86/kernel/setup.c @@ -870,8 +870,6 @@ void __init setup_arch(char **cmdline_p) #ifdef CONFIG_BLK_DEV_RAM rd_image_start = boot_params.hdr.ram_size & RAMDISK_IMAGE_START_MASK; - rd_prompt = ((boot_params.hdr.ram_size & RAMDISK_PROMPT_FLAG) != 0); - rd_doload = ((boot_params.hdr.ram_size & RAMDISK_LOAD_FLAG) != 0); #endif #ifdef CONFIG_EFI if (!strncmp((char *)&boot_params.efi_info.efi_loader_signature, diff --git a/include/linux/initrd.h b/include/linux/initrd.h index aa59143557286..8db6f8c8030b6 100644 --- a/include/linux/initrd.h +++ b/include/linux/initrd.h @@ -2,12 +2,6 @@ #define INITRD_MINOR 250 /* shouldn't collide with /dev/ram* too soon ... */ -/* 1 = load ramdisk, 0 = don't load */ -extern int rd_doload; - -/* 1 = prompt for ramdisk, 0 = don't prompt */ -extern int rd_prompt; - /* starting block # of image */ extern int rd_image_start; diff --git a/init/do_mounts.c b/init/do_mounts.c index 1a4dfa17fb289..a7f22cf58c7ef 100644 --- a/init/do_mounts.c +++ b/init/do_mounts.c @@ -28,8 +28,6 @@ #include "do_mounts.h" -int __initdata rd_doload; /* 1 = load RAM disk, 0 = don't load */ - int root_mountflags = MS_RDONLY | MS_SILENT; static char * __initdata root_device_name; static char __initdata saved_root_name[64]; @@ -39,7 +37,7 @@ dev_t ROOT_DEV; static int __init load_ramdisk(char *str) { - rd_doload = simple_strtol(str,NULL,0) & 3; + pr_warn("ignoring the deprecated load_ramdisk= option\n"); return 1; } __setup("load_ramdisk=", load_ramdisk); @@ -553,66 +551,20 @@ static int __init mount_cifs_root(void) } #endif -#if defined(CONFIG_BLK_DEV_RAM) || defined(CONFIG_BLK_DEV_FD) -void __init change_floppy(char *fmt, ...) -{ - struct termios termios; - char buf[80]; - char c; - int fd; - va_list args; - va_start(args, fmt); - vsprintf(buf, fmt, args); - va_end(args); - fd = ksys_open("/dev/root", O_RDWR | O_NDELAY, 0); - if (fd >= 0) { - ksys_ioctl(fd, FDEJECT, 0); - ksys_close(fd); - } - printk(KERN_NOTICE "VFS: Insert %s and press ENTER\n", buf); - fd = ksys_open("/dev/console", O_RDWR, 0); - if (fd >= 0) { - ksys_ioctl(fd, TCGETS, (long)&termios); - termios.c_lflag &= ~ICANON; - ksys_ioctl(fd, TCSETSF, (long)&termios); - ksys_read(fd, &c, 1); - termios.c_lflag |= ICANON; - ksys_ioctl(fd, TCSETSF, (long)&termios); - ksys_close(fd); - } -} -#endif - void __init mount_root(void) { #ifdef CONFIG_ROOT_NFS if (ROOT_DEV == Root_NFS) { - if (mount_nfs_root()) - return; - - printk(KERN_ERR "VFS: Unable to mount root fs via NFS, trying floppy.\n"); - ROOT_DEV = Root_FD0; + if (!mount_nfs_root()) + printk(KERN_ERR "VFS: Unable to mount root fs via NFS.\n"); + return; } #endif #ifdef CONFIG_CIFS_ROOT if (ROOT_DEV == Root_CIFS) { - if (mount_cifs_root()) - return; - - printk(KERN_ERR "VFS: Unable to mount root fs via SMB, trying floppy.\n"); - ROOT_DEV = Root_FD0; - } -#endif -#ifdef CONFIG_BLK_DEV_FD - if (MAJOR(ROOT_DEV) == FLOPPY_MAJOR) { - /* rd_doload is 2 for a dual initrd/ramload setup */ - if (rd_doload==2) { - if (rd_load_disk(1)) { - ROOT_DEV = Root_RAM1; - root_device_name = NULL; - } - } else - change_floppy("root floppy"); + if (!mount_cifs_root()) + printk(KERN_ERR "VFS: Unable to mount root fs via SMB.\n"); + return; } #endif #ifdef CONFIG_BLOCK @@ -631,8 +583,6 @@ void __init mount_root(void) */ void __init prepare_namespace(void) { - int is_floppy; - if (root_delay) { printk(KERN_INFO "Waiting %d sec before mounting root device...\n", root_delay); @@ -675,11 +625,6 @@ void __init prepare_namespace(void) async_synchronize_full(); } - is_floppy = MAJOR(ROOT_DEV) == FLOPPY_MAJOR; - - if (is_floppy && rd_doload && rd_load_disk(0)) - ROOT_DEV = Root_RAM0; - mount_root(); out: devtmpfs_mount(); diff --git a/init/do_mounts.h b/init/do_mounts.h index 50d6c8941e15a..c855b3f0e06d1 100644 --- a/init/do_mounts.h +++ b/init/do_mounts.h @@ -9,7 +9,6 @@ #include #include -void change_floppy(char *fmt, ...); void mount_block_root(char *name, int flags); void mount_root(void); extern int root_mountflags; diff --git a/init/do_mounts_rd.c b/init/do_mounts_rd.c index 32fb049d18f9b..0a5c3ebee61da 100644 --- a/init/do_mounts_rd.c +++ b/init/do_mounts_rd.c @@ -15,11 +15,9 @@ #include -int __initdata rd_prompt = 1;/* 1 = prompt for RAM disk, 0 = don't prompt */ - static int __init prompt_ramdisk(char *str) { - rd_prompt = simple_strtol(str,NULL,0) & 1; + pr_warn("ignoring the deprecated prompt_ramdisk= option\n"); return 1; } __setup("prompt_ramdisk=", prompt_ramdisk); @@ -178,7 +176,7 @@ int __init rd_load_image(char *from) int res = 0; int in_fd, out_fd; unsigned long rd_blocks, devblocks; - int nblocks, i, disk; + int nblocks, i; char *buf = NULL; unsigned short rotate = 0; decompress_fn decompressor = NULL; @@ -243,21 +241,15 @@ int __init rd_load_image(char *from) printk(KERN_NOTICE "RAMDISK: Loading %dKiB [%ld disk%s] into ram disk... ", nblocks, ((nblocks-1)/devblocks)+1, nblocks>devblocks ? "s" : ""); - for (i = 0, disk = 1; i < nblocks; i++) { + for (i = 0; i < nblocks; i++) { if (i && (i % devblocks == 0)) { - pr_cont("done disk #%d.\n", disk++); + pr_cont("done disk #1.\n"); rotate = 0; if (ksys_close(in_fd)) { printk("Error closing the disk.\n"); goto noclose_input; } - change_floppy("disk #%d", disk); - in_fd = ksys_open(from, O_RDONLY, 0); - if (in_fd < 0) { - printk("Error opening disk.\n"); - goto noclose_input; - } - printk("Loading disk #%d... ", disk); + break; } ksys_read(in_fd, buf, BLOCK_SIZE); ksys_write(out_fd, buf, BLOCK_SIZE); @@ -284,8 +276,6 @@ out: int __init rd_load_disk(int n) { - if (rd_prompt) - change_floppy("root floppy disk to be loaded into RAM disk"); create_dev("/dev/root", ROOT_DEV); create_dev("/dev/ram", MKDEV(RAMDISK_MAJOR, n)); return rd_load_image("/dev/root"); -- GitLab From 899ac10cc0bdaaee3b908e390bf94d2f1ba5d512 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Sun, 7 Jun 2020 17:40:30 +0200 Subject: [PATCH 1145/1476] initrd: remove the BLKFLSBUF call in handle_initrd BLKFLSBUF used to be overloaded for the ramdisk driver to free the whole ramdisk, which was completely different behavior compared to all other drivers. But this magic overload got removed in commit ff26956875c2 ("brd: remove support for BLKFLSBUF"), so this call is entirely pointless now. Signed-off-by: Christoph Hellwig Acked-by: Linus Torvalds --- init/do_mounts_initrd.c | 9 --------- 1 file changed, 9 deletions(-) diff --git a/init/do_mounts_initrd.c b/init/do_mounts_initrd.c index d72beda824aa7..e4f88e9e1c083 100644 --- a/init/do_mounts_initrd.c +++ b/init/do_mounts_initrd.c @@ -115,21 +115,12 @@ static void __init handle_initrd(void) if (!error) printk("okay\n"); else { - int fd = ksys_open("/dev/root.old", O_RDWR, 0); if (error == -ENOENT) printk("/initrd does not exist. Ignored.\n"); else printk("failed\n"); printk(KERN_NOTICE "Unmounting old root\n"); ksys_umount("/old", MNT_DETACH); - printk(KERN_NOTICE "Trying to free ramdisk memory ... "); - if (fd < 0) { - error = fd; - } else { - error = ksys_ioctl(fd, BLKFLSBUF, 0); - ksys_close(fd); - } - printk(!error ? "okay\n" : "failed\n"); } } -- GitLab From bef173299613404f55b11180d9a865861637f31d Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Sat, 6 Jun 2020 14:49:58 +0200 Subject: [PATCH 1146/1476] initrd: switch initrd loading to struct file based APIs There is no good reason to mess with file descriptors from in-kernel code, switch the initrd loading to struct file based read and writes instead. Also Pass an explicit offset instead of ->f_pos, and to make that easier, use file scope file structs and offsets everywhere except for identify_ramdisk_image instead of the current strange mix. Signed-off-by: Christoph Hellwig Acked-by: Linus Torvalds --- fs/read_write.c | 2 +- include/linux/syscalls.h | 1 - init/do_mounts_rd.c | 79 ++++++++++++++++++++-------------------- 3 files changed, 40 insertions(+), 42 deletions(-) diff --git a/fs/read_write.c b/fs/read_write.c index 4fb797822567a..5db58b8c78d0d 100644 --- a/fs/read_write.c +++ b/fs/read_write.c @@ -301,7 +301,7 @@ loff_t vfs_llseek(struct file *file, loff_t offset, int whence) } EXPORT_SYMBOL(vfs_llseek); -off_t ksys_lseek(unsigned int fd, off_t offset, unsigned int whence) +static off_t ksys_lseek(unsigned int fd, off_t offset, unsigned int whence) { off_t retval; struct fd f = fdget_pos(fd); diff --git a/include/linux/syscalls.h b/include/linux/syscalls.h index b951a87da9877..10843a6adb770 100644 --- a/include/linux/syscalls.h +++ b/include/linux/syscalls.h @@ -1246,7 +1246,6 @@ int ksys_fchown(unsigned int fd, uid_t user, gid_t group); int ksys_getdents64(unsigned int fd, struct linux_dirent64 __user *dirent, unsigned int count); int ksys_ioctl(unsigned int fd, unsigned int cmd, unsigned long arg); -off_t ksys_lseek(unsigned int fd, off_t offset, unsigned int whence); ssize_t ksys_read(unsigned int fd, char __user *buf, size_t count); void ksys_sync(void); int ksys_unshare(unsigned long unshare_flags); diff --git a/init/do_mounts_rd.c b/init/do_mounts_rd.c index 0a5c3ebee61da..d4255c10432a8 100644 --- a/init/do_mounts_rd.c +++ b/init/do_mounts_rd.c @@ -14,6 +14,8 @@ #include +static struct file *in_file, *out_file; +static loff_t in_pos, out_pos; static int __init prompt_ramdisk(char *str) { @@ -31,7 +33,7 @@ static int __init ramdisk_start_setup(char *str) } __setup("ramdisk_start=", ramdisk_start_setup); -static int __init crd_load(int in_fd, int out_fd, decompress_fn deco); +static int __init crd_load(decompress_fn deco); /* * This routine tries to find a RAM disk image to load, and returns the @@ -53,7 +55,8 @@ static int __init crd_load(int in_fd, int out_fd, decompress_fn deco); * lz4 */ static int __init -identify_ramdisk_image(int fd, int start_block, decompress_fn *decompressor) +identify_ramdisk_image(struct file *file, loff_t pos, + decompress_fn *decompressor) { const int size = 512; struct minix_super_block *minixsb; @@ -64,6 +67,7 @@ identify_ramdisk_image(int fd, int start_block, decompress_fn *decompressor) unsigned char *buf; const char *compress_name; unsigned long n; + int start_block = rd_image_start; buf = kmalloc(size, GFP_KERNEL); if (!buf) @@ -78,8 +82,8 @@ identify_ramdisk_image(int fd, int start_block, decompress_fn *decompressor) /* * Read block 0 to test for compressed kernel */ - ksys_lseek(fd, start_block * BLOCK_SIZE, 0); - ksys_read(fd, buf, size); + pos = start_block * BLOCK_SIZE; + kernel_read(file, buf, size, &pos); *decompressor = decompress_method(buf, size, &compress_name); if (compress_name) { @@ -124,8 +128,8 @@ identify_ramdisk_image(int fd, int start_block, decompress_fn *decompressor) /* * Read 512 bytes further to check if cramfs is padded */ - ksys_lseek(fd, start_block * BLOCK_SIZE + 0x200, 0); - ksys_read(fd, buf, size); + pos = start_block * BLOCK_SIZE + 0x200; + kernel_read(file, buf, size, &pos); if (cramfsb->magic == CRAMFS_MAGIC) { printk(KERN_NOTICE @@ -138,8 +142,8 @@ identify_ramdisk_image(int fd, int start_block, decompress_fn *decompressor) /* * Read block 1 to test for minix and ext2 superblock */ - ksys_lseek(fd, (start_block+1) * BLOCK_SIZE, 0); - ksys_read(fd, buf, size); + pos = (start_block + 1) * BLOCK_SIZE; + kernel_read(file, buf, size, &pos); /* Try minix */ if (minixsb->s_magic == MINIX_SUPER_MAGIC || @@ -166,15 +170,22 @@ identify_ramdisk_image(int fd, int start_block, decompress_fn *decompressor) start_block); done: - ksys_lseek(fd, start_block * BLOCK_SIZE, 0); kfree(buf); return nblocks; } +static unsigned long nr_blocks(struct file *file) +{ + struct inode *inode = file->f_mapping->host; + + if (!S_ISBLK(inode->i_mode)) + return 0; + return i_size_read(inode) >> 10; +} + int __init rd_load_image(char *from) { int res = 0; - int in_fd, out_fd; unsigned long rd_blocks, devblocks; int nblocks, i; char *buf = NULL; @@ -184,20 +195,21 @@ int __init rd_load_image(char *from) char rotator[4] = { '|' , '/' , '-' , '\\' }; #endif - out_fd = ksys_open("/dev/ram", O_RDWR, 0); - if (out_fd < 0) + out_file = filp_open("/dev/ram", O_RDWR, 0); + if (IS_ERR(out_file)) goto out; - in_fd = ksys_open(from, O_RDONLY, 0); - if (in_fd < 0) + in_file = filp_open(from, O_RDONLY, 0); + if (IS_ERR(in_file)) goto noclose_input; - nblocks = identify_ramdisk_image(in_fd, rd_image_start, &decompressor); + in_pos = rd_image_start * BLOCK_SIZE; + nblocks = identify_ramdisk_image(in_file, in_pos, &decompressor); if (nblocks < 0) goto done; if (nblocks == 0) { - if (crd_load(in_fd, out_fd, decompressor) == 0) + if (crd_load(decompressor) == 0) goto successful_load; goto done; } @@ -206,11 +218,7 @@ int __init rd_load_image(char *from) * NOTE NOTE: nblocks is not actually blocks but * the number of kibibytes of data to load into a ramdisk. */ - if (ksys_ioctl(out_fd, BLKGETSIZE, (unsigned long)&rd_blocks) < 0) - rd_blocks = 0; - else - rd_blocks >>= 1; - + rd_blocks = nr_blocks(out_file); if (nblocks > rd_blocks) { printk("RAMDISK: image too big! (%dKiB/%ldKiB)\n", nblocks, rd_blocks); @@ -220,13 +228,10 @@ int __init rd_load_image(char *from) /* * OK, time to copy in the data */ - if (ksys_ioctl(in_fd, BLKGETSIZE, (unsigned long)&devblocks) < 0) - devblocks = 0; - else - devblocks >>= 1; - if (strcmp(from, "/initrd.image") == 0) devblocks = nblocks; + else + devblocks = nr_blocks(in_file); if (devblocks == 0) { printk(KERN_ERR "RAMDISK: could not determine device size\n"); @@ -245,14 +250,11 @@ int __init rd_load_image(char *from) if (i && (i % devblocks == 0)) { pr_cont("done disk #1.\n"); rotate = 0; - if (ksys_close(in_fd)) { - printk("Error closing the disk.\n"); - goto noclose_input; - } + fput(in_file); break; } - ksys_read(in_fd, buf, BLOCK_SIZE); - ksys_write(out_fd, buf, BLOCK_SIZE); + kernel_read(in_file, buf, BLOCK_SIZE, &in_pos); + kernel_write(out_file, buf, BLOCK_SIZE, &out_pos); #if !defined(CONFIG_S390) if (!(i % 16)) { pr_cont("%c\b", rotator[rotate & 0x3]); @@ -265,9 +267,9 @@ int __init rd_load_image(char *from) successful_load: res = 1; done: - ksys_close(in_fd); + fput(in_file); noclose_input: - ksys_close(out_fd); + fput(out_file); out: kfree(buf); ksys_unlink("/dev/ram"); @@ -283,11 +285,10 @@ int __init rd_load_disk(int n) static int exit_code; static int decompress_error; -static int crd_infd, crd_outfd; static long __init compr_fill(void *buf, unsigned long len) { - long r = ksys_read(crd_infd, buf, len); + long r = kernel_read(in_file, buf, len, &in_pos); if (r < 0) printk(KERN_ERR "RAMDISK: error while reading compressed data"); else if (r == 0) @@ -297,7 +298,7 @@ static long __init compr_fill(void *buf, unsigned long len) static long __init compr_flush(void *window, unsigned long outcnt) { - long written = ksys_write(crd_outfd, window, outcnt); + long written = kernel_write(out_file, window, outcnt, &out_pos); if (written != outcnt) { if (decompress_error == 0) printk(KERN_ERR @@ -316,11 +317,9 @@ static void __init error(char *x) decompress_error = 1; } -static int __init crd_load(int in_fd, int out_fd, decompress_fn deco) +static int __init crd_load(decompress_fn deco) { int result; - crd_infd = in_fd; - crd_outfd = out_fd; if (!deco) { pr_emerg("Invalid ramdisk decompression routine. " -- GitLab From f0ea68f1390ca11aae9e08a042c6dba5bc48eea2 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Sat, 6 Jun 2020 15:54:39 +0200 Subject: [PATCH 1147/1476] initrd: mark init_linuxrc as __init Signed-off-by: Christoph Hellwig Acked-by: Linus Torvalds --- init/do_mounts_initrd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/init/do_mounts_initrd.c b/init/do_mounts_initrd.c index e4f88e9e1c083..57ad5b2da8f5f 100644 --- a/init/do_mounts_initrd.c +++ b/init/do_mounts_initrd.c @@ -56,7 +56,7 @@ static int __init early_initrd(char *p) } early_param("initrd", early_initrd); -static int init_linuxrc(struct subprocess_info *info, struct cred *new) +static int __init init_linuxrc(struct subprocess_info *info, struct cred *new) { ksys_unshare(CLONE_FS | CLONE_FILES); console_on_rootfs(); -- GitLab From 9acc17baf1fd6369cf969b1130bcf8c4b616ecfb Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Wed, 8 Jul 2020 18:18:54 +0200 Subject: [PATCH 1148/1476] initrd: mark initrd support as deprecated The classic initial ramdisk has been replaced by the much more flexible and efficient initramfs a long time. Warn about it being removed soon. Includes a spelling fix from Colin Ian King . Signed-off-by: Christoph Hellwig Acked-by: Linus Torvalds --- init/do_mounts_initrd.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/init/do_mounts_initrd.c b/init/do_mounts_initrd.c index 57ad5b2da8f5f..e08669187d63b 100644 --- a/init/do_mounts_initrd.c +++ b/init/do_mounts_initrd.c @@ -75,6 +75,8 @@ static void __init handle_initrd(void) extern char *envp_init[]; int error; + pr_warn("using deprecated initrd support, will be removed in 2021.\n"); + real_root_dev = new_encode_dev(ROOT_DEV); create_dev("/dev/root.old", Root_RAM0); /* mount initrd on rootfs' /root */ -- GitLab From 9ab6b718491b1a3c3eeb52f32a428dc0cbf5bb80 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Sat, 6 Jun 2020 13:51:52 +0200 Subject: [PATCH 1149/1476] initramfs: remove the populate_initrd_image and clean_rootfs stubs If initrd support is not enable just print the warning directly instead of hiding the fact that we just failed behind two stub functions. Signed-off-by: Christoph Hellwig Acked-by: Linus Torvalds --- init/initramfs.c | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/init/initramfs.c b/init/initramfs.c index 7a38012e1af74..d10404625c31f 100644 --- a/init/initramfs.c +++ b/init/initramfs.c @@ -618,13 +618,7 @@ static void __init clean_rootfs(void) ksys_close(fd); kfree(buf); } -#else -static inline void clean_rootfs(void) -{ -} -#endif /* CONFIG_BLK_DEV_RAM */ -#ifdef CONFIG_BLK_DEV_RAM static void __init populate_initrd_image(char *err) { ssize_t written; @@ -644,11 +638,6 @@ static void __init populate_initrd_image(char *err) written, initrd_end - initrd_start); ksys_close(fd); } -#else -static void __init populate_initrd_image(char *err) -{ - printk(KERN_EMERG "Initramfs unpacking failed: %s\n", err); -} #endif /* CONFIG_BLK_DEV_RAM */ static int __init populate_rootfs(void) @@ -668,8 +657,12 @@ static int __init populate_rootfs(void) err = unpack_to_rootfs((char *)initrd_start, initrd_end - initrd_start); if (err) { +#ifdef CONFIG_BLK_DEV_RAM clean_rootfs(); populate_initrd_image(err); +#else + printk(KERN_EMERG "Initramfs unpacking failed: %s\n", err); +#endif } done: -- GitLab From b2a74d5f9d446ae33a381ed7ba5f283f09bed726 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Sat, 6 Jun 2020 13:59:54 +0200 Subject: [PATCH 1150/1476] initramfs: remove clean_rootfs There is no point in trying to clean up after unpacking the initramfs failed, as it should never get past the magic number check. In addition the current code only removes file that are direct children of the root entry, which wasn't complete anyway Fixes: df52092f3c97 ("fastboot: remove duplicate unpack_to_rootfs()") Reported-by: Marek Szyprowski Signed-off-by: Christoph Hellwig --- init/initramfs.c | 49 +----------------------------------------------- 1 file changed, 1 insertion(+), 48 deletions(-) diff --git a/init/initramfs.c b/init/initramfs.c index d10404625c31f..1bc977ebfe316 100644 --- a/init/initramfs.c +++ b/init/initramfs.c @@ -11,6 +11,7 @@ #include #include #include +#include static ssize_t __init xwrite(int fd, const char *p, size_t count) { @@ -572,53 +573,6 @@ static inline bool kexec_free_initrd(void) #endif /* CONFIG_KEXEC_CORE */ #ifdef CONFIG_BLK_DEV_RAM -#define BUF_SIZE 1024 -static void __init clean_rootfs(void) -{ - int fd; - void *buf; - struct linux_dirent64 *dirp; - int num; - - fd = ksys_open("/", O_RDONLY, 0); - WARN_ON(fd < 0); - if (fd < 0) - return; - buf = kzalloc(BUF_SIZE, GFP_KERNEL); - WARN_ON(!buf); - if (!buf) { - ksys_close(fd); - return; - } - - dirp = buf; - num = ksys_getdents64(fd, dirp, BUF_SIZE); - while (num > 0) { - while (num > 0) { - struct kstat st; - int ret; - - ret = vfs_lstat(dirp->d_name, &st); - WARN_ON_ONCE(ret); - if (!ret) { - if (S_ISDIR(st.mode)) - ksys_rmdir(dirp->d_name); - else - ksys_unlink(dirp->d_name); - } - - num -= dirp->d_reclen; - dirp = (void *)dirp + dirp->d_reclen; - } - dirp = buf; - memset(buf, 0, BUF_SIZE); - num = ksys_getdents64(fd, dirp, BUF_SIZE); - } - - ksys_close(fd); - kfree(buf); -} - static void __init populate_initrd_image(char *err) { ssize_t written; @@ -658,7 +612,6 @@ static int __init populate_rootfs(void) err = unpack_to_rootfs((char *)initrd_start, initrd_end - initrd_start); if (err) { #ifdef CONFIG_BLK_DEV_RAM - clean_rootfs(); populate_initrd_image(err); #else printk(KERN_EMERG "Initramfs unpacking failed: %s\n", err); -- GitLab From be6f885e97e9304541057fbf25148685847ef310 Mon Sep 17 00:00:00 2001 From: Nathan Lynch Date: Thu, 27 Jun 2019 00:15:35 -0500 Subject: [PATCH 1151/1476] powerpc/cacheinfo: Use name@unit instead of full DT path in debug messages We know that every OF node we deal with in this code is under /cpus, so we can make the debug messages a little less verbose without losing information. E.g. cacheinfo: creating L1 dcache and icache for /cpus/PowerPC,POWER8@0 cacheinfo: creating L2 ucache for /cpus/l2-cache@2006 cacheinfo: creating L3 ucache for /cpus/l3-cache@3106 becomes cacheinfo: creating L1 dcache and icache for PowerPC,POWER8@0 cacheinfo: creating L2 ucache for l2-cache@2006 cacheinfo: creating L3 ucache for l3-cache@3106 Replace all '%pOF' specifiers with '%pOFP'. Signed-off-by: Nathan Lynch Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20190627051537.7298-3-nathanl@linux.ibm.com --- arch/powerpc/kernel/cacheinfo.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/arch/powerpc/kernel/cacheinfo.c b/arch/powerpc/kernel/cacheinfo.c index 751a4ffcd588b..879154c839f33 100644 --- a/arch/powerpc/kernel/cacheinfo.c +++ b/arch/powerpc/kernel/cacheinfo.c @@ -168,7 +168,7 @@ static void release_cache_debugcheck(struct cache *cache) list_for_each_entry(iter, &cache_list, list) WARN_ONCE(iter->next_local == cache, - "cache for %pOF(%s) refers to cache for %pOF(%s)\n", + "cache for %pOFP(%s) refers to cache for %pOFP(%s)\n", iter->ofnode, cache_type_string(iter), cache->ofnode, @@ -180,7 +180,7 @@ static void release_cache(struct cache *cache) if (!cache) return; - pr_debug("freeing L%d %s cache for %pOF\n", cache->level, + pr_debug("freeing L%d %s cache for %pOFP\n", cache->level, cache_type_string(cache), cache->ofnode); release_cache_debugcheck(cache); @@ -195,7 +195,7 @@ static void cache_cpu_set(struct cache *cache, int cpu) while (next) { WARN_ONCE(cpumask_test_cpu(cpu, &next->shared_cpu_map), - "CPU %i already accounted in %pOF(%s)\n", + "CPU %i already accounted in %pOFP(%s)\n", cpu, next->ofnode, cache_type_string(next)); cpumask_set_cpu(cpu, &next->shared_cpu_map); @@ -354,7 +354,7 @@ static int cache_is_unified_d(const struct device_node *np) static struct cache *cache_do_one_devnode_unified(struct device_node *node, int level) { - pr_debug("creating L%d ucache for %pOF\n", level, node); + pr_debug("creating L%d ucache for %pOFP\n", level, node); return new_cache(cache_is_unified_d(node), level, node); } @@ -364,7 +364,7 @@ static struct cache *cache_do_one_devnode_split(struct device_node *node, { struct cache *dcache, *icache; - pr_debug("creating L%d dcache and icache for %pOF\n", level, + pr_debug("creating L%d dcache and icache for %pOFP\n", level, node); dcache = new_cache(CACHE_TYPE_DATA, level, node); @@ -746,13 +746,13 @@ static void cacheinfo_create_index_opt_attrs(struct cache_index_dir *dir) rc = attr->show(&dir->kobj, attr, buf); if (rc <= 0) { pr_debug("not creating %s attribute for " - "%pOF(%s) (rc = %zd)\n", + "%pOFP(%s) (rc = %zd)\n", attr->attr.name, cache->ofnode, cache_type, rc); continue; } if (sysfs_create_file(&dir->kobj, &attr->attr)) - pr_debug("could not create %s attribute for %pOF(%s)\n", + pr_debug("could not create %s attribute for %pOFP(%s)\n", attr->attr.name, cache->ofnode, cache_type); } @@ -868,7 +868,7 @@ static void cache_cpu_clear(struct cache *cache, int cpu) struct cache *next = cache->next_local; WARN_ONCE(!cpumask_test_cpu(cpu, &cache->shared_cpu_map), - "CPU %i not accounted in %pOF(%s)\n", + "CPU %i not accounted in %pOFP(%s)\n", cpu, cache->ofnode, cache_type_string(cache)); -- GitLab From 1b3da8ffaa158e9a95c19b17c14d7259d58bc0cd Mon Sep 17 00:00:00 2001 From: Nathan Lynch Date: Thu, 27 Jun 2019 00:15:36 -0500 Subject: [PATCH 1152/1476] powerpc/cacheinfo: Improve diagnostics about malformed cache lists If we have a bug which causes us to start with the wrong kind of OF node when linking up the cache tree, it's helpful for debugging to print information about what we found vs what we expected. So replace uses of WARN_ON_ONCE with WARN_ONCE, which lets us include an informative message instead of a contentless backtrace. Signed-off-by: Nathan Lynch Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20190627051537.7298-4-nathanl@linux.ibm.com --- arch/powerpc/kernel/cacheinfo.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/arch/powerpc/kernel/cacheinfo.c b/arch/powerpc/kernel/cacheinfo.c index 879154c839f33..a116d5bb6b121 100644 --- a/arch/powerpc/kernel/cacheinfo.c +++ b/arch/powerpc/kernel/cacheinfo.c @@ -424,8 +424,14 @@ static void link_cache_lists(struct cache *smaller, struct cache *bigger) static void do_subsidiary_caches_debugcheck(struct cache *cache) { - WARN_ON_ONCE(cache->level != 1); - WARN_ON_ONCE(!of_node_is_type(cache->ofnode, "cpu")); + WARN_ONCE(cache->level != 1, + "instantiating cache chain from L%d %s cache for " + "%pOFP instead of an L1\n", cache->level, + cache_type_string(cache), cache->ofnode); + WARN_ONCE(!of_node_is_type(cache->ofnode, "cpu"), + "instantiating cache chain from node %pOFP of type '%s' " + "instead of a cpu node\n", cache->ofnode, + of_node_get_device_type(cache->ofnode)); } static void do_subsidiary_caches(struct cache *cache) -- GitLab From 6ec54363f198aae9c1343f82ff5b865546944a73 Mon Sep 17 00:00:00 2001 From: Nathan Lynch Date: Thu, 27 Jun 2019 00:15:37 -0500 Subject: [PATCH 1153/1476] powerpc/cacheinfo: Warn if cache object chain becomes unordered This can catch cases where the device tree has gotten mishandled into an inconsistent state at runtime, e.g. the cache nodes for both the source and the destination are present after a migration. Signed-off-by: Nathan Lynch Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20190627051537.7298-5-nathanl@linux.ibm.com --- arch/powerpc/kernel/cacheinfo.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/arch/powerpc/kernel/cacheinfo.c b/arch/powerpc/kernel/cacheinfo.c index a116d5bb6b121..65ab9fcebd317 100644 --- a/arch/powerpc/kernel/cacheinfo.c +++ b/arch/powerpc/kernel/cacheinfo.c @@ -420,6 +420,15 @@ static void link_cache_lists(struct cache *smaller, struct cache *bigger) } smaller->next_local = bigger; + + /* + * The cache->next_local list sorts by level ascending: + * L1d -> L1i -> L2 -> L3 ... + */ + WARN_ONCE((smaller->level == 1 && bigger->level > 2) || + (smaller->level > 1 && bigger->level != smaller->level + 1), + "linking L%i cache %pOFP to L%i cache %pOFP; skipped a level?\n", + smaller->level, smaller->ofnode, bigger->level, bigger->ofnode); } static void do_subsidiary_caches_debugcheck(struct cache *cache) -- GitLab From 494a66f34e00b6a1897b5a1ab150a19265696b17 Mon Sep 17 00:00:00 2001 From: Nathan Lynch Date: Thu, 27 Jun 2019 00:30:43 -0500 Subject: [PATCH 1154/1476] powerpc/pseries/mobility: Set pr_fmt() The pr_err() callsites in mobility.c already manually include a "mobility:" prefix, let's make it official for the benefit of messages to be added later. Signed-off-by: Nathan Lynch Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20190627053044.9238-2-nathanl@linux.ibm.com --- arch/powerpc/platforms/pseries/mobility.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/arch/powerpc/platforms/pseries/mobility.c b/arch/powerpc/platforms/pseries/mobility.c index 78cd772a579b0..0ede49a35121b 100644 --- a/arch/powerpc/platforms/pseries/mobility.c +++ b/arch/powerpc/platforms/pseries/mobility.c @@ -6,6 +6,9 @@ * Copyright (C) 2010 IBM Corporation */ + +#define pr_fmt(fmt) "mobility: " fmt + #include #include #include @@ -396,11 +399,11 @@ static int __init mobility_sysfs_init(void) rc = sysfs_create_file(mobility_kobj, &class_attr_migration.attr); if (rc) - pr_err("mobility: unable to create migration sysfs file (%d)\n", rc); + pr_err("unable to create migration sysfs file (%d)\n", rc); rc = sysfs_create_file(mobility_kobj, &class_attr_api_version.attr.attr); if (rc) - pr_err("mobility: unable to create api_version sysfs file (%d)\n", rc); + pr_err("unable to create api_version sysfs file (%d)\n", rc); return 0; } -- GitLab From 5d8b1f9dea17b4bf5e5f088f39eeab32c7e487be Mon Sep 17 00:00:00 2001 From: Nathan Lynch Date: Thu, 27 Jun 2019 00:30:44 -0500 Subject: [PATCH 1155/1476] powerpc/pseries/mobility: Add pr_debug() for device tree changes When investigating issues with partition migration or resource reassignments it is helpful to have a log of which nodes and properties in the device tree have changed. Use pr_debug() so it's easy to enable these at runtime with the dynamic debug facility. Signed-off-by: Nathan Lynch Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20190627053044.9238-3-nathanl@linux.ibm.com --- arch/powerpc/platforms/pseries/mobility.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/arch/powerpc/platforms/pseries/mobility.c b/arch/powerpc/platforms/pseries/mobility.c index 0ede49a35121b..d6f4162478a50 100644 --- a/arch/powerpc/platforms/pseries/mobility.c +++ b/arch/powerpc/platforms/pseries/mobility.c @@ -67,6 +67,8 @@ static int delete_dt_node(__be32 phandle) if (!dn) return -ENOENT; + pr_debug("removing node %pOFfp\n", dn); + dlpar_detach_node(dn); of_node_put(dn); return 0; @@ -125,6 +127,7 @@ static int update_dt_property(struct device_node *dn, struct property **prop, } if (!more) { + pr_debug("updating node %pOF property %s\n", dn, name); of_update_property(dn, new_prop); *prop = NULL; } @@ -243,6 +246,8 @@ static int add_dt_node(__be32 parent_phandle, __be32 drc_index) if (rc) dlpar_free_cc_nodes(dn); + pr_debug("added node %pOFfp\n", dn); + of_node_put(parent_dn); return rc; } -- GitLab From a0ff72f9f5a780341e7ff5e9ba50a0dad5fa1980 Mon Sep 17 00:00:00 2001 From: Nathan Lynch Date: Thu, 19 Sep 2019 18:16:33 -0500 Subject: [PATCH 1156/1476] powerpc/pseries/hotplug-cpu: Remove double free in error path In the unlikely event that the device tree lacks a /cpus node, find_dlpar_cpus_to_add() oddly frees the cpu_drcs buffer it has been passed before returning an error. Its only caller also frees the buffer on error. Remove the less conventional kfree() of a caller-supplied buffer from find_dlpar_cpus_to_add(). Fixes: 90edf184b9b7 ("powerpc/pseries: Add CPU dlpar add functionality") Signed-off-by: Nathan Lynch Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20190919231633.1344-1-nathanl@linux.ibm.com --- arch/powerpc/platforms/pseries/hotplug-cpu.c | 1 - 1 file changed, 1 deletion(-) diff --git a/arch/powerpc/platforms/pseries/hotplug-cpu.c b/arch/powerpc/platforms/pseries/hotplug-cpu.c index 4bad7a83addc1..c6e0d8abf75ea 100644 --- a/arch/powerpc/platforms/pseries/hotplug-cpu.c +++ b/arch/powerpc/platforms/pseries/hotplug-cpu.c @@ -737,7 +737,6 @@ static int dlpar_cpu_add_by_count(u32 cpus_to_add) parent = of_find_node_by_path("/cpus"); if (!parent) { pr_warn("Could not find CPU root node in device tree\n"); - kfree(cpu_drcs); return -1; } -- GitLab From d3a133aa0e029e0bbb67170f5f18c8fcd4701370 Mon Sep 17 00:00:00 2001 From: Michal Suchanek Date: Mon, 6 Apr 2020 23:00:22 +0200 Subject: [PATCH 1157/1476] powerpc/perf: Consolidate perf_callchain_user_[64|32]() perf_callchain_user_64() and perf_callchain_user_32() are nearly identical. Consolidate into one function with thin wrappers. Suggested-by: Nicholas Piggin Signed-off-by: Michal Suchanek [mpe: Adapt to copy_from_user_nofault(), minor formatting] Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20200406210022.32265-1-msuchanek@suse.de --- arch/powerpc/perf/callchain.h | 25 ++++++++++++++++++++++++- arch/powerpc/perf/callchain_32.c | 21 ++------------------- arch/powerpc/perf/callchain_64.c | 13 +++---------- 3 files changed, 29 insertions(+), 30 deletions(-) diff --git a/arch/powerpc/perf/callchain.h b/arch/powerpc/perf/callchain.h index 7a2cb9e1181a7..ae24d4a00da60 100644 --- a/arch/powerpc/perf/callchain.h +++ b/arch/powerpc/perf/callchain.h @@ -2,7 +2,7 @@ #ifndef _POWERPC_PERF_CALLCHAIN_H #define _POWERPC_PERF_CALLCHAIN_H -int read_user_stack_slow(void __user *ptr, void *buf, int nb); +int read_user_stack_slow(const void __user *ptr, void *buf, int nb); void perf_callchain_user_64(struct perf_callchain_entry_ctx *entry, struct pt_regs *regs); void perf_callchain_user_32(struct perf_callchain_entry_ctx *entry, @@ -16,4 +16,27 @@ static inline bool invalid_user_sp(unsigned long sp) return (!sp || (sp & mask) || (sp > top)); } +/* + * On 32-bit we just access the address and let hash_page create a + * HPTE if necessary, so there is no need to fall back to reading + * the page tables. Since this is called at interrupt level, + * do_page_fault() won't treat a DSI as a page fault. + */ +static inline int __read_user_stack(const void __user *ptr, void *ret, + size_t size) +{ + unsigned long addr = (unsigned long)ptr; + int rc; + + if (addr > TASK_SIZE - size || (addr & (size - 1))) + return -EFAULT; + + rc = copy_from_user_nofault(ret, ptr, size); + + if (IS_ENABLED(CONFIG_PPC64) && rc) + return read_user_stack_slow(ptr, ret, size); + + return rc; +} + #endif /* _POWERPC_PERF_CALLCHAIN_H */ diff --git a/arch/powerpc/perf/callchain_32.c b/arch/powerpc/perf/callchain_32.c index 542e68b8eae0b..64e4013d8060b 100644 --- a/arch/powerpc/perf/callchain_32.c +++ b/arch/powerpc/perf/callchain_32.c @@ -30,26 +30,9 @@ #endif /* CONFIG_PPC64 */ -/* - * On 32-bit we just access the address and let hash_page create a - * HPTE if necessary, so there is no need to fall back to reading - * the page tables. Since this is called at interrupt level, - * do_page_fault() won't treat a DSI as a page fault. - */ -static int read_user_stack_32(unsigned int __user *ptr, unsigned int *ret) +static int read_user_stack_32(const unsigned int __user *ptr, unsigned int *ret) { - int rc; - - if ((unsigned long)ptr > TASK_SIZE - sizeof(unsigned int) || - ((unsigned long)ptr & 3)) - return -EFAULT; - - rc = copy_from_user_nofault(ret, ptr, sizeof(*ret)); - - if (IS_ENABLED(CONFIG_PPC64) && rc) - return read_user_stack_slow(ptr, ret, 4); - - return rc; + return __read_user_stack(ptr, ret, sizeof(*ret)); } /* diff --git a/arch/powerpc/perf/callchain_64.c b/arch/powerpc/perf/callchain_64.c index fa2a1b83b9b0e..fed90e827f3ad 100644 --- a/arch/powerpc/perf/callchain_64.c +++ b/arch/powerpc/perf/callchain_64.c @@ -23,7 +23,7 @@ * interrupt context, so if the access faults, we read the page tables * to find which page (if any) is mapped and access it directly. */ -int read_user_stack_slow(void __user *ptr, void *buf, int nb) +int read_user_stack_slow(const void __user *ptr, void *buf, int nb) { unsigned long addr = (unsigned long) ptr; @@ -44,16 +44,9 @@ int read_user_stack_slow(void __user *ptr, void *buf, int nb) return -EFAULT; } -static int read_user_stack_64(unsigned long __user *ptr, unsigned long *ret) +static int read_user_stack_64(const unsigned long __user *ptr, unsigned long *ret) { - if ((unsigned long)ptr > TASK_SIZE - sizeof(unsigned long) || - ((unsigned long)ptr & 7)) - return -EFAULT; - - if (!copy_from_user_nofault(ret, ptr, sizeof(*ret))) - return 0; - - return read_user_stack_slow(ptr, ret, 8); + return __read_user_stack(ptr, ret, sizeof(*ret)); } /* -- GitLab From dfa03fff86027e58c8dba5c03ae68150d4e513ad Mon Sep 17 00:00:00 2001 From: Sandipan Das Date: Thu, 30 Jul 2020 10:38:46 +0530 Subject: [PATCH 1158/1476] selftests/powerpc: Fix online CPU selection The size of the CPU affinity mask must be large enough for systems with a very large number of CPUs. Otherwise, tests which try to determine the first online CPU by calling sched_getaffinity() will fail. This makes sure that the size of the allocated affinity mask is dependent on the number of CPUs as reported by get_nprocs_conf(). Fixes: 3752e453f6ba ("selftests/powerpc: Add tests of PMU EBBs") Reported-by: Shirisha Ganta Signed-off-by: Sandipan Das Reviewed-by: Kamalesh Babulal Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/a408c4b8e9a23bb39b539417a21eb0ff47bb5127.1596084858.git.sandipan@linux.ibm.com --- tools/testing/selftests/powerpc/utils.c | 37 +++++++++++++++++-------- 1 file changed, 25 insertions(+), 12 deletions(-) diff --git a/tools/testing/selftests/powerpc/utils.c b/tools/testing/selftests/powerpc/utils.c index 933678f1ed0a0..18b6a773d5c73 100644 --- a/tools/testing/selftests/powerpc/utils.c +++ b/tools/testing/selftests/powerpc/utils.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include @@ -88,28 +89,40 @@ void *get_auxv_entry(int type) int pick_online_cpu(void) { - cpu_set_t mask; - int cpu; + int ncpus, cpu = -1; + cpu_set_t *mask; + size_t size; + + ncpus = get_nprocs_conf(); + size = CPU_ALLOC_SIZE(ncpus); + mask = CPU_ALLOC(ncpus); + if (!mask) { + perror("malloc"); + return -1; + } - CPU_ZERO(&mask); + CPU_ZERO_S(size, mask); - if (sched_getaffinity(0, sizeof(mask), &mask)) { + if (sched_getaffinity(0, size, mask)) { perror("sched_getaffinity"); - return -1; + goto done; } /* We prefer a primary thread, but skip 0 */ - for (cpu = 8; cpu < CPU_SETSIZE; cpu += 8) - if (CPU_ISSET(cpu, &mask)) - return cpu; + for (cpu = 8; cpu < ncpus; cpu += 8) + if (CPU_ISSET_S(cpu, size, mask)) + goto done; /* Search for anything, but in reverse */ - for (cpu = CPU_SETSIZE - 1; cpu >= 0; cpu--) - if (CPU_ISSET(cpu, &mask)) - return cpu; + for (cpu = ncpus - 1; cpu >= 0; cpu--) + if (CPU_ISSET_S(cpu, size, mask)) + goto done; printf("No cpus in affinity mask?!\n"); - return -1; + +done: + CPU_FREE(mask); + return cpu; } bool is_ppc64le(void) -- GitLab From 3af0ada7dd98c6da35c1fd7f107af3b9aa5e904c Mon Sep 17 00:00:00 2001 From: "Gautham R. Shenoy" Date: Thu, 30 Jul 2020 11:02:55 +0530 Subject: [PATCH 1159/1476] cpuidle: pseries: Set the latency-hint before entering CEDE As per the PAPR, each H_CEDE call is associated with a latency-hint to be passed in the VPA field "cede_latency_hint". The CEDE states that we were implicitly entering so far is CEDE with latency-hint = 0. This patch explicitly sets the latency hint corresponding to the CEDE state that we are currently entering. While at it, we save the previous hint, to be restored once we wakeup from CEDE. This will be required in the future when we expose extended-cede states through the cpuidle framework, where each of them will have a different cede-latency hint. Signed-off-by: Gautham R. Shenoy [mpe: Make cede_latency_hint static] Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/1596087177-30329-2-git-send-email-ego@linux.vnet.ibm.com --- drivers/cpuidle/cpuidle-pseries.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/drivers/cpuidle/cpuidle-pseries.c b/drivers/cpuidle/cpuidle-pseries.c index 3e058ad2bb514..31c8e51162b27 100644 --- a/drivers/cpuidle/cpuidle-pseries.c +++ b/drivers/cpuidle/cpuidle-pseries.c @@ -86,19 +86,27 @@ static void check_and_cede_processor(void) } } +#define NR_DEDICATED_STATES 2 /* snooze, CEDE */ + +static u8 cede_latency_hint[NR_DEDICATED_STATES]; + static int dedicated_cede_loop(struct cpuidle_device *dev, struct cpuidle_driver *drv, int index) { + u8 old_latency_hint; pseries_idle_prolog(); get_lppaca()->donate_dedicated_cpu = 1; + old_latency_hint = get_lppaca()->cede_latency_hint; + get_lppaca()->cede_latency_hint = cede_latency_hint[index]; HMT_medium(); check_and_cede_processor(); local_irq_disable(); get_lppaca()->donate_dedicated_cpu = 0; + get_lppaca()->cede_latency_hint = old_latency_hint; pseries_idle_epilog(); @@ -130,7 +138,7 @@ static int shared_cede_loop(struct cpuidle_device *dev, /* * States for dedicated partition case. */ -static struct cpuidle_state dedicated_states[] = { +static struct cpuidle_state dedicated_states[NR_DEDICATED_STATES] = { { /* Snooze */ .name = "snooze", .desc = "snooze", @@ -233,7 +241,7 @@ static int pseries_idle_probe(void) max_idle_state = ARRAY_SIZE(shared_states); } else { cpuidle_state_table = dedicated_states; - max_idle_state = ARRAY_SIZE(dedicated_states); + max_idle_state = NR_DEDICATED_STATES; } } else return -ENODEV; -- GitLab From 054e44ba99ae36918631fcbf5f034e466c2f1b73 Mon Sep 17 00:00:00 2001 From: "Gautham R. Shenoy" Date: Thu, 30 Jul 2020 11:02:56 +0530 Subject: [PATCH 1160/1476] cpuidle: pseries: Add function to parse extended CEDE records Currently we use CEDE with latency-hint 0 as the only other idle state on a dedicated LPAR apart from the polling "snooze" state. The platform might support additional extended CEDE idle states, which can be discovered through the "ibm,get-system-parameter" rtas-call made with CEDE_LATENCY_TOKEN. This patch adds a function to obtain information about the extended CEDE idle states from the platform and parse the contents to populate an array of extended CEDE states. These idle states thus discovered will be added to the cpuidle framework in the next patch. dmesg on a POWER8 and POWER9 LPAR, demonstrating the output of parsing the extended CEDE latency parameters are as follows POWER8 [ 10.093279] xcede : xcede_record_size = 10 [ 10.093285] xcede : Record 0 : hint = 1, latency = 0x3c00 tb ticks, Wake-on-irq = 1 [ 10.093291] xcede : Record 1 : hint = 2, latency = 0x4e2000 tb ticks, Wake-on-irq = 0 [ 10.093297] cpuidle : Skipping the 2 Extended CEDE idle states POWER9 [ 5.913180] xcede : xcede_record_size = 10 [ 5.913183] xcede : Record 0 : hint = 1, latency = 0x400 tb ticks, Wake-on-irq = 1 [ 5.913188] xcede : Record 1 : hint = 2, latency = 0x3e8000 tb ticks, Wake-on-irq = 0 [ 5.913193] cpuidle : Skipping the 2 Extended CEDE idle states Signed-off-by: Gautham R. Shenoy [mpe: Make space for 16 records, drop memset, minor cleanup & formatting] Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/1596087177-30329-3-git-send-email-ego@linux.vnet.ibm.com --- drivers/cpuidle/cpuidle-pseries.c | 136 +++++++++++++++++++++++++++++- 1 file changed, 135 insertions(+), 1 deletion(-) diff --git a/drivers/cpuidle/cpuidle-pseries.c b/drivers/cpuidle/cpuidle-pseries.c index 31c8e51162b27..abfc160c9837e 100644 --- a/drivers/cpuidle/cpuidle-pseries.c +++ b/drivers/cpuidle/cpuidle-pseries.c @@ -21,6 +21,7 @@ #include #include #include +#include static struct cpuidle_driver pseries_idle_driver = { .name = "pseries_idle", @@ -86,8 +87,131 @@ static void check_and_cede_processor(void) } } -#define NR_DEDICATED_STATES 2 /* snooze, CEDE */ +/* + * XCEDE: Extended CEDE states discovered through the + * "ibm,get-systems-parameter" RTAS call with the token + * CEDE_LATENCY_TOKEN + */ + +/* + * Section 7.3.16 System Parameters Option of PAPR version 2.8.1 has a + * table with all the parameters to ibm,get-system-parameters. + * CEDE_LATENCY_TOKEN corresponds to the token value for Cede Latency + * Settings Information. + */ +#define CEDE_LATENCY_TOKEN 45 + +/* + * If the platform supports the cede latency settings information system + * parameter it must provide the following information in the NULL terminated + * parameter string: + * + * a. The first byte is the length “N” of each cede latency setting record minus + * one (zero indicates a length of 1 byte). + * + * b. For each supported cede latency setting a cede latency setting record + * consisting of the first “N” bytes as per the following table. + * + * ----------------------------- + * | Field | Field | + * | Name | Length | + * ----------------------------- + * | Cede Latency | 1 Byte | + * | Specifier Value | | + * ----------------------------- + * | Maximum wakeup | | + * | latency in | 8 Bytes | + * | tb-ticks | | + * ----------------------------- + * | Responsive to | | + * | external | 1 Byte | + * | interrupts | | + * ----------------------------- + * + * This version has cede latency record size = 10. + * + * The structure xcede_latency_payload represents a) and b) with + * xcede_latency_record representing the table in b). + * + * xcede_latency_parameter is what gets returned by + * ibm,get-systems-parameter RTAS call when made with + * CEDE_LATENCY_TOKEN. + * + * These structures are only used to represent the data obtained by the RTAS + * call. The data is in big-endian. + */ +struct xcede_latency_record { + u8 hint; + __be64 latency_ticks; + u8 wake_on_irqs; +} __packed; + +// Make space for 16 records, which "should be enough". +struct xcede_latency_payload { + u8 record_size; + struct xcede_latency_record records[16]; +} __packed; + +struct xcede_latency_parameter { + __be16 payload_size; + struct xcede_latency_payload payload; + u8 null_char; +} __packed; + +static unsigned int nr_xcede_records; +static struct xcede_latency_parameter xcede_latency_parameter __initdata; + +static int __init parse_cede_parameters(void) +{ + struct xcede_latency_payload *payload; + u32 total_xcede_records_size; + u8 xcede_record_size; + u16 payload_size; + int ret, i; + + ret = rtas_call(rtas_token("ibm,get-system-parameter"), 3, 1, + NULL, CEDE_LATENCY_TOKEN, __pa(&xcede_latency_parameter), + sizeof(xcede_latency_parameter)); + if (ret) { + pr_err("xcede: Error parsing CEDE_LATENCY_TOKEN\n"); + return ret; + } + + payload_size = be16_to_cpu(xcede_latency_parameter.payload_size); + payload = &xcede_latency_parameter.payload; + xcede_record_size = payload->record_size + 1; + + if (xcede_record_size != sizeof(struct xcede_latency_record)) { + pr_err("xcede: Expected record-size %lu. Observed size %u.\n", + sizeof(struct xcede_latency_record), xcede_record_size); + return -EINVAL; + } + + pr_info("xcede: xcede_record_size = %d\n", xcede_record_size); + + /* + * Since the payload_size includes the last NULL byte and the + * xcede_record_size, the remaining bytes correspond to array of all + * cede_latency settings. + */ + total_xcede_records_size = payload_size - 2; + nr_xcede_records = total_xcede_records_size / xcede_record_size; + + for (i = 0; i < nr_xcede_records; i++) { + struct xcede_latency_record *record = &payload->records[i]; + u64 latency_ticks = be64_to_cpu(record->latency_ticks); + u8 wake_on_irqs = record->wake_on_irqs; + u8 hint = record->hint; + + pr_info("xcede: Record %d : hint = %u, latency = 0x%llx tb ticks, Wake-on-irq = %u\n", + i, hint, latency_ticks, wake_on_irqs); + } + + return 0; +} + +#define NR_DEDICATED_STATES 2 /* snooze, CEDE */ static u8 cede_latency_hint[NR_DEDICATED_STATES]; static int dedicated_cede_loop(struct cpuidle_device *dev, @@ -219,6 +343,15 @@ static int pseries_cpuidle_driver_init(void) return 0; } +static void __init parse_xcede_idle_states(void) +{ + if (parse_cede_parameters()) + return; + + pr_info("cpuidle : Skipping the %d Extended CEDE idle states\n", + nr_xcede_records); +} + /* * pseries_idle_probe() * Choose state table for shared versus dedicated partition @@ -240,6 +373,7 @@ static int pseries_idle_probe(void) cpuidle_state_table = shared_states; max_idle_state = ARRAY_SIZE(shared_states); } else { + parse_xcede_idle_states(); cpuidle_state_table = dedicated_states; max_idle_state = NR_DEDICATED_STATES; } -- GitLab From d947fb4c965cdb7242f3f91124ea16079c49fa8b Mon Sep 17 00:00:00 2001 From: "Gautham R. Shenoy" Date: Thu, 30 Jul 2020 11:02:57 +0530 Subject: [PATCH 1161/1476] cpuidle: pseries: Fixup exit latency for CEDE(0) We are currently assuming that CEDE(0) has exit latency 10us, since there is no way for us to query from the platform. However, if the wakeup latency of an Extended CEDE state is smaller than 10us, then we can be sure that the exit latency of CEDE(0) cannot be more than that. In this patch, we fix the exit latency of CEDE(0) if we discover an Extended CEDE state with wakeup latency smaller than 10us. Benchmark results: On POWER8, this patch does not have any impact since the advertized latency of Extended CEDE (1) is 30us which is higher than the default latency of CEDE (0) which is 10us. On POWER9 we see improvement the single-threaded performance of ebizzy, and no regression in the wakeup latency or the number of context-switches. ebizzy: 2 ebizzy threads bound to the same big-core. 25% improvement in the avg records/s with patch. x without_patch * with_patch N Min Max Median Avg Stddev x 10 2491089 5834307 5398375 4244335 1596244.9 * 10 2893813 5834474 5832448 5327281.3 1055941.4 context_switch2: There is no major regression observed with this patch as seen from the context_switch2 benchmark. context_switch2 across CPU0 CPU1 (Both belong to same big-core, but different small cores). We observe a minor 0.14% regression in the number of context-switches (higher is better). x without_patch * with_patch N Min Max Median Avg Stddev x 500 348872 362236 354712 354745.69 2711.827 * 500 349422 361452 353942 354215.4 2576.9258 Difference at 99.0% confidence -530.288 +/- 430.963 -0.149484% +/- 0.121485% (Student's t, pooled s = 2645.24) context_switch2 across CPU0 CPU8 (Different big-cores). We observe a 0.37% improvement in the number of context-switches (higher is better). x without_patch * with_patch N Min Max Median Avg Stddev x 500 287956 294940 288896 288977.23 646.59295 * 500 288300 294646 289582 290064.76 1161.9992 Difference at 99.0% confidence 1087.53 +/- 153.194 0.376337% +/- 0.0530125% (Student's t, pooled s = 940.299) schbench: No major difference could be seen until the 99.9th percentile. Without-patch: Latency percentiles (usec) 50.0th: 29 75.0th: 39 90.0th: 49 95.0th: 59 *99.0th: 13104 99.5th: 14672 99.9th: 15824 min=0, max=17993 With-patch: Latency percentiles (usec) 50.0th: 29 75.0th: 40 90.0th: 50 95.0th: 61 *99.0th: 13648 99.5th: 14768 99.9th: 15664 min=0, max=29812 Signed-off-by: Gautham R. Shenoy [mpe: Minor formatting] Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/1596087177-30329-4-git-send-email-ego@linux.vnet.ibm.com --- drivers/cpuidle/cpuidle-pseries.c | 45 ++++++++++++++++++++++++++++--- 1 file changed, 42 insertions(+), 3 deletions(-) diff --git a/drivers/cpuidle/cpuidle-pseries.c b/drivers/cpuidle/cpuidle-pseries.c index abfc160c9837e..ff6d99e923a49 100644 --- a/drivers/cpuidle/cpuidle-pseries.c +++ b/drivers/cpuidle/cpuidle-pseries.c @@ -343,13 +343,52 @@ static int pseries_cpuidle_driver_init(void) return 0; } -static void __init parse_xcede_idle_states(void) +static void __init fixup_cede0_latency(void) { + struct xcede_latency_payload *payload; + u64 min_latency_us; + int i; + + min_latency_us = dedicated_states[1].exit_latency; // CEDE latency + if (parse_cede_parameters()) return; - pr_info("cpuidle : Skipping the %d Extended CEDE idle states\n", + pr_info("cpuidle: Skipping the %d Extended CEDE idle states\n", nr_xcede_records); + + payload = &xcede_latency_parameter.payload; + for (i = 0; i < nr_xcede_records; i++) { + struct xcede_latency_record *record = &payload->records[i]; + u64 latency_tb = be64_to_cpu(record->latency_ticks); + u64 latency_us = tb_to_ns(latency_tb) / NSEC_PER_USEC; + + if (latency_us < min_latency_us) + min_latency_us = latency_us; + } + + /* + * By default, we assume that CEDE(0) has exit latency 10us, + * since there is no way for us to query from the platform. + * + * However, if the wakeup latency of an Extended CEDE state is + * smaller than 10us, then we can be sure that CEDE(0) + * requires no more than that. + * + * Perform the fix-up. + */ + if (min_latency_us < dedicated_states[1].exit_latency) { + u64 cede0_latency = min_latency_us - 1; + + if (cede0_latency <= 0) + cede0_latency = min_latency_us; + + dedicated_states[1].exit_latency = cede0_latency; + dedicated_states[1].target_residency = 10 * (cede0_latency); + pr_info("cpuidle: Fixed up CEDE exit latency to %llu us\n", + cede0_latency); + } + } /* @@ -373,7 +412,7 @@ static int pseries_idle_probe(void) cpuidle_state_table = shared_states; max_idle_state = ARRAY_SIZE(shared_states); } else { - parse_xcede_idle_states(); + fixup_cede0_latency(); cpuidle_state_table = dedicated_states; max_idle_state = NR_DEDICATED_STATES; } -- GitLab From dea286bb71baded7d2fb4f090e3b9fd2c1ccac58 Mon Sep 17 00:00:00 2001 From: Logan Gunthorpe Date: Wed, 29 Jul 2020 17:18:44 -0600 Subject: [PATCH 1162/1476] PCI/P2PDMA: Allow P2PDMA on AMD Zen and newer CPUs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Allow P2PDMA if the CPU vendor is AMD and family is 0x17 (Zen) or greater. [bhelgaas: commit log, simplify #if/#else/#endif] Link: https://lore.kernel.org/r/20200729231844.4653-1-logang@deltatee.com Signed-off-by: Logan Gunthorpe Signed-off-by: Bjorn Helgaas Reviewed-by: Alex Deucher Cc: Christian König Cc: Huang Rui --- drivers/pci/p2pdma.c | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/drivers/pci/p2pdma.c b/drivers/pci/p2pdma.c index e8e444eeb1cd2..1ec61fced4c3a 100644 --- a/drivers/pci/p2pdma.c +++ b/drivers/pci/p2pdma.c @@ -273,6 +273,19 @@ static void seq_buf_print_bus_devfn(struct seq_buf *buf, struct pci_dev *pdev) seq_buf_printf(buf, "%s;", pci_name(pdev)); } +static bool cpu_supports_p2pdma(void) +{ +#ifdef CONFIG_X86 + struct cpuinfo_x86 *c = &cpu_data(0); + + /* Any AMD CPU whose family ID is Zen or newer supports p2pdma */ + if (c->x86_vendor == X86_VENDOR_AMD && c->x86 >= 0x17) + return true; +#endif + + return false; +} + static const struct pci_p2pdma_whitelist_entry { unsigned short vendor; unsigned short device; @@ -280,11 +293,6 @@ static const struct pci_p2pdma_whitelist_entry { REQ_SAME_HOST_BRIDGE = 1 << 0, } flags; } pci_p2pdma_whitelist[] = { - /* AMD ZEN */ - {PCI_VENDOR_ID_AMD, 0x1450, 0}, - {PCI_VENDOR_ID_AMD, 0x15d0, 0}, - {PCI_VENDOR_ID_AMD, 0x1630, 0}, - /* Intel Xeon E5/Core i7 */ {PCI_VENDOR_ID_INTEL, 0x3c00, REQ_SAME_HOST_BRIDGE}, {PCI_VENDOR_ID_INTEL, 0x3c01, REQ_SAME_HOST_BRIDGE}, @@ -473,7 +481,8 @@ upstream_bridge_distance(struct pci_dev *provider, struct pci_dev *client, acs_redirects, acs_list); if (map_type == PCI_P2PDMA_MAP_THRU_HOST_BRIDGE) { - if (!host_bridge_whitelist(provider, client)) + if (!cpu_supports_p2pdma() && + !host_bridge_whitelist(provider, client)) map_type = PCI_P2PDMA_MAP_NOT_SUPPORTED; } -- GitLab From 2e4770a5661a727be1ccb65ebc97baec3fa863a0 Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Tue, 21 Jul 2020 23:17:35 +0800 Subject: [PATCH 1163/1476] PCI: rpadlpar: Make functions static The sparse tool report build warnings as follows: drivers/pci/hotplug/rpadlpar_core.c:355:5: warning: symbol 'dlpar_remove_pci_slot' was not declared. Should it be static? drivers/pci/hotplug/rpadlpar_core.c:461:12: warning: symbol 'rpadlpar_io_init' was not declared. Should it be static? drivers/pci/hotplug/rpadlpar_core.c:473:6: warning: symbol 'rpadlpar_io_exit' was not declared. Should it be static? Those functions are not used outside of this file, so mark them static. Also mark rpadlpar_io_exit() as __exit. Link: https://lore.kernel.org/r/20200721151735.41181-1-weiyongjun1@huawei.com Reported-by: Hulk Robot Signed-off-by: Wei Yongjun Signed-off-by: Bjorn Helgaas --- drivers/pci/hotplug/rpadlpar_core.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/pci/hotplug/rpadlpar_core.c b/drivers/pci/hotplug/rpadlpar_core.c index c5eb509c72f0f..f979b7098acf3 100644 --- a/drivers/pci/hotplug/rpadlpar_core.c +++ b/drivers/pci/hotplug/rpadlpar_core.c @@ -352,7 +352,7 @@ static int dlpar_remove_vio_slot(char *drc_name, struct device_node *dn) * -ENODEV Not a valid drc_name * -EIO Internal PCI Error */ -int dlpar_remove_pci_slot(char *drc_name, struct device_node *dn) +static int dlpar_remove_pci_slot(char *drc_name, struct device_node *dn) { struct pci_bus *bus; struct slot *slot; @@ -458,7 +458,7 @@ static inline int is_dlpar_capable(void) return (int) (rc != RTAS_UNKNOWN_SERVICE); } -int __init rpadlpar_io_init(void) +static int __init rpadlpar_io_init(void) { if (!is_dlpar_capable()) { @@ -470,7 +470,7 @@ int __init rpadlpar_io_init(void) return dlpar_sysfs_init(); } -void rpadlpar_io_exit(void) +static void __exit rpadlpar_io_exit(void) { dlpar_sysfs_exit(); } -- GitLab From 6184358da0004c8fd940afda6c0a0fa4027dc911 Mon Sep 17 00:00:00 2001 From: Guo Ren Date: Sat, 27 Jun 2020 13:57:06 +0000 Subject: [PATCH 1164/1476] riscv: Fixup static_obj() fail When enable LOCKDEP, static_obj() will cause error. Because some __initdata static variables is before _stext: static int static_obj(const void *obj) { unsigned long start = (unsigned long) &_stext, end = (unsigned long) &_end, addr = (unsigned long) obj; /* * static variable? */ if ((addr >= start) && (addr < end)) return 1; [ 0.067192] INFO: trying to register non-static key. [ 0.067325] the code is fine but needs lockdep annotation. [ 0.067449] turning off the locking correctness validator. [ 0.067718] CPU: 0 PID: 0 Comm: swapper/0 Not tainted 5.7.0-rc7-dirty #44 [ 0.067945] Call Trace: [ 0.068369] [] walk_stackframe+0x0/0xa4 [ 0.068506] [] show_stack+0x2a/0x34 [ 0.068631] [] dump_stack+0x94/0xca [ 0.068757] [] register_lock_class+0x5b8/0x5bc [ 0.068969] [] __lock_acquire+0x6c/0x1d5c [ 0.069101] [] lock_acquire+0xae/0x312 [ 0.069228] [] _raw_spin_lock_irqsave+0x40/0x5a [ 0.069357] [] complete+0x1e/0x50 [ 0.069479] [] rest_init+0x1b0/0x28a [ 0.069660] [] 0xffffffe0000016a2 [ 0.069779] [] 0xffffffe000001b84 [ 0.069953] [] 0xffffffe000001092 static __initdata DECLARE_COMPLETION(kthreadd_done); noinline void __ref rest_init(void) { ... complete(&kthreadd_done); Signed-off-by: Guo Ren Signed-off-by: Palmer Dabbelt --- arch/riscv/kernel/vmlinux.lds.S | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/riscv/kernel/vmlinux.lds.S b/arch/riscv/kernel/vmlinux.lds.S index e6f8016b366ab..f3586e31ed1ec 100644 --- a/arch/riscv/kernel/vmlinux.lds.S +++ b/arch/riscv/kernel/vmlinux.lds.S @@ -22,6 +22,7 @@ SECTIONS /* Beginning of code and text segment */ . = LOAD_OFFSET; _start = .; + _stext = .; HEAD_TEXT_SECTION . = ALIGN(PAGE_SIZE); @@ -54,7 +55,6 @@ SECTIONS . = ALIGN(SECTION_ALIGN); .text : { _text = .; - _stext = .; TEXT_TEXT SCHED_TEXT CPUIDLE_TEXT -- GitLab From c15959921f8d31bd332c9572650896941bb094c0 Mon Sep 17 00:00:00 2001 From: Zong Li Date: Sat, 27 Jun 2020 13:57:07 +0000 Subject: [PATCH 1165/1476] riscv: Fixup lockdep_assert_held with wrong param cpu_running The cpu_running is not a lock-class, it lacks the dep_map member in completion. It causes the error as follow: arch/riscv/kernel/smpboot.c: In function '__cpu_up': ./include/linux/lockdep.h:364:52: error: 'struct completion' has no member named 'dep_map' 364 | #define lockdep_is_held(lock) lock_is_held(&(lock)->dep_map) | ^~ ./include/asm-generic/bug.h:113:25: note: in definition of macro 'WARN_ON' 113 | int __ret_warn_on = !!(condition); \ | ^~~~~~~~~ ./include/linux/lockdep.h:390:27: note: in expansion of macro 'lockdep_is_held' 390 | WARN_ON(debug_locks && !lockdep_is_held(l)); \ | ^~~~~~~~~~~~~~~ arch/riscv/kernel/smpboot.c:118:2: note: in expansion of macro 'lockdep_assert_held' 118 | lockdep_assert_held(&cpu_running); There are a lot of archs which use cpu_running in smpboot.c (arm, arm64, openrisc, xtensa, s390, x86, mips), but none of them try lockdep_assert_held(&cpu_running.wait.lock). So Just remove it. Signed-off-by: Zong Li Signed-off-by: Guo Ren Signed-off-by: Palmer Dabbelt --- arch/riscv/kernel/smpboot.c | 1 - 1 file changed, 1 deletion(-) diff --git a/arch/riscv/kernel/smpboot.c b/arch/riscv/kernel/smpboot.c index 3e033e97dd083..403bd93412a4e 100644 --- a/arch/riscv/kernel/smpboot.c +++ b/arch/riscv/kernel/smpboot.c @@ -121,7 +121,6 @@ int __cpu_up(unsigned int cpu, struct task_struct *tidle) ret = start_secondary_cpu(cpu, tidle); if (!ret) { - lockdep_assert_held(&cpu_running); wait_for_completion_timeout(&cpu_running, msecs_to_jiffies(1000)); -- GitLab From 3c46979829824da5af8766d89fa877976bdae884 Mon Sep 17 00:00:00 2001 From: Guo Ren Date: Sat, 27 Jun 2020 13:57:08 +0000 Subject: [PATCH 1166/1476] riscv: Enable LOCKDEP_SUPPORT & fixup TRACE_IRQFLAGS_SUPPORT Lockdep is needed by proving the spinlocks and rwlocks. To suupport it, we need fixup TRACE_IRQFLAGS_SUPPORT in kernel/entry.S. This patch follow Documentation/irqflags-tracing.txt. Signed-off-by: Guo Ren Signed-off-by: Palmer Dabbelt --- arch/riscv/Kconfig | 3 +++ arch/riscv/kernel/entry.S | 34 +++++++++++++++++++++++++++++++++- 2 files changed, 36 insertions(+), 1 deletion(-) diff --git a/arch/riscv/Kconfig b/arch/riscv/Kconfig index 089293e4ad46d..543335df7376f 100644 --- a/arch/riscv/Kconfig +++ b/arch/riscv/Kconfig @@ -179,6 +179,9 @@ config PGTABLE_LEVELS default 3 if 64BIT default 2 +config LOCKDEP_SUPPORT + def_bool y + source "arch/riscv/Kconfig.socs" menu "Platform type" diff --git a/arch/riscv/kernel/entry.S b/arch/riscv/kernel/entry.S index cae7e6d4c7ef1..45c81e49462d5 100644 --- a/arch/riscv/kernel/entry.S +++ b/arch/riscv/kernel/entry.S @@ -97,19 +97,26 @@ _save_context: la gp, __global_pointer$ .option pop - la ra, ret_from_exception +#ifdef CONFIG_TRACE_IRQFLAGS + call trace_hardirqs_off +#endif /* * MSB of cause differentiates between * interrupts and exceptions */ bge s4, zero, 1f + la ra, ret_from_exception + /* Handle interrupts */ move a0, sp /* pt_regs */ la a1, handle_arch_irq REG_L a1, (a1) jr a1 1: +#ifdef CONFIG_TRACE_IRQFLAGS + call trace_hardirqs_on +#endif /* * Exceptions run with interrupts enabled or disabled depending on the * state of SR_PIE in m/sstatus. @@ -119,6 +126,7 @@ _save_context: csrs CSR_STATUS, SR_IE 1: + la ra, ret_from_exception /* Handle syscalls */ li t0, EXC_SYSCALL beq s4, t0, handle_syscall @@ -137,6 +145,17 @@ _save_context: tail do_trap_unknown handle_syscall: +#ifdef CONFIG_TRACE_IRQFLAGS + /* Recover a0 - a7 for system calls */ + REG_L a0, PT_A0(sp) + REG_L a1, PT_A1(sp) + REG_L a2, PT_A2(sp) + REG_L a3, PT_A3(sp) + REG_L a4, PT_A4(sp) + REG_L a5, PT_A5(sp) + REG_L a6, PT_A6(sp) + REG_L a7, PT_A7(sp) +#endif /* save the initial A0 value (needed in signal handlers) */ REG_S a0, PT_ORIG_A0(sp) /* @@ -190,6 +209,9 @@ ret_from_syscall_rejected: ret_from_exception: REG_L s0, PT_STATUS(sp) csrc CSR_STATUS, SR_IE +#ifdef CONFIG_TRACE_IRQFLAGS + call trace_hardirqs_off +#endif #ifdef CONFIG_RISCV_M_MODE /* the MPP value is too large to be used as an immediate arg for addi */ li t0, SR_MPP @@ -216,6 +238,16 @@ resume_userspace: csrw CSR_SCRATCH, tp restore_all: +#ifdef CONFIG_TRACE_IRQFLAGS + REG_L s1, PT_STATUS(sp) + andi t0, s1, SR_PIE + beqz t0, 1f + call trace_hardirqs_on + j 2f +1: + call trace_hardirqs_off +2: +#endif REG_L a0, PT_STATUS(sp) /* * The current load reservation is effectively part of the processor's -- GitLab From 298447928bb1c6bda9ad6f2ce3958d15c329816b Mon Sep 17 00:00:00 2001 From: Greentime Hu Date: Wed, 24 Jun 2020 17:03:15 +0800 Subject: [PATCH 1167/1476] riscv: Support irq_work via self IPIs Support for arch_irq_work_raise() and arch_irq_work_has_interrupt() was missing from riscv (a prerequisite for FULL_NOHZ). Signed-off-by: Greentime Hu Signed-off-by: Palmer Dabbelt --- arch/riscv/include/asm/irq_work.h | 10 ++++++++++ arch/riscv/kernel/smp.c | 15 +++++++++++++++ 2 files changed, 25 insertions(+) create mode 100644 arch/riscv/include/asm/irq_work.h diff --git a/arch/riscv/include/asm/irq_work.h b/arch/riscv/include/asm/irq_work.h new file mode 100644 index 0000000000000..d6c277992f76a --- /dev/null +++ b/arch/riscv/include/asm/irq_work.h @@ -0,0 +1,10 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _ASM_RISCV_IRQ_WORK_H +#define _ASM_RISCV_IRQ_WORK_H + +static inline bool arch_irq_work_has_interrupt(void) +{ + return true; +} +extern void arch_irq_work_raise(void); +#endif /* _ASM_RISCV_IRQ_WORK_H */ diff --git a/arch/riscv/kernel/smp.c b/arch/riscv/kernel/smp.c index b1d4f452f8436..554b0fb470608 100644 --- a/arch/riscv/kernel/smp.c +++ b/arch/riscv/kernel/smp.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include @@ -26,6 +27,7 @@ enum ipi_message_type { IPI_RESCHEDULE, IPI_CALL_FUNC, IPI_CPU_STOP, + IPI_IRQ_WORK, IPI_MAX }; @@ -123,6 +125,13 @@ static inline void clear_ipi(void) clint_clear_ipi(cpuid_to_hartid_map(smp_processor_id())); } +#ifdef CONFIG_IRQ_WORK +void arch_irq_work_raise(void) +{ + send_ipi_single(smp_processor_id(), IPI_IRQ_WORK); +} +#endif + void handle_IPI(struct pt_regs *regs) { struct pt_regs *old_regs = set_irq_regs(regs); @@ -158,6 +167,11 @@ void handle_IPI(struct pt_regs *regs) ipi_stop(); } + if (ops & (1 << IPI_IRQ_WORK)) { + stats[IPI_IRQ_WORK]++; + irq_work_run(); + } + BUG_ON((ops >> IPI_MAX) != 0); /* Order data access and bit testing. */ @@ -173,6 +187,7 @@ static const char * const ipi_names[] = { [IPI_RESCHEDULE] = "Rescheduling interrupts", [IPI_CALL_FUNC] = "Function call interrupts", [IPI_CPU_STOP] = "CPU stop interrupts", + [IPI_IRQ_WORK] = "IRQ work interrupts", }; void show_ipi_stats(struct seq_file *p, int prec) -- GitLab From ed48b297fe211400531a129c592165958d661def Mon Sep 17 00:00:00 2001 From: Greentime Hu Date: Wed, 24 Jun 2020 17:03:16 +0800 Subject: [PATCH 1168/1476] riscv: Enable context tracking This patch implements and enables context tracking for riscv (which is a prerequisite for CONFIG_NO_HZ_FULL support) It adds checking for previous state in the entry that all excepttions and interrupts goes to and calls context_tracking_user_exit() if it comes from user space. It also calls context_tracking_user_enter() if it will return to user space before restore_all. This patch is tested with the dynticks-testing testcase in qemu-system-riscv64 virt machine and Unleashed board. git://git.kernel.org/pub/scm/linux/kernel/git/frederic/dynticks-testing.git We can see the log here. The tick got mostly stopped during the execution of the user loop. _-----=> irqs-off / _----=> need-resched | / _---=> hardirq/softirq || / _--=> preempt-depth ||| / delay TASK-PID CPU# |||| TIMESTAMP FUNCTION | | | |||| | | -0 [001] d..2 604.183512: sched_switch: prev_comm=swapper/1 prev_pid=0 prev_prio=120 prev_state=R ==> next_comm=taskset next_pid=273 next_prio=120 user_loop-273 [001] d.h1 604.184788: hrtimer_expire_entry: hrtimer=000000002eda5fab function=tick_sched_timer now=604176096300 user_loop-273 [001] d.s2 604.184897: workqueue_queue_work: work struct=00000000383402c2 function=vmstat_update workqueue=00000000f36d35d4 req_cpu=1 cpu=1 user_loop-273 [001] dns2 604.185039: tick_stop: success=0 dependency=SCHED user_loop-273 [001] dn.1 604.185103: tick_stop: success=0 dependency=SCHED user_loop-273 [001] d..2 604.185154: sched_switch: prev_comm=taskset prev_pid=273 prev_prio=120 prev_state=R+ ==> next_comm=kworker/1:1 next_pid=46 next_prio=120 <...>-46 [001] .... 604.185194: workqueue_execute_start: work struct 00000000383402c2: function vmstat_update <...>-46 [001] d..2 604.185266: sched_switch: prev_comm=kworker/1:1 prev_pid=46 prev_prio=120 prev_state=I ==> next_comm=taskset next_pid=273 next_prio=120 user_loop-273 [001] d.h1 604.188812: hrtimer_expire_entry: hrtimer=000000002eda5fab function=tick_sched_timer now=604180133400 user_loop-273 [001] d..1 604.189050: tick_stop: success=1 dependency=NONE user_loop-273 [001] d..2 614.251386: sched_switch: prev_comm=user_loop prev_pid=273 prev_prio=120 prev_state=X ==> next_comm=swapper/1 next_pid=0 next_prio=120 -0 [001] d..2 614.315391: sched_switch: prev_comm=swapper/1 prev_pid=0 prev_prio=120 prev_state=R ==> next_comm=taskset next_pid=276 next_prio=120 Signed-off-by: Greentime Hu Signed-off-by: Palmer Dabbelt --- arch/riscv/Kconfig | 1 + arch/riscv/kernel/entry.S | 16 +++++++++++++++- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/arch/riscv/Kconfig b/arch/riscv/Kconfig index 543335df7376f..b7efe87329dd5 100644 --- a/arch/riscv/Kconfig +++ b/arch/riscv/Kconfig @@ -53,6 +53,7 @@ config RISCV select HAVE_ARCH_SECCOMP_FILTER select HAVE_ARCH_TRACEHOOK select HAVE_ASM_MODVERSIONS + select HAVE_CONTEXT_TRACKING select HAVE_COPY_THREAD_TLS select HAVE_DMA_CONTIGUOUS if MMU select HAVE_EBPF_JIT if MMU diff --git a/arch/riscv/kernel/entry.S b/arch/riscv/kernel/entry.S index 45c81e49462d5..124506ef4c5df 100644 --- a/arch/riscv/kernel/entry.S +++ b/arch/riscv/kernel/entry.S @@ -100,6 +100,16 @@ _save_context: #ifdef CONFIG_TRACE_IRQFLAGS call trace_hardirqs_off #endif + +#ifdef CONFIG_CONTEXT_TRACKING + /* If previous state is in user mode, call context_tracking_user_exit. */ + li a0, SR_PP + and a0, s1, a0 + bnez a0, skip_context_tracking + call context_tracking_user_exit +skip_context_tracking: +#endif + /* * MSB of cause differentiates between * interrupts and exceptions @@ -145,7 +155,7 @@ _save_context: tail do_trap_unknown handle_syscall: -#ifdef CONFIG_TRACE_IRQFLAGS +#if defined(CONFIG_TRACE_IRQFLAGS) || defined(CONFIG_CONTEXT_TRACKING) /* Recover a0 - a7 for system calls */ REG_L a0, PT_A0(sp) REG_L a1, PT_A1(sp) @@ -227,6 +237,10 @@ resume_userspace: andi s1, s0, _TIF_WORK_MASK bnez s1, work_pending +#ifdef CONFIG_CONTEXT_TRACKING + call context_tracking_user_enter +#endif + /* Save unwound kernel stack pointer in thread_info */ addi s0, sp, PT_SIZE_ON_STACK REG_S s0, TASK_TI_KERNEL_SP(tp) -- GitLab From 20d38f7c45a44e4b762b586a7bcacbc93ddb3153 Mon Sep 17 00:00:00 2001 From: Tobias Klauser Date: Fri, 26 Jun 2020 14:40:56 +0200 Subject: [PATCH 1169/1476] riscv: Allow building with kcov coverage Add ARCH_HAS_KCOV and HAVE_GCC_PLUGINS to the riscv Kconfig. Also disable instrumentation of some early boot code and vdso. Boot-tested on QEMU's riscv64 virt machine. Signed-off-by: Tobias Klauser Acked-by: Dmitry Vyukov Signed-off-by: Palmer Dabbelt --- arch/riscv/Kconfig | 2 ++ arch/riscv/boot/Makefile | 2 ++ arch/riscv/kernel/vdso/Makefile | 1 + arch/riscv/mm/Makefile | 2 ++ 4 files changed, 7 insertions(+) diff --git a/arch/riscv/Kconfig b/arch/riscv/Kconfig index b7efe87329dd5..0b40449a2eae0 100644 --- a/arch/riscv/Kconfig +++ b/arch/riscv/Kconfig @@ -19,6 +19,7 @@ config RISCV select ARCH_HAS_DEBUG_WX select ARCH_HAS_GCOV_PROFILE_ALL select ARCH_HAS_GIGANTIC_PAGE + select ARCH_HAS_KCOV select ARCH_HAS_MMIOWB select ARCH_HAS_PTE_SPECIAL select ARCH_HAS_SET_DIRECT_MAP @@ -58,6 +59,7 @@ config RISCV select HAVE_DMA_CONTIGUOUS if MMU select HAVE_EBPF_JIT if MMU select HAVE_FUTEX_CMPXCHG if FUTEX + select HAVE_GCC_PLUGINS select HAVE_GENERIC_VDSO if MMU && 64BIT select HAVE_PCI select HAVE_PERF_EVENTS diff --git a/arch/riscv/boot/Makefile b/arch/riscv/boot/Makefile index 3530c59b3ea73..c59fca695f9dd 100644 --- a/arch/riscv/boot/Makefile +++ b/arch/riscv/boot/Makefile @@ -14,6 +14,8 @@ # Based on the ia64 and arm64 boot/Makefile. # +KCOV_INSTRUMENT := n + OBJCOPYFLAGS_Image :=-O binary -R .note -R .note.gnu.build-id -R .comment -S targets := Image loader diff --git a/arch/riscv/kernel/vdso/Makefile b/arch/riscv/kernel/vdso/Makefile index e4c7c2c8a02fa..c8c9e40023c68 100644 --- a/arch/riscv/kernel/vdso/Makefile +++ b/arch/riscv/kernel/vdso/Makefile @@ -32,6 +32,7 @@ CFLAGS_REMOVE_vgettimeofday.o = $(CC_FLAGS_FTRACE) -Os # Disable gcov profiling for VDSO code GCOV_PROFILE := n +KCOV_INSTRUMENT := n # Force dependency $(obj)/vdso.o: $(obj)/vdso.so diff --git a/arch/riscv/mm/Makefile b/arch/riscv/mm/Makefile index 363ef01c30b1c..c0185e556ca51 100644 --- a/arch/riscv/mm/Makefile +++ b/arch/riscv/mm/Makefile @@ -5,6 +5,8 @@ ifdef CONFIG_FTRACE CFLAGS_REMOVE_init.o = -pg endif +KCOV_INSTRUMENT_init.o := n + obj-y += init.o obj-y += extable.o obj-$(CONFIG_MMU) += fault.o pageattr.o -- GitLab From cbb3d91d3bcff7eae3e9c63ffa436fbb4c6c657e Mon Sep 17 00:00:00 2001 From: Tobias Klauser Date: Sat, 27 Jun 2020 12:50:50 +0200 Subject: [PATCH 1170/1476] riscv: Add kmemleak support Tested using syzkaller in QEMU's riscv64 virt machine. Signed-off-by: Tobias Klauser Signed-off-by: Palmer Dabbelt --- arch/riscv/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/riscv/Kconfig b/arch/riscv/Kconfig index 0b40449a2eae0..b124ca4323bf5 100644 --- a/arch/riscv/Kconfig +++ b/arch/riscv/Kconfig @@ -56,6 +56,7 @@ config RISCV select HAVE_ASM_MODVERSIONS select HAVE_CONTEXT_TRACKING select HAVE_COPY_THREAD_TLS + select HAVE_DEBUG_KMEMLEAK select HAVE_DMA_CONTIGUOUS if MMU select HAVE_EBPF_JIT if MMU select HAVE_FUTEX_CMPXCHG if FUTEX -- GitLab From 08b5985e7be54d15d058191adda2722bd395df32 Mon Sep 17 00:00:00 2001 From: Tobias Klauser Date: Fri, 3 Jul 2020 15:49:11 +0200 Subject: [PATCH 1171/1476] riscv: Fix typo in asm/hwcap.h uapi header s/userpsace/userspace/ Signed-off-by: Tobias Klauser Signed-off-by: Palmer Dabbelt --- arch/riscv/include/uapi/asm/hwcap.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/riscv/include/uapi/asm/hwcap.h b/arch/riscv/include/uapi/asm/hwcap.h index dee98ee283183..46dc3f5ee99f9 100644 --- a/arch/riscv/include/uapi/asm/hwcap.h +++ b/arch/riscv/include/uapi/asm/hwcap.h @@ -11,7 +11,7 @@ /* * Linux saves the floating-point registers according to the ISA Linux is * executing on, as opposed to the ISA the user program is compiled for. This - * is necessary for a handful of esoteric use cases: for example, userpsace + * is necessary for a handful of esoteric use cases: for example, userspace * threading libraries must be able to examine the actual machine state in * order to fully reconstruct the state of a thread. */ -- GitLab From f2c9699f65557a31fed4ddb9e5b4d9489b1bf32f Mon Sep 17 00:00:00 2001 From: Guo Ren Date: Fri, 10 Jul 2020 16:19:57 +0000 Subject: [PATCH 1172/1476] riscv: Add STACKPROTECTOR supported The -fstack-protector & -fstack-protector-strong features are from gcc. The patch only add basic kernel support to stack-protector feature and some arch could have its own solution such as ARM64_PTR_AUTH. After enabling STACKPROTECTOR and STACKPROTECTOR_STRONG, the .text size is expanded from 0x7de066 to 0x81fb32 (only 5%) to add canary checking code. Signed-off-by: Guo Ren Reviewed-by: Kees Cook Signed-off-by: Palmer Dabbelt --- arch/riscv/Kconfig | 1 + arch/riscv/include/asm/stackprotector.h | 33 +++++++++++++++++++++++++ arch/riscv/kernel/process.c | 6 +++++ 3 files changed, 40 insertions(+) create mode 100644 arch/riscv/include/asm/stackprotector.h diff --git a/arch/riscv/Kconfig b/arch/riscv/Kconfig index b124ca4323bf5..b55be0980b2b6 100644 --- a/arch/riscv/Kconfig +++ b/arch/riscv/Kconfig @@ -66,6 +66,7 @@ config RISCV select HAVE_PERF_EVENTS select HAVE_PERF_REGS select HAVE_PERF_USER_STACK_DUMP + select HAVE_STACKPROTECTOR select HAVE_SYSCALL_TRACEPOINTS select IRQ_DOMAIN select MODULES_USE_ELF_RELA if MODULES diff --git a/arch/riscv/include/asm/stackprotector.h b/arch/riscv/include/asm/stackprotector.h new file mode 100644 index 0000000000000..d95f7b2a7f375 --- /dev/null +++ b/arch/riscv/include/asm/stackprotector.h @@ -0,0 +1,33 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +#ifndef _ASM_RISCV_STACKPROTECTOR_H +#define _ASM_RISCV_STACKPROTECTOR_H + +#include +#include +#include + +extern unsigned long __stack_chk_guard; + +/* + * Initialize the stackprotector canary value. + * + * NOTE: this must only be called from functions that never return, + * and it must always be inlined. + */ +static __always_inline void boot_init_stack_canary(void) +{ + unsigned long canary; + unsigned long tsc; + + /* Try to get a semi random initial value. */ + get_random_bytes(&canary, sizeof(canary)); + tsc = get_cycles(); + canary += tsc + (tsc << BITS_PER_LONG/2); + canary ^= LINUX_VERSION_CODE; + canary &= CANARY_MASK; + + current->stack_canary = canary; + __stack_chk_guard = current->stack_canary; +} +#endif /* _ASM_RISCV_STACKPROTECTOR_H */ diff --git a/arch/riscv/kernel/process.c b/arch/riscv/kernel/process.c index 824d117cf202b..65489297ed2ee 100644 --- a/arch/riscv/kernel/process.c +++ b/arch/riscv/kernel/process.c @@ -24,6 +24,12 @@ register unsigned long gp_in_global __asm__("gp"); +#ifdef CONFIG_STACKPROTECTOR +#include +unsigned long __stack_chk_guard __read_mostly; +EXPORT_SYMBOL(__stack_chk_guard); +#endif + extern asmlinkage void ret_from_fork(void); extern asmlinkage void ret_from_kernel_thread(void); -- GitLab From 8e0c02f27253cde5400f7e57b1369690c6e614a0 Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Mon, 6 Jul 2020 00:02:36 +0200 Subject: [PATCH 1173/1476] Replace HTTP links with HTTPS ones: RISC-V Rationale: Reduces attack surface on kernel devs opening the links for MITM as HTTPS traffic is much harder to manipulate. Deterministic algorithm: For each file: If not .svg: For each line: If doesn't contain `\bxmlns\b`: For each link, `\bhttp://[^# \t\r\n]*(?:\w|/)`: If both the HTTP and HTTPS versions return 200 OK and serve the same content: Replace HTTP with HTTPS. Signed-off-by: Alexander A. Klimov Signed-off-by: Palmer Dabbelt --- arch/riscv/include/uapi/asm/unistd.h | 2 +- tools/arch/riscv/include/uapi/asm/unistd.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/riscv/include/uapi/asm/unistd.h b/arch/riscv/include/uapi/asm/unistd.h index 13ce76cc5affe..4b989ae15d59f 100644 --- a/arch/riscv/include/uapi/asm/unistd.h +++ b/arch/riscv/include/uapi/asm/unistd.h @@ -12,7 +12,7 @@ * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program. If not, see . + * along with this program. If not, see . */ #ifdef __LP64__ diff --git a/tools/arch/riscv/include/uapi/asm/unistd.h b/tools/arch/riscv/include/uapi/asm/unistd.h index 0e2eeeb1fd27b..f506cca520b06 100644 --- a/tools/arch/riscv/include/uapi/asm/unistd.h +++ b/tools/arch/riscv/include/uapi/asm/unistd.h @@ -12,7 +12,7 @@ * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program. If not, see . + * along with this program. If not, see . */ #ifdef __LP64__ -- GitLab From 11a54f422b0d2680be57bbb9f85696ef89d9cda1 Mon Sep 17 00:00:00 2001 From: Emil Renner Berthing Date: Tue, 14 Jul 2020 09:40:43 +0200 Subject: [PATCH 1174/1476] riscv: Support R_RISCV_ADD64 and R_RISCV_SUB64 relocs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit These are needed for the __jump_table in modules using static keys/jump-labels with the layout from HAVE_ARCH_JUMP_LABEL_RELATIVE on 64bit kernels. Signed-off-by: Emil Renner Berthing Reviewed-by: Björn Töpel Tested-by: Björn Töpel Signed-off-by: Palmer Dabbelt --- arch/riscv/kernel/module.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/arch/riscv/kernel/module.c b/arch/riscv/kernel/module.c index 7191342c54da9..104fba889cf76 100644 --- a/arch/riscv/kernel/module.c +++ b/arch/riscv/kernel/module.c @@ -263,6 +263,13 @@ static int apply_r_riscv_add32_rela(struct module *me, u32 *location, return 0; } +static int apply_r_riscv_add64_rela(struct module *me, u32 *location, + Elf_Addr v) +{ + *(u64 *)location += (u64)v; + return 0; +} + static int apply_r_riscv_sub32_rela(struct module *me, u32 *location, Elf_Addr v) { @@ -270,6 +277,13 @@ static int apply_r_riscv_sub32_rela(struct module *me, u32 *location, return 0; } +static int apply_r_riscv_sub64_rela(struct module *me, u32 *location, + Elf_Addr v) +{ + *(u64 *)location -= (u64)v; + return 0; +} + static int (*reloc_handlers_rela[]) (struct module *me, u32 *location, Elf_Addr v) = { [R_RISCV_32] = apply_r_riscv_32_rela, @@ -290,7 +304,9 @@ static int (*reloc_handlers_rela[]) (struct module *me, u32 *location, [R_RISCV_RELAX] = apply_r_riscv_relax_rela, [R_RISCV_ALIGN] = apply_r_riscv_align_rela, [R_RISCV_ADD32] = apply_r_riscv_add32_rela, + [R_RISCV_ADD64] = apply_r_riscv_add64_rela, [R_RISCV_SUB32] = apply_r_riscv_sub32_rela, + [R_RISCV_SUB64] = apply_r_riscv_sub64_rela, }; int apply_relocate_add(Elf_Shdr *sechdrs, const char *strtab, -- GitLab From ebc00dde8a975a543f5e1a7cdac93fef89fefe58 Mon Sep 17 00:00:00 2001 From: Emil Renner Berthing Date: Tue, 14 Jul 2020 09:40:44 +0200 Subject: [PATCH 1175/1476] riscv: Add jump-label implementation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add jump-label implementation based on the ARM64 version and add CONFIG_JUMP_LABEL=y to the defconfigs. Signed-off-by: Emil Renner Berthing Reviewed-by: Björn Töpel Tested-by: Björn Töpel Signed-off-by: Palmer Dabbelt --- .../core/jump-labels/arch-support.txt | 2 +- arch/riscv/Kconfig | 2 + arch/riscv/configs/defconfig | 1 + arch/riscv/configs/nommu_k210_defconfig | 1 + arch/riscv/configs/nommu_virt_defconfig | 1 + arch/riscv/configs/rv32_defconfig | 1 + arch/riscv/include/asm/jump_label.h | 60 +++++++++++++++++++ arch/riscv/kernel/Makefile | 2 + arch/riscv/kernel/jump_label.c | 53 ++++++++++++++++ 9 files changed, 122 insertions(+), 1 deletion(-) create mode 100644 arch/riscv/include/asm/jump_label.h create mode 100644 arch/riscv/kernel/jump_label.c diff --git a/Documentation/features/core/jump-labels/arch-support.txt b/Documentation/features/core/jump-labels/arch-support.txt index 632a1c7aefa22..760243d18ed7d 100644 --- a/Documentation/features/core/jump-labels/arch-support.txt +++ b/Documentation/features/core/jump-labels/arch-support.txt @@ -23,7 +23,7 @@ | openrisc: | TODO | | parisc: | ok | | powerpc: | ok | - | riscv: | TODO | + | riscv: | ok | | s390: | ok | | sh: | TODO | | sparc: | ok | diff --git a/arch/riscv/Kconfig b/arch/riscv/Kconfig index b55be0980b2b6..bf6a9feee8039 100644 --- a/arch/riscv/Kconfig +++ b/arch/riscv/Kconfig @@ -47,6 +47,8 @@ config RISCV select GENERIC_TIME_VSYSCALL if MMU && 64BIT select HANDLE_DOMAIN_IRQ select HAVE_ARCH_AUDITSYSCALL + select HAVE_ARCH_JUMP_LABEL + select HAVE_ARCH_JUMP_LABEL_RELATIVE select HAVE_ARCH_KASAN if MMU && 64BIT select HAVE_ARCH_KGDB select HAVE_ARCH_KGDB_QXFER_PKT diff --git a/arch/riscv/configs/defconfig b/arch/riscv/configs/defconfig index 4da4886246a48..d58c93efb6036 100644 --- a/arch/riscv/configs/defconfig +++ b/arch/riscv/configs/defconfig @@ -17,6 +17,7 @@ CONFIG_BPF_SYSCALL=y CONFIG_SOC_SIFIVE=y CONFIG_SOC_VIRT=y CONFIG_SMP=y +CONFIG_JUMP_LABEL=y CONFIG_MODULES=y CONFIG_MODULE_UNLOAD=y CONFIG_NET=y diff --git a/arch/riscv/configs/nommu_k210_defconfig b/arch/riscv/configs/nommu_k210_defconfig index b48138e329eab..cd1df62b13c72 100644 --- a/arch/riscv/configs/nommu_k210_defconfig +++ b/arch/riscv/configs/nommu_k210_defconfig @@ -33,6 +33,7 @@ CONFIG_SMP=y CONFIG_NR_CPUS=2 CONFIG_CMDLINE="earlycon console=ttySIF0" CONFIG_CMDLINE_FORCE=y +CONFIG_JUMP_LABEL=y # CONFIG_BLOCK is not set CONFIG_BINFMT_FLAT=y # CONFIG_COREDUMP is not set diff --git a/arch/riscv/configs/nommu_virt_defconfig b/arch/riscv/configs/nommu_virt_defconfig index cf74e179bf90e..f27596e9663e3 100644 --- a/arch/riscv/configs/nommu_virt_defconfig +++ b/arch/riscv/configs/nommu_virt_defconfig @@ -30,6 +30,7 @@ CONFIG_MAXPHYSMEM_2GB=y CONFIG_SMP=y CONFIG_CMDLINE="root=/dev/vda rw earlycon=uart8250,mmio,0x10000000,115200n8 console=ttyS0" CONFIG_CMDLINE_FORCE=y +CONFIG_JUMP_LABEL=y # CONFIG_BLK_DEV_BSG is not set CONFIG_PARTITION_ADVANCED=y # CONFIG_MSDOS_PARTITION is not set diff --git a/arch/riscv/configs/rv32_defconfig b/arch/riscv/configs/rv32_defconfig index 05bbf52405690..3a55f0e00d6c0 100644 --- a/arch/riscv/configs/rv32_defconfig +++ b/arch/riscv/configs/rv32_defconfig @@ -17,6 +17,7 @@ CONFIG_BPF_SYSCALL=y CONFIG_SOC_VIRT=y CONFIG_ARCH_RV32I=y CONFIG_SMP=y +CONFIG_JUMP_LABEL=y CONFIG_MODULES=y CONFIG_MODULE_UNLOAD=y CONFIG_NET=y diff --git a/arch/riscv/include/asm/jump_label.h b/arch/riscv/include/asm/jump_label.h new file mode 100644 index 0000000000000..38af2ec7b9bf9 --- /dev/null +++ b/arch/riscv/include/asm/jump_label.h @@ -0,0 +1,60 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (C) 2020 Emil Renner Berthing + * + * Based on arch/arm64/include/asm/jump_label.h + */ +#ifndef __ASM_JUMP_LABEL_H +#define __ASM_JUMP_LABEL_H + +#ifndef __ASSEMBLY__ + +#include +#include + +#define JUMP_LABEL_NOP_SIZE 4 + +static __always_inline bool arch_static_branch(struct static_key *key, + bool branch) +{ + asm_volatile_goto( + " .option push \n\t" + " .option norelax \n\t" + " .option norvc \n\t" + "1: nop \n\t" + " .option pop \n\t" + " .pushsection __jump_table, \"aw\" \n\t" + " .align " RISCV_LGPTR " \n\t" + " .long 1b - ., %l[label] - . \n\t" + " " RISCV_PTR " %0 - . \n\t" + " .popsection \n\t" + : : "i"(&((char *)key)[branch]) : : label); + + return false; +label: + return true; +} + +static __always_inline bool arch_static_branch_jump(struct static_key *key, + bool branch) +{ + asm_volatile_goto( + " .option push \n\t" + " .option norelax \n\t" + " .option norvc \n\t" + "1: jal zero, %l[label] \n\t" + " .option pop \n\t" + " .pushsection __jump_table, \"aw\" \n\t" + " .align " RISCV_LGPTR " \n\t" + " .long 1b - ., %l[label] - . \n\t" + " " RISCV_PTR " %0 - . \n\t" + " .popsection \n\t" + : : "i"(&((char *)key)[branch]) : : label); + + return false; +label: + return true; +} + +#endif /* __ASSEMBLY__ */ +#endif /* __ASM_JUMP_LABEL_H */ diff --git a/arch/riscv/kernel/Makefile b/arch/riscv/kernel/Makefile index b355cf4856718..a5287ab9f7f25 100644 --- a/arch/riscv/kernel/Makefile +++ b/arch/riscv/kernel/Makefile @@ -53,4 +53,6 @@ endif obj-$(CONFIG_HOTPLUG_CPU) += cpu-hotplug.o obj-$(CONFIG_KGDB) += kgdb.o +obj-$(CONFIG_JUMP_LABEL) += jump_label.o + clean: diff --git a/arch/riscv/kernel/jump_label.c b/arch/riscv/kernel/jump_label.c new file mode 100644 index 0000000000000..20e09056d141f --- /dev/null +++ b/arch/riscv/kernel/jump_label.c @@ -0,0 +1,53 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2020 Emil Renner Berthing + * + * Based on arch/arm64/kernel/jump_label.c + */ +#include +#include +#include +#include +#include +#include + +#define RISCV_INSN_NOP 0x00000013U +#define RISCV_INSN_JAL 0x0000006fU + +void arch_jump_label_transform(struct jump_entry *entry, + enum jump_label_type type) +{ + void *addr = (void *)jump_entry_code(entry); + u32 insn; + + if (type == JUMP_LABEL_JMP) { + long offset = jump_entry_target(entry) - jump_entry_code(entry); + + if (WARN_ON(offset & 1 || offset < -524288 || offset >= 524288)) + return; + + insn = RISCV_INSN_JAL | + (((u32)offset & GENMASK(19, 12)) << (12 - 12)) | + (((u32)offset & GENMASK(11, 11)) << (20 - 11)) | + (((u32)offset & GENMASK(10, 1)) << (21 - 1)) | + (((u32)offset & GENMASK(20, 20)) << (31 - 20)); + } else { + insn = RISCV_INSN_NOP; + } + + mutex_lock(&text_mutex); + patch_text_nosync(addr, &insn, sizeof(insn)); + mutex_unlock(&text_mutex); +} + +void arch_jump_label_transform_static(struct jump_entry *entry, + enum jump_label_type type) +{ + /* + * We use the same instructions in the arch_static_branch and + * arch_static_branch_jump inline functions, so there's no + * need to patch them up here. + * The core will call arch_jump_label_transform when those + * instructions need to be replaced. + */ +} -- GitLab From 3e7b669c6c5375131db751e6e748af27bab318f6 Mon Sep 17 00:00:00 2001 From: Guo Ren Date: Sun, 12 Jul 2020 13:41:49 +0000 Subject: [PATCH 1176/1476] riscv: Cleanup unnecessary define in asm-offset.c - TASK_THREAD_SP is duplicated define - TASK_STACK is no use at all - Don't worry about thread_info's offset in task_struct, have a look on comment in include/linux/sched.h: struct task_struct { /* * For reasons of header soup (see current_thread_info()), this * must be the first element of task_struct. */ struct thread_info thread_info; Signed-off-by: Guo Ren Signed-off-by: Palmer Dabbelt --- arch/riscv/kernel/asm-offsets.c | 3 --- arch/riscv/kernel/entry.S | 6 +----- 2 files changed, 1 insertion(+), 8 deletions(-) diff --git a/arch/riscv/kernel/asm-offsets.c b/arch/riscv/kernel/asm-offsets.c index 07cb9c10de4ec..db203442c08f9 100644 --- a/arch/riscv/kernel/asm-offsets.c +++ b/arch/riscv/kernel/asm-offsets.c @@ -27,9 +27,6 @@ void asm_offsets(void) OFFSET(TASK_THREAD_S9, task_struct, thread.s[9]); OFFSET(TASK_THREAD_S10, task_struct, thread.s[10]); OFFSET(TASK_THREAD_S11, task_struct, thread.s[11]); - OFFSET(TASK_THREAD_SP, task_struct, thread.sp); - OFFSET(TASK_STACK, task_struct, stack); - OFFSET(TASK_TI, task_struct, thread_info); OFFSET(TASK_TI_FLAGS, task_struct, thread_info.flags); OFFSET(TASK_TI_PREEMPT_COUNT, task_struct, thread_info.preempt_count); OFFSET(TASK_TI_KERNEL_SP, task_struct, thread_info.kernel_sp); diff --git a/arch/riscv/kernel/entry.S b/arch/riscv/kernel/entry.S index 124506ef4c5df..524d918f3601b 100644 --- a/arch/riscv/kernel/entry.S +++ b/arch/riscv/kernel/entry.S @@ -435,12 +435,8 @@ ENTRY(__switch_to) lw a4, TASK_TI_CPU(a1) sw a3, TASK_TI_CPU(a1) sw a4, TASK_TI_CPU(a0) -#if TASK_TI != 0 -#error "TASK_TI != 0: tp will contain a 'struct thread_info', not a 'struct task_struct' so get_current() won't work." - addi tp, a1, TASK_TI -#else + /* The offset of thread_info in task_struct is zero. */ move tp, a1 -#endif ret ENDPROC(__switch_to) -- GitLab From 63bb76de4aeec833ae7ba05d85a7819ac4558126 Mon Sep 17 00:00:00 2001 From: Pekka Enberg Date: Wed, 15 Jul 2020 08:33:39 +0300 Subject: [PATCH 1177/1476] mm: pgtable: Make generic pgprot_* macros available for no-MMU The header defines some generic pgprot_* implementations, but they are only available when CONFIG_MMU is enabled. The RISC-V architecture, for example, therefore defines some of these pgprot_* macros for !NOMMU. Let's make the pgprot_* generic available even for !NOMMU so we can remove the RISC-V specific definitions. Compile-tested with x86 defconfig, and riscv defconfig and !MMU defconfig. Suggested-by: Palmer Dabbelt Reviewed-by: Mike Rapoport Acked-by: David Rientjes Signed-off-by: Pekka Enberg Signed-off-by: Palmer Dabbelt --- include/linux/pgtable.h | 71 +++++++++++++++++++++-------------------- 1 file changed, 37 insertions(+), 34 deletions(-) diff --git a/include/linux/pgtable.h b/include/linux/pgtable.h index 56c1e8eb7bb0a..53e97da1e8e20 100644 --- a/include/linux/pgtable.h +++ b/include/linux/pgtable.h @@ -647,40 +647,6 @@ static inline int arch_unmap_one(struct mm_struct *mm, #define flush_tlb_fix_spurious_fault(vma, address) flush_tlb_page(vma, address) #endif -#ifndef pgprot_nx -#define pgprot_nx(prot) (prot) -#endif - -#ifndef pgprot_noncached -#define pgprot_noncached(prot) (prot) -#endif - -#ifndef pgprot_writecombine -#define pgprot_writecombine pgprot_noncached -#endif - -#ifndef pgprot_writethrough -#define pgprot_writethrough pgprot_noncached -#endif - -#ifndef pgprot_device -#define pgprot_device pgprot_noncached -#endif - -#ifndef pgprot_modify -#define pgprot_modify pgprot_modify -static inline pgprot_t pgprot_modify(pgprot_t oldprot, pgprot_t newprot) -{ - if (pgprot_val(oldprot) == pgprot_val(pgprot_noncached(oldprot))) - newprot = pgprot_noncached(newprot); - if (pgprot_val(oldprot) == pgprot_val(pgprot_writecombine(oldprot))) - newprot = pgprot_writecombine(newprot); - if (pgprot_val(oldprot) == pgprot_val(pgprot_device(oldprot))) - newprot = pgprot_device(newprot); - return newprot; -} -#endif - /* * When walking page tables, get the address of the next boundary, * or the end address of the range if that comes earlier. Although no @@ -840,6 +806,43 @@ static inline void ptep_modify_prot_commit(struct vm_area_struct *vma, * No-op macros that just return the current protection value. Defined here * because these macros can be used used even if CONFIG_MMU is not defined. */ + +#ifndef pgprot_nx +#define pgprot_nx(prot) (prot) +#endif + +#ifndef pgprot_noncached +#define pgprot_noncached(prot) (prot) +#endif + +#ifndef pgprot_writecombine +#define pgprot_writecombine pgprot_noncached +#endif + +#ifndef pgprot_writethrough +#define pgprot_writethrough pgprot_noncached +#endif + +#ifndef pgprot_device +#define pgprot_device pgprot_noncached +#endif + +#ifdef CONFIG_MMU +#ifndef pgprot_modify +#define pgprot_modify pgprot_modify +static inline pgprot_t pgprot_modify(pgprot_t oldprot, pgprot_t newprot) +{ + if (pgprot_val(oldprot) == pgprot_val(pgprot_noncached(oldprot))) + newprot = pgprot_noncached(newprot); + if (pgprot_val(oldprot) == pgprot_val(pgprot_writecombine(oldprot))) + newprot = pgprot_writecombine(newprot); + if (pgprot_val(oldprot) == pgprot_val(pgprot_device(oldprot))) + newprot = pgprot_device(newprot); + return newprot; +} +#endif +#endif /* CONFIG_MMU */ + #ifndef pgprot_encrypted #define pgprot_encrypted(prot) (prot) #endif -- GitLab From 89b03cc1dff0654fc01958f3bdd5005c9a510e2e Mon Sep 17 00:00:00 2001 From: Pekka Enberg Date: Wed, 15 Jul 2020 08:33:40 +0300 Subject: [PATCH 1178/1476] riscv: Use generic pgprot_* macros from The header now defines generic pgprot_ macros also for the no-MMU configuration, so let's use them. Signed-off-by: Pekka Enberg Signed-off-by: Palmer Dabbelt --- arch/riscv/include/asm/mmio.h | 6 ------ 1 file changed, 6 deletions(-) diff --git a/arch/riscv/include/asm/mmio.h b/arch/riscv/include/asm/mmio.h index 56053c9838b2f..aff6c33ab0c08 100644 --- a/arch/riscv/include/asm/mmio.h +++ b/arch/riscv/include/asm/mmio.h @@ -14,12 +14,6 @@ #include #include -#ifndef CONFIG_MMU -#define pgprot_noncached(x) (x) -#define pgprot_writecombine(x) (x) -#define pgprot_device(x) (x) -#endif /* CONFIG_MMU */ - /* Generic IO read/write. These perform native-endian accesses. */ #define __raw_writeb __raw_writeb static inline void __raw_writeb(u8 val, volatile void __iomem *addr) -- GitLab From 925ac7b6636b6d6a195e93b2179741d3589f4be1 Mon Sep 17 00:00:00 2001 From: Emil Renner Berthing Date: Tue, 14 Jul 2020 23:26:11 +0200 Subject: [PATCH 1179/1476] riscv: Select ARCH_HAS_DEBUG_VM_PGTABLE This allows the pgtable tests to be built. Signed-off-by: Emil Renner Berthing Signed-off-by: Palmer Dabbelt --- Documentation/features/debug/debug-vm-pgtable/arch-support.txt | 2 +- arch/riscv/Kconfig | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/Documentation/features/debug/debug-vm-pgtable/arch-support.txt b/Documentation/features/debug/debug-vm-pgtable/arch-support.txt index c527d05c04595..c9a764c3c7956 100644 --- a/Documentation/features/debug/debug-vm-pgtable/arch-support.txt +++ b/Documentation/features/debug/debug-vm-pgtable/arch-support.txt @@ -23,7 +23,7 @@ | openrisc: | TODO | | parisc: | TODO | | powerpc: | ok | - | riscv: | TODO | + | riscv: | ok | | s390: | ok | | sh: | TODO | | sparc: | TODO | diff --git a/arch/riscv/Kconfig b/arch/riscv/Kconfig index bf6a9feee8039..bc37241a6875d 100644 --- a/arch/riscv/Kconfig +++ b/arch/riscv/Kconfig @@ -15,6 +15,7 @@ config RISCV select ARCH_CLOCKSOURCE_INIT select ARCH_SUPPORTS_ATOMIC_RMW select ARCH_HAS_BINFMT_FLAT + select ARCH_HAS_DEBUG_VM_PGTABLE select ARCH_HAS_DEBUG_VIRTUAL if MMU select ARCH_HAS_DEBUG_WX select ARCH_HAS_GCOV_PROFILE_ALL -- GitLab From 79b1feba54558bfb7df7c516dab29a7c9dba093c Mon Sep 17 00:00:00 2001 From: Atish Patra Date: Wed, 15 Jul 2020 16:30:06 -0700 Subject: [PATCH 1180/1476] RISC-V: Setup exception vector early The trap vector is set only in trap_init which may be too late in some cases. Early ioremap/efi spits many warning messages which may be useful. Setup the trap vector early so that any warning/bug can be handled before generic code invokes trap_init. Signed-off-by: Atish Patra Signed-off-by: Palmer Dabbelt --- arch/riscv/kernel/head.S | 10 ++++++++-- arch/riscv/kernel/smpboot.c | 2 +- arch/riscv/kernel/traps.c | 8 +------- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/arch/riscv/kernel/head.S b/arch/riscv/kernel/head.S index 7ed1b22950fdb..d0c5c316e9bb1 100644 --- a/arch/riscv/kernel/head.S +++ b/arch/riscv/kernel/head.S @@ -77,10 +77,16 @@ relocate: csrw CSR_SATP, a0 .align 2 1: - /* Set trap vector to spin forever to help debug */ - la a0, .Lsecondary_park + /* Set trap vector to exception handler */ + la a0, handle_exception csrw CSR_TVEC, a0 + /* + * Set sup0 scratch register to 0, indicating to exception vector that + * we are presently executing in kernel. + */ + csrw CSR_SCRATCH, zero + /* Reload the global pointer */ .option push .option norelax diff --git a/arch/riscv/kernel/smpboot.c b/arch/riscv/kernel/smpboot.c index 403bd93412a4e..47404d4ad7219 100644 --- a/arch/riscv/kernel/smpboot.c +++ b/arch/riscv/kernel/smpboot.c @@ -154,10 +154,10 @@ asmlinkage __visible void smp_callin(void) mmgrab(mm); current->active_mm = mm; - trap_init(); notify_cpu_starting(curr_cpuid); update_siblings_masks(curr_cpuid); set_cpu_online(curr_cpuid, 1); + /* * Remote TLB flushes are ignored while the CPU is offline, so emit * a local TLB flush right now just in case. diff --git a/arch/riscv/kernel/traps.c b/arch/riscv/kernel/traps.c index 7d95cce5e47c4..ad14f4466d924 100644 --- a/arch/riscv/kernel/traps.c +++ b/arch/riscv/kernel/traps.c @@ -174,13 +174,7 @@ int is_valid_bugaddr(unsigned long pc) } #endif /* CONFIG_GENERIC_BUG */ +/* stvec & scratch is already set from head.S */ void trap_init(void) { - /* - * Set sup0 scratch register to 0, indicating to exception vector - * that we are presently executing in the kernel - */ - csr_write(CSR_SCRATCH, 0); - /* Set the exception vector address */ - csr_write(CSR_TVEC, &handle_exception); } -- GitLab From e3ef4d69456e3977117bd11edf0161a9200c1a78 Mon Sep 17 00:00:00 2001 From: Zong Li Date: Thu, 16 Jul 2020 14:15:26 +0800 Subject: [PATCH 1181/1476] riscv: Fix build warning for mm/init Add static keyword for resource_init, this function is only used in this object file. Signed-off-by: Zong Li Signed-off-by: Palmer Dabbelt --- arch/riscv/mm/init.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/riscv/mm/init.c b/arch/riscv/mm/init.c index 92002952c621d..66f5952f39c05 100644 --- a/arch/riscv/mm/init.c +++ b/arch/riscv/mm/init.c @@ -517,7 +517,7 @@ void mark_rodata_ro(void) } #endif -void __init resource_init(void) +static void __init resource_init(void) { struct memblock_region *region; -- GitLab From 3843aca0521d039efce0240f54b37bea23ba9f16 Mon Sep 17 00:00:00 2001 From: Zong Li Date: Thu, 16 Jul 2020 14:15:27 +0800 Subject: [PATCH 1182/1476] riscv: fix build warning of mm/pageattr Add hearder for missing prototype. Also, static keyword should be at beginning of declaration. Signed-off-by: Zong Li Reviewed-by: Pekka Enberg Signed-off-by: Palmer Dabbelt --- arch/riscv/mm/pageattr.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/arch/riscv/mm/pageattr.c b/arch/riscv/mm/pageattr.c index 289a9a5ea5b51..19fecb362d815 100644 --- a/arch/riscv/mm/pageattr.c +++ b/arch/riscv/mm/pageattr.c @@ -7,6 +7,7 @@ #include #include #include +#include struct pageattr_masks { pgprot_t set_mask; @@ -94,7 +95,7 @@ static int pageattr_pte_hole(unsigned long addr, unsigned long next, return 0; } -const static struct mm_walk_ops pageattr_ops = { +static const struct mm_walk_ops pageattr_ops = { .pgd_entry = pageattr_pgd_entry, .p4d_entry = pageattr_p4d_entry, .pud_entry = pageattr_pud_entry, -- GitLab From a9d0ba6772a0056457838022b624c3d2a17cee21 Mon Sep 17 00:00:00 2001 From: Kevin Hao Date: Thu, 30 Jul 2020 16:23:17 +0800 Subject: [PATCH 1183/1476] tracing/hwlat: Drop the duplicate assignment in start_kthread() We have set 'current_mask' to '&save_cpumask' in its declaration, so there is no need to assign again. Link: https://lkml.kernel.org/r/20200730082318.42584-1-haokexin@gmail.com Signed-off-by: Kevin Hao Signed-off-by: Steven Rostedt (VMware) --- kernel/trace/trace_hwlat.c | 1 - 1 file changed, 1 deletion(-) diff --git a/kernel/trace/trace_hwlat.c b/kernel/trace/trace_hwlat.c index e2be7bb7ef7e2..ddb528a6cd515 100644 --- a/kernel/trace/trace_hwlat.c +++ b/kernel/trace/trace_hwlat.c @@ -371,7 +371,6 @@ static int start_kthread(struct trace_array *tr) return 0; /* Just pick the first CPU on first iteration */ - current_mask = &save_cpumask; get_online_cpus(); cpumask_and(current_mask, cpu_online_mask, tracing_buffer_mask); put_online_cpus(); -- GitLab From 96b4833b6827a62c295b149213c68b559514c929 Mon Sep 17 00:00:00 2001 From: Kevin Hao Date: Thu, 30 Jul 2020 16:23:18 +0800 Subject: [PATCH 1184/1476] tracing/hwlat: Honor the tracing_cpumask In calculation of the cpu mask for the hwlat kernel thread, the wrong cpu mask is used instead of the tracing_cpumask, this causes the tracing/tracing_cpumask useless for hwlat tracer. Fixes it. Link: https://lkml.kernel.org/r/20200730082318.42584-2-haokexin@gmail.com Cc: Ingo Molnar Cc: stable@vger.kernel.org Fixes: 0330f7aa8ee6 ("tracing: Have hwlat trace migrate across tracing_cpumask CPUs") Signed-off-by: Kevin Hao Signed-off-by: Steven Rostedt (VMware) --- kernel/trace/trace_hwlat.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/kernel/trace/trace_hwlat.c b/kernel/trace/trace_hwlat.c index ddb528a6cd515..17873e5d03531 100644 --- a/kernel/trace/trace_hwlat.c +++ b/kernel/trace/trace_hwlat.c @@ -283,6 +283,7 @@ static bool disable_migrate; static void move_to_next_cpu(void) { struct cpumask *current_mask = &save_cpumask; + struct trace_array *tr = hwlat_trace; int next_cpu; if (disable_migrate) @@ -296,7 +297,7 @@ static void move_to_next_cpu(void) goto disable; get_online_cpus(); - cpumask_and(current_mask, cpu_online_mask, tracing_buffer_mask); + cpumask_and(current_mask, cpu_online_mask, tr->tracing_cpumask); next_cpu = cpumask_next(smp_processor_id(), current_mask); put_online_cpus(); @@ -372,7 +373,7 @@ static int start_kthread(struct trace_array *tr) /* Just pick the first CPU on first iteration */ get_online_cpus(); - cpumask_and(current_mask, cpu_online_mask, tracing_buffer_mask); + cpumask_and(current_mask, cpu_online_mask, tr->tracing_cpumask); put_online_cpus(); next_cpu = cpumask_first(current_mask); -- GitLab From 8a224ffb3f52b0027f6b7279854c71a31c48fc97 Mon Sep 17 00:00:00 2001 From: Chengming Zhou Date: Wed, 29 Jul 2020 02:05:53 +0800 Subject: [PATCH 1185/1476] ftrace: Setup correct FTRACE_FL_REGS flags for module When module loaded and enabled, we will use __ftrace_replace_code for module if any ftrace_ops referenced it found. But we will get wrong ftrace_addr for module rec in ftrace_get_addr_new, because rec->flags has not been setup correctly. It can cause the callback function of a ftrace_ops has FTRACE_OPS_FL_SAVE_REGS to be called with pt_regs set to NULL. So setup correct FTRACE_FL_REGS flags for rec when we call referenced_filters to find ftrace_ops references it. Link: https://lkml.kernel.org/r/20200728180554.65203-1-zhouchengming@bytedance.com Cc: stable@vger.kernel.org Fixes: 8c4f3c3fa9681 ("ftrace: Check module functions being traced on reload") Signed-off-by: Chengming Zhou Signed-off-by: Muchun Song Signed-off-by: Steven Rostedt (VMware) --- kernel/trace/ftrace.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/kernel/trace/ftrace.c b/kernel/trace/ftrace.c index c141d347f71a8..d052f856f1cf3 100644 --- a/kernel/trace/ftrace.c +++ b/kernel/trace/ftrace.c @@ -6198,8 +6198,11 @@ static int referenced_filters(struct dyn_ftrace *rec) int cnt = 0; for (ops = ftrace_ops_list; ops != &ftrace_list_end; ops = ops->next) { - if (ops_references_rec(ops, rec)) - cnt++; + if (ops_references_rec(ops, rec)) { + cnt++; + if (ops->flags & FTRACE_OPS_FL_SAVE_REGS) + rec->flags |= FTRACE_FL_REGS; + } } return cnt; @@ -6378,8 +6381,8 @@ void ftrace_module_enable(struct module *mod) if (ftrace_start_up) cnt += referenced_filters(rec); - /* This clears FTRACE_FL_DISABLED */ - rec->flags = cnt; + rec->flags &= ~FTRACE_FL_DISABLED; + rec->flags += cnt; if (ftrace_start_up && cnt) { int failed = __ftrace_replace_code(rec, 1); -- GitLab From c5f51572a7fdff5be9e67c231de593155f394ab3 Mon Sep 17 00:00:00 2001 From: Chengming Zhou Date: Wed, 29 Jul 2020 02:05:54 +0800 Subject: [PATCH 1186/1476] ftrace: Do not let direct or IPMODIFY ftrace_ops be added to module and set trampolines When inserting a module, we find all ftrace_ops referencing it on the ftrace_ops_list. But FTRACE_OPS_FL_DIRECT and FTRACE_OPS_FL_IPMODIFY flags are special, and should not be set automatically. So warn and skip ftrace_ops that have these two flags set and adding new code. Also check if only one ftrace_ops references the module, in which case we can use a trampoline as an optimization. Link: https://lkml.kernel.org/r/20200728180554.65203-2-zhouchengming@bytedance.com Signed-off-by: Chengming Zhou Signed-off-by: Muchun Song Signed-off-by: Steven Rostedt (VMware) --- kernel/trace/ftrace.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/kernel/trace/ftrace.c b/kernel/trace/ftrace.c index d052f856f1cf3..f433cb44300a0 100644 --- a/kernel/trace/ftrace.c +++ b/kernel/trace/ftrace.c @@ -6199,9 +6199,17 @@ static int referenced_filters(struct dyn_ftrace *rec) for (ops = ftrace_ops_list; ops != &ftrace_list_end; ops = ops->next) { if (ops_references_rec(ops, rec)) { + if (WARN_ON_ONCE(ops->flags & FTRACE_OPS_FL_DIRECT)) + continue; + if (WARN_ON_ONCE(ops->flags & FTRACE_OPS_FL_IPMODIFY)) + continue; cnt++; if (ops->flags & FTRACE_OPS_FL_SAVE_REGS) rec->flags |= FTRACE_FL_REGS; + if (cnt == 1 && ops->trampoline) + rec->flags |= FTRACE_FL_TRAMP; + else + rec->flags &= ~FTRACE_FL_TRAMP; } } -- GitLab From ee896ee8051a618ea4fe3d91c58d3e6ef9faf705 Mon Sep 17 00:00:00 2001 From: Vincent Whitchurch Date: Mon, 27 Jul 2020 11:28:40 +0200 Subject: [PATCH 1187/1476] tracing: Remove outdated comment in stack handling This comment describes the behaviour before commit 2a820bf74918 ("tracing: Use percpu stack trace buffer more intelligently"). Since that commit, interrupts and NMIs do use the per-cpu stacks so the comment is no longer correct. Remove it. (Note that the FTRACE_STACK_SIZE mentioned in the comment has never existed, it probably should have said FTRACE_STACK_ENTRIES.) Link: https://lkml.kernel.org/r/20200727092840.18659-1-vincent.whitchurch@axis.com Signed-off-by: Vincent Whitchurch Signed-off-by: Steven Rostedt (VMware) --- kernel/trace/trace.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c index 4aab712f95676..dbcacdd56b020 100644 --- a/kernel/trace/trace.c +++ b/kernel/trace/trace.c @@ -2930,12 +2930,6 @@ static void __ftrace_trace_stack(struct trace_buffer *buffer, skip++; #endif - /* - * Since events can happen in NMIs there's no safe way to - * use the per cpu ftrace_stacks. We reserve it and if an interrupt - * or NMI comes in, it will just have to use the default - * FTRACE_STACK_SIZE. - */ preempt_disable_notrace(); stackidx = __this_cpu_inc_return(ftrace_stack_reserve) - 1; -- GitLab From bf6419e4d5440c6d414a320506c5488857a5b001 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Tue, 14 Jul 2020 08:56:19 +0200 Subject: [PATCH 1188/1476] initramfs: switch initramfs unpacking to struct file based APIs There is no good reason to mess with file descriptors from in-kernel code, switch the initramfs unpacking to struct file based write instead. Signed-off-by: Christoph Hellwig Acked-by: Linus Torvalds --- init/initramfs.c | 47 ++++++++++++++++++++++++++--------------------- 1 file changed, 26 insertions(+), 21 deletions(-) diff --git a/init/initramfs.c b/init/initramfs.c index 1bc977ebfe316..35c26668d463f 100644 --- a/init/initramfs.c +++ b/init/initramfs.c @@ -13,13 +13,14 @@ #include #include -static ssize_t __init xwrite(int fd, const char *p, size_t count) +static ssize_t __init xwrite(struct file *file, const char *p, size_t count, + loff_t *pos) { ssize_t out = 0; /* sys_write only can write MAX_RW_COUNT aka 2G-4K bytes at most */ while (count) { - ssize_t rv = ksys_write(fd, p, count); + ssize_t rv = kernel_write(file, p, count, pos); if (rv < 0) { if (rv == -EINTR || rv == -EAGAIN) @@ -317,7 +318,8 @@ static int __init maybe_link(void) return 0; } -static __initdata int wfd; +static __initdata struct file *wfile; +static __initdata loff_t wfile_pos; static int __init do_name(void) { @@ -334,16 +336,17 @@ static int __init do_name(void) int openflags = O_WRONLY|O_CREAT; if (ml != 1) openflags |= O_TRUNC; - wfd = ksys_open(collected, openflags, mode); - - if (wfd >= 0) { - ksys_fchown(wfd, uid, gid); - ksys_fchmod(wfd, mode); - if (body_len) - ksys_ftruncate(wfd, body_len); - vcollected = kstrdup(collected, GFP_KERNEL); - state = CopyFile; - } + wfile = filp_open(collected, openflags, mode); + if (IS_ERR(wfile)) + return 0; + wfile_pos = 0; + + vfs_fchown(wfile, uid, gid); + vfs_fchmod(wfile, mode); + if (body_len) + vfs_truncate(&wfile->f_path, body_len); + vcollected = kstrdup(collected, GFP_KERNEL); + state = CopyFile; } } else if (S_ISDIR(mode)) { ksys_mkdir(collected, mode); @@ -365,16 +368,16 @@ static int __init do_name(void) static int __init do_copy(void) { if (byte_count >= body_len) { - if (xwrite(wfd, victim, body_len) != body_len) + if (xwrite(wfile, victim, body_len, &wfile_pos) != body_len) error("write error"); - ksys_close(wfd); + fput(wfile); do_utime(vcollected, mtime); kfree(vcollected); eat(body_len); state = SkipIt; return 0; } else { - if (xwrite(wfd, victim, byte_count) != byte_count) + if (xwrite(wfile, victim, byte_count, &wfile_pos) != byte_count) error("write error"); body_len -= byte_count; eat(byte_count); @@ -576,21 +579,23 @@ static inline bool kexec_free_initrd(void) static void __init populate_initrd_image(char *err) { ssize_t written; - int fd; + struct file *file; + loff_t pos = 0; unpack_to_rootfs(__initramfs_start, __initramfs_size); printk(KERN_INFO "rootfs image is not initramfs (%s); looks like an initrd\n", err); - fd = ksys_open("/initrd.image", O_WRONLY | O_CREAT, 0700); - if (fd < 0) + file = filp_open("/initrd.image", O_WRONLY | O_CREAT, 0700); + if (IS_ERR(file)) return; - written = xwrite(fd, (char *)initrd_start, initrd_end - initrd_start); + written = xwrite(file, (char *)initrd_start, initrd_end - initrd_start, + &pos); if (written != initrd_end - initrd_start) pr_err("/initrd.image: incomplete write (%zd != %ld)\n", written, initrd_end - initrd_start); - ksys_close(fd); + fput(file); } #endif /* CONFIG_BLK_DEV_RAM */ -- GitLab From 8f740636d9049a3f9665a0ec87a00822ec1120b4 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Sun, 7 Jun 2020 17:44:28 +0200 Subject: [PATCH 1189/1476] init: open code setting up stdin/stdout/stderr Don't rely on the implicit set_fs(KERNEL_DS) for ksys_open to work, but instead open a struct file for /dev/console and then install it as FD 0/1/2 manually. Signed-off-by: Christoph Hellwig Acked-by: Linus Torvalds --- init/main.c | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/init/main.c b/init/main.c index 0ead83e86b5aa..db0621dfbb046 100644 --- a/init/main.c +++ b/init/main.c @@ -1457,15 +1457,19 @@ static int __ref kernel_init(void *unused) "See Linux Documentation/admin-guide/init.rst for guidance."); } +/* Open /dev/console, for stdin/stdout/stderr, this should never fail */ void console_on_rootfs(void) { - /* Open the /dev/console as stdin, this should never fail */ - if (ksys_open((const char __user *) "/dev/console", O_RDWR, 0) < 0) - pr_err("Warning: unable to open an initial console.\n"); + struct file *file = filp_open("/dev/console", O_RDWR, 0); - /* create stdout/stderr */ - (void) ksys_dup(0); - (void) ksys_dup(0); + if (IS_ERR(file)) { + pr_err("Warning: unable to open an initial console.\n"); + return; + } + get_file_rcu_many(file, 2); + fd_install(get_unused_fd_flags(0), file); + fd_install(get_unused_fd_flags(0), file); + fd_install(get_unused_fd_flags(0), file); } static noinline void __init kernel_init_freeable(void) -- GitLab From fb2da16cd70a5140acdd7a102e5cd3b697c3404f Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Tue, 14 Jul 2020 09:02:07 +0200 Subject: [PATCH 1190/1476] fs: remove ksys_getdents64 Just open code it in the only caller. Signed-off-by: Christoph Hellwig Acked-by: Linus Torvalds --- fs/readdir.c | 11 ++--------- include/linux/syscalls.h | 2 -- 2 files changed, 2 insertions(+), 11 deletions(-) diff --git a/fs/readdir.c b/fs/readdir.c index a49f07c11cfbd..19434b3c982cd 100644 --- a/fs/readdir.c +++ b/fs/readdir.c @@ -348,8 +348,8 @@ efault: return -EFAULT; } -int ksys_getdents64(unsigned int fd, struct linux_dirent64 __user *dirent, - unsigned int count) +SYSCALL_DEFINE3(getdents64, unsigned int, fd, + struct linux_dirent64 __user *, dirent, unsigned int, count) { struct fd f; struct getdents_callback64 buf = { @@ -380,13 +380,6 @@ int ksys_getdents64(unsigned int fd, struct linux_dirent64 __user *dirent, return error; } - -SYSCALL_DEFINE3(getdents64, unsigned int, fd, - struct linux_dirent64 __user *, dirent, unsigned int, count) -{ - return ksys_getdents64(fd, dirent, count); -} - #ifdef CONFIG_COMPAT struct compat_old_linux_dirent { compat_ulong_t d_ino; diff --git a/include/linux/syscalls.h b/include/linux/syscalls.h index 10843a6adb770..a998651629c71 100644 --- a/include/linux/syscalls.h +++ b/include/linux/syscalls.h @@ -1243,8 +1243,6 @@ ssize_t ksys_write(unsigned int fd, const char __user *buf, size_t count); int ksys_chdir(const char __user *filename); int ksys_fchmod(unsigned int fd, umode_t mode); int ksys_fchown(unsigned int fd, uid_t user, gid_t group); -int ksys_getdents64(unsigned int fd, struct linux_dirent64 __user *dirent, - unsigned int count); int ksys_ioctl(unsigned int fd, unsigned int cmd, unsigned long arg); ssize_t ksys_read(unsigned int fd, char __user *buf, size_t count); void ksys_sync(void); -- GitLab From 166e07c37c6417c9713666268fc0eb89a9ce48b9 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Sat, 6 Jun 2020 15:03:21 +0200 Subject: [PATCH 1191/1476] fs: remove ksys_open Just open code it in the two callers. Signed-off-by: Christoph Hellwig Acked-by: Linus Torvalds --- fs/open.c | 11 ++++++++--- include/linux/syscalls.h | 11 ----------- 2 files changed, 8 insertions(+), 14 deletions(-) diff --git a/fs/open.c b/fs/open.c index 75166f071d280..ab3671af8a970 100644 --- a/fs/open.c +++ b/fs/open.c @@ -1208,7 +1208,9 @@ long do_sys_open(int dfd, const char __user *filename, int flags, umode_t mode) SYSCALL_DEFINE3(open, const char __user *, filename, int, flags, umode_t, mode) { - return ksys_open(filename, flags, mode); + if (force_o_largefile()) + flags |= O_LARGEFILE; + return do_sys_open(AT_FDCWD, filename, flags, mode); } SYSCALL_DEFINE4(openat, int, dfd, const char __user *, filename, int, flags, @@ -1270,9 +1272,12 @@ COMPAT_SYSCALL_DEFINE4(openat, int, dfd, const char __user *, filename, int, fla */ SYSCALL_DEFINE2(creat, const char __user *, pathname, umode_t, mode) { - return ksys_open(pathname, O_CREAT | O_WRONLY | O_TRUNC, mode); -} + int flags = O_CREAT | O_WRONLY | O_TRUNC; + if (force_o_largefile()) + flags |= O_LARGEFILE; + return do_sys_open(AT_FDCWD, pathname, flags, mode); +} #endif /* diff --git a/include/linux/syscalls.h b/include/linux/syscalls.h index a998651629c71..363baaadf8e19 100644 --- a/include/linux/syscalls.h +++ b/include/linux/syscalls.h @@ -1374,17 +1374,6 @@ static inline int ksys_close(unsigned int fd) return __close_fd(current->files, fd); } -extern long do_sys_open(int dfd, const char __user *filename, int flags, - umode_t mode); - -static inline long ksys_open(const char __user *filename, int flags, - umode_t mode) -{ - if (force_o_largefile()) - flags |= O_LARGEFILE; - return do_sys_open(AT_FDCWD, filename, flags, mode); -} - extern long do_sys_truncate(const char __user *pathname, loff_t length); static inline long ksys_truncate(const char __user *pathname, loff_t length) -- GitLab From bc1cd99a9ad7e2c768e7ea92ae9c6ad4a4e0f7f7 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Tue, 14 Jul 2020 08:58:49 +0200 Subject: [PATCH 1192/1476] fs: remove ksys_dup Fold it into the only remaining caller. Signed-off-by: Christoph Hellwig Acked-by: Linus Torvalds --- fs/file.c | 7 +------ include/linux/syscalls.h | 1 - 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/fs/file.c b/fs/file.c index abb8b7081d7a4..85b7993165dd2 100644 --- a/fs/file.c +++ b/fs/file.c @@ -985,7 +985,7 @@ SYSCALL_DEFINE2(dup2, unsigned int, oldfd, unsigned int, newfd) return ksys_dup3(oldfd, newfd, 0); } -int ksys_dup(unsigned int fildes) +SYSCALL_DEFINE1(dup, unsigned int, fildes) { int ret = -EBADF; struct file *file = fget_raw(fildes); @@ -1000,11 +1000,6 @@ int ksys_dup(unsigned int fildes) return ret; } -SYSCALL_DEFINE1(dup, unsigned int, fildes) -{ - return ksys_dup(fildes); -} - int f_dupfd(unsigned int from, struct file *file, unsigned flags) { int err; diff --git a/include/linux/syscalls.h b/include/linux/syscalls.h index 363baaadf8e19..b6d9005747626 100644 --- a/include/linux/syscalls.h +++ b/include/linux/syscalls.h @@ -1237,7 +1237,6 @@ asmlinkage long sys_ni_syscall(void); */ int ksys_umount(char __user *name, int flags); -int ksys_dup(unsigned int fildes); int ksys_chroot(const char __user *filename); ssize_t ksys_write(unsigned int fd, const char __user *buf, size_t count); int ksys_chdir(const char __user *filename); -- GitLab From b25ba7c3c9acdc4cf69f5bd69989819cabfc4e3b Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Tue, 14 Jul 2020 08:59:57 +0200 Subject: [PATCH 1193/1476] fs: remove ksys_fchmod Fold it into the only remaining caller. Signed-off-by: Christoph Hellwig Acked-by: Linus Torvalds --- fs/open.c | 7 +------ include/linux/syscalls.h | 1 - 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/fs/open.c b/fs/open.c index ab3671af8a970..b316dd6a86a8b 100644 --- a/fs/open.c +++ b/fs/open.c @@ -608,7 +608,7 @@ int vfs_fchmod(struct file *file, umode_t mode) return chmod_common(&file->f_path, mode); } -int ksys_fchmod(unsigned int fd, umode_t mode) +SYSCALL_DEFINE2(fchmod, unsigned int, fd, umode_t, mode) { struct fd f = fdget(fd); int err = -EBADF; @@ -620,11 +620,6 @@ int ksys_fchmod(unsigned int fd, umode_t mode) return err; } -SYSCALL_DEFINE2(fchmod, unsigned int, fd, umode_t, mode) -{ - return ksys_fchmod(fd, mode); -} - int do_fchmodat(int dfd, const char __user *filename, umode_t mode) { struct path path; diff --git a/include/linux/syscalls.h b/include/linux/syscalls.h index b6d9005747626..39ff738997a17 100644 --- a/include/linux/syscalls.h +++ b/include/linux/syscalls.h @@ -1240,7 +1240,6 @@ int ksys_umount(char __user *name, int flags); int ksys_chroot(const char __user *filename); ssize_t ksys_write(unsigned int fd, const char __user *buf, size_t count); int ksys_chdir(const char __user *filename); -int ksys_fchmod(unsigned int fd, umode_t mode); int ksys_fchown(unsigned int fd, uid_t user, gid_t group); int ksys_ioctl(unsigned int fd, unsigned int cmd, unsigned long arg); ssize_t ksys_read(unsigned int fd, char __user *buf, size_t count); -- GitLab From 863b67e15177a7cd0c27b3e36e42fe7907dec9bd Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Tue, 14 Jul 2020 09:00:51 +0200 Subject: [PATCH 1194/1476] fs: remove ksys_ioctl Fold it into the only remaining caller. Signed-off-by: Christoph Hellwig Acked-by: Linus Torvalds --- fs/ioctl.c | 7 +------ include/linux/syscalls.h | 1 - 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/fs/ioctl.c b/fs/ioctl.c index d69786d1dd911..4e6cc0a7d69c9 100644 --- a/fs/ioctl.c +++ b/fs/ioctl.c @@ -736,7 +736,7 @@ static int do_vfs_ioctl(struct file *filp, unsigned int fd, return -ENOIOCTLCMD; } -int ksys_ioctl(unsigned int fd, unsigned int cmd, unsigned long arg) +SYSCALL_DEFINE3(ioctl, unsigned int, fd, unsigned int, cmd, unsigned long, arg) { struct fd f = fdget(fd); int error; @@ -757,11 +757,6 @@ out: return error; } -SYSCALL_DEFINE3(ioctl, unsigned int, fd, unsigned int, cmd, unsigned long, arg) -{ - return ksys_ioctl(fd, cmd, arg); -} - #ifdef CONFIG_COMPAT /** * compat_ptr_ioctl - generic implementation of .compat_ioctl file operation diff --git a/include/linux/syscalls.h b/include/linux/syscalls.h index 39ff738997a17..5b0f1fca4cfb9 100644 --- a/include/linux/syscalls.h +++ b/include/linux/syscalls.h @@ -1241,7 +1241,6 @@ int ksys_chroot(const char __user *filename); ssize_t ksys_write(unsigned int fd, const char __user *buf, size_t count); int ksys_chdir(const char __user *filename); int ksys_fchown(unsigned int fd, uid_t user, gid_t group); -int ksys_ioctl(unsigned int fd, unsigned int cmd, unsigned long arg); ssize_t ksys_read(unsigned int fd, char __user *buf, size_t count); void ksys_sync(void); int ksys_unshare(unsigned long unshare_flags); -- GitLab From 9d4b74aee80436d067d79a6c32ebb544b0ae5460 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Sun, 7 Jun 2020 18:26:37 +0200 Subject: [PATCH 1195/1476] fs: refactor do_utimes Split out one helper each for path vs fd based operations. Signed-off-by: Christoph Hellwig Acked-by: Linus Torvalds --- fs/utimes.c | 100 ++++++++++++++++++++++++++++------------------------ 1 file changed, 54 insertions(+), 46 deletions(-) diff --git a/fs/utimes.c b/fs/utimes.c index b7b927502d6e4..c667517b6eb11 100644 --- a/fs/utimes.c +++ b/fs/utimes.c @@ -70,6 +70,57 @@ out: return error; } +static int do_utimes_path(int dfd, const char __user *filename, + struct timespec64 *times, int flags) +{ + struct path path; + int lookup_flags = 0, error; + + if (times && + (!nsec_valid(times[0].tv_nsec) || !nsec_valid(times[1].tv_nsec))) + return -EINVAL; + if (flags & ~(AT_SYMLINK_NOFOLLOW | AT_EMPTY_PATH)) + return -EINVAL; + + if (!(flags & AT_SYMLINK_NOFOLLOW)) + lookup_flags |= LOOKUP_FOLLOW; + if (flags & AT_EMPTY_PATH) + lookup_flags |= LOOKUP_EMPTY; + +retry: + error = user_path_at(dfd, filename, lookup_flags, &path); + if (error) + return error; + + error = utimes_common(&path, times); + path_put(&path); + if (retry_estale(error, lookup_flags)) { + lookup_flags |= LOOKUP_REVAL; + goto retry; + } + + return error; +} + +static int do_utimes_fd(int fd, struct timespec64 *times, int flags) +{ + struct fd f; + int error; + + if (times && + (!nsec_valid(times[0].tv_nsec) || !nsec_valid(times[1].tv_nsec))) + return -EINVAL; + if (flags) + return -EINVAL; + + f = fdget(fd); + if (!f.file) + return -EBADF; + error = utimes_common(&f.file->f_path, times); + fdput(f); + return error; +} + /* * do_utimes - change times on filename or file descriptor * @dfd: open file descriptor, -1 or AT_FDCWD @@ -88,52 +139,9 @@ out: long do_utimes(int dfd, const char __user *filename, struct timespec64 *times, int flags) { - int error = -EINVAL; - - if (times && (!nsec_valid(times[0].tv_nsec) || - !nsec_valid(times[1].tv_nsec))) { - goto out; - } - - if (flags & ~(AT_SYMLINK_NOFOLLOW | AT_EMPTY_PATH)) - goto out; - - if (filename == NULL && dfd != AT_FDCWD) { - struct fd f; - - if (flags) - goto out; - - f = fdget(dfd); - error = -EBADF; - if (!f.file) - goto out; - - error = utimes_common(&f.file->f_path, times); - fdput(f); - } else { - struct path path; - int lookup_flags = 0; - - if (!(flags & AT_SYMLINK_NOFOLLOW)) - lookup_flags |= LOOKUP_FOLLOW; - if (flags & AT_EMPTY_PATH) - lookup_flags |= LOOKUP_EMPTY; -retry: - error = user_path_at(dfd, filename, lookup_flags, &path); - if (error) - goto out; - - error = utimes_common(&path, times); - path_put(&path); - if (retry_estale(error, lookup_flags)) { - lookup_flags |= LOOKUP_REVAL; - goto retry; - } - } - -out: - return error; + if (filename == NULL && dfd != AT_FDCWD) + return do_utimes_fd(dfd, times, flags); + return do_utimes_path(dfd, filename, times, flags); } SYSCALL_DEFINE4(utimensat, int, dfd, const char __user *, filename, -- GitLab From 27eb11c9632c66754ea7f67512b3516f998ed213 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Wed, 15 Jul 2020 08:15:36 +0200 Subject: [PATCH 1196/1476] fs: move timespec validation into utimes_common Consolidate the validation of the timespec from the two callers into utimes_common. That means it is done a little later (e.g. after the path lookup), but I can't find anything that requires a specific order of processing the errors. Signed-off-by: Christoph Hellwig Acked-by: Linus Torvalds --- fs/utimes.c | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/fs/utimes.c b/fs/utimes.c index c667517b6eb11..bfd86e81c5901 100644 --- a/fs/utimes.c +++ b/fs/utimes.c @@ -23,14 +23,19 @@ static int utimes_common(const struct path *path, struct timespec64 *times) struct inode *inode = path->dentry->d_inode; struct inode *delegated_inode = NULL; + if (times) { + if (!nsec_valid(times[0].tv_nsec) || + !nsec_valid(times[1].tv_nsec)) + return -EINVAL; + if (times[0].tv_nsec == UTIME_NOW && + times[1].tv_nsec == UTIME_NOW) + times = NULL; + } + error = mnt_want_write(path->mnt); if (error) goto out; - if (times && times[0].tv_nsec == UTIME_NOW && - times[1].tv_nsec == UTIME_NOW) - times = NULL; - newattrs.ia_valid = ATTR_CTIME | ATTR_MTIME | ATTR_ATIME; if (times) { if (times[0].tv_nsec == UTIME_OMIT) @@ -76,9 +81,6 @@ static int do_utimes_path(int dfd, const char __user *filename, struct path path; int lookup_flags = 0, error; - if (times && - (!nsec_valid(times[0].tv_nsec) || !nsec_valid(times[1].tv_nsec))) - return -EINVAL; if (flags & ~(AT_SYMLINK_NOFOLLOW | AT_EMPTY_PATH)) return -EINVAL; @@ -107,9 +109,6 @@ static int do_utimes_fd(int fd, struct timespec64 *times, int flags) struct fd f; int error; - if (times && - (!nsec_valid(times[0].tv_nsec) || !nsec_valid(times[1].tv_nsec))) - return -EINVAL; if (flags) return -EINVAL; -- GitLab From fd5ad30c782351ab4d4a15941fc61e743a1bd66c Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Wed, 15 Jul 2020 08:19:55 +0200 Subject: [PATCH 1197/1476] fs: expose utimes_common Rename utimes_common to vfs_utimes and make it available outside of utimes.c. This will be used by the initramfs unpacking code. Signed-off-by: Christoph Hellwig Acked-by: Linus Torvalds --- fs/utimes.c | 6 +++--- include/linux/fs.h | 1 + 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/fs/utimes.c b/fs/utimes.c index bfd86e81c5901..fd3cc42262241 100644 --- a/fs/utimes.c +++ b/fs/utimes.c @@ -16,7 +16,7 @@ static bool nsec_valid(long nsec) return nsec >= 0 && nsec <= 999999999; } -static int utimes_common(const struct path *path, struct timespec64 *times) +int vfs_utimes(const struct path *path, struct timespec64 *times) { int error; struct iattr newattrs; @@ -94,7 +94,7 @@ retry: if (error) return error; - error = utimes_common(&path, times); + error = vfs_utimes(&path, times); path_put(&path); if (retry_estale(error, lookup_flags)) { lookup_flags |= LOOKUP_REVAL; @@ -115,7 +115,7 @@ static int do_utimes_fd(int fd, struct timespec64 *times, int flags) f = fdget(fd); if (!f.file) return -EBADF; - error = utimes_common(&f.file->f_path, times); + error = vfs_utimes(&f.file->f_path, times); fdput(f); return error; } diff --git a/include/linux/fs.h b/include/linux/fs.h index 635086726f205..a1d2685a48786 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -1746,6 +1746,7 @@ int vfs_mkobj(struct dentry *, umode_t, int vfs_fchown(struct file *file, uid_t user, gid_t group); int vfs_fchmod(struct file *file, umode_t mode); +int vfs_utimes(const struct path *path, struct timespec64 *times); extern long vfs_ioctl(struct file *file, unsigned int cmd, unsigned long arg); -- GitLab From 38b082236e77d403fed23ac2d30d570598744ec3 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Thu, 30 Jul 2020 08:19:00 +0200 Subject: [PATCH 1198/1476] initramfs: use vfs_utimes in do_copy Don't bother saving away the pathname and just use the new struct path based utimes helper instead. Signed-off-by: Christoph Hellwig Acked-by: Linus Torvalds --- init/initramfs.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/init/initramfs.c b/init/initramfs.c index 35c26668d463f..9820fca4d4e36 100644 --- a/init/initramfs.c +++ b/init/initramfs.c @@ -202,7 +202,6 @@ static inline void __init eat(unsigned n) byte_count -= n; } -static __initdata char *vcollected; static __initdata char *collected; static long remains __initdata; static __initdata char *collect; @@ -345,7 +344,6 @@ static int __init do_name(void) vfs_fchmod(wfile, mode); if (body_len) vfs_truncate(&wfile->f_path, body_len); - vcollected = kstrdup(collected, GFP_KERNEL); state = CopyFile; } } else if (S_ISDIR(mode)) { @@ -368,11 +366,15 @@ static int __init do_name(void) static int __init do_copy(void) { if (byte_count >= body_len) { + struct timespec64 t[2] = { }; if (xwrite(wfile, victim, body_len, &wfile_pos) != body_len) error("write error"); + + t[0].tv_sec = mtime; + t[1].tv_sec = mtime; + vfs_utimes(&wfile->f_path, t); + fput(wfile); - do_utime(vcollected, mtime); - kfree(vcollected); eat(body_len); state = SkipIt; return 0; -- GitLab From a1e6aaa3743a3daa98d0f9275df8243eef8bb666 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Thu, 4 Jun 2020 09:41:08 +0200 Subject: [PATCH 1199/1476] fs: refactor do_mount Factor out a path_mount helper that takes a struct path * instead of the actual file name. This will allow to convert the init and devtmpfs code to properly mount based on a kernel pointer instead of relying on the implicit set_fs(KERNEL_DS) during early init. Signed-off-by: Christoph Hellwig --- fs/namespace.c | 67 ++++++++++++++++++++++++++------------------------ 1 file changed, 35 insertions(+), 32 deletions(-) diff --git a/fs/namespace.c b/fs/namespace.c index f30ed401cc6d7..6f8234f74bed9 100644 --- a/fs/namespace.c +++ b/fs/namespace.c @@ -3115,12 +3115,11 @@ char *copy_mount_string(const void __user *data) * Therefore, if this magic number is present, it carries no information * and must be discarded. */ -long do_mount(const char *dev_name, const char __user *dir_name, +static int path_mount(const char *dev_name, struct path *path, const char *type_page, unsigned long flags, void *data_page) { - struct path path; unsigned int mnt_flags = 0, sb_flags; - int retval = 0; + int ret; /* Discard magic */ if ((flags & MS_MGC_MSK) == MS_MGC_VAL) @@ -3133,19 +3132,13 @@ long do_mount(const char *dev_name, const char __user *dir_name, if (flags & MS_NOUSER) return -EINVAL; - /* ... and get the mountpoint */ - retval = user_path_at(AT_FDCWD, dir_name, LOOKUP_FOLLOW, &path); - if (retval) - return retval; - - retval = security_sb_mount(dev_name, &path, - type_page, flags, data_page); - if (!retval && !may_mount()) - retval = -EPERM; - if (!retval && (flags & SB_MANDLOCK) && !may_mandlock()) - retval = -EPERM; - if (retval) - goto dput_out; + ret = security_sb_mount(dev_name, path, type_page, flags, data_page); + if (ret) + return ret; + if (!may_mount()) + return -EPERM; + if ((flags & SB_MANDLOCK) && !may_mandlock()) + return -EPERM; /* Default to relatime unless overriden */ if (!(flags & MS_NOATIME)) @@ -3172,7 +3165,7 @@ long do_mount(const char *dev_name, const char __user *dir_name, ((flags & (MS_NOATIME | MS_NODIRATIME | MS_RELATIME | MS_STRICTATIME)) == 0)) { mnt_flags &= ~MNT_ATIME_MASK; - mnt_flags |= path.mnt->mnt_flags & MNT_ATIME_MASK; + mnt_flags |= path->mnt->mnt_flags & MNT_ATIME_MASK; } sb_flags = flags & (SB_RDONLY | @@ -3185,22 +3178,32 @@ long do_mount(const char *dev_name, const char __user *dir_name, SB_I_VERSION); if ((flags & (MS_REMOUNT | MS_BIND)) == (MS_REMOUNT | MS_BIND)) - retval = do_reconfigure_mnt(&path, mnt_flags); - else if (flags & MS_REMOUNT) - retval = do_remount(&path, flags, sb_flags, mnt_flags, - data_page); - else if (flags & MS_BIND) - retval = do_loopback(&path, dev_name, flags & MS_REC); - else if (flags & (MS_SHARED | MS_PRIVATE | MS_SLAVE | MS_UNBINDABLE)) - retval = do_change_type(&path, flags); - else if (flags & MS_MOVE) - retval = do_move_mount_old(&path, dev_name); - else - retval = do_new_mount(&path, type_page, sb_flags, mnt_flags, - dev_name, data_page); -dput_out: + return do_reconfigure_mnt(path, mnt_flags); + if (flags & MS_REMOUNT) + return do_remount(path, flags, sb_flags, mnt_flags, data_page); + if (flags & MS_BIND) + return do_loopback(path, dev_name, flags & MS_REC); + if (flags & (MS_SHARED | MS_PRIVATE | MS_SLAVE | MS_UNBINDABLE)) + return do_change_type(path, flags); + if (flags & MS_MOVE) + return do_move_mount_old(path, dev_name); + + return do_new_mount(path, type_page, sb_flags, mnt_flags, dev_name, + data_page); +} + +long do_mount(const char *dev_name, const char __user *dir_name, + const char *type_page, unsigned long flags, void *data_page) +{ + struct path path; + int ret; + + ret = user_path_at(AT_FDCWD, dir_name, LOOKUP_FOLLOW, &path); + if (ret) + return ret; + ret = path_mount(dev_name, &path, type_page, flags, data_page); path_put(&path); - return retval; + return ret; } static struct ucounts *inc_mnt_namespaces(struct user_namespace *ns) -- GitLab From 41525f56e2564c2feff4fb2824823900efb3a39f Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Tue, 21 Jul 2020 10:54:34 +0200 Subject: [PATCH 1200/1476] fs: refactor ksys_umount Factor out a path_umount helper that takes a struct path * instead of the actual file name. This will allow to convert the init and devtmpfs code to properly mount based on a kernel pointer instead of relying on the implicit set_fs(KERNEL_DS) during early init. Signed-off-by: Christoph Hellwig --- fs/namespace.c | 40 ++++++++++++++++++---------------------- 1 file changed, 18 insertions(+), 22 deletions(-) diff --git a/fs/namespace.c b/fs/namespace.c index 6f8234f74bed9..43834b59eff6c 100644 --- a/fs/namespace.c +++ b/fs/namespace.c @@ -1706,36 +1706,19 @@ static inline bool may_mandlock(void) } #endif -/* - * Now umount can handle mount points as well as block devices. - * This is important for filesystems which use unnamed block devices. - * - * We now support a flag for forced unmount like the other 'big iron' - * unixes. Our API is identical to OSF/1 to avoid making a mess of AMD - */ - -int ksys_umount(char __user *name, int flags) +static int path_umount(struct path *path, int flags) { - struct path path; struct mount *mnt; int retval; - int lookup_flags = LOOKUP_MOUNTPOINT; if (flags & ~(MNT_FORCE | MNT_DETACH | MNT_EXPIRE | UMOUNT_NOFOLLOW)) return -EINVAL; - if (!may_mount()) return -EPERM; - if (!(flags & UMOUNT_NOFOLLOW)) - lookup_flags |= LOOKUP_FOLLOW; - - retval = user_path_at(AT_FDCWD, name, lookup_flags, &path); - if (retval) - goto out; - mnt = real_mount(path.mnt); + mnt = real_mount(path->mnt); retval = -EINVAL; - if (path.dentry != path.mnt->mnt_root) + if (path->dentry != path->mnt->mnt_root) goto dput_and_out; if (!check_mnt(mnt)) goto dput_and_out; @@ -1748,12 +1731,25 @@ int ksys_umount(char __user *name, int flags) retval = do_umount(mnt, flags); dput_and_out: /* we mustn't call path_put() as that would clear mnt_expiry_mark */ - dput(path.dentry); + dput(path->dentry); mntput_no_expire(mnt); -out: return retval; } +int ksys_umount(char __user *name, int flags) +{ + int lookup_flags = LOOKUP_MOUNTPOINT; + struct path path; + int ret; + + if (!(flags & UMOUNT_NOFOLLOW)) + lookup_flags |= LOOKUP_FOLLOW; + ret = user_path_at(AT_FDCWD, name, lookup_flags, &path); + if (ret) + return ret; + return path_umount(&path, flags); +} + SYSCALL_DEFINE2(umount, char __user *, name, int, flags) { return ksys_umount(name, flags); -- GitLab From e24ab0ef689de43649327f54cd1088f3dad25bb3 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Tue, 21 Jul 2020 10:48:15 +0200 Subject: [PATCH 1201/1476] fs: push the getname from do_rmdir into the callers This mirrors do_unlinkat and will make life a little easier for the init code to reuse the whole function with a kernel filename. Signed-off-by: Christoph Hellwig --- fs/internal.h | 2 +- fs/namei.c | 10 ++++------ include/linux/syscalls.h | 4 ++-- 3 files changed, 7 insertions(+), 9 deletions(-) diff --git a/fs/internal.h b/fs/internal.h index 9b863a7bd7089..e903d5aae139a 100644 --- a/fs/internal.h +++ b/fs/internal.h @@ -65,7 +65,7 @@ extern int vfs_path_lookup(struct dentry *, struct vfsmount *, long do_mknodat(int dfd, const char __user *filename, umode_t mode, unsigned int dev); long do_mkdirat(int dfd, const char __user *pathname, umode_t mode); -long do_rmdir(int dfd, const char __user *pathname); +long do_rmdir(int dfd, struct filename *name); long do_unlinkat(int dfd, struct filename *name); long do_symlinkat(const char __user *oldname, int newdfd, const char __user *newname); diff --git a/fs/namei.c b/fs/namei.c index 72d4219c93acb..d75a6039ae396 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -3720,17 +3720,16 @@ out: } EXPORT_SYMBOL(vfs_rmdir); -long do_rmdir(int dfd, const char __user *pathname) +long do_rmdir(int dfd, struct filename *name) { int error = 0; - struct filename *name; struct dentry *dentry; struct path path; struct qstr last; int type; unsigned int lookup_flags = 0; retry: - name = filename_parentat(dfd, getname(pathname), lookup_flags, + name = filename_parentat(dfd, name, lookup_flags, &path, &last, &type); if (IS_ERR(name)) return PTR_ERR(name); @@ -3781,7 +3780,7 @@ exit1: SYSCALL_DEFINE1(rmdir, const char __user *, pathname) { - return do_rmdir(AT_FDCWD, pathname); + return do_rmdir(AT_FDCWD, getname(pathname)); } /** @@ -3926,8 +3925,7 @@ SYSCALL_DEFINE3(unlinkat, int, dfd, const char __user *, pathname, int, flag) return -EINVAL; if (flag & AT_REMOVEDIR) - return do_rmdir(dfd, pathname); - + return do_rmdir(dfd, getname(pathname)); return do_unlinkat(dfd, getname(pathname)); } diff --git a/include/linux/syscalls.h b/include/linux/syscalls.h index 5b0f1fca4cfb9..e43816198e600 100644 --- a/include/linux/syscalls.h +++ b/include/linux/syscalls.h @@ -1281,11 +1281,11 @@ static inline long ksys_unlink(const char __user *pathname) return do_unlinkat(AT_FDCWD, getname(pathname)); } -extern long do_rmdir(int dfd, const char __user *pathname); +long do_rmdir(int dfd, struct filename *name); static inline long ksys_rmdir(const char __user *pathname) { - return do_rmdir(AT_FDCWD, pathname); + return do_rmdir(AT_FDCWD, getname(pathname)); } extern long do_mkdirat(int dfd, const char __user *pathname, umode_t mode); -- GitLab From bcbacc4909f1b93242ba2894d0282c151c9d0e68 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Wed, 22 Jul 2020 10:48:25 +0200 Subject: [PATCH 1202/1476] devtmpfs: refactor devtmpfsd() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Split the main worker loop into a separate function. This allows devtmpfsd_setup to be marked __init, which will allows us to call __init routines for the setup work. devtmpfѕ itself needs a __ref marker for that to work, and a comment explaining why it works. Signed-off-by: Christoph Hellwig --- drivers/base/devtmpfs.c | 52 ++++++++++++++++++++++++----------------- 1 file changed, 31 insertions(+), 21 deletions(-) diff --git a/drivers/base/devtmpfs.c b/drivers/base/devtmpfs.c index c9017e0584c00..d697634bc0d48 100644 --- a/drivers/base/devtmpfs.c +++ b/drivers/base/devtmpfs.c @@ -378,7 +378,30 @@ static int handle(const char *name, umode_t mode, kuid_t uid, kgid_t gid, return handle_remove(name, dev); } -static int devtmpfs_setup(void *p) +static void __noreturn devtmpfs_work_loop(void) +{ + while (1) { + spin_lock(&req_lock); + while (requests) { + struct req *req = requests; + requests = NULL; + spin_unlock(&req_lock); + while (req) { + struct req *next = req->next; + req->err = handle(req->name, req->mode, + req->uid, req->gid, req->dev); + complete(&req->done); + req = next; + } + spin_lock(&req_lock); + } + __set_current_state(TASK_INTERRUPTIBLE); + spin_unlock(&req_lock); + schedule(); + } +} + +static int __init devtmpfs_setup(void *p) { int err; @@ -396,31 +419,18 @@ out: return err; } -static int devtmpfsd(void *p) +/* + * The __ref is because devtmpfs_setup needs to be __init for the routines it + * calls. That call is done while devtmpfs_init, which is marked __init, + * synchronously waits for it to complete. + */ +static int __ref devtmpfsd(void *p) { int err = devtmpfs_setup(p); if (err) return err; - while (1) { - spin_lock(&req_lock); - while (requests) { - struct req *req = requests; - requests = NULL; - spin_unlock(&req_lock); - while (req) { - struct req *next = req->next; - req->err = handle(req->name, req->mode, - req->uid, req->gid, req->dev); - complete(&req->done); - req = next; - } - spin_lock(&req_lock); - } - __set_current_state(TASK_INTERRUPTIBLE); - spin_unlock(&req_lock); - schedule(); - } + devtmpfs_work_loop(); return 0; } -- GitLab From 916db733deca5dc1e14ea8e3acfc121600a9f462 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Sun, 7 Jun 2020 09:06:29 +0200 Subject: [PATCH 1203/1476] init: initialize ramdisk_execute_command at compile time Set ramdisk_execute_command to "/init" at compile time. The command line can still override it, but this saves a few instructions and removes a NULL check. Signed-off-by: Christoph Hellwig --- init/main.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/init/main.c b/init/main.c index db0621dfbb046..c2c9143db9679 100644 --- a/init/main.c +++ b/init/main.c @@ -154,7 +154,7 @@ static bool initargs_found; #endif static char *execute_command; -static char *ramdisk_execute_command; +static char *ramdisk_execute_command = "/init"; /* * Used to generate warnings if static_key manipulation functions are used @@ -1514,10 +1514,6 @@ static noinline void __init kernel_init_freeable(void) * check if there is an early userspace init. If yes, let it do all * the work */ - - if (!ramdisk_execute_command) - ramdisk_execute_command = "/init"; - if (ksys_access((const char __user *) ramdisk_execute_command, 0) != 0) { ramdisk_execute_command = NULL; -- GitLab From a94b5214487b626a148c6aab86809fbeb8fcd6b1 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Tue, 28 Jul 2020 18:23:21 +0200 Subject: [PATCH 1204/1476] init: mark console_on_rootfs as __init This helper is only used for the early init code. Signed-off-by: Christoph Hellwig --- init/main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/init/main.c b/init/main.c index c2c9143db9679..47698427b15f6 100644 --- a/init/main.c +++ b/init/main.c @@ -1458,7 +1458,7 @@ static int __ref kernel_init(void *unused) } /* Open /dev/console, for stdin/stdout/stderr, this should never fail */ -void console_on_rootfs(void) +void __init console_on_rootfs(void) { struct file *file = filp_open("/dev/console", O_RDWR, 0); -- GitLab From 09cbcec07b578c04ab4ab0e31940c20126f79c4b Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Tue, 21 Jul 2020 12:20:16 +0200 Subject: [PATCH 1205/1476] init: mark create_dev as __init This helper is only used for the early init code. Signed-off-by: Christoph Hellwig --- init/do_mounts.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/init/do_mounts.h b/init/do_mounts.h index c855b3f0e06d1..021e2f60223e2 100644 --- a/init/do_mounts.h +++ b/init/do_mounts.h @@ -13,7 +13,7 @@ void mount_block_root(char *name, int flags); void mount_root(void); extern int root_mountflags; -static inline int create_dev(char *name, dev_t dev) +static inline __init int create_dev(char *name, dev_t dev) { ksys_unlink(name); return ksys_mknod(name, S_IFBLK|0600, new_encode_dev(dev)); -- GitLab From c60166f04283ffba7b88b45d824bbfb2bfccee24 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Tue, 21 Jul 2020 11:12:08 +0200 Subject: [PATCH 1206/1476] init: add an init_mount helper Like do_mount, but takes a kernel pointer for the destination path. Switch over the mounts in the init code and devtmpfs to it, which just happen to work due to the implicit set_fs(KERNEL_DS) during early init right now. Signed-off-by: Christoph Hellwig --- drivers/base/devtmpfs.c | 5 +++-- fs/Makefile | 2 +- fs/init.c | 25 +++++++++++++++++++++++++ fs/internal.h | 4 ++++ fs/namespace.c | 2 +- include/linux/init_syscalls.h | 4 ++++ init/do_mounts.c | 8 ++++---- init/do_mounts.h | 1 + init/do_mounts_initrd.c | 6 +++--- 9 files changed, 46 insertions(+), 11 deletions(-) create mode 100644 fs/init.c create mode 100644 include/linux/init_syscalls.h diff --git a/drivers/base/devtmpfs.c b/drivers/base/devtmpfs.c index d697634bc0d48..32af6cb987b42 100644 --- a/drivers/base/devtmpfs.c +++ b/drivers/base/devtmpfs.c @@ -25,6 +25,7 @@ #include #include #include +#include #include #include "base.h" @@ -359,7 +360,7 @@ int __init devtmpfs_mount(void) if (!thread) return 0; - err = do_mount("devtmpfs", "dev", "devtmpfs", MS_SILENT, NULL); + err = init_mount("devtmpfs", "dev", "devtmpfs", MS_SILENT, NULL); if (err) printk(KERN_INFO "devtmpfs: error mounting %i\n", err); else @@ -408,7 +409,7 @@ static int __init devtmpfs_setup(void *p) err = ksys_unshare(CLONE_NEWNS); if (err) goto out; - err = do_mount("devtmpfs", "/", "devtmpfs", MS_SILENT, NULL); + err = init_mount("devtmpfs", "/", "devtmpfs", MS_SILENT, NULL); if (err) goto out; ksys_chdir("/.."); /* will traverse into overmounted root */ diff --git a/fs/Makefile b/fs/Makefile index 2ce5112b02c86..1c7b0e3f6daa1 100644 --- a/fs/Makefile +++ b/fs/Makefile @@ -13,7 +13,7 @@ obj-y := open.o read_write.o file_table.o super.o \ seq_file.o xattr.o libfs.o fs-writeback.o \ pnode.o splice.o sync.o utimes.o d_path.o \ stack.o fs_struct.o statfs.o fs_pin.o nsfs.o \ - fs_types.o fs_context.o fs_parser.o fsopen.o + fs_types.o fs_context.o fs_parser.o fsopen.o init.o ifeq ($(CONFIG_BLOCK),y) obj-y += buffer.o block_dev.o direct-io.o mpage.o diff --git a/fs/init.c b/fs/init.c new file mode 100644 index 0000000000000..c6eb724e1c7b2 --- /dev/null +++ b/fs/init.c @@ -0,0 +1,25 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Routines that mimic syscalls, but don't use the user address space or file + * descriptors. Only for init/ and related early init code. + */ +#include +#include +#include +#include +#include +#include "internal.h" + +int __init init_mount(const char *dev_name, const char *dir_name, + const char *type_page, unsigned long flags, void *data_page) +{ + struct path path; + int ret; + + ret = kern_path(dir_name, LOOKUP_FOLLOW, &path); + if (ret) + return ret; + ret = path_mount(dev_name, &path, type_page, flags, data_page); + path_put(&path); + return ret; +} diff --git a/fs/internal.h b/fs/internal.h index e903d5aae139a..72ea0b6f7435a 100644 --- a/fs/internal.h +++ b/fs/internal.h @@ -89,6 +89,10 @@ extern int __mnt_want_write_file(struct file *); extern void __mnt_drop_write_file(struct file *); extern void dissolve_on_fput(struct vfsmount *); + +int path_mount(const char *dev_name, struct path *path, + const char *type_page, unsigned long flags, void *data_page); + /* * fs_struct.c */ diff --git a/fs/namespace.c b/fs/namespace.c index 43834b59eff6c..2c4d759209748 100644 --- a/fs/namespace.c +++ b/fs/namespace.c @@ -3111,7 +3111,7 @@ char *copy_mount_string(const void __user *data) * Therefore, if this magic number is present, it carries no information * and must be discarded. */ -static int path_mount(const char *dev_name, struct path *path, +int path_mount(const char *dev_name, struct path *path, const char *type_page, unsigned long flags, void *data_page) { unsigned int mnt_flags = 0, sb_flags; diff --git a/include/linux/init_syscalls.h b/include/linux/init_syscalls.h new file mode 100644 index 0000000000000..af9ea88a60e0b --- /dev/null +++ b/include/linux/init_syscalls.h @@ -0,0 +1,4 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +int __init init_mount(const char *dev_name, const char *dir_name, + const char *type_page, unsigned long flags, void *data_page); diff --git a/init/do_mounts.c b/init/do_mounts.c index a7f22cf58c7ef..83db87b6e5d1e 100644 --- a/init/do_mounts.c +++ b/init/do_mounts.c @@ -395,16 +395,16 @@ static int __init do_mount_root(const char *name, const char *fs, int ret; if (data) { - /* do_mount() requires a full page as fifth argument */ + /* init_mount() requires a full page as fifth argument */ p = alloc_page(GFP_KERNEL); if (!p) return -ENOMEM; data_page = page_address(p); - /* zero-pad. do_mount() will make sure it's terminated */ + /* zero-pad. init_mount() will make sure it's terminated */ strncpy(data_page, data, PAGE_SIZE); } - ret = do_mount(name, "/root", fs, flags, data_page); + ret = init_mount(name, "/root", fs, flags, data_page); if (ret) goto out; @@ -628,7 +628,7 @@ void __init prepare_namespace(void) mount_root(); out: devtmpfs_mount(); - do_mount(".", "/", NULL, MS_MOVE, NULL); + init_mount(".", "/", NULL, MS_MOVE, NULL); ksys_chroot("."); } diff --git a/init/do_mounts.h b/init/do_mounts.h index 021e2f60223e2..20e7fec8cb499 100644 --- a/init/do_mounts.h +++ b/init/do_mounts.h @@ -8,6 +8,7 @@ #include #include #include +#include void mount_block_root(char *name, int flags); void mount_root(void); diff --git a/init/do_mounts_initrd.c b/init/do_mounts_initrd.c index e08669187d63b..1f9336209ad9c 100644 --- a/init/do_mounts_initrd.c +++ b/init/do_mounts_initrd.c @@ -62,7 +62,7 @@ static int __init init_linuxrc(struct subprocess_info *info, struct cred *new) console_on_rootfs(); /* move initrd over / and chdir/chroot in initrd root */ ksys_chdir("/root"); - do_mount(".", "/", NULL, MS_MOVE, NULL); + init_mount(".", "/", NULL, MS_MOVE, NULL); ksys_chroot("."); ksys_setsid(); return 0; @@ -99,7 +99,7 @@ static void __init handle_initrd(void) current->flags &= ~PF_FREEZER_SKIP; /* move initrd to rootfs' /old */ - do_mount("..", ".", NULL, MS_MOVE, NULL); + init_mount("..", ".", NULL, MS_MOVE, NULL); /* switch root and cwd back to / of rootfs */ ksys_chroot(".."); @@ -113,7 +113,7 @@ static void __init handle_initrd(void) mount_root(); printk(KERN_NOTICE "Trying to move old root to /initrd ... "); - error = do_mount("/old", "/root/initrd", NULL, MS_MOVE, NULL); + error = init_mount("/old", "/root/initrd", NULL, MS_MOVE, NULL); if (!error) printk("okay\n"); else { -- GitLab From 09267defa36aaff6ff829bd2fc8b043ec151cc3e Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Thu, 23 Jul 2020 08:23:08 +0200 Subject: [PATCH 1207/1476] init: add an init_umount helper Like ksys_umount, but takes a kernel pointer for the destination path. Switch over the umount in the init code, which just happen to work due to the implicit set_fs(KERNEL_DS) during early init right now. Signed-off-by: Christoph Hellwig --- fs/init.c | 14 ++++++++++++++ fs/internal.h | 1 + fs/namespace.c | 4 ++-- include/linux/init_syscalls.h | 1 + include/linux/syscalls.h | 1 - init/do_mounts_initrd.c | 2 +- 6 files changed, 19 insertions(+), 4 deletions(-) diff --git a/fs/init.c b/fs/init.c index c6eb724e1c7b2..9c8e31fdb048c 100644 --- a/fs/init.c +++ b/fs/init.c @@ -23,3 +23,17 @@ int __init init_mount(const char *dev_name, const char *dir_name, path_put(&path); return ret; } + +int __init init_umount(const char *name, int flags) +{ + int lookup_flags = LOOKUP_MOUNTPOINT; + struct path path; + int ret; + + if (!(flags & UMOUNT_NOFOLLOW)) + lookup_flags |= LOOKUP_FOLLOW; + ret = kern_path(name, lookup_flags, &path); + if (ret) + return ret; + return path_umount(&path, flags); +} diff --git a/fs/internal.h b/fs/internal.h index 72ea0b6f7435a..491d1e63809b3 100644 --- a/fs/internal.h +++ b/fs/internal.h @@ -92,6 +92,7 @@ extern void dissolve_on_fput(struct vfsmount *); int path_mount(const char *dev_name, struct path *path, const char *type_page, unsigned long flags, void *data_page); +int path_umount(struct path *path, int flags); /* * fs_struct.c diff --git a/fs/namespace.c b/fs/namespace.c index 2c4d759209748..a7301790abb21 100644 --- a/fs/namespace.c +++ b/fs/namespace.c @@ -1706,7 +1706,7 @@ static inline bool may_mandlock(void) } #endif -static int path_umount(struct path *path, int flags) +int path_umount(struct path *path, int flags) { struct mount *mnt; int retval; @@ -1736,7 +1736,7 @@ dput_and_out: return retval; } -int ksys_umount(char __user *name, int flags) +static int ksys_umount(char __user *name, int flags) { int lookup_flags = LOOKUP_MOUNTPOINT; struct path path; diff --git a/include/linux/init_syscalls.h b/include/linux/init_syscalls.h index af9ea88a60e0b..a5a2e7f199169 100644 --- a/include/linux/init_syscalls.h +++ b/include/linux/init_syscalls.h @@ -2,3 +2,4 @@ int __init init_mount(const char *dev_name, const char *dir_name, const char *type_page, unsigned long flags, void *data_page); +int __init init_umount(const char *name, int flags); diff --git a/include/linux/syscalls.h b/include/linux/syscalls.h index e43816198e600..1a4f5d8ee7044 100644 --- a/include/linux/syscalls.h +++ b/include/linux/syscalls.h @@ -1236,7 +1236,6 @@ asmlinkage long sys_ni_syscall(void); * the ksys_xyzyyz() functions prototyped below. */ -int ksys_umount(char __user *name, int flags); int ksys_chroot(const char __user *filename); ssize_t ksys_write(unsigned int fd, const char __user *buf, size_t count); int ksys_chdir(const char __user *filename); diff --git a/init/do_mounts_initrd.c b/init/do_mounts_initrd.c index 1f9336209ad9c..6b020a0699025 100644 --- a/init/do_mounts_initrd.c +++ b/init/do_mounts_initrd.c @@ -122,7 +122,7 @@ static void __init handle_initrd(void) else printk("failed\n"); printk(KERN_NOTICE "Unmounting old root\n"); - ksys_umount("/old", MNT_DETACH); + init_umount("/old", MNT_DETACH); } } -- GitLab From 8fb9f73e5a539ab3aa4785f30fb52c65fa98600c Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Thu, 23 Jul 2020 08:23:40 +0200 Subject: [PATCH 1208/1476] init: add an init_unlink helper Add a simple helper to unlink with a kernel space file name and switch the early init code over to it. Remove the now unused ksys_unlink. Signed-off-by: Christoph Hellwig --- fs/init.c | 5 +++++ include/linux/init_syscalls.h | 1 + include/linux/syscalls.h | 7 ------- init/do_mounts.h | 2 +- init/do_mounts_initrd.c | 4 ++-- init/do_mounts_rd.c | 2 +- init/initramfs.c | 3 ++- 7 files changed, 12 insertions(+), 12 deletions(-) diff --git a/fs/init.c b/fs/init.c index 9c8e31fdb048c..507ffbb5d146d 100644 --- a/fs/init.c +++ b/fs/init.c @@ -37,3 +37,8 @@ int __init init_umount(const char *name, int flags) return ret; return path_umount(&path, flags); } + +int __init init_unlink(const char *pathname) +{ + return do_unlinkat(AT_FDCWD, getname_kernel(pathname)); +} diff --git a/include/linux/init_syscalls.h b/include/linux/init_syscalls.h index a5a2e7f199169..00d597249549e 100644 --- a/include/linux/init_syscalls.h +++ b/include/linux/init_syscalls.h @@ -3,3 +3,4 @@ int __init init_mount(const char *dev_name, const char *dir_name, const char *type_page, unsigned long flags, void *data_page); int __init init_umount(const char *name, int flags); +int __init init_unlink(const char *pathname); diff --git a/include/linux/syscalls.h b/include/linux/syscalls.h index 1a4f5d8ee7044..26f9738e5ab86 100644 --- a/include/linux/syscalls.h +++ b/include/linux/syscalls.h @@ -1273,13 +1273,6 @@ int compat_ksys_ipc(u32 call, int first, int second, * The following kernel syscall equivalents are just wrappers to fs-internal * functions. Therefore, provide stubs to be inlined at the callsites. */ -extern long do_unlinkat(int dfd, struct filename *name); - -static inline long ksys_unlink(const char __user *pathname) -{ - return do_unlinkat(AT_FDCWD, getname(pathname)); -} - long do_rmdir(int dfd, struct filename *name); static inline long ksys_rmdir(const char __user *pathname) diff --git a/init/do_mounts.h b/init/do_mounts.h index 20e7fec8cb499..104d8431725ae 100644 --- a/init/do_mounts.h +++ b/init/do_mounts.h @@ -16,7 +16,7 @@ extern int root_mountflags; static inline __init int create_dev(char *name, dev_t dev) { - ksys_unlink(name); + init_unlink(name); return ksys_mknod(name, S_IFBLK|0600, new_encode_dev(dev)); } diff --git a/init/do_mounts_initrd.c b/init/do_mounts_initrd.c index 6b020a0699025..8b44dd017842a 100644 --- a/init/do_mounts_initrd.c +++ b/init/do_mounts_initrd.c @@ -137,11 +137,11 @@ bool __init initrd_load(void) * mounted in the normal path. */ if (rd_load_image("/initrd.image") && ROOT_DEV != Root_RAM0) { - ksys_unlink("/initrd.image"); + init_unlink("/initrd.image"); handle_initrd(); return true; } } - ksys_unlink("/initrd.image"); + init_unlink("/initrd.image"); return false; } diff --git a/init/do_mounts_rd.c b/init/do_mounts_rd.c index d4255c10432a8..ac021ae6e6fa7 100644 --- a/init/do_mounts_rd.c +++ b/init/do_mounts_rd.c @@ -272,7 +272,7 @@ noclose_input: fput(out_file); out: kfree(buf); - ksys_unlink("/dev/ram"); + init_unlink("/dev/ram"); return res; } diff --git a/init/initramfs.c b/init/initramfs.c index 9820fca4d4e36..eb58cee6dadb4 100644 --- a/init/initramfs.c +++ b/init/initramfs.c @@ -12,6 +12,7 @@ #include #include #include +#include static ssize_t __init xwrite(struct file *file, const char *p, size_t count, loff_t *pos) @@ -301,7 +302,7 @@ static void __init clean_path(char *path, umode_t fmode) if (S_ISDIR(st.mode)) ksys_rmdir(path); else - ksys_unlink(path); + init_unlink(path); } } -- GitLab From 20cce026c3e0972017b9cb4a7cccfb8cacf187d5 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Wed, 22 Jul 2020 11:11:45 +0200 Subject: [PATCH 1209/1476] init: add an init_rmdir helper Add a simple helper to rmdir with a kernel space file name and switch the early init code over to it. Remove the now unused ksys_rmdir. Signed-off-by: Christoph Hellwig --- fs/init.c | 5 +++++ include/linux/init_syscalls.h | 1 + include/linux/syscalls.h | 7 ------- init/initramfs.c | 2 +- 4 files changed, 7 insertions(+), 8 deletions(-) diff --git a/fs/init.c b/fs/init.c index 507ffbb5d146d..eabd9ed2b5109 100644 --- a/fs/init.c +++ b/fs/init.c @@ -42,3 +42,8 @@ int __init init_unlink(const char *pathname) { return do_unlinkat(AT_FDCWD, getname_kernel(pathname)); } + +int __init init_rmdir(const char *pathname) +{ + return do_rmdir(AT_FDCWD, getname_kernel(pathname)); +} diff --git a/include/linux/init_syscalls.h b/include/linux/init_syscalls.h index 00d597249549e..abf3af563c0b3 100644 --- a/include/linux/init_syscalls.h +++ b/include/linux/init_syscalls.h @@ -4,3 +4,4 @@ int __init init_mount(const char *dev_name, const char *dir_name, const char *type_page, unsigned long flags, void *data_page); int __init init_umount(const char *name, int flags); int __init init_unlink(const char *pathname); +int __init init_rmdir(const char *pathname); diff --git a/include/linux/syscalls.h b/include/linux/syscalls.h index 26f9738e5ab86..a7b14258d245e 100644 --- a/include/linux/syscalls.h +++ b/include/linux/syscalls.h @@ -1273,13 +1273,6 @@ int compat_ksys_ipc(u32 call, int first, int second, * The following kernel syscall equivalents are just wrappers to fs-internal * functions. Therefore, provide stubs to be inlined at the callsites. */ -long do_rmdir(int dfd, struct filename *name); - -static inline long ksys_rmdir(const char __user *pathname) -{ - return do_rmdir(AT_FDCWD, getname(pathname)); -} - extern long do_mkdirat(int dfd, const char __user *pathname, umode_t mode); static inline long ksys_mkdir(const char __user *pathname, umode_t mode) diff --git a/init/initramfs.c b/init/initramfs.c index eb58cee6dadb4..1a9159bf452fe 100644 --- a/init/initramfs.c +++ b/init/initramfs.c @@ -300,7 +300,7 @@ static void __init clean_path(char *path, umode_t fmode) if (!vfs_lstat(path, &st) && (st.mode ^ fmode) & S_IFMT) { if (S_ISDIR(st.mode)) - ksys_rmdir(path); + init_rmdir(path); else init_unlink(path); } -- GitLab From db63f1e315384590b979f8f74abd1b5363b69894 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Wed, 22 Jul 2020 11:25:21 +0200 Subject: [PATCH 1210/1476] init: add an init_chdir helper Add a simple helper to chdir with a kernel space file name and switch the early init code over to it. Remove the now unused ksys_chdir. Signed-off-by: Christoph Hellwig --- drivers/base/devtmpfs.c | 2 +- fs/init.c | 16 ++++++++++++++++ fs/open.c | 7 +------ include/linux/init_syscalls.h | 1 + include/linux/syscalls.h | 1 - init/do_mounts.c | 2 +- init/do_mounts_initrd.c | 8 ++++---- 7 files changed, 24 insertions(+), 13 deletions(-) diff --git a/drivers/base/devtmpfs.c b/drivers/base/devtmpfs.c index 32af6cb987b42..e48aaba3166b5 100644 --- a/drivers/base/devtmpfs.c +++ b/drivers/base/devtmpfs.c @@ -412,7 +412,7 @@ static int __init devtmpfs_setup(void *p) err = init_mount("devtmpfs", "/", "devtmpfs", MS_SILENT, NULL); if (err) goto out; - ksys_chdir("/.."); /* will traverse into overmounted root */ + init_chdir("/.."); /* will traverse into overmounted root */ ksys_chroot("."); out: *(int *)p = err; diff --git a/fs/init.c b/fs/init.c index eabd9ed2b5109..64d4e12eba933 100644 --- a/fs/init.c +++ b/fs/init.c @@ -7,6 +7,7 @@ #include #include #include +#include #include #include "internal.h" @@ -38,6 +39,21 @@ int __init init_umount(const char *name, int flags) return path_umount(&path, flags); } +int __init init_chdir(const char *filename) +{ + struct path path; + int error; + + error = kern_path(filename, LOOKUP_FOLLOW | LOOKUP_DIRECTORY, &path); + if (error) + return error; + error = inode_permission(path.dentry->d_inode, MAY_EXEC | MAY_CHDIR); + if (!error) + set_fs_pwd(current->fs, &path); + path_put(&path); + return error; +} + int __init init_unlink(const char *pathname) { return do_unlinkat(AT_FDCWD, getname_kernel(pathname)); diff --git a/fs/open.c b/fs/open.c index b316dd6a86a8b..723e0ac898935 100644 --- a/fs/open.c +++ b/fs/open.c @@ -482,7 +482,7 @@ SYSCALL_DEFINE2(access, const char __user *, filename, int, mode) return do_faccessat(AT_FDCWD, filename, mode, 0); } -int ksys_chdir(const char __user *filename) +SYSCALL_DEFINE1(chdir, const char __user *, filename) { struct path path; int error; @@ -508,11 +508,6 @@ out: return error; } -SYSCALL_DEFINE1(chdir, const char __user *, filename) -{ - return ksys_chdir(filename); -} - SYSCALL_DEFINE1(fchdir, unsigned int, fd) { struct fd f = fdget_raw(fd); diff --git a/include/linux/init_syscalls.h b/include/linux/init_syscalls.h index abf3af563c0b3..1e845910ae56e 100644 --- a/include/linux/init_syscalls.h +++ b/include/linux/init_syscalls.h @@ -3,5 +3,6 @@ int __init init_mount(const char *dev_name, const char *dir_name, const char *type_page, unsigned long flags, void *data_page); int __init init_umount(const char *name, int flags); +int __init init_chdir(const char *filename); int __init init_unlink(const char *pathname); int __init init_rmdir(const char *pathname); diff --git a/include/linux/syscalls.h b/include/linux/syscalls.h index a7b14258d245e..31fa67fb9894b 100644 --- a/include/linux/syscalls.h +++ b/include/linux/syscalls.h @@ -1238,7 +1238,6 @@ asmlinkage long sys_ni_syscall(void); int ksys_chroot(const char __user *filename); ssize_t ksys_write(unsigned int fd, const char __user *buf, size_t count); -int ksys_chdir(const char __user *filename); int ksys_fchown(unsigned int fd, uid_t user, gid_t group); ssize_t ksys_read(unsigned int fd, char __user *buf, size_t count); void ksys_sync(void); diff --git a/init/do_mounts.c b/init/do_mounts.c index 83db87b6e5d1e..a7581c6e85f26 100644 --- a/init/do_mounts.c +++ b/init/do_mounts.c @@ -408,7 +408,7 @@ static int __init do_mount_root(const char *name, const char *fs, if (ret) goto out; - ksys_chdir("/root"); + init_chdir("/root"); s = current->fs->pwd.dentry->d_sb; ROOT_DEV = s->s_dev; printk(KERN_INFO diff --git a/init/do_mounts_initrd.c b/init/do_mounts_initrd.c index 8b44dd017842a..04627fd22a921 100644 --- a/init/do_mounts_initrd.c +++ b/init/do_mounts_initrd.c @@ -61,7 +61,7 @@ static int __init init_linuxrc(struct subprocess_info *info, struct cred *new) ksys_unshare(CLONE_FS | CLONE_FILES); console_on_rootfs(); /* move initrd over / and chdir/chroot in initrd root */ - ksys_chdir("/root"); + init_chdir("/root"); init_mount(".", "/", NULL, MS_MOVE, NULL); ksys_chroot("."); ksys_setsid(); @@ -82,7 +82,7 @@ static void __init handle_initrd(void) /* mount initrd on rootfs' /root */ mount_block_root("/dev/root.old", root_mountflags & ~MS_RDONLY); ksys_mkdir("/old", 0700); - ksys_chdir("/old"); + init_chdir("/old"); /* * In case that a resume from disk is carried out by linuxrc or one of @@ -104,11 +104,11 @@ static void __init handle_initrd(void) ksys_chroot(".."); if (new_decode_dev(real_root_dev) == Root_RAM0) { - ksys_chdir("/old"); + init_chdir("/old"); return; } - ksys_chdir("/"); + init_chdir("/"); ROOT_DEV = new_decode_dev(real_root_dev); mount_root(); -- GitLab From 4b7ca5014cbef51cdb99fd644eae4f3773747a05 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Wed, 22 Jul 2020 11:26:13 +0200 Subject: [PATCH 1211/1476] init: add an init_chroot helper Add a simple helper to chroot with a kernel space file name and switch the early init code over to it. Remove the now unused ksys_chroot. Signed-off-by: Christoph Hellwig --- drivers/base/devtmpfs.c | 2 +- fs/init.c | 24 ++++++++++++++++++++++++ fs/open.c | 7 +------ include/linux/init_syscalls.h | 1 + include/linux/syscalls.h | 2 -- init/do_mounts.c | 2 +- init/do_mounts_initrd.c | 4 ++-- 7 files changed, 30 insertions(+), 12 deletions(-) diff --git a/drivers/base/devtmpfs.c b/drivers/base/devtmpfs.c index e48aaba3166b5..eac184e6d6577 100644 --- a/drivers/base/devtmpfs.c +++ b/drivers/base/devtmpfs.c @@ -413,7 +413,7 @@ static int __init devtmpfs_setup(void *p) if (err) goto out; init_chdir("/.."); /* will traverse into overmounted root */ - ksys_chroot("."); + init_chroot("."); out: *(int *)p = err; complete(&setup_done); diff --git a/fs/init.c b/fs/init.c index 64d4e12eba933..2c78f24814dde 100644 --- a/fs/init.c +++ b/fs/init.c @@ -9,6 +9,7 @@ #include #include #include +#include #include "internal.h" int __init init_mount(const char *dev_name, const char *dir_name, @@ -54,6 +55,29 @@ int __init init_chdir(const char *filename) return error; } +int __init init_chroot(const char *filename) +{ + struct path path; + int error; + + error = kern_path(filename, LOOKUP_FOLLOW | LOOKUP_DIRECTORY, &path); + if (error) + return error; + error = inode_permission(path.dentry->d_inode, MAY_EXEC | MAY_CHDIR); + if (error) + goto dput_and_out; + error = -EPERM; + if (!ns_capable(current_user_ns(), CAP_SYS_CHROOT)) + goto dput_and_out; + error = security_path_chroot(&path); + if (error) + goto dput_and_out; + set_fs_root(current->fs, &path); +dput_and_out: + path_put(&path); + return error; +} + int __init init_unlink(const char *pathname) { return do_unlinkat(AT_FDCWD, getname_kernel(pathname)); diff --git a/fs/open.c b/fs/open.c index 723e0ac898935..f62f4752bb436 100644 --- a/fs/open.c +++ b/fs/open.c @@ -530,7 +530,7 @@ out: return error; } -int ksys_chroot(const char __user *filename) +SYSCALL_DEFINE1(chroot, const char __user *, filename) { struct path path; int error; @@ -563,11 +563,6 @@ out: return error; } -SYSCALL_DEFINE1(chroot, const char __user *, filename) -{ - return ksys_chroot(filename); -} - static int chmod_common(const struct path *path, umode_t mode) { struct inode *inode = path->dentry->d_inode; diff --git a/include/linux/init_syscalls.h b/include/linux/init_syscalls.h index 1e845910ae56e..e07099a14b91d 100644 --- a/include/linux/init_syscalls.h +++ b/include/linux/init_syscalls.h @@ -4,5 +4,6 @@ int __init init_mount(const char *dev_name, const char *dir_name, const char *type_page, unsigned long flags, void *data_page); int __init init_umount(const char *name, int flags); int __init init_chdir(const char *filename); +int __init init_chroot(const char *filename); int __init init_unlink(const char *pathname); int __init init_rmdir(const char *pathname); diff --git a/include/linux/syscalls.h b/include/linux/syscalls.h index 31fa67fb9894b..e89d62e944dc0 100644 --- a/include/linux/syscalls.h +++ b/include/linux/syscalls.h @@ -1235,8 +1235,6 @@ asmlinkage long sys_ni_syscall(void); * Instead, use one of the functions which work equivalently, such as * the ksys_xyzyyz() functions prototyped below. */ - -int ksys_chroot(const char __user *filename); ssize_t ksys_write(unsigned int fd, const char __user *buf, size_t count); int ksys_fchown(unsigned int fd, uid_t user, gid_t group); ssize_t ksys_read(unsigned int fd, char __user *buf, size_t count); diff --git a/init/do_mounts.c b/init/do_mounts.c index a7581c6e85f26..b5f9604d0c98a 100644 --- a/init/do_mounts.c +++ b/init/do_mounts.c @@ -629,7 +629,7 @@ void __init prepare_namespace(void) out: devtmpfs_mount(); init_mount(".", "/", NULL, MS_MOVE, NULL); - ksys_chroot("."); + init_chroot("."); } static bool is_tmpfs; diff --git a/init/do_mounts_initrd.c b/init/do_mounts_initrd.c index 04627fd22a921..a6b447b191dbc 100644 --- a/init/do_mounts_initrd.c +++ b/init/do_mounts_initrd.c @@ -63,7 +63,7 @@ static int __init init_linuxrc(struct subprocess_info *info, struct cred *new) /* move initrd over / and chdir/chroot in initrd root */ init_chdir("/root"); init_mount(".", "/", NULL, MS_MOVE, NULL); - ksys_chroot("."); + init_chroot("."); ksys_setsid(); return 0; } @@ -101,7 +101,7 @@ static void __init handle_initrd(void) /* move initrd to rootfs' /old */ init_mount("..", ".", NULL, MS_MOVE, NULL); /* switch root and cwd back to / of rootfs */ - ksys_chroot(".."); + init_chroot(".."); if (new_decode_dev(real_root_dev) == Root_RAM0) { init_chdir("/old"); -- GitLab From b873498f99c77e7b5be3aa5ffe9ca67437232fe0 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Wed, 22 Jul 2020 11:13:26 +0200 Subject: [PATCH 1212/1476] init: add an init_chown helper Add a simple helper to chown with a kernel space file name and switch the early init code over to it. Signed-off-by: Christoph Hellwig --- fs/init.c | 18 ++++++++++++++++++ fs/internal.h | 2 +- fs/open.c | 2 +- include/linux/init_syscalls.h | 1 + init/initramfs.c | 6 +++--- 5 files changed, 24 insertions(+), 5 deletions(-) diff --git a/fs/init.c b/fs/init.c index 2c78f24814dde..edd0244655956 100644 --- a/fs/init.c +++ b/fs/init.c @@ -78,6 +78,24 @@ dput_and_out: return error; } +int __init init_chown(const char *filename, uid_t user, gid_t group, int flags) +{ + int lookup_flags = (flags & AT_SYMLINK_NOFOLLOW) ? 0 : LOOKUP_FOLLOW; + struct path path; + int error; + + error = kern_path(filename, lookup_flags, &path); + if (error) + return error; + error = mnt_want_write(path.mnt); + if (!error) { + error = chown_common(&path, user, group); + mnt_drop_write(path.mnt); + } + path_put(&path); + return error; +} + int __init init_unlink(const char *pathname) { return do_unlinkat(AT_FDCWD, getname_kernel(pathname)); diff --git a/fs/internal.h b/fs/internal.h index 491d1e63809b3..e81b9e23c3ea3 100644 --- a/fs/internal.h +++ b/fs/internal.h @@ -134,7 +134,7 @@ long do_sys_ftruncate(unsigned int fd, loff_t length, int small); int do_fchmodat(int dfd, const char __user *filename, umode_t mode); int do_fchownat(int dfd, const char __user *filename, uid_t user, gid_t group, int flag); - +int chown_common(const struct path *path, uid_t user, gid_t group); extern int vfs_open(const struct path *, struct file *); /* diff --git a/fs/open.c b/fs/open.c index f62f4752bb436..49960a1248f14 100644 --- a/fs/open.c +++ b/fs/open.c @@ -639,7 +639,7 @@ SYSCALL_DEFINE2(chmod, const char __user *, filename, umode_t, mode) return do_fchmodat(AT_FDCWD, filename, mode); } -static int chown_common(const struct path *path, uid_t user, gid_t group) +int chown_common(const struct path *path, uid_t user, gid_t group) { struct inode *inode = path->dentry->d_inode; struct inode *delegated_inode = NULL; diff --git a/include/linux/init_syscalls.h b/include/linux/init_syscalls.h index e07099a14b91d..0da59d76133e1 100644 --- a/include/linux/init_syscalls.h +++ b/include/linux/init_syscalls.h @@ -5,5 +5,6 @@ int __init init_mount(const char *dev_name, const char *dir_name, int __init init_umount(const char *name, int flags); int __init init_chdir(const char *filename); int __init init_chroot(const char *filename); +int __init init_chown(const char *filename, uid_t user, gid_t group, int flags); int __init init_unlink(const char *pathname); int __init init_rmdir(const char *pathname); diff --git a/init/initramfs.c b/init/initramfs.c index 1a9159bf452fe..358dcd93cb9d8 100644 --- a/init/initramfs.c +++ b/init/initramfs.c @@ -349,14 +349,14 @@ static int __init do_name(void) } } else if (S_ISDIR(mode)) { ksys_mkdir(collected, mode); - ksys_chown(collected, uid, gid); + init_chown(collected, uid, gid, 0); ksys_chmod(collected, mode); dir_add(collected, mtime); } else if (S_ISBLK(mode) || S_ISCHR(mode) || S_ISFIFO(mode) || S_ISSOCK(mode)) { if (maybe_link() == 0) { ksys_mknod(collected, mode, rdev); - ksys_chown(collected, uid, gid); + init_chown(collected, uid, gid, 0); ksys_chmod(collected, mode); do_utime(collected, mtime); } @@ -393,7 +393,7 @@ static int __init do_symlink(void) collected[N_ALIGN(name_len) + body_len] = '\0'; clean_path(collected, 0); ksys_symlink(collected + N_ALIGN(name_len), collected); - ksys_lchown(collected, uid, gid); + init_chown(collected, uid, gid, AT_SYMLINK_NOFOLLOW); do_utime(collected, mtime); state = SkipIt; next_state = Reset; -- GitLab From 1097742efc643ffc667c5c6684635b2663145a7d Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Wed, 22 Jul 2020 11:41:02 +0200 Subject: [PATCH 1213/1476] init: add an init_chmod helper Add a simple helper to chmod with a kernel space file name and switch the early init code over to it. Signed-off-by: Christoph Hellwig --- fs/init.c | 13 +++++++++++++ fs/internal.h | 2 +- fs/open.c | 4 ++-- include/linux/init_syscalls.h | 1 + include/linux/syscalls.h | 7 ------- init/initramfs.c | 4 ++-- 6 files changed, 19 insertions(+), 12 deletions(-) diff --git a/fs/init.c b/fs/init.c index edd0244655956..a66032d128b61 100644 --- a/fs/init.c +++ b/fs/init.c @@ -96,6 +96,19 @@ int __init init_chown(const char *filename, uid_t user, gid_t group, int flags) return error; } +int __init init_chmod(const char *filename, umode_t mode) +{ + struct path path; + int error; + + error = kern_path(filename, LOOKUP_FOLLOW, &path); + if (error) + return error; + error = chmod_common(&path, mode); + path_put(&path); + return error; +} + int __init init_unlink(const char *pathname) { return do_unlinkat(AT_FDCWD, getname_kernel(pathname)); diff --git a/fs/internal.h b/fs/internal.h index e81b9e23c3ea3..6d82681c7d837 100644 --- a/fs/internal.h +++ b/fs/internal.h @@ -131,7 +131,7 @@ extern struct open_how build_open_how(int flags, umode_t mode); extern int build_open_flags(const struct open_how *how, struct open_flags *op); long do_sys_ftruncate(unsigned int fd, loff_t length, int small); -int do_fchmodat(int dfd, const char __user *filename, umode_t mode); +int chmod_common(const struct path *path, umode_t mode); int do_fchownat(int dfd, const char __user *filename, uid_t user, gid_t group, int flag); int chown_common(const struct path *path, uid_t user, gid_t group); diff --git a/fs/open.c b/fs/open.c index 49960a1248f14..7ba89eae46c56 100644 --- a/fs/open.c +++ b/fs/open.c @@ -563,7 +563,7 @@ out: return error; } -static int chmod_common(const struct path *path, umode_t mode) +int chmod_common(const struct path *path, umode_t mode) { struct inode *inode = path->dentry->d_inode; struct inode *delegated_inode = NULL; @@ -610,7 +610,7 @@ SYSCALL_DEFINE2(fchmod, unsigned int, fd, umode_t, mode) return err; } -int do_fchmodat(int dfd, const char __user *filename, umode_t mode) +static int do_fchmodat(int dfd, const char __user *filename, umode_t mode) { struct path path; int error; diff --git a/include/linux/init_syscalls.h b/include/linux/init_syscalls.h index 0da59d76133e1..2b1b4dc586825 100644 --- a/include/linux/init_syscalls.h +++ b/include/linux/init_syscalls.h @@ -6,5 +6,6 @@ int __init init_umount(const char *name, int flags); int __init init_chdir(const char *filename); int __init init_chroot(const char *filename); int __init init_chown(const char *filename, uid_t user, gid_t group, int flags); +int __init init_chmod(const char *filename, umode_t mode); int __init init_unlink(const char *pathname); int __init init_rmdir(const char *pathname); diff --git a/include/linux/syscalls.h b/include/linux/syscalls.h index e89d62e944dc0..8b71fa321ca20 100644 --- a/include/linux/syscalls.h +++ b/include/linux/syscalls.h @@ -1304,13 +1304,6 @@ static inline long ksys_link(const char __user *oldname, return do_linkat(AT_FDCWD, oldname, AT_FDCWD, newname, 0); } -extern int do_fchmodat(int dfd, const char __user *filename, umode_t mode); - -static inline int ksys_chmod(const char __user *filename, umode_t mode) -{ - return do_fchmodat(AT_FDCWD, filename, mode); -} - long do_faccessat(int dfd, const char __user *filename, int mode, int flags); static inline long ksys_access(const char __user *filename, int mode) diff --git a/init/initramfs.c b/init/initramfs.c index 358dcd93cb9d8..efa9fb70038df 100644 --- a/init/initramfs.c +++ b/init/initramfs.c @@ -350,14 +350,14 @@ static int __init do_name(void) } else if (S_ISDIR(mode)) { ksys_mkdir(collected, mode); init_chown(collected, uid, gid, 0); - ksys_chmod(collected, mode); + init_chmod(collected, mode); dir_add(collected, mtime); } else if (S_ISBLK(mode) || S_ISCHR(mode) || S_ISFIFO(mode) || S_ISSOCK(mode)) { if (maybe_link() == 0) { ksys_mknod(collected, mode, rdev); init_chown(collected, uid, gid, 0); - ksys_chmod(collected, mode); + init_chmod(collected, mode); do_utime(collected, mtime); } } -- GitLab From eb9d7d390e51108b4c6a9a7993ed9be92548c8f7 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Wed, 22 Jul 2020 11:14:02 +0200 Subject: [PATCH 1214/1476] init: add an init_eaccess helper Add a simple helper to check if a file exists based on kernel space file name and switch the early init code over to it. Note that this theoretically changes behavior as it always is based on the effective permissions. But during early init that doesn't make a difference. Signed-off-by: Christoph Hellwig --- fs/init.c | 13 +++++++++++++ fs/open.c | 2 +- include/linux/init_syscalls.h | 1 + include/linux/syscalls.h | 7 ------- init/main.c | 4 ++-- 5 files changed, 17 insertions(+), 10 deletions(-) diff --git a/fs/init.c b/fs/init.c index a66032d128b61..6d9af40d2897b 100644 --- a/fs/init.c +++ b/fs/init.c @@ -109,6 +109,19 @@ int __init init_chmod(const char *filename, umode_t mode) return error; } +int __init init_eaccess(const char *filename) +{ + struct path path; + int error; + + error = kern_path(filename, LOOKUP_FOLLOW, &path); + if (error) + return error; + error = inode_permission(d_inode(path.dentry), MAY_ACCESS); + path_put(&path); + return error; +} + int __init init_unlink(const char *pathname) { return do_unlinkat(AT_FDCWD, getname_kernel(pathname)); diff --git a/fs/open.c b/fs/open.c index 7ba89eae46c56..aafecd1f7ba1a 100644 --- a/fs/open.c +++ b/fs/open.c @@ -394,7 +394,7 @@ static const struct cred *access_override_creds(void) return old_cred; } -long do_faccessat(int dfd, const char __user *filename, int mode, int flags) +static long do_faccessat(int dfd, const char __user *filename, int mode, int flags) { struct path path; struct inode *inode; diff --git a/include/linux/init_syscalls.h b/include/linux/init_syscalls.h index 2b1b4dc586825..7031c0934bee9 100644 --- a/include/linux/init_syscalls.h +++ b/include/linux/init_syscalls.h @@ -7,5 +7,6 @@ int __init init_chdir(const char *filename); int __init init_chroot(const char *filename); int __init init_chown(const char *filename, uid_t user, gid_t group, int flags); int __init init_chmod(const char *filename, umode_t mode); +int __init init_eaccess(const char *filename); int __init init_unlink(const char *pathname); int __init init_rmdir(const char *pathname); diff --git a/include/linux/syscalls.h b/include/linux/syscalls.h index 8b71fa321ca20..a2779638e4144 100644 --- a/include/linux/syscalls.h +++ b/include/linux/syscalls.h @@ -1304,13 +1304,6 @@ static inline long ksys_link(const char __user *oldname, return do_linkat(AT_FDCWD, oldname, AT_FDCWD, newname, 0); } -long do_faccessat(int dfd, const char __user *filename, int mode, int flags); - -static inline long ksys_access(const char __user *filename, int mode) -{ - return do_faccessat(AT_FDCWD, filename, mode, 0); -} - extern int do_fchownat(int dfd, const char __user *filename, uid_t user, gid_t group, int flag); diff --git a/init/main.c b/init/main.c index 47698427b15f6..1c710d3e1d461 100644 --- a/init/main.c +++ b/init/main.c @@ -96,6 +96,7 @@ #include #include #include +#include #include #include @@ -1514,8 +1515,7 @@ static noinline void __init kernel_init_freeable(void) * check if there is an early userspace init. If yes, let it do all * the work */ - if (ksys_access((const char __user *) - ramdisk_execute_command, 0) != 0) { + if (init_eaccess(ramdisk_execute_command) != 0) { ramdisk_execute_command = NULL; prepare_namespace(); } -- GitLab From 812931d693da58cc24d2bb8dec01c2b4a7f4668f Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Wed, 22 Jul 2020 11:14:19 +0200 Subject: [PATCH 1215/1476] init: add an init_link helper Add a simple helper to link with a kernel space file name and switch the early init code over to it. Remove the now unused ksys_link. Signed-off-by: Christoph Hellwig --- fs/init.c | 33 +++++++++++++++++++++++++++++++++ fs/internal.h | 3 +-- fs/namei.c | 4 ++-- include/linux/init_syscalls.h | 1 + include/linux/syscalls.h | 9 --------- init/initramfs.c | 2 +- 6 files changed, 38 insertions(+), 14 deletions(-) diff --git a/fs/init.c b/fs/init.c index 6d9af40d2897b..5db9d9f74868e 100644 --- a/fs/init.c +++ b/fs/init.c @@ -122,6 +122,39 @@ int __init init_eaccess(const char *filename) return error; } +int __init init_link(const char *oldname, const char *newname) +{ + struct dentry *new_dentry; + struct path old_path, new_path; + int error; + + error = kern_path(oldname, 0, &old_path); + if (error) + return error; + + new_dentry = kern_path_create(AT_FDCWD, newname, &new_path, 0); + error = PTR_ERR(new_dentry); + if (IS_ERR(new_dentry)) + goto out; + + error = -EXDEV; + if (old_path.mnt != new_path.mnt) + goto out_dput; + error = may_linkat(&old_path); + if (unlikely(error)) + goto out_dput; + error = security_path_link(old_path.dentry, &new_path, new_dentry); + if (error) + goto out_dput; + error = vfs_link(old_path.dentry, new_path.dentry->d_inode, new_dentry, + NULL); +out_dput: + done_path_create(&new_path, new_dentry); +out: + path_put(&old_path); + return error; +} + int __init init_unlink(const char *pathname) { return do_unlinkat(AT_FDCWD, getname_kernel(pathname)); diff --git a/fs/internal.h b/fs/internal.h index 6d82681c7d837..58451b033d269 100644 --- a/fs/internal.h +++ b/fs/internal.h @@ -69,8 +69,7 @@ long do_rmdir(int dfd, struct filename *name); long do_unlinkat(int dfd, struct filename *name); long do_symlinkat(const char __user *oldname, int newdfd, const char __user *newname); -int do_linkat(int olddfd, const char __user *oldname, int newdfd, - const char __user *newname, int flags); +int may_linkat(struct path *link); /* * namespace.c diff --git a/fs/namei.c b/fs/namei.c index d75a6039ae396..13de64c6be764 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -1024,7 +1024,7 @@ static bool safe_hardlink_source(struct inode *inode) * * Returns 0 if successful, -ve on error. */ -static int may_linkat(struct path *link) +int may_linkat(struct path *link) { struct inode *inode = link->dentry->d_inode; @@ -4086,7 +4086,7 @@ EXPORT_SYMBOL(vfs_link); * with linux 2.0, and to avoid hard-linking to directories * and other special files. --ADM */ -int do_linkat(int olddfd, const char __user *oldname, int newdfd, +static int do_linkat(int olddfd, const char __user *oldname, int newdfd, const char __user *newname, int flags) { struct dentry *new_dentry; diff --git a/include/linux/init_syscalls.h b/include/linux/init_syscalls.h index 7031c0934bee9..5ca15a5b55b7d 100644 --- a/include/linux/init_syscalls.h +++ b/include/linux/init_syscalls.h @@ -8,5 +8,6 @@ int __init init_chroot(const char *filename); int __init init_chown(const char *filename, uid_t user, gid_t group, int flags); int __init init_chmod(const char *filename, umode_t mode); int __init init_eaccess(const char *filename); +int __init init_link(const char *oldname, const char *newname); int __init init_unlink(const char *pathname); int __init init_rmdir(const char *pathname); diff --git a/include/linux/syscalls.h b/include/linux/syscalls.h index a2779638e4144..4b18b91ce4657 100644 --- a/include/linux/syscalls.h +++ b/include/linux/syscalls.h @@ -1295,15 +1295,6 @@ static inline long ksys_mknod(const char __user *filename, umode_t mode, return do_mknodat(AT_FDCWD, filename, mode, dev); } -extern int do_linkat(int olddfd, const char __user *oldname, int newdfd, - const char __user *newname, int flags); - -static inline long ksys_link(const char __user *oldname, - const char __user *newname) -{ - return do_linkat(AT_FDCWD, oldname, AT_FDCWD, newname, 0); -} - extern int do_fchownat(int dfd, const char __user *filename, uid_t user, gid_t group, int flag); diff --git a/init/initramfs.c b/init/initramfs.c index efa9fb70038df..516a66900d7ad 100644 --- a/init/initramfs.c +++ b/init/initramfs.c @@ -312,7 +312,7 @@ static int __init maybe_link(void) char *old = find_link(major, minor, ino, mode, collected); if (old) { clean_path(collected, 0); - return (ksys_link(old, collected) < 0) ? -1 : 1; + return (init_link(old, collected) < 0) ? -1 : 1; } } return 0; -- GitLab From cd3acb6a79349f346714ab3d26d203a0c6ca5ab0 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Wed, 22 Jul 2020 11:14:36 +0200 Subject: [PATCH 1216/1476] init: add an init_symlink helper Add a simple helper to symlink with a kernel space file name and switch the early init code over to it. Remove the now unused ksys_symlink. Signed-off-by: Christoph Hellwig --- fs/init.c | 16 ++++++++++++++++ fs/internal.h | 2 -- fs/namei.c | 2 +- include/linux/init_syscalls.h | 1 + include/linux/syscalls.h | 9 --------- init/initramfs.c | 2 +- 6 files changed, 19 insertions(+), 13 deletions(-) diff --git a/fs/init.c b/fs/init.c index 5db9d9f74868e..09ef2b58d48ca 100644 --- a/fs/init.c +++ b/fs/init.c @@ -155,6 +155,22 @@ out: return error; } +int __init init_symlink(const char *oldname, const char *newname) +{ + struct dentry *dentry; + struct path path; + int error; + + dentry = kern_path_create(AT_FDCWD, newname, &path, 0); + if (IS_ERR(dentry)) + return PTR_ERR(dentry); + error = security_path_symlink(&path, dentry, oldname); + if (!error) + error = vfs_symlink(path.dentry->d_inode, dentry, oldname); + done_path_create(&path, dentry); + return error; +} + int __init init_unlink(const char *pathname) { return do_unlinkat(AT_FDCWD, getname_kernel(pathname)); diff --git a/fs/internal.h b/fs/internal.h index 58451b033d269..40b50a222d7a2 100644 --- a/fs/internal.h +++ b/fs/internal.h @@ -67,8 +67,6 @@ long do_mknodat(int dfd, const char __user *filename, umode_t mode, long do_mkdirat(int dfd, const char __user *pathname, umode_t mode); long do_rmdir(int dfd, struct filename *name); long do_unlinkat(int dfd, struct filename *name); -long do_symlinkat(const char __user *oldname, int newdfd, - const char __user *newname); int may_linkat(struct path *link); /* diff --git a/fs/namei.c b/fs/namei.c index 13de64c6be764..2f6fa53eb3da2 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -3955,7 +3955,7 @@ int vfs_symlink(struct inode *dir, struct dentry *dentry, const char *oldname) } EXPORT_SYMBOL(vfs_symlink); -long do_symlinkat(const char __user *oldname, int newdfd, +static long do_symlinkat(const char __user *oldname, int newdfd, const char __user *newname) { int error; diff --git a/include/linux/init_syscalls.h b/include/linux/init_syscalls.h index 5ca15a5b55b7d..125f55ae3f80b 100644 --- a/include/linux/init_syscalls.h +++ b/include/linux/init_syscalls.h @@ -9,5 +9,6 @@ int __init init_chown(const char *filename, uid_t user, gid_t group, int flags); int __init init_chmod(const char *filename, umode_t mode); int __init init_eaccess(const char *filename); int __init init_link(const char *oldname, const char *newname); +int __init init_symlink(const char *oldname, const char *newname); int __init init_unlink(const char *pathname); int __init init_rmdir(const char *pathname); diff --git a/include/linux/syscalls.h b/include/linux/syscalls.h index 4b18b91ce4657..7cdc0d749a049 100644 --- a/include/linux/syscalls.h +++ b/include/linux/syscalls.h @@ -1277,15 +1277,6 @@ static inline long ksys_mkdir(const char __user *pathname, umode_t mode) return do_mkdirat(AT_FDCWD, pathname, mode); } -extern long do_symlinkat(const char __user *oldname, int newdfd, - const char __user *newname); - -static inline long ksys_symlink(const char __user *oldname, - const char __user *newname) -{ - return do_symlinkat(oldname, AT_FDCWD, newname); -} - extern long do_mknodat(int dfd, const char __user *filename, umode_t mode, unsigned int dev); diff --git a/init/initramfs.c b/init/initramfs.c index 516a66900d7ad..c91fc9a51d2a7 100644 --- a/init/initramfs.c +++ b/init/initramfs.c @@ -392,7 +392,7 @@ static int __init do_symlink(void) { collected[N_ALIGN(name_len) + body_len] = '\0'; clean_path(collected, 0); - ksys_symlink(collected + N_ALIGN(name_len), collected); + init_symlink(collected + N_ALIGN(name_len), collected); init_chown(collected, uid, gid, AT_SYMLINK_NOFOLLOW); do_utime(collected, mtime); state = SkipIt; -- GitLab From 83ff98c3e9cd2b82b4289e185f2ce7d635a9cbd3 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Wed, 22 Jul 2020 11:14:59 +0200 Subject: [PATCH 1217/1476] init: add an init_mkdir helper Add a simple helper to mkdir with a kernel space file name and switch the early init code over to it. Remove the now unused ksys_mkdir. Signed-off-by: Christoph Hellwig --- fs/init.c | 18 ++++++++++++++++++ fs/internal.h | 1 - fs/namei.c | 2 +- include/linux/init_syscalls.h | 1 + include/linux/syscalls.h | 7 ------- init/do_mounts_initrd.c | 2 +- init/initramfs.c | 2 +- init/noinitramfs.c | 5 +++-- 8 files changed, 25 insertions(+), 13 deletions(-) diff --git a/fs/init.c b/fs/init.c index 09ef2b58d48ca..127033d084260 100644 --- a/fs/init.c +++ b/fs/init.c @@ -176,6 +176,24 @@ int __init init_unlink(const char *pathname) return do_unlinkat(AT_FDCWD, getname_kernel(pathname)); } +int __init init_mkdir(const char *pathname, umode_t mode) +{ + struct dentry *dentry; + struct path path; + int error; + + dentry = kern_path_create(AT_FDCWD, pathname, &path, LOOKUP_DIRECTORY); + if (IS_ERR(dentry)) + return PTR_ERR(dentry); + if (!IS_POSIXACL(path.dentry->d_inode)) + mode &= ~current_umask(); + error = security_path_mkdir(&path, dentry, mode); + if (!error) + error = vfs_mkdir(path.dentry->d_inode, dentry, mode); + done_path_create(&path, dentry); + return error; +} + int __init init_rmdir(const char *pathname) { return do_rmdir(AT_FDCWD, getname_kernel(pathname)); diff --git a/fs/internal.h b/fs/internal.h index 40b50a222d7a2..4741e591e923b 100644 --- a/fs/internal.h +++ b/fs/internal.h @@ -64,7 +64,6 @@ extern int vfs_path_lookup(struct dentry *, struct vfsmount *, const char *, unsigned int, struct path *); long do_mknodat(int dfd, const char __user *filename, umode_t mode, unsigned int dev); -long do_mkdirat(int dfd, const char __user *pathname, umode_t mode); long do_rmdir(int dfd, struct filename *name); long do_unlinkat(int dfd, struct filename *name); int may_linkat(struct path *link); diff --git a/fs/namei.c b/fs/namei.c index 2f6fa53eb3da2..d6b25dd32f4d5 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -3645,7 +3645,7 @@ int vfs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode) } EXPORT_SYMBOL(vfs_mkdir); -long do_mkdirat(int dfd, const char __user *pathname, umode_t mode) +static long do_mkdirat(int dfd, const char __user *pathname, umode_t mode) { struct dentry *dentry; struct path path; diff --git a/include/linux/init_syscalls.h b/include/linux/init_syscalls.h index 125f55ae3f80b..d808985231f8f 100644 --- a/include/linux/init_syscalls.h +++ b/include/linux/init_syscalls.h @@ -11,4 +11,5 @@ int __init init_eaccess(const char *filename); int __init init_link(const char *oldname, const char *newname); int __init init_symlink(const char *oldname, const char *newname); int __init init_unlink(const char *pathname); +int __init init_mkdir(const char *pathname, umode_t mode); int __init init_rmdir(const char *pathname); diff --git a/include/linux/syscalls.h b/include/linux/syscalls.h index 7cdc0d749a049..5ef77a91382aa 100644 --- a/include/linux/syscalls.h +++ b/include/linux/syscalls.h @@ -1270,13 +1270,6 @@ int compat_ksys_ipc(u32 call, int first, int second, * The following kernel syscall equivalents are just wrappers to fs-internal * functions. Therefore, provide stubs to be inlined at the callsites. */ -extern long do_mkdirat(int dfd, const char __user *pathname, umode_t mode); - -static inline long ksys_mkdir(const char __user *pathname, umode_t mode) -{ - return do_mkdirat(AT_FDCWD, pathname, mode); -} - extern long do_mknodat(int dfd, const char __user *filename, umode_t mode, unsigned int dev); diff --git a/init/do_mounts_initrd.c b/init/do_mounts_initrd.c index a6b447b191dbc..3f5ac81913dde 100644 --- a/init/do_mounts_initrd.c +++ b/init/do_mounts_initrd.c @@ -81,7 +81,7 @@ static void __init handle_initrd(void) create_dev("/dev/root.old", Root_RAM0); /* mount initrd on rootfs' /root */ mount_block_root("/dev/root.old", root_mountflags & ~MS_RDONLY); - ksys_mkdir("/old", 0700); + init_mkdir("/old", 0700); init_chdir("/old"); /* diff --git a/init/initramfs.c b/init/initramfs.c index c91fc9a51d2a7..0489eb65b3b8d 100644 --- a/init/initramfs.c +++ b/init/initramfs.c @@ -348,7 +348,7 @@ static int __init do_name(void) state = CopyFile; } } else if (S_ISDIR(mode)) { - ksys_mkdir(collected, mode); + init_mkdir(collected, mode); init_chown(collected, uid, gid, 0); init_chmod(collected, mode); dir_add(collected, mtime); diff --git a/init/noinitramfs.c b/init/noinitramfs.c index fa9cdfa7101d3..94cc4df74b11f 100644 --- a/init/noinitramfs.c +++ b/init/noinitramfs.c @@ -9,6 +9,7 @@ #include #include #include +#include /* * Create a simple rootfs that is similar to the default initramfs @@ -17,7 +18,7 @@ static int __init default_rootfs(void) { int err; - err = ksys_mkdir((const char __user __force *) "/dev", 0755); + err = init_mkdir("/dev", 0755); if (err < 0) goto out; @@ -27,7 +28,7 @@ static int __init default_rootfs(void) if (err < 0) goto out; - err = ksys_mkdir((const char __user __force *) "/root", 0700); + err = init_mkdir("/root", 0700); if (err < 0) goto out; -- GitLab From 5fee64fcde0770c41e926ff981022eaa512d8980 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Wed, 22 Jul 2020 11:41:20 +0200 Subject: [PATCH 1218/1476] init: add an init_mknod helper Add a simple helper to mknod with a kernel space file name and switch the early init code over to it. Remove the now unused ksys_mknod. Signed-off-by: Christoph Hellwig --- fs/init.c | 25 +++++++++++++++++++++++++ fs/internal.h | 2 -- fs/namei.c | 2 +- include/linux/init_syscalls.h | 1 + include/linux/syscalls.h | 9 --------- init/do_mounts.h | 2 +- init/initramfs.c | 2 +- init/noinitramfs.c | 3 +-- 8 files changed, 30 insertions(+), 16 deletions(-) diff --git a/fs/init.c b/fs/init.c index 127033d084260..145fb31b7a5f2 100644 --- a/fs/init.c +++ b/fs/init.c @@ -122,6 +122,31 @@ int __init init_eaccess(const char *filename) return error; } +int __init init_mknod(const char *filename, umode_t mode, unsigned int dev) +{ + struct dentry *dentry; + struct path path; + int error; + + if (S_ISFIFO(mode) || S_ISSOCK(mode)) + dev = 0; + else if (!(S_ISBLK(mode) || S_ISCHR(mode))) + return -EINVAL; + + dentry = kern_path_create(AT_FDCWD, filename, &path, 0); + if (IS_ERR(dentry)) + return PTR_ERR(dentry); + + if (!IS_POSIXACL(path.dentry->d_inode)) + mode &= ~current_umask(); + error = security_path_mknod(&path, dentry, mode, dev); + if (!error) + error = vfs_mknod(path.dentry->d_inode, dentry, mode, + new_decode_dev(dev)); + done_path_create(&path, dentry); + return error; +} + int __init init_link(const char *oldname, const char *newname) { struct dentry *new_dentry; diff --git a/fs/internal.h b/fs/internal.h index 4741e591e923b..07e145b2f88c4 100644 --- a/fs/internal.h +++ b/fs/internal.h @@ -62,8 +62,6 @@ extern int filename_lookup(int dfd, struct filename *name, unsigned flags, struct path *path, struct path *root); extern int vfs_path_lookup(struct dentry *, struct vfsmount *, const char *, unsigned int, struct path *); -long do_mknodat(int dfd, const char __user *filename, umode_t mode, - unsigned int dev); long do_rmdir(int dfd, struct filename *name); long do_unlinkat(int dfd, struct filename *name); int may_linkat(struct path *link); diff --git a/fs/namei.c b/fs/namei.c index d6b25dd32f4d5..fde8fe086c090 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -3564,7 +3564,7 @@ static int may_mknod(umode_t mode) } } -long do_mknodat(int dfd, const char __user *filename, umode_t mode, +static long do_mknodat(int dfd, const char __user *filename, umode_t mode, unsigned int dev) { struct dentry *dentry; diff --git a/include/linux/init_syscalls.h b/include/linux/init_syscalls.h index d808985231f8f..fa1fe7a877795 100644 --- a/include/linux/init_syscalls.h +++ b/include/linux/init_syscalls.h @@ -8,6 +8,7 @@ int __init init_chroot(const char *filename); int __init init_chown(const char *filename, uid_t user, gid_t group, int flags); int __init init_chmod(const char *filename, umode_t mode); int __init init_eaccess(const char *filename); +int __init init_mknod(const char *filename, umode_t mode, unsigned int dev); int __init init_link(const char *oldname, const char *newname); int __init init_symlink(const char *oldname, const char *newname); int __init init_unlink(const char *pathname); diff --git a/include/linux/syscalls.h b/include/linux/syscalls.h index 5ef77a91382aa..63046c5e9fc5d 100644 --- a/include/linux/syscalls.h +++ b/include/linux/syscalls.h @@ -1270,15 +1270,6 @@ int compat_ksys_ipc(u32 call, int first, int second, * The following kernel syscall equivalents are just wrappers to fs-internal * functions. Therefore, provide stubs to be inlined at the callsites. */ -extern long do_mknodat(int dfd, const char __user *filename, umode_t mode, - unsigned int dev); - -static inline long ksys_mknod(const char __user *filename, umode_t mode, - unsigned int dev) -{ - return do_mknodat(AT_FDCWD, filename, mode, dev); -} - extern int do_fchownat(int dfd, const char __user *filename, uid_t user, gid_t group, int flag); diff --git a/init/do_mounts.h b/init/do_mounts.h index 104d8431725ae..7a29ac3e427ba 100644 --- a/init/do_mounts.h +++ b/init/do_mounts.h @@ -17,7 +17,7 @@ extern int root_mountflags; static inline __init int create_dev(char *name, dev_t dev) { init_unlink(name); - return ksys_mknod(name, S_IFBLK|0600, new_encode_dev(dev)); + return init_mknod(name, S_IFBLK | 0600, new_encode_dev(dev)); } #ifdef CONFIG_BLK_DEV_RAM diff --git a/init/initramfs.c b/init/initramfs.c index 0489eb65b3b8d..425addaf7c694 100644 --- a/init/initramfs.c +++ b/init/initramfs.c @@ -355,7 +355,7 @@ static int __init do_name(void) } else if (S_ISBLK(mode) || S_ISCHR(mode) || S_ISFIFO(mode) || S_ISSOCK(mode)) { if (maybe_link() == 0) { - ksys_mknod(collected, mode, rdev); + init_mknod(collected, mode, rdev); init_chown(collected, uid, gid, 0); init_chmod(collected, mode); do_utime(collected, mtime); diff --git a/init/noinitramfs.c b/init/noinitramfs.c index 94cc4df74b11f..3d62b07f3bb9c 100644 --- a/init/noinitramfs.c +++ b/init/noinitramfs.c @@ -22,8 +22,7 @@ static int __init default_rootfs(void) if (err < 0) goto out; - err = ksys_mknod((const char __user __force *) "/dev/console", - S_IFCHR | S_IRUSR | S_IWUSR, + err = init_mknod("/dev/console", S_IFCHR | S_IRUSR | S_IWUSR, new_encode_dev(MKDEV(5, 1))); if (err < 0) goto out; -- GitLab From 716308a5331bf907b819f9db8dc942b19568f925 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Wed, 22 Jul 2020 11:15:40 +0200 Subject: [PATCH 1219/1476] init: add an init_stat helper Add a simple helper to stat with a kernel space file name and switch the early init code over to it. Signed-off-by: Christoph Hellwig --- drivers/md/md-autodetect.c | 3 ++- fs/init.c | 15 +++++++++++++++ include/linux/init_syscalls.h | 1 + init/initramfs.c | 3 ++- 4 files changed, 20 insertions(+), 2 deletions(-) diff --git a/drivers/md/md-autodetect.c b/drivers/md/md-autodetect.c index 14b6e86814c06..6bbec89976a74 100644 --- a/drivers/md/md-autodetect.c +++ b/drivers/md/md-autodetect.c @@ -5,6 +5,7 @@ #include #include #include +#include #include #include #include @@ -151,7 +152,7 @@ static void __init md_setup_drive(struct md_setup_args *args) if (strncmp(devname, "/dev/", 5) == 0) devname += 5; snprintf(comp_name, 63, "/dev/%s", devname); - if (vfs_stat(comp_name, &stat) == 0 && S_ISBLK(stat.mode)) + if (init_stat(comp_name, &stat, 0) == 0 && S_ISBLK(stat.mode)) dev = new_decode_dev(stat.rdev); if (!dev) { pr_warn("md: Unknown device name: %s\n", devname); diff --git a/fs/init.c b/fs/init.c index 145fb31b7a5f2..51646ba38099e 100644 --- a/fs/init.c +++ b/fs/init.c @@ -122,6 +122,21 @@ int __init init_eaccess(const char *filename) return error; } +int __init init_stat(const char *filename, struct kstat *stat, int flags) +{ + int lookup_flags = (flags & AT_SYMLINK_NOFOLLOW) ? 0 : LOOKUP_FOLLOW; + struct path path; + int error; + + error = kern_path(filename, lookup_flags, &path); + if (error) + return error; + error = vfs_getattr(&path, stat, STATX_BASIC_STATS, + flags | AT_NO_AUTOMOUNT); + path_put(&path); + return error; +} + int __init init_mknod(const char *filename, umode_t mode, unsigned int dev) { struct dentry *dentry; diff --git a/include/linux/init_syscalls.h b/include/linux/init_syscalls.h index fa1fe7a877795..b2fda50daca6c 100644 --- a/include/linux/init_syscalls.h +++ b/include/linux/init_syscalls.h @@ -8,6 +8,7 @@ int __init init_chroot(const char *filename); int __init init_chown(const char *filename, uid_t user, gid_t group, int flags); int __init init_chmod(const char *filename, umode_t mode); int __init init_eaccess(const char *filename); +int __init init_stat(const char *filename, struct kstat *stat, int flags); int __init init_mknod(const char *filename, umode_t mode, unsigned int dev); int __init init_link(const char *oldname, const char *newname); int __init init_symlink(const char *oldname, const char *newname); diff --git a/init/initramfs.c b/init/initramfs.c index 425addaf7c694..744e111baba41 100644 --- a/init/initramfs.c +++ b/init/initramfs.c @@ -298,7 +298,8 @@ static void __init clean_path(char *path, umode_t fmode) { struct kstat st; - if (!vfs_lstat(path, &st) && (st.mode ^ fmode) & S_IFMT) { + if (init_stat(path, &st, AT_SYMLINK_NOFOLLOW) && + (st.mode ^ fmode) & S_IFMT) { if (S_ISDIR(st.mode)) init_rmdir(path); else -- GitLab From 235e57935bf328c4cce371ffc4dd1d8fab4885cd Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Tue, 21 Jul 2020 16:05:31 +0200 Subject: [PATCH 1220/1476] init: add an init_utimes helper Add a simple helper to set timestamps with a kernel space file name and switch the early init code over to it. Signed-off-by: Christoph Hellwig --- fs/init.c | 13 +++++++++++++ include/linux/init_syscalls.h | 1 + init/initramfs.c | 3 +-- 3 files changed, 15 insertions(+), 2 deletions(-) diff --git a/fs/init.c b/fs/init.c index 51646ba38099e..db5c48a85644f 100644 --- a/fs/init.c +++ b/fs/init.c @@ -238,3 +238,16 @@ int __init init_rmdir(const char *pathname) { return do_rmdir(AT_FDCWD, getname_kernel(pathname)); } + +int __init init_utimes(char *filename, struct timespec64 *ts) +{ + struct path path; + int error; + + error = kern_path(filename, 0, &path); + if (error) + return error; + error = vfs_utimes(&path, ts); + path_put(&path); + return error; +} diff --git a/include/linux/init_syscalls.h b/include/linux/init_syscalls.h index b2fda50daca6c..3654b525ac0b1 100644 --- a/include/linux/init_syscalls.h +++ b/include/linux/init_syscalls.h @@ -15,3 +15,4 @@ int __init init_symlink(const char *oldname, const char *newname); int __init init_unlink(const char *pathname); int __init init_mkdir(const char *pathname, umode_t mode); int __init init_rmdir(const char *pathname); +int __init init_utimes(char *filename, struct timespec64 *ts); diff --git a/init/initramfs.c b/init/initramfs.c index 744e111baba41..e6dbfb767057d 100644 --- a/init/initramfs.c +++ b/init/initramfs.c @@ -111,8 +111,7 @@ static long __init do_utime(char *filename, time64_t mtime) t[0].tv_nsec = 0; t[1].tv_sec = mtime; t[1].tv_nsec = 0; - - return do_utimes(AT_FDCWD, filename, t, AT_SYMLINK_NOFOLLOW); + return init_utimes(filename, t); } static __initdata LIST_HEAD(dir_list); -- GitLab From 46cbd0b05799e8234b719d18f3a4b27679c4c92e Mon Sep 17 00:00:00 2001 From: Crag Wang Date: Thu, 30 Jul 2020 11:26:09 +0800 Subject: [PATCH 1221/1476] power: supply: wilco_ec: Add long life charging mode This is a long life mode set in the factory for extended warranty battery, the power charging rate is customized so that battery at work last longer. Presently switching to a different battery charging mode is through EC PID 0x0710 to configure the battery firmware, this operation will be blocked by EC with failure code 0x01 when PLL mode is already in use. Signed-off-by: Crag Wang Reviewed-by: Mario Limonciello Signed-off-by: Sebastian Reichel --- Documentation/ABI/testing/sysfs-class-power-wilco | 4 ++++ drivers/power/supply/power_supply_sysfs.c | 1 + drivers/power/supply/wilco-charger.c | 5 +++++ include/linux/power_supply.h | 1 + 4 files changed, 11 insertions(+) diff --git a/Documentation/ABI/testing/sysfs-class-power-wilco b/Documentation/ABI/testing/sysfs-class-power-wilco index da1d6ffe5e3ce..84fde1d0ada06 100644 --- a/Documentation/ABI/testing/sysfs-class-power-wilco +++ b/Documentation/ABI/testing/sysfs-class-power-wilco @@ -14,6 +14,10 @@ Description: Charging begins when level drops below charge_control_start_threshold, and ceases when level is above charge_control_end_threshold. + Long Life: Customized charge rate for last longer battery life. + On Wilco device this mode is pre-configured in the factory + through EC's private PID. Swiching to a different mode will + be denied by Wilco EC when Long Life mode is enabled. What: /sys/class/power_supply/wilco-charger/charge_control_start_threshold Date: April 2019 diff --git a/drivers/power/supply/power_supply_sysfs.c b/drivers/power/supply/power_supply_sysfs.c index b903cb4dca2be..3d383086018c5 100644 --- a/drivers/power/supply/power_supply_sysfs.c +++ b/drivers/power/supply/power_supply_sysfs.c @@ -87,6 +87,7 @@ static const char * const POWER_SUPPLY_CHARGE_TYPE_TEXT[] = { [POWER_SUPPLY_CHARGE_TYPE_STANDARD] = "Standard", [POWER_SUPPLY_CHARGE_TYPE_ADAPTIVE] = "Adaptive", [POWER_SUPPLY_CHARGE_TYPE_CUSTOM] = "Custom", + [POWER_SUPPLY_CHARGE_TYPE_LONGLIFE] = "Long Life", }; static const char * const POWER_SUPPLY_HEALTH_TEXT[] = { diff --git a/drivers/power/supply/wilco-charger.c b/drivers/power/supply/wilco-charger.c index b3c6d7cdd7318..98ade073ef05f 100644 --- a/drivers/power/supply/wilco-charger.c +++ b/drivers/power/supply/wilco-charger.c @@ -27,6 +27,7 @@ enum charge_mode { CHARGE_MODE_AC = 3, /* Mostly AC use, used for Trickle */ CHARGE_MODE_AUTO = 4, /* Used for Adaptive */ CHARGE_MODE_CUSTOM = 5, /* Used for Custom */ + CHARGE_MODE_LONGLIFE = 6, /* Used for Long Life */ }; #define CHARGE_LOWER_LIMIT_MIN 50 @@ -48,6 +49,8 @@ static int psp_val_to_charge_mode(int psp_val) return CHARGE_MODE_AUTO; case POWER_SUPPLY_CHARGE_TYPE_CUSTOM: return CHARGE_MODE_CUSTOM; + case POWER_SUPPLY_CHARGE_TYPE_LONGLIFE: + return CHARGE_MODE_LONGLIFE; default: return -EINVAL; } @@ -67,6 +70,8 @@ static int charge_mode_to_psp_val(enum charge_mode mode) return POWER_SUPPLY_CHARGE_TYPE_ADAPTIVE; case CHARGE_MODE_CUSTOM: return POWER_SUPPLY_CHARGE_TYPE_CUSTOM; + case CHARGE_MODE_LONGLIFE: + return POWER_SUPPLY_CHARGE_TYPE_LONGLIFE; default: return -EINVAL; } diff --git a/include/linux/power_supply.h b/include/linux/power_supply.h index b5ee35d3c3046..97cc4b85bf612 100644 --- a/include/linux/power_supply.h +++ b/include/linux/power_supply.h @@ -48,6 +48,7 @@ enum { POWER_SUPPLY_CHARGE_TYPE_STANDARD, /* normal speed */ POWER_SUPPLY_CHARGE_TYPE_ADAPTIVE, /* dynamically adjusted speed */ POWER_SUPPLY_CHARGE_TYPE_CUSTOM, /* use CHARGE_CONTROL_* props */ + POWER_SUPPLY_CHARGE_TYPE_LONGLIFE, /* slow speed, longer life */ }; enum { -- GitLab From 2d02bf835e5731de632c8a13567905fa7c0da01c Mon Sep 17 00:00:00 2001 From: Vaibhav Jain Date: Fri, 31 Jul 2020 12:11:52 +0530 Subject: [PATCH 1222/1476] powerpc/papr_scm: Fetch nvdimm performance stats from PHYP Update papr_scm.c to query dimm performance statistics from PHYP via H_SCM_PERFORMANCE_STATS hcall and export them to user-space as PAPR specific NVDIMM attribute 'perf_stats' in sysfs. The patch also provide a sysfs ABI documentation for the stats being reported and their meanings. During NVDIMM probe time in papr_scm_nvdimm_init() a special variant of H_SCM_PERFORMANCE_STATS hcall is issued to check if collection of performance statistics is supported or not. If successful then a PHYP returns a maximum possible buffer length needed to read all performance stats. This returned value is stored in a per-nvdimm attribute 'stat_buffer_len'. The layout of request buffer for reading NVDIMM performance stats from PHYP is defined in 'struct papr_scm_perf_stats' and 'struct papr_scm_perf_stat'. These structs are used in newly introduced drc_pmem_query_stats() that issues the H_SCM_PERFORMANCE_STATS hcall. The sysfs access function perf_stats_show() uses value 'stat_buffer_len' to allocate a buffer large enough to hold all possible NVDIMM performance stats and passes it to drc_pmem_query_stats() to populate. Finally statistics reported in the buffer are formatted into the sysfs access function output buffer. Signed-off-by: Vaibhav Jain Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20200731064153.182203-2-vaibhav@linux.ibm.com --- Documentation/ABI/testing/sysfs-bus-papr-pmem | 27 ++++ arch/powerpc/platforms/pseries/papr_scm.c | 150 ++++++++++++++++++ 2 files changed, 177 insertions(+) diff --git a/Documentation/ABI/testing/sysfs-bus-papr-pmem b/Documentation/ABI/testing/sysfs-bus-papr-pmem index 5b10d036a8d43..c1a67275c43f8 100644 --- a/Documentation/ABI/testing/sysfs-bus-papr-pmem +++ b/Documentation/ABI/testing/sysfs-bus-papr-pmem @@ -25,3 +25,30 @@ Description: NVDIMM have been scrubbed. * "locked" : Indicating that NVDIMM contents cant be modified until next power cycle. + +What: /sys/bus/nd/devices/nmemX/papr/perf_stats +Date: May, 2020 +KernelVersion: v5.9 +Contact: linuxppc-dev , linux-nvdimm@lists.01.org, +Description: + (RO) Report various performance stats related to papr-scm NVDIMM + device. Each stat is reported on a new line with each line + composed of a stat-identifier followed by it value. Below are + currently known dimm performance stats which are reported: + + * "CtlResCt" : Controller Reset Count + * "CtlResTm" : Controller Reset Elapsed Time + * "PonSecs " : Power-on Seconds + * "MemLife " : Life Remaining + * "CritRscU" : Critical Resource Utilization + * "HostLCnt" : Host Load Count + * "HostSCnt" : Host Store Count + * "HostSDur" : Host Store Duration + * "HostLDur" : Host Load Duration + * "MedRCnt " : Media Read Count + * "MedWCnt " : Media Write Count + * "MedRDur " : Media Read Duration + * "MedWDur " : Media Write Duration + * "CchRHCnt" : Cache Read Hit Count + * "CchWHCnt" : Cache Write Hit Count + * "FastWCnt" : Fast Write Count \ No newline at end of file diff --git a/arch/powerpc/platforms/pseries/papr_scm.c b/arch/powerpc/platforms/pseries/papr_scm.c index 3d1235a76ba96..f37f3f70007d7 100644 --- a/arch/powerpc/platforms/pseries/papr_scm.c +++ b/arch/powerpc/platforms/pseries/papr_scm.c @@ -64,6 +64,26 @@ PAPR_PMEM_HEALTH_FATAL | \ PAPR_PMEM_HEALTH_UNHEALTHY) +#define PAPR_SCM_PERF_STATS_EYECATCHER __stringify(SCMSTATS) +#define PAPR_SCM_PERF_STATS_VERSION 0x1 + +/* Struct holding a single performance metric */ +struct papr_scm_perf_stat { + u8 stat_id[8]; + __be64 stat_val; +} __packed; + +/* Struct exchanged between kernel and PHYP for fetching drc perf stats */ +struct papr_scm_perf_stats { + u8 eye_catcher[8]; + /* Should be PAPR_SCM_PERF_STATS_VERSION */ + __be32 stats_version; + /* Number of stats following */ + __be32 num_statistics; + /* zero or more performance matrics */ + struct papr_scm_perf_stat scm_statistic[]; +} __packed; + /* private struct associated with each region */ struct papr_scm_priv { struct platform_device *pdev; @@ -92,6 +112,9 @@ struct papr_scm_priv { /* Health information for the dimm */ u64 health_bitmap; + + /* length of the stat buffer as expected by phyp */ + size_t stat_buffer_len; }; static LIST_HEAD(papr_nd_regions); @@ -200,6 +223,79 @@ err_out: return drc_pmem_bind(p); } +/* + * Query the Dimm performance stats from PHYP and copy them (if returned) to + * provided struct papr_scm_perf_stats instance 'stats' that can hold atleast + * (num_stats + header) bytes. + * - If buff_stats == NULL the return value is the size in byes of the buffer + * needed to hold all supported performance-statistics. + * - If buff_stats != NULL and num_stats == 0 then we copy all known + * performance-statistics to 'buff_stat' and expect to be large enough to + * hold them. + * - if buff_stats != NULL and num_stats > 0 then copy the requested + * performance-statistics to buff_stats. + */ +static ssize_t drc_pmem_query_stats(struct papr_scm_priv *p, + struct papr_scm_perf_stats *buff_stats, + unsigned int num_stats) +{ + unsigned long ret[PLPAR_HCALL_BUFSIZE]; + size_t size; + s64 rc; + + /* Setup the out buffer */ + if (buff_stats) { + memcpy(buff_stats->eye_catcher, + PAPR_SCM_PERF_STATS_EYECATCHER, 8); + buff_stats->stats_version = + cpu_to_be32(PAPR_SCM_PERF_STATS_VERSION); + buff_stats->num_statistics = + cpu_to_be32(num_stats); + + /* + * Calculate the buffer size based on num-stats provided + * or use the prefetched max buffer length + */ + if (num_stats) + /* Calculate size from the num_stats */ + size = sizeof(struct papr_scm_perf_stats) + + num_stats * sizeof(struct papr_scm_perf_stat); + else + size = p->stat_buffer_len; + } else { + /* In case of no out buffer ignore the size */ + size = 0; + } + + /* Do the HCALL asking PHYP for info */ + rc = plpar_hcall(H_SCM_PERFORMANCE_STATS, ret, p->drc_index, + buff_stats ? virt_to_phys(buff_stats) : 0, + size); + + /* Check if the error was due to an unknown stat-id */ + if (rc == H_PARTIAL) { + dev_err(&p->pdev->dev, + "Unknown performance stats, Err:0x%016lX\n", ret[0]); + return -ENOENT; + } else if (rc != H_SUCCESS) { + dev_err(&p->pdev->dev, + "Failed to query performance stats, Err:%lld\n", rc); + return -EIO; + + } else if (!size) { + /* Handle case where stat buffer size was requested */ + dev_dbg(&p->pdev->dev, + "Performance stats size %ld\n", ret[0]); + return ret[0]; + } + + /* Successfully fetched the requested stats from phyp */ + dev_dbg(&p->pdev->dev, + "Performance stats returned %d stats\n", + be32_to_cpu(buff_stats->num_statistics)); + return 0; +} + /* * Issue hcall to retrieve dimm health info and populate papr_scm_priv with the * health information. @@ -637,6 +733,48 @@ static int papr_scm_ndctl(struct nvdimm_bus_descriptor *nd_desc, return 0; } +static ssize_t perf_stats_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int index, rc; + struct seq_buf s; + struct papr_scm_perf_stat *stat; + struct papr_scm_perf_stats *stats; + struct nvdimm *dimm = to_nvdimm(dev); + struct papr_scm_priv *p = nvdimm_provider_data(dimm); + + if (!p->stat_buffer_len) + return -ENOENT; + + /* Allocate the buffer for phyp where stats are written */ + stats = kzalloc(p->stat_buffer_len, GFP_KERNEL); + if (!stats) + return -ENOMEM; + + /* Ask phyp to return all dimm perf stats */ + rc = drc_pmem_query_stats(p, stats, 0); + if (rc) + goto free_stats; + /* + * Go through the returned output buffer and print stats and + * values. Since stat_id is essentially a char string of + * 8 bytes, simply use the string format specifier to print it. + */ + seq_buf_init(&s, buf, PAGE_SIZE); + for (index = 0, stat = stats->scm_statistic; + index < be32_to_cpu(stats->num_statistics); + ++index, ++stat) { + seq_buf_printf(&s, "%.8s = 0x%016llX\n", + stat->stat_id, + be64_to_cpu(stat->stat_val)); + } + +free_stats: + kfree(stats); + return rc ? rc : seq_buf_used(&s); +} +DEVICE_ATTR_RO(perf_stats); + static ssize_t flags_show(struct device *dev, struct device_attribute *attr, char *buf) { @@ -682,6 +820,7 @@ DEVICE_ATTR_RO(flags); /* papr_scm specific dimm attributes */ static struct attribute *papr_nd_attributes[] = { &dev_attr_flags.attr, + &dev_attr_perf_stats.attr, NULL, }; @@ -702,6 +841,7 @@ static int papr_scm_nvdimm_init(struct papr_scm_priv *p) struct nd_region_desc ndr_desc; unsigned long dimm_flags; int target_nid, online_nid; + ssize_t stat_size; p->bus_desc.ndctl = papr_scm_ndctl; p->bus_desc.module = THIS_MODULE; @@ -769,6 +909,16 @@ static int papr_scm_nvdimm_init(struct papr_scm_priv *p) list_add_tail(&p->region_list, &papr_nd_regions); mutex_unlock(&papr_ndr_lock); + /* Try retriving the stat buffer and see if its supported */ + stat_size = drc_pmem_query_stats(p, NULL, 0); + if (stat_size > 0) { + p->stat_buffer_len = stat_size; + dev_dbg(&p->pdev->dev, "Max perf-stat size %lu-bytes\n", + p->stat_buffer_len); + } else { + dev_info(&p->pdev->dev, "Dimm performance stats unavailable\n"); + } + return 0; err: nvdimm_bus_unregister(p->bus); -- GitLab From af0870c4e75655b1931d0a5ffde2f448a2794362 Mon Sep 17 00:00:00 2001 From: Vaibhav Jain Date: Fri, 31 Jul 2020 12:11:53 +0530 Subject: [PATCH 1223/1476] powerpc/papr_scm: Add support for fetching nvdimm 'fuel-gauge' metric We add support for reporting 'fuel-gauge' NVDIMM metric via PAPR_PDSM_HEALTH pdsm payload. 'fuel-gauge' metric indicates the usage life remaining of a papr-scm compatible NVDIMM. PHYP exposes this metric via the H_SCM_PERFORMANCE_STATS. The metric value is returned from the pdsm by extending the return payload 'struct nd_papr_pdsm_health' without breaking the ABI. A new field 'dimm_fuel_gauge' to hold the metric value is introduced at the end of the payload struct and its presence is indicated by by extension flag PDSM_DIMM_HEALTH_RUN_GAUGE_VALID. The patch introduces a new function papr_pdsm_fuel_gauge() that is called from papr_pdsm_health(). If fetching NVDIMM performance stats is supported then 'papr_pdsm_fuel_gauge()' allocated an output buffer large enough to hold the performance stat and passes it to drc_pmem_query_stats() that issues the HCALL to PHYP. The return value of the stat is then populated in the 'struct nd_papr_pdsm_health.dimm_fuel_gauge' field with extension flag 'PDSM_DIMM_HEALTH_RUN_GAUGE_VALID' set in 'struct nd_papr_pdsm_health.extension_flags' Signed-off-by: Vaibhav Jain Reviewed-by: Aneesh Kumar K.V Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20200731064153.182203-3-vaibhav@linux.ibm.com --- arch/powerpc/include/uapi/asm/papr_pdsm.h | 9 +++++ arch/powerpc/platforms/pseries/papr_scm.c | 49 +++++++++++++++++++++++ 2 files changed, 58 insertions(+) diff --git a/arch/powerpc/include/uapi/asm/papr_pdsm.h b/arch/powerpc/include/uapi/asm/papr_pdsm.h index 9ccecc1d6840a..50ef95e2f5b18 100644 --- a/arch/powerpc/include/uapi/asm/papr_pdsm.h +++ b/arch/powerpc/include/uapi/asm/papr_pdsm.h @@ -72,6 +72,11 @@ #define PAPR_PDSM_DIMM_CRITICAL 2 #define PAPR_PDSM_DIMM_FATAL 3 +/* struct nd_papr_pdsm_health.extension_flags field flags */ + +/* Indicate that the 'dimm_fuel_gauge' field is valid */ +#define PDSM_DIMM_HEALTH_RUN_GAUGE_VALID 1 + /* * Struct exchanged between kernel & ndctl in for PAPR_PDSM_HEALTH * Various flags indicate the health status of the dimm. @@ -84,6 +89,7 @@ * dimm_locked : Contents of the dimm cant be modified until CEC reboot * dimm_encrypted : Contents of dimm are encrypted. * dimm_health : Dimm health indicator. One of PAPR_PDSM_DIMM_XXXX + * dimm_fuel_gauge : Life remaining of DIMM as a percentage from 0-100 */ struct nd_papr_pdsm_health { union { @@ -96,6 +102,9 @@ struct nd_papr_pdsm_health { __u8 dimm_locked; __u8 dimm_encrypted; __u16 dimm_health; + + /* Extension flag PDSM_DIMM_HEALTH_RUN_GAUGE_VALID */ + __u16 dimm_fuel_gauge; }; __u8 buf[ND_PDSM_PAYLOAD_MAX_SIZE]; }; diff --git a/arch/powerpc/platforms/pseries/papr_scm.c b/arch/powerpc/platforms/pseries/papr_scm.c index f37f3f70007d7..f439f0dfea7d1 100644 --- a/arch/powerpc/platforms/pseries/papr_scm.c +++ b/arch/powerpc/platforms/pseries/papr_scm.c @@ -518,6 +518,51 @@ static int is_cmd_valid(struct nvdimm *nvdimm, unsigned int cmd, void *buf, return 0; } +static int papr_pdsm_fuel_gauge(struct papr_scm_priv *p, + union nd_pdsm_payload *payload) +{ + int rc, size; + u64 statval; + struct papr_scm_perf_stat *stat; + struct papr_scm_perf_stats *stats; + + /* Silently fail if fetching performance metrics isn't supported */ + if (!p->stat_buffer_len) + return 0; + + /* Allocate request buffer enough to hold single performance stat */ + size = sizeof(struct papr_scm_perf_stats) + + sizeof(struct papr_scm_perf_stat); + + stats = kzalloc(size, GFP_KERNEL); + if (!stats) + return -ENOMEM; + + stat = &stats->scm_statistic[0]; + memcpy(&stat->stat_id, "MemLife ", sizeof(stat->stat_id)); + stat->stat_val = 0; + + /* Fetch the fuel gauge and populate it in payload */ + rc = drc_pmem_query_stats(p, stats, 1); + if (rc < 0) { + dev_dbg(&p->pdev->dev, "Err(%d) fetching fuel gauge\n", rc); + goto free_stats; + } + + statval = be64_to_cpu(stat->stat_val); + dev_dbg(&p->pdev->dev, + "Fetched fuel-gauge %llu", statval); + payload->health.extension_flags |= + PDSM_DIMM_HEALTH_RUN_GAUGE_VALID; + payload->health.dimm_fuel_gauge = statval; + + rc = sizeof(struct nd_papr_pdsm_health); + +free_stats: + kfree(stats); + return rc; +} + /* Fetch the DIMM health info and populate it in provided package. */ static int papr_pdsm_health(struct papr_scm_priv *p, union nd_pdsm_payload *payload) @@ -558,6 +603,10 @@ static int papr_pdsm_health(struct papr_scm_priv *p, /* struct populated hence can release the mutex now */ mutex_unlock(&p->health_mutex); + + /* Populate the fuel gauge meter in the payload */ + papr_pdsm_fuel_gauge(p, payload); + rc = sizeof(struct nd_papr_pdsm_health); out: -- GitLab From 5f11723b204aaf461e7a5242fc732222d2f0aa17 Mon Sep 17 00:00:00 2001 From: Logan Gunthorpe Date: Tue, 28 Jul 2020 13:24:33 -0600 Subject: [PATCH 1224/1476] PCI: switchtec: Add missing __iomem and __user tags to fix sparse warnings Fix a number of missing __iomem and __user tags in the ioctl functions of the switchtec driver. This fixes a number of sparse warnings of the form: $ make C=2 drivers/pci/switch/ drivers/pci/switch/switchtec.c:... incorrect type in ... (different address spaces) Fixes: 52eabba5bcdb ("switchtec: Add IOCTLs to the Switchtec driver") Link: https://lore.kernel.org/r/20200728192434.18993-1-logang@deltatee.com Signed-off-by: Logan Gunthorpe Signed-off-by: Bjorn Helgaas --- drivers/pci/switch/switchtec.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/drivers/pci/switch/switchtec.c b/drivers/pci/switch/switchtec.c index 850cfeb746088..3d5da7f44378b 100644 --- a/drivers/pci/switch/switchtec.c +++ b/drivers/pci/switch/switchtec.c @@ -940,7 +940,7 @@ static u32 __iomem *event_hdr_addr(struct switchtec_dev *stdev, size_t off; if (event_id < 0 || event_id >= SWITCHTEC_IOCTL_MAX_EVENTS) - return ERR_PTR(-EINVAL); + return (u32 __iomem *)ERR_PTR(-EINVAL); off = event_regs[event_id].offset; @@ -948,10 +948,10 @@ static u32 __iomem *event_hdr_addr(struct switchtec_dev *stdev, if (index == SWITCHTEC_IOCTL_EVENT_LOCAL_PART_IDX) index = stdev->partition; else if (index < 0 || index >= stdev->partition_count) - return ERR_PTR(-EINVAL); + return (u32 __iomem *)ERR_PTR(-EINVAL); } else if (event_regs[event_id].map_reg == pff_ev_reg) { if (index < 0 || index >= stdev->pff_csr_count) - return ERR_PTR(-EINVAL); + return (u32 __iomem *)ERR_PTR(-EINVAL); } return event_regs[event_id].map_reg(stdev, off, index); @@ -1057,11 +1057,11 @@ static int ioctl_event_ctl(struct switchtec_dev *stdev, } static int ioctl_pff_to_port(struct switchtec_dev *stdev, - struct switchtec_ioctl_pff_port *up) + struct switchtec_ioctl_pff_port __user *up) { int i, part; u32 reg; - struct part_cfg_regs *pcfg; + struct part_cfg_regs __iomem *pcfg; struct switchtec_ioctl_pff_port p; if (copy_from_user(&p, up, sizeof(p))) @@ -1104,10 +1104,10 @@ static int ioctl_pff_to_port(struct switchtec_dev *stdev, } static int ioctl_port_to_pff(struct switchtec_dev *stdev, - struct switchtec_ioctl_pff_port *up) + struct switchtec_ioctl_pff_port __user *up) { struct switchtec_ioctl_pff_port p; - struct part_cfg_regs *pcfg; + struct part_cfg_regs __iomem *pcfg; if (copy_from_user(&p, up, sizeof(p))) return -EFAULT; -- GitLab From 42dae8937406c98ce0cb6ff0d683765c854098d9 Mon Sep 17 00:00:00 2001 From: Logan Gunthorpe Date: Tue, 28 Jul 2020 13:24:34 -0600 Subject: [PATCH 1225/1476] PCI: switchtec: Add missing __iomem tag to fix sparse warnings Fix a missing __iomem tag in the init_pfn() function. This fixes a sparse warning of the form: $ make C=2 drivers/pci/switch/ drivers/pci/switch/switchtec.c:... incorrect type assignment(different address spaces) Fixes: 080b47def5e5 ("MicroSemi Switchtec management interface driver") Link: https://lore.kernel.org/r/20200728192434.18993-2-logang@deltatee.com Signed-off-by: Logan Gunthorpe Signed-off-by: Bjorn Helgaas --- drivers/pci/switch/switchtec.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/pci/switch/switchtec.c b/drivers/pci/switch/switchtec.c index 3d5da7f44378b..ba52459928f7f 100644 --- a/drivers/pci/switch/switchtec.c +++ b/drivers/pci/switch/switchtec.c @@ -1484,7 +1484,7 @@ static void init_pff(struct switchtec_dev *stdev) { int i; u32 reg; - struct part_cfg_regs *pcfg = stdev->mmio_part_cfg; + struct part_cfg_regs __iomem *pcfg = stdev->mmio_part_cfg; for (i = 0; i < SWITCHTEC_MAX_PFF_CSR; i++) { reg = ioread16(&stdev->mmio_pff_csr[i].vendor_id); -- GitLab From e398fb4bdf522abdeb8ddb61235bbf66065fc105 Mon Sep 17 00:00:00 2001 From: Souptick Joarder Date: Sun, 12 Jul 2020 09:09:53 +0530 Subject: [PATCH 1226/1476] xen/privcmd: Corrected error handling path Previously, if lock_pages() end up partially mapping pages, it used to return -ERRNO due to which unlock_pages() have to go through each pages[i] till *nr_pages* to validate them. This can be avoided by passing correct number of partially mapped pages & -ERRNO separately, while returning from lock_pages() due to error. With this fix unlock_pages() doesn't need to validate pages[i] till *nr_pages* for error scenario and few condition checks can be ignored. Signed-off-by: Souptick Joarder Reviewed-by: Juergen Gross Reviewed-by: Paul Durrant Cc: John Hubbard Cc: Boris Ostrovsky Cc: Paul Durrant Link: https://lore.kernel.org/r/1594525195-28345-2-git-send-email-jrdr.linux@gmail.com Signed-off-by: Juergen Gross --- drivers/xen/privcmd.c | 31 +++++++++++++++---------------- 1 file changed, 15 insertions(+), 16 deletions(-) diff --git a/drivers/xen/privcmd.c b/drivers/xen/privcmd.c index a250d118144a0..a9b2baa2c7bef 100644 --- a/drivers/xen/privcmd.c +++ b/drivers/xen/privcmd.c @@ -580,13 +580,13 @@ out_unlock: static int lock_pages( struct privcmd_dm_op_buf kbufs[], unsigned int num, - struct page *pages[], unsigned int nr_pages) + struct page *pages[], unsigned int nr_pages, unsigned int *pinned) { unsigned int i; for (i = 0; i < num; i++) { unsigned int requested; - int pinned; + int page_count; requested = DIV_ROUND_UP( offset_in_page(kbufs[i].uptr) + kbufs[i].size, @@ -594,14 +594,15 @@ static int lock_pages( if (requested > nr_pages) return -ENOSPC; - pinned = get_user_pages_fast( + page_count = get_user_pages_fast( (unsigned long) kbufs[i].uptr, requested, FOLL_WRITE, pages); - if (pinned < 0) - return pinned; + if (page_count < 0) + return page_count; - nr_pages -= pinned; - pages += pinned; + *pinned += page_count; + nr_pages -= page_count; + pages += page_count; } return 0; @@ -611,13 +612,8 @@ static void unlock_pages(struct page *pages[], unsigned int nr_pages) { unsigned int i; - if (!pages) - return; - - for (i = 0; i < nr_pages; i++) { - if (pages[i]) - put_page(pages[i]); - } + for (i = 0; i < nr_pages; i++) + put_page(pages[i]); } static long privcmd_ioctl_dm_op(struct file *file, void __user *udata) @@ -630,6 +626,7 @@ static long privcmd_ioctl_dm_op(struct file *file, void __user *udata) struct xen_dm_op_buf *xbufs = NULL; unsigned int i; long rc; + unsigned int pinned = 0; if (copy_from_user(&kdata, udata, sizeof(kdata))) return -EFAULT; @@ -683,9 +680,11 @@ static long privcmd_ioctl_dm_op(struct file *file, void __user *udata) goto out; } - rc = lock_pages(kbufs, kdata.num, pages, nr_pages); - if (rc) + rc = lock_pages(kbufs, kdata.num, pages, nr_pages, &pinned); + if (rc < 0) { + nr_pages = pinned; goto out; + } for (i = 0; i < kdata.num; i++) { set_xen_guest_handle(xbufs[i].h, kbufs[i].uptr); -- GitLab From a0c34d225183fd4aca61dccb50e5783fa2f26db1 Mon Sep 17 00:00:00 2001 From: Souptick Joarder Date: Sun, 12 Jul 2020 09:09:54 +0530 Subject: [PATCH 1227/1476] xen/privcmd: Mark pages as dirty pages need to be marked as dirty before unpinned it in unlock_pages() which was oversight. This is fixed now. Signed-off-by: Souptick Joarder Suggested-by: John Hubbard Reviewed-by: Juergen Gross Reviewed-by: Paul Durrant Cc: John Hubbard Cc: Boris Ostrovsky Cc: Paul Durrant Link: https://lore.kernel.org/r/1594525195-28345-3-git-send-email-jrdr.linux@gmail.com Signed-off-by: Juergen Gross --- drivers/xen/privcmd.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/xen/privcmd.c b/drivers/xen/privcmd.c index a9b2baa2c7bef..aa8ca58e6efc5 100644 --- a/drivers/xen/privcmd.c +++ b/drivers/xen/privcmd.c @@ -612,8 +612,11 @@ static void unlock_pages(struct page *pages[], unsigned int nr_pages) { unsigned int i; - for (i = 0; i < nr_pages; i++) + for (i = 0; i < nr_pages; i++) { + if (!PageDirty(pages[i])) + set_page_dirty_lock(pages[i]); put_page(pages[i]); + } } static long privcmd_ioctl_dm_op(struct file *file, void __user *udata) -- GitLab From ff669aa8124035a432d2b7eecdd876b308cd48ee Mon Sep 17 00:00:00 2001 From: Souptick Joarder Date: Sun, 12 Jul 2020 09:09:55 +0530 Subject: [PATCH 1228/1476] xen/privcmd: Convert get_user_pages*() to pin_user_pages*() In 2019, we introduced pin_user_pages*() and now we are converting get_user_pages*() to the new API as appropriate. [1] & [2] could be referred for more information. This is case 5 as per document [1]. [1] Documentation/core-api/pin_user_pages.rst [2] "Explicit pinning of user-space pages": https://lwn.net/Articles/807108/ Signed-off-by: Souptick Joarder Reviewed-by: Juergen Gross Reviewed-by: Paul Durrant Cc: John Hubbard Cc: Boris Ostrovsky Cc: Paul Durrant Link: https://lore.kernel.org/r/1594525195-28345-4-git-send-email-jrdr.linux@gmail.com Signed-off-by: Juergen Gross --- drivers/xen/privcmd.c | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/drivers/xen/privcmd.c b/drivers/xen/privcmd.c index aa8ca58e6efc5..095d683ad574c 100644 --- a/drivers/xen/privcmd.c +++ b/drivers/xen/privcmd.c @@ -594,7 +594,7 @@ static int lock_pages( if (requested > nr_pages) return -ENOSPC; - page_count = get_user_pages_fast( + page_count = pin_user_pages_fast( (unsigned long) kbufs[i].uptr, requested, FOLL_WRITE, pages); if (page_count < 0) @@ -610,13 +610,7 @@ static int lock_pages( static void unlock_pages(struct page *pages[], unsigned int nr_pages) { - unsigned int i; - - for (i = 0; i < nr_pages; i++) { - if (!PageDirty(pages[i])) - set_page_dirty_lock(pages[i]); - put_page(pages[i]); - } + unpin_user_pages_dirty_lock(pages, nr_pages, true); } static long privcmd_ioctl_dm_op(struct file *file, void __user *udata) -- GitLab From e5a52fd2b8cdb700b3c07b030e050a49ef3156b9 Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Sat, 18 Jul 2020 17:33:17 -0700 Subject: [PATCH 1229/1476] xen/gntdev: gntdev.h: drop a duplicated word Drop the repeated word "of" in a comment. Signed-off-by: Randy Dunlap Reviewed-by: Juergen Gross Cc: Boris Ostrovsky Cc: Juergen Gross Cc: xen-devel@lists.xenproject.org Link: https://lore.kernel.org/r/20200719003317.21454-1-rdunlap@infradead.org Signed-off-by: Juergen Gross --- include/uapi/xen/gntdev.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/uapi/xen/gntdev.h b/include/uapi/xen/gntdev.h index fe4423e518c67..9ac5515b9bc22 100644 --- a/include/uapi/xen/gntdev.h +++ b/include/uapi/xen/gntdev.h @@ -66,7 +66,7 @@ struct ioctl_gntdev_map_grant_ref { /* * Removes the grant references from the mapping table of an instance of - * of gntdev. N.B. munmap() must be called on the relevant virtual address(es) + * gntdev. N.B. munmap() must be called on the relevant virtual address(es) * before this ioctl is called, or an error will result. */ #define IOCTL_GNTDEV_UNMAP_GRANT_REF \ -- GitLab From 4e722d4fe27899f90491065edb0bb0a757f0b955 Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Sat, 25 Jul 2020 17:17:31 -0700 Subject: [PATCH 1230/1476] xen: hypercall.h: fix duplicated word Change the repeated word "as" to "as a". Signed-off-by: Randy Dunlap Reviewed-by: Juergen Gross Cc: Boris Ostrovsky Cc: Juergen Gross Cc: xen-devel@lists.xenproject.org Link: https://lore.kernel.org/r/20200726001731.19540-1-rdunlap@infradead.org Signed-off-by: Juergen Gross --- arch/x86/include/asm/xen/hypercall.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/x86/include/asm/xen/hypercall.h b/arch/x86/include/asm/xen/hypercall.h index ba4c1b15908b2..454b20815f357 100644 --- a/arch/x86/include/asm/xen/hypercall.h +++ b/arch/x86/include/asm/xen/hypercall.h @@ -82,7 +82,7 @@ struct xen_dm_op_buf; * - clobber the rest * * The result certainly isn't pretty, and it really shows up cpp's - * weakness as as macro language. Sorry. (But let's just give thanks + * weakness as a macro language. Sorry. (But let's just give thanks * there aren't more than 5 arguments...) */ -- GitLab From 1951fa33ec259abdf3497bfee7b63e7ddbb1a394 Mon Sep 17 00:00:00 2001 From: Roger Pau Monne Date: Mon, 27 Jul 2020 11:13:39 +0200 Subject: [PATCH 1231/1476] xen/balloon: fix accounting in alloc_xenballooned_pages error path MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit target_unpopulated is incremented with nr_pages at the start of the function, but the call to free_xenballooned_pages will only subtract pgno number of pages, and thus the rest need to be subtracted before returning or else accounting will be skewed. Signed-off-by: Roger Pau Monné Reviewed-by: Juergen Gross Cc: stable@vger.kernel.org Link: https://lore.kernel.org/r/20200727091342.52325-2-roger.pau@citrix.com Signed-off-by: Juergen Gross --- drivers/xen/balloon.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/xen/balloon.c b/drivers/xen/balloon.c index 77c57568e5d7f..3cb10ed32557a 100644 --- a/drivers/xen/balloon.c +++ b/drivers/xen/balloon.c @@ -630,6 +630,12 @@ int alloc_xenballooned_pages(int nr_pages, struct page **pages) out_undo: mutex_unlock(&balloon_mutex); free_xenballooned_pages(pgno, pages); + /* + * NB: free_xenballooned_pages will only subtract pgno pages, but since + * target_unpopulated is incremented with nr_pages at the start we need + * to remove the remaining ones also, or accounting will be screwed. + */ + balloon_stats.target_unpopulated -= nr_pages - pgno; return ret; } EXPORT_SYMBOL(alloc_xenballooned_pages); -- GitLab From 88a479ff6ef8af7f07e11593d58befc644244ff7 Mon Sep 17 00:00:00 2001 From: Roger Pau Monne Date: Mon, 27 Jul 2020 11:13:40 +0200 Subject: [PATCH 1232/1476] xen/balloon: make the balloon wait interruptible MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit So it can be killed, or else processes can get hung indefinitely waiting for balloon pages. Signed-off-by: Roger Pau Monné Reviewed-by: Juergen Gross Cc: stable@vger.kernel.org Link: https://lore.kernel.org/r/20200727091342.52325-3-roger.pau@citrix.com Signed-off-by: Juergen Gross --- drivers/xen/balloon.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/xen/balloon.c b/drivers/xen/balloon.c index 3cb10ed32557a..292413b275754 100644 --- a/drivers/xen/balloon.c +++ b/drivers/xen/balloon.c @@ -568,11 +568,13 @@ static int add_ballooned_pages(int nr_pages) if (xen_hotplug_unpopulated) { st = reserve_additional_memory(); if (st != BP_ECANCELED) { + int rc; + mutex_unlock(&balloon_mutex); - wait_event(balloon_wq, + rc = wait_event_interruptible(balloon_wq, !list_empty(&ballooned_pages)); mutex_lock(&balloon_mutex); - return 0; + return rc ? -ENOMEM : 0; } } -- GitLab From f5ec6723269d9652ce42927cc03485061d663b23 Mon Sep 17 00:00:00 2001 From: Roger Pau Monne Date: Mon, 27 Jul 2020 11:13:41 +0200 Subject: [PATCH 1233/1476] Revert "xen/balloon: Fix crash when ballooning on x86 32 bit PAE" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit dfd74a1edfaba5864276a2859190a8d242d18952. This has been fixed by commit dca4436d1cf9e0d237c which added the out of bounds check to __add_memory, so that trying to add blocks past MAX_PHYSMEM_BITS will fail. Note the check in the Xen balloon driver was bogus anyway, as it checked the start address of the resource, but it should instead test the end address to assert the whole resource falls below MAX_PHYSMEM_BITS. Signed-off-by: Roger Pau Monné Reviewed-by: Juergen Gross Link: https://lore.kernel.org/r/20200727091342.52325-4-roger.pau@citrix.com Signed-off-by: Juergen Gross --- drivers/xen/balloon.c | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/drivers/xen/balloon.c b/drivers/xen/balloon.c index 292413b275754..b1d8b028bf804 100644 --- a/drivers/xen/balloon.c +++ b/drivers/xen/balloon.c @@ -266,20 +266,6 @@ static struct resource *additional_memory_resource(phys_addr_t size) return NULL; } -#ifdef CONFIG_SPARSEMEM - { - unsigned long limit = 1UL << (MAX_PHYSMEM_BITS - PAGE_SHIFT); - unsigned long pfn = res->start >> PAGE_SHIFT; - - if (pfn > limit) { - pr_err("New System RAM resource outside addressable RAM (%lu > %lu)\n", - pfn, limit); - release_memory_resource(res); - return NULL; - } - } -#endif - return res; } -- GitLab From 8d8a629d00a5283874b81b594f31f8d436dc57d8 Mon Sep 17 00:00:00 2001 From: Michael Ellerman Date: Wed, 22 Jul 2020 12:24:22 +1000 Subject: [PATCH 1234/1476] powerpc/40x: Fix assembler warning about r0 The assembler says: arch/powerpc/kernel/head_40x.S:623: Warning: invalid register expression It's objecting to the use of r0 as the RA argument. That's because when RA = 0 the literal value 0 is used, rather than the content of r0, making the use of r0 in the source potentially confusing. Fix it to use a literal 0, the generated code is identical. Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20200722022422.825197-1-mpe@ellerman.id.au --- arch/powerpc/kernel/head_40x.S | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/powerpc/kernel/head_40x.S b/arch/powerpc/kernel/head_40x.S index 926bfa73586a4..5b282d9965a56 100644 --- a/arch/powerpc/kernel/head_40x.S +++ b/arch/powerpc/kernel/head_40x.S @@ -620,7 +620,7 @@ start_here: ori r6, r6, swapper_pg_dir@l lis r5, abatron_pteptrs@h ori r5, r5, abatron_pteptrs@l - stw r5, 0xf0(r0) /* Must match your Abatron config file */ + stw r5, 0xf0(0) /* Must match your Abatron config file */ tophys(r5,r5) stw r6, 0(r5) -- GitLab From 872d11bca9c29ed19595c993b9f552ffe9b63dcb Mon Sep 17 00:00:00 2001 From: Michael Ellerman Date: Mon, 3 Aug 2020 12:07:19 +1000 Subject: [PATCH 1235/1476] selftests/powerpc: Skip vmx/vsx/tar/etc tests on older CPUs Some of our tests use VSX or newer VMX instructions, so need to be skipped on older CPUs to avoid SIGILL'ing. Similarly TAR was added in v2.07, and the PMU event used in the stcx fail test only works on Power8 or later. Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20200803020719.96114-1-mpe@ellerman.id.au --- tools/testing/selftests/powerpc/math/Makefile | 8 ++++---- tools/testing/selftests/powerpc/math/vmx_preempt.c | 3 +++ tools/testing/selftests/powerpc/math/vmx_signal.c | 3 +++ tools/testing/selftests/powerpc/math/vmx_syscall.c | 7 ++++++- tools/testing/selftests/powerpc/math/vsx_preempt.c | 2 ++ tools/testing/selftests/powerpc/pmu/count_stcx_fail.c | 4 ++++ tools/testing/selftests/powerpc/ptrace/ptrace-tar.c | 3 +++ tools/testing/selftests/powerpc/ptrace/ptrace-vsx.c | 2 ++ tools/testing/selftests/powerpc/stringloops/Makefile | 2 +- tools/testing/selftests/powerpc/stringloops/memcmp.c | 6 ++++++ 10 files changed, 34 insertions(+), 6 deletions(-) diff --git a/tools/testing/selftests/powerpc/math/Makefile b/tools/testing/selftests/powerpc/math/Makefile index 4e2049d2fd8db..fcc91c2059841 100644 --- a/tools/testing/selftests/powerpc/math/Makefile +++ b/tools/testing/selftests/powerpc/math/Makefile @@ -11,9 +11,9 @@ $(OUTPUT)/fpu_syscall: fpu_asm.S $(OUTPUT)/fpu_preempt: fpu_asm.S $(OUTPUT)/fpu_signal: fpu_asm.S -$(OUTPUT)/vmx_syscall: vmx_asm.S -$(OUTPUT)/vmx_preempt: vmx_asm.S -$(OUTPUT)/vmx_signal: vmx_asm.S +$(OUTPUT)/vmx_syscall: vmx_asm.S ../utils.c +$(OUTPUT)/vmx_preempt: vmx_asm.S ../utils.c +$(OUTPUT)/vmx_signal: vmx_asm.S ../utils.c $(OUTPUT)/vsx_preempt: CFLAGS += -mvsx -$(OUTPUT)/vsx_preempt: vsx_asm.S +$(OUTPUT)/vsx_preempt: vsx_asm.S ../utils.c diff --git a/tools/testing/selftests/powerpc/math/vmx_preempt.c b/tools/testing/selftests/powerpc/math/vmx_preempt.c index 2e059f154e775..6761d6ce30eca 100644 --- a/tools/testing/selftests/powerpc/math/vmx_preempt.c +++ b/tools/testing/selftests/powerpc/math/vmx_preempt.c @@ -57,6 +57,9 @@ int test_preempt_vmx(void) int i, rc, threads; pthread_t *tids; + // vcmpequd used in vmx_asm.S is v2.07 + SKIP_IF(!have_hwcap2(PPC_FEATURE2_ARCH_2_07)); + threads = sysconf(_SC_NPROCESSORS_ONLN) * THREAD_FACTOR; tids = malloc(threads * sizeof(pthread_t)); FAIL_IF(!tids); diff --git a/tools/testing/selftests/powerpc/math/vmx_signal.c b/tools/testing/selftests/powerpc/math/vmx_signal.c index 785a48e0976fa..b340a5c4e79dd 100644 --- a/tools/testing/selftests/powerpc/math/vmx_signal.c +++ b/tools/testing/selftests/powerpc/math/vmx_signal.c @@ -96,6 +96,9 @@ int test_signal_vmx(void) void *rc_p; pthread_t *tids; + // vcmpequd used in vmx_asm.S is v2.07 + SKIP_IF(!have_hwcap2(PPC_FEATURE2_ARCH_2_07)); + threads = sysconf(_SC_NPROCESSORS_ONLN) * THREAD_FACTOR; tids = malloc(threads * sizeof(pthread_t)); FAIL_IF(!tids); diff --git a/tools/testing/selftests/powerpc/math/vmx_syscall.c b/tools/testing/selftests/powerpc/math/vmx_syscall.c index 9ee293cc868e8..03c78dfe34440 100644 --- a/tools/testing/selftests/powerpc/math/vmx_syscall.c +++ b/tools/testing/selftests/powerpc/math/vmx_syscall.c @@ -49,9 +49,14 @@ int test_vmx_syscall(void) * Setup an environment with much context switching */ pid_t pid2; - pid_t pid = fork(); + pid_t pid; int ret; int child_ret; + + // vcmpequd used in vmx_asm.S is v2.07 + SKIP_IF(!have_hwcap2(PPC_FEATURE2_ARCH_2_07)); + + pid = fork(); FAIL_IF(pid == -1); pid2 = fork(); diff --git a/tools/testing/selftests/powerpc/math/vsx_preempt.c b/tools/testing/selftests/powerpc/math/vsx_preempt.c index 63de9c6e2cd3d..d1601bb889d4c 100644 --- a/tools/testing/selftests/powerpc/math/vsx_preempt.c +++ b/tools/testing/selftests/powerpc/math/vsx_preempt.c @@ -92,6 +92,8 @@ int test_preempt_vsx(void) int i, rc, threads; pthread_t *tids; + SKIP_IF(!have_hwcap(PPC_FEATURE_HAS_VSX)); + threads = sysconf(_SC_NPROCESSORS_ONLN) * THREAD_FACTOR; tids = malloc(threads * sizeof(pthread_t)); FAIL_IF(!tids); diff --git a/tools/testing/selftests/powerpc/pmu/count_stcx_fail.c b/tools/testing/selftests/powerpc/pmu/count_stcx_fail.c index 7b4ac4537702c..2980abca31e0d 100644 --- a/tools/testing/selftests/powerpc/pmu/count_stcx_fail.c +++ b/tools/testing/selftests/powerpc/pmu/count_stcx_fail.c @@ -9,6 +9,7 @@ #include #include #include +#include #include "event.h" #include "utils.h" @@ -104,6 +105,9 @@ static int test_body(void) struct event events[3]; u64 overhead; + // The STCX_FAIL event we use works on Power8 or later + SKIP_IF(!have_hwcap2(PPC_FEATURE2_ARCH_2_07)); + setup_event(&events[0], PERF_COUNT_HW_INSTRUCTIONS, PERF_TYPE_HARDWARE, "instructions"); setup_event(&events[1], PERF_COUNT_HW_CPU_CYCLES, PERF_TYPE_HARDWARE, "cycles"); setup_event(&events[2], PM_STCX_FAIL, PERF_TYPE_RAW, "stcx_fail"); diff --git a/tools/testing/selftests/powerpc/ptrace/ptrace-tar.c b/tools/testing/selftests/powerpc/ptrace/ptrace-tar.c index 58cb1a860cc9b..4436ca9d3caf8 100644 --- a/tools/testing/selftests/powerpc/ptrace/ptrace-tar.c +++ b/tools/testing/selftests/powerpc/ptrace/ptrace-tar.c @@ -78,6 +78,9 @@ int ptrace_tar(void) pid_t pid; int ret, status; + // TAR was added in v2.07 + SKIP_IF(!have_hwcap2(PPC_FEATURE2_ARCH_2_07)); + shm_id = shmget(IPC_PRIVATE, sizeof(int) * 3, 0777|IPC_CREAT); pid = fork(); if (pid < 0) { diff --git a/tools/testing/selftests/powerpc/ptrace/ptrace-vsx.c b/tools/testing/selftests/powerpc/ptrace/ptrace-vsx.c index c4fe0e893306d..cb9875f764ca2 100644 --- a/tools/testing/selftests/powerpc/ptrace/ptrace-vsx.c +++ b/tools/testing/selftests/powerpc/ptrace/ptrace-vsx.c @@ -61,6 +61,8 @@ int ptrace_vsx(void) pid_t pid; int ret, status, i; + SKIP_IF(!have_hwcap(PPC_FEATURE_HAS_VSX)); + shm_id = shmget(IPC_PRIVATE, sizeof(int) * 2, 0777|IPC_CREAT); for (i = 0; i < VEC_MAX; i++) diff --git a/tools/testing/selftests/powerpc/stringloops/Makefile b/tools/testing/selftests/powerpc/stringloops/Makefile index 7fc0623d85c31..9c39f55a58ff3 100644 --- a/tools/testing/selftests/powerpc/stringloops/Makefile +++ b/tools/testing/selftests/powerpc/stringloops/Makefile @@ -8,7 +8,7 @@ build_32bit = $(shell if ($(CC) $(CFLAGS) -m32 -o /dev/null memcmp.c >/dev/null TEST_GEN_PROGS := memcmp_64 strlen -$(OUTPUT)/memcmp_64: memcmp.c +$(OUTPUT)/memcmp_64: memcmp.c ../utils.c $(OUTPUT)/memcmp_64: CFLAGS += -m64 -maltivec ifeq ($(build_32bit),1) diff --git a/tools/testing/selftests/powerpc/stringloops/memcmp.c b/tools/testing/selftests/powerpc/stringloops/memcmp.c index e4605ca850dc1..979df3d983685 100644 --- a/tools/testing/selftests/powerpc/stringloops/memcmp.c +++ b/tools/testing/selftests/powerpc/stringloops/memcmp.c @@ -4,6 +4,7 @@ #include #include #include +#include #include "utils.h" #define SIZE 256 @@ -151,6 +152,11 @@ static int testcase(bool islarge) static int testcases(void) { +#ifdef __powerpc64__ + // vcmpequd used in memcmp_64.S is v2.07 + SKIP_IF(!have_hwcap2(PPC_FEATURE2_ARCH_2_07)); +#endif + testcase(0); testcase(1); return 0; -- GitLab From 2075ec9896c5aef01e837198381d04cfa6452317 Mon Sep 17 00:00:00 2001 From: Oliver O'Halloran Date: Mon, 3 Aug 2020 17:54:08 +1000 Subject: [PATCH 1236/1476] powerpc/powernv/sriov: Fix use of uninitialised variable Initialising the value before using it is generally regarded as a good idea so do that. Fixes: 4c51f3e1e870 ("powerpc/powernv/sriov: Make single PE mode a per-BAR setting") Reported-by: Nathan Chancellor Signed-off-by: Oliver O'Halloran Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20200803075408.132601-1-oohall@gmail.com --- arch/powerpc/platforms/powernv/pci-sriov.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/arch/powerpc/platforms/powernv/pci-sriov.c b/arch/powerpc/platforms/powernv/pci-sriov.c index 7894745fd4f85..c4434f20f42fa 100644 --- a/arch/powerpc/platforms/powernv/pci-sriov.c +++ b/arch/powerpc/platforms/powernv/pci-sriov.c @@ -253,9 +253,9 @@ void pnv_pci_ioda_fixup_iov(struct pci_dev *pdev) resource_size_t pnv_pci_iov_resource_alignment(struct pci_dev *pdev, int resno) { + resource_size_t align = pci_iov_resource_size(pdev, resno); struct pnv_phb *phb = pci_bus_to_pnvhb(pdev->bus); struct pnv_iov_data *iov = pnv_iov_get(pdev); - resource_size_t align; /* * iov can be null if we have an SR-IOV device with IOV BAR that can't @@ -266,8 +266,6 @@ resource_size_t pnv_pci_iov_resource_alignment(struct pci_dev *pdev, if (!iov) return align; - align = pci_iov_resource_size(pdev, resno); - /* * If we're using single mode then we can just use the native VF BAR * alignment. We validated that it's possible to use a single PE -- GitLab From f3e25911a430ed16ec209929183df762fe9c785b Mon Sep 17 00:00:00 2001 From: Kishon Vijay Abraham I Date: Wed, 22 Jul 2020 16:33:15 +0530 Subject: [PATCH 1237/1476] PCI: j721e: Add TI J721E PCIe driver Add support for PCIe controller in J721E SoC. The controller uses the Cadence PCIe core programmed by pcie-cadence*.c. The PCIe controller will work in both host mode and device mode. Some of the features of the controller are: *) Supports both RC mode and EP mode *) Supports MSI and MSI-X support *) Supports upto GEN3 speed mode *) Supports SR-IOV capability *) Ability to route all transactions via SMMU (support will be added in a later patch). Link: https://lore.kernel.org/r/20200722110317.4744-14-kishon@ti.com Signed-off-by: Kishon Vijay Abraham I Signed-off-by: Lorenzo Pieralisi --- drivers/pci/controller/cadence/Kconfig | 23 + drivers/pci/controller/cadence/Makefile | 1 + drivers/pci/controller/cadence/pci-j721e.c | 493 ++++++++++++++++++ .../controller/cadence/pcie-cadence-host.c | 4 +- drivers/pci/controller/cadence/pcie-cadence.h | 8 + 5 files changed, 527 insertions(+), 2 deletions(-) create mode 100644 drivers/pci/controller/cadence/pci-j721e.c diff --git a/drivers/pci/controller/cadence/Kconfig b/drivers/pci/controller/cadence/Kconfig index b76b3cf55ce5b..5d30564190e1f 100644 --- a/drivers/pci/controller/cadence/Kconfig +++ b/drivers/pci/controller/cadence/Kconfig @@ -42,4 +42,27 @@ config PCIE_CADENCE_PLAT_EP endpoint mode. This PCIe controller may be embedded into many different vendors SoCs. +config PCI_J721E + bool + +config PCI_J721E_HOST + bool "TI J721E PCIe platform host controller" + depends on OF + select PCIE_CADENCE_HOST + select PCI_J721E + help + Say Y here if you want to support the TI J721E PCIe platform + controller in host mode. TI J721E PCIe controller uses Cadence PCIe + core. + +config PCI_J721E_EP + bool "TI J721E PCIe platform endpoint controller" + depends on OF + depends on PCI_ENDPOINT + select PCIE_CADENCE_EP + select PCI_J721E + help + Say Y here if you want to support the TI J721E PCIe platform + controller in endpoint mode. TI J721E PCIe controller uses Cadence PCIe + core. endmenu diff --git a/drivers/pci/controller/cadence/Makefile b/drivers/pci/controller/cadence/Makefile index 232a3f20876ad..9bac5fb2f13da 100644 --- a/drivers/pci/controller/cadence/Makefile +++ b/drivers/pci/controller/cadence/Makefile @@ -3,3 +3,4 @@ obj-$(CONFIG_PCIE_CADENCE) += pcie-cadence.o obj-$(CONFIG_PCIE_CADENCE_HOST) += pcie-cadence-host.o obj-$(CONFIG_PCIE_CADENCE_EP) += pcie-cadence-ep.o obj-$(CONFIG_PCIE_CADENCE_PLAT) += pcie-cadence-plat.o +obj-$(CONFIG_PCI_J721E) += pci-j721e.o diff --git a/drivers/pci/controller/cadence/pci-j721e.c b/drivers/pci/controller/cadence/pci-j721e.c new file mode 100644 index 0000000000000..23ad8fa699c48 --- /dev/null +++ b/drivers/pci/controller/cadence/pci-j721e.c @@ -0,0 +1,493 @@ +// SPDX-License-Identifier: GPL-2.0 +/** + * pci-j721e - PCIe controller driver for TI's J721E SoCs + * + * Copyright (C) 2020 Texas Instruments Incorporated - http://www.ti.com + * Author: Kishon Vijay Abraham I + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../../pci.h" +#include "pcie-cadence.h" + +#define ENABLE_REG_SYS_2 0x108 +#define STATUS_REG_SYS_2 0x508 +#define STATUS_CLR_REG_SYS_2 0x708 +#define LINK_DOWN BIT(1) + +#define J721E_PCIE_USER_CMD_STATUS 0x4 +#define LINK_TRAINING_ENABLE BIT(0) + +#define J721E_PCIE_USER_LINKSTATUS 0x14 +#define LINK_STATUS GENMASK(1, 0) + +enum link_status { + NO_RECEIVERS_DETECTED, + LINK_TRAINING_IN_PROGRESS, + LINK_UP_DL_IN_PROGRESS, + LINK_UP_DL_COMPLETED, +}; + +#define J721E_MODE_RC BIT(7) +#define LANE_COUNT_MASK BIT(8) +#define LANE_COUNT(n) ((n) << 8) + +#define GENERATION_SEL_MASK GENMASK(1, 0) + +#define MAX_LANES 2 + +struct j721e_pcie { + struct device *dev; + u32 mode; + u32 num_lanes; + struct cdns_pcie *cdns_pcie; + void __iomem *user_cfg_base; + void __iomem *intd_cfg_base; +}; + +enum j721e_pcie_mode { + PCI_MODE_RC, + PCI_MODE_EP, +}; + +struct j721e_pcie_data { + enum j721e_pcie_mode mode; +}; + +static inline u32 j721e_pcie_user_readl(struct j721e_pcie *pcie, u32 offset) +{ + return readl(pcie->user_cfg_base + offset); +} + +static inline void j721e_pcie_user_writel(struct j721e_pcie *pcie, u32 offset, + u32 value) +{ + writel(value, pcie->user_cfg_base + offset); +} + +static inline u32 j721e_pcie_intd_readl(struct j721e_pcie *pcie, u32 offset) +{ + return readl(pcie->intd_cfg_base + offset); +} + +static inline void j721e_pcie_intd_writel(struct j721e_pcie *pcie, u32 offset, + u32 value) +{ + writel(value, pcie->intd_cfg_base + offset); +} + +static irqreturn_t j721e_pcie_link_irq_handler(int irq, void *priv) +{ + struct j721e_pcie *pcie = priv; + struct device *dev = pcie->dev; + u32 reg; + + reg = j721e_pcie_intd_readl(pcie, STATUS_REG_SYS_2); + if (!(reg & LINK_DOWN)) + return IRQ_NONE; + + dev_err(dev, "LINK DOWN!\n"); + + j721e_pcie_intd_writel(pcie, STATUS_CLR_REG_SYS_2, LINK_DOWN); + return IRQ_HANDLED; +} + +static void j721e_pcie_config_link_irq(struct j721e_pcie *pcie) +{ + u32 reg; + + reg = j721e_pcie_intd_readl(pcie, ENABLE_REG_SYS_2); + reg |= LINK_DOWN; + j721e_pcie_intd_writel(pcie, ENABLE_REG_SYS_2, reg); +} + +static int j721e_pcie_start_link(struct cdns_pcie *cdns_pcie) +{ + struct j721e_pcie *pcie = dev_get_drvdata(cdns_pcie->dev); + u32 reg; + + reg = j721e_pcie_user_readl(pcie, J721E_PCIE_USER_CMD_STATUS); + reg |= LINK_TRAINING_ENABLE; + j721e_pcie_user_writel(pcie, J721E_PCIE_USER_CMD_STATUS, reg); + + return 0; +} + +static void j721e_pcie_stop_link(struct cdns_pcie *cdns_pcie) +{ + struct j721e_pcie *pcie = dev_get_drvdata(cdns_pcie->dev); + u32 reg; + + reg = j721e_pcie_user_readl(pcie, J721E_PCIE_USER_CMD_STATUS); + reg &= ~LINK_TRAINING_ENABLE; + j721e_pcie_user_writel(pcie, J721E_PCIE_USER_CMD_STATUS, reg); +} + +static bool j721e_pcie_link_up(struct cdns_pcie *cdns_pcie) +{ + struct j721e_pcie *pcie = dev_get_drvdata(cdns_pcie->dev); + u32 reg; + + reg = j721e_pcie_user_readl(pcie, J721E_PCIE_USER_LINKSTATUS); + reg &= LINK_STATUS; + if (reg == LINK_UP_DL_COMPLETED) + return true; + + return false; +} + +static const struct cdns_pcie_ops j721e_pcie_ops = { + .start_link = j721e_pcie_start_link, + .stop_link = j721e_pcie_stop_link, + .link_up = j721e_pcie_link_up, +}; + +static int j721e_pcie_set_mode(struct j721e_pcie *pcie, struct regmap *syscon) +{ + struct device *dev = pcie->dev; + u32 mask = J721E_MODE_RC; + u32 mode = pcie->mode; + u32 val = 0; + int ret = 0; + + if (mode == PCI_MODE_RC) + val = J721E_MODE_RC; + + ret = regmap_update_bits(syscon, 0, mask, val); + if (ret) + dev_err(dev, "failed to set pcie mode\n"); + + return ret; +} + +static int j721e_pcie_set_link_speed(struct j721e_pcie *pcie, + struct regmap *syscon) +{ + struct device *dev = pcie->dev; + struct device_node *np = dev->of_node; + int link_speed; + u32 val = 0; + int ret; + + link_speed = of_pci_get_max_link_speed(np); + if (link_speed < 2) + link_speed = 2; + + val = link_speed - 1; + ret = regmap_update_bits(syscon, 0, GENERATION_SEL_MASK, val); + if (ret) + dev_err(dev, "failed to set link speed\n"); + + return ret; +} + +static int j721e_pcie_set_lane_count(struct j721e_pcie *pcie, + struct regmap *syscon) +{ + struct device *dev = pcie->dev; + u32 lanes = pcie->num_lanes; + u32 val = 0; + int ret; + + val = LANE_COUNT(lanes - 1); + ret = regmap_update_bits(syscon, 0, LANE_COUNT_MASK, val); + if (ret) + dev_err(dev, "failed to set link count\n"); + + return ret; +} + +static int j721e_pcie_ctrl_init(struct j721e_pcie *pcie) +{ + struct device *dev = pcie->dev; + struct device_node *node = dev->of_node; + struct regmap *syscon; + int ret; + + syscon = syscon_regmap_lookup_by_phandle(node, "ti,syscon-pcie-ctrl"); + if (IS_ERR(syscon)) { + dev_err(dev, "Unable to get ti,syscon-pcie-ctrl regmap\n"); + return PTR_ERR(syscon); + } + + ret = j721e_pcie_set_mode(pcie, syscon); + if (ret < 0) { + dev_err(dev, "Failed to set pci mode\n"); + return ret; + } + + ret = j721e_pcie_set_link_speed(pcie, syscon); + if (ret < 0) { + dev_err(dev, "Failed to set link speed\n"); + return ret; + } + + ret = j721e_pcie_set_lane_count(pcie, syscon); + if (ret < 0) { + dev_err(dev, "Failed to set num-lanes\n"); + return ret; + } + + return 0; +} + +static int cdns_ti_pcie_config_read(struct pci_bus *bus, unsigned int devfn, + int where, int size, u32 *value) +{ + struct pci_host_bridge *bridge = pci_find_host_bridge(bus); + struct cdns_pcie_rc *rc = pci_host_bridge_priv(bridge); + unsigned int busn = bus->number; + + if (busn == rc->bus_range->start) + return pci_generic_config_read32(bus, devfn, where, size, + value); + + return pci_generic_config_read(bus, devfn, where, size, value); +} + +static int cdns_ti_pcie_config_write(struct pci_bus *bus, unsigned int devfn, + int where, int size, u32 value) +{ + struct pci_host_bridge *bridge = pci_find_host_bridge(bus); + struct cdns_pcie_rc *rc = pci_host_bridge_priv(bridge); + unsigned int busn = bus->number; + + if (busn == rc->bus_range->start) + return pci_generic_config_write32(bus, devfn, where, size, + value); + + return pci_generic_config_write(bus, devfn, where, size, value); +} + +static struct pci_ops cdns_ti_pcie_host_ops = { + .map_bus = cdns_pci_map_bus, + .read = cdns_ti_pcie_config_read, + .write = cdns_ti_pcie_config_write, +}; + +static const struct j721e_pcie_data j721e_pcie_rc_data = { + .mode = PCI_MODE_RC, +}; + +static const struct j721e_pcie_data j721e_pcie_ep_data = { + .mode = PCI_MODE_EP, +}; + +static const struct of_device_id of_j721e_pcie_match[] = { + { + .compatible = "ti,j721e-pcie-host", + .data = &j721e_pcie_rc_data, + }, + { + .compatible = "ti,j721e-pcie-ep", + .data = &j721e_pcie_ep_data, + }, + {}, +}; + +static int j721e_pcie_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct device_node *node = dev->of_node; + struct pci_host_bridge *bridge; + struct j721e_pcie_data *data; + struct cdns_pcie *cdns_pcie; + struct j721e_pcie *pcie; + struct cdns_pcie_rc *rc; + struct cdns_pcie_ep *ep; + struct gpio_desc *gpiod; + void __iomem *base; + u32 num_lanes; + u32 mode; + int ret; + int irq; + + data = (struct j721e_pcie_data *)of_device_get_match_data(dev); + if (!data) + return -EINVAL; + + mode = (u32)data->mode; + + pcie = devm_kzalloc(dev, sizeof(*pcie), GFP_KERNEL); + if (!pcie) + return -ENOMEM; + + pcie->dev = dev; + pcie->mode = mode; + + base = devm_platform_ioremap_resource_byname(pdev, "intd_cfg"); + if (IS_ERR(base)) + return PTR_ERR(base); + pcie->intd_cfg_base = base; + + base = devm_platform_ioremap_resource_byname(pdev, "user_cfg"); + if (IS_ERR(base)) + return PTR_ERR(base); + pcie->user_cfg_base = base; + + ret = of_property_read_u32(node, "num-lanes", &num_lanes); + if (ret || num_lanes > MAX_LANES) + num_lanes = 1; + pcie->num_lanes = num_lanes; + + if (dma_set_mask_and_coherent(dev, DMA_BIT_MASK(48))) + return -EINVAL; + + irq = platform_get_irq_byname(pdev, "link_state"); + if (irq < 0) + return irq; + + dev_set_drvdata(dev, pcie); + pm_runtime_enable(dev); + ret = pm_runtime_get_sync(dev); + if (ret < 0) { + dev_err(dev, "pm_runtime_get_sync failed\n"); + goto err_get_sync; + } + + ret = j721e_pcie_ctrl_init(pcie); + if (ret < 0) { + dev_err(dev, "pm_runtime_get_sync failed\n"); + goto err_get_sync; + } + + ret = devm_request_irq(dev, irq, j721e_pcie_link_irq_handler, 0, + "j721e-pcie-link-down-irq", pcie); + if (ret < 0) { + dev_err(dev, "failed to request link state IRQ %d\n", irq); + goto err_get_sync; + } + + j721e_pcie_config_link_irq(pcie); + + switch (mode) { + case PCI_MODE_RC: + if (!IS_ENABLED(CONFIG_PCIE_CADENCE_HOST)) { + ret = -ENODEV; + goto err_get_sync; + } + + bridge = devm_pci_alloc_host_bridge(dev, sizeof(*rc)); + if (!bridge) { + ret = -ENOMEM; + goto err_get_sync; + } + + bridge->ops = &cdns_ti_pcie_host_ops; + rc = pci_host_bridge_priv(bridge); + + cdns_pcie = &rc->pcie; + cdns_pcie->dev = dev; + cdns_pcie->ops = &j721e_pcie_ops; + pcie->cdns_pcie = cdns_pcie; + + gpiod = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW); + if (IS_ERR(gpiod)) { + ret = PTR_ERR(gpiod); + if (ret != -EPROBE_DEFER) + dev_err(dev, "Failed to get reset GPIO\n"); + goto err_get_sync; + } + + ret = cdns_pcie_init_phy(dev, cdns_pcie); + if (ret) { + dev_err(dev, "Failed to init phy\n"); + goto err_get_sync; + } + + /* + * "Power Sequencing and Reset Signal Timings" table in + * PCI EXPRESS CARD ELECTROMECHANICAL SPECIFICATION, REV. 3.0 + * indicates PERST# should be deasserted after minimum of 100us + * once REFCLK is stable. The REFCLK to the connector in RC + * mode is selected while enabling the PHY. So deassert PERST# + * after 100 us. + */ + if (gpiod) { + usleep_range(100, 200); + gpiod_set_value_cansleep(gpiod, 1); + } + + ret = cdns_pcie_host_setup(rc); + if (ret < 0) + goto err_pcie_setup; + + break; + case PCI_MODE_EP: + if (!IS_ENABLED(CONFIG_PCIE_CADENCE_EP)) { + ret = -ENODEV; + goto err_get_sync; + } + + ep = devm_kzalloc(dev, sizeof(*ep), GFP_KERNEL); + if (!ep) { + ret = -ENOMEM; + goto err_get_sync; + } + + cdns_pcie = &ep->pcie; + cdns_pcie->dev = dev; + cdns_pcie->ops = &j721e_pcie_ops; + pcie->cdns_pcie = cdns_pcie; + + ret = cdns_pcie_init_phy(dev, cdns_pcie); + if (ret) { + dev_err(dev, "Failed to init phy\n"); + goto err_get_sync; + } + + ret = cdns_pcie_ep_setup(ep); + if (ret < 0) + goto err_pcie_setup; + + break; + default: + dev_err(dev, "INVALID device type %d\n", mode); + } + + return 0; + +err_pcie_setup: + cdns_pcie_disable_phy(cdns_pcie); + +err_get_sync: + pm_runtime_put(dev); + pm_runtime_disable(dev); + + return ret; +} + +static int j721e_pcie_remove(struct platform_device *pdev) +{ + struct j721e_pcie *pcie = platform_get_drvdata(pdev); + struct cdns_pcie *cdns_pcie = pcie->cdns_pcie; + struct device *dev = &pdev->dev; + + cdns_pcie_disable_phy(cdns_pcie); + pm_runtime_put(dev); + pm_runtime_disable(dev); + + return 0; +} + +static struct platform_driver j721e_pcie_driver = { + .probe = j721e_pcie_probe, + .remove = j721e_pcie_remove, + .driver = { + .name = "j721e-pcie", + .of_match_table = of_j721e_pcie_match, + .suppress_bind_attrs = true, + }, +}; +builtin_platform_driver(j721e_pcie_driver); diff --git a/drivers/pci/controller/cadence/pcie-cadence-host.c b/drivers/pci/controller/cadence/pcie-cadence-host.c index 89d26324b2a8d..8d86560196aaf 100644 --- a/drivers/pci/controller/cadence/pcie-cadence-host.c +++ b/drivers/pci/controller/cadence/pcie-cadence-host.c @@ -23,8 +23,8 @@ static u8 bar_aperture_mask[] = { [RP_BAR1] = 0xF, }; -static void __iomem *cdns_pci_map_bus(struct pci_bus *bus, unsigned int devfn, - int where) +void __iomem *cdns_pci_map_bus(struct pci_bus *bus, unsigned int devfn, + int where) { struct pci_host_bridge *bridge = pci_find_host_bridge(bus); struct cdns_pcie_rc *rc = pci_host_bridge_priv(bridge); diff --git a/drivers/pci/controller/cadence/pcie-cadence.h b/drivers/pci/controller/cadence/pcie-cadence.h index dd910a1c30fbf..00e44256c3e8b 100644 --- a/drivers/pci/controller/cadence/pcie-cadence.h +++ b/drivers/pci/controller/cadence/pcie-cadence.h @@ -475,11 +475,19 @@ static inline bool cdns_pcie_link_up(struct cdns_pcie *pcie) #ifdef CONFIG_PCIE_CADENCE_HOST int cdns_pcie_host_setup(struct cdns_pcie_rc *rc); +void __iomem *cdns_pci_map_bus(struct pci_bus *bus, unsigned int devfn, + int where); #else static inline int cdns_pcie_host_setup(struct cdns_pcie_rc *rc) { return 0; } + +static inline void __iomem *cdns_pci_map_bus(struct pci_bus *bus, unsigned int devfn, + int where) +{ + return NULL; +} #endif #ifdef CONFIG_PCIE_CADENCE_EP -- GitLab From 6546ae29964e85af909f366ebeec25c47adbc95d Mon Sep 17 00:00:00 2001 From: Kishon Vijay Abraham I Date: Wed, 22 Jul 2020 16:33:16 +0530 Subject: [PATCH 1238/1476] misc: pci_endpoint_test: Add J721E in pci_device_id table Add J721E in pci_device_id table so that pci-epf-test can be used for testing PCIe EP in J721E. Link: https://lore.kernel.org/r/20200722110317.4744-15-kishon@ti.com Signed-off-by: Kishon Vijay Abraham I Signed-off-by: Lorenzo Pieralisi Reviewed-by: Rob Herring --- drivers/misc/pci_endpoint_test.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/misc/pci_endpoint_test.c b/drivers/misc/pci_endpoint_test.c index 41c40971979e5..e060796f9caa6 100644 --- a/drivers/misc/pci_endpoint_test.c +++ b/drivers/misc/pci_endpoint_test.c @@ -68,6 +68,7 @@ #define PCI_ENDPOINT_TEST_FLAGS 0x2c #define FLAG_USE_DMA BIT(0) +#define PCI_DEVICE_ID_TI_J721E 0xb00d #define PCI_DEVICE_ID_TI_AM654 0xb00c #define is_am654_pci_dev(pdev) \ @@ -932,6 +933,11 @@ static const struct pci_endpoint_test_data am654_data = { .irq_type = IRQ_TYPE_MSI, }; +static const struct pci_endpoint_test_data j721e_data = { + .alignment = 256, + .irq_type = IRQ_TYPE_MSI, +}; + static const struct pci_device_id pci_endpoint_test_tbl[] = { { PCI_DEVICE(PCI_VENDOR_ID_TI, PCI_DEVICE_ID_TI_DRA74x), .driver_data = (kernel_ulong_t)&default_data, @@ -946,6 +952,9 @@ static const struct pci_device_id pci_endpoint_test_tbl[] = { }, { PCI_DEVICE(PCI_VENDOR_ID_RENESAS, PCI_DEVICE_ID_RENESAS_R8A774C0), }, + { PCI_DEVICE(PCI_VENDOR_ID_TI, PCI_DEVICE_ID_TI_J721E), + .driver_data = (kernel_ulong_t)&j721e_data, + }, { } }; MODULE_DEVICE_TABLE(pci, pci_endpoint_test_tbl); -- GitLab From 0dbe77c9d2f8e6ef1792db99e150dd0025a977eb Mon Sep 17 00:00:00 2001 From: Kishon Vijay Abraham I Date: Wed, 22 Jul 2020 16:33:17 +0530 Subject: [PATCH 1239/1476] MAINTAINERS: Add Kishon Vijay Abraham I for TI J721E SoC PCIe Add Kishon Vijay Abraham I as MAINTAINER for TI J721E SoC PCIe. Link: https://lore.kernel.org/r/20200722110317.4744-16-kishon@ti.com Signed-off-by: Kishon Vijay Abraham I Signed-off-by: Lorenzo Pieralisi Acked-by: Rob Herring --- MAINTAINERS | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/MAINTAINERS b/MAINTAINERS index 68f21d46614c4..6df9b54d30034 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -13149,12 +13149,14 @@ S: Maintained F: Documentation/devicetree/bindings/pci/designware-pcie.txt F: drivers/pci/controller/dwc/*designware* -PCI DRIVER FOR TI DRA7XX +PCI DRIVER FOR TI DRA7XX/J721E M: Kishon Vijay Abraham I L: linux-omap@vger.kernel.org L: linux-pci@vger.kernel.org +L: linux-arm-kernel@lists.infradead.org S: Supported F: Documentation/devicetree/bindings/pci/ti-pci.txt +F: drivers/pci/controller/cadence/pci-j721e.c F: drivers/pci/controller/dwc/pci-dra7xx.c PCI DRIVER FOR TI KEYSTONE -- GitLab From caecb05c800081c57907749f787f05f62011564e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20Wilczy=C5=84ski?= Date: Sun, 2 Aug 2020 14:25:53 +0000 Subject: [PATCH 1240/1476] PCI: Remove dev_err() when handing an error from platform_get_irq() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There is no need to call the dev_err() function directly to print a custom message when handling an error from either the platform_get_irq() or platform_get_irq_byname() functions as both are going to display an appropriate error message in case of a failure. This change is as per suggestions from Coccinelle, e.g., drivers/pci/controller/dwc/pcie-armada8k.c:252:2-9: line 252 is redundant because platform_get_irq() already prints an error [bhelgaas: squashed into one commit] Suggested-by: Bjorn Helgaas Link: https://lore.kernel.org/r/20200802142601.1635926-2-kw@linux.com Link: https://lore.kernel.org/r/20200802142601.1635926-3-kw@linux.com Link: https://lore.kernel.org/r/20200802142601.1635926-4-kw@linux.com Link: https://lore.kernel.org/r/20200802142601.1635926-5-kw@linux.com Link: https://lore.kernel.org/r/20200802142601.1635926-6-kw@linux.com Link: https://lore.kernel.org/r/20200802142601.1635926-7-kw@linux.com Link: https://lore.kernel.org/r/20200802142601.1635926-8-kw@linux.com Link: https://lore.kernel.org/r/20200802142601.1635926-9-kw@linux.com Link: https://lore.kernel.org/r/20200802142601.1635926-10-kw@linux.com Link: https://lore.kernel.org/r/20200803071040.1663662-1-kw@linux.com Signed-off-by: Krzysztof Wilczyński Signed-off-by: Bjorn Helgaas Reviewed-by: Ley Foon Tan # altera Acked-by: Jesper Nilsson # dwc --- drivers/pci/controller/dwc/pci-dra7xx.c | 8 ++------ drivers/pci/controller/dwc/pci-exynos.c | 9 +++------ drivers/pci/controller/dwc/pci-imx6.c | 4 +--- drivers/pci/controller/dwc/pci-keystone.c | 4 +--- drivers/pci/controller/dwc/pci-meson.c | 4 +--- drivers/pci/controller/dwc/pcie-armada8k.c | 4 +--- drivers/pci/controller/dwc/pcie-artpec6.c | 4 +--- drivers/pci/controller/dwc/pcie-histb.c | 4 +--- drivers/pci/controller/dwc/pcie-kirin.c | 5 +---- drivers/pci/controller/dwc/pcie-spear13xx.c | 5 ++--- drivers/pci/controller/dwc/pcie-tegra194.c | 4 +--- .../pci/controller/mobiveil/pcie-layerscape-gen4.c | 5 ++--- drivers/pci/controller/mobiveil/pcie-mobiveil-host.c | 4 +--- drivers/pci/controller/pci-tegra.c | 8 ++------ drivers/pci/controller/pci-v3-semi.c | 5 ++--- drivers/pci/controller/pci-xgene-msi.c | 2 -- drivers/pci/controller/pcie-altera-msi.c | 1 - drivers/pci/controller/pcie-altera.c | 4 +--- drivers/pci/controller/pcie-rockchip-host.c | 12 +++--------- drivers/pci/controller/pcie-tango.c | 4 +--- drivers/pci/controller/pcie-xilinx-nwl.c | 11 ++--------- 21 files changed, 29 insertions(+), 82 deletions(-) diff --git a/drivers/pci/controller/dwc/pci-dra7xx.c b/drivers/pci/controller/dwc/pci-dra7xx.c index 6184ebc9392db..66465dd698584 100644 --- a/drivers/pci/controller/dwc/pci-dra7xx.c +++ b/drivers/pci/controller/dwc/pci-dra7xx.c @@ -629,10 +629,8 @@ static int __init dra7xx_add_pcie_port(struct dra7xx_pcie *dra7xx, struct resource *res; pp->irq = platform_get_irq(pdev, 1); - if (pp->irq < 0) { - dev_err(dev, "missing IRQ resource\n"); + if (pp->irq < 0) return pp->irq; - } ret = dra7xx_pcie_init_irq_domain(pp); if (ret < 0) @@ -871,10 +869,8 @@ static int __init dra7xx_pcie_probe(struct platform_device *pdev) pci->ops = &dw_pcie_ops; irq = platform_get_irq(pdev, 0); - if (irq < 0) { - dev_err(dev, "missing IRQ resource: %d\n", irq); + if (irq < 0) return irq; - } base = devm_platform_ioremap_resource_byname(pdev, "ti_conf"); if (IS_ERR(base)) diff --git a/drivers/pci/controller/dwc/pci-exynos.c b/drivers/pci/controller/dwc/pci-exynos.c index c5043d951e809..f721fddc521e6 100644 --- a/drivers/pci/controller/dwc/pci-exynos.c +++ b/drivers/pci/controller/dwc/pci-exynos.c @@ -402,10 +402,9 @@ static int __init exynos_add_pcie_port(struct exynos_pcie *ep, int ret; pp->irq = platform_get_irq(pdev, 1); - if (pp->irq < 0) { - dev_err(dev, "failed to get irq\n"); + if (pp->irq < 0) return pp->irq; - } + ret = devm_request_irq(dev, pp->irq, exynos_pcie_irq_handler, IRQF_SHARED, "exynos-pcie", ep); if (ret) { @@ -415,10 +414,8 @@ static int __init exynos_add_pcie_port(struct exynos_pcie *ep, if (IS_ENABLED(CONFIG_PCI_MSI)) { pp->msi_irq = platform_get_irq(pdev, 0); - if (pp->msi_irq < 0) { - dev_err(dev, "failed to get msi irq\n"); + if (pp->msi_irq < 0) return pp->msi_irq; - } } pp->ops = &exynos_pcie_host_ops; diff --git a/drivers/pci/controller/dwc/pci-imx6.c b/drivers/pci/controller/dwc/pci-imx6.c index 8f08ae53f53e4..aba0a1514a86d 100644 --- a/drivers/pci/controller/dwc/pci-imx6.c +++ b/drivers/pci/controller/dwc/pci-imx6.c @@ -868,10 +868,8 @@ static int imx6_add_pcie_port(struct imx6_pcie *imx6_pcie, if (IS_ENABLED(CONFIG_PCI_MSI)) { pp->msi_irq = platform_get_irq_byname(pdev, "msi"); - if (pp->msi_irq < 0) { - dev_err(dev, "failed to get MSI irq\n"); + if (pp->msi_irq < 0) return pp->msi_irq; - } } pp->ops = &imx6_pcie_host_ops; diff --git a/drivers/pci/controller/dwc/pci-keystone.c b/drivers/pci/controller/dwc/pci-keystone.c index 790679fdfa488..99df9aeece1f2 100644 --- a/drivers/pci/controller/dwc/pci-keystone.c +++ b/drivers/pci/controller/dwc/pci-keystone.c @@ -1250,10 +1250,8 @@ static int __init ks_pcie_probe(struct platform_device *pdev) pci->version = version; irq = platform_get_irq(pdev, 0); - if (irq < 0) { - dev_err(dev, "missing IRQ resource: %d\n", irq); + if (irq < 0) return irq; - } ret = request_irq(irq, ks_pcie_err_irq_handler, IRQF_SHARED, "ks-pcie-error-irq", ks_pcie); diff --git a/drivers/pci/controller/dwc/pci-meson.c b/drivers/pci/controller/dwc/pci-meson.c index ca59ba9e0ecd3..4f183b96afbbf 100644 --- a/drivers/pci/controller/dwc/pci-meson.c +++ b/drivers/pci/controller/dwc/pci-meson.c @@ -488,10 +488,8 @@ static int meson_add_pcie_port(struct meson_pcie *mp, if (IS_ENABLED(CONFIG_PCI_MSI)) { pp->msi_irq = platform_get_irq(pdev, 0); - if (pp->msi_irq < 0) { - dev_err(dev, "failed to get MSI IRQ\n"); + if (pp->msi_irq < 0) return pp->msi_irq; - } } pp->ops = &meson_pcie_host_ops; diff --git a/drivers/pci/controller/dwc/pcie-armada8k.c b/drivers/pci/controller/dwc/pcie-armada8k.c index 49596547e8c29..66ec21cd9487f 100644 --- a/drivers/pci/controller/dwc/pcie-armada8k.c +++ b/drivers/pci/controller/dwc/pcie-armada8k.c @@ -248,10 +248,8 @@ static int armada8k_add_pcie_port(struct armada8k_pcie *pcie, pp->ops = &armada8k_pcie_host_ops; pp->irq = platform_get_irq(pdev, 0); - if (pp->irq < 0) { - dev_err(dev, "failed to get irq for port\n"); + if (pp->irq < 0) return pp->irq; - } ret = devm_request_irq(dev, pp->irq, armada8k_pcie_irq_handler, IRQF_SHARED, "armada8k-pcie", pcie); diff --git a/drivers/pci/controller/dwc/pcie-artpec6.c b/drivers/pci/controller/dwc/pcie-artpec6.c index 28d5a10952000..d429bd2b24066 100644 --- a/drivers/pci/controller/dwc/pcie-artpec6.c +++ b/drivers/pci/controller/dwc/pcie-artpec6.c @@ -387,10 +387,8 @@ static int artpec6_add_pcie_port(struct artpec6_pcie *artpec6_pcie, if (IS_ENABLED(CONFIG_PCI_MSI)) { pp->msi_irq = platform_get_irq_byname(pdev, "msi"); - if (pp->msi_irq < 0) { - dev_err(dev, "failed to get MSI irq\n"); + if (pp->msi_irq < 0) return pp->msi_irq; - } } pp->ops = &artpec6_pcie_host_ops; diff --git a/drivers/pci/controller/dwc/pcie-histb.c b/drivers/pci/controller/dwc/pcie-histb.c index 811b5c6d62eae..006437eadc314 100644 --- a/drivers/pci/controller/dwc/pcie-histb.c +++ b/drivers/pci/controller/dwc/pcie-histb.c @@ -402,10 +402,8 @@ static int histb_pcie_probe(struct platform_device *pdev) if (IS_ENABLED(CONFIG_PCI_MSI)) { pp->msi_irq = platform_get_irq_byname(pdev, "msi"); - if (pp->msi_irq < 0) { - dev_err(dev, "Failed to get MSI IRQ\n"); + if (pp->msi_irq < 0) return pp->msi_irq; - } } hipcie->phy = devm_phy_get(dev, "phy"); diff --git a/drivers/pci/controller/dwc/pcie-kirin.c b/drivers/pci/controller/dwc/pcie-kirin.c index c19617a912bd5..2e0181ce68957 100644 --- a/drivers/pci/controller/dwc/pcie-kirin.c +++ b/drivers/pci/controller/dwc/pcie-kirin.c @@ -455,11 +455,8 @@ static int kirin_pcie_add_msi(struct dw_pcie *pci, if (IS_ENABLED(CONFIG_PCI_MSI)) { irq = platform_get_irq(pdev, 0); - if (irq < 0) { - dev_err(&pdev->dev, - "failed to get MSI IRQ (%d)\n", irq); + if (irq < 0) return irq; - } pci->pp.msi_irq = irq; } diff --git a/drivers/pci/controller/dwc/pcie-spear13xx.c b/drivers/pci/controller/dwc/pcie-spear13xx.c index 7d0cdfd8138b9..ad5d08d199268 100644 --- a/drivers/pci/controller/dwc/pcie-spear13xx.c +++ b/drivers/pci/controller/dwc/pcie-spear13xx.c @@ -198,10 +198,9 @@ static int spear13xx_add_pcie_port(struct spear13xx_pcie *spear13xx_pcie, int ret; pp->irq = platform_get_irq(pdev, 0); - if (pp->irq < 0) { - dev_err(dev, "failed to get irq\n"); + if (pp->irq < 0) return pp->irq; - } + ret = devm_request_irq(dev, pp->irq, spear13xx_pcie_irq_handler, IRQF_SHARED | IRQF_NO_THREAD, "spear1340-pcie", spear13xx_pcie); diff --git a/drivers/pci/controller/dwc/pcie-tegra194.c b/drivers/pci/controller/dwc/pcie-tegra194.c index 92b77f7d83546..70498689d0c0a 100644 --- a/drivers/pci/controller/dwc/pcie-tegra194.c +++ b/drivers/pci/controller/dwc/pcie-tegra194.c @@ -2189,10 +2189,8 @@ static int tegra_pcie_dw_probe(struct platform_device *pdev) } pp->irq = platform_get_irq_byname(pdev, "intr"); - if (pp->irq < 0) { - dev_err(dev, "Failed to get \"intr\" interrupt\n"); + if (pp->irq < 0) return pp->irq; - } pcie->bpmp = tegra_bpmp_get(dev); if (IS_ERR(pcie->bpmp)) diff --git a/drivers/pci/controller/mobiveil/pcie-layerscape-gen4.c b/drivers/pci/controller/mobiveil/pcie-layerscape-gen4.c index a6d2190a67531..ee0156921ebcb 100644 --- a/drivers/pci/controller/mobiveil/pcie-layerscape-gen4.c +++ b/drivers/pci/controller/mobiveil/pcie-layerscape-gen4.c @@ -170,10 +170,9 @@ static int ls_pcie_g4_interrupt_init(struct mobiveil_pcie *mv_pci) int ret; pcie->irq = platform_get_irq_byname(pdev, "intr"); - if (pcie->irq < 0) { - dev_err(dev, "Can't get 'intr' IRQ, errno = %d\n", pcie->irq); + if (pcie->irq < 0) return pcie->irq; - } + ret = devm_request_irq(dev, pcie->irq, ls_pcie_g4_isr, IRQF_SHARED, pdev->name, pcie); if (ret) { diff --git a/drivers/pci/controller/mobiveil/pcie-mobiveil-host.c b/drivers/pci/controller/mobiveil/pcie-mobiveil-host.c index 5907baa9b1f21..fa321fcdd4bc8 100644 --- a/drivers/pci/controller/mobiveil/pcie-mobiveil-host.c +++ b/drivers/pci/controller/mobiveil/pcie-mobiveil-host.c @@ -522,10 +522,8 @@ static int mobiveil_pcie_integrated_interrupt_init(struct mobiveil_pcie *pcie) mobiveil_pcie_enable_msi(pcie); rp->irq = platform_get_irq(pdev, 0); - if (rp->irq < 0) { - dev_err(dev, "failed to map IRQ: %d\n", rp->irq); + if (rp->irq < 0) return rp->irq; - } /* initialize the IRQ domains */ ret = mobiveil_pcie_init_irq_domain(pcie); diff --git a/drivers/pci/controller/pci-tegra.c b/drivers/pci/controller/pci-tegra.c index 235b456698fc7..b11397475a4da 100644 --- a/drivers/pci/controller/pci-tegra.c +++ b/drivers/pci/controller/pci-tegra.c @@ -1520,10 +1520,8 @@ static int tegra_pcie_get_resources(struct tegra_pcie *pcie) /* request interrupt */ err = platform_get_irq_byname(pdev, "intr"); - if (err < 0) { - dev_err(dev, "failed to get IRQ: %d\n", err); + if (err < 0) goto phys_put; - } pcie->irq = err; @@ -1738,10 +1736,8 @@ static int tegra_pcie_msi_setup(struct tegra_pcie *pcie) } err = platform_get_irq_byname(pdev, "msi"); - if (err < 0) { - dev_err(dev, "failed to get IRQ: %d\n", err); + if (err < 0) goto free_irq_domain; - } msi->irq = err; diff --git a/drivers/pci/controller/pci-v3-semi.c b/drivers/pci/controller/pci-v3-semi.c index 3681e5af38785..2a19f2a989453 100644 --- a/drivers/pci/controller/pci-v3-semi.c +++ b/drivers/pci/controller/pci-v3-semi.c @@ -777,10 +777,9 @@ static int v3_pci_probe(struct platform_device *pdev) /* Get and request error IRQ resource */ irq = platform_get_irq(pdev, 0); - if (irq < 0) { - dev_err(dev, "unable to obtain PCIv3 error IRQ\n"); + if (irq < 0) return irq; - } + ret = devm_request_irq(dev, irq, v3_irq, 0, "PCIv3 error", v3); if (ret < 0) { diff --git a/drivers/pci/controller/pci-xgene-msi.c b/drivers/pci/controller/pci-xgene-msi.c index f4c02da84e599..02271c6d17a11 100644 --- a/drivers/pci/controller/pci-xgene-msi.c +++ b/drivers/pci/controller/pci-xgene-msi.c @@ -478,8 +478,6 @@ static int xgene_msi_probe(struct platform_device *pdev) for (irq_index = 0; irq_index < NR_HW_IRQS; irq_index++) { virt_msir = platform_get_irq(pdev, irq_index); if (virt_msir < 0) { - dev_err(&pdev->dev, "Cannot translate IRQ index %d\n", - irq_index); rc = virt_msir; goto error; } diff --git a/drivers/pci/controller/pcie-altera-msi.c b/drivers/pci/controller/pcie-altera-msi.c index 16d938920ca57..bec666eda1eb1 100644 --- a/drivers/pci/controller/pcie-altera-msi.c +++ b/drivers/pci/controller/pcie-altera-msi.c @@ -256,7 +256,6 @@ static int altera_msi_probe(struct platform_device *pdev) msi->irq = platform_get_irq(pdev, 0); if (msi->irq < 0) { - dev_err(&pdev->dev, "failed to map IRQ: %d\n", msi->irq); ret = msi->irq; goto err; } diff --git a/drivers/pci/controller/pcie-altera.c b/drivers/pci/controller/pcie-altera.c index 24cb1c3310584..f6d73d282230f 100644 --- a/drivers/pci/controller/pcie-altera.c +++ b/drivers/pci/controller/pcie-altera.c @@ -713,10 +713,8 @@ static int altera_pcie_parse_dt(struct altera_pcie *pcie) /* setup IRQ */ pcie->irq = platform_get_irq(pdev, 0); - if (pcie->irq < 0) { - dev_err(dev, "failed to get IRQ: %d\n", pcie->irq); + if (pcie->irq < 0) return pcie->irq; - } irq_set_chained_handler_and_data(pcie->irq, altera_pcie_isr, pcie); return 0; diff --git a/drivers/pci/controller/pcie-rockchip-host.c b/drivers/pci/controller/pcie-rockchip-host.c index 94af6f5828a3d..eebe05ab354fd 100644 --- a/drivers/pci/controller/pcie-rockchip-host.c +++ b/drivers/pci/controller/pcie-rockchip-host.c @@ -549,10 +549,8 @@ static int rockchip_pcie_setup_irq(struct rockchip_pcie *rockchip) struct platform_device *pdev = to_platform_device(dev); irq = platform_get_irq_byname(pdev, "sys"); - if (irq < 0) { - dev_err(dev, "missing sys IRQ resource\n"); + if (irq < 0) return irq; - } err = devm_request_irq(dev, irq, rockchip_pcie_subsys_irq_handler, IRQF_SHARED, "pcie-sys", rockchip); @@ -562,20 +560,16 @@ static int rockchip_pcie_setup_irq(struct rockchip_pcie *rockchip) } irq = platform_get_irq_byname(pdev, "legacy"); - if (irq < 0) { - dev_err(dev, "missing legacy IRQ resource\n"); + if (irq < 0) return irq; - } irq_set_chained_handler_and_data(irq, rockchip_pcie_legacy_int_handler, rockchip); irq = platform_get_irq_byname(pdev, "client"); - if (irq < 0) { - dev_err(dev, "missing client IRQ resource\n"); + if (irq < 0) return irq; - } err = devm_request_irq(dev, irq, rockchip_pcie_client_irq_handler, IRQF_SHARED, "pcie-client", rockchip); diff --git a/drivers/pci/controller/pcie-tango.c b/drivers/pci/controller/pcie-tango.c index 8f640c70f9369..d093a8ce4bb17 100644 --- a/drivers/pci/controller/pcie-tango.c +++ b/drivers/pci/controller/pcie-tango.c @@ -273,10 +273,8 @@ static int tango_pcie_probe(struct platform_device *pdev) writel_relaxed(0, pcie->base + SMP8759_ENABLE + offset); virq = platform_get_irq(pdev, 1); - if (virq < 0) { - dev_err(dev, "Failed to map IRQ\n"); + if (virq < 0) return virq; - } irq_dom = irq_domain_create_linear(fwnode, MSI_MAX, &dom_ops, pcie); if (!irq_dom) { diff --git a/drivers/pci/controller/pcie-xilinx-nwl.c b/drivers/pci/controller/pcie-xilinx-nwl.c index 9bd1427f2fd6d..55489b7284845 100644 --- a/drivers/pci/controller/pcie-xilinx-nwl.c +++ b/drivers/pci/controller/pcie-xilinx-nwl.c @@ -586,7 +586,6 @@ static int nwl_pcie_enable_msi(struct nwl_pcie *pcie) /* Get msi_1 IRQ number */ msi->irq_msi1 = platform_get_irq_byname(pdev, "msi1"); if (msi->irq_msi1 < 0) { - dev_err(dev, "failed to get IRQ#%d\n", msi->irq_msi1); ret = -EINVAL; goto err; } @@ -597,7 +596,6 @@ static int nwl_pcie_enable_msi(struct nwl_pcie *pcie) /* Get msi_0 IRQ number */ msi->irq_msi0 = platform_get_irq_byname(pdev, "msi0"); if (msi->irq_msi0 < 0) { - dev_err(dev, "failed to get IRQ#%d\n", msi->irq_msi0); ret = -EINVAL; goto err; } @@ -728,11 +726,8 @@ static int nwl_pcie_bridge_init(struct nwl_pcie *pcie) /* Get misc IRQ number */ pcie->irq_misc = platform_get_irq_byname(pdev, "misc"); - if (pcie->irq_misc < 0) { - dev_err(dev, "failed to get misc IRQ %d\n", - pcie->irq_misc); + if (pcie->irq_misc < 0) return -EINVAL; - } err = devm_request_irq(dev, pcie->irq_misc, nwl_pcie_misc_handler, IRQF_SHARED, @@ -797,10 +792,8 @@ static int nwl_pcie_parse_dt(struct nwl_pcie *pcie, /* Get intx IRQ number */ pcie->irq_intx = platform_get_irq_byname(pdev, "intx"); - if (pcie->irq_intx < 0) { - dev_err(dev, "failed to get intx IRQ %d\n", pcie->irq_intx); + if (pcie->irq_intx < 0) return pcie->irq_intx; - } irq_set_chained_handler_and_data(pcie->irq_intx, nwl_pcie_leg_handler, pcie); -- GitLab From 0f69dae4d1069963fdcc16e63655927d83281006 Mon Sep 17 00:00:00 2001 From: Zhaoyang Huang Date: Fri, 31 Jul 2020 08:27:45 +0800 Subject: [PATCH 1241/1476] trace : Have tracing buffer info use kvzalloc instead of kzalloc MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit High order memory stuff within trace could introduce OOM, use kvzalloc instead. Please find the bellowing for the call stack we run across in an android system. The scenario happens when traced_probes is woken up to get a large quantity of trace even if free memory is even higher than watermark_low.  traced_probes invoked oom-killer: gfp_mask=0x140c0c0(GFP_KERNEL|__GFP_COMP|__GFP_ZERO), nodemask=(null), order=2, oom_score_adj=-1 traced_probes cpuset=system-background mems_allowed=0 CPU: 3 PID: 588 Comm: traced_probes Tainted: G W O 4.14.181 #1 Hardware name: Generic DT based system (unwind_backtrace) from [] (show_stack+0x20/0x24) (show_stack) from [] (dump_stack+0xa8/0xec) (dump_stack) from [] (dump_header+0x9c/0x220) (dump_header) from [] (oom_kill_process+0xc0/0x5c4) (oom_kill_process) from [] (out_of_memory+0x220/0x310) (out_of_memory) from [] (__alloc_pages_nodemask+0xff8/0x13a4) (__alloc_pages_nodemask) from [] (kmalloc_order+0x30/0x48) (kmalloc_order) from [] (kmalloc_order_trace+0x30/0x118) (kmalloc_order_trace) from [] (tracing_buffers_open+0x50/0xfc) (tracing_buffers_open) from [] (do_dentry_open+0x278/0x34c) (do_dentry_open) from [] (vfs_open+0x50/0x70) (vfs_open) from [] (path_openat+0x5fc/0x169c) (path_openat) from [] (do_filp_open+0x94/0xf8) (do_filp_open) from [] (do_sys_open+0x168/0x26c) (do_sys_open) from [] (SyS_openat+0x34/0x38) (SyS_openat) from [] (ret_fast_syscall+0x0/0x28) Link: https://lkml.kernel.org/r/1596155265-32365-1-git-send-email-zhaoyang.huang@unisoc.com Signed-off-by: Zhaoyang Huang Signed-off-by: Steven Rostedt (VMware) --- kernel/trace/trace.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c index dbcacdd56b020..06c0feae5ff98 100644 --- a/kernel/trace/trace.c +++ b/kernel/trace/trace.c @@ -7402,7 +7402,7 @@ static int tracing_buffers_open(struct inode *inode, struct file *filp) if (ret) return ret; - info = kzalloc(sizeof(*info), GFP_KERNEL); + info = kvzalloc(sizeof(*info), GFP_KERNEL); if (!info) { trace_array_put(tr); return -ENOMEM; @@ -7528,7 +7528,7 @@ static int tracing_buffers_release(struct inode *inode, struct file *file) if (info->spare) ring_buffer_free_read_page(iter->array_buffer->buffer, info->spare_cpu, info->spare); - kfree(info); + kvfree(info); mutex_unlock(&trace_types_lock); -- GitLab From f3751ad0116fb6881f2c3c957d66a9327f69cefb Mon Sep 17 00:00:00 2001 From: Nick Desaulniers Date: Thu, 30 Jul 2020 15:45:54 -0700 Subject: [PATCH 1242/1476] tracepoint: Mark __tracepoint_string's __used __tracepoint_string's have their string data stored in .rodata, and an address to that data stored in the "__tracepoint_str" section. Functions that refer to those strings refer to the symbol of the address. Compiler optimization can replace those address references with references directly to the string data. If the address doesn't appear to have other uses, then it appears dead to the compiler and is removed. This can break the /tracing/printk_formats sysfs node which iterates the addresses stored in the "__tracepoint_str" section. Like other strings stored in custom sections in this header, mark these __used to inform the compiler that there are other non-obvious users of the address, so they should still be emitted. Link: https://lkml.kernel.org/r/20200730224555.2142154-2-ndesaulniers@google.com Cc: Ingo Molnar Cc: Miguel Ojeda Cc: stable@vger.kernel.org Fixes: 102c9323c35a8 ("tracing: Add __tracepoint_string() to export string pointers") Reported-by: Tim Murray Reported-by: Simon MacMullen Suggested-by: Greg Hackmann Signed-off-by: Nick Desaulniers Signed-off-by: Steven Rostedt (VMware) --- include/linux/tracepoint.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/linux/tracepoint.h b/include/linux/tracepoint.h index a1fecf3116217..3a5b717d92e80 100644 --- a/include/linux/tracepoint.h +++ b/include/linux/tracepoint.h @@ -361,7 +361,7 @@ static inline struct tracepoint *tracepoint_ptr_deref(tracepoint_ptr_t *p) static const char *___tp_str __tracepoint_string = str; \ ___tp_str; \ }) -#define __tracepoint_string __attribute__((section("__tracepoint_str"))) +#define __tracepoint_string __attribute__((section("__tracepoint_str"), used)) #else /* * tracepoint_string() is used to save the string address for userspace -- GitLab From 1c39d761ff5c90227fc582c45018d5922efaa253 Mon Sep 17 00:00:00 2001 From: Nick Desaulniers Date: Thu, 30 Jul 2020 15:45:55 -0700 Subject: [PATCH 1243/1476] tracepoint: Use __used attribute definitions from compiler_attributes.h Just a small cleanup while I was touching this header. compiler_attributes.h does feature detection of these __attributes__(()) and provides more concise ways to invoke them. Link: https://lkml.kernel.org/r/20200730224555.2142154-3-ndesaulniers@google.com Acked-by: Miguel Ojeda Signed-off-by: Nick Desaulniers Signed-off-by: Steven Rostedt (VMware) --- include/linux/tracepoint.h | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/include/linux/tracepoint.h b/include/linux/tracepoint.h index 3a5b717d92e80..598fec9f9dbf8 100644 --- a/include/linux/tracepoint.h +++ b/include/linux/tracepoint.h @@ -116,8 +116,7 @@ static inline struct tracepoint *tracepoint_ptr_deref(tracepoint_ptr_t *p) #define __TRACEPOINT_ENTRY(name) \ static tracepoint_ptr_t __tracepoint_ptr_##name __used \ - __attribute__((section("__tracepoints_ptrs"))) = \ - &__tracepoint_##name + __section(__tracepoints_ptrs) = &__tracepoint_##name #endif #endif /* _LINUX_TRACEPOINT_H */ @@ -280,9 +279,9 @@ static inline struct tracepoint *tracepoint_ptr_deref(tracepoint_ptr_t *p) */ #define DEFINE_TRACE_FN(name, reg, unreg) \ static const char __tpstrtab_##name[] \ - __attribute__((section("__tracepoints_strings"))) = #name; \ - struct tracepoint __tracepoint_##name \ - __attribute__((section("__tracepoints"), used)) = \ + __section(__tracepoints_strings) = #name; \ + struct tracepoint __tracepoint_##name __used \ + __section(__tracepoints) = \ { __tpstrtab_##name, STATIC_KEY_INIT_FALSE, reg, unreg, NULL };\ __TRACEPOINT_ENTRY(name); @@ -361,7 +360,7 @@ static inline struct tracepoint *tracepoint_ptr_deref(tracepoint_ptr_t *p) static const char *___tp_str __tracepoint_string = str; \ ___tp_str; \ }) -#define __tracepoint_string __attribute__((section("__tracepoint_str"), used)) +#define __tracepoint_string __used __section(__tracepoint_str) #else /* * tracepoint_string() is used to save the string address for userspace -- GitLab From c58b6b0372de0d4cd0536d6585addd1b36b151ae Mon Sep 17 00:00:00 2001 From: Josef Bacik Date: Fri, 24 Jul 2020 20:50:48 -0400 Subject: [PATCH 1244/1476] ftrace: Fix ftrace_trace_task return value I was attempting to use pid filtering with function_graph, but it wasn't allowing anything to make it through. Turns out ftrace_trace_task returns false if ftrace_ignore_pid is not-empty, which isn't correct anymore. We're now setting it to FTRACE_PID_IGNORE if we need to ignore that pid, otherwise it's set to the pid (which is weird considering the name) or to FTRACE_PID_TRACE. Fix the check to check for != FTRACE_PID_IGNORE. With this we can now use function_graph with pid filtering. Link: https://lkml.kernel.org/r/20200725005048.1790-1-josef@toxicpanda.com Fixes: 717e3f5ebc82 ("ftrace: Make function trace pid filtering a bit more exact") Signed-off-by: Josef Bacik Signed-off-by: Steven Rostedt (VMware) --- kernel/trace/ftrace.c | 3 --- kernel/trace/trace.h | 7 ++++++- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/kernel/trace/ftrace.c b/kernel/trace/ftrace.c index f433cb44300a0..4e3a5d79c0786 100644 --- a/kernel/trace/ftrace.c +++ b/kernel/trace/ftrace.c @@ -139,9 +139,6 @@ static inline void ftrace_ops_init(struct ftrace_ops *ops) #endif } -#define FTRACE_PID_IGNORE -1 -#define FTRACE_PID_TRACE -2 - static void ftrace_pid_func(unsigned long ip, unsigned long parent_ip, struct ftrace_ops *op, struct pt_regs *regs) { diff --git a/kernel/trace/trace.h b/kernel/trace/trace.h index f21607f871891..610d21355526d 100644 --- a/kernel/trace/trace.h +++ b/kernel/trace/trace.h @@ -1103,6 +1103,10 @@ print_graph_function_flags(struct trace_iterator *iter, u32 flags) extern struct list_head ftrace_pids; #ifdef CONFIG_FUNCTION_TRACER + +#define FTRACE_PID_IGNORE -1 +#define FTRACE_PID_TRACE -2 + struct ftrace_func_command { struct list_head list; char *name; @@ -1114,7 +1118,8 @@ struct ftrace_func_command { extern bool ftrace_filter_param __initdata; static inline int ftrace_trace_task(struct trace_array *tr) { - return !this_cpu_read(tr->array_buffer.data->ftrace_ignore_pid); + return this_cpu_read(tr->array_buffer.data->ftrace_ignore_pid) != + FTRACE_PID_IGNORE; } extern int ftrace_is_dead(void); int ftrace_create_function_files(struct trace_array *tr, -- GitLab From 0cb2f1372baa60af8456388a574af6133edd7d80 Mon Sep 17 00:00:00 2001 From: Muchun Song Date: Tue, 28 Jul 2020 14:45:36 +0800 Subject: [PATCH 1245/1476] kprobes: Fix NULL pointer dereference at kprobe_ftrace_handler We found a case of kernel panic on our server. The stack trace is as follows(omit some irrelevant information): BUG: kernel NULL pointer dereference, address: 0000000000000080 RIP: 0010:kprobe_ftrace_handler+0x5e/0xe0 RSP: 0018:ffffb512c6550998 EFLAGS: 00010282 RAX: 0000000000000000 RBX: ffff8e9d16eea018 RCX: 0000000000000000 RDX: ffffffffbe1179c0 RSI: ffffffffc0535564 RDI: ffffffffc0534ec0 RBP: ffffffffc0534ec1 R08: ffff8e9d1bbb0f00 R09: 0000000000000004 R10: 0000000000000000 R11: 0000000000000000 R12: 0000000000000000 R13: ffff8e9d1f797060 R14: 000000000000bacc R15: ffff8e9ce13eca00 CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 CR2: 0000000000000080 CR3: 00000008453d0005 CR4: 00000000003606e0 DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000 DR3: 0000000000000000 DR6: 00000000fffe0ff0 DR7: 0000000000000400 Call Trace: ftrace_ops_assist_func+0x56/0xe0 ftrace_call+0x5/0x34 tcpa_statistic_send+0x5/0x130 [ttcp_engine] The tcpa_statistic_send is the function being kprobed. After analysis, the root cause is that the fourth parameter regs of kprobe_ftrace_handler is NULL. Why regs is NULL? We use the crash tool to analyze the kdump. crash> dis tcpa_statistic_send -r : callq 0xffffffffbd8018c0 The tcpa_statistic_send calls ftrace_caller instead of ftrace_regs_caller. So it is reasonable that the fourth parameter regs of kprobe_ftrace_handler is NULL. In theory, we should call the ftrace_regs_caller instead of the ftrace_caller. After in-depth analysis, we found a reproducible path. Writing a simple kernel module which starts a periodic timer. The timer's handler is named 'kprobe_test_timer_handler'. The module name is kprobe_test.ko. 1) insmod kprobe_test.ko 2) bpftrace -e 'kretprobe:kprobe_test_timer_handler {}' 3) echo 0 > /proc/sys/kernel/ftrace_enabled 4) rmmod kprobe_test 5) stop step 2) kprobe 6) insmod kprobe_test.ko 7) bpftrace -e 'kretprobe:kprobe_test_timer_handler {}' We mark the kprobe as GONE but not disarm the kprobe in the step 4). The step 5) also do not disarm the kprobe when unregister kprobe. So we do not remove the ip from the filter. In this case, when the module loads again in the step 6), we will replace the code to ftrace_caller via the ftrace_module_enable(). When we register kprobe again, we will not replace ftrace_caller to ftrace_regs_caller because the ftrace is disabled in the step 3). So the step 7) will trigger kernel panic. Fix this problem by disarming the kprobe when the module is going away. Link: https://lkml.kernel.org/r/20200728064536.24405-1-songmuchun@bytedance.com Cc: stable@vger.kernel.org Fixes: ae6aa16fdc16 ("kprobes: introduce ftrace based optimization") Acked-by: Masami Hiramatsu Signed-off-by: Muchun Song Co-developed-by: Chengming Zhou Signed-off-by: Chengming Zhou Signed-off-by: Steven Rostedt (VMware) --- kernel/kprobes.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/kernel/kprobes.c b/kernel/kprobes.c index 4a904cc56d68f..07bf03fcf574a 100644 --- a/kernel/kprobes.c +++ b/kernel/kprobes.c @@ -2113,6 +2113,13 @@ static void kill_kprobe(struct kprobe *p) * the original probed function (which will be freed soon) any more. */ arch_remove_kprobe(p); + + /* + * The module is going away. We should disarm the kprobe which + * is using ftrace. + */ + if (kprobe_ftrace(p)) + disarm_kprobe_ftrace(p); } /* Disable one kprobe */ -- GitLab From 231621d0c570765d5cebd95c582f1c8df5c46028 Mon Sep 17 00:00:00 2001 From: Peng Fan Date: Fri, 24 Jul 2020 11:24:24 +0800 Subject: [PATCH 1246/1476] tracing/uprobe: Remove dead code in trace_uprobe_register() In the function trace_uprobe_register(), the statement "return 0;" out of switch case is dead code, remove it. Link: https://lkml.kernel.org/r/1595561064-29186-1-git-send-email-fanpeng@loongson.cn Signed-off-by: Peng Fan Signed-off-by: Steven Rostedt (VMware) --- kernel/trace/trace_uprobe.c | 1 - 1 file changed, 1 deletion(-) diff --git a/kernel/trace/trace_uprobe.c b/kernel/trace/trace_uprobe.c index fdd47f99b18fd..f4286c9bdeb49 100644 --- a/kernel/trace/trace_uprobe.c +++ b/kernel/trace/trace_uprobe.c @@ -1456,7 +1456,6 @@ trace_uprobe_register(struct trace_event_call *event, enum trace_reg type, default: return 0; } - return 0; } static int uprobe_dispatcher(struct uprobe_consumer *con, struct pt_regs *regs) -- GitLab From 80a6e707dd9742390776a9306b400b1fbe405b4a Mon Sep 17 00:00:00 2001 From: Masami Hiramatsu Date: Fri, 17 Jul 2020 17:42:55 +0900 Subject: [PATCH 1247/1476] kprobes: Remove show_registers() function prototype Remove show_registers() function prototype because this function has been renamed by commit 57da8b960b9a ("x86: Avoid double stack traces with show_regs()"), and commit 80006dbee674 ("kprobes/x86: Remove jprobe implementation") has removed the caller in kprobes. So this doesn't exist anymore. Signed-off-by: Masami Hiramatsu Signed-off-by: Steven Rostedt (VMware) --- include/linux/kprobes.h | 1 - 1 file changed, 1 deletion(-) diff --git a/include/linux/kprobes.h b/include/linux/kprobes.h index 6adf90f248d70..81cb7e00ccdcd 100644 --- a/include/linux/kprobes.h +++ b/include/linux/kprobes.h @@ -227,7 +227,6 @@ extern int arch_prepare_kprobe(struct kprobe *p); extern void arch_arm_kprobe(struct kprobe *p); extern void arch_disarm_kprobe(struct kprobe *p); extern int arch_init_kprobes(void); -extern void show_registers(struct pt_regs *regs); extern void kprobes_inc_nmissed_count(struct kprobe *p); extern bool arch_within_kprobe_blacklist(unsigned long addr); extern int arch_populate_kprobe_blacklist(void); -- GitLab From a2de2f86ae3831736dc906f9559b600f186403fe Mon Sep 17 00:00:00 2001 From: Masami Hiramatsu Date: Thu, 16 Jul 2020 01:00:29 +0900 Subject: [PATCH 1248/1476] lib/bootconfig: Add override operator support Add the value override operator (":=") support to the bootconfig. This value override operator will be useful for the bootloaders which will only update the existing bootconfig according to the bootloader boot options. Without this override operator, the bootloader needs to parse the existing bootconfig and update it. However, with this assignment, it can just append the updated (partial) bootconfig text at the tail of existing one without parsing it. (Of course, it must update the size, checksum and magic, but that will be done easily) Link: https://lkml.kernel.org/r/159482882954.126704.16209517125614438640.stgit@devnote2 Signed-off-by: Masami Hiramatsu Signed-off-by: Steven Rostedt (VMware) --- lib/bootconfig.c | 33 +++++++++++++++++++++++---------- 1 file changed, 23 insertions(+), 10 deletions(-) diff --git a/lib/bootconfig.c b/lib/bootconfig.c index 912ef49213986..a5f701161f6bd 100644 --- a/lib/bootconfig.c +++ b/lib/bootconfig.c @@ -329,22 +329,30 @@ const char * __init xbc_node_find_next_key_value(struct xbc_node *root, /* XBC parse and tree build */ +static int __init xbc_init_node(struct xbc_node *node, char *data, u32 flag) +{ + unsigned long offset = data - xbc_data; + + if (WARN_ON(offset >= XBC_DATA_MAX)) + return -EINVAL; + + node->data = (u16)offset | flag; + node->child = 0; + node->next = 0; + + return 0; +} + static struct xbc_node * __init xbc_add_node(char *data, u32 flag) { struct xbc_node *node; - unsigned long offset; if (xbc_node_num == XBC_NODE_MAX) return NULL; node = &xbc_nodes[xbc_node_num++]; - offset = data - xbc_data; - node->data = (u16)offset; - if (WARN_ON(offset >= XBC_DATA_MAX)) + if (xbc_init_node(node, data, flag) < 0) return NULL; - node->data |= flag; - node->child = 0; - node->next = 0; return node; } @@ -603,7 +611,9 @@ static int __init xbc_parse_kv(char **k, char *v, int op) if (c < 0) return c; - if (!xbc_add_sibling(v, XBC_VALUE)) + if (op == ':' && child) { + xbc_init_node(child, v, XBC_VALUE); + } else if (!xbc_add_sibling(v, XBC_VALUE)) return -ENOMEM; if (c == ',') { /* Array */ @@ -787,7 +797,7 @@ int __init xbc_init(char *buf, const char **emsg, int *epos) p = buf; do { - q = strpbrk(p, "{}=+;\n#"); + q = strpbrk(p, "{}=+;:\n#"); if (!q) { p = skip_spaces(p); if (*p != '\0') @@ -798,9 +808,12 @@ int __init xbc_init(char *buf, const char **emsg, int *epos) c = *q; *q++ = '\0'; switch (c) { + case ':': case '+': if (*q++ != '=') { - ret = xbc_parse_error("Wrong '+' operator", + ret = xbc_parse_error(c == '+' ? + "Wrong '+' operator" : + "Wrong ':' operator", q - 2); break; } -- GitLab From 81464192839de0b5bc84c5739381101e04d94f62 Mon Sep 17 00:00:00 2001 From: Masami Hiramatsu Date: Thu, 16 Jul 2020 01:00:38 +0900 Subject: [PATCH 1249/1476] tools/bootconfig: Add testcases for value override operator Add some testcases and examples for value override operator. Link: https://lkml.kernel.org/r/159482883824.126704.2166030493721357163.stgit@devnote2 Signed-off-by: Masami Hiramatsu Signed-off-by: Steven Rostedt (VMware) --- tools/bootconfig/samples/bad-override.bconf | 3 +++ tools/bootconfig/samples/bad-override2.bconf | 3 +++ tools/bootconfig/samples/good-override.bconf | 6 ++++++ tools/bootconfig/test-bootconfig.sh | 13 +++++++++++++ 4 files changed, 25 insertions(+) create mode 100644 tools/bootconfig/samples/bad-override.bconf create mode 100644 tools/bootconfig/samples/bad-override2.bconf create mode 100644 tools/bootconfig/samples/good-override.bconf diff --git a/tools/bootconfig/samples/bad-override.bconf b/tools/bootconfig/samples/bad-override.bconf new file mode 100644 index 0000000000000..fde6c561512e1 --- /dev/null +++ b/tools/bootconfig/samples/bad-override.bconf @@ -0,0 +1,3 @@ +key.subkey = value +# We can not override pre-defined subkeys with value +key := value diff --git a/tools/bootconfig/samples/bad-override2.bconf b/tools/bootconfig/samples/bad-override2.bconf new file mode 100644 index 0000000000000..688587cb023c3 --- /dev/null +++ b/tools/bootconfig/samples/bad-override2.bconf @@ -0,0 +1,3 @@ +key = value +# We can not override pre-defined value with subkey +key.subkey := value diff --git a/tools/bootconfig/samples/good-override.bconf b/tools/bootconfig/samples/good-override.bconf new file mode 100644 index 0000000000000..7d31d5f8fbd8c --- /dev/null +++ b/tools/bootconfig/samples/good-override.bconf @@ -0,0 +1,6 @@ +# Override the value +key.word = 1,2,4 +key.word := 2,3 + +# No pre-defined key +key.new.word := "new" diff --git a/tools/bootconfig/test-bootconfig.sh b/tools/bootconfig/test-bootconfig.sh index 3c2ab9e757303..56284b98d8f0b 100755 --- a/tools/bootconfig/test-bootconfig.sh +++ b/tools/bootconfig/test-bootconfig.sh @@ -117,6 +117,19 @@ xpass grep -q "bar" $OUTFILE xpass grep -q "baz" $OUTFILE xpass grep -q "qux" $OUTFILE +echo "Override same-key values" +cat > $TEMPCONF << EOF +key = bar, baz +key := qux +EOF +echo > $INITRD + +xpass $BOOTCONF -a $TEMPCONF $INITRD +$BOOTCONF $INITRD > $OUTFILE +xfail grep -q "bar" $OUTFILE +xfail grep -q "baz" $OUTFILE +xpass grep -q "qux" $OUTFILE + echo "Double/single quotes test" echo "key = '\"string\"';" > $TEMPCONF $BOOTCONF -a $TEMPCONF $INITRD -- GitLab From c58b46cba71750c6e969625abb1cf3ddabb15e06 Mon Sep 17 00:00:00 2001 From: Masami Hiramatsu Date: Thu, 16 Jul 2020 01:00:47 +0900 Subject: [PATCH 1250/1476] Documentation: bootconfig: Add bootconfig override operator Add a sentence about bootconfig override operator (":=") to bootconfig.rst. Link: https://lkml.kernel.org/r/159482884682.126704.7198860675721719878.stgit@devnote2 Signed-off-by: Masami Hiramatsu Signed-off-by: Steven Rostedt (VMware) --- Documentation/admin-guide/bootconfig.rst | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/Documentation/admin-guide/bootconfig.rst b/Documentation/admin-guide/bootconfig.rst index d6b3b77a41296..a22024f9175e7 100644 --- a/Documentation/admin-guide/bootconfig.rst +++ b/Documentation/admin-guide/bootconfig.rst @@ -71,6 +71,16 @@ For example,:: foo = bar, baz foo = qux # !ERROR! we can not re-define same key +If you want to update the value, you must use the override operator +``:=`` explicitly. For example:: + + foo = bar, baz + foo := qux + +then, the ``qux`` is assigned to ``foo`` key. This is useful for +overriding the default value by adding (partial) custom bootconfigs +without parsing the default bootconfig. + If you want to append the value to existing key as an array member, you can use ``+=`` operator. For example:: @@ -84,6 +94,7 @@ For example, following config is NOT allowed.:: foo = value1 foo.bar = value2 # !ERROR! subkey "bar" and value "value1" can NOT co-exist + foo.bar := value2 # !ERROR! even with the override operator, this is NOT allowed. Comments -- GitLab From c3917df54cf925754e74e48748493ac5f232a2f1 Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Mon, 13 Jul 2020 17:11:37 +0200 Subject: [PATCH 1251/1476] mailbox: Replace HTTP links with HTTPS ones Rationale: Reduces attack surface on kernel devs opening the links for MITM as HTTPS traffic is much harder to manipulate. Deterministic algorithm: For each file: If not .svg: For each line: If doesn't contain `\bxmlns\b`: For each link, `\bhttp://[^# \t\r\n]*(?:\w|/)`: If neither `\bgnu\.org/license`, nor `\bmozilla\.org/MPL\b`: If both the HTTP and HTTPS versions return 200 OK and serve the same content: Replace HTTP with HTTPS. Signed-off-by: Alexander A. Klimov Signed-off-by: Jassi Brar --- drivers/mailbox/omap-mailbox.c | 2 +- drivers/mailbox/ti-msgmgr.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/mailbox/omap-mailbox.c b/drivers/mailbox/omap-mailbox.c index 5978a35aac6d0..93fe08aef3ca3 100644 --- a/drivers/mailbox/omap-mailbox.c +++ b/drivers/mailbox/omap-mailbox.c @@ -3,7 +3,7 @@ * OMAP mailbox driver * * Copyright (C) 2006-2009 Nokia Corporation. All rights reserved. - * Copyright (C) 2013-2019 Texas Instruments Incorporated - http://www.ti.com + * Copyright (C) 2013-2019 Texas Instruments Incorporated - https://www.ti.com * * Contact: Hiroshi DOYU * Suman Anna diff --git a/drivers/mailbox/ti-msgmgr.c b/drivers/mailbox/ti-msgmgr.c index 88047d835211c..0130628f4d9db 100644 --- a/drivers/mailbox/ti-msgmgr.c +++ b/drivers/mailbox/ti-msgmgr.c @@ -2,7 +2,7 @@ /* * Texas Instruments' Message Manager Driver * - * Copyright (C) 2015-2017 Texas Instruments Incorporated - http://www.ti.com/ + * Copyright (C) 2015-2017 Texas Instruments Incorporated - https://www.ti.com/ * Nishanth Menon */ -- GitLab From 425ab036754af08bb0dc8273efad686484c98f3c Mon Sep 17 00:00:00 2001 From: Hanjun Guo Date: Wed, 22 Jul 2020 17:40:40 +0800 Subject: [PATCH 1252/1476] mailbox: pcc: Put the PCCT table for error path The acpi_get_table() should be coupled with acpi_put_table() if the mapped table is not used at runtime to release the table mapping. In acpi_pcc_probe(), the PCCT table entries will be used as private data for communication chan at runtime, but the table should be put for error path. Signed-off-by: Hanjun Guo Signed-off-by: Jassi Brar --- drivers/mailbox/pcc.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/drivers/mailbox/pcc.c b/drivers/mailbox/pcc.c index 8c7fac38bb1cb..ef9ecd1f59588 100644 --- a/drivers/mailbox/pcc.c +++ b/drivers/mailbox/pcc.c @@ -457,14 +457,17 @@ static int __init acpi_pcc_probe(void) pr_warn("Error parsing PCC subspaces from PCCT\n"); else pr_warn("Invalid PCCT: %d PCC subspaces\n", count); - return -EINVAL; + + rc = -EINVAL; + goto err_put_pcct; } pcc_mbox_channels = kcalloc(count, sizeof(struct mbox_chan), GFP_KERNEL); if (!pcc_mbox_channels) { pr_err("Could not allocate space for PCC mbox channels\n"); - return -ENOMEM; + rc = -ENOMEM; + goto err_put_pcct; } pcc_doorbell_vaddr = kcalloc(count, sizeof(void *), GFP_KERNEL); @@ -535,6 +538,8 @@ err_free_db_vaddr: kfree(pcc_doorbell_vaddr); err_free_mbox: kfree(pcc_mbox_channels); +err_put_pcct: + acpi_put_table(pcct_tbl); return rc; } -- GitLab From 03b70130abb4e37796d22617e9d0a2843e7621bd Mon Sep 17 00:00:00 2001 From: Nathan Chancellor Date: Mon, 22 Jun 2020 18:04:03 -0700 Subject: [PATCH 1253/1476] mailbox: imx: Mark PM functions as __maybe_unused When CONFIG_PM and CONFIG_PM_SLEEP are unset, the following warnings occur: drivers/mailbox/imx-mailbox.c:638:12: warning: 'imx_mu_runtime_resume' defined but not used [-Wunused-function] 638 | static int imx_mu_runtime_resume(struct device *dev) | ^~~~~~~~~~~~~~~~~~~~~ drivers/mailbox/imx-mailbox.c:629:12: warning: 'imx_mu_runtime_suspend' defined but not used [-Wunused-function] 629 | static int imx_mu_runtime_suspend(struct device *dev) | ^~~~~~~~~~~~~~~~~~~~~~ drivers/mailbox/imx-mailbox.c:611:12: warning: 'imx_mu_resume_noirq' defined but not used [-Wunused-function] 611 | static int imx_mu_resume_noirq(struct device *dev) | ^~~~~~~~~~~~~~~~~~~ drivers/mailbox/imx-mailbox.c:601:12: warning: 'imx_mu_suspend_noirq' defined but not used [-Wunused-function] 601 | static int imx_mu_suspend_noirq(struct device *dev) | ^~~~~~~~~~~~~~~~~~~~ Mark these functions as __maybe_unused, which is the standard procedure for PM functions. Fixes: bb2b2624dbe2 ("mailbox: imx: Add runtime PM callback to handle MU clocks") Signed-off-by: Nathan Chancellor Reviewed-by: Dong Aisheng Signed-off-by: Jassi Brar --- drivers/mailbox/imx-mailbox.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/mailbox/imx-mailbox.c b/drivers/mailbox/imx-mailbox.c index 7205b825c8b54..2543c7b6948b6 100644 --- a/drivers/mailbox/imx-mailbox.c +++ b/drivers/mailbox/imx-mailbox.c @@ -598,7 +598,7 @@ static const struct of_device_id imx_mu_dt_ids[] = { }; MODULE_DEVICE_TABLE(of, imx_mu_dt_ids); -static int imx_mu_suspend_noirq(struct device *dev) +static int __maybe_unused imx_mu_suspend_noirq(struct device *dev) { struct imx_mu_priv *priv = dev_get_drvdata(dev); @@ -608,7 +608,7 @@ static int imx_mu_suspend_noirq(struct device *dev) return 0; } -static int imx_mu_resume_noirq(struct device *dev) +static int __maybe_unused imx_mu_resume_noirq(struct device *dev) { struct imx_mu_priv *priv = dev_get_drvdata(dev); @@ -626,7 +626,7 @@ static int imx_mu_resume_noirq(struct device *dev) return 0; } -static int imx_mu_runtime_suspend(struct device *dev) +static int __maybe_unused imx_mu_runtime_suspend(struct device *dev) { struct imx_mu_priv *priv = dev_get_drvdata(dev); @@ -635,7 +635,7 @@ static int imx_mu_runtime_suspend(struct device *dev) return 0; } -static int imx_mu_runtime_resume(struct device *dev) +static int __maybe_unused imx_mu_runtime_resume(struct device *dev) { struct imx_mu_priv *priv = dev_get_drvdata(dev); int ret; -- GitLab From cfbf813e598125293493341bc2238acdf6ba6a6f Mon Sep 17 00:00:00 2001 From: Konrad Dybcio Date: Mon, 22 Jun 2020 21:25:57 +0200 Subject: [PATCH 1254/1476] mailbox: qcom: Add sdm660 hmss compatible The Qualcomm SDM660 platform has a APCS HMSS GLOBAL block, add the compatible for this. Signed-off-by: Konrad Dybcio Reviewed-by: Bjorn Andersson Acked-by: Rob Herring Signed-off-by: Jassi Brar --- .../devicetree/bindings/mailbox/qcom,apcs-kpss-global.yaml | 1 + drivers/mailbox/qcom-apcs-ipc-mailbox.c | 5 +++++ 2 files changed, 6 insertions(+) diff --git a/Documentation/devicetree/bindings/mailbox/qcom,apcs-kpss-global.yaml b/Documentation/devicetree/bindings/mailbox/qcom,apcs-kpss-global.yaml index 12eff942708de..b4501c6b5c6f2 100644 --- a/Documentation/devicetree/bindings/mailbox/qcom,apcs-kpss-global.yaml +++ b/Documentation/devicetree/bindings/mailbox/qcom,apcs-kpss-global.yaml @@ -22,6 +22,7 @@ properties: - qcom,msm8998-apcs-hmss-global - qcom,qcs404-apcs-apps-global - qcom,sc7180-apss-shared + - qcom,sdm660-apcs-hmss-global - qcom,sdm845-apss-shared - qcom,sm8150-apss-shared diff --git a/drivers/mailbox/qcom-apcs-ipc-mailbox.c b/drivers/mailbox/qcom-apcs-ipc-mailbox.c index cec34f0af6ce3..ab0275869434f 100644 --- a/drivers/mailbox/qcom-apcs-ipc-mailbox.c +++ b/drivers/mailbox/qcom-apcs-ipc-mailbox.c @@ -49,6 +49,10 @@ static const struct qcom_apcs_ipc_data msm8998_apcs_data = { .offset = 8, .clk_name = NULL }; +static const struct qcom_apcs_ipc_data sdm660_apcs_data = { + .offset = 8, .clk_name = NULL +}; + static const struct qcom_apcs_ipc_data apps_shared_apcs_data = { .offset = 12, .clk_name = NULL }; @@ -150,6 +154,7 @@ static const struct of_device_id qcom_apcs_ipc_of_match[] = { { .compatible = "qcom,msm8998-apcs-hmss-global", .data = &msm8998_apcs_data }, { .compatible = "qcom,qcs404-apcs-apps-global", .data = &msm8916_apcs_data }, { .compatible = "qcom,sc7180-apss-shared", .data = &apps_shared_apcs_data }, + { .compatible = "qcom,sdm660-apcs-hmss-global", .data = &sdm660_apcs_data }, { .compatible = "qcom,sdm845-apss-shared", .data = &apps_shared_apcs_data }, { .compatible = "qcom,sm8150-apss-shared", .data = &apps_shared_apcs_data }, {} -- GitLab From dde6805690cd8f156e93b0ed4cf1172a5ffbefdd Mon Sep 17 00:00:00 2001 From: Konrad Dybcio Date: Wed, 24 Jun 2020 17:01:03 +0200 Subject: [PATCH 1255/1476] mailbox: qcom: Add msm8994 apcs compatible MSM8994 has an APCS block similar to 8916, but with a different clock driver due to the former one having 2 clusters. Signed-off-by: Konrad Dybcio Acked-by: Rob Herring Reviewed-by: Bjorn Andersson Signed-off-by: Jassi Brar --- .../devicetree/bindings/mailbox/qcom,apcs-kpss-global.yaml | 1 + drivers/mailbox/qcom-apcs-ipc-mailbox.c | 5 +++++ 2 files changed, 6 insertions(+) diff --git a/Documentation/devicetree/bindings/mailbox/qcom,apcs-kpss-global.yaml b/Documentation/devicetree/bindings/mailbox/qcom,apcs-kpss-global.yaml index b4501c6b5c6f2..8f810fc5c1839 100644 --- a/Documentation/devicetree/bindings/mailbox/qcom,apcs-kpss-global.yaml +++ b/Documentation/devicetree/bindings/mailbox/qcom,apcs-kpss-global.yaml @@ -18,6 +18,7 @@ properties: enum: - qcom,ipq8074-apcs-apps-global - qcom,msm8916-apcs-kpss-global + - qcom,msm8994-apcs-kpss-global - qcom,msm8996-apcs-hmss-global - qcom,msm8998-apcs-hmss-global - qcom,qcs404-apcs-apps-global diff --git a/drivers/mailbox/qcom-apcs-ipc-mailbox.c b/drivers/mailbox/qcom-apcs-ipc-mailbox.c index ab0275869434f..077e5c6a9ef7d 100644 --- a/drivers/mailbox/qcom-apcs-ipc-mailbox.c +++ b/drivers/mailbox/qcom-apcs-ipc-mailbox.c @@ -41,6 +41,10 @@ static const struct qcom_apcs_ipc_data msm8916_apcs_data = { .offset = 8, .clk_name = "qcom-apcs-msm8916-clk" }; +static const struct qcom_apcs_ipc_data msm8994_apcs_data = { + .offset = 8, .clk_name = NULL +}; + static const struct qcom_apcs_ipc_data msm8996_apcs_data = { .offset = 16, .clk_name = NULL }; @@ -150,6 +154,7 @@ static const struct of_device_id qcom_apcs_ipc_of_match[] = { { .compatible = "qcom,ipq6018-apcs-apps-global", .data = &ipq6018_apcs_data }, { .compatible = "qcom,ipq8074-apcs-apps-global", .data = &ipq8074_apcs_data }, { .compatible = "qcom,msm8916-apcs-kpss-global", .data = &msm8916_apcs_data }, + { .compatible = "qcom,msm8994-apcs-kpss-global", .data = &msm8994_apcs_data }, { .compatible = "qcom,msm8996-apcs-hmss-global", .data = &msm8996_apcs_data }, { .compatible = "qcom,msm8998-apcs-hmss-global", .data = &msm8998_apcs_data }, { .compatible = "qcom,qcs404-apcs-apps-global", .data = &msm8916_apcs_data }, -- GitLab From 6cb4f3b055fe42bb0f20c6ad6401d325b39d1e2a Mon Sep 17 00:00:00 2001 From: Dennis YC Hsieh Date: Sun, 5 Jul 2020 14:37:13 +0800 Subject: [PATCH 1256/1476] dt-binding: gce: add gce header file for mt6779 Add documentation for the mt6779 gce. Add gce header file defined the gce hardware event, subsys number and constant for mt6779. Signed-off-by: Dennis YC Hsieh Reviewed-by: Rob Herring Reviewed-by: CK Hu Reviewed-by: Bibby Hsieh Signed-off-by: Jassi Brar --- .../devicetree/bindings/mailbox/mtk-gce.txt | 8 +- include/dt-bindings/gce/mt6779-gce.h | 222 ++++++++++++++++++ 2 files changed, 227 insertions(+), 3 deletions(-) create mode 100644 include/dt-bindings/gce/mt6779-gce.h diff --git a/Documentation/devicetree/bindings/mailbox/mtk-gce.txt b/Documentation/devicetree/bindings/mailbox/mtk-gce.txt index 0b5b2a6bcc488..cf48cd806e002 100644 --- a/Documentation/devicetree/bindings/mailbox/mtk-gce.txt +++ b/Documentation/devicetree/bindings/mailbox/mtk-gce.txt @@ -9,7 +9,8 @@ CMDQ driver uses mailbox framework for communication. Please refer to mailbox.txt for generic information about mailbox device-tree bindings. Required properties: -- compatible: can be "mediatek,mt8173-gce" or "mediatek,mt8183-gce" +- compatible: can be "mediatek,mt8173-gce", "mediatek,mt8183-gce" or + "mediatek,mt6779-gce". - reg: Address range of the GCE unit - interrupts: The interrupt signal from the GCE block - clock: Clocks according to the common clock binding @@ -34,8 +35,9 @@ Optional properties for a client device: start_offset: the start offset of register address that GCE can access. size: the total size of register address that GCE can access. -Some vaules of properties are defined in 'dt-bindings/gce/mt8173-gce.h' -or 'dt-binding/gce/mt8183-gce.h'. Such as sub-system ids, thread priority, event ids. +Some vaules of properties are defined in 'dt-bindings/gce/mt8173-gce.h', +'dt-binding/gce/mt8183-gce.h' or 'dt-bindings/gce/mt6779-gce.h'. Such as +sub-system ids, thread priority, event ids. Example: diff --git a/include/dt-bindings/gce/mt6779-gce.h b/include/dt-bindings/gce/mt6779-gce.h new file mode 100644 index 0000000000000..06101316ace42 --- /dev/null +++ b/include/dt-bindings/gce/mt6779-gce.h @@ -0,0 +1,222 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2019 MediaTek Inc. + * Author: Dennis-YC Hsieh + */ + +#ifndef _DT_BINDINGS_GCE_MT6779_H +#define _DT_BINDINGS_GCE_MT6779_H + +#define CMDQ_NO_TIMEOUT 0xffffffff + +/* GCE HW thread priority */ +#define CMDQ_THR_PRIO_LOWEST 0 +#define CMDQ_THR_PRIO_1 1 +#define CMDQ_THR_PRIO_2 2 +#define CMDQ_THR_PRIO_3 3 +#define CMDQ_THR_PRIO_4 4 +#define CMDQ_THR_PRIO_5 5 +#define CMDQ_THR_PRIO_6 6 +#define CMDQ_THR_PRIO_HIGHEST 7 + +/* GCE subsys table */ +#define SUBSYS_1300XXXX 0 +#define SUBSYS_1400XXXX 1 +#define SUBSYS_1401XXXX 2 +#define SUBSYS_1402XXXX 3 +#define SUBSYS_1502XXXX 4 +#define SUBSYS_1880XXXX 5 +#define SUBSYS_1881XXXX 6 +#define SUBSYS_1882XXXX 7 +#define SUBSYS_1883XXXX 8 +#define SUBSYS_1884XXXX 9 +#define SUBSYS_1000XXXX 10 +#define SUBSYS_1001XXXX 11 +#define SUBSYS_1002XXXX 12 +#define SUBSYS_1003XXXX 13 +#define SUBSYS_1004XXXX 14 +#define SUBSYS_1005XXXX 15 +#define SUBSYS_1020XXXX 16 +#define SUBSYS_1028XXXX 17 +#define SUBSYS_1700XXXX 18 +#define SUBSYS_1701XXXX 19 +#define SUBSYS_1702XXXX 20 +#define SUBSYS_1703XXXX 21 +#define SUBSYS_1800XXXX 22 +#define SUBSYS_1801XXXX 23 +#define SUBSYS_1802XXXX 24 +#define SUBSYS_1804XXXX 25 +#define SUBSYS_1805XXXX 26 +#define SUBSYS_1808XXXX 27 +#define SUBSYS_180aXXXX 28 +#define SUBSYS_180bXXXX 29 +#define CMDQ_SUBSYS_OFF 32 + +/* GCE hardware events */ +#define CMDQ_EVENT_DISP_RDMA0_SOF 0 +#define CMDQ_EVENT_DISP_RDMA1_SOF 1 +#define CMDQ_EVENT_MDP_RDMA0_SOF 2 +#define CMDQ_EVENT_MDP_RDMA1_SOF 3 +#define CMDQ_EVENT_MDP_RSZ0_SOF 4 +#define CMDQ_EVENT_MDP_RSZ1_SOF 5 +#define CMDQ_EVENT_MDP_TDSHP_SOF 6 +#define CMDQ_EVENT_MDP_WROT0_SOF 7 +#define CMDQ_EVENT_MDP_WROT1_SOF 8 +#define CMDQ_EVENT_DISP_OVL0_SOF 9 +#define CMDQ_EVENT_DISP_2L_OVL0_SOF 10 +#define CMDQ_EVENT_DISP_2L_OVL1_SOF 11 +#define CMDQ_EVENT_DISP_WDMA0_SOF 12 +#define CMDQ_EVENT_DISP_COLOR0_SOF 13 +#define CMDQ_EVENT_DISP_CCORR0_SOF 14 +#define CMDQ_EVENT_DISP_AAL0_SOF 15 +#define CMDQ_EVENT_DISP_GAMMA0_SOF 16 +#define CMDQ_EVENT_DISP_DITHER0_SOF 17 +#define CMDQ_EVENT_DISP_PWM0_SOF 18 +#define CMDQ_EVENT_DISP_DSI0_SOF 19 +#define CMDQ_EVENT_DISP_DPI0_SOF 20 +#define CMDQ_EVENT_DISP_POSTMASK0_SOF 21 +#define CMDQ_EVENT_DISP_RSZ0_SOF 22 +#define CMDQ_EVENT_MDP_AAL_SOF 23 +#define CMDQ_EVENT_MDP_CCORR_SOF 24 +#define CMDQ_EVENT_DISP_DBI0_SOF 25 +#define CMDQ_EVENT_ISP_RELAY_SOF 26 +#define CMDQ_EVENT_IPU_RELAY_SOF 27 +#define CMDQ_EVENT_DISP_RDMA0_EOF 28 +#define CMDQ_EVENT_DISP_RDMA1_EOF 29 +#define CMDQ_EVENT_MDP_RDMA0_EOF 30 +#define CMDQ_EVENT_MDP_RDMA1_EOF 31 +#define CMDQ_EVENT_MDP_RSZ0_EOF 32 +#define CMDQ_EVENT_MDP_RSZ1_EOF 33 +#define CMDQ_EVENT_MDP_TDSHP_EOF 34 +#define CMDQ_EVENT_MDP_WROT0_W_EOF 35 +#define CMDQ_EVENT_MDP_WROT1_W_EOF 36 +#define CMDQ_EVENT_DISP_OVL0_EOF 37 +#define CMDQ_EVENT_DISP_2L_OVL0_EOF 38 +#define CMDQ_EVENT_DISP_2L_OVL1_EOF 39 +#define CMDQ_EVENT_DISP_WDMA0_EOF 40 +#define CMDQ_EVENT_DISP_COLOR0_EOF 41 +#define CMDQ_EVENT_DISP_CCORR0_EOF 42 +#define CMDQ_EVENT_DISP_AAL0_EOF 43 +#define CMDQ_EVENT_DISP_GAMMA0_EOF 44 +#define CMDQ_EVENT_DISP_DITHER0_EOF 45 +#define CMDQ_EVENT_DISP_DSI0_EOF 46 +#define CMDQ_EVENT_DISP_DPI0_EOF 47 +#define CMDQ_EVENT_DISP_RSZ0_EOF 49 +#define CMDQ_EVENT_MDP_AAL_FRAME_DONE 50 +#define CMDQ_EVENT_MDP_CCORR_FRAME_DONE 51 +#define CMDQ_EVENT_DISP_POSTMASK0_FRAME_DONE 52 +#define CMDQ_EVENT_MUTEX0_STREAM_EOF 130 +#define CMDQ_EVENT_MUTEX1_STREAM_EOF 131 +#define CMDQ_EVENT_MUTEX2_STREAM_EOF 132 +#define CMDQ_EVENT_MUTEX3_STREAM_EOF 133 +#define CMDQ_EVENT_MUTEX4_STREAM_EOF 134 +#define CMDQ_EVENT_MUTEX5_STREAM_EOF 135 +#define CMDQ_EVENT_MUTEX6_STREAM_EOF 136 +#define CMDQ_EVENT_MUTEX7_STREAM_EOF 137 +#define CMDQ_EVENT_MUTEX8_STREAM_EOF 138 +#define CMDQ_EVENT_MUTEX9_STREAM_EOF 139 +#define CMDQ_EVENT_MUTEX10_STREAM_EOF 140 +#define CMDQ_EVENT_MUTEX11_STREAM_EOF 141 +#define CMDQ_EVENT_DISP_RDMA0_UNDERRUN 142 +#define CMDQ_EVENT_DISP_RDMA1_UNDERRUN 143 +#define CMDQ_EVENT_DISP_RDMA2_UNDERRUN 144 +#define CMDQ_EVENT_DISP_RDMA3_UNDERRUN 145 +#define CMDQ_EVENT_DSI0_TE 146 +#define CMDQ_EVENT_DSI0_IRQ_EVENT 147 +#define CMDQ_EVENT_DSI0_DONE_EVENT 148 +#define CMDQ_EVENT_DISP_POSTMASK0_RST_DONE 150 +#define CMDQ_EVENT_DISP_WDMA0_RST_DONE 151 +#define CMDQ_EVENT_MDP_WROT0_RST_DONE 153 +#define CMDQ_EVENT_MDP_RDMA0_RST_DONE 154 +#define CMDQ_EVENT_DISP_OVL0_RST_DONE 155 +#define CMDQ_EVENT_DISP_OVL0_2L_RST_DONE 156 +#define CMDQ_EVENT_DISP_OVL1_2L_RST_DONE 157 +#define CMDQ_EVENT_DIP_CQ_THREAD0_EOF 257 +#define CMDQ_EVENT_DIP_CQ_THREAD1_EOF 258 +#define CMDQ_EVENT_DIP_CQ_THREAD2_EOF 259 +#define CMDQ_EVENT_DIP_CQ_THREAD3_EOF 260 +#define CMDQ_EVENT_DIP_CQ_THREAD4_EOF 261 +#define CMDQ_EVENT_DIP_CQ_THREAD5_EOF 262 +#define CMDQ_EVENT_DIP_CQ_THREAD6_EOF 263 +#define CMDQ_EVENT_DIP_CQ_THREAD7_EOF 264 +#define CMDQ_EVENT_DIP_CQ_THREAD8_EOF 265 +#define CMDQ_EVENT_DIP_CQ_THREAD9_EOF 266 +#define CMDQ_EVENT_DIP_CQ_THREAD10_EOF 267 +#define CMDQ_EVENT_DIP_CQ_THREAD11_EOF 268 +#define CMDQ_EVENT_DIP_CQ_THREAD12_EOF 269 +#define CMDQ_EVENT_DIP_CQ_THREAD13_EOF 270 +#define CMDQ_EVENT_DIP_CQ_THREAD14_EOF 271 +#define CMDQ_EVENT_DIP_CQ_THREAD15_EOF 272 +#define CMDQ_EVENT_DIP_CQ_THREAD16_EOF 273 +#define CMDQ_EVENT_DIP_CQ_THREAD17_EOF 274 +#define CMDQ_EVENT_DIP_CQ_THREAD18_EOF 275 +#define CMDQ_EVENT_DIP_DMA_ERR_EVENT 276 +#define CMDQ_EVENT_AMD_FRAME_DONE 277 +#define CMDQ_EVENT_MFB_DONE 278 +#define CMDQ_EVENT_WPE_A_EOF 279 +#define CMDQ_EVENT_VENC_EOF 289 +#define CMDQ_EVENT_VENC_CMDQ_PAUSE_DONE 290 +#define CMDQ_EVENT_JPEG_ENC_EOF 291 +#define CMDQ_EVENT_VENC_MB_DONE 292 +#define CMDQ_EVENT_VENC_128BYTE_CNT_DONE 293 +#define CMDQ_EVENT_ISP_FRAME_DONE_A 321 +#define CMDQ_EVENT_ISP_FRAME_DONE_B 322 +#define CMDQ_EVENT_ISP_FRAME_DONE_C 323 +#define CMDQ_EVENT_ISP_CAMSV_0_PASS1_DONE 324 +#define CMDQ_EVENT_ISP_CAMSV_0_2_PASS1_DONE 325 +#define CMDQ_EVENT_ISP_CAMSV_1_PASS1_DONE 326 +#define CMDQ_EVENT_ISP_CAMSV_2_PASS1_DONE 327 +#define CMDQ_EVENT_ISP_CAMSV_3_PASS1_DONE 328 +#define CMDQ_EVENT_ISP_TSF_DONE 329 +#define CMDQ_EVENT_SENINF_0_FIFO_FULL 330 +#define CMDQ_EVENT_SENINF_1_FIFO_FULL 331 +#define CMDQ_EVENT_SENINF_2_FIFO_FULL 332 +#define CMDQ_EVENT_SENINF_3_FIFO_FULL 333 +#define CMDQ_EVENT_SENINF_4_FIFO_FULL 334 +#define CMDQ_EVENT_SENINF_5_FIFO_FULL 335 +#define CMDQ_EVENT_SENINF_6_FIFO_FULL 336 +#define CMDQ_EVENT_SENINF_7_FIFO_FULL 337 +#define CMDQ_EVENT_TG_OVRUN_A_INT_DLY 338 +#define CMDQ_EVENT_TG_OVRUN_B_INT_DLY 339 +#define CMDQ_EVENT_TG_OVRUN_C_INT 340 +#define CMDQ_EVENT_TG_GRABERR_A_INT_DLY 341 +#define CMDQ_EVENT_TG_GRABERR_B_INT_DLY 342 +#define CMDQ_EVENT_TG_GRABERR_C_INT 343 +#define CMDQ_EVENT_CQ_VR_SNAP_A_INT_DLY 344 +#define CMDQ_EVENT_CQ_VR_SNAP_B_INT_DLY 345 +#define CMDQ_EVENT_CQ_VR_SNAP_C_INT 346 +#define CMDQ_EVENT_DMA_R1_ERROR_A_INT_DLY 347 +#define CMDQ_EVENT_DMA_R1_ERROR_B_INT_DLY 348 +#define CMDQ_EVENT_DMA_R1_ERROR_C_INT 349 +#define CMDQ_EVENT_APU_GCE_CORE0_EVENT_0 353 +#define CMDQ_EVENT_APU_GCE_CORE0_EVENT_1 354 +#define CMDQ_EVENT_APU_GCE_CORE0_EVENT_2 355 +#define CMDQ_EVENT_APU_GCE_CORE0_EVENT_3 356 +#define CMDQ_EVENT_APU_GCE_CORE1_EVENT_0 385 +#define CMDQ_EVENT_APU_GCE_CORE1_EVENT_1 386 +#define CMDQ_EVENT_APU_GCE_CORE1_EVENT_2 387 +#define CMDQ_EVENT_APU_GCE_CORE1_EVENT_3 388 +#define CMDQ_EVENT_VDEC_EVENT_0 416 +#define CMDQ_EVENT_VDEC_EVENT_1 417 +#define CMDQ_EVENT_VDEC_EVENT_2 418 +#define CMDQ_EVENT_VDEC_EVENT_3 419 +#define CMDQ_EVENT_VDEC_EVENT_4 420 +#define CMDQ_EVENT_VDEC_EVENT_5 421 +#define CMDQ_EVENT_VDEC_EVENT_6 422 +#define CMDQ_EVENT_VDEC_EVENT_7 423 +#define CMDQ_EVENT_VDEC_EVENT_8 424 +#define CMDQ_EVENT_VDEC_EVENT_9 425 +#define CMDQ_EVENT_VDEC_EVENT_10 426 +#define CMDQ_EVENT_VDEC_EVENT_11 427 +#define CMDQ_EVENT_VDEC_EVENT_12 428 +#define CMDQ_EVENT_VDEC_EVENT_13 429 +#define CMDQ_EVENT_VDEC_EVENT_14 430 +#define CMDQ_EVENT_VDEC_EVENT_15 431 +#define CMDQ_EVENT_FDVT_DONE 449 +#define CMDQ_EVENT_FE_DONE 450 +#define CMDQ_EVENT_RSC_EOF 451 +#define CMDQ_EVENT_DVS_DONE_ASYNC_SHOT 452 +#define CMDQ_EVENT_DVP_DONE_ASYNC_SHOT 453 +#define CMDQ_EVENT_DSI0_TE_INFRA 898 + +#endif -- GitLab From 0858fde496f84fff2fdae53d9e33c7b308195f74 Mon Sep 17 00:00:00 2001 From: Dennis YC Hsieh Date: Sun, 5 Jul 2020 14:37:14 +0800 Subject: [PATCH 1257/1476] mailbox: cmdq: variablize address shift in platform Some gce hardware shift pc and end address in register to support large dram addressing. Implement gce address shift when write or read pc and end register. And add shift bit in platform definition. Signed-off-by: Dennis YC Hsieh Signed-off-by: Jassi Brar --- drivers/mailbox/mtk-cmdq-mailbox.c | 57 ++++++++++++++++++------ include/linux/mailbox/mtk-cmdq-mailbox.h | 2 + 2 files changed, 46 insertions(+), 13 deletions(-) diff --git a/drivers/mailbox/mtk-cmdq-mailbox.c b/drivers/mailbox/mtk-cmdq-mailbox.c index b24822ad8409c..49d9264145aa8 100644 --- a/drivers/mailbox/mtk-cmdq-mailbox.c +++ b/drivers/mailbox/mtk-cmdq-mailbox.c @@ -75,8 +75,22 @@ struct cmdq { struct cmdq_thread *thread; struct clk *clock; bool suspended; + u8 shift_pa; }; +struct gce_plat { + u32 thread_nr; + u8 shift; +}; + +u8 cmdq_get_shift_pa(struct mbox_chan *chan) +{ + struct cmdq *cmdq = container_of(chan->mbox, struct cmdq, mbox); + + return cmdq->shift_pa; +} +EXPORT_SYMBOL(cmdq_get_shift_pa); + static int cmdq_thread_suspend(struct cmdq *cmdq, struct cmdq_thread *thread) { u32 status; @@ -183,13 +197,15 @@ static void cmdq_task_handle_error(struct cmdq_task *task) { struct cmdq_thread *thread = task->thread; struct cmdq_task *next_task; + struct cmdq *cmdq = task->cmdq; - dev_err(task->cmdq->mbox.dev, "task 0x%p error\n", task); - WARN_ON(cmdq_thread_suspend(task->cmdq, thread) < 0); + dev_err(cmdq->mbox.dev, "task 0x%p error\n", task); + WARN_ON(cmdq_thread_suspend(cmdq, thread) < 0); next_task = list_first_entry_or_null(&thread->task_busy_list, struct cmdq_task, list_entry); if (next_task) - writel(next_task->pa_base, thread->base + CMDQ_THR_CURR_ADDR); + writel(next_task->pa_base >> cmdq->shift_pa, + thread->base + CMDQ_THR_CURR_ADDR); cmdq_thread_resume(thread); } @@ -219,7 +235,7 @@ static void cmdq_thread_irq_handler(struct cmdq *cmdq, else return; - curr_pa = readl(thread->base + CMDQ_THR_CURR_ADDR); + curr_pa = readl(thread->base + CMDQ_THR_CURR_ADDR) << cmdq->shift_pa; list_for_each_entry_safe(task, tmp, &thread->task_busy_list, list_entry) { @@ -335,27 +351,31 @@ static int cmdq_mbox_send_data(struct mbox_chan *chan, void *data) WARN_ON(clk_enable(cmdq->clock) < 0); WARN_ON(cmdq_thread_reset(cmdq, thread) < 0); - writel(task->pa_base, thread->base + CMDQ_THR_CURR_ADDR); - writel(task->pa_base + pkt->cmd_buf_size, + writel(task->pa_base >> cmdq->shift_pa, + thread->base + CMDQ_THR_CURR_ADDR); + writel((task->pa_base + pkt->cmd_buf_size) >> cmdq->shift_pa, thread->base + CMDQ_THR_END_ADDR); + writel(thread->priority, thread->base + CMDQ_THR_PRIORITY); writel(CMDQ_THR_IRQ_EN, thread->base + CMDQ_THR_IRQ_ENABLE); writel(CMDQ_THR_ENABLED, thread->base + CMDQ_THR_ENABLE_TASK); } else { WARN_ON(cmdq_thread_suspend(cmdq, thread) < 0); - curr_pa = readl(thread->base + CMDQ_THR_CURR_ADDR); - end_pa = readl(thread->base + CMDQ_THR_END_ADDR); + curr_pa = readl(thread->base + CMDQ_THR_CURR_ADDR) << + cmdq->shift_pa; + end_pa = readl(thread->base + CMDQ_THR_END_ADDR) << + cmdq->shift_pa; /* check boundary */ if (curr_pa == end_pa - CMDQ_INST_SIZE || curr_pa == end_pa) { /* set to this task directly */ - writel(task->pa_base, + writel(task->pa_base >> cmdq->shift_pa, thread->base + CMDQ_THR_CURR_ADDR); } else { cmdq_task_insert_into_thread(task); smp_mb(); /* modify jump before enable thread */ } - writel(task->pa_base + pkt->cmd_buf_size, + writel((task->pa_base + pkt->cmd_buf_size) >> cmdq->shift_pa, thread->base + CMDQ_THR_END_ADDR); cmdq_thread_resume(thread); } @@ -453,6 +473,7 @@ static int cmdq_probe(struct platform_device *pdev) struct resource *res; struct cmdq *cmdq; int err, i; + struct gce_plat *plat_data; cmdq = devm_kzalloc(dev, sizeof(*cmdq), GFP_KERNEL); if (!cmdq) @@ -471,7 +492,14 @@ static int cmdq_probe(struct platform_device *pdev) return -EINVAL; } - cmdq->thread_nr = (u32)(unsigned long)of_device_get_match_data(dev); + plat_data = (struct gce_plat *)of_device_get_match_data(dev); + if (!plat_data) { + dev_err(dev, "failed to get match data\n"); + return -EINVAL; + } + + cmdq->thread_nr = plat_data->thread_nr; + cmdq->shift_pa = plat_data->shift; cmdq->irq_mask = GENMASK(cmdq->thread_nr - 1, 0); err = devm_request_irq(dev, cmdq->irq, cmdq_irq_handler, IRQF_SHARED, "mtk_cmdq", cmdq); @@ -534,9 +562,12 @@ static const struct dev_pm_ops cmdq_pm_ops = { .resume = cmdq_resume, }; +static const struct gce_plat gce_plat_v2 = {.thread_nr = 16}; +static const struct gce_plat gce_plat_v3 = {.thread_nr = 24}; + static const struct of_device_id cmdq_of_ids[] = { - {.compatible = "mediatek,mt8173-gce", .data = (void *)16}, - {.compatible = "mediatek,mt8183-gce", .data = (void *)24}, + {.compatible = "mediatek,mt8173-gce", .data = (void *)&gce_plat_v2}, + {.compatible = "mediatek,mt8183-gce", .data = (void *)&gce_plat_v3}, {} }; diff --git a/include/linux/mailbox/mtk-cmdq-mailbox.h b/include/linux/mailbox/mtk-cmdq-mailbox.h index a4dc45fbec0a4..c342b8799be84 100644 --- a/include/linux/mailbox/mtk-cmdq-mailbox.h +++ b/include/linux/mailbox/mtk-cmdq-mailbox.h @@ -88,4 +88,6 @@ struct cmdq_pkt { void *cl; }; +u8 cmdq_get_shift_pa(struct mbox_chan *chan); + #endif /* __MTK_CMDQ_MAILBOX_H__ */ -- GitLab From bb2b06e08157c1f542506ce3ed59cc3309eacb78 Mon Sep 17 00:00:00 2001 From: Dennis YC Hsieh Date: Sun, 5 Jul 2020 14:37:15 +0800 Subject: [PATCH 1258/1476] mailbox: cmdq: support mt6779 gce platform definition Add gce v4 hardware support with different thread number and shift. Signed-off-by: Dennis YC Hsieh Reviewed-by: CK Hu Reviewed-by: Matthias Brugger Reviewed-by: Bibby Hsieh Signed-off-by: Jassi Brar --- drivers/mailbox/mtk-cmdq-mailbox.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/mailbox/mtk-cmdq-mailbox.c b/drivers/mailbox/mtk-cmdq-mailbox.c index 49d9264145aa8..08bd4f1eb469d 100644 --- a/drivers/mailbox/mtk-cmdq-mailbox.c +++ b/drivers/mailbox/mtk-cmdq-mailbox.c @@ -564,10 +564,12 @@ static const struct dev_pm_ops cmdq_pm_ops = { static const struct gce_plat gce_plat_v2 = {.thread_nr = 16}; static const struct gce_plat gce_plat_v3 = {.thread_nr = 24}; +static const struct gce_plat gce_plat_v4 = {.thread_nr = 24, .shift = 3}; static const struct of_device_id cmdq_of_ids[] = { {.compatible = "mediatek,mt8173-gce", .data = (void *)&gce_plat_v2}, {.compatible = "mediatek,mt8183-gce", .data = (void *)&gce_plat_v3}, + {.compatible = "mediatek,mt6779-gce", .data = (void *)&gce_plat_v4}, {} }; -- GitLab From 884996986347dbe3b735cfa9bc041dd98a533796 Mon Sep 17 00:00:00 2001 From: Dennis YC Hsieh Date: Sun, 5 Jul 2020 14:37:16 +0800 Subject: [PATCH 1259/1476] mailbox: mediatek: cmdq: clear task in channel before shutdown Do success callback in channel when shutdown. For those task not finish, callback with error code thus client has chance to cleanup or reset. Signed-off-by: Dennis YC Hsieh Reviewed-by: CK Hu Reviewed-by: Bibby Hsieh Signed-off-by: Jassi Brar --- drivers/mailbox/mtk-cmdq-mailbox.c | 38 ++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/drivers/mailbox/mtk-cmdq-mailbox.c b/drivers/mailbox/mtk-cmdq-mailbox.c index 08bd4f1eb469d..484d4438cd835 100644 --- a/drivers/mailbox/mtk-cmdq-mailbox.c +++ b/drivers/mailbox/mtk-cmdq-mailbox.c @@ -349,6 +349,12 @@ static int cmdq_mbox_send_data(struct mbox_chan *chan, void *data) if (list_empty(&thread->task_busy_list)) { WARN_ON(clk_enable(cmdq->clock) < 0); + /* + * The thread reset will clear thread related register to 0, + * including pc, end, priority, irq, suspend and enable. Thus + * set CMDQ_THR_ENABLED to CMDQ_THR_ENABLE_TASK will enable + * thread and make it running. + */ WARN_ON(cmdq_thread_reset(cmdq, thread) < 0); writel(task->pa_base >> cmdq->shift_pa, @@ -391,6 +397,38 @@ static int cmdq_mbox_startup(struct mbox_chan *chan) static void cmdq_mbox_shutdown(struct mbox_chan *chan) { + struct cmdq_thread *thread = (struct cmdq_thread *)chan->con_priv; + struct cmdq *cmdq = dev_get_drvdata(chan->mbox->dev); + struct cmdq_task *task, *tmp; + unsigned long flags; + + spin_lock_irqsave(&thread->chan->lock, flags); + if (list_empty(&thread->task_busy_list)) + goto done; + + WARN_ON(cmdq_thread_suspend(cmdq, thread) < 0); + + /* make sure executed tasks have success callback */ + cmdq_thread_irq_handler(cmdq, thread); + if (list_empty(&thread->task_busy_list)) + goto done; + + list_for_each_entry_safe(task, tmp, &thread->task_busy_list, + list_entry) { + cmdq_task_exec_done(task, CMDQ_CB_ERROR); + kfree(task); + } + + cmdq_thread_disable(cmdq, thread); + clk_disable(cmdq->clock); +done: + /* + * The thread->task_busy_list empty means thread already disable. The + * cmdq_mbox_send_data() always reset thread which clear disable and + * suspend statue when first pkt send to channel, so there is no need + * to do any operation here, only unlock and leave. + */ + spin_unlock_irqrestore(&thread->chan->lock, flags); } static int cmdq_mbox_flush(struct mbox_chan *chan, unsigned long timeout) -- GitLab From 8b1e868f66076490189a36d984fcce286cdd6295 Mon Sep 17 00:00:00 2001 From: Boris Ostrovsky Date: Fri, 10 Jul 2020 15:34:17 -0700 Subject: [PATCH 1260/1476] swiotlb-xen: use vmalloc_to_page on vmalloc virt addresses xen_alloc_coherent_pages might return pages for which virt_to_phys and virt_to_page don't work, e.g. ioremap'ed pages. So in xen_swiotlb_free_coherent we can't assume that virt_to_page works. Instead add a is_vmalloc_addr check and use vmalloc_to_page on vmalloc virt addresses. This patch fixes the following crash at boot on RPi4 (the underlying issue is not RPi4 specific): https://marc.info/?l=xen-devel&m=158862573216800 Signed-off-by: Boris Ostrovsky Signed-off-by: Stefano Stabellini Reviewed-by: Boris Ostrovsky Tested-by: Corey Minyard Tested-by: Roman Shaposhnik Link: https://lore.kernel.org/r/20200710223427.6897-1-sstabellini@kernel.org Signed-off-by: Juergen Gross --- drivers/xen/swiotlb-xen.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/drivers/xen/swiotlb-xen.c b/drivers/xen/swiotlb-xen.c index b6d27762c6f8c..5fbadd07819bd 100644 --- a/drivers/xen/swiotlb-xen.c +++ b/drivers/xen/swiotlb-xen.c @@ -335,6 +335,7 @@ xen_swiotlb_free_coherent(struct device *hwdev, size_t size, void *vaddr, int order = get_order(size); phys_addr_t phys; u64 dma_mask = DMA_BIT_MASK(32); + struct page *page; if (hwdev && hwdev->coherent_dma_mask) dma_mask = hwdev->coherent_dma_mask; @@ -346,9 +347,14 @@ xen_swiotlb_free_coherent(struct device *hwdev, size_t size, void *vaddr, /* Convert the size to actually allocated. */ size = 1UL << (order + XEN_PAGE_SHIFT); + if (is_vmalloc_addr(vaddr)) + page = vmalloc_to_page(vaddr); + else + page = virt_to_page(vaddr); + if (!WARN_ON((dev_addr + size - 1 > dma_mask) || range_straddles_page_boundary(phys, size)) && - TestClearPageXenRemapped(virt_to_page(vaddr))) + TestClearPageXenRemapped(page)) xen_destroy_contiguous_region(phys, order); xen_free_coherent_pages(hwdev, size, vaddr, (dma_addr_t)phys, attrs); -- GitLab From ae4f0a17ee591c15efbee87746290d22aea81617 Mon Sep 17 00:00:00 2001 From: Stefano Stabellini Date: Fri, 10 Jul 2020 15:34:18 -0700 Subject: [PATCH 1261/1476] swiotlb-xen: remove start_dma_addr It is not strictly needed. Call virt_to_phys on xen_io_tlb_start instead. It will be useful not to have a start_dma_addr around with the next patches. Note that virt_to_phys is not the same as xen_virt_to_bus but actually it is used to compared again __pa(xen_io_tlb_start) as passed to swiotlb_init_with_tbl, so virt_to_phys is actually what we want. Signed-off-by: Stefano Stabellini Reviewed-by: Boris Ostrovsky Tested-by: Corey Minyard Tested-by: Roman Shaposhnik Link: https://lore.kernel.org/r/20200710223427.6897-2-sstabellini@kernel.org Signed-off-by: Juergen Gross --- drivers/xen/swiotlb-xen.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/drivers/xen/swiotlb-xen.c b/drivers/xen/swiotlb-xen.c index 5fbadd07819bd..89a775948a02c 100644 --- a/drivers/xen/swiotlb-xen.c +++ b/drivers/xen/swiotlb-xen.c @@ -52,8 +52,6 @@ static unsigned long xen_io_tlb_nslabs; * Quick lookup value of the bus address of the IOTLB. */ -static u64 start_dma_addr; - /* * Both of these functions should avoid XEN_PFN_PHYS because phys_addr_t * can be 32bit when dma_addr_t is 64bit leading to a loss in @@ -241,7 +239,6 @@ retry: m_ret = XEN_SWIOTLB_EFIXUP; goto error; } - start_dma_addr = xen_virt_to_bus(xen_io_tlb_start); if (early) { if (swiotlb_init_with_tbl(xen_io_tlb_start, xen_io_tlb_nslabs, verbose)) @@ -392,8 +389,8 @@ static dma_addr_t xen_swiotlb_map_page(struct device *dev, struct page *page, */ trace_swiotlb_bounced(dev, dev_addr, size, swiotlb_force); - map = swiotlb_tbl_map_single(dev, start_dma_addr, phys, - size, size, dir, attrs); + map = swiotlb_tbl_map_single(dev, virt_to_phys(xen_io_tlb_start), + phys, size, size, dir, attrs); if (map == (phys_addr_t)DMA_MAPPING_ERROR) return DMA_MAPPING_ERROR; -- GitLab From 2cf6a91347b94528c646cbdd9d295770ea7d447d Mon Sep 17 00:00:00 2001 From: Stefano Stabellini Date: Fri, 10 Jul 2020 15:34:19 -0700 Subject: [PATCH 1262/1476] swiotlb-xen: add struct device * parameter to xen_phys_to_bus No functional changes. The parameter is unused in this patch but will be used by next patches. Signed-off-by: Stefano Stabellini Reviewed-by: Boris Ostrovsky Tested-by: Corey Minyard Tested-by: Roman Shaposhnik Link: https://lore.kernel.org/r/20200710223427.6897-3-sstabellini@kernel.org Signed-off-by: Juergen Gross --- drivers/xen/swiotlb-xen.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/drivers/xen/swiotlb-xen.c b/drivers/xen/swiotlb-xen.c index 89a775948a02c..dbe710a59bf22 100644 --- a/drivers/xen/swiotlb-xen.c +++ b/drivers/xen/swiotlb-xen.c @@ -57,7 +57,7 @@ static unsigned long xen_io_tlb_nslabs; * can be 32bit when dma_addr_t is 64bit leading to a loss in * information if the shift is done before casting to 64bit. */ -static inline dma_addr_t xen_phys_to_bus(phys_addr_t paddr) +static inline dma_addr_t xen_phys_to_bus(struct device *dev, phys_addr_t paddr) { unsigned long bfn = pfn_to_bfn(XEN_PFN_DOWN(paddr)); dma_addr_t dma = (dma_addr_t)bfn << XEN_PAGE_SHIFT; @@ -78,9 +78,9 @@ static inline phys_addr_t xen_bus_to_phys(dma_addr_t baddr) return paddr; } -static inline dma_addr_t xen_virt_to_bus(void *address) +static inline dma_addr_t xen_virt_to_bus(struct device *dev, void *address) { - return xen_phys_to_bus(virt_to_phys(address)); + return xen_phys_to_bus(dev, virt_to_phys(address)); } static inline int range_straddles_page_boundary(phys_addr_t p, size_t size) @@ -309,7 +309,7 @@ xen_swiotlb_alloc_coherent(struct device *hwdev, size_t size, * Do not use virt_to_phys(ret) because on ARM it doesn't correspond * to *dma_handle. */ phys = *dma_handle; - dev_addr = xen_phys_to_bus(phys); + dev_addr = xen_phys_to_bus(hwdev, phys); if (((dev_addr + size - 1 <= dma_mask)) && !range_straddles_page_boundary(phys, size)) *dma_handle = dev_addr; @@ -370,7 +370,7 @@ static dma_addr_t xen_swiotlb_map_page(struct device *dev, struct page *page, unsigned long attrs) { phys_addr_t map, phys = page_to_phys(page) + offset; - dma_addr_t dev_addr = xen_phys_to_bus(phys); + dma_addr_t dev_addr = xen_phys_to_bus(dev, phys); BUG_ON(dir == DMA_NONE); /* @@ -395,7 +395,7 @@ static dma_addr_t xen_swiotlb_map_page(struct device *dev, struct page *page, return DMA_MAPPING_ERROR; phys = map; - dev_addr = xen_phys_to_bus(map); + dev_addr = xen_phys_to_bus(dev, map); /* * Ensure that the address returned is DMA'ble @@ -539,7 +539,7 @@ xen_swiotlb_sync_sg_for_device(struct device *dev, struct scatterlist *sgl, static int xen_swiotlb_dma_supported(struct device *hwdev, u64 mask) { - return xen_virt_to_bus(xen_io_tlb_end - 1) <= mask; + return xen_virt_to_bus(hwdev, xen_io_tlb_end - 1) <= mask; } const struct dma_map_ops xen_swiotlb_dma_ops = { -- GitLab From d900781a170da5ed0fc9840c260384bac0710c09 Mon Sep 17 00:00:00 2001 From: Stefano Stabellini Date: Fri, 10 Jul 2020 15:34:20 -0700 Subject: [PATCH 1263/1476] swiotlb-xen: add struct device * parameter to xen_bus_to_phys No functional changes. The parameter is unused in this patch but will be used by next patches. Signed-off-by: Stefano Stabellini Reviewed-by: Boris Ostrovsky Tested-by: Corey Minyard Tested-by: Roman Shaposhnik Link: https://lore.kernel.org/r/20200710223427.6897-4-sstabellini@kernel.org Signed-off-by: Juergen Gross --- drivers/xen/swiotlb-xen.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/xen/swiotlb-xen.c b/drivers/xen/swiotlb-xen.c index dbe710a59bf22..a8e447137faf9 100644 --- a/drivers/xen/swiotlb-xen.c +++ b/drivers/xen/swiotlb-xen.c @@ -67,7 +67,7 @@ static inline dma_addr_t xen_phys_to_bus(struct device *dev, phys_addr_t paddr) return dma; } -static inline phys_addr_t xen_bus_to_phys(dma_addr_t baddr) +static inline phys_addr_t xen_bus_to_phys(struct device *dev, dma_addr_t baddr) { unsigned long xen_pfn = bfn_to_pfn(XEN_PFN_DOWN(baddr)); dma_addr_t dma = (dma_addr_t)xen_pfn << XEN_PAGE_SHIFT; @@ -339,7 +339,7 @@ xen_swiotlb_free_coherent(struct device *hwdev, size_t size, void *vaddr, /* do not use virt_to_phys because on ARM it doesn't return you the * physical address */ - phys = xen_bus_to_phys(dev_addr); + phys = xen_bus_to_phys(hwdev, dev_addr); /* Convert the size to actually allocated. */ size = 1UL << (order + XEN_PAGE_SHIFT); @@ -423,7 +423,7 @@ done: static void xen_swiotlb_unmap_page(struct device *hwdev, dma_addr_t dev_addr, size_t size, enum dma_data_direction dir, unsigned long attrs) { - phys_addr_t paddr = xen_bus_to_phys(dev_addr); + phys_addr_t paddr = xen_bus_to_phys(hwdev, dev_addr); BUG_ON(dir == DMA_NONE); @@ -439,7 +439,7 @@ static void xen_swiotlb_sync_single_for_cpu(struct device *dev, dma_addr_t dma_addr, size_t size, enum dma_data_direction dir) { - phys_addr_t paddr = xen_bus_to_phys(dma_addr); + phys_addr_t paddr = xen_bus_to_phys(dev, dma_addr); if (!dev_is_dma_coherent(dev)) xen_dma_sync_for_cpu(dma_addr, paddr, size, dir); @@ -452,7 +452,7 @@ static void xen_swiotlb_sync_single_for_device(struct device *dev, dma_addr_t dma_addr, size_t size, enum dma_data_direction dir) { - phys_addr_t paddr = xen_bus_to_phys(dma_addr); + phys_addr_t paddr = xen_bus_to_phys(dev, dma_addr); if (is_xen_swiotlb_buffer(dma_addr)) swiotlb_tbl_sync_single(dev, paddr, size, dir, SYNC_FOR_DEVICE); -- GitLab From 297f7f82636115d6db4211b8d81764223baf2908 Mon Sep 17 00:00:00 2001 From: Stefano Stabellini Date: Fri, 10 Jul 2020 15:34:21 -0700 Subject: [PATCH 1264/1476] swiotlb-xen: add struct device * parameter to xen_dma_sync_for_cpu No functional changes. The parameter is unused in this patch but will be used by next patches. Signed-off-by: Stefano Stabellini Reviewed-by: Boris Ostrovsky Tested-by: Corey Minyard Tested-by: Roman Shaposhnik Link: https://lore.kernel.org/r/20200710223427.6897-5-sstabellini@kernel.org Signed-off-by: Juergen Gross --- arch/arm/xen/mm.c | 5 +++-- drivers/xen/swiotlb-xen.c | 4 ++-- include/xen/swiotlb-xen.h | 5 +++-- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/arch/arm/xen/mm.c b/arch/arm/xen/mm.c index d40e9e5fc52b7..1a00e8003c645 100644 --- a/arch/arm/xen/mm.c +++ b/arch/arm/xen/mm.c @@ -71,8 +71,9 @@ static void dma_cache_maint(dma_addr_t handle, size_t size, u32 op) * pfn_valid returns true the pages is local and we can use the native * dma-direct functions, otherwise we call the Xen specific version. */ -void xen_dma_sync_for_cpu(dma_addr_t handle, phys_addr_t paddr, size_t size, - enum dma_data_direction dir) +void xen_dma_sync_for_cpu(struct device *dev, dma_addr_t handle, + phys_addr_t paddr, size_t size, + enum dma_data_direction dir) { if (pfn_valid(PFN_DOWN(handle))) arch_sync_dma_for_cpu(paddr, size, dir); diff --git a/drivers/xen/swiotlb-xen.c b/drivers/xen/swiotlb-xen.c index a8e447137faf9..d04b7a15124f5 100644 --- a/drivers/xen/swiotlb-xen.c +++ b/drivers/xen/swiotlb-xen.c @@ -428,7 +428,7 @@ static void xen_swiotlb_unmap_page(struct device *hwdev, dma_addr_t dev_addr, BUG_ON(dir == DMA_NONE); if (!dev_is_dma_coherent(hwdev) && !(attrs & DMA_ATTR_SKIP_CPU_SYNC)) - xen_dma_sync_for_cpu(dev_addr, paddr, size, dir); + xen_dma_sync_for_cpu(hwdev, dev_addr, paddr, size, dir); /* NOTE: We use dev_addr here, not paddr! */ if (is_xen_swiotlb_buffer(dev_addr)) @@ -442,7 +442,7 @@ xen_swiotlb_sync_single_for_cpu(struct device *dev, dma_addr_t dma_addr, phys_addr_t paddr = xen_bus_to_phys(dev, dma_addr); if (!dev_is_dma_coherent(dev)) - xen_dma_sync_for_cpu(dma_addr, paddr, size, dir); + xen_dma_sync_for_cpu(dev, dma_addr, paddr, size, dir); if (is_xen_swiotlb_buffer(dma_addr)) swiotlb_tbl_sync_single(dev, paddr, size, dir, SYNC_FOR_CPU); diff --git a/include/xen/swiotlb-xen.h b/include/xen/swiotlb-xen.h index ffc0d3902b717..f62d1854780bb 100644 --- a/include/xen/swiotlb-xen.h +++ b/include/xen/swiotlb-xen.h @@ -4,8 +4,9 @@ #include -void xen_dma_sync_for_cpu(dma_addr_t handle, phys_addr_t paddr, size_t size, - enum dma_data_direction dir); +void xen_dma_sync_for_cpu(struct device *dev, dma_addr_t handle, + phys_addr_t paddr, size_t size, + enum dma_data_direction dir); void xen_dma_sync_for_device(dma_addr_t handle, phys_addr_t paddr, size_t size, enum dma_data_direction dir); -- GitLab From 995d3556694edd3c6dda7671b46ad4a6b3043f2f Mon Sep 17 00:00:00 2001 From: Stefano Stabellini Date: Fri, 10 Jul 2020 15:34:22 -0700 Subject: [PATCH 1265/1476] swiotlb-xen: add struct device * parameter to xen_dma_sync_for_device No functional changes. The parameter is unused in this patch but will be used by next patches. Signed-off-by: Stefano Stabellini Reviewed-by: Boris Ostrovsky Tested-by: Corey Minyard Tested-by: Roman Shaposhnik Link: https://lore.kernel.org/r/20200710223427.6897-6-sstabellini@kernel.org Signed-off-by: Juergen Gross --- arch/arm/xen/mm.c | 5 +++-- drivers/xen/swiotlb-xen.c | 4 ++-- include/xen/swiotlb-xen.h | 5 +++-- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/arch/arm/xen/mm.c b/arch/arm/xen/mm.c index 1a00e8003c645..f2414ea40a794 100644 --- a/arch/arm/xen/mm.c +++ b/arch/arm/xen/mm.c @@ -81,8 +81,9 @@ void xen_dma_sync_for_cpu(struct device *dev, dma_addr_t handle, dma_cache_maint(handle, size, GNTTAB_CACHE_INVAL); } -void xen_dma_sync_for_device(dma_addr_t handle, phys_addr_t paddr, size_t size, - enum dma_data_direction dir) +void xen_dma_sync_for_device(struct device *dev, dma_addr_t handle, + phys_addr_t paddr, size_t size, + enum dma_data_direction dir) { if (pfn_valid(PFN_DOWN(handle))) arch_sync_dma_for_device(paddr, size, dir); diff --git a/drivers/xen/swiotlb-xen.c b/drivers/xen/swiotlb-xen.c index d04b7a15124f5..8a3a7bcc5ec04 100644 --- a/drivers/xen/swiotlb-xen.c +++ b/drivers/xen/swiotlb-xen.c @@ -408,7 +408,7 @@ static dma_addr_t xen_swiotlb_map_page(struct device *dev, struct page *page, done: if (!dev_is_dma_coherent(dev) && !(attrs & DMA_ATTR_SKIP_CPU_SYNC)) - xen_dma_sync_for_device(dev_addr, phys, size, dir); + xen_dma_sync_for_device(dev, dev_addr, phys, size, dir); return dev_addr; } @@ -458,7 +458,7 @@ xen_swiotlb_sync_single_for_device(struct device *dev, dma_addr_t dma_addr, swiotlb_tbl_sync_single(dev, paddr, size, dir, SYNC_FOR_DEVICE); if (!dev_is_dma_coherent(dev)) - xen_dma_sync_for_device(dma_addr, paddr, size, dir); + xen_dma_sync_for_device(dev, dma_addr, paddr, size, dir); } /* diff --git a/include/xen/swiotlb-xen.h b/include/xen/swiotlb-xen.h index f62d1854780bb..6d235fe2b92d0 100644 --- a/include/xen/swiotlb-xen.h +++ b/include/xen/swiotlb-xen.h @@ -7,8 +7,9 @@ void xen_dma_sync_for_cpu(struct device *dev, dma_addr_t handle, phys_addr_t paddr, size_t size, enum dma_data_direction dir); -void xen_dma_sync_for_device(dma_addr_t handle, phys_addr_t paddr, size_t size, - enum dma_data_direction dir); +void xen_dma_sync_for_device(struct device *dev, dma_addr_t handle, + phys_addr_t paddr, size_t size, + enum dma_data_direction dir); extern int xen_swiotlb_init(int verbose, bool early); extern const struct dma_map_ops xen_swiotlb_dma_ops; -- GitLab From 38ba51de46767cbcef04305c3e8a99f0965fc9df Mon Sep 17 00:00:00 2001 From: Stefano Stabellini Date: Fri, 10 Jul 2020 15:34:23 -0700 Subject: [PATCH 1266/1476] swiotlb-xen: add struct device * parameter to is_xen_swiotlb_buffer No functional changes. The parameter is unused in this patch but will be used by next patches. Signed-off-by: Stefano Stabellini Reviewed-by: Boris Ostrovsky Tested-by: Corey Minyard Tested-by: Roman Shaposhnik Link: https://lore.kernel.org/r/20200710223427.6897-7-sstabellini@kernel.org Signed-off-by: Juergen Gross --- drivers/xen/swiotlb-xen.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/xen/swiotlb-xen.c b/drivers/xen/swiotlb-xen.c index 8a3a7bcc5ec04..e2c35f45f91e7 100644 --- a/drivers/xen/swiotlb-xen.c +++ b/drivers/xen/swiotlb-xen.c @@ -97,7 +97,7 @@ static inline int range_straddles_page_boundary(phys_addr_t p, size_t size) return 0; } -static int is_xen_swiotlb_buffer(dma_addr_t dma_addr) +static int is_xen_swiotlb_buffer(struct device *dev, dma_addr_t dma_addr) { unsigned long bfn = XEN_PFN_DOWN(dma_addr); unsigned long xen_pfn = bfn_to_local_pfn(bfn); @@ -431,7 +431,7 @@ static void xen_swiotlb_unmap_page(struct device *hwdev, dma_addr_t dev_addr, xen_dma_sync_for_cpu(hwdev, dev_addr, paddr, size, dir); /* NOTE: We use dev_addr here, not paddr! */ - if (is_xen_swiotlb_buffer(dev_addr)) + if (is_xen_swiotlb_buffer(hwdev, dev_addr)) swiotlb_tbl_unmap_single(hwdev, paddr, size, size, dir, attrs); } @@ -444,7 +444,7 @@ xen_swiotlb_sync_single_for_cpu(struct device *dev, dma_addr_t dma_addr, if (!dev_is_dma_coherent(dev)) xen_dma_sync_for_cpu(dev, dma_addr, paddr, size, dir); - if (is_xen_swiotlb_buffer(dma_addr)) + if (is_xen_swiotlb_buffer(dev, dma_addr)) swiotlb_tbl_sync_single(dev, paddr, size, dir, SYNC_FOR_CPU); } @@ -454,7 +454,7 @@ xen_swiotlb_sync_single_for_device(struct device *dev, dma_addr_t dma_addr, { phys_addr_t paddr = xen_bus_to_phys(dev, dma_addr); - if (is_xen_swiotlb_buffer(dma_addr)) + if (is_xen_swiotlb_buffer(dev, dma_addr)) swiotlb_tbl_sync_single(dev, paddr, size, dir, SYNC_FOR_DEVICE); if (!dev_is_dma_coherent(dev)) -- GitLab From e9aab7e4ffbbf6eba8eaa66e351557c0c7562cff Mon Sep 17 00:00:00 2001 From: Stefano Stabellini Date: Fri, 10 Jul 2020 15:34:24 -0700 Subject: [PATCH 1267/1476] swiotlb-xen: remove XEN_PFN_PHYS XEN_PFN_PHYS is only used in one place in swiotlb-xen making things more complex than need to be. Remove the definition of XEN_PFN_PHYS and open code the cast in the one place where it is needed. Signed-off-by: Stefano Stabellini Reviewed-by: Juergen Gross Link: https://lore.kernel.org/r/20200710223427.6897-8-sstabellini@kernel.org Signed-off-by: Juergen Gross --- drivers/xen/swiotlb-xen.c | 7 +------ include/xen/page.h | 1 - 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/drivers/xen/swiotlb-xen.c b/drivers/xen/swiotlb-xen.c index e2c35f45f91e7..03d118b6c1418 100644 --- a/drivers/xen/swiotlb-xen.c +++ b/drivers/xen/swiotlb-xen.c @@ -52,11 +52,6 @@ static unsigned long xen_io_tlb_nslabs; * Quick lookup value of the bus address of the IOTLB. */ -/* - * Both of these functions should avoid XEN_PFN_PHYS because phys_addr_t - * can be 32bit when dma_addr_t is 64bit leading to a loss in - * information if the shift is done before casting to 64bit. - */ static inline dma_addr_t xen_phys_to_bus(struct device *dev, phys_addr_t paddr) { unsigned long bfn = pfn_to_bfn(XEN_PFN_DOWN(paddr)); @@ -101,7 +96,7 @@ static int is_xen_swiotlb_buffer(struct device *dev, dma_addr_t dma_addr) { unsigned long bfn = XEN_PFN_DOWN(dma_addr); unsigned long xen_pfn = bfn_to_local_pfn(bfn); - phys_addr_t paddr = XEN_PFN_PHYS(xen_pfn); + phys_addr_t paddr = (phys_addr_t)xen_pfn << XEN_PAGE_SHIFT; /* If the address is outside our domain, it CAN * have the same virtual address as another address diff --git a/include/xen/page.h b/include/xen/page.h index df6d6b6ec66e9..285677b42943a 100644 --- a/include/xen/page.h +++ b/include/xen/page.h @@ -24,7 +24,6 @@ #define XEN_PFN_DOWN(x) ((x) >> XEN_PAGE_SHIFT) #define XEN_PFN_UP(x) (((x) + XEN_PAGE_SIZE-1) >> XEN_PAGE_SHIFT) -#define XEN_PFN_PHYS(x) ((phys_addr_t)(x) << XEN_PAGE_SHIFT) #include -- GitLab From 91ffe4ad534ab2262cbcfb339c5ae190c7ac0e7c Mon Sep 17 00:00:00 2001 From: Stefano Stabellini Date: Fri, 10 Jul 2020 15:34:25 -0700 Subject: [PATCH 1268/1476] swiotlb-xen: introduce phys_to_dma/dma_to_phys translations With some devices physical addresses are different than dma addresses. To be able to deal with these cases, we need to call phys_to_dma on physical addresses (including machine addresses in Xen terminology) before returning them from xen_swiotlb_alloc_coherent and xen_swiotlb_map_page. We also need to convert dma addresses back to physical addresses using dma_to_phys in xen_swiotlb_free_coherent and xen_swiotlb_unmap_page if we want to do any operations on them. Call dma_to_phys in is_xen_swiotlb_buffer. Introduce xen_phys_to_dma and call phys_to_dma in its implementation. Introduce xen_dma_to_phys and call dma_to_phys in its implementation. Call xen_phys_to_dma/xen_dma_to_phys instead of xen_phys_to_bus/xen_bus_to_phys through swiotlb-xen.c. Everything is taken care of by these changes except for xen_swiotlb_alloc_coherent and xen_swiotlb_free_coherent, which need a few explicit phys_to_dma/dma_to_phys calls. Signed-off-by: Stefano Stabellini Tested-by: Corey Minyard Tested-by: Roman Shaposhnik Reviewed-by: Juergen Gross Link: https://lore.kernel.org/r/20200710223427.6897-9-sstabellini@kernel.org Signed-off-by: Juergen Gross --- drivers/xen/swiotlb-xen.c | 53 +++++++++++++++++++++++---------------- 1 file changed, 32 insertions(+), 21 deletions(-) diff --git a/drivers/xen/swiotlb-xen.c b/drivers/xen/swiotlb-xen.c index 03d118b6c1418..a6a95358a8cb6 100644 --- a/drivers/xen/swiotlb-xen.c +++ b/drivers/xen/swiotlb-xen.c @@ -52,30 +52,39 @@ static unsigned long xen_io_tlb_nslabs; * Quick lookup value of the bus address of the IOTLB. */ -static inline dma_addr_t xen_phys_to_bus(struct device *dev, phys_addr_t paddr) +static inline phys_addr_t xen_phys_to_bus(struct device *dev, phys_addr_t paddr) { unsigned long bfn = pfn_to_bfn(XEN_PFN_DOWN(paddr)); - dma_addr_t dma = (dma_addr_t)bfn << XEN_PAGE_SHIFT; + phys_addr_t baddr = (phys_addr_t)bfn << XEN_PAGE_SHIFT; - dma |= paddr & ~XEN_PAGE_MASK; + baddr |= paddr & ~XEN_PAGE_MASK; + return baddr; +} - return dma; +static inline dma_addr_t xen_phys_to_dma(struct device *dev, phys_addr_t paddr) +{ + return phys_to_dma(dev, xen_phys_to_bus(dev, paddr)); } -static inline phys_addr_t xen_bus_to_phys(struct device *dev, dma_addr_t baddr) +static inline phys_addr_t xen_bus_to_phys(struct device *dev, + phys_addr_t baddr) { unsigned long xen_pfn = bfn_to_pfn(XEN_PFN_DOWN(baddr)); - dma_addr_t dma = (dma_addr_t)xen_pfn << XEN_PAGE_SHIFT; - phys_addr_t paddr = dma; - - paddr |= baddr & ~XEN_PAGE_MASK; + phys_addr_t paddr = (xen_pfn << XEN_PAGE_SHIFT) | + (baddr & ~XEN_PAGE_MASK); return paddr; } +static inline phys_addr_t xen_dma_to_phys(struct device *dev, + dma_addr_t dma_addr) +{ + return xen_bus_to_phys(dev, dma_to_phys(dev, dma_addr)); +} + static inline dma_addr_t xen_virt_to_bus(struct device *dev, void *address) { - return xen_phys_to_bus(dev, virt_to_phys(address)); + return xen_phys_to_dma(dev, virt_to_phys(address)); } static inline int range_straddles_page_boundary(phys_addr_t p, size_t size) @@ -94,7 +103,7 @@ static inline int range_straddles_page_boundary(phys_addr_t p, size_t size) static int is_xen_swiotlb_buffer(struct device *dev, dma_addr_t dma_addr) { - unsigned long bfn = XEN_PFN_DOWN(dma_addr); + unsigned long bfn = XEN_PFN_DOWN(dma_to_phys(dev, dma_addr)); unsigned long xen_pfn = bfn_to_local_pfn(bfn); phys_addr_t paddr = (phys_addr_t)xen_pfn << XEN_PAGE_SHIFT; @@ -299,12 +308,12 @@ xen_swiotlb_alloc_coherent(struct device *hwdev, size_t size, if (hwdev && hwdev->coherent_dma_mask) dma_mask = hwdev->coherent_dma_mask; - /* At this point dma_handle is the physical address, next we are + /* At this point dma_handle is the dma address, next we are * going to set it to the machine address. * Do not use virt_to_phys(ret) because on ARM it doesn't correspond * to *dma_handle. */ - phys = *dma_handle; - dev_addr = xen_phys_to_bus(hwdev, phys); + phys = dma_to_phys(hwdev, *dma_handle); + dev_addr = xen_phys_to_dma(hwdev, phys); if (((dev_addr + size - 1 <= dma_mask)) && !range_straddles_page_boundary(phys, size)) *dma_handle = dev_addr; @@ -314,6 +323,7 @@ xen_swiotlb_alloc_coherent(struct device *hwdev, size_t size, xen_free_coherent_pages(hwdev, size, ret, (dma_addr_t)phys, attrs); return NULL; } + *dma_handle = phys_to_dma(hwdev, *dma_handle); SetPageXenRemapped(virt_to_page(ret)); } memset(ret, 0, size); @@ -334,7 +344,7 @@ xen_swiotlb_free_coherent(struct device *hwdev, size_t size, void *vaddr, /* do not use virt_to_phys because on ARM it doesn't return you the * physical address */ - phys = xen_bus_to_phys(hwdev, dev_addr); + phys = xen_dma_to_phys(hwdev, dev_addr); /* Convert the size to actually allocated. */ size = 1UL << (order + XEN_PAGE_SHIFT); @@ -349,7 +359,8 @@ xen_swiotlb_free_coherent(struct device *hwdev, size_t size, void *vaddr, TestClearPageXenRemapped(page)) xen_destroy_contiguous_region(phys, order); - xen_free_coherent_pages(hwdev, size, vaddr, (dma_addr_t)phys, attrs); + xen_free_coherent_pages(hwdev, size, vaddr, phys_to_dma(hwdev, phys), + attrs); } /* @@ -365,7 +376,7 @@ static dma_addr_t xen_swiotlb_map_page(struct device *dev, struct page *page, unsigned long attrs) { phys_addr_t map, phys = page_to_phys(page) + offset; - dma_addr_t dev_addr = xen_phys_to_bus(dev, phys); + dma_addr_t dev_addr = xen_phys_to_dma(dev, phys); BUG_ON(dir == DMA_NONE); /* @@ -390,7 +401,7 @@ static dma_addr_t xen_swiotlb_map_page(struct device *dev, struct page *page, return DMA_MAPPING_ERROR; phys = map; - dev_addr = xen_phys_to_bus(dev, map); + dev_addr = xen_phys_to_dma(dev, map); /* * Ensure that the address returned is DMA'ble @@ -418,7 +429,7 @@ done: static void xen_swiotlb_unmap_page(struct device *hwdev, dma_addr_t dev_addr, size_t size, enum dma_data_direction dir, unsigned long attrs) { - phys_addr_t paddr = xen_bus_to_phys(hwdev, dev_addr); + phys_addr_t paddr = xen_dma_to_phys(hwdev, dev_addr); BUG_ON(dir == DMA_NONE); @@ -434,7 +445,7 @@ static void xen_swiotlb_sync_single_for_cpu(struct device *dev, dma_addr_t dma_addr, size_t size, enum dma_data_direction dir) { - phys_addr_t paddr = xen_bus_to_phys(dev, dma_addr); + phys_addr_t paddr = xen_dma_to_phys(dev, dma_addr); if (!dev_is_dma_coherent(dev)) xen_dma_sync_for_cpu(dev, dma_addr, paddr, size, dir); @@ -447,7 +458,7 @@ static void xen_swiotlb_sync_single_for_device(struct device *dev, dma_addr_t dma_addr, size_t size, enum dma_data_direction dir) { - phys_addr_t paddr = xen_bus_to_phys(dev, dma_addr); + phys_addr_t paddr = xen_dma_to_phys(dev, dma_addr); if (is_xen_swiotlb_buffer(dev, dma_addr)) swiotlb_tbl_sync_single(dev, paddr, size, dir, SYNC_FOR_DEVICE); -- GitLab From 63f0620cc552c4cd5bb2747f77efce407487cb12 Mon Sep 17 00:00:00 2001 From: Stefano Stabellini Date: Fri, 10 Jul 2020 15:34:26 -0700 Subject: [PATCH 1269/1476] xen/arm: introduce phys/dma translations in xen_dma_sync_for_* xen_dma_sync_for_cpu, xen_dma_sync_for_device, xen_arch_need_swiotlb are getting called passing dma addresses. On some platforms dma addresses could be different from physical addresses. Before doing any operations on these addresses we need to convert them back to physical addresses using dma_to_phys. Move the arch_sync_dma_for_cpu and arch_sync_dma_for_device calls from xen_dma_sync_for_cpu/device to swiotlb-xen.c, and add a call dma_to_phys to do address translations there. dma_cache_maint is fixed by the next patch. Signed-off-by: Stefano Stabellini Tested-by: Corey Minyard Tested-by: Roman Shaposhnik Acked-by: Juergen Gross Link: https://lore.kernel.org/r/20200710223427.6897-10-sstabellini@kernel.org Signed-off-by: Juergen Gross --- arch/arm/xen/mm.c | 17 ++++++----------- drivers/xen/swiotlb-xen.c | 32 ++++++++++++++++++++++++-------- include/xen/swiotlb-xen.h | 6 ++---- 3 files changed, 32 insertions(+), 23 deletions(-) diff --git a/arch/arm/xen/mm.c b/arch/arm/xen/mm.c index f2414ea40a794..a8251a70f442d 100644 --- a/arch/arm/xen/mm.c +++ b/arch/arm/xen/mm.c @@ -1,5 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only #include +#include #include #include #include @@ -72,22 +73,16 @@ static void dma_cache_maint(dma_addr_t handle, size_t size, u32 op) * dma-direct functions, otherwise we call the Xen specific version. */ void xen_dma_sync_for_cpu(struct device *dev, dma_addr_t handle, - phys_addr_t paddr, size_t size, - enum dma_data_direction dir) + size_t size, enum dma_data_direction dir) { - if (pfn_valid(PFN_DOWN(handle))) - arch_sync_dma_for_cpu(paddr, size, dir); - else if (dir != DMA_TO_DEVICE) + if (dir != DMA_TO_DEVICE) dma_cache_maint(handle, size, GNTTAB_CACHE_INVAL); } void xen_dma_sync_for_device(struct device *dev, dma_addr_t handle, - phys_addr_t paddr, size_t size, - enum dma_data_direction dir) + size_t size, enum dma_data_direction dir) { - if (pfn_valid(PFN_DOWN(handle))) - arch_sync_dma_for_device(paddr, size, dir); - else if (dir == DMA_FROM_DEVICE) + if (dir == DMA_FROM_DEVICE) dma_cache_maint(handle, size, GNTTAB_CACHE_INVAL); else dma_cache_maint(handle, size, GNTTAB_CACHE_CLEAN); @@ -98,7 +93,7 @@ bool xen_arch_need_swiotlb(struct device *dev, dma_addr_t dev_addr) { unsigned int xen_pfn = XEN_PFN_DOWN(phys); - unsigned int bfn = XEN_PFN_DOWN(dev_addr); + unsigned int bfn = XEN_PFN_DOWN(dma_to_phys(dev, dev_addr)); /* * The swiotlb buffer should be used if diff --git a/drivers/xen/swiotlb-xen.c b/drivers/xen/swiotlb-xen.c index a6a95358a8cb6..39a0f2e0847c9 100644 --- a/drivers/xen/swiotlb-xen.c +++ b/drivers/xen/swiotlb-xen.c @@ -413,8 +413,12 @@ static dma_addr_t xen_swiotlb_map_page(struct device *dev, struct page *page, } done: - if (!dev_is_dma_coherent(dev) && !(attrs & DMA_ATTR_SKIP_CPU_SYNC)) - xen_dma_sync_for_device(dev, dev_addr, phys, size, dir); + if (!dev_is_dma_coherent(dev) && !(attrs & DMA_ATTR_SKIP_CPU_SYNC)) { + if (pfn_valid(PFN_DOWN(dma_to_phys(dev, dev_addr)))) + arch_sync_dma_for_device(phys, size, dir); + else + xen_dma_sync_for_device(dev, dev_addr, size, dir); + } return dev_addr; } @@ -433,8 +437,12 @@ static void xen_swiotlb_unmap_page(struct device *hwdev, dma_addr_t dev_addr, BUG_ON(dir == DMA_NONE); - if (!dev_is_dma_coherent(hwdev) && !(attrs & DMA_ATTR_SKIP_CPU_SYNC)) - xen_dma_sync_for_cpu(hwdev, dev_addr, paddr, size, dir); + if (!dev_is_dma_coherent(hwdev) && !(attrs & DMA_ATTR_SKIP_CPU_SYNC)) { + if (pfn_valid(PFN_DOWN(dma_to_phys(hwdev, dev_addr)))) + arch_sync_dma_for_cpu(paddr, size, dir); + else + xen_dma_sync_for_cpu(hwdev, dev_addr, size, dir); + } /* NOTE: We use dev_addr here, not paddr! */ if (is_xen_swiotlb_buffer(hwdev, dev_addr)) @@ -447,8 +455,12 @@ xen_swiotlb_sync_single_for_cpu(struct device *dev, dma_addr_t dma_addr, { phys_addr_t paddr = xen_dma_to_phys(dev, dma_addr); - if (!dev_is_dma_coherent(dev)) - xen_dma_sync_for_cpu(dev, dma_addr, paddr, size, dir); + if (!dev_is_dma_coherent(dev)) { + if (pfn_valid(PFN_DOWN(dma_to_phys(dev, dma_addr)))) + arch_sync_dma_for_cpu(paddr, size, dir); + else + xen_dma_sync_for_cpu(dev, dma_addr, size, dir); + } if (is_xen_swiotlb_buffer(dev, dma_addr)) swiotlb_tbl_sync_single(dev, paddr, size, dir, SYNC_FOR_CPU); @@ -463,8 +475,12 @@ xen_swiotlb_sync_single_for_device(struct device *dev, dma_addr_t dma_addr, if (is_xen_swiotlb_buffer(dev, dma_addr)) swiotlb_tbl_sync_single(dev, paddr, size, dir, SYNC_FOR_DEVICE); - if (!dev_is_dma_coherent(dev)) - xen_dma_sync_for_device(dev, dma_addr, paddr, size, dir); + if (!dev_is_dma_coherent(dev)) { + if (pfn_valid(PFN_DOWN(dma_to_phys(dev, dma_addr)))) + arch_sync_dma_for_device(paddr, size, dir); + else + xen_dma_sync_for_device(dev, dma_addr, size, dir); + } } /* diff --git a/include/xen/swiotlb-xen.h b/include/xen/swiotlb-xen.h index 6d235fe2b92d0..d5eaf9d682b80 100644 --- a/include/xen/swiotlb-xen.h +++ b/include/xen/swiotlb-xen.h @@ -5,11 +5,9 @@ #include void xen_dma_sync_for_cpu(struct device *dev, dma_addr_t handle, - phys_addr_t paddr, size_t size, - enum dma_data_direction dir); + size_t size, enum dma_data_direction dir); void xen_dma_sync_for_device(struct device *dev, dma_addr_t handle, - phys_addr_t paddr, size_t size, - enum dma_data_direction dir); + size_t size, enum dma_data_direction dir); extern int xen_swiotlb_init(int verbose, bool early); extern const struct dma_map_ops xen_swiotlb_dma_ops; -- GitLab From d7b461caa6cc64dd190577b46b0ec892a8d5e7c0 Mon Sep 17 00:00:00 2001 From: Stefano Stabellini Date: Fri, 10 Jul 2020 15:34:27 -0700 Subject: [PATCH 1270/1476] xen/arm: call dma_to_phys on the dma_addr_t parameter of dma_cache_maint dma_cache_maint is getting called passing a dma address which could be different from a physical address. Add a struct device* parameter to dma_cache_maint. Translate the dma_addr_t parameter of dma_cache_maint by calling dma_to_phys. Do it for the first page and all the following pages, in case of multipage handling. Signed-off-by: Stefano Stabellini Reviewed-by: Boris Ostrovsky Tested-by: Corey Minyard Tested-by: Roman Shaposhnik Link: https://lore.kernel.org/r/20200710223427.6897-11-sstabellini@kernel.org Signed-off-by: Juergen Gross --- arch/arm/xen/mm.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/arch/arm/xen/mm.c b/arch/arm/xen/mm.c index a8251a70f442d..396797ffe2b1d 100644 --- a/arch/arm/xen/mm.c +++ b/arch/arm/xen/mm.c @@ -43,15 +43,18 @@ unsigned long xen_get_swiotlb_free_pages(unsigned int order) static bool hypercall_cflush = false; /* buffers in highmem or foreign pages cannot cross page boundaries */ -static void dma_cache_maint(dma_addr_t handle, size_t size, u32 op) +static void dma_cache_maint(struct device *dev, dma_addr_t handle, + size_t size, u32 op) { struct gnttab_cache_flush cflush; - cflush.a.dev_bus_addr = handle & XEN_PAGE_MASK; cflush.offset = xen_offset_in_page(handle); cflush.op = op; + handle &= XEN_PAGE_MASK; do { + cflush.a.dev_bus_addr = dma_to_phys(dev, handle); + if (size + cflush.offset > XEN_PAGE_SIZE) cflush.length = XEN_PAGE_SIZE - cflush.offset; else @@ -60,7 +63,7 @@ static void dma_cache_maint(dma_addr_t handle, size_t size, u32 op) HYPERVISOR_grant_table_op(GNTTABOP_cache_flush, &cflush, 1); cflush.offset = 0; - cflush.a.dev_bus_addr += cflush.length; + handle += cflush.length; size -= cflush.length; } while (size); } @@ -76,16 +79,16 @@ void xen_dma_sync_for_cpu(struct device *dev, dma_addr_t handle, size_t size, enum dma_data_direction dir) { if (dir != DMA_TO_DEVICE) - dma_cache_maint(handle, size, GNTTAB_CACHE_INVAL); + dma_cache_maint(dev, handle, size, GNTTAB_CACHE_INVAL); } void xen_dma_sync_for_device(struct device *dev, dma_addr_t handle, size_t size, enum dma_data_direction dir) { if (dir == DMA_FROM_DEVICE) - dma_cache_maint(handle, size, GNTTAB_CACHE_INVAL); + dma_cache_maint(dev, handle, size, GNTTAB_CACHE_INVAL); else - dma_cache_maint(handle, size, GNTTAB_CACHE_CLEAN); + dma_cache_maint(dev, handle, size, GNTTAB_CACHE_CLEAN); } bool xen_arch_need_swiotlb(struct device *dev, -- GitLab From 403d2d116ec011942a8505068b943c5c6cd91176 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Fri, 31 Jul 2020 19:03:26 +0200 Subject: [PATCH 1271/1476] PM: runtime: Add kerneldoc comments to multiple helpers Add kerneldoc comments to multiple PM-runtime helper functions defined as static inline wrappers around lower-level routines to provide quick reference decumentation of their behavior. Some of them are similar to each other with subtle differences only and the behavior of some of them may appear as counter-intuitive, so clarify all that to avoid confusion. Signed-off-by: Rafael J. Wysocki Acked-by: Alan Stern Reviewed-by: Sakari Ailus --- include/linux/pm_runtime.h | 246 +++++++++++++++++++++++++++++++++++++ 1 file changed, 246 insertions(+) diff --git a/include/linux/pm_runtime.h b/include/linux/pm_runtime.h index 3dbc207bff53c..6245caa18034c 100644 --- a/include/linux/pm_runtime.h +++ b/include/linux/pm_runtime.h @@ -60,58 +60,151 @@ extern void pm_runtime_put_suppliers(struct device *dev); extern void pm_runtime_new_link(struct device *dev); extern void pm_runtime_drop_link(struct device *dev); +/** + * pm_runtime_get_if_in_use - Conditionally bump up runtime PM usage counter. + * @dev: Target device. + * + * Increment the runtime PM usage counter of @dev if its runtime PM status is + * %RPM_ACTIVE and its runtime PM usage counter is greater than 0. + */ static inline int pm_runtime_get_if_in_use(struct device *dev) { return pm_runtime_get_if_active(dev, false); } +/** + * pm_suspend_ignore_children - Set runtime PM behavior regarding children. + * @dev: Target device. + * @enable: Whether or not to ignore possible dependencies on children. + * + * The dependencies of @dev on its children will not be taken into account by + * the runtime PM framework going forward if @enable is %true, or they will + * be taken into account otherwise. + */ static inline void pm_suspend_ignore_children(struct device *dev, bool enable) { dev->power.ignore_children = enable; } +/** + * pm_runtime_get_noresume - Bump up runtime PM usage counter of a device. + * @dev: Target device. + */ static inline void pm_runtime_get_noresume(struct device *dev) { atomic_inc(&dev->power.usage_count); } +/** + * pm_runtime_put_noidle - Drop runtime PM usage counter of a device. + * @dev: Target device. + * + * Decrement the runtime PM usage counter of @dev unless it is 0 already. + */ static inline void pm_runtime_put_noidle(struct device *dev) { atomic_add_unless(&dev->power.usage_count, -1, 0); } +/** + * pm_runtime_suspended - Check whether or not a device is runtime-suspended. + * @dev: Target device. + * + * Return %true if runtime PM is enabled for @dev and its runtime PM status is + * %RPM_SUSPENDED, or %false otherwise. + * + * Note that the return value of this function can only be trusted if it is + * called under the runtime PM lock of @dev or under conditions in which + * runtime PM cannot be either disabled or enabled for @dev and its runtime PM + * status cannot change. + */ static inline bool pm_runtime_suspended(struct device *dev) { return dev->power.runtime_status == RPM_SUSPENDED && !dev->power.disable_depth; } +/** + * pm_runtime_active - Check whether or not a device is runtime-active. + * @dev: Target device. + * + * Return %true if runtime PM is enabled for @dev and its runtime PM status is + * %RPM_ACTIVE, or %false otherwise. + * + * Note that the return value of this function can only be trusted if it is + * called under the runtime PM lock of @dev or under conditions in which + * runtime PM cannot be either disabled or enabled for @dev and its runtime PM + * status cannot change. + */ static inline bool pm_runtime_active(struct device *dev) { return dev->power.runtime_status == RPM_ACTIVE || dev->power.disable_depth; } +/** + * pm_runtime_status_suspended - Check if runtime PM status is "suspended". + * @dev: Target device. + * + * Return %true if the runtime PM status of @dev is %RPM_SUSPENDED, or %false + * otherwise, regardless of whether or not runtime PM has been enabled for @dev. + * + * Note that the return value of this function can only be trusted if it is + * called under the runtime PM lock of @dev or under conditions in which the + * runtime PM status of @dev cannot change. + */ static inline bool pm_runtime_status_suspended(struct device *dev) { return dev->power.runtime_status == RPM_SUSPENDED; } +/** + * pm_runtime_enabled - Check if runtime PM is enabled. + * @dev: Target device. + * + * Return %true if runtime PM is enabled for @dev or %false otherwise. + * + * Note that the return value of this function can only be trusted if it is + * called under the runtime PM lock of @dev or under conditions in which + * runtime PM cannot be either disabled or enabled for @dev. + */ static inline bool pm_runtime_enabled(struct device *dev) { return !dev->power.disable_depth; } +/** + * pm_runtime_has_no_callbacks - Check if runtime PM callbacks may be present. + * @dev: Target device. + * + * Return %true if @dev is a special device without runtime PM callbacks or + * %false otherwise. + */ static inline bool pm_runtime_has_no_callbacks(struct device *dev) { return dev->power.no_callbacks; } +/** + * pm_runtime_mark_last_busy - Update the last access time of a device. + * @dev: Target device. + * + * Update the last access time of @dev used by the runtime PM autosuspend + * mechanism to the current time as returned by ktime_get_mono_fast_ns(). + */ static inline void pm_runtime_mark_last_busy(struct device *dev) { WRITE_ONCE(dev->power.last_busy, ktime_get_mono_fast_ns()); } +/** + * pm_runtime_is_irq_safe - Check if runtime PM can work in interrupt context. + * @dev: Target device. + * + * Return %true if @dev has been marked as an "IRQ-safe" device (with respect + * to runtime PM), in which case its runtime PM callabcks can be expected to + * work correctly when invoked from interrupt handlers. + */ static inline bool pm_runtime_is_irq_safe(struct device *dev) { return dev->power.irq_safe; @@ -191,97 +284,250 @@ static inline void pm_runtime_drop_link(struct device *dev) {} #endif /* !CONFIG_PM */ +/** + * pm_runtime_idle - Conditionally set up autosuspend of a device or suspend it. + * @dev: Target device. + * + * Invoke the "idle check" callback of @dev and, depending on its return value, + * set up autosuspend of @dev or suspend it (depending on whether or not + * autosuspend has been enabled for it). + */ static inline int pm_runtime_idle(struct device *dev) { return __pm_runtime_idle(dev, 0); } +/** + * pm_runtime_suspend - Suspend a device synchronously. + * @dev: Target device. + */ static inline int pm_runtime_suspend(struct device *dev) { return __pm_runtime_suspend(dev, 0); } +/** + * pm_runtime_autosuspend - Set up autosuspend of a device or suspend it. + * @dev: Target device. + * + * Set up autosuspend of @dev or suspend it (depending on whether or not + * autosuspend is enabled for it) without engaging its "idle check" callback. + */ static inline int pm_runtime_autosuspend(struct device *dev) { return __pm_runtime_suspend(dev, RPM_AUTO); } +/** + * pm_runtime_resume - Resume a device synchronously. + * @dev: Target device. + */ static inline int pm_runtime_resume(struct device *dev) { return __pm_runtime_resume(dev, 0); } +/** + * pm_request_idle - Queue up "idle check" execution for a device. + * @dev: Target device. + * + * Queue up a work item to run an equivalent of pm_runtime_idle() for @dev + * asynchronously. + */ static inline int pm_request_idle(struct device *dev) { return __pm_runtime_idle(dev, RPM_ASYNC); } +/** + * pm_request_resume - Queue up runtime-resume of a device. + * @dev: Target device. + */ static inline int pm_request_resume(struct device *dev) { return __pm_runtime_resume(dev, RPM_ASYNC); } +/** + * pm_request_autosuspend - Queue up autosuspend of a device. + * @dev: Target device. + * + * Queue up a work item to run an equivalent pm_runtime_autosuspend() for @dev + * asynchronously. + */ static inline int pm_request_autosuspend(struct device *dev) { return __pm_runtime_suspend(dev, RPM_ASYNC | RPM_AUTO); } +/** + * pm_runtime_get - Bump up usage counter and queue up resume of a device. + * @dev: Target device. + * + * Bump up the runtime PM usage counter of @dev and queue up a work item to + * carry out runtime-resume of it. + */ static inline int pm_runtime_get(struct device *dev) { return __pm_runtime_resume(dev, RPM_GET_PUT | RPM_ASYNC); } +/** + * pm_runtime_get_sync - Bump up usage counter of a device and resume it. + * @dev: Target device. + * + * Bump up the runtime PM usage counter of @dev and carry out runtime-resume of + * it synchronously. + * + * The possible return values of this function are the same as for + * pm_runtime_resume() and the runtime PM usage counter of @dev remains + * incremented in all cases, even if it returns an error code. + */ static inline int pm_runtime_get_sync(struct device *dev) { return __pm_runtime_resume(dev, RPM_GET_PUT); } +/** + * pm_runtime_put - Drop device usage counter and queue up "idle check" if 0. + * @dev: Target device. + * + * Decrement the runtime PM usage counter of @dev and if it turns out to be + * equal to 0, queue up a work item for @dev like in pm_request_idle(). + */ static inline int pm_runtime_put(struct device *dev) { return __pm_runtime_idle(dev, RPM_GET_PUT | RPM_ASYNC); } +/** + * pm_runtime_put_autosuspend - Drop device usage counter and queue autosuspend if 0. + * @dev: Target device. + * + * Decrement the runtime PM usage counter of @dev and if it turns out to be + * equal to 0, queue up a work item for @dev like in pm_request_autosuspend(). + */ static inline int pm_runtime_put_autosuspend(struct device *dev) { return __pm_runtime_suspend(dev, RPM_GET_PUT | RPM_ASYNC | RPM_AUTO); } +/** + * pm_runtime_put_sync - Drop device usage counter and run "idle check" if 0. + * @dev: Target device. + * + * Decrement the runtime PM usage counter of @dev and if it turns out to be + * equal to 0, invoke the "idle check" callback of @dev and, depending on its + * return value, set up autosuspend of @dev or suspend it (depending on whether + * or not autosuspend has been enabled for it). + * + * The possible return values of this function are the same as for + * pm_runtime_idle() and the runtime PM usage counter of @dev remains + * decremented in all cases, even if it returns an error code. + */ static inline int pm_runtime_put_sync(struct device *dev) { return __pm_runtime_idle(dev, RPM_GET_PUT); } +/** + * pm_runtime_put_sync_suspend - Drop device usage counter and suspend if 0. + * @dev: Target device. + * + * Decrement the runtime PM usage counter of @dev and if it turns out to be + * equal to 0, carry out runtime-suspend of @dev synchronously. + * + * The possible return values of this function are the same as for + * pm_runtime_suspend() and the runtime PM usage counter of @dev remains + * decremented in all cases, even if it returns an error code. + */ static inline int pm_runtime_put_sync_suspend(struct device *dev) { return __pm_runtime_suspend(dev, RPM_GET_PUT); } +/** + * pm_runtime_put_sync_autosuspend - Drop device usage counter and autosuspend if 0. + * @dev: Target device. + * + * Decrement the runtime PM usage counter of @dev and if it turns out to be + * equal to 0, set up autosuspend of @dev or suspend it synchronously (depending + * on whether or not autosuspend has been enabled for it). + * + * The possible return values of this function are the same as for + * pm_runtime_autosuspend() and the runtime PM usage counter of @dev remains + * decremented in all cases, even if it returns an error code. + */ static inline int pm_runtime_put_sync_autosuspend(struct device *dev) { return __pm_runtime_suspend(dev, RPM_GET_PUT | RPM_AUTO); } +/** + * pm_runtime_set_active - Set runtime PM status to "active". + * @dev: Target device. + * + * Set the runtime PM status of @dev to %RPM_ACTIVE and ensure that dependencies + * of it will be taken into account. + * + * It is not valid to call this function for devices with runtime PM enabled. + */ static inline int pm_runtime_set_active(struct device *dev) { return __pm_runtime_set_status(dev, RPM_ACTIVE); } +/** + * pm_runtime_set_suspended - Set runtime PM status to "active". + * @dev: Target device. + * + * Set the runtime PM status of @dev to %RPM_SUSPENDED and ensure that + * dependencies of it will be taken into account. + * + * It is not valid to call this function for devices with runtime PM enabled. + */ static inline int pm_runtime_set_suspended(struct device *dev) { return __pm_runtime_set_status(dev, RPM_SUSPENDED); } +/** + * pm_runtime_disable - Disable runtime PM for a device. + * @dev: Target device. + * + * Prevent the runtime PM framework from working with @dev (by incrementing its + * "blocking" counter). + * + * For each invocation of this function for @dev there must be a matching + * pm_runtime_enable() call in order for runtime PM to be enabled for it. + */ static inline void pm_runtime_disable(struct device *dev) { __pm_runtime_disable(dev, true); } +/** + * pm_runtime_use_autosuspend - Allow autosuspend to be used for a device. + * @dev: Target device. + * + * Allow the runtime PM autosuspend mechanism to be used for @dev whenever + * requested (or "autosuspend" will be handled as direct runtime-suspend for + * it). + */ static inline void pm_runtime_use_autosuspend(struct device *dev) { __pm_runtime_use_autosuspend(dev, true); } +/** + * pm_runtime_dont_use_autosuspend - Prevent autosuspend from being used. + * @dev: Target device. + * + * Prevent the runtime PM autosuspend mechanism from being used for @dev which + * means that "autosuspend" will be handled as direct runtime-suspend for it + * going forward. + */ static inline void pm_runtime_dont_use_autosuspend(struct device *dev) { __pm_runtime_use_autosuspend(dev, false); -- GitLab From 0abf803ef1b52cd3d33fceb7d7c3cd2f4d519ec9 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Fri, 31 Jul 2020 19:04:11 +0200 Subject: [PATCH 1272/1476] PM: runtime: Improve kerneldoc of pm_runtime_get_if_active() The kerneldoc comment of pm_runtime_get_if_active() doesn't list the second argument of the function properly, so fix that and while at it clarify that comment somewhat and add some markup to it. Signed-off-by: Rafael J. Wysocki Reviewed-by: Sakari Ailus --- drivers/base/power/runtime.c | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/drivers/base/power/runtime.c b/drivers/base/power/runtime.c index 9f62790f644c5..8143210a5c547 100644 --- a/drivers/base/power/runtime.c +++ b/drivers/base/power/runtime.c @@ -1085,24 +1085,26 @@ int __pm_runtime_resume(struct device *dev, int rpmflags) EXPORT_SYMBOL_GPL(__pm_runtime_resume); /** - * pm_runtime_get_if_active - Conditionally bump up the device's usage counter. + * pm_runtime_get_if_active - Conditionally bump up device usage counter. * @dev: Device to handle. + * @ign_usage_count: Whether or not to look at the current usage counter value. * - * Return -EINVAL if runtime PM is disabled for the device. + * Return -EINVAL if runtime PM is disabled for @dev. * - * Otherwise, if the device's runtime PM status is RPM_ACTIVE and either - * ign_usage_count is true or the device's usage_count is non-zero, increment - * the counter and return 1. Otherwise return 0 without changing the counter. + * Otherwise, if the runtime PM status of @dev is %RPM_ACTIVE and either + * @ign_usage_count is %true or the runtime PM usage counter of @dev is not + * zero, increment the usage counter of @dev and return 1. Otherwise, return 0 + * without changing the usage counter. * - * If ign_usage_count is true, the function can be used to prevent suspending - * the device when its runtime PM status is RPM_ACTIVE. + * If @ign_usage_count is %true, this function can be used to prevent suspending + * the device when its runtime PM status is %RPM_ACTIVE. * - * If ign_usage_count is false, the function can be used to prevent suspending - * the device when both its runtime PM status is RPM_ACTIVE and its usage_count - * is non-zero. + * If @ign_usage_count is %false, this function can be used to prevent + * suspending the device when both its runtime PM status is %RPM_ACTIVE and its + * runtime PM usage counter is not zero. * - * The caller is resposible for putting the device's usage count when ther - * return value is greater than zero. + * The caller is resposible for decrementing the runtime PM usage counter of + * @dev after this function has returned a positive value for it. */ int pm_runtime_get_if_active(struct device *dev, bool ign_usage_count) { -- GitLab From 4daca379c703ff55edc065e8e5173dcfeecf0148 Mon Sep 17 00:00:00 2001 From: Srinivas Pandruvada Date: Mon, 3 Aug 2020 11:37:20 -0700 Subject: [PATCH 1273/1476] cpufreq: intel_pstate: Fix cpuinfo_max_freq when MSR_TURBO_RATIO_LIMIT is 0 The MSR_TURBO_RATIO_LIMIT can be 0. This is not an error. User can update this MSR via BIOS settings on some systems or can use msr tools to update. Also some systems boot with value = 0. This results in display of cpufreq/cpuinfo_max_freq wrong. This value will be equal to cpufreq/base_frequency, even though turbo is enabled. But platform will still function normally in HWP mode as we get max 1-core frequency from the MSR_HWP_CAPABILITIES. This MSR is already used to calculate cpu->pstate.turbo_freq, which is used for to set policy->cpuinfo.max_freq. But some other places cpu->pstate.turbo_pstate is used. For example to set policy->max. To fix this, also update cpu->pstate.turbo_pstate when updating cpu->pstate.turbo_freq. Signed-off-by: Srinivas Pandruvada Signed-off-by: Rafael J. Wysocki --- drivers/cpufreq/intel_pstate.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/cpufreq/intel_pstate.c b/drivers/cpufreq/intel_pstate.c index 7f5d819314834..fc459c9c00ff7 100644 --- a/drivers/cpufreq/intel_pstate.c +++ b/drivers/cpufreq/intel_pstate.c @@ -1647,6 +1647,7 @@ static void intel_pstate_get_cpu_pstates(struct cpudata *cpu) intel_pstate_get_hwp_max(cpu->cpu, &phy_max, ¤t_max); cpu->pstate.turbo_freq = phy_max * cpu->pstate.scaling; + cpu->pstate.turbo_pstate = phy_max; } else { cpu->pstate.turbo_freq = cpu->pstate.turbo_pstate * cpu->pstate.scaling; } -- GitLab From 0c83b277ada72b585e6a3e52b067669df15bcedb Mon Sep 17 00:00:00 2001 From: Michael Ellerman Date: Tue, 4 Aug 2020 22:44:06 +1000 Subject: [PATCH 1274/1476] powerpc: Fix circular dependency between percpu.h and mmu.h Recently random.h started including percpu.h (see commit f227e3ec3b5c ("random32: update the net random state on interrupt and activity")), which broke corenet64_smp_defconfig: In file included from /linux/arch/powerpc/include/asm/paca.h:18, from /linux/arch/powerpc/include/asm/percpu.h:13, from /linux/include/linux/random.h:14, from /linux/lib/uuid.c:14: /linux/arch/powerpc/include/asm/mmu.h:139:22: error: unknown type name 'next_tlbcam_idx' 139 | DECLARE_PER_CPU(int, next_tlbcam_idx); This is due to a circular header dependency: asm/mmu.h includes asm/percpu.h, which includes asm/paca.h, which includes asm/mmu.h Which means DECLARE_PER_CPU() isn't defined when mmu.h needs it. We can fix it by moving the include of paca.h below the include of asm-generic/percpu.h. This moves the include of paca.h out of the #ifdef __powerpc64__, but that is OK because paca.h is almost entirely inside #ifdef CONFIG_PPC64 anyway. It also moves the include of paca.h out of the #ifdef CONFIG_SMP, which could possibly break something, but seems to have no ill effects. Fixes: f227e3ec3b5c ("random32: update the net random state on interrupt and activity") Cc: stable@vger.kernel.org # v5.8 Reported-by: Stephen Rothwell Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20200804130558.292328-1-mpe@ellerman.id.au --- arch/powerpc/include/asm/percpu.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/powerpc/include/asm/percpu.h b/arch/powerpc/include/asm/percpu.h index dce863a7635cd..8e5b7d0b851c6 100644 --- a/arch/powerpc/include/asm/percpu.h +++ b/arch/powerpc/include/asm/percpu.h @@ -10,8 +10,6 @@ #ifdef CONFIG_SMP -#include - #define __my_cpu_offset local_paca->data_offset #endif /* CONFIG_SMP */ @@ -19,4 +17,6 @@ #include +#include + #endif /* _ASM_POWERPC_PERCPU_H_ */ -- GitLab From 4a957563fe0231e0b7764bbb35c135a468643d5f Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Tue, 21 Jul 2020 20:25:12 -0600 Subject: [PATCH 1275/1476] PCI: rcar-gen2: Convert to use modern host bridge probe functions The rcar-gen2 host driver still uses the old Arm PCI setup function pci_common_init_dev(). Let's update it to use the modern devm_pci_alloc_host_bridge(), pci_parse_request_of_pci_ranges() and pci_host_probe() functions. Link: https://lore.kernel.org/r/20200722022514.1283916-18-robh@kernel.org Signed-off-by: Rob Herring Signed-off-by: Lorenzo Pieralisi Acked-by: Bjorn Helgaas Cc: Marek Vasut Cc: Yoshihiro Shimoda Cc: Lorenzo Pieralisi Cc: Bjorn Helgaas Cc: linux-renesas-soc@vger.kernel.org --- drivers/pci/controller/pci-rcar-gen2.c | 172 +++++++------------------ 1 file changed, 46 insertions(+), 126 deletions(-) diff --git a/drivers/pci/controller/pci-rcar-gen2.c b/drivers/pci/controller/pci-rcar-gen2.c index 326171cb1a978..7bc6c1750ebe2 100644 --- a/drivers/pci/controller/pci-rcar-gen2.c +++ b/drivers/pci/controller/pci-rcar-gen2.c @@ -98,22 +98,17 @@ struct rcar_pci_priv { void __iomem *reg; struct resource mem_res; struct resource *cfg_res; - unsigned busnr; int irq; - unsigned long window_size; - unsigned long window_addr; - unsigned long window_pci; }; /* PCI configuration space operations */ static void __iomem *rcar_pci_cfg_base(struct pci_bus *bus, unsigned int devfn, int where) { - struct pci_sys_data *sys = bus->sysdata; - struct rcar_pci_priv *priv = sys->private_data; + struct rcar_pci_priv *priv = bus->sysdata; int slot, val; - if (sys->busnr != bus->number || PCI_FUNC(devfn)) + if (!pci_is_root_bus(bus) || PCI_FUNC(devfn)) return NULL; /* Only one EHCI/OHCI device built-in */ @@ -132,20 +127,6 @@ static void __iomem *rcar_pci_cfg_base(struct pci_bus *bus, unsigned int devfn, return priv->reg + (slot >> 1) * 0x100 + where; } -/* PCI interrupt mapping */ -static int rcar_pci_map_irq(const struct pci_dev *dev, u8 slot, u8 pin) -{ - struct pci_sys_data *sys = dev->bus->sysdata; - struct rcar_pci_priv *priv = sys->private_data; - int irq; - - irq = of_irq_parse_and_map_pci(dev, slot, pin); - if (!irq) - irq = priv->irq; - - return irq; -} - #ifdef CONFIG_PCI_DEBUG /* if debug enabled, then attach an error handler irq to the bridge */ @@ -189,19 +170,33 @@ static inline void rcar_pci_setup_errirq(struct rcar_pci_priv *priv) { } #endif /* PCI host controller setup */ -static int rcar_pci_setup(int nr, struct pci_sys_data *sys) +static void rcar_pci_setup(struct rcar_pci_priv *priv) { - struct rcar_pci_priv *priv = sys->private_data; + struct pci_host_bridge *bridge = pci_host_bridge_from_priv(priv); struct device *dev = priv->dev; void __iomem *reg = priv->reg; + struct resource_entry *entry; + unsigned long window_size; + unsigned long window_addr; + unsigned long window_pci; u32 val; - int ret; + + entry = resource_list_first_type(&bridge->dma_ranges, IORESOURCE_MEM); + if (!entry) { + window_addr = 0x40000000; + window_pci = 0x40000000; + window_size = SZ_1G; + } else { + window_addr = entry->res->start; + window_pci = entry->res->start - entry->offset; + window_size = resource_size(entry->res); + } pm_runtime_enable(dev); pm_runtime_get_sync(dev); val = ioread32(reg + RCAR_PCI_UNIT_REV_REG); - dev_info(dev, "PCI: bus%u revision %x\n", sys->busnr, val); + dev_info(dev, "PCI: revision %x\n", val); /* Disable Direct Power Down State and assert reset */ val = ioread32(reg + RCAR_USBCTR_REG) & ~RCAR_USBCTR_DIRPD; @@ -214,7 +209,7 @@ static int rcar_pci_setup(int nr, struct pci_sys_data *sys) RCAR_USBCTR_USBH_RST | RCAR_USBCTR_PLL_RST); /* Setup PCIAHB window1 size */ - switch (priv->window_size) { + switch (window_size) { case SZ_2G: val |= RCAR_USBCTR_PCIAHB_WIN1_2G; break; @@ -226,8 +221,8 @@ static int rcar_pci_setup(int nr, struct pci_sys_data *sys) break; default: pr_warn("unknown window size %ld - defaulting to 256M\n", - priv->window_size); - priv->window_size = SZ_256M; + window_size); + window_size = SZ_256M; /* fall-through */ case SZ_256M: val |= RCAR_USBCTR_PCIAHB_WIN1_256M; @@ -245,7 +240,7 @@ static int rcar_pci_setup(int nr, struct pci_sys_data *sys) iowrite32(val, reg + RCAR_PCI_ARBITER_CTR_REG); /* PCI-AHB mapping */ - iowrite32(priv->window_addr | RCAR_PCIAHB_PREFETCH16, + iowrite32(window_addr | RCAR_PCIAHB_PREFETCH16, reg + RCAR_PCIAHB_WIN1_CTR_REG); /* AHB-PCI mapping: OHCI/EHCI registers */ @@ -256,7 +251,7 @@ static int rcar_pci_setup(int nr, struct pci_sys_data *sys) iowrite32(RCAR_AHBPCI_WIN1_HOST | RCAR_AHBPCI_WIN_CTR_CFG, reg + RCAR_AHBPCI_WIN1_CTR_REG); /* Set PCI-AHB Window1 address */ - iowrite32(priv->window_pci | PCI_BASE_ADDRESS_MEM_PREFETCH, + iowrite32(window_pci | PCI_BASE_ADDRESS_MEM_PREFETCH, reg + PCI_BASE_ADDRESS_1); /* Set AHB-PCI bridge PCI communication area address */ val = priv->cfg_res->start + RCAR_AHBPCI_PCICOM_OFFSET; @@ -271,18 +266,7 @@ static int rcar_pci_setup(int nr, struct pci_sys_data *sys) iowrite32(RCAR_PCI_INT_A | RCAR_PCI_INT_B | RCAR_PCI_INT_PME, reg + RCAR_PCI_INT_ENABLE_REG); - if (priv->irq > 0) - rcar_pci_setup_errirq(priv); - - /* Add PCI resources */ - pci_add_resource(&sys->resources, &priv->mem_res); - ret = devm_request_pci_bus_resources(dev, &sys->resources); - if (ret < 0) - return ret; - - /* Setup bus number based on platform device id / of bus-range */ - sys->busnr = priv->busnr; - return 1; + rcar_pci_setup_errirq(priv); } static struct pci_ops rcar_pci_ops = { @@ -291,55 +275,21 @@ static struct pci_ops rcar_pci_ops = { .write = pci_generic_config_write, }; -static int rcar_pci_parse_map_dma_ranges(struct rcar_pci_priv *pci, - struct device_node *np) -{ - struct device *dev = pci->dev; - struct of_pci_range range; - struct of_pci_range_parser parser; - int index = 0; - - /* Failure to parse is ok as we fall back to defaults */ - if (of_pci_dma_range_parser_init(&parser, np)) - return 0; - - /* Get the dma-ranges from DT */ - for_each_of_pci_range(&parser, &range) { - /* Hardware only allows one inbound 32-bit range */ - if (index) - return -EINVAL; - - pci->window_addr = (unsigned long)range.cpu_addr; - pci->window_pci = (unsigned long)range.pci_addr; - pci->window_size = (unsigned long)range.size; - - /* Catch HW limitations */ - if (!(range.flags & IORESOURCE_PREFETCH)) { - dev_err(dev, "window must be prefetchable\n"); - return -EINVAL; - } - if (pci->window_addr) { - u32 lowaddr = 1 << (ffs(pci->window_addr) - 1); - - if (lowaddr < pci->window_size) { - dev_err(dev, "invalid window size/addr\n"); - return -EINVAL; - } - } - index++; - } - - return 0; -} - static int rcar_pci_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct resource *cfg_res, *mem_res; struct rcar_pci_priv *priv; + struct pci_host_bridge *bridge; void __iomem *reg; - struct hw_pci hw; - void *hw_private[1]; + int ret; + + bridge = devm_pci_alloc_host_bridge(dev, sizeof(*priv)); + if (!bridge) + return -ENOMEM; + + priv = pci_host_bridge_priv(bridge); + bridge->sysdata = priv; cfg_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); reg = devm_ioremap_resource(dev, cfg_res); @@ -353,10 +303,6 @@ static int rcar_pci_probe(struct platform_device *pdev) if (mem_res->start & 0xFFFF) return -EINVAL; - priv = devm_kzalloc(dev, sizeof(struct rcar_pci_priv), GFP_KERNEL); - if (!priv) - return -ENOMEM; - priv->mem_res = *mem_res; priv->cfg_res = cfg_res; @@ -369,44 +315,18 @@ static int rcar_pci_probe(struct platform_device *pdev) return priv->irq; } - /* default window addr and size if not specified in DT */ - priv->window_addr = 0x40000000; - priv->window_pci = 0x40000000; - priv->window_size = SZ_1G; - - if (dev->of_node) { - struct resource busnr; - int ret; - - ret = of_pci_parse_bus_range(dev->of_node, &busnr); - if (ret < 0) { - dev_err(dev, "failed to parse bus-range\n"); - return ret; - } - - priv->busnr = busnr.start; - if (busnr.end != busnr.start) - dev_warn(dev, "only one bus number supported\n"); - - ret = rcar_pci_parse_map_dma_ranges(priv, dev->of_node); - if (ret < 0) { - dev_err(dev, "failed to parse dma-range\n"); - return ret; - } - } else { - priv->busnr = pdev->id; - } + ret = pci_parse_request_of_pci_ranges(dev, &bridge->windows, + &bridge->dma_ranges, NULL); + if (ret) + return ret; + + bridge->ops = &rcar_pci_ops; + + pci_add_flags(PCI_REASSIGN_ALL_BUS); + + rcar_pci_setup(priv); - hw_private[0] = priv; - memset(&hw, 0, sizeof(hw)); - hw.nr_controllers = ARRAY_SIZE(hw_private); - hw.io_optional = 1; - hw.private_data = hw_private; - hw.map_irq = rcar_pci_map_irq; - hw.ops = &rcar_pci_ops; - hw.setup = rcar_pci_setup; - pci_common_init_dev(dev, &hw); - return 0; + return pci_host_probe(bridge); } static const struct of_device_id rcar_pci_of_match[] = { -- GitLab From 669cbc708122fc7a02282058a09f096200cee090 Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Tue, 21 Jul 2020 20:25:13 -0600 Subject: [PATCH 1276/1476] PCI: Move DT resource setup into devm_pci_alloc_host_bridge() Now that pci_parse_request_of_pci_ranges() callers just setup pci_host_bridge.windows and dma_ranges directly and don't need the bus range returned, we can just initialize them when allocating the pci_host_bridge struct. With this, pci_parse_request_of_pci_ranges() becomes a static function. Link: https://lore.kernel.org/r/20200722022514.1283916-19-robh@kernel.org Signed-off-by: Rob Herring Signed-off-by: Lorenzo Pieralisi Acked-by: Bjorn Helgaas Cc: Lorenzo Pieralisi Cc: Bjorn Helgaas --- .../controller/cadence/pcie-cadence-host.c | 6 --- .../pci/controller/dwc/pcie-designware-host.c | 5 --- .../controller/mobiveil/pcie-mobiveil-host.c | 8 ---- drivers/pci/controller/pci-aardvark.c | 7 ---- drivers/pci/controller/pci-ftpci100.c | 5 --- drivers/pci/controller/pci-host-common.c | 13 +++---- drivers/pci/controller/pci-loongson.c | 7 ---- drivers/pci/controller/pci-rcar-gen2.c | 6 --- drivers/pci/controller/pci-tegra.c | 6 --- drivers/pci/controller/pci-v3-semi.c | 5 --- drivers/pci/controller/pci-versatile.c | 7 +--- drivers/pci/controller/pci-xgene.c | 5 --- drivers/pci/controller/pcie-altera.c | 7 ---- drivers/pci/controller/pcie-brcmstb.c | 5 --- drivers/pci/controller/pcie-iproc-platform.c | 7 ---- drivers/pci/controller/pcie-mediatek.c | 7 ---- drivers/pci/controller/pcie-rcar-host.c | 5 --- drivers/pci/controller/pcie-rockchip-host.c | 5 --- drivers/pci/controller/pcie-xilinx-nwl.c | 7 ---- drivers/pci/controller/pcie-xilinx.c | 7 ---- drivers/pci/of.c | 37 +++++++++---------- drivers/pci/pci.h | 8 ++++ drivers/pci/probe.c | 4 ++ include/linux/pci.h | 12 ------ 24 files changed, 36 insertions(+), 155 deletions(-) diff --git a/drivers/pci/controller/cadence/pcie-cadence-host.c b/drivers/pci/controller/cadence/pcie-cadence-host.c index 39a8a7a3051e7..436eb9f3d68e2 100644 --- a/drivers/pci/controller/cadence/pcie-cadence-host.c +++ b/drivers/pci/controller/cadence/pcie-cadence-host.c @@ -171,14 +171,8 @@ static int cdns_pcie_host_init_address_translation(struct cdns_pcie_rc *rc) static int cdns_pcie_host_init(struct device *dev, struct cdns_pcie_rc *rc) { - struct pci_host_bridge *bridge = pci_host_bridge_from_priv(rc); int err; - /* Parse our PCI ranges and request their resources */ - err = pci_parse_request_of_pci_ranges(dev, &bridge->windows, NULL, NULL); - if (err) - return err; - err = cdns_pcie_host_init_root_port(rc); if (err) return err; diff --git a/drivers/pci/controller/dwc/pcie-designware-host.c b/drivers/pci/controller/dwc/pcie-designware-host.c index 9775558acdc88..f9fa7b1f429df 100644 --- a/drivers/pci/controller/dwc/pcie-designware-host.c +++ b/drivers/pci/controller/dwc/pcie-designware-host.c @@ -346,11 +346,6 @@ int dw_pcie_host_init(struct pcie_port *pp) if (!bridge) return -ENOMEM; - ret = pci_parse_request_of_pci_ranges(dev, &bridge->windows, - &bridge->dma_ranges, NULL); - if (ret) - return ret; - /* Get the I/O and memory ranges from DT */ resource_list_for_each_entry(win, &bridge->windows) { switch (resource_type(win->res)) { diff --git a/drivers/pci/controller/mobiveil/pcie-mobiveil-host.c b/drivers/pci/controller/mobiveil/pcie-mobiveil-host.c index 7250b84a7efee..2954d6ad8333e 100644 --- a/drivers/pci/controller/mobiveil/pcie-mobiveil-host.c +++ b/drivers/pci/controller/mobiveil/pcie-mobiveil-host.c @@ -577,14 +577,6 @@ int mobiveil_pcie_host_probe(struct mobiveil_pcie *pcie) if (!mobiveil_pcie_is_bridge(pcie)) return -ENODEV; - /* parse the host bridge base addresses from the device tree file */ - ret = pci_parse_request_of_pci_ranges(dev, &bridge->windows, - &bridge->dma_ranges, NULL); - if (ret) { - dev_err(dev, "Getting bridge resources failed\n"); - return ret; - } - /* * configure all inbound and outbound windows and prepare the RC for * config access diff --git a/drivers/pci/controller/pci-aardvark.c b/drivers/pci/controller/pci-aardvark.c index 36dc2412c66fe..7ee14cfb7f5a8 100644 --- a/drivers/pci/controller/pci-aardvark.c +++ b/drivers/pci/controller/pci-aardvark.c @@ -1130,13 +1130,6 @@ static int advk_pcie_probe(struct platform_device *pdev) return ret; } - ret = pci_parse_request_of_pci_ranges(dev, &bridge->windows, - &bridge->dma_ranges, NULL); - if (ret) { - dev_err(dev, "Failed to parse resources\n"); - return ret; - } - pcie->reset_gpio = devm_gpiod_get_from_of_node(dev, dev->of_node, "reset-gpios", 0, GPIOD_OUT_LOW, diff --git a/drivers/pci/controller/pci-ftpci100.c b/drivers/pci/controller/pci-ftpci100.c index 5441a5f927399..94f6ab5fff706 100644 --- a/drivers/pci/controller/pci-ftpci100.c +++ b/drivers/pci/controller/pci-ftpci100.c @@ -465,11 +465,6 @@ static int faraday_pci_probe(struct platform_device *pdev) if (IS_ERR(p->base)) return PTR_ERR(p->base); - ret = pci_parse_request_of_pci_ranges(dev, &host->windows, - &host->dma_ranges, NULL); - if (ret) - return ret; - win = resource_list_first_type(&host->windows, IORESOURCE_IO); if (win) { io = win->res; diff --git a/drivers/pci/controller/pci-host-common.c b/drivers/pci/controller/pci-host-common.c index e662910fe0328..509624175260a 100644 --- a/drivers/pci/controller/pci-host-common.c +++ b/drivers/pci/controller/pci-host-common.c @@ -25,21 +25,20 @@ static struct pci_config_window *gen_pci_init(struct device *dev, { int err; struct resource cfgres; - struct resource *bus_range = NULL; + struct resource_entry *bus; struct pci_config_window *cfg; - /* Parse our PCI ranges and request their resources */ - err = pci_parse_request_of_pci_ranges(dev, &bridge->windows, NULL, &bus_range); - if (err) - return ERR_PTR(err); - err = of_address_to_resource(dev->of_node, 0, &cfgres); if (err) { dev_err(dev, "missing \"reg\" property\n"); return ERR_PTR(err); } - cfg = pci_ecam_create(dev, &cfgres, bus_range, ops); + bus = resource_list_first_type(&bridge->windows, IORESOURCE_BUS); + if (!bus) + return ERR_PTR(-ENODEV); + + cfg = pci_ecam_create(dev, &cfgres, bus->res, ops); if (IS_ERR(cfg)) return cfg; diff --git a/drivers/pci/controller/pci-loongson.c b/drivers/pci/controller/pci-loongson.c index 0198c15ed97c5..a7a7fbe2b7a50 100644 --- a/drivers/pci/controller/pci-loongson.c +++ b/drivers/pci/controller/pci-loongson.c @@ -218,13 +218,6 @@ static int loongson_pci_probe(struct platform_device *pdev) } } - err = pci_parse_request_of_pci_ranges(dev, &bridge->windows, - &bridge->dma_ranges, NULL); - if (err) { - dev_err(dev, "failed to get bridge resources\n"); - return err; - } - bridge->sysdata = priv; bridge->ops = &loongson_pci_ops; bridge->map_irq = loongson_map_irq; diff --git a/drivers/pci/controller/pci-rcar-gen2.c b/drivers/pci/controller/pci-rcar-gen2.c index 7bc6c1750ebe2..c9530038ca9a5 100644 --- a/drivers/pci/controller/pci-rcar-gen2.c +++ b/drivers/pci/controller/pci-rcar-gen2.c @@ -282,7 +282,6 @@ static int rcar_pci_probe(struct platform_device *pdev) struct rcar_pci_priv *priv; struct pci_host_bridge *bridge; void __iomem *reg; - int ret; bridge = devm_pci_alloc_host_bridge(dev, sizeof(*priv)); if (!bridge) @@ -315,11 +314,6 @@ static int rcar_pci_probe(struct platform_device *pdev) return priv->irq; } - ret = pci_parse_request_of_pci_ranges(dev, &bridge->windows, - &bridge->dma_ranges, NULL); - if (ret) - return ret; - bridge->ops = &rcar_pci_ops; pci_add_flags(PCI_REASSIGN_ALL_BUS); diff --git a/drivers/pci/controller/pci-tegra.c b/drivers/pci/controller/pci-tegra.c index 8f37cbe7657ef..c75ee4bb914e1 100644 --- a/drivers/pci/controller/pci-tegra.c +++ b/drivers/pci/controller/pci-tegra.c @@ -2682,12 +2682,6 @@ static int tegra_pcie_probe(struct platform_device *pdev) INIT_LIST_HEAD(&pcie->ports); pcie->dev = dev; - err = pci_parse_request_of_pci_ranges(dev, &host->windows, NULL, NULL); - if (err) { - dev_err(dev, "Getting bridge resources failed\n"); - return err; - } - err = tegra_pcie_parse_dt(pcie); if (err < 0) return err; diff --git a/drivers/pci/controller/pci-v3-semi.c b/drivers/pci/controller/pci-v3-semi.c index d2619f583bfbe..1a2cbc56b34b1 100644 --- a/drivers/pci/controller/pci-v3-semi.c +++ b/drivers/pci/controller/pci-v3-semi.c @@ -764,11 +764,6 @@ static int v3_pci_probe(struct platform_device *pdev) if (IS_ERR(v3->config_base)) return PTR_ERR(v3->config_base); - ret = pci_parse_request_of_pci_ranges(dev, &host->windows, - &host->dma_ranges, NULL); - if (ret) - return ret; - /* Get and request error IRQ resource */ irq = platform_get_irq(pdev, 0); if (irq < 0) { diff --git a/drivers/pci/controller/pci-versatile.c b/drivers/pci/controller/pci-versatile.c index 66f5c61b8eb8e..fb45f8d158fec 100644 --- a/drivers/pci/controller/pci-versatile.c +++ b/drivers/pci/controller/pci-versatile.c @@ -67,7 +67,7 @@ static int versatile_pci_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; struct resource *res; struct resource_entry *entry; - int ret, i, myslot = -1, mem = 1; + int i, myslot = -1, mem = 1; u32 val; void __iomem *local_pci_cfg_base; struct pci_host_bridge *bridge; @@ -89,11 +89,6 @@ static int versatile_pci_probe(struct platform_device *pdev) if (IS_ERR(versatile_cfg_base[1])) return PTR_ERR(versatile_cfg_base[1]); - ret = pci_parse_request_of_pci_ranges(dev, &bridge->windows, - NULL, NULL); - if (ret) - return ret; - resource_list_for_each_entry(entry, &bridge->windows) { if (resource_type(entry->res) == IORESOURCE_MEM) { writel(entry->res->start >> 28, PCI_IMAP(mem)); diff --git a/drivers/pci/controller/pci-xgene.c b/drivers/pci/controller/pci-xgene.c index d3aa28e1a4829..190234b7a3a9c 100644 --- a/drivers/pci/controller/pci-xgene.c +++ b/drivers/pci/controller/pci-xgene.c @@ -614,11 +614,6 @@ static int xgene_pcie_probe(struct platform_device *pdev) if (ret) return ret; - ret = pci_parse_request_of_pci_ranges(dev, &bridge->windows, - &bridge->dma_ranges, NULL); - if (ret) - return ret; - ret = xgene_pcie_setup(port); if (ret) return ret; diff --git a/drivers/pci/controller/pcie-altera.c b/drivers/pci/controller/pcie-altera.c index 4424430dacb07..d93489e8978ec 100644 --- a/drivers/pci/controller/pcie-altera.c +++ b/drivers/pci/controller/pcie-altera.c @@ -794,13 +794,6 @@ static int altera_pcie_probe(struct platform_device *pdev) return ret; } - ret = pci_parse_request_of_pci_ranges(dev, &bridge->windows, - &bridge->dma_ranges, NULL); - if (ret) { - dev_err(dev, "Failed add resources\n"); - return ret; - } - ret = altera_pcie_init_irq_domain(pcie); if (ret) { dev_err(dev, "Failed creating IRQ Domain\n"); diff --git a/drivers/pci/controller/pcie-brcmstb.c b/drivers/pci/controller/pcie-brcmstb.c index 20e1aa23edc58..f0f283ec17a06 100644 --- a/drivers/pci/controller/pcie-brcmstb.c +++ b/drivers/pci/controller/pcie-brcmstb.c @@ -970,11 +970,6 @@ static int brcm_pcie_probe(struct platform_device *pdev) pcie->ssc = of_property_read_bool(np, "brcm,enable-ssc"); - ret = pci_parse_request_of_pci_ranges(pcie->dev, &bridge->windows, - &bridge->dma_ranges, NULL); - if (ret) - return ret; - ret = clk_prepare_enable(pcie->clk); if (ret) { dev_err(&pdev->dev, "could not enable clock\n"); diff --git a/drivers/pci/controller/pcie-iproc-platform.c b/drivers/pci/controller/pcie-iproc-platform.c index ff0a81a632a12..7c10c1cb6f65d 100644 --- a/drivers/pci/controller/pcie-iproc-platform.c +++ b/drivers/pci/controller/pcie-iproc-platform.c @@ -95,13 +95,6 @@ static int iproc_pcie_pltfm_probe(struct platform_device *pdev) if (IS_ERR(pcie->phy)) return PTR_ERR(pcie->phy); - ret = pci_parse_request_of_pci_ranges(dev, &bridge->windows, - &bridge->dma_ranges, NULL); - if (ret) { - dev_err(dev, "unable to get PCI host bridge resources\n"); - return ret; - } - /* PAXC doesn't support legacy IRQs, skip mapping */ switch (pcie->type) { case IPROC_PCIE_PAXC: diff --git a/drivers/pci/controller/pcie-mediatek.c b/drivers/pci/controller/pcie-mediatek.c index ac6dfa0872471..a745f35a83962 100644 --- a/drivers/pci/controller/pcie-mediatek.c +++ b/drivers/pci/controller/pcie-mediatek.c @@ -1027,15 +1027,8 @@ static int mtk_pcie_setup(struct mtk_pcie *pcie) struct device *dev = pcie->dev; struct device_node *node = dev->of_node, *child; struct mtk_pcie_port *port, *tmp; - struct pci_host_bridge *host = pci_host_bridge_from_priv(pcie); - struct list_head *windows = &host->windows; int err; - err = pci_parse_request_of_pci_ranges(dev, windows, - &host->dma_ranges, NULL); - if (err) - return err; - for_each_available_child_of_node(node, child) { int slot; diff --git a/drivers/pci/controller/pcie-rcar-host.c b/drivers/pci/controller/pcie-rcar-host.c index fa7b893789047..67f2a9d3bc298 100644 --- a/drivers/pci/controller/pcie-rcar-host.c +++ b/drivers/pci/controller/pcie-rcar-host.c @@ -913,11 +913,6 @@ static int rcar_pcie_probe(struct platform_device *pdev) pcie->dev = dev; platform_set_drvdata(pdev, host); - err = pci_parse_request_of_pci_ranges(dev, &bridge->windows, - &bridge->dma_ranges, NULL); - if (err) - return err; - pm_runtime_enable(pcie->dev); err = pm_runtime_get_sync(pcie->dev); if (err < 0) { diff --git a/drivers/pci/controller/pcie-rockchip-host.c b/drivers/pci/controller/pcie-rockchip-host.c index 9a30d08976d89..fed4f6cd1b7bf 100644 --- a/drivers/pci/controller/pcie-rockchip-host.c +++ b/drivers/pci/controller/pcie-rockchip-host.c @@ -989,11 +989,6 @@ static int rockchip_pcie_probe(struct platform_device *pdev) if (err < 0) goto err_deinit_port; - err = pci_parse_request_of_pci_ranges(dev, &bridge->windows, - &bridge->dma_ranges, NULL); - if (err) - goto err_remove_irq_domain; - err = rockchip_pcie_cfg_atu(rockchip); if (err) goto err_remove_irq_domain; diff --git a/drivers/pci/controller/pcie-xilinx-nwl.c b/drivers/pci/controller/pcie-xilinx-nwl.c index 566165c18fad1..7e7c23c555c7c 100644 --- a/drivers/pci/controller/pcie-xilinx-nwl.c +++ b/drivers/pci/controller/pcie-xilinx-nwl.c @@ -838,13 +838,6 @@ static int nwl_pcie_probe(struct platform_device *pdev) return err; } - err = pci_parse_request_of_pci_ranges(dev, &bridge->windows, - &bridge->dma_ranges, NULL); - if (err) { - dev_err(dev, "Getting bridge resources failed\n"); - return err; - } - err = nwl_pcie_init_irq_domain(pcie); if (err) { dev_err(dev, "Failed creating IRQ Domain\n"); diff --git a/drivers/pci/controller/pcie-xilinx.c b/drivers/pci/controller/pcie-xilinx.c index 7bf80f68efa97..f8b8ccea5cbc7 100644 --- a/drivers/pci/controller/pcie-xilinx.c +++ b/drivers/pci/controller/pcie-xilinx.c @@ -641,13 +641,6 @@ static int xilinx_pcie_probe(struct platform_device *pdev) return err; } - err = pci_parse_request_of_pci_ranges(dev, &bridge->windows, - &bridge->dma_ranges, NULL); - if (err) { - dev_err(dev, "Getting bridge resources failed\n"); - return err; - } - bridge->sysdata = port; bridge->ops = &xilinx_pcie_ops; bridge->map_irq = of_irq_parse_and_map_pci; diff --git a/drivers/pci/of.c b/drivers/pci/of.c index cfb940c8b3995..5e06aae1b4cdd 100644 --- a/drivers/pci/of.c +++ b/drivers/pci/of.c @@ -521,28 +521,26 @@ int of_irq_parse_and_map_pci(const struct pci_dev *dev, u8 slot, u8 pin) EXPORT_SYMBOL_GPL(of_irq_parse_and_map_pci); #endif /* CONFIG_OF_IRQ */ -int pci_parse_request_of_pci_ranges(struct device *dev, - struct list_head *resources, - struct list_head *ib_resources, - struct resource **bus_range) +static int pci_parse_request_of_pci_ranges(struct device *dev, + struct pci_host_bridge *bridge) { int err, res_valid = 0; resource_size_t iobase; struct resource_entry *win, *tmp; - INIT_LIST_HEAD(resources); - if (ib_resources) - INIT_LIST_HEAD(ib_resources); - err = devm_of_pci_get_host_bridge_resources(dev, 0, 0xff, resources, - ib_resources, &iobase); + INIT_LIST_HEAD(&bridge->windows); + INIT_LIST_HEAD(&bridge->dma_ranges); + + err = devm_of_pci_get_host_bridge_resources(dev, 0, 0xff, &bridge->windows, + &bridge->dma_ranges, &iobase); if (err) return err; - err = devm_request_pci_bus_resources(dev, resources); + err = devm_request_pci_bus_resources(dev, &bridge->windows); if (err) - goto out_release_res; + return err; - resource_list_for_each_entry_safe(win, tmp, resources) { + resource_list_for_each_entry_safe(win, tmp, &bridge->windows) { struct resource *res = win->res; switch (resource_type(res)) { @@ -557,10 +555,6 @@ int pci_parse_request_of_pci_ranges(struct device *dev, case IORESOURCE_MEM: res_valid |= !(res->flags & IORESOURCE_PREFETCH); break; - case IORESOURCE_BUS: - if (bus_range) - *bus_range = res; - break; } } @@ -568,12 +562,15 @@ int pci_parse_request_of_pci_ranges(struct device *dev, dev_warn(dev, "non-prefetchable memory resource required\n"); return 0; +} - out_release_res: - pci_free_resource_list(resources); - return err; +int devm_of_pci_bridge_init(struct device *dev, struct pci_host_bridge *bridge) +{ + if (!dev->of_node) + return 0; + + return pci_parse_request_of_pci_ranges(dev, bridge); } -EXPORT_SYMBOL_GPL(pci_parse_request_of_pci_ranges); #endif /* CONFIG_PCI */ diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index 6d3f758671064..56d67071e1161 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h @@ -627,6 +627,8 @@ void pci_release_of_node(struct pci_dev *dev); void pci_set_bus_of_node(struct pci_bus *bus); void pci_release_bus_of_node(struct pci_bus *bus); +int devm_of_pci_bridge_init(struct device *dev, struct pci_host_bridge *bridge); + #else static inline int of_pci_parse_bus_range(struct device_node *node, struct resource *res) @@ -650,6 +652,12 @@ static inline void pci_set_of_node(struct pci_dev *dev) { } static inline void pci_release_of_node(struct pci_dev *dev) { } static inline void pci_set_bus_of_node(struct pci_bus *bus) { } static inline void pci_release_bus_of_node(struct pci_bus *bus) { } + +static inline int devm_of_pci_bridge_init(struct device *dev, struct pci_host_bridge *bridge) +{ + return 0; +} + #endif /* CONFIG_OF */ #ifdef CONFIG_PCIEAER diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index f850782efc35f..998f615cdb6d7 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -635,6 +635,10 @@ struct pci_host_bridge *devm_pci_alloc_host_bridge(struct device *dev, if (ret) return NULL; + ret = devm_of_pci_bridge_init(dev, bridge); + if (ret) + return NULL; + return bridge; } EXPORT_SYMBOL(devm_pci_alloc_host_bridge); diff --git a/include/linux/pci.h b/include/linux/pci.h index c79d83304e529..2830799208fd0 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -2303,10 +2303,6 @@ int pci_vpd_find_info_keyword(const u8 *buf, unsigned int off, struct device_node; struct irq_domain; struct irq_domain *pci_host_bridge_of_msi_domain(struct pci_bus *bus); -int pci_parse_request_of_pci_ranges(struct device *dev, - struct list_head *resources, - struct list_head *ib_resources, - struct resource **bus_range); /* Arch may override this (weak) */ struct device_node *pcibios_get_phb_of_node(struct pci_bus *bus); @@ -2314,14 +2310,6 @@ struct device_node *pcibios_get_phb_of_node(struct pci_bus *bus); #else /* CONFIG_OF */ static inline struct irq_domain * pci_host_bridge_of_msi_domain(struct pci_bus *bus) { return NULL; } -static inline int -pci_parse_request_of_pci_ranges(struct device *dev, - struct list_head *resources, - struct list_head *ib_resources, - struct resource **bus_range) -{ - return -EINVAL; -} #endif /* CONFIG_OF */ static inline struct device_node * -- GitLab From b64aa11eb2ddb0600361178286d7b7ed5a069fd8 Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Tue, 21 Jul 2020 20:25:14 -0600 Subject: [PATCH 1277/1476] PCI: Set bridge map_irq and swizzle_irq to default functions The majority of DT based host drivers use the default .map_irq() and .swizzle_irq() functions, so let's initialize the function pointers to the default and drop setting them in the host drivers. Drivers like iProc which don't support legacy interrupts need to set .map_irq() back to NULL. Link: https://lore.kernel.org/r/20200722022514.1283916-20-robh@kernel.org Signed-off-by: Rob Herring Signed-off-by: Lorenzo Pieralisi Acked-by: Bjorn Helgaas Cc: Lorenzo Pieralisi Cc: Bjorn Helgaas --- drivers/pci/controller/cadence/pcie-cadence-host.c | 2 -- drivers/pci/controller/dwc/pcie-designware-host.c | 2 -- drivers/pci/controller/mobiveil/pcie-mobiveil-host.c | 2 -- drivers/pci/controller/pci-aardvark.c | 2 -- drivers/pci/controller/pci-ftpci100.c | 2 -- drivers/pci/controller/pci-host-common.c | 2 -- drivers/pci/controller/pci-mvebu.c | 2 -- drivers/pci/controller/pci-tegra.c | 1 - drivers/pci/controller/pci-v3-semi.c | 2 -- drivers/pci/controller/pci-versatile.c | 2 -- drivers/pci/controller/pci-xgene.c | 2 -- drivers/pci/controller/pcie-altera.c | 2 -- drivers/pci/controller/pcie-brcmstb.c | 2 -- drivers/pci/controller/pcie-iproc-platform.c | 3 ++- drivers/pci/controller/pcie-iproc.c | 1 - drivers/pci/controller/pcie-mediatek.c | 2 -- drivers/pci/controller/pcie-rcar-host.c | 2 -- drivers/pci/controller/pcie-rockchip-host.c | 2 -- drivers/pci/controller/pcie-xilinx-nwl.c | 2 -- drivers/pci/controller/pcie-xilinx.c | 2 -- drivers/pci/of.c | 3 +++ 21 files changed, 5 insertions(+), 37 deletions(-) diff --git a/drivers/pci/controller/cadence/pcie-cadence-host.c b/drivers/pci/controller/cadence/pcie-cadence-host.c index 436eb9f3d68e2..e60f72780ab09 100644 --- a/drivers/pci/controller/cadence/pcie-cadence-host.c +++ b/drivers/pci/controller/cadence/pcie-cadence-host.c @@ -231,8 +231,6 @@ int cdns_pcie_host_setup(struct cdns_pcie_rc *rc) goto err_init; bridge->ops = &cdns_pcie_host_ops; - bridge->map_irq = of_irq_parse_and_map_pci; - bridge->swizzle_irq = pci_common_swizzle; ret = pci_host_probe(bridge); if (ret < 0) diff --git a/drivers/pci/controller/dwc/pcie-designware-host.c b/drivers/pci/controller/dwc/pcie-designware-host.c index f9fa7b1f429df..7461357d98f6d 100644 --- a/drivers/pci/controller/dwc/pcie-designware-host.c +++ b/drivers/pci/controller/dwc/pcie-designware-host.c @@ -470,8 +470,6 @@ int dw_pcie_host_init(struct pcie_port *pp) bridge->sysdata = pp; bridge->ops = &dw_pcie_ops; - bridge->map_irq = of_irq_parse_and_map_pci; - bridge->swizzle_irq = pci_common_swizzle; ret = pci_scan_root_bus_bridge(bridge); if (ret) diff --git a/drivers/pci/controller/mobiveil/pcie-mobiveil-host.c b/drivers/pci/controller/mobiveil/pcie-mobiveil-host.c index 2954d6ad8333e..33ab36d73906e 100644 --- a/drivers/pci/controller/mobiveil/pcie-mobiveil-host.c +++ b/drivers/pci/controller/mobiveil/pcie-mobiveil-host.c @@ -596,8 +596,6 @@ int mobiveil_pcie_host_probe(struct mobiveil_pcie *pcie) /* Initialize bridge */ bridge->sysdata = pcie; bridge->ops = &mobiveil_pcie_ops; - bridge->map_irq = of_irq_parse_and_map_pci; - bridge->swizzle_irq = pci_common_swizzle; ret = mobiveil_bringup_link(pcie); if (ret) { diff --git a/drivers/pci/controller/pci-aardvark.c b/drivers/pci/controller/pci-aardvark.c index 7ee14cfb7f5a8..795e7632b0680 100644 --- a/drivers/pci/controller/pci-aardvark.c +++ b/drivers/pci/controller/pci-aardvark.c @@ -1175,8 +1175,6 @@ static int advk_pcie_probe(struct platform_device *pdev) bridge->sysdata = pcie; bridge->ops = &advk_pcie_ops; - bridge->map_irq = of_irq_parse_and_map_pci; - bridge->swizzle_irq = pci_common_swizzle; ret = pci_host_probe(bridge); if (ret < 0) { diff --git a/drivers/pci/controller/pci-ftpci100.c b/drivers/pci/controller/pci-ftpci100.c index 94f6ab5fff706..da3cd216da007 100644 --- a/drivers/pci/controller/pci-ftpci100.c +++ b/drivers/pci/controller/pci-ftpci100.c @@ -437,8 +437,6 @@ static int faraday_pci_probe(struct platform_device *pdev) return -ENOMEM; host->ops = &faraday_pci_ops; - host->map_irq = of_irq_parse_and_map_pci; - host->swizzle_irq = pci_common_swizzle; p = pci_host_bridge_priv(host); host->sysdata = p; p->dev = dev; diff --git a/drivers/pci/controller/pci-host-common.c b/drivers/pci/controller/pci-host-common.c index 509624175260a..6ce34a1deecb2 100644 --- a/drivers/pci/controller/pci-host-common.c +++ b/drivers/pci/controller/pci-host-common.c @@ -77,8 +77,6 @@ int pci_host_common_probe(struct platform_device *pdev) bridge->sysdata = cfg; bridge->ops = (struct pci_ops *)&ops->pci_ops; - bridge->map_irq = of_irq_parse_and_map_pci; - bridge->swizzle_irq = pci_common_swizzle; platform_set_drvdata(pdev, bridge); diff --git a/drivers/pci/controller/pci-mvebu.c b/drivers/pci/controller/pci-mvebu.c index db93823a2dcb1..12d30fb6ae6ee 100644 --- a/drivers/pci/controller/pci-mvebu.c +++ b/drivers/pci/controller/pci-mvebu.c @@ -1118,8 +1118,6 @@ static int mvebu_pcie_probe(struct platform_device *pdev) bridge->sysdata = pcie; bridge->ops = &mvebu_pcie_ops; - bridge->map_irq = of_irq_parse_and_map_pci; - bridge->swizzle_irq = pci_common_swizzle; bridge->align_resource = mvebu_pcie_align_resource; bridge->msi = pcie->msi; diff --git a/drivers/pci/controller/pci-tegra.c b/drivers/pci/controller/pci-tegra.c index c75ee4bb914e1..2789d5500f2c9 100644 --- a/drivers/pci/controller/pci-tegra.c +++ b/drivers/pci/controller/pci-tegra.c @@ -2707,7 +2707,6 @@ static int tegra_pcie_probe(struct platform_device *pdev) host->ops = &tegra_pcie_ops; host->map_irq = tegra_pcie_map_irq; - host->swizzle_irq = pci_common_swizzle; err = pci_host_probe(host); if (err < 0) { diff --git a/drivers/pci/controller/pci-v3-semi.c b/drivers/pci/controller/pci-v3-semi.c index 1a2cbc56b34b1..a38a416bcf3b0 100644 --- a/drivers/pci/controller/pci-v3-semi.c +++ b/drivers/pci/controller/pci-v3-semi.c @@ -722,8 +722,6 @@ static int v3_pci_probe(struct platform_device *pdev) return -ENOMEM; host->ops = &v3_pci_ops; - host->map_irq = of_irq_parse_and_map_pci; - host->swizzle_irq = pci_common_swizzle; v3 = pci_host_bridge_priv(host); host->sysdata = v3; v3->dev = dev; diff --git a/drivers/pci/controller/pci-versatile.c b/drivers/pci/controller/pci-versatile.c index fb45f8d158fec..653d5d0ecf817 100644 --- a/drivers/pci/controller/pci-versatile.c +++ b/drivers/pci/controller/pci-versatile.c @@ -149,8 +149,6 @@ static int versatile_pci_probe(struct platform_device *pdev) pci_add_flags(PCI_REASSIGN_ALL_BUS); bridge->ops = &pci_versatile_ops; - bridge->map_irq = of_irq_parse_and_map_pci; - bridge->swizzle_irq = pci_common_swizzle; return pci_host_probe(bridge); } diff --git a/drivers/pci/controller/pci-xgene.c b/drivers/pci/controller/pci-xgene.c index 190234b7a3a9c..8e0db84f089da 100644 --- a/drivers/pci/controller/pci-xgene.c +++ b/drivers/pci/controller/pci-xgene.c @@ -620,8 +620,6 @@ static int xgene_pcie_probe(struct platform_device *pdev) bridge->sysdata = port; bridge->ops = &xgene_pcie_ops; - bridge->map_irq = of_irq_parse_and_map_pci; - bridge->swizzle_irq = pci_common_swizzle; return pci_host_probe(bridge); } diff --git a/drivers/pci/controller/pcie-altera.c b/drivers/pci/controller/pcie-altera.c index d93489e8978ec..70c04829b8199 100644 --- a/drivers/pci/controller/pcie-altera.c +++ b/drivers/pci/controller/pcie-altera.c @@ -809,8 +809,6 @@ static int altera_pcie_probe(struct platform_device *pdev) bridge->sysdata = pcie; bridge->busnr = pcie->root_bus_nr; bridge->ops = &altera_pcie_ops; - bridge->map_irq = of_irq_parse_and_map_pci; - bridge->swizzle_irq = pci_common_swizzle; return pci_host_probe(bridge); } diff --git a/drivers/pci/controller/pcie-brcmstb.c b/drivers/pci/controller/pcie-brcmstb.c index f0f283ec17a06..85fa7d54f11fb 100644 --- a/drivers/pci/controller/pcie-brcmstb.c +++ b/drivers/pci/controller/pcie-brcmstb.c @@ -991,8 +991,6 @@ static int brcm_pcie_probe(struct platform_device *pdev) bridge->ops = &brcm_pcie_ops; bridge->sysdata = pcie; - bridge->map_irq = of_irq_parse_and_map_pci; - bridge->swizzle_irq = pci_common_swizzle; platform_set_drvdata(pdev, pcie); diff --git a/drivers/pci/controller/pcie-iproc-platform.c b/drivers/pci/controller/pcie-iproc-platform.c index 7c10c1cb6f65d..a956b0c18bd1b 100644 --- a/drivers/pci/controller/pcie-iproc-platform.c +++ b/drivers/pci/controller/pcie-iproc-platform.c @@ -99,9 +99,10 @@ static int iproc_pcie_pltfm_probe(struct platform_device *pdev) switch (pcie->type) { case IPROC_PCIE_PAXC: case IPROC_PCIE_PAXC_V2: + pcie->map_irq = 0; break; default: - pcie->map_irq = of_irq_parse_and_map_pci; + break; } ret = iproc_pcie_setup(pcie, &bridge->windows); diff --git a/drivers/pci/controller/pcie-iproc.c b/drivers/pci/controller/pcie-iproc.c index e98dafd0fff4a..905e938082432 100644 --- a/drivers/pci/controller/pcie-iproc.c +++ b/drivers/pci/controller/pcie-iproc.c @@ -1526,7 +1526,6 @@ int iproc_pcie_setup(struct iproc_pcie *pcie, struct list_head *res) host->ops = &iproc_pcie_ops; host->sysdata = pcie; host->map_irq = pcie->map_irq; - host->swizzle_irq = pci_common_swizzle; ret = pci_host_probe(host); if (ret < 0) { diff --git a/drivers/pci/controller/pcie-mediatek.c b/drivers/pci/controller/pcie-mediatek.c index a745f35a83962..cf4c18f0c25ab 100644 --- a/drivers/pci/controller/pcie-mediatek.c +++ b/drivers/pci/controller/pcie-mediatek.c @@ -1083,8 +1083,6 @@ static int mtk_pcie_probe(struct platform_device *pdev) return err; host->ops = pcie->soc->ops; - host->map_irq = of_irq_parse_and_map_pci; - host->swizzle_irq = pci_common_swizzle; host->sysdata = pcie; err = pci_host_probe(host); diff --git a/drivers/pci/controller/pcie-rcar-host.c b/drivers/pci/controller/pcie-rcar-host.c index 67f2a9d3bc298..f6f41db31d47b 100644 --- a/drivers/pci/controller/pcie-rcar-host.c +++ b/drivers/pci/controller/pcie-rcar-host.c @@ -294,8 +294,6 @@ static int rcar_pcie_enable(struct rcar_pcie_host *host) bridge->sysdata = host; bridge->ops = &rcar_pcie_ops; - bridge->map_irq = of_irq_parse_and_map_pci; - bridge->swizzle_irq = pci_common_swizzle; if (IS_ENABLED(CONFIG_PCI_MSI)) bridge->msi = &host->msi.chip; diff --git a/drivers/pci/controller/pcie-rockchip-host.c b/drivers/pci/controller/pcie-rockchip-host.c index fed4f6cd1b7bf..153bc95ab29f8 100644 --- a/drivers/pci/controller/pcie-rockchip-host.c +++ b/drivers/pci/controller/pcie-rockchip-host.c @@ -1001,8 +1001,6 @@ static int rockchip_pcie_probe(struct platform_device *pdev) bridge->sysdata = rockchip; bridge->ops = &rockchip_pcie_ops; - bridge->map_irq = of_irq_parse_and_map_pci; - bridge->swizzle_irq = pci_common_swizzle; err = pci_host_probe(bridge); if (err < 0) diff --git a/drivers/pci/controller/pcie-xilinx-nwl.c b/drivers/pci/controller/pcie-xilinx-nwl.c index 7e7c23c555c7c..97305bfe81b59 100644 --- a/drivers/pci/controller/pcie-xilinx-nwl.c +++ b/drivers/pci/controller/pcie-xilinx-nwl.c @@ -846,8 +846,6 @@ static int nwl_pcie_probe(struct platform_device *pdev) bridge->sysdata = pcie; bridge->ops = &nwl_pcie_ops; - bridge->map_irq = of_irq_parse_and_map_pci; - bridge->swizzle_irq = pci_common_swizzle; if (IS_ENABLED(CONFIG_PCI_MSI)) { err = nwl_pcie_enable_msi(pcie); diff --git a/drivers/pci/controller/pcie-xilinx.c b/drivers/pci/controller/pcie-xilinx.c index f8b8ccea5cbc7..8523be61bba58 100644 --- a/drivers/pci/controller/pcie-xilinx.c +++ b/drivers/pci/controller/pcie-xilinx.c @@ -643,8 +643,6 @@ static int xilinx_pcie_probe(struct platform_device *pdev) bridge->sysdata = port; bridge->ops = &xilinx_pcie_ops; - bridge->map_irq = of_irq_parse_and_map_pci; - bridge->swizzle_irq = pci_common_swizzle; #ifdef CONFIG_PCI_MSI xilinx_pcie_msi_chip.dev = dev; diff --git a/drivers/pci/of.c b/drivers/pci/of.c index 5e06aae1b4cdd..8f478d9231962 100644 --- a/drivers/pci/of.c +++ b/drivers/pci/of.c @@ -569,6 +569,9 @@ int devm_of_pci_bridge_init(struct device *dev, struct pci_host_bridge *bridge) if (!dev->of_node) return 0; + bridge->swizzle_irq = pci_common_swizzle; + bridge->map_irq = of_irq_parse_and_map_pci; + return pci_parse_request_of_pci_ranges(dev, bridge); } -- GitLab From a84c4308333a2cbd54593649a8b144df95c68227 Mon Sep 17 00:00:00 2001 From: Damien Le Moal Date: Thu, 16 Jul 2020 13:38:12 +0900 Subject: [PATCH 1278/1476] dm verity: Fix compilation warning For the case !CONFIG_DM_VERITY_VERIFY_ROOTHASH_SIG, declare the functions verity_verify_root_hash(), verity_verify_is_sig_opt_arg(), verity_verify_sig_parse_opt_args() and verity_verify_sig_opts_cleanup() as inline to avoid a "no previous prototype for xxx" compilation warning when compiling with W=1. Signed-off-by: Damien Le Moal Signed-off-by: Mike Snitzer --- drivers/md/dm-verity-verify-sig.h | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/drivers/md/dm-verity-verify-sig.h b/drivers/md/dm-verity-verify-sig.h index 19b1547aa741e..3987c7141f79a 100644 --- a/drivers/md/dm-verity-verify-sig.h +++ b/drivers/md/dm-verity-verify-sig.h @@ -34,25 +34,25 @@ void verity_verify_sig_opts_cleanup(struct dm_verity_sig_opts *sig_opts); #define DM_VERITY_ROOT_HASH_VERIFICATION_OPTS 0 -int verity_verify_root_hash(const void *data, size_t data_len, - const void *sig_data, size_t sig_len) +static inline int verity_verify_root_hash(const void *data, size_t data_len, + const void *sig_data, size_t sig_len) { return 0; } -bool verity_verify_is_sig_opt_arg(const char *arg_name) +static inline bool verity_verify_is_sig_opt_arg(const char *arg_name) { return false; } -int verity_verify_sig_parse_opt_args(struct dm_arg_set *as, struct dm_verity *v, - struct dm_verity_sig_opts *sig_opts, - unsigned int *argc, const char *arg_name) +static inline int verity_verify_sig_parse_opt_args(struct dm_arg_set *as, + struct dm_verity *v, struct dm_verity_sig_opts *sig_opts, + unsigned int *argc, const char *arg_name) { return -EINVAL; } -void verity_verify_sig_opts_cleanup(struct dm_verity_sig_opts *sig_opts) +static inline void verity_verify_sig_opts_cleanup(struct dm_verity_sig_opts *sig_opts) { } -- GitLab From 04dc5330e5deebde2c88f8422c446e8212090ba3 Mon Sep 17 00:00:00 2001 From: Damien Le Moal Date: Thu, 16 Jul 2020 13:38:13 +0900 Subject: [PATCH 1279/1476] dm raid: Remove empty if statement In super_init_validation(), remove a body-less if statement testing only variables to avoid a compilation warning when compiling with W=1. Signed-off-by: Damien Le Moal Signed-off-by: Mike Snitzer --- drivers/md/dm-raid.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/md/dm-raid.c b/drivers/md/dm-raid.c index 10e8b2fe787b5..08023c50aaa0d 100644 --- a/drivers/md/dm-raid.c +++ b/drivers/md/dm-raid.c @@ -2345,8 +2345,6 @@ static int super_init_validation(struct raid_set *rs, struct md_rdev *rdev) if (new_devs == rs->raid_disks || !rebuilds) { /* Replace a broken device */ - if (new_devs == 1 && !rs->delta_disks) - ; if (new_devs == rs->raid_disks) { DMINFO("Superblocks created for new raid set"); set_bit(MD_ARRAY_FIRST_USE, &mddev->flags); -- GitLab From 1aeb6e7cd118b90f1a227c79b26100af6a15960e Mon Sep 17 00:00:00 2001 From: Damien Le Moal Date: Thu, 16 Jul 2020 13:38:14 +0900 Subject: [PATCH 1280/1476] dm ioctl: Fix compilation warning MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In retrieve_status(), when copying the target type name in the target_type string field of struct dm_target_spec, copy at most DM_MAX_TYPE_NAME - 1 character to avoid the compilation warning: warning: ‘__builtin_strncpy’ specified bound 16 equals destination size [-Wstringop-truncation] when compiling with W-1. Signed-off-by: Damien Le Moal Signed-off-by: Mike Snitzer --- drivers/md/dm-ioctl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/md/dm-ioctl.c b/drivers/md/dm-ioctl.c index 489935d5f22d1..5792878dbff67 100644 --- a/drivers/md/dm-ioctl.c +++ b/drivers/md/dm-ioctl.c @@ -1168,7 +1168,7 @@ static void retrieve_status(struct dm_table *table, spec->sector_start = ti->begin; spec->length = ti->len; strncpy(spec->target_type, ti->type->name, - sizeof(spec->target_type)); + sizeof(spec->target_type) - 1); outptr += sizeof(struct dm_target_spec); remaining = len - (outptr - outbuf); -- GitLab From 90e6bf0659f11f3d8ffa7ae1d7a4fa2af015747a Mon Sep 17 00:00:00 2001 From: Damien Le Moal Date: Thu, 16 Jul 2020 13:38:15 +0900 Subject: [PATCH 1281/1476] dm init: Set file local variable static Declare dm_allowed_targets as static to avoid the warning: drivers/md/dm-init.c:39:12: warning: symbol 'dm_allowed_targets' was not declared. Should it be static? when compiling with C=1. Signed-off-by: Damien Le Moal Signed-off-by: Mike Snitzer --- drivers/md/dm-init.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/md/dm-init.c b/drivers/md/dm-init.c index b869316d37229..b0c45c6ebe0bf 100644 --- a/drivers/md/dm-init.c +++ b/drivers/md/dm-init.c @@ -36,7 +36,7 @@ struct dm_device { struct list_head list; }; -const char * const dm_allowed_targets[] __initconst = { +static const char * const dm_allowed_targets[] __initconst = { "crypt", "delay", "linear", -- GitLab From 4cb6f22612511ff2aba4c33fb0f281cae7c23772 Mon Sep 17 00:00:00 2001 From: John Dorminy Date: Fri, 31 Jul 2020 18:46:45 -0400 Subject: [PATCH 1282/1476] dm ebs: Fix incorrect checking for REQ_OP_FLUSH REQ_OP_FLUSH was being treated as a flag, but the operation part of bio->bi_opf must be treated as a whole. Change to accessing the operation part via bio_op(bio) and checking for equality. Signed-off-by: John Dorminy Acked-by: Heinz Mauelshagen Fixes: d3c7b35c20d60 ("dm: add emulated block size target") Cc: stable@vger.kernel.org Signed-off-by: Mike Snitzer --- drivers/md/dm-ebs-target.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/md/dm-ebs-target.c b/drivers/md/dm-ebs-target.c index 44451276f1281..cb85610527c2c 100644 --- a/drivers/md/dm-ebs-target.c +++ b/drivers/md/dm-ebs-target.c @@ -363,7 +363,7 @@ static int ebs_map(struct dm_target *ti, struct bio *bio) bio_set_dev(bio, ec->dev->bdev); bio->bi_iter.bi_sector = ec->start + dm_target_offset(ti, bio->bi_iter.bi_sector); - if (unlikely(bio->bi_opf & REQ_OP_FLUSH)) + if (unlikely(bio_op(bio) == REQ_OP_FLUSH)) return DM_MAPIO_REMAPPED; /* * Only queue for bufio processing in case of partial or overlapping buffers -- GitLab From a9cb9f4148ef6bb8fabbdaa85c42b2171fbd5a0d Mon Sep 17 00:00:00 2001 From: Johannes Thumshirn Date: Tue, 4 Aug 2020 18:25:01 +0900 Subject: [PATCH 1283/1476] dm: don't call report zones for more than the user requested Don't call report zones for more zones than the user actually requested, otherwise this can lead to out-of-bounds accesses in the callback functions. Such a situation can happen if the target's ->report_zones() callback function returns 0 because we've reached the end of the target and then restart the report zones on the second target. We're again calling into ->report_zones() and ultimately into the user supplied callback function but when we're not subtracting the number of zones already processed this may lead to out-of-bounds accesses in the user callbacks. Signed-off-by: Johannes Thumshirn Reviewed-by: Damien Le Moal Fixes: d41003513e61 ("block: rework zone reporting") Cc: stable@vger.kernel.org # v5.5+ Signed-off-by: Mike Snitzer --- drivers/md/dm.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/md/dm.c b/drivers/md/dm.c index 52449afd58ebb..937a4194442f8 100644 --- a/drivers/md/dm.c +++ b/drivers/md/dm.c @@ -503,7 +503,8 @@ static int dm_blk_report_zones(struct gendisk *disk, sector_t sector, } args.tgt = tgt; - ret = tgt->type->report_zones(tgt, &args, nr_zones); + ret = tgt->type->report_zones(tgt, &args, + nr_zones - args.zone_idx); if (ret < 0) goto out; } while (args.zone_idx < nr_zones && -- GitLab From 477d08478170469d10b533624342d13701e24b34 Mon Sep 17 00:00:00 2001 From: Masami Hiramatsu Date: Tue, 4 Aug 2020 11:52:13 +0900 Subject: [PATCH 1284/1476] bootconfig: Fix to find the initargs correctly Since the parse_args() stops parsing at '--', bootconfig_params() will never get the '--' as param and initargs_found never be true. In the result, if we pass some init arguments via the bootconfig, those are always appended to the kernel command line with '--' even if the kernel command line already has '--'. To fix this correctly, check the return value of parse_args() and set initargs_found true if the return value is not an error but a valid address. Link: https://lkml.kernel.org/r/159650953285.270383.14822353843556363851.stgit@devnote2 Fixes: f61872bb58a1 ("bootconfig: Use parse_args() to find bootconfig and '--'") Cc: stable@vger.kernel.org Reported-by: Arvind Sankar Suggested-by: Arvind Sankar Signed-off-by: Masami Hiramatsu Signed-off-by: Steven Rostedt (VMware) --- init/main.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/init/main.c b/init/main.c index 0ead83e86b5aa..883ded3638e59 100644 --- a/init/main.c +++ b/init/main.c @@ -387,8 +387,6 @@ static int __init bootconfig_params(char *param, char *val, { if (strcmp(param, "bootconfig") == 0) { bootconfig_found = true; - } else if (strcmp(param, "--") == 0) { - initargs_found = true; } return 0; } @@ -399,19 +397,23 @@ static void __init setup_boot_config(const char *cmdline) const char *msg; int pos; u32 size, csum; - char *data, *copy; + char *data, *copy, *err; int ret; /* Cut out the bootconfig data even if we have no bootconfig option */ data = get_boot_config_from_initrd(&size, &csum); strlcpy(tmp_cmdline, boot_command_line, COMMAND_LINE_SIZE); - parse_args("bootconfig", tmp_cmdline, NULL, 0, 0, 0, NULL, - bootconfig_params); + err = parse_args("bootconfig", tmp_cmdline, NULL, 0, 0, 0, NULL, + bootconfig_params); - if (!bootconfig_found) + if (IS_ERR(err) || !bootconfig_found) return; + /* parse_args() stops at '--' and returns an address */ + if (err) + initargs_found = true; + if (!data) { pr_err("'bootconfig' found on command line, but no bootconfig found\n"); return; -- GitLab From 635093e306a3e4543937b079dc797c2ee0bafa09 Mon Sep 17 00:00:00 2001 From: Atish Patra Date: Wed, 29 Jul 2020 17:25:35 -0700 Subject: [PATCH 1285/1476] RISC-V: Fix build warning for smpboot.c The following warnings are reported by kbuild with W=1. >> arch/riscv/kernel/smpboot.c:109:5: warning: no previous prototype for 'start_secondary_cpu' [-Wmissing-prototypes] 109 | int start_secondary_cpu(int cpu, struct task_struct *tidle) | ^~~~~~~~~~~~~~~~~~~ arch/riscv/kernel/smpboot.c:146:34: warning: no previous prototype for 'smp_callin' [-Wmissing-prototypes] 146 | asmlinkage __visible void __init smp_callin(void) | ^~~~~~~~~~ Fix the warnings by marking the local functions static and adding the prototype for the global function. Signed-off-by: Atish Patra Signed-off-by: Palmer Dabbelt --- arch/riscv/include/asm/smp.h | 3 +++ arch/riscv/kernel/smpboot.c | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/arch/riscv/include/asm/smp.h b/arch/riscv/include/asm/smp.h index 40bb1c15a731f..6dfd2a1446d56 100644 --- a/arch/riscv/include/asm/smp.h +++ b/arch/riscv/include/asm/smp.h @@ -40,6 +40,9 @@ void arch_send_call_function_single_ipi(int cpu); int riscv_hartid_to_cpuid(int hartid); void riscv_cpuid_to_hartid_mask(const struct cpumask *in, struct cpumask *out); +/* Secondary hart entry */ +asmlinkage void smp_callin(void); + /* * Obtains the hart ID of the currently executing task. This relies on * THREAD_INFO_IN_TASK, but we define that unconditionally. diff --git a/arch/riscv/kernel/smpboot.c b/arch/riscv/kernel/smpboot.c index 47404d4ad7219..356825a57551c 100644 --- a/arch/riscv/kernel/smpboot.c +++ b/arch/riscv/kernel/smpboot.c @@ -106,7 +106,7 @@ void __init setup_smp(void) } } -int start_secondary_cpu(int cpu, struct task_struct *tidle) +static int start_secondary_cpu(int cpu, struct task_struct *tidle) { if (cpu_ops[cpu]->cpu_start) return cpu_ops[cpu]->cpu_start(cpu, tidle); -- GitLab From 40284a072c42f6177184fb1f62ba94c69e0c0277 Mon Sep 17 00:00:00 2001 From: Tobias Klauser Date: Wed, 29 Jul 2020 09:51:29 +0200 Subject: [PATCH 1286/1476] riscv: disable stack-protector for vDSO Currently, building the vDSO with clang leads assembler errors like the following: /tmp/vgettimeofday-1ae0d2.s: Assembler messages: /tmp/vgettimeofday-1ae0d2.s:28: Error: bad expression /tmp/vgettimeofday-1ae0d2.s:28: Error: illegal operands `auipc a2,%got_pcrel_hi(__stack_chk_guard)' Disable the stack-protector for vDSO to fix these. Link: https://github.com/ClangBuiltLinux/linux/issues/1112 Signed-off-by: Tobias Klauser Signed-off-by: Palmer Dabbelt --- arch/riscv/kernel/vdso/Makefile | 2 ++ 1 file changed, 2 insertions(+) diff --git a/arch/riscv/kernel/vdso/Makefile b/arch/riscv/kernel/vdso/Makefile index c8c9e40023c68..478e7338ddc10 100644 --- a/arch/riscv/kernel/vdso/Makefile +++ b/arch/riscv/kernel/vdso/Makefile @@ -16,6 +16,8 @@ vdso-syms += flush_icache # Files to link into the vdso obj-vdso = $(patsubst %, %.o, $(vdso-syms)) note.o +ccflags-y := -fno-stack-protector + ifneq ($(c-gettimeofday-y),) CFLAGS_vgettimeofday.o += -fPIC -include $(c-gettimeofday-y) endif -- GitLab From a7aaa2f26bfd932a654706b19859e7adf802bee2 Mon Sep 17 00:00:00 2001 From: Sandipan Das Date: Tue, 4 Aug 2020 23:01:37 +0530 Subject: [PATCH 1287/1476] selftests/powerpc: Fix pkey syscall redefinitions On distros using older glibc versions, the pkey tests encounter build failures due to redefinition of the pkey syscall numbers. For compatibility, commit 743f3544fffb added a wrapper for the gettid() syscall and included syscall.h if the version of glibc used is older than 2.30. This leads to different definitions of SYS_pkey_* as the ones in the pkey test header set numeric constants where as the ones from syscall.h reuse __NR_pkey_*. The compiler complains about redefinitions since they are different. This replaces SYS_pkey_* definitions with __NR_pkey_* such that the definitions in both syscall.h and pkeys.h are alike. This way, if syscall.h has to be included for compatibility reasons, builds will still succeed. Fixes: 743f3544fffb ("selftests/powerpc: Add wrapper for gettid") Reported-by: Sachin Sant Suggested-by: David Laight Suggested-by: Michael Ellerman Signed-off-by: Sandipan Das Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/a4956d838bf59b0a71a2553c5ca81131ea8b49b9.1596561758.git.sandipan@linux.ibm.com --- tools/testing/selftests/powerpc/include/pkeys.h | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tools/testing/selftests/powerpc/include/pkeys.h b/tools/testing/selftests/powerpc/include/pkeys.h index 6ba95039a0343..3312cb1b058d7 100644 --- a/tools/testing/selftests/powerpc/include/pkeys.h +++ b/tools/testing/selftests/powerpc/include/pkeys.h @@ -31,9 +31,9 @@ #define SI_PKEY_OFFSET 0x20 -#define SYS_pkey_mprotect 386 -#define SYS_pkey_alloc 384 -#define SYS_pkey_free 385 +#define __NR_pkey_mprotect 386 +#define __NR_pkey_alloc 384 +#define __NR_pkey_free 385 #define PKEY_BITS_PER_PKEY 2 #define NR_PKEYS 32 @@ -62,17 +62,17 @@ void pkey_set_rights(int pkey, unsigned long rights) int sys_pkey_mprotect(void *addr, size_t len, int prot, int pkey) { - return syscall(SYS_pkey_mprotect, addr, len, prot, pkey); + return syscall(__NR_pkey_mprotect, addr, len, prot, pkey); } int sys_pkey_alloc(unsigned long flags, unsigned long rights) { - return syscall(SYS_pkey_alloc, flags, rights); + return syscall(__NR_pkey_alloc, flags, rights); } int sys_pkey_free(int pkey) { - return syscall(SYS_pkey_free, pkey); + return syscall(__NR_pkey_free, pkey); } int pkeys_unsupported(void) -- GitLab From afcab636657421f7ebfa0783a91f90256bba0091 Mon Sep 17 00:00:00 2001 From: "Steven Rostedt (VMware)" Date: Tue, 4 Aug 2020 20:00:02 -0400 Subject: [PATCH 1288/1476] tracing: Use trace_sched_process_free() instead of exit() for pid tracing On exit, if a process is preempted after the trace_sched_process_exit() tracepoint but before the process is done exiting, then when it gets scheduled in, the function tracers will not filter it properly against the function tracing pid filters. That is because the function tracing pid filters hooks to the sched_process_exit() tracepoint to remove the exiting task's pid from the filter list. Because the filtering happens at the sched_switch tracepoint, when the exiting task schedules back in to finish up the exit, it will no longer be in the function pid filtering tables. This was noticeable in the notrace self tests on a preemptable kernel, as the tests would fail as it exits and preempted after being taken off the notrace filter table and on scheduling back in it would not be in the notrace list, and then the ending of the exit function would trace. The test detected this and would fail. Cc: stable@vger.kernel.org Cc: Namhyung Kim Fixes: 1e10486ffee0a ("ftrace: Add 'function-fork' trace option") Fixes: c37775d57830a ("tracing: Add infrastructure to allow set_event_pid to follow children" Signed-off-by: Steven Rostedt (VMware) --- kernel/trace/ftrace.c | 4 ++-- kernel/trace/trace_events.c | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/kernel/trace/ftrace.c b/kernel/trace/ftrace.c index 4e3a5d79c0786..76f2dd6fd4141 100644 --- a/kernel/trace/ftrace.c +++ b/kernel/trace/ftrace.c @@ -6985,12 +6985,12 @@ void ftrace_pid_follow_fork(struct trace_array *tr, bool enable) if (enable) { register_trace_sched_process_fork(ftrace_pid_follow_sched_process_fork, tr); - register_trace_sched_process_exit(ftrace_pid_follow_sched_process_exit, + register_trace_sched_process_free(ftrace_pid_follow_sched_process_exit, tr); } else { unregister_trace_sched_process_fork(ftrace_pid_follow_sched_process_fork, tr); - unregister_trace_sched_process_exit(ftrace_pid_follow_sched_process_exit, + unregister_trace_sched_process_free(ftrace_pid_follow_sched_process_exit, tr); } } diff --git a/kernel/trace/trace_events.c b/kernel/trace/trace_events.c index f6f55682d3e2d..a85effb2373bf 100644 --- a/kernel/trace/trace_events.c +++ b/kernel/trace/trace_events.c @@ -538,12 +538,12 @@ void trace_event_follow_fork(struct trace_array *tr, bool enable) if (enable) { register_trace_prio_sched_process_fork(event_filter_pid_sched_process_fork, tr, INT_MIN); - register_trace_prio_sched_process_exit(event_filter_pid_sched_process_exit, + register_trace_prio_sched_process_free(event_filter_pid_sched_process_exit, tr, INT_MAX); } else { unregister_trace_sched_process_fork(event_filter_pid_sched_process_fork, tr); - unregister_trace_sched_process_exit(event_filter_pid_sched_process_exit, + unregister_trace_sched_process_free(event_filter_pid_sched_process_exit, tr); } } -- GitLab From f073531070d24bbb82cb2658952d949f4851024b Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Tue, 28 Jul 2020 17:49:47 +0200 Subject: [PATCH 1289/1476] init: add an init_dup helper Add a simple helper to grab a reference to a file and install it at the next available fd, and switch the early init code over to it. Signed-off-by: Christoph Hellwig Signed-off-by: Al Viro --- fs/init.c | 12 ++++++++++++ include/linux/init_syscalls.h | 1 + init/main.c | 8 ++++---- 3 files changed, 17 insertions(+), 4 deletions(-) diff --git a/fs/init.c b/fs/init.c index db5c48a85644f..e9c320a48cf15 100644 --- a/fs/init.c +++ b/fs/init.c @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include "internal.h" @@ -251,3 +252,14 @@ int __init init_utimes(char *filename, struct timespec64 *ts) path_put(&path); return error; } + +int __init init_dup(struct file *file) +{ + int fd; + + fd = get_unused_fd_flags(0); + if (fd < 0) + return fd; + fd_install(fd, get_file(file)); + return 0; +} diff --git a/include/linux/init_syscalls.h b/include/linux/init_syscalls.h index 3654b525ac0b1..92045d18cbfc9 100644 --- a/include/linux/init_syscalls.h +++ b/include/linux/init_syscalls.h @@ -16,3 +16,4 @@ int __init init_unlink(const char *pathname); int __init init_mkdir(const char *pathname, umode_t mode); int __init init_rmdir(const char *pathname); int __init init_utimes(char *filename, struct timespec64 *ts); +int __init init_dup(struct file *file); diff --git a/init/main.c b/init/main.c index 1c710d3e1d461..9dae9c4f806bb 100644 --- a/init/main.c +++ b/init/main.c @@ -1467,10 +1467,10 @@ void __init console_on_rootfs(void) pr_err("Warning: unable to open an initial console.\n"); return; } - get_file_rcu_many(file, 2); - fd_install(get_unused_fd_flags(0), file); - fd_install(get_unused_fd_flags(0), file); - fd_install(get_unused_fd_flags(0), file); + init_dup(file); + init_dup(file); + init_dup(file); + fput(file); } static noinline void __init kernel_init_freeable(void) -- GitLab From e22fadb1d014e0fd7e43a3af667b2e3ec1a85c1f Mon Sep 17 00:00:00 2001 From: Bharat Kumar Gogada Date: Tue, 16 Jun 2020 18:26:53 +0530 Subject: [PATCH 1290/1476] PCI: xilinx-cpm: Add YAML schemas for Versal CPM Root Port Add YAML schemas documentation for Versal CPM Root Port driver. Link: https://lore.kernel.org/r/1592312214-9347-2-git-send-email-bharat.kumar.gogada@xilinx.com Signed-off-by: Bharat Kumar Gogada Signed-off-by: Lorenzo Pieralisi Signed-off-by: Bjorn Helgaas Reviewed-by: Rob Herring --- .../bindings/pci/xilinx-versal-cpm.yaml | 99 +++++++++++++++++++ 1 file changed, 99 insertions(+) create mode 100644 Documentation/devicetree/bindings/pci/xilinx-versal-cpm.yaml diff --git a/Documentation/devicetree/bindings/pci/xilinx-versal-cpm.yaml b/Documentation/devicetree/bindings/pci/xilinx-versal-cpm.yaml new file mode 100644 index 0000000000000..a2bbc0eb7220e --- /dev/null +++ b/Documentation/devicetree/bindings/pci/xilinx-versal-cpm.yaml @@ -0,0 +1,99 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/pci/xilinx-versal-cpm.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: CPM Host Controller device tree for Xilinx Versal SoCs + +maintainers: + - Bharat Kumar Gogada + +allOf: + - $ref: /schemas/pci/pci-bus.yaml# + +properties: + compatible: + const: xlnx,versal-cpm-host-1.00 + + reg: + items: + - description: Configuration space region and bridge registers. + - description: CPM system level control and status registers. + + reg-names: + items: + - const: cfg + - const: cpm_slcr + + interrupts: + maxItems: 1 + + msi-map: + description: + Maps a Requester ID to an MSI controller and associated MSI sideband data. + + ranges: + maxItems: 2 + + "#interrupt-cells": + const: 1 + + interrupt-controller: + description: Interrupt controller node for handling legacy PCI interrupts. + type: object + properties: + "#address-cells": + const: 0 + "#interrupt-cells": + const: 1 + "interrupt-controller": true + additionalProperties: false + +required: + - reg + - reg-names + - "#interrupt-cells" + - interrupts + - interrupt-parent + - interrupt-map + - interrupt-map-mask + - bus-range + - msi-map + - interrupt-controller + +unevaluatedProperties: false + +examples: + - | + + versal { + #address-cells = <2>; + #size-cells = <2>; + cpm_pcie: pcie@fca10000 { + compatible = "xlnx,versal-cpm-host-1.00"; + device_type = "pci"; + #address-cells = <3>; + #interrupt-cells = <1>; + #size-cells = <2>; + interrupts = <0 72 4>; + interrupt-parent = <&gic>; + interrupt-map-mask = <0 0 0 7>; + interrupt-map = <0 0 0 1 &pcie_intc_0 0>, + <0 0 0 2 &pcie_intc_0 1>, + <0 0 0 3 &pcie_intc_0 2>, + <0 0 0 4 &pcie_intc_0 3>; + bus-range = <0x00 0xff>; + ranges = <0x02000000 0x0 0xe0000000 0x0 0xe0000000 0x0 0x10000000>, + <0x43000000 0x80 0x00000000 0x80 0x00000000 0x0 0x80000000>; + msi-map = <0x0 &its_gic 0x0 0x10000>; + reg = <0x6 0x00000000 0x0 0x10000000>, + <0x0 0xfca10000 0x0 0x1000>; + reg-names = "cfg", "cpm_slcr"; + pcie_intc_0: interrupt-controller { + #address-cells = <0>; + #interrupt-cells = <1>; + interrupt-controller; + }; + }; + }; -- GitLab From 508f610648b97012d39f97590e3f3f1059471607 Mon Sep 17 00:00:00 2001 From: Bharat Kumar Gogada Date: Tue, 16 Jun 2020 18:26:54 +0530 Subject: [PATCH 1291/1476] PCI: xilinx-cpm: Add Versal CPM Root Port driver Add support for Versal CPM as Root Port. The Versal ACAP devices include CCIX-PCIe Module (CPM). The integrated block for CPM along with the integrated bridge can function as PCIe Root Port. Bridge error and legacy interrupts in Versal CPM are handled using Versal CPM specific interrupt line. [bhelgaas: fold in kerneldoc fix from https://lore.kernel.org/linux-acpi/20200729201224.26799-7-krzk@kernel.org/] Link: https://lore.kernel.org/r/1592312214-9347-3-git-send-email-bharat.kumar.gogada@xilinx.com Signed-off-by: Bharat Kumar Gogada Signed-off-by: Lorenzo Pieralisi Signed-off-by: Bjorn Helgaas Reviewed-by: Rob Herring --- drivers/pci/controller/Kconfig | 8 + drivers/pci/controller/Makefile | 1 + drivers/pci/controller/pcie-xilinx-cpm.c | 614 +++++++++++++++++++++++ 3 files changed, 623 insertions(+) create mode 100644 drivers/pci/controller/pcie-xilinx-cpm.c diff --git a/drivers/pci/controller/Kconfig b/drivers/pci/controller/Kconfig index adddf21fa3815..f18c3725ef805 100644 --- a/drivers/pci/controller/Kconfig +++ b/drivers/pci/controller/Kconfig @@ -99,6 +99,14 @@ config PCIE_XILINX Say 'Y' here if you want kernel to support the Xilinx AXI PCIe Host Bridge driver. +config PCIE_XILINX_CPM + bool "Xilinx Versal CPM host bridge support" + depends on ARCH_ZYNQMP || COMPILE_TEST + select PCI_HOST_COMMON + help + Say 'Y' here if you want kernel support for the + Xilinx Versal CPM host bridge. + config PCI_XGENE bool "X-Gene PCIe controller" depends on ARM64 || COMPILE_TEST diff --git a/drivers/pci/controller/Makefile b/drivers/pci/controller/Makefile index efd9733ead264..bcdbf49ab1e40 100644 --- a/drivers/pci/controller/Makefile +++ b/drivers/pci/controller/Makefile @@ -13,6 +13,7 @@ obj-$(CONFIG_PCI_HOST_COMMON) += pci-host-common.o obj-$(CONFIG_PCI_HOST_GENERIC) += pci-host-generic.o obj-$(CONFIG_PCIE_XILINX) += pcie-xilinx.o obj-$(CONFIG_PCIE_XILINX_NWL) += pcie-xilinx-nwl.o +obj-$(CONFIG_PCIE_XILINX_CPM) += pcie-xilinx-cpm.o obj-$(CONFIG_PCI_V3_SEMI) += pci-v3-semi.o obj-$(CONFIG_PCI_XGENE_MSI) += pci-xgene-msi.o obj-$(CONFIG_PCI_VERSATILE) += pci-versatile.o diff --git a/drivers/pci/controller/pcie-xilinx-cpm.c b/drivers/pci/controller/pcie-xilinx-cpm.c new file mode 100644 index 0000000000000..cd8c77457b5f6 --- /dev/null +++ b/drivers/pci/controller/pcie-xilinx-cpm.c @@ -0,0 +1,614 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * PCIe host controller driver for Xilinx Versal CPM DMA Bridge + * + * (C) Copyright 2019 - 2020, Xilinx, Inc. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../pci.h" + +/* Register definitions */ +#define XILINX_CPM_PCIE_REG_IDR 0x00000E10 +#define XILINX_CPM_PCIE_REG_IMR 0x00000E14 +#define XILINX_CPM_PCIE_REG_PSCR 0x00000E1C +#define XILINX_CPM_PCIE_REG_RPSC 0x00000E20 +#define XILINX_CPM_PCIE_REG_RPEFR 0x00000E2C +#define XILINX_CPM_PCIE_REG_IDRN 0x00000E38 +#define XILINX_CPM_PCIE_REG_IDRN_MASK 0x00000E3C +#define XILINX_CPM_PCIE_MISC_IR_STATUS 0x00000340 +#define XILINX_CPM_PCIE_MISC_IR_ENABLE 0x00000348 +#define XILINX_CPM_PCIE_MISC_IR_LOCAL BIT(1) + +/* Interrupt registers definitions */ +#define XILINX_CPM_PCIE_INTR_LINK_DOWN 0 +#define XILINX_CPM_PCIE_INTR_HOT_RESET 3 +#define XILINX_CPM_PCIE_INTR_CFG_PCIE_TIMEOUT 4 +#define XILINX_CPM_PCIE_INTR_CFG_TIMEOUT 8 +#define XILINX_CPM_PCIE_INTR_CORRECTABLE 9 +#define XILINX_CPM_PCIE_INTR_NONFATAL 10 +#define XILINX_CPM_PCIE_INTR_FATAL 11 +#define XILINX_CPM_PCIE_INTR_CFG_ERR_POISON 12 +#define XILINX_CPM_PCIE_INTR_PME_TO_ACK_RCVD 15 +#define XILINX_CPM_PCIE_INTR_INTX 16 +#define XILINX_CPM_PCIE_INTR_PM_PME_RCVD 17 +#define XILINX_CPM_PCIE_INTR_SLV_UNSUPP 20 +#define XILINX_CPM_PCIE_INTR_SLV_UNEXP 21 +#define XILINX_CPM_PCIE_INTR_SLV_COMPL 22 +#define XILINX_CPM_PCIE_INTR_SLV_ERRP 23 +#define XILINX_CPM_PCIE_INTR_SLV_CMPABT 24 +#define XILINX_CPM_PCIE_INTR_SLV_ILLBUR 25 +#define XILINX_CPM_PCIE_INTR_MST_DECERR 26 +#define XILINX_CPM_PCIE_INTR_MST_SLVERR 27 +#define XILINX_CPM_PCIE_INTR_SLV_PCIE_TIMEOUT 28 + +#define IMR(x) BIT(XILINX_CPM_PCIE_INTR_ ##x) + +#define XILINX_CPM_PCIE_IMR_ALL_MASK \ + ( \ + IMR(LINK_DOWN) | \ + IMR(HOT_RESET) | \ + IMR(CFG_PCIE_TIMEOUT) | \ + IMR(CFG_TIMEOUT) | \ + IMR(CORRECTABLE) | \ + IMR(NONFATAL) | \ + IMR(FATAL) | \ + IMR(CFG_ERR_POISON) | \ + IMR(PME_TO_ACK_RCVD) | \ + IMR(INTX) | \ + IMR(PM_PME_RCVD) | \ + IMR(SLV_UNSUPP) | \ + IMR(SLV_UNEXP) | \ + IMR(SLV_COMPL) | \ + IMR(SLV_ERRP) | \ + IMR(SLV_CMPABT) | \ + IMR(SLV_ILLBUR) | \ + IMR(MST_DECERR) | \ + IMR(MST_SLVERR) | \ + IMR(SLV_PCIE_TIMEOUT) \ + ) + +#define XILINX_CPM_PCIE_IDR_ALL_MASK 0xFFFFFFFF +#define XILINX_CPM_PCIE_IDRN_MASK GENMASK(19, 16) +#define XILINX_CPM_PCIE_IDRN_SHIFT 16 + +/* Root Port Error FIFO Read Register definitions */ +#define XILINX_CPM_PCIE_RPEFR_ERR_VALID BIT(18) +#define XILINX_CPM_PCIE_RPEFR_REQ_ID GENMASK(15, 0) +#define XILINX_CPM_PCIE_RPEFR_ALL_MASK 0xFFFFFFFF + +/* Root Port Status/control Register definitions */ +#define XILINX_CPM_PCIE_REG_RPSC_BEN BIT(0) + +/* Phy Status/Control Register definitions */ +#define XILINX_CPM_PCIE_REG_PSCR_LNKUP BIT(11) + +/** + * struct xilinx_cpm_pcie_port - PCIe port information + * @reg_base: Bridge Register Base + * @cpm_base: CPM System Level Control and Status Register(SLCR) Base + * @dev: Device pointer + * @intx_domain: Legacy IRQ domain pointer + * @cpm_domain: CPM IRQ domain pointer + * @cfg: Holds mappings of config space window + * @intx_irq: legacy interrupt number + * @irq: Error interrupt number + * @lock: lock protecting shared register access + */ +struct xilinx_cpm_pcie_port { + void __iomem *reg_base; + void __iomem *cpm_base; + struct device *dev; + struct irq_domain *intx_domain; + struct irq_domain *cpm_domain; + struct pci_config_window *cfg; + int intx_irq; + int irq; + raw_spinlock_t lock; +}; + +static u32 pcie_read(struct xilinx_cpm_pcie_port *port, u32 reg) +{ + return readl_relaxed(port->reg_base + reg); +} + +static void pcie_write(struct xilinx_cpm_pcie_port *port, + u32 val, u32 reg) +{ + writel_relaxed(val, port->reg_base + reg); +} + +static bool cpm_pcie_link_up(struct xilinx_cpm_pcie_port *port) +{ + return (pcie_read(port, XILINX_CPM_PCIE_REG_PSCR) & + XILINX_CPM_PCIE_REG_PSCR_LNKUP); +} + +static void cpm_pcie_clear_err_interrupts(struct xilinx_cpm_pcie_port *port) +{ + unsigned long val = pcie_read(port, XILINX_CPM_PCIE_REG_RPEFR); + + if (val & XILINX_CPM_PCIE_RPEFR_ERR_VALID) { + dev_dbg(port->dev, "Requester ID %lu\n", + val & XILINX_CPM_PCIE_RPEFR_REQ_ID); + pcie_write(port, XILINX_CPM_PCIE_RPEFR_ALL_MASK, + XILINX_CPM_PCIE_REG_RPEFR); + } +} + +static void xilinx_cpm_mask_leg_irq(struct irq_data *data) +{ + struct xilinx_cpm_pcie_port *port = irq_data_get_irq_chip_data(data); + unsigned long flags; + u32 mask; + u32 val; + + mask = BIT(data->hwirq + XILINX_CPM_PCIE_IDRN_SHIFT); + raw_spin_lock_irqsave(&port->lock, flags); + val = pcie_read(port, XILINX_CPM_PCIE_REG_IDRN_MASK); + pcie_write(port, (val & (~mask)), XILINX_CPM_PCIE_REG_IDRN_MASK); + raw_spin_unlock_irqrestore(&port->lock, flags); +} + +static void xilinx_cpm_unmask_leg_irq(struct irq_data *data) +{ + struct xilinx_cpm_pcie_port *port = irq_data_get_irq_chip_data(data); + unsigned long flags; + u32 mask; + u32 val; + + mask = BIT(data->hwirq + XILINX_CPM_PCIE_IDRN_SHIFT); + raw_spin_lock_irqsave(&port->lock, flags); + val = pcie_read(port, XILINX_CPM_PCIE_REG_IDRN_MASK); + pcie_write(port, (val | mask), XILINX_CPM_PCIE_REG_IDRN_MASK); + raw_spin_unlock_irqrestore(&port->lock, flags); +} + +static struct irq_chip xilinx_cpm_leg_irq_chip = { + .name = "INTx", + .irq_mask = xilinx_cpm_mask_leg_irq, + .irq_unmask = xilinx_cpm_unmask_leg_irq, +}; + +/** + * xilinx_cpm_pcie_intx_map - Set the handler for the INTx and mark IRQ as valid + * @domain: IRQ domain + * @irq: Virtual IRQ number + * @hwirq: HW interrupt number + * + * Return: Always returns 0. + */ +static int xilinx_cpm_pcie_intx_map(struct irq_domain *domain, + unsigned int irq, irq_hw_number_t hwirq) +{ + irq_set_chip_and_handler(irq, &xilinx_cpm_leg_irq_chip, + handle_level_irq); + irq_set_chip_data(irq, domain->host_data); + irq_set_status_flags(irq, IRQ_LEVEL); + + return 0; +} + +/* INTx IRQ Domain operations */ +static const struct irq_domain_ops intx_domain_ops = { + .map = xilinx_cpm_pcie_intx_map, +}; + +static void xilinx_cpm_pcie_intx_flow(struct irq_desc *desc) +{ + struct xilinx_cpm_pcie_port *port = irq_desc_get_handler_data(desc); + struct irq_chip *chip = irq_desc_get_chip(desc); + unsigned long val; + int i; + + chained_irq_enter(chip, desc); + + val = FIELD_GET(XILINX_CPM_PCIE_IDRN_MASK, + pcie_read(port, XILINX_CPM_PCIE_REG_IDRN)); + + for_each_set_bit(i, &val, PCI_NUM_INTX) + generic_handle_irq(irq_find_mapping(port->intx_domain, i)); + + chained_irq_exit(chip, desc); +} + +static void xilinx_cpm_mask_event_irq(struct irq_data *d) +{ + struct xilinx_cpm_pcie_port *port = irq_data_get_irq_chip_data(d); + u32 val; + + raw_spin_lock(&port->lock); + val = pcie_read(port, XILINX_CPM_PCIE_REG_IMR); + val &= ~BIT(d->hwirq); + pcie_write(port, val, XILINX_CPM_PCIE_REG_IMR); + raw_spin_unlock(&port->lock); +} + +static void xilinx_cpm_unmask_event_irq(struct irq_data *d) +{ + struct xilinx_cpm_pcie_port *port = irq_data_get_irq_chip_data(d); + u32 val; + + raw_spin_lock(&port->lock); + val = pcie_read(port, XILINX_CPM_PCIE_REG_IMR); + val |= BIT(d->hwirq); + pcie_write(port, val, XILINX_CPM_PCIE_REG_IMR); + raw_spin_unlock(&port->lock); +} + +static struct irq_chip xilinx_cpm_event_irq_chip = { + .name = "RC-Event", + .irq_mask = xilinx_cpm_mask_event_irq, + .irq_unmask = xilinx_cpm_unmask_event_irq, +}; + +static int xilinx_cpm_pcie_event_map(struct irq_domain *domain, + unsigned int irq, irq_hw_number_t hwirq) +{ + irq_set_chip_and_handler(irq, &xilinx_cpm_event_irq_chip, + handle_level_irq); + irq_set_chip_data(irq, domain->host_data); + irq_set_status_flags(irq, IRQ_LEVEL); + return 0; +} + +static const struct irq_domain_ops event_domain_ops = { + .map = xilinx_cpm_pcie_event_map, +}; + +static void xilinx_cpm_pcie_event_flow(struct irq_desc *desc) +{ + struct xilinx_cpm_pcie_port *port = irq_desc_get_handler_data(desc); + struct irq_chip *chip = irq_desc_get_chip(desc); + unsigned long val; + int i; + + chained_irq_enter(chip, desc); + val = pcie_read(port, XILINX_CPM_PCIE_REG_IDR); + val &= pcie_read(port, XILINX_CPM_PCIE_REG_IMR); + for_each_set_bit(i, &val, 32) + generic_handle_irq(irq_find_mapping(port->cpm_domain, i)); + pcie_write(port, val, XILINX_CPM_PCIE_REG_IDR); + + /* + * XILINX_CPM_PCIE_MISC_IR_STATUS register is mapped to + * CPM SLCR block. + */ + val = readl_relaxed(port->cpm_base + XILINX_CPM_PCIE_MISC_IR_STATUS); + if (val) + writel_relaxed(val, + port->cpm_base + XILINX_CPM_PCIE_MISC_IR_STATUS); + + chained_irq_exit(chip, desc); +} + +#define _IC(x, s) \ + [XILINX_CPM_PCIE_INTR_ ## x] = { __stringify(x), s } + +static const struct { + const char *sym; + const char *str; +} intr_cause[32] = { + _IC(LINK_DOWN, "Link Down"), + _IC(HOT_RESET, "Hot reset"), + _IC(CFG_TIMEOUT, "ECAM access timeout"), + _IC(CORRECTABLE, "Correctable error message"), + _IC(NONFATAL, "Non fatal error message"), + _IC(FATAL, "Fatal error message"), + _IC(SLV_UNSUPP, "Slave unsupported request"), + _IC(SLV_UNEXP, "Slave unexpected completion"), + _IC(SLV_COMPL, "Slave completion timeout"), + _IC(SLV_ERRP, "Slave Error Poison"), + _IC(SLV_CMPABT, "Slave Completer Abort"), + _IC(SLV_ILLBUR, "Slave Illegal Burst"), + _IC(MST_DECERR, "Master decode error"), + _IC(MST_SLVERR, "Master slave error"), + _IC(CFG_PCIE_TIMEOUT, "PCIe ECAM access timeout"), + _IC(CFG_ERR_POISON, "ECAM poisoned completion received"), + _IC(PME_TO_ACK_RCVD, "PME_TO_ACK message received"), + _IC(PM_PME_RCVD, "PM_PME message received"), + _IC(SLV_PCIE_TIMEOUT, "PCIe completion timeout received"), +}; + +static irqreturn_t xilinx_cpm_pcie_intr_handler(int irq, void *dev_id) +{ + struct xilinx_cpm_pcie_port *port = dev_id; + struct device *dev = port->dev; + struct irq_data *d; + + d = irq_domain_get_irq_data(port->cpm_domain, irq); + + switch (d->hwirq) { + case XILINX_CPM_PCIE_INTR_CORRECTABLE: + case XILINX_CPM_PCIE_INTR_NONFATAL: + case XILINX_CPM_PCIE_INTR_FATAL: + cpm_pcie_clear_err_interrupts(port); + fallthrough; + + default: + if (intr_cause[d->hwirq].str) + dev_warn(dev, "%s\n", intr_cause[d->hwirq].str); + else + dev_warn(dev, "Unknown IRQ %ld\n", d->hwirq); + } + + return IRQ_HANDLED; +} + +static void xilinx_cpm_free_irq_domains(struct xilinx_cpm_pcie_port *port) +{ + if (port->intx_domain) { + irq_domain_remove(port->intx_domain); + port->intx_domain = NULL; + } + + if (port->cpm_domain) { + irq_domain_remove(port->cpm_domain); + port->cpm_domain = NULL; + } +} + +/** + * xilinx_cpm_pcie_init_irq_domain - Initialize IRQ domain + * @port: PCIe port information + * + * Return: '0' on success and error value on failure + */ +static int xilinx_cpm_pcie_init_irq_domain(struct xilinx_cpm_pcie_port *port) +{ + struct device *dev = port->dev; + struct device_node *node = dev->of_node; + struct device_node *pcie_intc_node; + + /* Setup INTx */ + pcie_intc_node = of_get_next_child(node, NULL); + if (!pcie_intc_node) { + dev_err(dev, "No PCIe Intc node found\n"); + return -EINVAL; + } + + port->cpm_domain = irq_domain_add_linear(pcie_intc_node, 32, + &event_domain_ops, + port); + if (!port->cpm_domain) + goto out; + + irq_domain_update_bus_token(port->cpm_domain, DOMAIN_BUS_NEXUS); + + port->intx_domain = irq_domain_add_linear(pcie_intc_node, PCI_NUM_INTX, + &intx_domain_ops, + port); + if (!port->intx_domain) + goto out; + + irq_domain_update_bus_token(port->intx_domain, DOMAIN_BUS_WIRED); + + of_node_put(pcie_intc_node); + raw_spin_lock_init(&port->lock); + + return 0; +out: + xilinx_cpm_free_irq_domains(port); + dev_err(dev, "Failed to allocate IRQ domains\n"); + + return -ENOMEM; +} + +static int xilinx_cpm_setup_irq(struct xilinx_cpm_pcie_port *port) +{ + struct device *dev = port->dev; + struct platform_device *pdev = to_platform_device(dev); + int i, irq; + + port->irq = platform_get_irq(pdev, 0); + if (port->irq < 0) + return port->irq; + + for (i = 0; i < ARRAY_SIZE(intr_cause); i++) { + int err; + + if (!intr_cause[i].str) + continue; + + irq = irq_create_mapping(port->cpm_domain, i); + if (!irq) { + dev_err(dev, "Failed to map interrupt\n"); + return -ENXIO; + } + + err = devm_request_irq(dev, irq, xilinx_cpm_pcie_intr_handler, + 0, intr_cause[i].sym, port); + if (err) { + dev_err(dev, "Failed to request IRQ %d\n", irq); + return err; + } + } + + port->intx_irq = irq_create_mapping(port->cpm_domain, + XILINX_CPM_PCIE_INTR_INTX); + if (!port->intx_irq) { + dev_err(dev, "Failed to map INTx interrupt\n"); + return -ENXIO; + } + + /* Plug the INTx chained handler */ + irq_set_chained_handler_and_data(port->intx_irq, + xilinx_cpm_pcie_intx_flow, port); + + /* Plug the main event chained handler */ + irq_set_chained_handler_and_data(port->irq, + xilinx_cpm_pcie_event_flow, port); + + return 0; +} + +/** + * xilinx_cpm_pcie_init_port - Initialize hardware + * @port: PCIe port information + */ +static void xilinx_cpm_pcie_init_port(struct xilinx_cpm_pcie_port *port) +{ + if (cpm_pcie_link_up(port)) + dev_info(port->dev, "PCIe Link is UP\n"); + else + dev_info(port->dev, "PCIe Link is DOWN\n"); + + /* Disable all interrupts */ + pcie_write(port, ~XILINX_CPM_PCIE_IDR_ALL_MASK, + XILINX_CPM_PCIE_REG_IMR); + + /* Clear pending interrupts */ + pcie_write(port, pcie_read(port, XILINX_CPM_PCIE_REG_IDR) & + XILINX_CPM_PCIE_IMR_ALL_MASK, + XILINX_CPM_PCIE_REG_IDR); + + /* + * XILINX_CPM_PCIE_MISC_IR_ENABLE register is mapped to + * CPM SLCR block. + */ + writel(XILINX_CPM_PCIE_MISC_IR_LOCAL, + port->cpm_base + XILINX_CPM_PCIE_MISC_IR_ENABLE); + /* Enable the Bridge enable bit */ + pcie_write(port, pcie_read(port, XILINX_CPM_PCIE_REG_RPSC) | + XILINX_CPM_PCIE_REG_RPSC_BEN, + XILINX_CPM_PCIE_REG_RPSC); +} + +/** + * xilinx_cpm_pcie_parse_dt - Parse Device tree + * @port: PCIe port information + * @bus_range: Bus resource + * + * Return: '0' on success and error value on failure + */ +static int xilinx_cpm_pcie_parse_dt(struct xilinx_cpm_pcie_port *port, + struct resource *bus_range) +{ + struct device *dev = port->dev; + struct platform_device *pdev = to_platform_device(dev); + struct resource *res; + + port->cpm_base = devm_platform_ioremap_resource_byname(pdev, + "cpm_slcr"); + if (IS_ERR(port->cpm_base)) + return PTR_ERR(port->cpm_base); + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "cfg"); + if (!res) + return -ENXIO; + + port->cfg = pci_ecam_create(dev, res, bus_range, + &pci_generic_ecam_ops); + if (IS_ERR(port->cfg)) + return PTR_ERR(port->cfg); + + port->reg_base = port->cfg->win; + + return 0; +} + +static void xilinx_cpm_free_interrupts(struct xilinx_cpm_pcie_port *port) +{ + irq_set_chained_handler_and_data(port->intx_irq, NULL, NULL); + irq_set_chained_handler_and_data(port->irq, NULL, NULL); +} + +/** + * xilinx_cpm_pcie_probe - Probe function + * @pdev: Platform device pointer + * + * Return: '0' on success and error value on failure + */ +static int xilinx_cpm_pcie_probe(struct platform_device *pdev) +{ + struct xilinx_cpm_pcie_port *port; + struct device *dev = &pdev->dev; + struct pci_host_bridge *bridge; + struct resource *bus_range; + int err; + + bridge = devm_pci_alloc_host_bridge(dev, sizeof(*port)); + if (!bridge) + return -ENODEV; + + port = pci_host_bridge_priv(bridge); + + port->dev = dev; + + err = pci_parse_request_of_pci_ranges(dev, &bridge->windows, + &bridge->dma_ranges, &bus_range); + if (err) { + dev_err(dev, "Getting bridge resources failed\n"); + return err; + } + + err = xilinx_cpm_pcie_init_irq_domain(port); + if (err) + return err; + + err = xilinx_cpm_pcie_parse_dt(port, bus_range); + if (err) { + dev_err(dev, "Parsing DT failed\n"); + goto err_parse_dt; + } + + xilinx_cpm_pcie_init_port(port); + + err = xilinx_cpm_setup_irq(port); + if (err) { + dev_err(dev, "Failed to set up interrupts\n"); + goto err_setup_irq; + } + + bridge->dev.parent = dev; + bridge->sysdata = port->cfg; + bridge->busnr = port->cfg->busr.start; + bridge->ops = (struct pci_ops *)&pci_generic_ecam_ops.pci_ops; + bridge->map_irq = of_irq_parse_and_map_pci; + bridge->swizzle_irq = pci_common_swizzle; + + err = pci_host_probe(bridge); + if (err < 0) + goto err_host_bridge; + + return 0; + +err_host_bridge: + xilinx_cpm_free_interrupts(port); +err_setup_irq: + pci_ecam_free(port->cfg); +err_parse_dt: + xilinx_cpm_free_irq_domains(port); + return err; +} + +static const struct of_device_id xilinx_cpm_pcie_of_match[] = { + { .compatible = "xlnx,versal-cpm-host-1.00", }, + {} +}; + +static struct platform_driver xilinx_cpm_pcie_driver = { + .driver = { + .name = "xilinx-cpm-pcie", + .of_match_table = xilinx_cpm_pcie_of_match, + .suppress_bind_attrs = true, + }, + .probe = xilinx_cpm_pcie_probe, +}; + +builtin_platform_driver(xilinx_cpm_pcie_driver); -- GitLab From 9b41d19aff40907bdd64ae230aaadbb3de3db710 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Wed, 29 Jul 2020 22:12:19 +0200 Subject: [PATCH 1292/1476] PCI: Fix kerneldoc warnings Fix kerneldoc warnings, e.g., $ make W=1 drivers/pci/ drivers/pci/ats.c:196: warning: Function parameter or member 'pdev' not described in 'pci_enable_pri' drivers/pci/ats.c:196: warning: Function parameter or member 'reqs' not described in 'pci_enable_pri' ... Link: https://lore.kernel.org/r/20200729201224.26799-2-krzk@kernel.org Link: https://lore.kernel.org/r/20200729201224.26799-3-krzk@kernel.org Link: https://lore.kernel.org/r/20200729201224.26799-4-krzk@kernel.org Link: https://lore.kernel.org/r/20200729201224.26799-5-krzk@kernel.org Link: https://lore.kernel.org/r/20200729201224.26799-6-krzk@kernel.org Link: https://lore.kernel.org/r/20200729201224.26799-7-krzk@kernel.org Signed-off-by: Krzysztof Kozlowski Signed-off-by: Bjorn Helgaas --- drivers/pci/ats.c | 3 ++- drivers/pci/controller/dwc/pcie-designware-ep.c | 2 +- drivers/pci/controller/pcie-rockchip-ep.c | 1 + drivers/pci/endpoint/functions/pci-epf-test.c | 2 +- drivers/pci/endpoint/pci-ep-cfs.c | 2 +- drivers/pci/endpoint/pci-epc-core.c | 2 +- drivers/pci/endpoint/pci-epc-mem.c | 2 +- drivers/pci/endpoint/pci-epf-core.c | 4 +++- drivers/pci/hotplug/acpi_pcihp.c | 4 ++-- drivers/pci/hotplug/pciehp_core.c | 1 + drivers/pci/of.c | 2 ++ drivers/pci/pci-pf-stub.c | 2 +- drivers/pci/setup-bus.c | 1 + drivers/pci/vc.c | 1 - 14 files changed, 18 insertions(+), 11 deletions(-) diff --git a/drivers/pci/ats.c b/drivers/pci/ats.c index b761c1f72f672..2e10730525093 100644 --- a/drivers/pci/ats.c +++ b/drivers/pci/ats.c @@ -188,7 +188,8 @@ void pci_pri_init(struct pci_dev *pdev) /** * pci_enable_pri - Enable PRI capability - * @ pdev: PCI device structure + * @pdev: PCI device structure + * @reqs: outstanding requests * * Returns 0 on success, negative value on error */ diff --git a/drivers/pci/controller/dwc/pcie-designware-ep.c b/drivers/pci/controller/dwc/pcie-designware-ep.c index 5e5b8821bed8c..305bfec2424d8 100644 --- a/drivers/pci/controller/dwc/pcie-designware-ep.c +++ b/drivers/pci/controller/dwc/pcie-designware-ep.c @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-2.0 -/** +/* * Synopsys DesignWare PCIe Endpoint controller driver * * Copyright (C) 2017 Texas Instruments diff --git a/drivers/pci/controller/pcie-rockchip-ep.c b/drivers/pci/controller/pcie-rockchip-ep.c index 5eaf36629a759..7631dc3961c12 100644 --- a/drivers/pci/controller/pcie-rockchip-ep.c +++ b/drivers/pci/controller/pcie-rockchip-ep.c @@ -22,6 +22,7 @@ /** * struct rockchip_pcie_ep - private data for PCIe endpoint controller driver * @rockchip: Rockchip PCIe controller + * @epc: PCI EPC device * @max_regions: maximum number of regions supported by hardware * @ob_region_map: bitmask of mapped outbound regions * @ob_addr: base addresses in the AXI bus where the outbound regions start diff --git a/drivers/pci/endpoint/functions/pci-epf-test.c b/drivers/pci/endpoint/functions/pci-epf-test.c index c89a9561439f9..e4e51d884553f 100644 --- a/drivers/pci/endpoint/functions/pci-epf-test.c +++ b/drivers/pci/endpoint/functions/pci-epf-test.c @@ -181,7 +181,7 @@ static int pci_epf_test_init_dma_chan(struct pci_epf_test *epf_test) /** * pci_epf_test_clean_dma_chan() - Function to cleanup EPF test DMA channel - * @epf: the EPF test device that performs data transfer operation + * @epf_test: the EPF test device that performs data transfer operation * * Helper to cleanup EPF test DMA channel. */ diff --git a/drivers/pci/endpoint/pci-ep-cfs.c b/drivers/pci/endpoint/pci-ep-cfs.c index 55edce50be96d..3710adf51912d 100644 --- a/drivers/pci/endpoint/pci-ep-cfs.c +++ b/drivers/pci/endpoint/pci-ep-cfs.c @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-2.0 -/** +/* * configfs to configure the PCI endpoint * * Copyright (C) 2017 Texas Instruments diff --git a/drivers/pci/endpoint/pci-epc-core.c b/drivers/pci/endpoint/pci-epc-core.c index 82ba0dc7f2f5f..cadd3db0cbb08 100644 --- a/drivers/pci/endpoint/pci-epc-core.c +++ b/drivers/pci/endpoint/pci-epc-core.c @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-2.0 -/** +/* * PCI Endpoint *Controller* (EPC) library * * Copyright (C) 2017 Texas Instruments diff --git a/drivers/pci/endpoint/pci-epc-mem.c b/drivers/pci/endpoint/pci-epc-mem.c index 80c46f3a45909..a97b56a6d2db2 100644 --- a/drivers/pci/endpoint/pci-epc-mem.c +++ b/drivers/pci/endpoint/pci-epc-mem.c @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-2.0 -/** +/* * PCI Endpoint *Controller* Address Space Management * * Copyright (C) 2017 Texas Instruments diff --git a/drivers/pci/endpoint/pci-epf-core.c b/drivers/pci/endpoint/pci-epf-core.c index 244e00f48c5cf..c977cf9dce561 100644 --- a/drivers/pci/endpoint/pci-epf-core.c +++ b/drivers/pci/endpoint/pci-epf-core.c @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-2.0 -/** +/* * PCI Endpoint *Function* (EPF) library * * Copyright (C) 2017 Texas Instruments @@ -71,6 +71,7 @@ EXPORT_SYMBOL_GPL(pci_epf_bind); /** * pci_epf_free_space() - free the allocated PCI EPF register space + * @epf: the EPF device from whom to free the memory * @addr: the virtual address of the PCI EPF register space * @bar: the BAR number corresponding to the register space * @@ -96,6 +97,7 @@ EXPORT_SYMBOL_GPL(pci_epf_free_space); /** * pci_epf_alloc_space() - allocate memory for the PCI EPF register space + * @epf: the EPF device to whom allocate the memory * @size: the size of the memory that has to be allocated * @bar: the BAR number corresponding to the allocated register space * @align: alignment size for the allocation region diff --git a/drivers/pci/hotplug/acpi_pcihp.c b/drivers/pci/hotplug/acpi_pcihp.c index 6b7c1ed58e7e0..2750a64cecd3a 100644 --- a/drivers/pci/hotplug/acpi_pcihp.c +++ b/drivers/pci/hotplug/acpi_pcihp.c @@ -61,7 +61,7 @@ static acpi_status acpi_run_oshp(acpi_handle handle) /** * acpi_get_hp_hw_control_from_firmware - * @dev: the pci_dev of the bridge that has a hotplug controller + * @pdev: the pci_dev of the bridge that has a hotplug controller * * Attempt to take hotplug control from firmware. */ @@ -191,7 +191,7 @@ check_hotplug(acpi_handle handle, u32 lvl, void *context, void **rv) /** * acpi_pci_detect_ejectable - check if the PCI bus has ejectable slots - * @handle - handle of the PCI bus to scan + * @handle: handle of the PCI bus to scan * * Returns 1 if the PCI bus has ACPI based ejectable slots, 0 otherwise. */ diff --git a/drivers/pci/hotplug/pciehp_core.c b/drivers/pci/hotplug/pciehp_core.c index bf779f291f152..ad3393930ecb4 100644 --- a/drivers/pci/hotplug/pciehp_core.c +++ b/drivers/pci/hotplug/pciehp_core.c @@ -153,6 +153,7 @@ static int get_adapter_status(struct hotplug_slot *hotplug_slot, u8 *value) /** * pciehp_check_presence() - synthesize event if presence has changed + * @ctrl: controller to check * * On probe and resume, an explicit presence check is necessary to bring up an * occupied slot or bring down an unoccupied slot. This can't be triggered by diff --git a/drivers/pci/of.c b/drivers/pci/of.c index 27839cd2459f6..4c3bd8696aaf2 100644 --- a/drivers/pci/of.c +++ b/drivers/pci/of.c @@ -243,6 +243,8 @@ EXPORT_SYMBOL_GPL(of_pci_check_probe_only); * @busno: bus number associated with the bridge root bus * @bus_max: maximum number of buses for this bridge * @resources: list where the range of resources will be added after DT parsing + * @ib_resources: list where the range of inbound resources (with addresses + * from 'dma-ranges') will be added after DT parsing * @io_base: pointer to a variable that will contain on return the physical * address for the start of the I/O range. Can be NULL if the caller doesn't * expect I/O ranges to be present in the device tree. diff --git a/drivers/pci/pci-pf-stub.c b/drivers/pci/pci-pf-stub.c index ef293e735c557..a0b2bd6c918a0 100644 --- a/drivers/pci/pci-pf-stub.c +++ b/drivers/pci/pci-pf-stub.c @@ -9,7 +9,7 @@ #include #include -/** +/* * pci_pf_stub_whitelist - White list of devices to bind pci-pf-stub onto * * This table provides the list of IDs this driver is supposed to bind diff --git a/drivers/pci/setup-bus.c b/drivers/pci/setup-bus.c index 9b94b1f16d80e..fb74d8eb80884 100644 --- a/drivers/pci/setup-bus.c +++ b/drivers/pci/setup-bus.c @@ -55,6 +55,7 @@ static void free_list(struct list_head *head) * @dev: Device to which the resource belongs * @res: Resource to be tracked * @add_size: Additional size to be optionally added to the resource + * @min_align: Minimum memory window alignment */ static int add_to_list(struct list_head *head, struct pci_dev *dev, struct resource *res, resource_size_t add_size, diff --git a/drivers/pci/vc.c b/drivers/pci/vc.c index 5486f8768c864..5fc59ac31145f 100644 --- a/drivers/pci/vc.c +++ b/drivers/pci/vc.c @@ -172,7 +172,6 @@ enable: * @dev: device * @pos: starting position of VC capability (VC/VC9/MFVC) * @save_state: buffer for save/restore - * @name: for error message * @save: if provided a buffer, this indicates what to do with it * * Walking Virtual Channel config space to size, save, or restore it -- GitLab From 6d10fc2b9bfeb34756c9f2bda135aa56105118d9 Mon Sep 17 00:00:00 2001 From: Colton Lewis Date: Sun, 19 Jul 2020 23:16:16 +0200 Subject: [PATCH 1293/1476] media: v4l2: Correct kernel-doc inconsistency Silence documentation build warnings by correcting kernel-doc comment v4l2_create_fwnode_links_to_pad and v4l2_create_fwnode_links functions. ./include/media/v4l2-mc.h:110: warning: Function parameter or member 'src_sd' not described in 'v4l2_create_fwnode_links_to_pad' ./include/media/v4l2-mc.h:110: warning: Function parameter or member 'sink' not described in 'v4l2_create_fwnode_links_to_pad' ./include/media/v4l2-mc.h:134: warning: Function parameter or member 'src_sd' not described in 'v4l2_create_fwnode_links' ./include/media/v4l2-mc.h:134: warning: Function parameter or member 'sink_sd' not described in 'v4l2_create_fwnode_links' Signed-off-by: Colton Lewis Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- include/media/v4l2-mc.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/include/media/v4l2-mc.h b/include/media/v4l2-mc.h index 246eed3986482..bdaa5f2f8ca21 100644 --- a/include/media/v4l2-mc.h +++ b/include/media/v4l2-mc.h @@ -89,8 +89,8 @@ int v4l_vb2q_enable_media_source(struct vb2_queue *q); * v4l2_create_fwnode_links_to_pad - Create fwnode-based links from a * source subdev to a sink subdev pad. * - * @src_sd - pointer to a source subdev - * @sink - pointer to a subdev sink pad + * @src_sd: pointer to a source subdev + * @sink: pointer to a subdev sink pad * * This function searches for fwnode endpoint connections from a source * subdevice to a single sink pad, and if suitable connections are found, @@ -113,8 +113,8 @@ int v4l2_create_fwnode_links_to_pad(struct v4l2_subdev *src_sd, * v4l2_create_fwnode_links - Create fwnode-based links from a source * subdev to a sink subdev. * - * @src_sd - pointer to a source subdevice - * @sink_sd - pointer to a sink subdevice + * @src_sd: pointer to a source subdevice + * @sink_sd: pointer to a sink subdevice * * This function searches for any and all fwnode endpoint connections * between source and sink subdevices, and translates them into media -- GitLab From 2f501169842c1626bf298b9bf497bf084635c6df Mon Sep 17 00:00:00 2001 From: Colton Lewis Date: Sun, 19 Jul 2020 23:16:24 +0200 Subject: [PATCH 1294/1476] media: v4l2: Correct kernel-doc inconsistency Silence documentation build warnings by correcting kernel-doc comment v4l2_subdev_get_fwnode_pad_1_to_1 function ./include/media/v4l2-subdev.h:1045: warning: Function parameter or member 'entity' not described in 'v4l2_subdev_get_fwnode_pad_1_to_1' ./include/media/v4l2-subdev.h:1045: warning: Function parameter or member 'endpoint' not described in 'v4l2_subdev_get_fwnode_pad_1_to_1' Signed-off-by: Colton Lewis Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- include/media/v4l2-subdev.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/media/v4l2-subdev.h b/include/media/v4l2-subdev.h index b855721879b82..d4e3b44cf14ce 100644 --- a/include/media/v4l2-subdev.h +++ b/include/media/v4l2-subdev.h @@ -1032,8 +1032,8 @@ static inline void *v4l2_get_subdev_hostdata(const struct v4l2_subdev *sd) * v4l2_subdev_get_fwnode_pad_1_to_1 - Get pad number from a subdev fwnode * endpoint, assuming 1:1 port:pad * - * @entity - Pointer to the subdev entity - * @endpoint - Pointer to a parsed fwnode endpoint + * @entity: Pointer to the subdev entity + * @endpoint: Pointer to a parsed fwnode endpoint * * This function can be used as the .get_fwnode_pad operation for * subdevices that map port numbers and pad indexes 1:1. If the endpoint -- GitLab From d20dcbb2ba45d8b7fa4a9cd67af8b42f1968e4a7 Mon Sep 17 00:00:00 2001 From: Jian-Jia Su Date: Mon, 20 Jul 2020 11:29:33 +0200 Subject: [PATCH 1295/1476] media: v4l2-dev/ioctl: Fix document for VIDIOC_QUERYCAP V4L2_CAP_VIDEO_M2M is documented as 0x00004000 V4L2_CAP_VIDEO_M2M_MPLANE is documented as 0x00008000 This is different from the definition in include/uapi/linux/videodev2.h Signed-off-by: Jian-Jia Su Reviewed-by: Helen Koike Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- Documentation/userspace-api/media/v4l/vidioc-querycap.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Documentation/userspace-api/media/v4l/vidioc-querycap.rst b/Documentation/userspace-api/media/v4l/vidioc-querycap.rst index 666ac4d420519..90347367ef06a 100644 --- a/Documentation/userspace-api/media/v4l/vidioc-querycap.rst +++ b/Documentation/userspace-api/media/v4l/vidioc-querycap.rst @@ -168,11 +168,11 @@ specification the ioctl returns an ``EINVAL`` error code. - The device supports the :ref:`multi-planar API ` through the :ref:`Video Output ` interface. * - ``V4L2_CAP_VIDEO_M2M`` - - 0x00004000 + - 0x00008000 - The device supports the single-planar API through the Video Memory-To-Memory interface. * - ``V4L2_CAP_VIDEO_M2M_MPLANE`` - - 0x00008000 + - 0x00004000 - The device supports the :ref:`multi-planar API ` through the Video Memory-To-Memory interface. * - ``V4L2_CAP_VIDEO_OVERLAY`` -- GitLab From 7aa87db5b81b0281c7493ab58e4f702525e85fe4 Mon Sep 17 00:00:00 2001 From: Vaibhav Gupta Date: Mon, 20 Jul 2020 12:27:01 +0200 Subject: [PATCH 1296/1476] media: saa7164: use generic power management The .suspend() and .resume() callbacks are not defined for this driver. Still, their power management structure follows the legacy framework. To bring it under the generic framework, simply remove the binding of callbacks from struct "pci_driver". Compile-tested only. Signed-off-by: Vaibhav Gupta Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/pci/saa7164/saa7164-core.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/media/pci/saa7164/saa7164-core.c b/drivers/media/pci/saa7164/saa7164-core.c index 126d085be9a7f..4b637891b79a3 100644 --- a/drivers/media/pci/saa7164/saa7164-core.c +++ b/drivers/media/pci/saa7164/saa7164-core.c @@ -1539,9 +1539,6 @@ static struct pci_driver saa7164_pci_driver = { .id_table = saa7164_pci_tbl, .probe = saa7164_initdev, .remove = saa7164_finidev, - /* TODO */ - .suspend = NULL, - .resume = NULL, }; static int __init saa7164_init(void) -- GitLab From e3083c3f369700cd1eec3de93b8d8ec0918ff778 Mon Sep 17 00:00:00 2001 From: Vaibhav Gupta Date: Tue, 21 Jul 2020 06:47:45 +0200 Subject: [PATCH 1297/1476] media: cafe-driver: use generic power management Drivers using legacy PM have to manage PCI states and device's PM states themselves. They also need to take care of configuration registers. With improved and powerful support of generic PM, PCI Core takes care of above mentioned, device-independent, jobs. This driver makes use of PCI helper functions like pci_save/restore_state() and pci_enable/disable_device() to do required operations. In generic mode, they are no longer needed. Change function parameter in both .suspend() and .resume() to "struct device*" type. Compile-tested only. Signed-off-by: Vaibhav Gupta Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- .../media/platform/marvell-ccic/cafe-driver.c | 31 +++++-------------- .../media/platform/marvell-ccic/mcam-core.c | 3 -- .../media/platform/marvell-ccic/mcam-core.h | 2 -- 3 files changed, 7 insertions(+), 29 deletions(-) diff --git a/drivers/media/platform/marvell-ccic/cafe-driver.c b/drivers/media/platform/marvell-ccic/cafe-driver.c index 9a09a10a3631d..58b9915ac7a40 100644 --- a/drivers/media/platform/marvell-ccic/cafe-driver.c +++ b/drivers/media/platform/marvell-ccic/cafe-driver.c @@ -604,44 +604,28 @@ static void cafe_pci_remove(struct pci_dev *pdev) } -#ifdef CONFIG_PM /* * Basic power management. */ -static int cafe_pci_suspend(struct pci_dev *pdev, pm_message_t state) +static int __maybe_unused cafe_pci_suspend(struct device *dev) { - struct v4l2_device *v4l2_dev = dev_get_drvdata(&pdev->dev); + struct v4l2_device *v4l2_dev = dev_get_drvdata(dev); struct cafe_camera *cam = to_cam(v4l2_dev); - int ret; - ret = pci_save_state(pdev); - if (ret) - return ret; mccic_suspend(&cam->mcam); - pci_disable_device(pdev); return 0; } -static int cafe_pci_resume(struct pci_dev *pdev) +static int __maybe_unused cafe_pci_resume(struct device *dev) { - struct v4l2_device *v4l2_dev = dev_get_drvdata(&pdev->dev); + struct v4l2_device *v4l2_dev = dev_get_drvdata(dev); struct cafe_camera *cam = to_cam(v4l2_dev); - int ret = 0; - pci_restore_state(pdev); - ret = pci_enable_device(pdev); - - if (ret) { - cam_warn(cam, "Unable to re-enable device on resume!\n"); - return ret; - } cafe_ctlr_init(&cam->mcam); return mccic_resume(&cam->mcam); } -#endif /* CONFIG_PM */ - static const struct pci_device_id cafe_ids[] = { { PCI_DEVICE(PCI_VENDOR_ID_MARVELL, PCI_DEVICE_ID_MARVELL_88ALP01_CCIC) }, @@ -650,15 +634,14 @@ static const struct pci_device_id cafe_ids[] = { MODULE_DEVICE_TABLE(pci, cafe_ids); +static SIMPLE_DEV_PM_OPS(cafe_pci_pm_ops, cafe_pci_suspend, cafe_pci_resume); + static struct pci_driver cafe_pci_driver = { .name = "cafe1000-ccic", .id_table = cafe_ids, .probe = cafe_pci_probe, .remove = cafe_pci_remove, -#ifdef CONFIG_PM - .suspend = cafe_pci_suspend, - .resume = cafe_pci_resume, -#endif + .driver.pm = &cafe_pci_pm_ops, }; diff --git a/drivers/media/platform/marvell-ccic/mcam-core.c b/drivers/media/platform/marvell-ccic/mcam-core.c index 326e79b8531c5..3d4242b8182ba 100644 --- a/drivers/media/platform/marvell-ccic/mcam-core.c +++ b/drivers/media/platform/marvell-ccic/mcam-core.c @@ -1969,8 +1969,6 @@ EXPORT_SYMBOL_GPL(mccic_shutdown); /* * Power management */ -#ifdef CONFIG_PM - void mccic_suspend(struct mcam_camera *cam) { mutex_lock(&cam->s_mutex); @@ -2017,7 +2015,6 @@ int mccic_resume(struct mcam_camera *cam) return ret; } EXPORT_SYMBOL_GPL(mccic_resume); -#endif /* CONFIG_PM */ MODULE_LICENSE("GPL v2"); MODULE_AUTHOR("Jonathan Corbet "); diff --git a/drivers/media/platform/marvell-ccic/mcam-core.h b/drivers/media/platform/marvell-ccic/mcam-core.h index 2e3a7567a76a3..b55545822fd2a 100644 --- a/drivers/media/platform/marvell-ccic/mcam-core.h +++ b/drivers/media/platform/marvell-ccic/mcam-core.h @@ -236,10 +236,8 @@ static inline void mcam_reg_set_bit(struct mcam_camera *cam, int mccic_register(struct mcam_camera *cam); int mccic_irq(struct mcam_camera *cam, unsigned int irqs); void mccic_shutdown(struct mcam_camera *cam); -#ifdef CONFIG_PM void mccic_suspend(struct mcam_camera *cam); int mccic_resume(struct mcam_camera *cam); -#endif /* * Register definitions for the m88alp01 camera interface. Offsets in bytes -- GitLab From 43663dc4f4300e6a4ff4dade9a17cef42e79232c Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Tue, 21 Jul 2020 23:32:29 +0200 Subject: [PATCH 1298/1476] media: meye: fix missing pm_mchip_mode field After commit 9c4a8489ab7c ("media: meye: use generic power management") the pm_mchip_mode field should now always be present, even if CONFIG_PM is not set. Signed-off-by: Hans Verkuil Reported-by: kernel test robot Fixes: 9c4a8489ab7c ("media: meye: use generic power management") Signed-off-by: Mauro Carvalho Chehab --- drivers/media/pci/meye/meye.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/media/pci/meye/meye.h b/drivers/media/pci/meye/meye.h index c957d9b55f894..5fa6552cf93d8 100644 --- a/drivers/media/pci/meye/meye.h +++ b/drivers/media/pci/meye/meye.h @@ -305,9 +305,7 @@ struct meye { u16 colour; struct meye_params params; /* additional parameters */ unsigned long in_use; /* set to 1 if the device is in use */ -#ifdef CONFIG_PM u8 pm_mchip_mode; /* old mchip mode */ -#endif }; #endif -- GitLab From 9bde425e70b6d6b49f3206a1d3817bccb92a893d Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Wed, 22 Jul 2020 14:38:48 +0200 Subject: [PATCH 1299/1476] media: allegro: fix an error pointer vs NULL check The allegro_mbox_init() function returns error pointers, it never returns NULL. Fixes: 94dc76560261 ("media: allegro: rework mbox handling") Signed-off-by: Dan Carpenter Reviewed-by: Michael Tretter Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/allegro-dvt/allegro-core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/media/allegro-dvt/allegro-core.c b/drivers/staging/media/allegro-dvt/allegro-core.c index 61beae1fca366..9f718f43282bc 100644 --- a/drivers/staging/media/allegro-dvt/allegro-core.c +++ b/drivers/staging/media/allegro-dvt/allegro-core.c @@ -2952,7 +2952,7 @@ static int allegro_mcu_hw_init(struct allegro_dev *dev, info->mailbox_size); dev->mbox_status = allegro_mbox_init(dev, info->mailbox_status, info->mailbox_size); - if (!dev->mbox_command || !dev->mbox_status) { + if (IS_ERR(dev->mbox_command) || IS_ERR(dev->mbox_status)) { v4l2_err(&dev->v4l2_dev, "failed to initialize mailboxes\n"); return -EIO; -- GitLab From dd4eddc4ba31fbf4554fc5fa12d3a553b50e1469 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Wed, 22 Jul 2020 14:39:14 +0200 Subject: [PATCH 1300/1476] media: mtk-mdp: Fix a refcounting bug on error in init We need to call of_node_put(comp->dev_node); on the error paths in this function. Fixes: c8eb2d7e8202 ("[media] media: Add Mediatek MDP Driver") Signed-off-by: Dan Carpenter Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/mtk-mdp/mtk_mdp_comp.c | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/drivers/media/platform/mtk-mdp/mtk_mdp_comp.c b/drivers/media/platform/mtk-mdp/mtk_mdp_comp.c index 362fff924aeff..b3426a551bead 100644 --- a/drivers/media/platform/mtk-mdp/mtk_mdp_comp.c +++ b/drivers/media/platform/mtk-mdp/mtk_mdp_comp.c @@ -57,6 +57,7 @@ int mtk_mdp_comp_init(struct device *dev, struct device_node *node, { struct device_node *larb_node; struct platform_device *larb_pdev; + int ret; int i; comp->dev_node = of_node_get(node); @@ -67,8 +68,8 @@ int mtk_mdp_comp_init(struct device *dev, struct device_node *node, if (IS_ERR(comp->clk[i])) { if (PTR_ERR(comp->clk[i]) != -EPROBE_DEFER) dev_err(dev, "Failed to get clock\n"); - - return PTR_ERR(comp->clk[i]); + ret = PTR_ERR(comp->clk[i]); + goto put_dev; } /* Only RDMA needs two clocks */ @@ -87,20 +88,27 @@ int mtk_mdp_comp_init(struct device *dev, struct device_node *node, if (!larb_node) { dev_err(dev, "Missing mediadek,larb phandle in %pOF node\n", node); - return -EINVAL; + ret = -EINVAL; + goto put_dev; } larb_pdev = of_find_device_by_node(larb_node); if (!larb_pdev) { dev_warn(dev, "Waiting for larb device %pOF\n", larb_node); of_node_put(larb_node); - return -EPROBE_DEFER; + ret = -EPROBE_DEFER; + goto put_dev; } of_node_put(larb_node); comp->larb_dev = &larb_pdev->dev; return 0; + +put_dev: + of_node_put(comp->dev_node); + + return ret; } void mtk_mdp_comp_deinit(struct device *dev, struct mtk_mdp_comp *comp) -- GitLab From 04320b3ef90bef04df6672e3a98a266b88c35b55 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Wed, 22 Jul 2020 15:09:03 +0200 Subject: [PATCH 1301/1476] media: allegro: fix potential null dereference on header The pointer header is an alias to msg and msg is being null checked. However, if msg is null then header is also null and this can lead to a null pointer dereference on the assignment type = header->type. Fix this just using header->type after the null check and removing the need for type as it is only used once. Addresses-Coverity: ("Dereference before null check") Fixes: 3de16839669f ("media: allegro: add explicit mail encoding and decoding") Signed-off-by: Colin Ian King Reviewed-by: Michael Tretter Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/allegro-dvt/allegro-mail.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/staging/media/allegro-dvt/allegro-mail.c b/drivers/staging/media/allegro-dvt/allegro-mail.c index 4ac65de124639..9286d21623772 100644 --- a/drivers/staging/media/allegro-dvt/allegro-mail.c +++ b/drivers/staging/media/allegro-dvt/allegro-mail.c @@ -462,13 +462,12 @@ allegro_dec_encode_frame(struct mcu_msg_encode_frame_response *msg, u32 *src) ssize_t allegro_encode_mail(u32 *dst, void *msg) { const struct mcu_msg_header *header = msg; - enum mcu_msg_type type = header->type; ssize_t size; if (!msg || !dst) return -EINVAL; - switch (type) { + switch (header->type) { case MCU_MSG_TYPE_INIT: size = allegro_enc_init(&dst[1], msg); break; -- GitLab From 670f7ef5f0fccd5b6dd7da80a0f7ec6b6cf9aafa Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Wed, 22 Jul 2020 17:16:30 +0200 Subject: [PATCH 1302/1476] media: radio: remove redundant assignment to variable retval The variable retval is being initialized with a value that is never read and it is being updated later with a new value. The initialization is redundant and can be removed. Addresses-Coverity: ("Unused value") Signed-off-by: Colin Ian King Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/radio/si4713/radio-usb-si4713.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/radio/si4713/radio-usb-si4713.c b/drivers/media/radio/si4713/radio-usb-si4713.c index 33274189c83c3..2cf36c8abddea 100644 --- a/drivers/media/radio/si4713/radio-usb-si4713.c +++ b/drivers/media/radio/si4713/radio-usb-si4713.c @@ -414,7 +414,7 @@ static int usb_si4713_probe(struct usb_interface *intf, struct si4713_usb_device *radio; struct i2c_adapter *adapter; struct v4l2_subdev *sd; - int retval = -ENOMEM; + int retval; dev_info(&intf->dev, "Si4713 development board discovered: (%04X:%04X)\n", id->idVendor, id->idProduct); -- GitLab From 602649eadaa0c977e362e641f51ec306bc1d365d Mon Sep 17 00:00:00 2001 From: Evgeny Novikov Date: Thu, 23 Jul 2020 19:04:53 +0200 Subject: [PATCH 1303/1476] media: davinci: vpif_capture: fix potential double free In case of errors vpif_probe_complete() releases memory for vpif_obj.sd and unregisters the V4L2 device. But then this is done again by vpif_probe() itself. The patch removes the cleaning from vpif_probe_complete(). Found by Linux Driver Verification project (linuxtesting.org). Signed-off-by: Evgeny Novikov Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/davinci/vpif_capture.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/media/platform/davinci/vpif_capture.c b/drivers/media/platform/davinci/vpif_capture.c index d9ec439faefa6..72a0e94e2e21a 100644 --- a/drivers/media/platform/davinci/vpif_capture.c +++ b/drivers/media/platform/davinci/vpif_capture.c @@ -1482,8 +1482,6 @@ probe_out: /* Unregister video device */ video_unregister_device(&ch->video_dev); } - kfree(vpif_obj.sd); - v4l2_device_unregister(&vpif_obj.v4l2_dev); return err; } -- GitLab From f45882cfb152f5d3a421fd58f177f227e44843b9 Mon Sep 17 00:00:00 2001 From: Evgeny Novikov Date: Thu, 23 Jul 2020 19:46:51 +0200 Subject: [PATCH 1304/1476] media: camss: fix memory leaks on error handling paths in probe camss_probe() does not free camss on error handling paths. The patch introduces an additional error label for this purpose. Besides, it removes call of v4l2_async_notifier_cleanup() from camss_of_parse_ports() since its caller, camss_probe(), cleans up all its resources itself. Found by Linux Driver Verification project (linuxtesting.org). Signed-off-by: Evgeny Novikov Co-developed-by: Anton Vasilyev Signed-off-by: Anton Vasilyev Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/qcom/camss/camss.c | 30 +++++++++++++++-------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/drivers/media/platform/qcom/camss/camss.c b/drivers/media/platform/qcom/camss/camss.c index 3fdc9f964a3c6..2483641799dfb 100644 --- a/drivers/media/platform/qcom/camss/camss.c +++ b/drivers/media/platform/qcom/camss/camss.c @@ -504,7 +504,6 @@ static int camss_of_parse_ports(struct camss *camss) return num_subdevs; err_cleanup: - v4l2_async_notifier_cleanup(&camss->notifier); of_node_put(node); return ret; } @@ -835,29 +834,38 @@ static int camss_probe(struct platform_device *pdev) camss->csid_num = 4; camss->vfe_num = 2; } else { - return -EINVAL; + ret = -EINVAL; + goto err_free; } camss->csiphy = devm_kcalloc(dev, camss->csiphy_num, sizeof(*camss->csiphy), GFP_KERNEL); - if (!camss->csiphy) - return -ENOMEM; + if (!camss->csiphy) { + ret = -ENOMEM; + goto err_free; + } camss->csid = devm_kcalloc(dev, camss->csid_num, sizeof(*camss->csid), GFP_KERNEL); - if (!camss->csid) - return -ENOMEM; + if (!camss->csid) { + ret = -ENOMEM; + goto err_free; + } camss->vfe = devm_kcalloc(dev, camss->vfe_num, sizeof(*camss->vfe), GFP_KERNEL); - if (!camss->vfe) - return -ENOMEM; + if (!camss->vfe) { + ret = -ENOMEM; + goto err_free; + } v4l2_async_notifier_init(&camss->notifier); num_subdevs = camss_of_parse_ports(camss); - if (num_subdevs < 0) - return num_subdevs; + if (num_subdevs < 0) { + ret = num_subdevs; + goto err_cleanup; + } ret = camss_init_subdevices(camss); if (ret < 0) @@ -936,6 +944,8 @@ err_register_entities: v4l2_device_unregister(&camss->v4l2_dev); err_cleanup: v4l2_async_notifier_cleanup(&camss->notifier); +err_free: + kfree(camss); return ret; } -- GitLab From 10de795a5addd1962406796a6e13ba6cc0fc6bee Mon Sep 17 00:00:00 2001 From: Muchun Song Date: Thu, 6 Aug 2020 01:20:46 +0800 Subject: [PATCH 1305/1476] kprobes: Fix compiler warning for !CONFIG_KPROBES_ON_FTRACE Fix compiler warning(as show below) for !CONFIG_KPROBES_ON_FTRACE. kernel/kprobes.c: In function 'kill_kprobe': kernel/kprobes.c:1116:33: warning: statement with no effect [-Wunused-value] 1116 | #define disarm_kprobe_ftrace(p) (-ENODEV) | ^ kernel/kprobes.c:2154:3: note: in expansion of macro 'disarm_kprobe_ftrace' 2154 | disarm_kprobe_ftrace(p); Link: https://lore.kernel.org/r/20200805142136.0331f7ea@canb.auug.org.au Link: https://lkml.kernel.org/r/20200805172046.19066-1-songmuchun@bytedance.com Reported-by: Stephen Rothwell Fixes: 0cb2f1372baa ("kprobes: Fix NULL pointer dereference at kprobe_ftrace_handler") Acked-by: Masami Hiramatsu Acked-by: John Fastabend Signed-off-by: Muchun Song Signed-off-by: Steven Rostedt (VMware) --- kernel/kprobes.c | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/kernel/kprobes.c b/kernel/kprobes.c index 07bf03fcf574a..66d14107968d5 100644 --- a/kernel/kprobes.c +++ b/kernel/kprobes.c @@ -1079,9 +1079,20 @@ static int disarm_kprobe_ftrace(struct kprobe *p) ipmodify ? &kprobe_ipmodify_enabled : &kprobe_ftrace_enabled); } #else /* !CONFIG_KPROBES_ON_FTRACE */ -#define prepare_kprobe(p) arch_prepare_kprobe(p) -#define arm_kprobe_ftrace(p) (-ENODEV) -#define disarm_kprobe_ftrace(p) (-ENODEV) +static inline int prepare_kprobe(struct kprobe *p) +{ + return arch_prepare_kprobe(p); +} + +static inline int arm_kprobe_ftrace(struct kprobe *p) +{ + return -ENODEV; +} + +static inline int disarm_kprobe_ftrace(struct kprobe *p) +{ + return -ENODEV; +} #endif /* Arm a kprobe with text_mutex */ -- GitLab From 0f5d0a4c01cc1b7b0590baa18809a96d45a1e278 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Fri, 7 Aug 2020 09:05:02 -0700 Subject: [PATCH 1306/1476] thermal: don't make THERMAL_NETLINK 'default y' We just don't do that. "default y" is for things that are needed for compatibility (when an old feature is made unconditional), or for things that are basically part of the infrastructure of a platform. And it can possibly be used for questions that don't enable code on their own, but are used to enable or disable a whole slew of other questions. A new feature that people aren't using is never 'default y', unless it cures cancer or ends world hunger. Signed-off-by: Linus Torvalds --- drivers/thermal/Kconfig | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig index 13991d68c8443..a2e710d464328 100644 --- a/drivers/thermal/Kconfig +++ b/drivers/thermal/Kconfig @@ -20,7 +20,6 @@ if THERMAL config THERMAL_NETLINK bool "Thermal netlink management" depends on NET - default y help The thermal framework has a netlink interface to do thermal zones discovery, temperature readings and events such as -- GitLab From 9bceb80b3cc483e6763c39a4928402fa82815d3e Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Fri, 7 Aug 2020 07:45:21 -0700 Subject: [PATCH 1307/1476] arm64: kaslr: Use standard early random function Commit 585524081ecd ("random: random.h should include archrandom.h, not the other way around") tries to fix a problem with recursive inclusion of linux/random.h and arch/archrandom.h for arm64. Unfortunately, this results in the following compile error if ARCH_RANDOM is disabled. arch/arm64/kernel/kaslr.c: In function 'kaslr_early_init': arch/arm64/kernel/kaslr.c:128:6: error: implicit declaration of function '__early_cpu_has_rndr'; did you mean '__early_pfn_to_nid'? [-Werror=implicit-function-declaration] if (__early_cpu_has_rndr()) { ^~~~~~~~~~~~~~~~~~~~ __early_pfn_to_nid arch/arm64/kernel/kaslr.c:131:7: error: implicit declaration of function '__arm64_rndr' [-Werror=implicit-function-declaration] if (__arm64_rndr(&raw)) ^~~~~~~~~~~~ The problem is that arch/archrandom.h is only included from linux/random.h if ARCH_RANDOM is enabled. If not, __arm64_rndr() and __early_cpu_has_rndr() are undeclared, causing the problem. Use arch_get_random_seed_long_early() instead of arm64 specific functions to solve the problem. Reported-by: Qian Cai Fixes: 585524081ecd ("random: random.h should include archrandom.h, not the other way around") Cc: Qian Cai Cc: Mark Brown Reviewed-by: Mark Rutland Reviewed-by: Mark Brown Tested-by: Mark Brown Signed-off-by: Guenter Roeck Signed-off-by: Linus Torvalds --- arch/arm64/kernel/kaslr.c | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/arch/arm64/kernel/kaslr.c b/arch/arm64/kernel/kaslr.c index 9ded4237e1c16..b181e0544b799 100644 --- a/arch/arm64/kernel/kaslr.c +++ b/arch/arm64/kernel/kaslr.c @@ -84,6 +84,7 @@ u64 __init kaslr_early_init(u64 dt_phys) void *fdt; u64 seed, offset, mask, module_range; const u8 *cmdline, *str; + unsigned long raw; int size; /* @@ -122,15 +123,12 @@ u64 __init kaslr_early_init(u64 dt_phys) } /* - * Mix in any entropy obtainable architecturally, open coded - * since this runs extremely early. + * Mix in any entropy obtainable architecturally if enabled + * and supported. */ - if (__early_cpu_has_rndr()) { - unsigned long raw; - if (__arm64_rndr(&raw)) - seed ^= raw; - } + if (arch_get_random_seed_long_early(&raw)) + seed ^= raw; if (!seed) { kaslr_status = KASLR_DISABLED_NO_SEED; -- GitLab From 995909a4e22bc7b3ea3a71388cbb62ffebd76e7b Mon Sep 17 00:00:00 2001 From: Joerg Roedel Date: Fri, 7 Aug 2020 10:40:13 +0200 Subject: [PATCH 1308/1476] x86/mm/64: Do not dereference non-present PGD entries The code for preallocate_vmalloc_pages() was written under the assumption that the p4d_offset() and pud_offset() functions will perform present checks before dereferencing the parent entries. This assumption is wrong an leads to a bug in the code which causes the physical address found in the PGD be used as a page-table page, even if the PGD is not present. So the code flow currently is: pgd = pgd_offset_k(addr); p4d = p4d_offset(pgd, addr); if (p4d_none(*p4d)) p4d = p4d_alloc(&init_mm, pgd, addr); This lacks a check for pgd_none() at least, the correct flow would be: pgd = pgd_offset_k(addr); if (pgd_none(*pgd)) p4d = p4d_alloc(&init_mm, pgd, addr); else p4d = p4d_offset(pgd, addr); But this is the same flow that the p4d_alloc() and the pud_alloc() functions use internally, so there is no need to duplicate them. Remove the p?d_none() checks from the function and just call into p4d_alloc() and pud_alloc() to correctly pre-allocate the PGD entries. Reported-and-tested-by: Jason A. Donenfeld Reviewed-by: Mike Rapoport Fixes: 6eb82f994026 ("x86/mm: Pre-allocate P4D/PUD pages for vmalloc area") Signed-off-by: Joerg Roedel Signed-off-by: Linus Torvalds --- arch/x86/mm/init_64.c | 31 +++++++++++++------------------ 1 file changed, 13 insertions(+), 18 deletions(-) diff --git a/arch/x86/mm/init_64.c b/arch/x86/mm/init_64.c index 3f4e29a78f2ba..449e071240e14 100644 --- a/arch/x86/mm/init_64.c +++ b/arch/x86/mm/init_64.c @@ -1253,28 +1253,23 @@ static void __init preallocate_vmalloc_pages(void) p4d_t *p4d; pud_t *pud; - p4d = p4d_offset(pgd, addr); - if (p4d_none(*p4d)) { - /* Can only happen with 5-level paging */ - p4d = p4d_alloc(&init_mm, pgd, addr); - if (!p4d) { - lvl = "p4d"; - goto failed; - } - } + lvl = "p4d"; + p4d = p4d_alloc(&init_mm, pgd, addr); + if (!p4d) + goto failed; + /* + * With 5-level paging the P4D level is not folded. So the PGDs + * are now populated and there is no need to walk down to the + * PUD level. + */ if (pgtable_l5_enabled()) continue; - pud = pud_offset(p4d, addr); - if (pud_none(*pud)) { - /* Ends up here only with 4-level paging */ - pud = pud_alloc(&init_mm, p4d, addr); - if (!pud) { - lvl = "pud"; - goto failed; - } - } + lvl = "pud"; + pud = pud_alloc(&init_mm, p4d, addr); + if (!pud) + goto failed; } return; -- GitLab From 11990a5bd7e558e9203c1070fc52fb6f0488e75b Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Thu, 6 Aug 2020 14:15:23 -0700 Subject: [PATCH 1309/1476] module: Correctly truncate sysfs sections output The only-root-readable /sys/module/$module/sections/$section files did not truncate their output to the available buffer size. While most paths into the kernfs read handlers end up using PAGE_SIZE buffers, it's possible to get there through other paths (e.g. splice, sendfile). Actually limit the output to the "count" passed into the read function, and report it back correctly. *sigh* Reported-by: kernel test robot Link: https://lore.kernel.org/lkml/20200805002015.GE23458@shao2-debian Fixes: ed66f991bb19 ("module: Refactor section attr into bin attribute") Cc: stable@vger.kernel.org Reviewed-by: Greg Kroah-Hartman Acked-by: Jessica Yu Signed-off-by: Kees Cook --- kernel/module.c | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/kernel/module.c b/kernel/module.c index aa183c9ac0a25..08c46084d8cca 100644 --- a/kernel/module.c +++ b/kernel/module.c @@ -1520,18 +1520,34 @@ struct module_sect_attrs { struct module_sect_attr attrs[]; }; +#define MODULE_SECT_READ_SIZE (3 /* "0x", "\n" */ + (BITS_PER_LONG / 4)) static ssize_t module_sect_read(struct file *file, struct kobject *kobj, struct bin_attribute *battr, char *buf, loff_t pos, size_t count) { struct module_sect_attr *sattr = container_of(battr, struct module_sect_attr, battr); + char bounce[MODULE_SECT_READ_SIZE + 1]; + size_t wrote; if (pos != 0) return -EINVAL; - return sprintf(buf, "0x%px\n", - kallsyms_show_value(file->f_cred) ? (void *)sattr->address : NULL); + /* + * Since we're a binary read handler, we must account for the + * trailing NUL byte that sprintf will write: if "buf" is + * too small to hold the NUL, or the NUL is exactly the last + * byte, the read will look like it got truncated by one byte. + * Since there is no way to ask sprintf nicely to not write + * the NUL, we have to use a bounce buffer. + */ + wrote = scnprintf(bounce, sizeof(bounce), "0x%px\n", + kallsyms_show_value(file->f_cred) + ? (void *)sattr->address : NULL); + count = min(count, wrote); + memcpy(buf, bounce, count); + + return count; } static void free_sect_attrs(struct module_sect_attrs *sect_attrs) @@ -1580,7 +1596,7 @@ static void add_sect_attrs(struct module *mod, const struct load_info *info) goto out; sect_attrs->nsections++; sattr->battr.read = module_sect_read; - sattr->battr.size = 3 /* "0x", "\n" */ + (BITS_PER_LONG / 4); + sattr->battr.size = MODULE_SECT_READ_SIZE; sattr->battr.attr.mode = 0400; *(gattr++) = &(sattr++)->battr; } -- GitLab From 9af47666cb0f331bfcd76799ee368cdfcb00882c Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Thu, 6 Aug 2020 23:01:09 -0700 Subject: [PATCH 1310/1476] selftests: splice: Check behavior of full and short splices In order to help catch regressions in splice vs read behavior in certain special files, test a few with various different kinds of internal kernel helpers. Cc: Shuah Khan Signed-off-by: Kees Cook --- tools/testing/selftests/splice/.gitignore | 1 + tools/testing/selftests/splice/Makefile | 4 +- tools/testing/selftests/splice/config | 1 + tools/testing/selftests/splice/settings | 1 + .../selftests/splice/short_splice_read.sh | 56 ++++++++++++++++++ tools/testing/selftests/splice/splice_read.c | 57 +++++++++++++++++++ 6 files changed, 118 insertions(+), 2 deletions(-) create mode 100644 tools/testing/selftests/splice/config create mode 100644 tools/testing/selftests/splice/settings create mode 100755 tools/testing/selftests/splice/short_splice_read.sh create mode 100644 tools/testing/selftests/splice/splice_read.c diff --git a/tools/testing/selftests/splice/.gitignore b/tools/testing/selftests/splice/.gitignore index d5a2da428752a..be8266f5d04c0 100644 --- a/tools/testing/selftests/splice/.gitignore +++ b/tools/testing/selftests/splice/.gitignore @@ -1,2 +1,3 @@ # SPDX-License-Identifier: GPL-2.0-only default_file_splice_read +splice_read diff --git a/tools/testing/selftests/splice/Makefile b/tools/testing/selftests/splice/Makefile index e519b159b60d8..541cd826d5a55 100644 --- a/tools/testing/selftests/splice/Makefile +++ b/tools/testing/selftests/splice/Makefile @@ -1,5 +1,5 @@ # SPDX-License-Identifier: GPL-2.0 -TEST_PROGS := default_file_splice_read.sh -TEST_GEN_PROGS_EXTENDED := default_file_splice_read +TEST_PROGS := default_file_splice_read.sh short_splice_read.sh +TEST_GEN_PROGS_EXTENDED := default_file_splice_read splice_read include ../lib.mk diff --git a/tools/testing/selftests/splice/config b/tools/testing/selftests/splice/config new file mode 100644 index 0000000000000..058c928368b89 --- /dev/null +++ b/tools/testing/selftests/splice/config @@ -0,0 +1 @@ +CONFIG_TEST_LKM=m diff --git a/tools/testing/selftests/splice/settings b/tools/testing/selftests/splice/settings new file mode 100644 index 0000000000000..89cedfc0d12b1 --- /dev/null +++ b/tools/testing/selftests/splice/settings @@ -0,0 +1 @@ +timeout=5 diff --git a/tools/testing/selftests/splice/short_splice_read.sh b/tools/testing/selftests/splice/short_splice_read.sh new file mode 100755 index 0000000000000..7810d3589d9ab --- /dev/null +++ b/tools/testing/selftests/splice/short_splice_read.sh @@ -0,0 +1,56 @@ +#!/bin/sh +# SPDX-License-Identifier: GPL-2.0 +set -e + +ret=0 + +do_splice() +{ + filename="$1" + bytes="$2" + expected="$3" + + out=$(./splice_read "$filename" "$bytes" | cat) + if [ "$out" = "$expected" ] ; then + echo "ok: $filename $bytes" + else + echo "FAIL: $filename $bytes" + ret=1 + fi +} + +test_splice() +{ + filename="$1" + + full=$(cat "$filename") + two=$(echo "$full" | grep -m1 . | cut -c-2) + + # Make sure full splice has the same contents as a standard read. + do_splice "$filename" 4096 "$full" + + # Make sure a partial splice see the first two characters. + do_splice "$filename" 2 "$two" +} + +# proc_single_open(), seq_read() +test_splice /proc/$$/limits +# special open, seq_read() +test_splice /proc/$$/comm + +# proc_handler, proc_dointvec_minmax +test_splice /proc/sys/fs/nr_open +# proc_handler, proc_dostring +test_splice /proc/sys/kernel/modprobe +# proc_handler, special read +test_splice /proc/sys/kernel/version + +if ! [ -d /sys/module/test_module/sections ] ; then + modprobe test_module +fi +# kernfs, attr +test_splice /sys/module/test_module/coresize +# kernfs, binattr +test_splice /sys/module/test_module/sections/.init.text + +exit $ret diff --git a/tools/testing/selftests/splice/splice_read.c b/tools/testing/selftests/splice/splice_read.c new file mode 100644 index 0000000000000..46dae6a25cfb9 --- /dev/null +++ b/tools/testing/selftests/splice/splice_read.c @@ -0,0 +1,57 @@ +// SPDX-License-Identifier: GPL-2.0 +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include + +int main(int argc, char *argv[]) +{ + int fd; + size_t size; + ssize_t spliced; + + if (argc < 2) { + fprintf(stderr, "Usage: %s INPUT [BYTES]\n", argv[0]); + return EXIT_FAILURE; + } + + fd = open(argv[1], O_RDONLY); + if (fd < 0) { + perror(argv[1]); + return EXIT_FAILURE; + } + + if (argc == 3) + size = atol(argv[2]); + else { + struct stat statbuf; + + if (fstat(fd, &statbuf) < 0) { + perror(argv[1]); + return EXIT_FAILURE; + } + + if (statbuf.st_size > INT_MAX) { + fprintf(stderr, "%s: Too big\n", argv[1]); + return EXIT_FAILURE; + } + + size = statbuf.st_size; + } + + /* splice(2) file to stdout. */ + spliced = splice(fd, NULL, STDOUT_FILENO, NULL, + size, SPLICE_F_MOVE); + if (spliced < 0) { + perror("splice"); + return EXIT_FAILURE; + } + + close(fd); + return EXIT_SUCCESS; +} -- GitLab From c1a06df6ebf6ca98fb7a672fe447c7469d6c1968 Mon Sep 17 00:00:00 2001 From: Ralph Campbell Date: Thu, 6 Aug 2020 23:17:09 -0700 Subject: [PATCH 1311/1476] mm/migrate: fix migrate_pgmap_owner w/o CONFIG_MMU_NOTIFIER On x86_64, when CONFIG_MMU_NOTIFIER is not set/enabled, there is a compiler error: mm/migrate.c: In function 'migrate_vma_collect': mm/migrate.c:2481:7: error: 'struct mmu_notifier_range' has no member named 'migrate_pgmap_owner' range.migrate_pgmap_owner = migrate->pgmap_owner; ^ Fixes: 998427b3ad2c ("mm/notifier: add migration invalidation type") Reported-by: Randy Dunlap Signed-off-by: Ralph Campbell Signed-off-by: Andrew Morton Tested-by: Randy Dunlap Acked-by: Randy Dunlap Cc: Jerome Glisse Cc: John Hubbard Cc: Christoph Hellwig Cc: "Jason Gunthorpe" Link: http://lkml.kernel.org/r/20200806193353.7124-1-rcampbell@nvidia.com Signed-off-by: Linus Torvalds --- include/linux/mmu_notifier.h | 13 +++++++++++++ mm/migrate.c | 6 +++--- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/include/linux/mmu_notifier.h b/include/linux/mmu_notifier.h index c6f0708195cdd..b8200782dedeb 100644 --- a/include/linux/mmu_notifier.h +++ b/include/linux/mmu_notifier.h @@ -521,6 +521,16 @@ static inline void mmu_notifier_range_init(struct mmu_notifier_range *range, range->flags = flags; } +static inline void mmu_notifier_range_init_migrate( + struct mmu_notifier_range *range, unsigned int flags, + struct vm_area_struct *vma, struct mm_struct *mm, + unsigned long start, unsigned long end, void *pgmap) +{ + mmu_notifier_range_init(range, MMU_NOTIFY_MIGRATE, flags, vma, mm, + start, end); + range->migrate_pgmap_owner = pgmap; +} + #define ptep_clear_flush_young_notify(__vma, __address, __ptep) \ ({ \ int __young; \ @@ -645,6 +655,9 @@ static inline void _mmu_notifier_range_init(struct mmu_notifier_range *range, #define mmu_notifier_range_init(range,event,flags,vma,mm,start,end) \ _mmu_notifier_range_init(range, start, end) +#define mmu_notifier_range_init_migrate(range, flags, vma, mm, start, end, \ + pgmap) \ + _mmu_notifier_range_init(range, start, end) static inline bool mmu_notifier_range_blockable(const struct mmu_notifier_range *range) diff --git a/mm/migrate.c b/mm/migrate.c index 4fcc465736ff7..d179657f8685c 100644 --- a/mm/migrate.c +++ b/mm/migrate.c @@ -2386,9 +2386,9 @@ static void migrate_vma_collect(struct migrate_vma *migrate) * that the registered device driver can skip invalidating device * private page mappings that won't be migrated. */ - mmu_notifier_range_init(&range, MMU_NOTIFY_MIGRATE, 0, migrate->vma, - migrate->vma->vm_mm, migrate->start, migrate->end); - range.migrate_pgmap_owner = migrate->pgmap_owner; + mmu_notifier_range_init_migrate(&range, 0, migrate->vma, + migrate->vma->vm_mm, migrate->start, migrate->end, + migrate->pgmap_owner); mmu_notifier_invalidate_range_start(&range); walk_page_range(migrate->vma->vm_mm, migrate->start, migrate->end, -- GitLab From 4a93025cbe4a0b19d1a25a2d763a3d2018bad0d9 Mon Sep 17 00:00:00 2001 From: David Hildenbrand Date: Thu, 6 Aug 2020 23:17:13 -0700 Subject: [PATCH 1312/1476] mm/shuffle: don't move pages between zones and don't read garbage memmaps Especially with memory hotplug, we can have offline sections (with a garbage memmap) and overlapping zones. We have to make sure to only touch initialized memmaps (online sections managed by the buddy) and that the zone matches, to not move pages between zones. To test if this can actually happen, I added a simple BUG_ON(page_zone(page_i) != page_zone(page_j)); right before the swap. When hotplugging a 256M DIMM to a 4G x86-64 VM and onlining the first memory block "online_movable" and the second memory block "online_kernel", it will trigger the BUG, as both zones (NORMAL and MOVABLE) overlap. This might result in all kinds of weird situations (e.g., double allocations, list corruptions, unmovable allocations ending up in the movable zone). Fixes: e900a918b098 ("mm: shuffle initial free memory to improve memory-side-cache utilization") Signed-off-by: David Hildenbrand Signed-off-by: Andrew Morton Reviewed-by: Wei Yang Acked-by: Michal Hocko Acked-by: Dan Williams Cc: Andrew Morton Cc: Johannes Weiner Cc: Michal Hocko Cc: Minchan Kim Cc: Huang Ying Cc: Wei Yang Cc: Mel Gorman Cc: [5.2+] Link: http://lkml.kernel.org/r/20200624094741.9918-2-david@redhat.com Signed-off-by: Linus Torvalds --- mm/shuffle.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/mm/shuffle.c b/mm/shuffle.c index 44406d9977c77..dd13ab851b3ee 100644 --- a/mm/shuffle.c +++ b/mm/shuffle.c @@ -58,25 +58,25 @@ module_param_call(shuffle, shuffle_store, shuffle_show, &shuffle_param, 0400); * For two pages to be swapped in the shuffle, they must be free (on a * 'free_area' lru), have the same order, and have the same migratetype. */ -static struct page * __meminit shuffle_valid_page(unsigned long pfn, int order) +static struct page * __meminit shuffle_valid_page(struct zone *zone, + unsigned long pfn, int order) { - struct page *page; + struct page *page = pfn_to_online_page(pfn); /* * Given we're dealing with randomly selected pfns in a zone we * need to ask questions like... */ - /* ...is the pfn even in the memmap? */ - if (!pfn_valid_within(pfn)) + /* ... is the page managed by the buddy? */ + if (!page) return NULL; - /* ...is the pfn in a present section or a hole? */ - if (!pfn_in_present_section(pfn)) + /* ... is the page assigned to the same zone? */ + if (page_zone(page) != zone) return NULL; /* ...is the page free and currently on a free_area list? */ - page = pfn_to_page(pfn); if (!PageBuddy(page)) return NULL; @@ -123,7 +123,7 @@ void __meminit __shuffle_zone(struct zone *z) * page_j randomly selected in the span @zone_start_pfn to * @spanned_pages. */ - page_i = shuffle_valid_page(i, order); + page_i = shuffle_valid_page(z, i, order); if (!page_i) continue; @@ -137,7 +137,7 @@ void __meminit __shuffle_zone(struct zone *z) j = z->zone_start_pfn + ALIGN_DOWN(get_random_long() % z->spanned_pages, order_pages); - page_j = shuffle_valid_page(j, order); + page_j = shuffle_valid_page(z, j, order); if (page_j && page_j != page_i) break; } -- GitLab From 38cf307c1f2011d413750c5acb725456f47d9172 Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Thu, 6 Aug 2020 23:17:16 -0700 Subject: [PATCH 1313/1476] mm: fix kthread_use_mm() vs TLB invalidate For SMP systems using IPI based TLB invalidation, looking at current->active_mm is entirely reasonable. This then presents the following race condition: CPU0 CPU1 flush_tlb_mm(mm) use_mm(mm) tsk->active_mm = mm; if (tsk->active_mm == mm) // flush TLBs switch_mm(old_mm,mm,tsk); Where it is possible the IPI flushed the TLBs for @old_mm, not @mm, because the IPI lands before we actually switched. Avoid this by disabling IRQs across changing ->active_mm and switch_mm(). Of the (SMP) architectures that have IPI based TLB invalidate: Alpha - checks active_mm ARC - ASID specific IA64 - checks active_mm MIPS - ASID specific flush OpenRISC - shoots down world PARISC - shoots down world SH - ASID specific SPARC - ASID specific x86 - N/A xtensa - checks active_mm So at the very least Alpha, IA64 and Xtensa are suspect. On top of this, for scheduler consistency we need at least preemption disabled across changing tsk->mm and doing switch_mm(), which is currently provided by task_lock(), but that's not sufficient for PREEMPT_RT. [akpm@linux-foundation.org: add comment] Reported-by: Andy Lutomirski Signed-off-by: Peter Zijlstra (Intel) Signed-off-by: Andrew Morton Cc: Nicholas Piggin Cc: Jens Axboe Cc: Kees Cook Cc: Jann Horn Cc: Will Deacon Cc: Christoph Hellwig Cc: Mathieu Desnoyers Cc: Link: http://lkml.kernel.org/r/20200721154106.GE10769@hirez.programming.kicks-ass.net Signed-off-by: Linus Torvalds --- kernel/kthread.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/kernel/kthread.c b/kernel/kthread.c index 1d9e2fdfd67ae..1c8964feeb013 100644 --- a/kernel/kthread.c +++ b/kernel/kthread.c @@ -1241,13 +1241,16 @@ void kthread_use_mm(struct mm_struct *mm) WARN_ON_ONCE(tsk->mm); task_lock(tsk); + /* Hold off tlb flush IPIs while switching mm's */ + local_irq_disable(); active_mm = tsk->active_mm; if (active_mm != mm) { mmgrab(mm); tsk->active_mm = mm; } tsk->mm = mm; - switch_mm(active_mm, mm, tsk); + switch_mm_irqs_off(active_mm, mm, tsk); + local_irq_enable(); task_unlock(tsk); #ifdef finish_arch_post_lock_switch finish_arch_post_lock_switch(); @@ -1276,9 +1279,11 @@ void kthread_unuse_mm(struct mm_struct *mm) task_lock(tsk); sync_mm_rss(mm); + local_irq_disable(); tsk->mm = NULL; /* active_mm is still 'mm' */ enter_lazy_tlb(mm, tsk); + local_irq_enable(); task_unlock(tsk); } EXPORT_SYMBOL_GPL(kthread_unuse_mm); -- GitLab From 4ca1085c9573ea08767521dabce62456e3fc2fd0 Mon Sep 17 00:00:00 2001 From: Ilias Stamatis Date: Thu, 6 Aug 2020 23:17:19 -0700 Subject: [PATCH 1314/1476] kthread: remove incorrect comment in kthread_create_on_cpu() Originally kthread_create_on_cpu() parked and woke up the new thread. However, since commit a65d40961dc7 ("kthread/smpboot: do not park in kthread_create_on_cpu()") this is no longer the case. This patch removes the comment that has been left behind and is now incorrect / stale. Fixes: a65d40961dc7 ("kthread/smpboot: do not park in kthread_create_on_cpu()") Signed-off-by: Ilias Stamatis Signed-off-by: Andrew Morton Reviewed-by: Petr Mladek Link: http://lkml.kernel.org/r/20200611135920.240551-1-stamatis.iliass@gmail.com Signed-off-by: Linus Torvalds --- kernel/kthread.c | 1 - 1 file changed, 1 deletion(-) diff --git a/kernel/kthread.c b/kernel/kthread.c index 1c8964feeb013..b2807e7be772d 100644 --- a/kernel/kthread.c +++ b/kernel/kthread.c @@ -480,7 +480,6 @@ EXPORT_SYMBOL(kthread_bind); * to "name.*%u". Code fills in cpu number. * * Description: This helper function creates and names a kernel thread - * The thread will be woken and put into park mode. */ struct task_struct *kthread_create_on_cpu(int (*threadfn)(void *data), void *data, unsigned int cpu, -- GitLab From 79e3ea5aab48c83de9410e43b52895406847eca7 Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Thu, 6 Aug 2020 23:17:22 -0700 Subject: [PATCH 1315/1476] tools/: replace HTTP links with HTTPS ones Rationale: Reduces attack surface on kernel devs opening the links for MITM as HTTPS traffic is much harder to manipulate. Signed-off-by: Alexander A. Klimov Signed-off-by: Andrew Morton Link: http://lkml.kernel.org/r/20200726120752.16768-1-grandmaster@al2klimov.de Signed-off-by: Linus Torvalds --- tools/include/linux/jhash.h | 2 +- tools/lib/rbtree.c | 2 +- tools/lib/traceevent/event-parse.h | 2 +- tools/testing/ktest/examples/README | 2 +- tools/testing/ktest/examples/crosstests.conf | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/tools/include/linux/jhash.h b/tools/include/linux/jhash.h index 348c6f47e4cc3..af8d0fe1c6ce9 100644 --- a/tools/include/linux/jhash.h +++ b/tools/include/linux/jhash.h @@ -5,7 +5,7 @@ * * Copyright (C) 2006. Bob Jenkins (bob_jenkins@burtleburtle.net) * - * http://burtleburtle.net/bob/hash/ + * https://burtleburtle.net/bob/hash/ * * These are the credits from Bob's sources: * diff --git a/tools/lib/rbtree.c b/tools/lib/rbtree.c index 06ac7bd2144b7..727396de6be54 100644 --- a/tools/lib/rbtree.c +++ b/tools/lib/rbtree.c @@ -13,7 +13,7 @@ #include /* - * red-black trees properties: http://en.wikipedia.org/wiki/Rbtree + * red-black trees properties: https://en.wikipedia.org/wiki/Rbtree * * 1) A node is either red or black * 2) The root is black diff --git a/tools/lib/traceevent/event-parse.h b/tools/lib/traceevent/event-parse.h index b77837f75a0d7..ad7799c854295 100644 --- a/tools/lib/traceevent/event-parse.h +++ b/tools/lib/traceevent/event-parse.h @@ -379,7 +379,7 @@ enum tep_errno { * errno since SUS requires the errno has distinct positive values. * See 'Issue 6' in the link below. * - * http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/errno.h.html + * https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/errno.h.html */ __TEP_ERRNO__START = -100000, diff --git a/tools/testing/ktest/examples/README b/tools/testing/ktest/examples/README index a12d295a09d84..4f048789b2608 100644 --- a/tools/testing/ktest/examples/README +++ b/tools/testing/ktest/examples/README @@ -11,7 +11,7 @@ crosstests.conf - this config shows an example of testing a git repo against lots of different architectures. It only does build tests, but makes it easy to compile test different archs. You can download the arch cross compilers from: - http://kernel.org/pub/tools/crosstool/files/bin/x86_64/ + https://kernel.org/pub/tools/crosstool/files/bin/x86_64/ test.conf - A generic example of a config. This is based on an actual config used to perform real testing. diff --git a/tools/testing/ktest/examples/crosstests.conf b/tools/testing/ktest/examples/crosstests.conf index 6907f32590b28..3b15e85f26bd6 100644 --- a/tools/testing/ktest/examples/crosstests.conf +++ b/tools/testing/ktest/examples/crosstests.conf @@ -3,7 +3,7 @@ # # In this config, it is expected that the tool chains from: # -# http://kernel.org/pub/tools/crosstool/files/bin/x86_64/ +# https://kernel.org/pub/tools/crosstool/files/bin/x86_64/ # # running on a x86_64 system have been downloaded and installed into: # -- GitLab From d830020656c5b68ced962ed3cb51a90e0a89d4c4 Mon Sep 17 00:00:00 2001 From: Gaurav Singh Date: Thu, 6 Aug 2020 23:17:25 -0700 Subject: [PATCH 1316/1476] tools/testing/selftests/cgroup/cgroup_util.c: cg_read_strcmp: fix null pointer dereference Haven't reproduced this issue. This PR is does a minor code cleanup. Signed-off-by: Gaurav Singh Signed-off-by: Andrew Morton Reviewed-by: Andrew Morton Cc: Shuah Khan Cc: Tejun Heo Cc: Michal Koutn Cc: Roman Gushchin Cc: Christian Brauner Cc: Chris Down Link: http://lkml.kernel.org/r/20200726013808.22242-1-gaurav1086@gmail.com Signed-off-by: Linus Torvalds --- tools/testing/selftests/cgroup/cgroup_util.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/testing/selftests/cgroup/cgroup_util.c b/tools/testing/selftests/cgroup/cgroup_util.c index 8a637ca7d73a4..05853b0b88318 100644 --- a/tools/testing/selftests/cgroup/cgroup_util.c +++ b/tools/testing/selftests/cgroup/cgroup_util.c @@ -106,7 +106,7 @@ int cg_read_strcmp(const char *cgroup, const char *control, /* Handle the case of comparing against empty string */ if (!expected) - size = 32; + return -1; else size = strlen(expected) + 1; -- GitLab From 4f491bb6ea2aef2f5b184f385904a73796d98554 Mon Sep 17 00:00:00 2001 From: Jialu Xu Date: Thu, 6 Aug 2020 23:17:29 -0700 Subject: [PATCH 1317/1476] scripts/tags.sh: collect compiled source precisely Parse compiled source from *.cmd but don't 'find' too many files that are not related to compilation. [xujialu@vimux.org: don't expand symlinks by add option -s for realpath] Link: http://lkml.kernel.org/r/5efc5bfb.1c69fb81.41bf5.7131SMTPIN_ADDED_MISSING@mx.google.com Signed-off-by: Jialu Xu Signed-off-by: Andrew Morton Cc: Greg Kroah-Hartman Cc: Masahiro Yamada Cc: Mauro Carvalho Chehab Cc: Jonathan Corbet Cc: Joe Perches Link: http://lkml.kernel.org/r/5ee5d8e3.1c69fb81.9b804.47b2SMTPIN_ADDED_MISSING@mx.google.com Signed-off-by: Linus Torvalds --- scripts/tags.sh | 18 ++++-------------- 1 file changed, 4 insertions(+), 14 deletions(-) diff --git a/scripts/tags.sh b/scripts/tags.sh index 4e18ae5282a69..32d3f53af10ba 100755 --- a/scripts/tags.sh +++ b/scripts/tags.sh @@ -91,20 +91,10 @@ all_sources() all_compiled_sources() { - for i in $(all_sources); do - case "$i" in - *.[cS]) - j=${i/\.[cS]/\.o} - j="${j#$tree}" - if [ -e $j ]; then - echo $i - fi - ;; - *) - echo $i - ;; - esac - done + realpath -es $([ -z "$KBUILD_ABS_SRCTREE" ] && echo --relative-to=.) \ + include/generated/autoconf.h $(find -name "*.cmd" -exec \ + grep -Poh '(?(?=^source_.* \K).*|(?=^ \K\S).*(?= \\))' {} \+ | + awk '!a[$0]++') | sort -u } all_target_sources() -- GitLab From 1d35b6054a9b94b48511e23a7fab6cd1dbf3fde6 Mon Sep 17 00:00:00 2001 From: Nikolay Borisov Date: Thu, 6 Aug 2020 23:17:32 -0700 Subject: [PATCH 1318/1476] scripts/bloat-o-meter: Support comparing library archives Library archives (.a) usually contain multiple object files so their output of nm --size-sort contains lines like: 00000000000003a8 t run_test extent-map-tests.o: bloat-o-meter currently doesn't handle them which results in errors when calling .split() on them. Fix this by simply ignoring them. This enables diffing subsystems which generate built-in.a files. Signed-off-by: Nikolay Borisov Signed-off-by: Andrew Morton Link: http://lkml.kernel.org/r/20200603103513.3712-1-nborisov@suse.com Signed-off-by: Linus Torvalds --- scripts/bloat-o-meter | 2 ++ 1 file changed, 2 insertions(+) diff --git a/scripts/bloat-o-meter b/scripts/bloat-o-meter index 8c965f6a98812..d7ca46c612b34 100755 --- a/scripts/bloat-o-meter +++ b/scripts/bloat-o-meter @@ -26,6 +26,8 @@ def getsizes(file, format): sym = {} with os.popen("nm --size-sort " + file) as f: for line in f: + if line.startswith("\n") or ":" in line: + continue size, type, name = line.split() if type in format: # strip generated symbols -- GitLab From f643b9ee97766e1846fab237c58a56277bf7e530 Mon Sep 17 00:00:00 2001 From: Konstantin Khlebnikov Date: Thu, 6 Aug 2020 23:17:35 -0700 Subject: [PATCH 1319/1476] scripts/decode_stacktrace.sh: skip missing symbols For now script turns missing symbols into '0' and make bogus decode. Skip them instead. Also simplify parsing output of 'nm'. Before: $ echo 'xxx+0x0/0x0' | ./scripts/decode_stacktrace.sh vmlinux "" xxx (home/khlebnikov/src/linux/./arch/x86/include/asm/processor.h:398) After: $ echo 'xxx+0x0/0x0' | ./scripts/decode_stacktrace.sh vmlinux "" xxx+0x0/0x0 Signed-off-by: Konstantin Khlebnikov Signed-off-by: Andrew Morton Cc: Sasha Levin Link: http://lkml.kernel.org/r/159282922499.248444.4883465570858385250.stgit@buzz Signed-off-by: Linus Torvalds --- scripts/decode_stacktrace.sh | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/scripts/decode_stacktrace.sh b/scripts/decode_stacktrace.sh index 0869def435ee7..b157037af6dfb 100755 --- a/scripts/decode_stacktrace.sh +++ b/scripts/decode_stacktrace.sh @@ -56,7 +56,11 @@ parse_symbol() { if [[ "${cache[$module,$name]+isset}" == "isset" ]]; then local base_addr=${cache[$module,$name]} else - local base_addr=$(nm "$objfile" | grep -i ' t ' | awk "/ $name\$/ {print \$1}" | head -n1) + local base_addr=$(nm "$objfile" | awk '$3 == "'$name'" && ($2 == "t" || $2 == "T") {print $1; exit}') + if [[ $base_addr == "" ]] ; then + # address not found + return + fi cache[$module,$name]="$base_addr" fi # Let's start doing the math to get the exact address into the -- GitLab From ecda6e27fa834b2aa37fc9ad49f89b80e9ee8f97 Mon Sep 17 00:00:00 2001 From: Konstantin Khlebnikov Date: Thu, 6 Aug 2020 23:17:38 -0700 Subject: [PATCH 1320/1476] scripts/decode_stacktrace.sh: guess basepath if not specified Guess path to kernel sources using known location of symbol "kernel_init". Make basepath argument optional. Before: $ echo 'vfs_open+0x0/0x0' | ./scripts/decode_stacktrace.sh vmlinux "" vfs_open (home/khlebnikov/src/linux/fs/open.c:912) After: $ echo 'vfs_open+0x0/0x0' | ./scripts/decode_stacktrace.sh vmlinux vfs_open (fs/open.c:912) Signed-off-by: Konstantin Khlebnikov Signed-off-by: Andrew Morton Cc: Sasha Levin Link: http://lkml.kernel.org/r/159282922803.248444.2379229451667913634.stgit@buzz Signed-off-by: Linus Torvalds --- scripts/decode_stacktrace.sh | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/scripts/decode_stacktrace.sh b/scripts/decode_stacktrace.sh index b157037af6dfb..62a70ede7f3e8 100755 --- a/scripts/decode_stacktrace.sh +++ b/scripts/decode_stacktrace.sh @@ -3,14 +3,14 @@ # (c) 2014, Sasha Levin #set -x -if [[ $# < 2 ]]; then +if [[ $# < 1 ]]; then echo "Usage:" - echo " $0 [vmlinux] [base path] [modules path]" + echo " $0 [base path] [modules path]" exit 1 fi vmlinux=$1 -basepath=$2 +basepath=${2-auto} modpath=$3 declare -A cache declare -A modcache @@ -152,6 +152,14 @@ handle_line() { echo "${words[@]}" "$symbol $module" } +if [[ $basepath == "auto" ]] ; then + module="" + symbol="kernel_init+0x0/0x0" + parse_symbol + basepath=${symbol#kernel_init (} + basepath=${basepath%/init/main.c:*)} +fi + while read line; do # Let's see if we have an address in the line if [[ $line =~ \[\<([^]]+)\>\] ]] || -- GitLab From 431151b64af6c8ebec85ee1db3597b56a613f526 Mon Sep 17 00:00:00 2001 From: Konstantin Khlebnikov Date: Thu, 6 Aug 2020 23:17:41 -0700 Subject: [PATCH 1321/1476] scripts/decode_stacktrace.sh: guess path to modules Try to find module in directory with vmlinux (for fresh build). Then try standard paths where debuginfo are usually placed. Pick first file which have elf section '.debug_line'. Before: $ echo 'tap_open+0x0/0x0 [tap]' | ./scripts/decode_stacktrace.sh /usr/lib/debug/boot/vmlinux-5.4.0-37-generic WARNING! Modules path isn't set, but is needed to parse this symbol tap_open+0x0/0x0 tap After: $ echo 'tap_open+0x0/0x0 [tap]' | ./scripts/decode_stacktrace.sh /usr/lib/debug/boot/vmlinux-5.4.0-37-generic tap_open (drivers/net/tap.c:502) tap Signed-off-by: Konstantin Khlebnikov Signed-off-by: Andrew Morton Cc: Sasha Levin Link: http://lkml.kernel.org/r/159282923068.248444.5461337458421616083.stgit@buzz Signed-off-by: Linus Torvalds --- scripts/decode_stacktrace.sh | 36 +++++++++++++++++++++++++++++++++--- 1 file changed, 33 insertions(+), 3 deletions(-) diff --git a/scripts/decode_stacktrace.sh b/scripts/decode_stacktrace.sh index 62a70ede7f3e8..e31e253fe5bd6 100755 --- a/scripts/decode_stacktrace.sh +++ b/scripts/decode_stacktrace.sh @@ -12,9 +12,40 @@ fi vmlinux=$1 basepath=${2-auto} modpath=$3 +release="" + declare -A cache declare -A modcache +find_module() { + if [[ "$modpath" != "" ]] ; then + for fn in $(find "$modpath" -name "${module//_/[-_]}.ko*") ; do + if readelf -WS "$fn" | grep -qwF .debug_line ; then + echo $fn + return + fi + done + return 1 + fi + + modpath=$(dirname "$vmlinux") + find_module && return + + if [[ $release == "" ]] ; then + release=$(gdb -ex 'print init_uts_ns.name.release' -ex 'quit' -quiet -batch "$vmlinux" | sed -n 's/\$1 = "\(.*\)".*/\1/p') + fi + + for dn in {/usr/lib/debug,}/lib/modules/$release ; do + if [ -e "$dn" ] ; then + modpath="$dn" + find_module && return + fi + done + + modpath="" + return 1 +} + parse_symbol() { # The structure of symbol at this point is: # ([name]+[offset]/[total length]) @@ -27,12 +58,11 @@ parse_symbol() { elif [[ "${modcache[$module]+isset}" == "isset" ]]; then local objfile=${modcache[$module]} else - if [[ $modpath == "" ]]; then + local objfile=$(find_module) + if [[ $objfile == "" ]] ; then echo "WARNING! Modules path isn't set, but is needed to parse this symbol" >&2 return fi - local objfile=$(find "$modpath" -name "${module//_/[-_]}.ko*" -print -quit) - [[ $objfile == "" ]] && return modcache[$module]=$objfile fi -- GitLab From f90dde44c57a1cf338ae01a691110eb980e48563 Mon Sep 17 00:00:00 2001 From: Konstantin Khlebnikov Date: Thu, 6 Aug 2020 23:17:43 -0700 Subject: [PATCH 1322/1476] scripts/decode_stacktrace.sh: guess path to vmlinux by release name Add option decode_stacktrace -r to specify only release name. This is enough to guess standard paths to vmlinux and modules: $ echo -e 'schedule+0x0/0x0 tap_open+0x0/0x0 [tap]' | ./scripts/decode_stacktrace.sh -r 5.4.0-37-generic schedule (kernel/sched/core.c:4138) tap_open (drivers/net/tap.c:502) tap Signed-off-by: Konstantin Khlebnikov Signed-off-by: Andrew Morton Cc: Sasha Levin Link: http://lkml.kernel.org/r/159282923334.248444.2399153100007347838.stgit@buzz Signed-off-by: Linus Torvalds --- scripts/decode_stacktrace.sh | 29 ++++++++++++++++++++++++----- 1 file changed, 24 insertions(+), 5 deletions(-) diff --git a/scripts/decode_stacktrace.sh b/scripts/decode_stacktrace.sh index e31e253fe5bd6..90398347e3664 100755 --- a/scripts/decode_stacktrace.sh +++ b/scripts/decode_stacktrace.sh @@ -5,14 +5,33 @@ if [[ $# < 1 ]]; then echo "Usage:" - echo " $0 [base path] [modules path]" + echo " $0 -r | [base path] [modules path]" exit 1 fi -vmlinux=$1 -basepath=${2-auto} -modpath=$3 -release="" +if [[ $1 == "-r" ]] ; then + vmlinux="" + basepath="auto" + modpath="" + release=$2 + + for fn in {,/usr/lib/debug}/boot/vmlinux-$release{,.debug} /lib/modules/$release{,/build}/vmlinux ; do + if [ -e "$fn" ] ; then + vmlinux=$fn + break + fi + done + + if [[ $vmlinux == "" ]] ; then + echo "ERROR! vmlinux image for release $release is not found" >&2 + exit 2 + fi +else + vmlinux=$1 + basepath=${2-auto} + modpath=$3 + release="" +fi declare -A cache declare -A modcache -- GitLab From d2b02165df683e1b05ed6e38e9051632625b528c Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Thu, 6 Aug 2020 23:17:46 -0700 Subject: [PATCH 1323/1476] const_structs.checkpatch: add regulator_ops Add regulator_ops to expected to be const list. Signed-off-by: Joe Perches Signed-off-by: Andrew Morton Cc: Pi-Hsun Shih Cc: Liam Girdwood Cc: Mark Brown Cc: Benson Leung Cc: Enric Balletbo i Serra Cc: Guenter Roeck Cc: Rikard Falkeborn Link: http://lkml.kernel.org/r/dab1ba1aa03a8236933cfb7a28937efb0b808f13.camel@perches.com Signed-off-by: Linus Torvalds --- scripts/const_structs.checkpatch | 1 + 1 file changed, 1 insertion(+) diff --git a/scripts/const_structs.checkpatch b/scripts/const_structs.checkpatch index ac5f1267151df..e9df9cc28a853 100644 --- a/scripts/const_structs.checkpatch +++ b/scripts/const_structs.checkpatch @@ -44,6 +44,7 @@ platform_hibernation_ops platform_suspend_ops proto_ops regmap_access_table +regulator_ops rpc_pipe_ops rtc_class_ops sd_desc -- GitLab From 70a175e5071f9b5066413d80a96b853b73a69fc6 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Thu, 6 Aug 2020 23:17:50 -0700 Subject: [PATCH 1324/1476] scripts/spelling.txt: add more spellings to spelling.txt Here are some of the more common spelling mistakes and typos that I've found while fixing up spelling mistakes in the kernel since April 2020. Signed-off-by: Colin Ian King Signed-off-by: Andrew Morton Link: http://lkml.kernel.org/r/20200714092837.173796-1-colin.king@canonical.com Signed-off-by: Linus Torvalds --- scripts/spelling.txt | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/scripts/spelling.txt b/scripts/spelling.txt index c45e9afaab2db..f253681e7e2a0 100644 --- a/scripts/spelling.txt +++ b/scripts/spelling.txt @@ -149,6 +149,7 @@ arbitary||arbitrary architechture||architecture arguement||argument arguements||arguments +arithmatic||arithmetic aritmetic||arithmetic arne't||aren't arraival||arrival @@ -454,6 +455,7 @@ destorys||destroys destroied||destroyed detabase||database deteced||detected +detectt||detect develope||develop developement||development developped||developed @@ -545,6 +547,7 @@ entires||entries entites||entities entrys||entries enocded||encoded +enought||enough enterily||entirely enviroiment||environment enviroment||environment @@ -556,11 +559,14 @@ equivelant||equivalent equivilant||equivalent eror||error errorr||error +errror||error estbalishment||establishment etsablishment||establishment etsbalishment||establishment +evalution||evaluation excecutable||executable exceded||exceeded +exceds||exceeds exceeed||exceed excellant||excellent execeeded||exceeded @@ -583,6 +589,7 @@ explictly||explicitly expresion||expression exprimental||experimental extened||extended +exteneded||extended||extended extensability||extensibility extention||extension extenstion||extension @@ -610,10 +617,12 @@ feautures||features fetaure||feature fetaures||features fileystem||filesystem +fimrware||firmware fimware||firmware firmare||firmware firmaware||firmware firware||firmware +firwmare||firmware finanize||finalize findn||find finilizes||finalizes @@ -661,6 +670,7 @@ globel||global grabing||grabbing grahical||graphical grahpical||graphical +granularty||granularity grapic||graphic grranted||granted guage||gauge @@ -906,6 +916,7 @@ miximum||maximum mmnemonic||mnemonic mnay||many modfiy||modify +modifer||modifier modulues||modules momery||memory memomry||memory @@ -915,6 +926,7 @@ monochromo||monochrome monocrome||monochrome mopdule||module mroe||more +multipler||multiplier mulitplied||multiplied multidimensionnal||multidimensional multipe||multiple @@ -952,6 +964,7 @@ occassionally||occasionally occationally||occasionally occurance||occurrence occurances||occurrences +occurd||occurred occured||occurred occurence||occurrence occure||occurred @@ -1058,6 +1071,7 @@ precission||precision preemptable||preemptible prefered||preferred prefferably||preferably +prefitler||prefilter premption||preemption prepaired||prepared preperation||preparation @@ -1101,6 +1115,7 @@ pronunce||pronounce propery||property propigate||propagate propigation||propagation +propogation||propagation propogate||propagate prosess||process protable||portable @@ -1316,6 +1331,7 @@ sturcture||structure subdirectoires||subdirectories suble||subtle substract||subtract +submited||submitted submition||submission suceed||succeed succesfully||successfully @@ -1324,6 +1340,7 @@ successed||succeeded successfull||successful successfuly||successfully sucessfully||successfully +sucessful||successful sucess||success superflous||superfluous superseeded||superseded @@ -1409,6 +1426,7 @@ transormed||transformed trasfer||transfer trasmission||transmission treshold||threshold +triggerd||triggered trigerred||triggered trigerring||triggering trun||turn @@ -1421,6 +1439,7 @@ uknown||unknown usccess||success usupported||unsupported uncommited||uncommitted +uncompatible||incompatible unconditionaly||unconditionally undeflow||underflow underun||underrun -- GitLab From 1146f7e2dc15a227a7e1ef9a1fb67182b25e687f Mon Sep 17 00:00:00 2001 From: Luca Stefani Date: Thu, 6 Aug 2020 23:17:53 -0700 Subject: [PATCH 1325/1476] ntfs: fix ntfs_test_inode and ntfs_init_locked_inode function type Clang's Control Flow Integrity (CFI) is a security mechanism that can help prevent JOP chains, deployed extensively in downstream kernels used in Android. Its deployment is hindered by mismatches in function signatures. For this case, we make callbacks match their intended function signature, and cast parameters within them rather than casting the callback when passed as a parameter. When running `mount -t ntfs ...` we observe the following trace: Call trace: __cfi_check_fail+0x1c/0x24 name_to_dev_t+0x0/0x404 iget5_locked+0x594/0x5e8 ntfs_fill_super+0xbfc/0x43ec mount_bdev+0x30c/0x3cc ntfs_mount+0x18/0x24 mount_fs+0x1b0/0x380 vfs_kern_mount+0x90/0x398 do_mount+0x5d8/0x1a10 SyS_mount+0x108/0x144 el0_svc_naked+0x34/0x38 Signed-off-by: Luca Stefani Signed-off-by: Andrew Morton Tested-by: freak07 Reviewed-by: Nick Desaulniers Reviewed-by: Nathan Chancellor Acked-by: Anton Altaparmakov Link: http://lkml.kernel.org/r/20200718112513.533800-1-luca.stefani.ge1@gmail.com Signed-off-by: Linus Torvalds --- fs/ntfs/dir.c | 2 +- fs/ntfs/inode.c | 27 ++++++++++++++------------- fs/ntfs/inode.h | 4 +--- fs/ntfs/mft.c | 4 ++-- 4 files changed, 18 insertions(+), 19 deletions(-) diff --git a/fs/ntfs/dir.c b/fs/ntfs/dir.c index a87d4391e6b55..cd96083a12c8e 100644 --- a/fs/ntfs/dir.c +++ b/fs/ntfs/dir.c @@ -1504,7 +1504,7 @@ static int ntfs_dir_fsync(struct file *filp, loff_t start, loff_t end, na.type = AT_BITMAP; na.name = I30; na.name_len = 4; - bmp_vi = ilookup5(vi->i_sb, vi->i_ino, (test_t)ntfs_test_inode, &na); + bmp_vi = ilookup5(vi->i_sb, vi->i_ino, ntfs_test_inode, &na); if (bmp_vi) { write_inode_now(bmp_vi, !datasync); iput(bmp_vi); diff --git a/fs/ntfs/inode.c b/fs/ntfs/inode.c index d4359a1df3d5e..9bb9f0952b186 100644 --- a/fs/ntfs/inode.c +++ b/fs/ntfs/inode.c @@ -30,10 +30,10 @@ /** * ntfs_test_inode - compare two (possibly fake) inodes for equality * @vi: vfs inode which to test - * @na: ntfs attribute which is being tested with + * @data: data which is being tested with * * Compare the ntfs attribute embedded in the ntfs specific part of the vfs - * inode @vi for equality with the ntfs attribute @na. + * inode @vi for equality with the ntfs attribute @data. * * If searching for the normal file/directory inode, set @na->type to AT_UNUSED. * @na->name and @na->name_len are then ignored. @@ -43,8 +43,9 @@ * NOTE: This function runs with the inode_hash_lock spin lock held so it is not * allowed to sleep. */ -int ntfs_test_inode(struct inode *vi, ntfs_attr *na) +int ntfs_test_inode(struct inode *vi, void *data) { + ntfs_attr *na = (ntfs_attr *)data; ntfs_inode *ni; if (vi->i_ino != na->mft_no) @@ -72,9 +73,9 @@ int ntfs_test_inode(struct inode *vi, ntfs_attr *na) /** * ntfs_init_locked_inode - initialize an inode * @vi: vfs inode to initialize - * @na: ntfs attribute which to initialize @vi to + * @data: data which to initialize @vi to * - * Initialize the vfs inode @vi with the values from the ntfs attribute @na in + * Initialize the vfs inode @vi with the values from the ntfs attribute @data in * order to enable ntfs_test_inode() to do its work. * * If initializing the normal file/directory inode, set @na->type to AT_UNUSED. @@ -87,8 +88,9 @@ int ntfs_test_inode(struct inode *vi, ntfs_attr *na) * NOTE: This function runs with the inode->i_lock spin lock held so it is not * allowed to sleep. (Hence the GFP_ATOMIC allocation.) */ -static int ntfs_init_locked_inode(struct inode *vi, ntfs_attr *na) +static int ntfs_init_locked_inode(struct inode *vi, void *data) { + ntfs_attr *na = (ntfs_attr *)data; ntfs_inode *ni = NTFS_I(vi); vi->i_ino = na->mft_no; @@ -131,7 +133,6 @@ static int ntfs_init_locked_inode(struct inode *vi, ntfs_attr *na) return 0; } -typedef int (*set_t)(struct inode *, void *); static int ntfs_read_locked_inode(struct inode *vi); static int ntfs_read_locked_attr_inode(struct inode *base_vi, struct inode *vi); static int ntfs_read_locked_index_inode(struct inode *base_vi, @@ -164,8 +165,8 @@ struct inode *ntfs_iget(struct super_block *sb, unsigned long mft_no) na.name = NULL; na.name_len = 0; - vi = iget5_locked(sb, mft_no, (test_t)ntfs_test_inode, - (set_t)ntfs_init_locked_inode, &na); + vi = iget5_locked(sb, mft_no, ntfs_test_inode, + ntfs_init_locked_inode, &na); if (unlikely(!vi)) return ERR_PTR(-ENOMEM); @@ -225,8 +226,8 @@ struct inode *ntfs_attr_iget(struct inode *base_vi, ATTR_TYPE type, na.name = name; na.name_len = name_len; - vi = iget5_locked(base_vi->i_sb, na.mft_no, (test_t)ntfs_test_inode, - (set_t)ntfs_init_locked_inode, &na); + vi = iget5_locked(base_vi->i_sb, na.mft_no, ntfs_test_inode, + ntfs_init_locked_inode, &na); if (unlikely(!vi)) return ERR_PTR(-ENOMEM); @@ -280,8 +281,8 @@ struct inode *ntfs_index_iget(struct inode *base_vi, ntfschar *name, na.name = name; na.name_len = name_len; - vi = iget5_locked(base_vi->i_sb, na.mft_no, (test_t)ntfs_test_inode, - (set_t)ntfs_init_locked_inode, &na); + vi = iget5_locked(base_vi->i_sb, na.mft_no, ntfs_test_inode, + ntfs_init_locked_inode, &na); if (unlikely(!vi)) return ERR_PTR(-ENOMEM); diff --git a/fs/ntfs/inode.h b/fs/ntfs/inode.h index 98e670fbdd31d..363e4e8206738 100644 --- a/fs/ntfs/inode.h +++ b/fs/ntfs/inode.h @@ -253,9 +253,7 @@ typedef struct { ATTR_TYPE type; } ntfs_attr; -typedef int (*test_t)(struct inode *, void *); - -extern int ntfs_test_inode(struct inode *vi, ntfs_attr *na); +extern int ntfs_test_inode(struct inode *vi, void *data); extern struct inode *ntfs_iget(struct super_block *sb, unsigned long mft_no); extern struct inode *ntfs_attr_iget(struct inode *base_vi, ATTR_TYPE type, diff --git a/fs/ntfs/mft.c b/fs/ntfs/mft.c index fbb9f1bc623dc..0d62cd5bb7f84 100644 --- a/fs/ntfs/mft.c +++ b/fs/ntfs/mft.c @@ -958,7 +958,7 @@ bool ntfs_may_write_mft_record(ntfs_volume *vol, const unsigned long mft_no, * dirty code path of the inode dirty code path when writing * $MFT occurs. */ - vi = ilookup5_nowait(sb, mft_no, (test_t)ntfs_test_inode, &na); + vi = ilookup5_nowait(sb, mft_no, ntfs_test_inode, &na); } if (vi) { ntfs_debug("Base inode 0x%lx is in icache.", mft_no); @@ -1019,7 +1019,7 @@ bool ntfs_may_write_mft_record(ntfs_volume *vol, const unsigned long mft_no, vi = igrab(mft_vi); BUG_ON(vi != mft_vi); } else - vi = ilookup5_nowait(sb, na.mft_no, (test_t)ntfs_test_inode, + vi = ilookup5_nowait(sb, na.mft_no, ntfs_test_inode, &na); if (!vi) { /* -- GitLab From 504ec37dfdfbf9c65166c51f7b12126d2a9b18dc Mon Sep 17 00:00:00 2001 From: Gang He Date: Thu, 6 Aug 2020 23:17:56 -0700 Subject: [PATCH 1326/1476] ocfs2: fix remounting needed after setfacl command When use setfacl command to change a file's acl, the user cannot get the latest acl information from the file via getfacl command, until remounting the file system. e.g. setfacl -m u:ivan:rw /ocfs2/ivan getfacl /ocfs2/ivan getfacl: Removing leading '/' from absolute path names file: ocfs2/ivan owner: root group: root user::rw- group::r-- mask::r-- other::r-- The latest acl record("u:ivan:rw") cannot be returned via getfacl command until remounting. Signed-off-by: Gang He Signed-off-by: Andrew Morton Reviewed-by: Joseph Qi Cc: Mark Fasheh Cc: Joel Becker Cc: Junxiao Bi Cc: Changwei Ge Cc: Jun Piao Link: http://lkml.kernel.org/r/20200717023751.9922-1-ghe@suse.com Signed-off-by: Linus Torvalds --- fs/ocfs2/acl.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/fs/ocfs2/acl.c b/fs/ocfs2/acl.c index bb981ec76456b..7b07f5df3a299 100644 --- a/fs/ocfs2/acl.c +++ b/fs/ocfs2/acl.c @@ -256,6 +256,8 @@ static int ocfs2_set_acl(handle_t *handle, ret = ocfs2_xattr_set(inode, name_index, "", value, size, 0); kfree(value); + if (!ret) + set_cached_acl(inode, type, acl); return ret; } -- GitLab From 7eba77d59e0593b91af0c438594cb70700893b18 Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Thu, 6 Aug 2020 23:17:59 -0700 Subject: [PATCH 1327/1476] ocfs2: suballoc.h: delete a duplicated word Drop the repeated word "is" in a comment. Signed-off-by: Randy Dunlap Signed-off-by: Andrew Morton Acked-by: Joseph Qi Cc: Mark Fasheh Cc: Joel Becker Cc: Joseph Qi Link: http://lkml.kernel.org/r/20200720001421.28823-1-rdunlap@infradead.org Signed-off-by: Linus Torvalds --- fs/ocfs2/suballoc.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/ocfs2/suballoc.h b/fs/ocfs2/suballoc.h index f0a5d30a175d9..50b36250beb6e 100644 --- a/fs/ocfs2/suballoc.h +++ b/fs/ocfs2/suballoc.h @@ -40,7 +40,7 @@ struct ocfs2_alloc_context { u64 ac_last_group; u64 ac_max_block; /* Highest block number to allocate. 0 is - is the same as ~0 - unlimited */ + the same as ~0 - unlimited */ int ac_find_loc_only; /* hack for reflink operation ordering */ struct ocfs2_suballoc_result *ac_find_loc_priv; /* */ -- GitLab From 38d51b2dd171ad973afc1f5faab825ed05a2d5e9 Mon Sep 17 00:00:00 2001 From: Junxiao Bi Date: Thu, 6 Aug 2020 23:18:02 -0700 Subject: [PATCH 1328/1476] ocfs2: change slot number type s16 to u16 Dan Carpenter reported the following static checker warning. fs/ocfs2/super.c:1269 ocfs2_parse_options() warn: '(-1)' 65535 can't fit into 32767 'mopt->slot' fs/ocfs2/suballoc.c:859 ocfs2_init_inode_steal_slot() warn: '(-1)' 65535 can't fit into 32767 'osb->s_inode_steal_slot' fs/ocfs2/suballoc.c:867 ocfs2_init_meta_steal_slot() warn: '(-1)' 65535 can't fit into 32767 'osb->s_meta_steal_slot' That's because OCFS2_INVALID_SLOT is (u16)-1. Slot number in ocfs2 can be never negative, so change s16 to u16. Fixes: 9277f8334ffc ("ocfs2: fix value of OCFS2_INVALID_SLOT") Reported-by: Dan Carpenter Signed-off-by: Junxiao Bi Signed-off-by: Andrew Morton Reviewed-by: Joseph Qi Reviewed-by: Gang He Cc: Mark Fasheh Cc: Joel Becker Cc: Junxiao Bi Cc: Changwei Ge Cc: Jun Piao Cc: Link: http://lkml.kernel.org/r/20200627001259.19757-1-junxiao.bi@oracle.com Signed-off-by: Linus Torvalds --- fs/ocfs2/ocfs2.h | 4 ++-- fs/ocfs2/suballoc.c | 4 ++-- fs/ocfs2/super.c | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/fs/ocfs2/ocfs2.h b/fs/ocfs2/ocfs2.h index 2dd71d626196d..7993d527edae9 100644 --- a/fs/ocfs2/ocfs2.h +++ b/fs/ocfs2/ocfs2.h @@ -327,8 +327,8 @@ struct ocfs2_super spinlock_t osb_lock; u32 s_next_generation; unsigned long osb_flags; - s16 s_inode_steal_slot; - s16 s_meta_steal_slot; + u16 s_inode_steal_slot; + u16 s_meta_steal_slot; atomic_t s_num_inodes_stolen; atomic_t s_num_meta_stolen; diff --git a/fs/ocfs2/suballoc.c b/fs/ocfs2/suballoc.c index 45745cc3408a5..8c8cf7f4eb34e 100644 --- a/fs/ocfs2/suballoc.c +++ b/fs/ocfs2/suballoc.c @@ -879,9 +879,9 @@ static void __ocfs2_set_steal_slot(struct ocfs2_super *osb, int slot, int type) { spin_lock(&osb->osb_lock); if (type == INODE_ALLOC_SYSTEM_INODE) - osb->s_inode_steal_slot = slot; + osb->s_inode_steal_slot = (u16)slot; else if (type == EXTENT_ALLOC_SYSTEM_INODE) - osb->s_meta_steal_slot = slot; + osb->s_meta_steal_slot = (u16)slot; spin_unlock(&osb->osb_lock); } diff --git a/fs/ocfs2/super.c b/fs/ocfs2/super.c index 71ea9ce71a6b8..1d91dd1e8711c 100644 --- a/fs/ocfs2/super.c +++ b/fs/ocfs2/super.c @@ -78,7 +78,7 @@ struct mount_options unsigned long commit_interval; unsigned long mount_opt; unsigned int atime_quantum; - signed short slot; + unsigned short slot; int localalloc_opt; unsigned int resv_level; int dir_resv_level; @@ -1349,7 +1349,7 @@ static int ocfs2_parse_options(struct super_block *sb, goto bail; } if (option) - mopt->slot = (s16)option; + mopt->slot = (u16)option; break; case Opt_commit: if (match_int(&args[0], &option)) { -- GitLab From 4510a5a98aff2d395e67890c480c0665f953ea00 Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Thu, 6 Aug 2020 23:18:06 -0700 Subject: [PATCH 1329/1476] ocfs2: replace HTTP links with HTTPS ones Rationale: Reduces attack surface on kernel devs opening the links for MITM as HTTPS traffic is much harder to manipulate. Deterministic algorithm: For each file: If not .svg: For each line: If doesn't contain `xmlns`: For each link, `http://[^# ]*(?:\w|/)`: If neither `gnu\.org/license`, nor `mozilla\.org/MPL`: If both the HTTP and HTTPS versions return 200 OK and serve the same content: Replace HTTP with HTTPS. Signed-off-by: Alexander A. Klimov Signed-off-by: Andrew Morton Acked-by: Joseph Qi Cc: Mark Fasheh Cc: Joel Becker Cc: Junxiao Bi Cc: Changwei Ge Cc: Gang He Cc: Jun Piao Link: http://lkml.kernel.org/r/20200713174456.36596-1-grandmaster@al2klimov.de Signed-off-by: Linus Torvalds --- Documentation/filesystems/dlmfs.rst | 2 +- Documentation/filesystems/ocfs2.rst | 2 +- fs/ocfs2/Kconfig | 6 +++--- fs/ocfs2/blockcheck.c | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Documentation/filesystems/dlmfs.rst b/Documentation/filesystems/dlmfs.rst index 68daaa7facf94..28dd41a63be24 100644 --- a/Documentation/filesystems/dlmfs.rst +++ b/Documentation/filesystems/dlmfs.rst @@ -12,7 +12,7 @@ dlmfs is built with OCFS2 as it requires most of its infrastructure. :Project web page: http://ocfs2.wiki.kernel.org :Tools web page: https://github.com/markfasheh/ocfs2-tools -:OCFS2 mailing lists: http://oss.oracle.com/projects/ocfs2/mailman/ +:OCFS2 mailing lists: https://oss.oracle.com/projects/ocfs2/mailman/ All code copyright 2005 Oracle except when otherwise noted. diff --git a/Documentation/filesystems/ocfs2.rst b/Documentation/filesystems/ocfs2.rst index 412386bc65069..42ca9a3d4c6ea 100644 --- a/Documentation/filesystems/ocfs2.rst +++ b/Documentation/filesystems/ocfs2.rst @@ -14,7 +14,7 @@ get "mount.ocfs2" and "ocfs2_hb_ctl". Project web page: http://ocfs2.wiki.kernel.org Tools git tree: https://github.com/markfasheh/ocfs2-tools -OCFS2 mailing lists: http://oss.oracle.com/projects/ocfs2/mailman/ +OCFS2 mailing lists: https://oss.oracle.com/projects/ocfs2/mailman/ All code copyright 2005 Oracle except when otherwise noted. diff --git a/fs/ocfs2/Kconfig b/fs/ocfs2/Kconfig index aca16624b3702..5d11380d87241 100644 --- a/fs/ocfs2/Kconfig +++ b/fs/ocfs2/Kconfig @@ -16,9 +16,9 @@ config OCFS2_FS You'll want to install the ocfs2-tools package in order to at least get "mount.ocfs2". - Project web page: http://oss.oracle.com/projects/ocfs2 - Tools web page: http://oss.oracle.com/projects/ocfs2-tools - OCFS2 mailing lists: http://oss.oracle.com/projects/ocfs2/mailman/ + Project web page: https://oss.oracle.com/projects/ocfs2 + Tools web page: https://oss.oracle.com/projects/ocfs2-tools + OCFS2 mailing lists: https://oss.oracle.com/projects/ocfs2/mailman/ For more information on OCFS2, see the file . diff --git a/fs/ocfs2/blockcheck.c b/fs/ocfs2/blockcheck.c index eaf042feaf5e0..6e07ddb0e3c01 100644 --- a/fs/ocfs2/blockcheck.c +++ b/fs/ocfs2/blockcheck.c @@ -124,7 +124,7 @@ u32 ocfs2_hamming_encode(u32 parity, void *data, unsigned int d, unsigned int nr * parity bits that are part of the bit number * representation. Huh? * - * + * * In other words, the parity bit at position 2^k * checks bits in positions having bit k set in * their binary representation. Conversely, for -- GitLab From 57c720d4144a9c2b88105c3e8f7b0e97e4b5cc93 Mon Sep 17 00:00:00 2001 From: Pavel Machek Date: Thu, 6 Aug 2020 23:18:09 -0700 Subject: [PATCH 1330/1476] ocfs2: fix unbalanced locking Based on what fails, function can return with nfs_sync_rwlock either locked or unlocked. That can not be right. Always return with lock unlocked on error. Fixes: 4cd9973f9ff6 ("ocfs2: avoid inode removal while nfsd is accessing it") Signed-off-by: Pavel Machek (CIP) Signed-off-by: Andrew Morton Reviewed-by: Joseph Qi Reviewed-by: Andrew Morton Cc: Mark Fasheh Cc: Joel Becker Cc: Junxiao Bi Cc: Changwei Ge Cc: Gang He Cc: Jun Piao Link: http://lkml.kernel.org/r/20200724124443.GA28164@duo.ucw.cz Signed-off-by: Linus Torvalds --- fs/ocfs2/dlmglue.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/fs/ocfs2/dlmglue.c b/fs/ocfs2/dlmglue.c index 751bc4dc74663..8e3a369086dbd 100644 --- a/fs/ocfs2/dlmglue.c +++ b/fs/ocfs2/dlmglue.c @@ -2871,9 +2871,15 @@ int ocfs2_nfs_sync_lock(struct ocfs2_super *osb, int ex) status = ocfs2_cluster_lock(osb, lockres, ex ? LKM_EXMODE : LKM_PRMODE, 0, 0); - if (status < 0) + if (status < 0) { mlog(ML_ERROR, "lock on nfs sync lock failed %d\n", status); + if (ex) + up_write(&osb->nfs_sync_rwlock); + else + up_read(&osb->nfs_sync_rwlock); + } + return status; } -- GitLab From 453431a54934d917153c65211b2dabf45562ca88 Mon Sep 17 00:00:00 2001 From: Waiman Long Date: Thu, 6 Aug 2020 23:18:13 -0700 Subject: [PATCH 1331/1476] mm, treewide: rename kzfree() to kfree_sensitive() As said by Linus: A symmetric naming is only helpful if it implies symmetries in use. Otherwise it's actively misleading. In "kzalloc()", the z is meaningful and an important part of what the caller wants. In "kzfree()", the z is actively detrimental, because maybe in the future we really _might_ want to use that "memfill(0xdeadbeef)" or something. The "zero" part of the interface isn't even _relevant_. The main reason that kzfree() exists is to clear sensitive information that should not be leaked to other future users of the same memory objects. Rename kzfree() to kfree_sensitive() to follow the example of the recently added kvfree_sensitive() and make the intention of the API more explicit. In addition, memzero_explicit() is used to clear the memory to make sure that it won't get optimized away by the compiler. The renaming is done by using the command sequence: git grep -w --name-only kzfree |\ xargs sed -i 's/kzfree/kfree_sensitive/' followed by some editing of the kfree_sensitive() kerneldoc and adding a kzfree backward compatibility macro in slab.h. [akpm@linux-foundation.org: fs/crypto/inline_crypt.c needs linux/slab.h] [akpm@linux-foundation.org: fix fs/crypto/inline_crypt.c some more] Suggested-by: Joe Perches Signed-off-by: Waiman Long Signed-off-by: Andrew Morton Acked-by: David Howells Acked-by: Michal Hocko Acked-by: Johannes Weiner Cc: Jarkko Sakkinen Cc: James Morris Cc: "Serge E. Hallyn" Cc: Joe Perches Cc: Matthew Wilcox Cc: David Rientjes Cc: Dan Carpenter Cc: "Jason A . Donenfeld" Link: http://lkml.kernel.org/r/20200616154311.12314-3-longman@redhat.com Signed-off-by: Linus Torvalds --- arch/s390/crypto/prng.c | 4 +-- arch/x86/power/hibernate.c | 2 +- crypto/adiantum.c | 2 +- crypto/ahash.c | 4 +-- crypto/api.c | 2 +- crypto/asymmetric_keys/verify_pefile.c | 4 +-- crypto/deflate.c | 2 +- crypto/drbg.c | 10 +++--- crypto/ecc.c | 8 ++--- crypto/ecdh.c | 2 +- crypto/gcm.c | 2 +- crypto/gf128mul.c | 4 +-- crypto/jitterentropy-kcapi.c | 2 +- crypto/rng.c | 2 +- crypto/rsa-pkcs1pad.c | 6 ++-- crypto/seqiv.c | 2 +- crypto/shash.c | 2 +- crypto/skcipher.c | 2 +- crypto/testmgr.c | 6 ++-- crypto/zstd.c | 2 +- .../allwinner/sun8i-ce/sun8i-ce-cipher.c | 2 +- .../allwinner/sun8i-ss/sun8i-ss-cipher.c | 2 +- drivers/crypto/amlogic/amlogic-gxl-cipher.c | 4 +-- drivers/crypto/atmel-ecc.c | 2 +- drivers/crypto/caam/caampkc.c | 28 +++++++-------- drivers/crypto/cavium/cpt/cptvf_main.c | 6 ++-- drivers/crypto/cavium/cpt/cptvf_reqmanager.c | 12 +++---- drivers/crypto/cavium/nitrox/nitrox_lib.c | 4 +-- drivers/crypto/cavium/zip/zip_crypto.c | 6 ++-- drivers/crypto/ccp/ccp-crypto-rsa.c | 6 ++-- drivers/crypto/ccree/cc_aead.c | 4 +-- drivers/crypto/ccree/cc_buffer_mgr.c | 4 +-- drivers/crypto/ccree/cc_cipher.c | 6 ++-- drivers/crypto/ccree/cc_hash.c | 8 ++--- drivers/crypto/ccree/cc_request_mgr.c | 2 +- drivers/crypto/marvell/cesa/hash.c | 2 +- .../crypto/marvell/octeontx/otx_cptvf_main.c | 6 ++-- .../marvell/octeontx/otx_cptvf_reqmgr.h | 2 +- drivers/crypto/nx/nx.c | 4 +-- drivers/crypto/virtio/virtio_crypto_algs.c | 12 +++---- drivers/crypto/virtio/virtio_crypto_core.c | 2 +- drivers/md/dm-crypt.c | 32 ++++++++--------- drivers/md/dm-integrity.c | 6 ++-- drivers/misc/ibmvmc.c | 6 ++-- .../hisilicon/hns3/hns3pf/hclge_mbx.c | 2 +- .../net/ethernet/intel/ixgbe/ixgbe_ipsec.c | 6 ++-- drivers/net/ppp/ppp_mppe.c | 6 ++-- drivers/net/wireguard/noise.c | 4 +-- drivers/net/wireguard/peer.c | 2 +- drivers/net/wireless/intel/iwlwifi/pcie/rx.c | 2 +- .../net/wireless/intel/iwlwifi/pcie/tx-gen2.c | 6 ++-- drivers/net/wireless/intel/iwlwifi/pcie/tx.c | 6 ++-- drivers/net/wireless/intersil/orinoco/wext.c | 4 +-- drivers/s390/crypto/ap_bus.h | 4 +-- drivers/staging/ks7010/ks_hostif.c | 2 +- drivers/staging/rtl8723bs/core/rtw_security.c | 2 +- drivers/staging/wlan-ng/p80211netdev.c | 2 +- drivers/target/iscsi/iscsi_target_auth.c | 2 +- fs/cifs/cifsencrypt.c | 2 +- fs/cifs/connect.c | 10 +++--- fs/cifs/dfs_cache.c | 2 +- fs/cifs/misc.c | 8 ++--- fs/crypto/inline_crypt.c | 5 +-- fs/crypto/keyring.c | 6 ++-- fs/crypto/keysetup_v1.c | 4 +-- fs/ecryptfs/keystore.c | 4 +-- fs/ecryptfs/messaging.c | 2 +- include/crypto/aead.h | 2 +- include/crypto/akcipher.h | 2 +- include/crypto/gf128mul.h | 2 +- include/crypto/hash.h | 2 +- include/crypto/internal/acompress.h | 2 +- include/crypto/kpp.h | 2 +- include/crypto/skcipher.h | 2 +- include/linux/slab.h | 4 ++- lib/mpi/mpiutil.c | 6 ++-- lib/test_kasan.c | 6 ++-- mm/slab_common.c | 8 ++--- net/atm/mpoa_caches.c | 4 +-- net/bluetooth/ecdh_helper.c | 6 ++-- net/bluetooth/smp.c | 24 ++++++------- net/core/sock.c | 2 +- net/ipv4/tcp_fastopen.c | 2 +- net/mac80211/aead_api.c | 4 +-- net/mac80211/aes_gmac.c | 2 +- net/mac80211/key.c | 2 +- net/mac802154/llsec.c | 20 +++++------ net/sctp/auth.c | 2 +- net/sunrpc/auth_gss/gss_krb5_crypto.c | 4 +-- net/sunrpc/auth_gss/gss_krb5_keys.c | 6 ++-- net/sunrpc/auth_gss/gss_krb5_mech.c | 2 +- net/tipc/crypto.c | 10 +++--- net/wireless/core.c | 2 +- net/wireless/ibss.c | 4 +-- net/wireless/lib80211_crypt_tkip.c | 2 +- net/wireless/lib80211_crypt_wep.c | 2 +- net/wireless/nl80211.c | 24 ++++++------- net/wireless/sme.c | 6 ++-- net/wireless/util.c | 2 +- net/wireless/wext-sme.c | 2 +- scripts/coccinelle/free/devm_free.cocci | 4 +-- scripts/coccinelle/free/ifnullfree.cocci | 4 +-- scripts/coccinelle/free/kfree.cocci | 6 ++-- scripts/coccinelle/free/kfreeaddr.cocci | 2 +- security/apparmor/domain.c | 4 +-- security/apparmor/include/file.h | 2 +- security/apparmor/policy.c | 24 ++++++------- security/apparmor/policy_ns.c | 6 ++-- security/apparmor/policy_unpack.c | 14 ++++---- security/keys/big_key.c | 6 ++-- security/keys/dh.c | 14 ++++---- security/keys/encrypted-keys/encrypted.c | 14 ++++---- security/keys/trusted-keys/trusted_tpm1.c | 34 +++++++++---------- security/keys/user_defined.c | 6 ++-- 114 files changed, 323 insertions(+), 320 deletions(-) diff --git a/arch/s390/crypto/prng.c b/arch/s390/crypto/prng.c index e1ae23911ccd9..5057773f82e94 100644 --- a/arch/s390/crypto/prng.c +++ b/arch/s390/crypto/prng.c @@ -249,7 +249,7 @@ static void prng_tdes_deinstantiate(void) { pr_debug("The prng module stopped " "after running in triple DES mode\n"); - kzfree(prng_data); + kfree_sensitive(prng_data); } @@ -442,7 +442,7 @@ outfree: static void prng_sha512_deinstantiate(void) { pr_debug("The prng module stopped after running in SHA-512 mode\n"); - kzfree(prng_data); + kfree_sensitive(prng_data); } diff --git a/arch/x86/power/hibernate.c b/arch/x86/power/hibernate.c index d147f1b2c9259..cd3914fc9f3d4 100644 --- a/arch/x86/power/hibernate.c +++ b/arch/x86/power/hibernate.c @@ -98,7 +98,7 @@ static int get_e820_md5(struct e820_table *table, void *buf) if (crypto_shash_digest(desc, (u8 *)table, size, buf)) ret = -EINVAL; - kzfree(desc); + kfree_sensitive(desc); free_tfm: crypto_free_shash(tfm); diff --git a/crypto/adiantum.c b/crypto/adiantum.c index 7fbdc32709842..ce4d5725342cb 100644 --- a/crypto/adiantum.c +++ b/crypto/adiantum.c @@ -177,7 +177,7 @@ static int adiantum_setkey(struct crypto_skcipher *tfm, const u8 *key, keyp += NHPOLY1305_KEY_SIZE; WARN_ON(keyp != &data->derived_keys[ARRAY_SIZE(data->derived_keys)]); out: - kzfree(data); + kfree_sensitive(data); return err; } diff --git a/crypto/ahash.c b/crypto/ahash.c index 68a0f0cb75c4c..d9d65d1cc6690 100644 --- a/crypto/ahash.c +++ b/crypto/ahash.c @@ -183,7 +183,7 @@ static int ahash_setkey_unaligned(struct crypto_ahash *tfm, const u8 *key, alignbuffer = (u8 *)ALIGN((unsigned long)buffer, alignmask + 1); memcpy(alignbuffer, key, keylen); ret = tfm->setkey(tfm, alignbuffer, keylen); - kzfree(buffer); + kfree_sensitive(buffer); return ret; } @@ -302,7 +302,7 @@ static void ahash_restore_req(struct ahash_request *req, int err) req->priv = NULL; /* Free the req->priv.priv from the ADJUSTED request. */ - kzfree(priv); + kfree_sensitive(priv); } static void ahash_notify_einprogress(struct ahash_request *req) diff --git a/crypto/api.c b/crypto/api.c index 5d8fe60b36c16..ed08cbd5b9d3f 100644 --- a/crypto/api.c +++ b/crypto/api.c @@ -571,7 +571,7 @@ void crypto_destroy_tfm(void *mem, struct crypto_tfm *tfm) alg->cra_exit(tfm); crypto_exit_ops(tfm); crypto_mod_put(alg); - kzfree(mem); + kfree_sensitive(mem); } EXPORT_SYMBOL_GPL(crypto_destroy_tfm); diff --git a/crypto/asymmetric_keys/verify_pefile.c b/crypto/asymmetric_keys/verify_pefile.c index cc9dbcecaacaa..7553ab18db898 100644 --- a/crypto/asymmetric_keys/verify_pefile.c +++ b/crypto/asymmetric_keys/verify_pefile.c @@ -376,7 +376,7 @@ static int pefile_digest_pe(const void *pebuf, unsigned int pelen, } error: - kzfree(desc); + kfree_sensitive(desc); error_no_desc: crypto_free_shash(tfm); kleave(" = %d", ret); @@ -447,6 +447,6 @@ int verify_pefile_signature(const void *pebuf, unsigned pelen, ret = pefile_digest_pe(pebuf, pelen, &ctx); error: - kzfree(ctx.digest); + kfree_sensitive(ctx.digest); return ret; } diff --git a/crypto/deflate.c b/crypto/deflate.c index 4c0e6c9d942ab..b2a46f6dc961e 100644 --- a/crypto/deflate.c +++ b/crypto/deflate.c @@ -163,7 +163,7 @@ static void __deflate_exit(void *ctx) static void deflate_free_ctx(struct crypto_scomp *tfm, void *ctx) { __deflate_exit(ctx); - kzfree(ctx); + kfree_sensitive(ctx); } static void deflate_exit(struct crypto_tfm *tfm) diff --git a/crypto/drbg.c b/crypto/drbg.c index 8d80d93cab970..e99fe34cfa005 100644 --- a/crypto/drbg.c +++ b/crypto/drbg.c @@ -1218,19 +1218,19 @@ static inline void drbg_dealloc_state(struct drbg_state *drbg) { if (!drbg) return; - kzfree(drbg->Vbuf); + kfree_sensitive(drbg->Vbuf); drbg->Vbuf = NULL; drbg->V = NULL; - kzfree(drbg->Cbuf); + kfree_sensitive(drbg->Cbuf); drbg->Cbuf = NULL; drbg->C = NULL; - kzfree(drbg->scratchpadbuf); + kfree_sensitive(drbg->scratchpadbuf); drbg->scratchpadbuf = NULL; drbg->reseed_ctr = 0; drbg->d_ops = NULL; drbg->core = NULL; if (IS_ENABLED(CONFIG_CRYPTO_FIPS)) { - kzfree(drbg->prev); + kfree_sensitive(drbg->prev); drbg->prev = NULL; drbg->fips_primed = false; } @@ -1701,7 +1701,7 @@ static int drbg_fini_hash_kernel(struct drbg_state *drbg) struct sdesc *sdesc = (struct sdesc *)drbg->priv_data; if (sdesc) { crypto_free_shash(sdesc->shash.tfm); - kzfree(sdesc); + kfree_sensitive(sdesc); } drbg->priv_data = NULL; return 0; diff --git a/crypto/ecc.c b/crypto/ecc.c index 8acf8433ca291..c80aa25994a0c 100644 --- a/crypto/ecc.c +++ b/crypto/ecc.c @@ -67,7 +67,7 @@ static u64 *ecc_alloc_digits_space(unsigned int ndigits) static void ecc_free_digits_space(u64 *space) { - kzfree(space); + kfree_sensitive(space); } static struct ecc_point *ecc_alloc_point(unsigned int ndigits) @@ -101,9 +101,9 @@ static void ecc_free_point(struct ecc_point *p) if (!p) return; - kzfree(p->x); - kzfree(p->y); - kzfree(p); + kfree_sensitive(p->x); + kfree_sensitive(p->y); + kfree_sensitive(p); } static void vli_clear(u64 *vli, unsigned int ndigits) diff --git a/crypto/ecdh.c b/crypto/ecdh.c index bd599053a8c4b..b0232d6ab4ce7 100644 --- a/crypto/ecdh.c +++ b/crypto/ecdh.c @@ -124,7 +124,7 @@ static int ecdh_compute_value(struct kpp_request *req) /* fall through */ free_all: - kzfree(shared_secret); + kfree_sensitive(shared_secret); free_pubkey: kfree(public_key); return ret; diff --git a/crypto/gcm.c b/crypto/gcm.c index 3a36a9533c96e..338ee07697479 100644 --- a/crypto/gcm.c +++ b/crypto/gcm.c @@ -139,7 +139,7 @@ static int crypto_gcm_setkey(struct crypto_aead *aead, const u8 *key, CRYPTO_TFM_REQ_MASK); err = crypto_ahash_setkey(ghash, (u8 *)&data->hash, sizeof(be128)); out: - kzfree(data); + kfree_sensitive(data); return err; } diff --git a/crypto/gf128mul.c b/crypto/gf128mul.c index a4b1c026aaeec..a69ae3e6c16cb 100644 --- a/crypto/gf128mul.c +++ b/crypto/gf128mul.c @@ -304,8 +304,8 @@ void gf128mul_free_64k(struct gf128mul_64k *t) int i; for (i = 0; i < 16; i++) - kzfree(t->t[i]); - kzfree(t); + kfree_sensitive(t->t[i]); + kfree_sensitive(t); } EXPORT_SYMBOL(gf128mul_free_64k); diff --git a/crypto/jitterentropy-kcapi.c b/crypto/jitterentropy-kcapi.c index b43684c0dade3..eb7d1dd506bf3 100644 --- a/crypto/jitterentropy-kcapi.c +++ b/crypto/jitterentropy-kcapi.c @@ -57,7 +57,7 @@ void *jent_zalloc(unsigned int len) void jent_zfree(void *ptr) { - kzfree(ptr); + kfree_sensitive(ptr); } int jent_fips_enabled(void) diff --git a/crypto/rng.c b/crypto/rng.c index 1490d210f1a19..a888d84b524a4 100644 --- a/crypto/rng.c +++ b/crypto/rng.c @@ -53,7 +53,7 @@ int crypto_rng_reset(struct crypto_rng *tfm, const u8 *seed, unsigned int slen) err = crypto_rng_alg(tfm)->seed(tfm, seed, slen); crypto_stats_rng_seed(alg, err); out: - kzfree(buf); + kfree_sensitive(buf); return err; } EXPORT_SYMBOL_GPL(crypto_rng_reset); diff --git a/crypto/rsa-pkcs1pad.c b/crypto/rsa-pkcs1pad.c index 4983b2b4a2235..ddd3d10ffc154 100644 --- a/crypto/rsa-pkcs1pad.c +++ b/crypto/rsa-pkcs1pad.c @@ -199,7 +199,7 @@ static int pkcs1pad_encrypt_sign_complete(struct akcipher_request *req, int err) sg_copy_from_buffer(req->dst, sg_nents_for_len(req->dst, ctx->key_size), out_buf, ctx->key_size); - kzfree(out_buf); + kfree_sensitive(out_buf); out: req->dst_len = ctx->key_size; @@ -322,7 +322,7 @@ static int pkcs1pad_decrypt_complete(struct akcipher_request *req, int err) out_buf + pos, req->dst_len); done: - kzfree(req_ctx->out_buf); + kfree_sensitive(req_ctx->out_buf); return err; } @@ -500,7 +500,7 @@ static int pkcs1pad_verify_complete(struct akcipher_request *req, int err) req->dst_len) != 0) err = -EKEYREJECTED; done: - kzfree(req_ctx->out_buf); + kfree_sensitive(req_ctx->out_buf); return err; } diff --git a/crypto/seqiv.c b/crypto/seqiv.c index 23e22d8b63e67..0899d527c2845 100644 --- a/crypto/seqiv.c +++ b/crypto/seqiv.c @@ -33,7 +33,7 @@ static void seqiv_aead_encrypt_complete2(struct aead_request *req, int err) memcpy(req->iv, subreq->iv, crypto_aead_ivsize(geniv)); out: - kzfree(subreq->iv); + kfree_sensitive(subreq->iv); } static void seqiv_aead_encrypt_complete(struct crypto_async_request *base, diff --git a/crypto/shash.c b/crypto/shash.c index e6a4b5f39b8c6..2e3433ad97629 100644 --- a/crypto/shash.c +++ b/crypto/shash.c @@ -44,7 +44,7 @@ static int shash_setkey_unaligned(struct crypto_shash *tfm, const u8 *key, alignbuffer = (u8 *)ALIGN((unsigned long)buffer, alignmask + 1); memcpy(alignbuffer, key, keylen); err = shash->setkey(tfm, alignbuffer, keylen); - kzfree(buffer); + kfree_sensitive(buffer); return err; } diff --git a/crypto/skcipher.c b/crypto/skcipher.c index 467af525848a1..b4dae640de9f5 100644 --- a/crypto/skcipher.c +++ b/crypto/skcipher.c @@ -592,7 +592,7 @@ static int skcipher_setkey_unaligned(struct crypto_skcipher *tfm, alignbuffer = (u8 *)ALIGN((unsigned long)buffer, alignmask + 1); memcpy(alignbuffer, key, keylen); ret = cipher->setkey(tfm, alignbuffer, keylen); - kzfree(buffer); + kfree_sensitive(buffer); return ret; } diff --git a/crypto/testmgr.c b/crypto/testmgr.c index 6863f911fcee2..23c27fc96394f 100644 --- a/crypto/testmgr.c +++ b/crypto/testmgr.c @@ -1744,7 +1744,7 @@ out: kfree(vec.plaintext); kfree(vec.digest); crypto_free_shash(generic_tfm); - kzfree(generic_desc); + kfree_sensitive(generic_desc); return err; } #else /* !CONFIG_CRYPTO_MANAGER_EXTRA_TESTS */ @@ -3665,7 +3665,7 @@ static int drbg_cavs_test(const struct drbg_testvec *test, int pr, if (IS_ERR(drng)) { printk(KERN_ERR "alg: drbg: could not allocate DRNG handle for " "%s\n", driver); - kzfree(buf); + kfree_sensitive(buf); return -ENOMEM; } @@ -3712,7 +3712,7 @@ static int drbg_cavs_test(const struct drbg_testvec *test, int pr, outbuf: crypto_free_rng(drng); - kzfree(buf); + kfree_sensitive(buf); return ret; } diff --git a/crypto/zstd.c b/crypto/zstd.c index 5a3ff258d8f75..1a3309f066f7e 100644 --- a/crypto/zstd.c +++ b/crypto/zstd.c @@ -137,7 +137,7 @@ static void __zstd_exit(void *ctx) static void zstd_free_ctx(struct crypto_scomp *tfm, void *ctx) { __zstd_exit(ctx); - kzfree(ctx); + kfree_sensitive(ctx); } static void zstd_exit(struct crypto_tfm *tfm) diff --git a/drivers/crypto/allwinner/sun8i-ce/sun8i-ce-cipher.c b/drivers/crypto/allwinner/sun8i-ce/sun8i-ce-cipher.c index 1e4f9a58bb241..b4d5fea27d20f 100644 --- a/drivers/crypto/allwinner/sun8i-ce/sun8i-ce-cipher.c +++ b/drivers/crypto/allwinner/sun8i-ce/sun8i-ce-cipher.c @@ -254,7 +254,7 @@ theend_iv: offset = areq->cryptlen - ivsize; if (rctx->op_dir & CE_DECRYPTION) { memcpy(areq->iv, backup_iv, ivsize); - kzfree(backup_iv); + kfree_sensitive(backup_iv); } else { scatterwalk_map_and_copy(areq->iv, areq->dst, offset, ivsize, 0); diff --git a/drivers/crypto/allwinner/sun8i-ss/sun8i-ss-cipher.c b/drivers/crypto/allwinner/sun8i-ss/sun8i-ss-cipher.c index 7a131675a41c1..7b39b44955715 100644 --- a/drivers/crypto/allwinner/sun8i-ss/sun8i-ss-cipher.c +++ b/drivers/crypto/allwinner/sun8i-ss/sun8i-ss-cipher.c @@ -249,7 +249,7 @@ theend_iv: if (rctx->op_dir & SS_DECRYPTION) { memcpy(areq->iv, backup_iv, ivsize); memzero_explicit(backup_iv, ivsize); - kzfree(backup_iv); + kfree_sensitive(backup_iv); } else { scatterwalk_map_and_copy(areq->iv, areq->dst, offset, ivsize, 0); diff --git a/drivers/crypto/amlogic/amlogic-gxl-cipher.c b/drivers/crypto/amlogic/amlogic-gxl-cipher.c index 5880b94dcb32d..d93210726697a 100644 --- a/drivers/crypto/amlogic/amlogic-gxl-cipher.c +++ b/drivers/crypto/amlogic/amlogic-gxl-cipher.c @@ -252,8 +252,8 @@ static int meson_cipher(struct skcipher_request *areq) } } theend: - kzfree(bkeyiv); - kzfree(backup_iv); + kfree_sensitive(bkeyiv); + kfree_sensitive(backup_iv); return err; } diff --git a/drivers/crypto/atmel-ecc.c b/drivers/crypto/atmel-ecc.c index ff02cc05affbb..9bd8e5167be34 100644 --- a/drivers/crypto/atmel-ecc.c +++ b/drivers/crypto/atmel-ecc.c @@ -69,7 +69,7 @@ static void atmel_ecdh_done(struct atmel_i2c_work_data *work_data, void *areq, /* fall through */ free_work_data: - kzfree(work_data); + kfree_sensitive(work_data); kpp_request_complete(req, status); } diff --git a/drivers/crypto/caam/caampkc.c b/drivers/crypto/caam/caampkc.c index 2e44d685618fd..dd5f101e43f83 100644 --- a/drivers/crypto/caam/caampkc.c +++ b/drivers/crypto/caam/caampkc.c @@ -854,14 +854,14 @@ static int caam_rsa_dec(struct akcipher_request *req) static void caam_rsa_free_key(struct caam_rsa_key *key) { - kzfree(key->d); - kzfree(key->p); - kzfree(key->q); - kzfree(key->dp); - kzfree(key->dq); - kzfree(key->qinv); - kzfree(key->tmp1); - kzfree(key->tmp2); + kfree_sensitive(key->d); + kfree_sensitive(key->p); + kfree_sensitive(key->q); + kfree_sensitive(key->dp); + kfree_sensitive(key->dq); + kfree_sensitive(key->qinv); + kfree_sensitive(key->tmp1); + kfree_sensitive(key->tmp2); kfree(key->e); kfree(key->n); memset(key, 0, sizeof(*key)); @@ -1018,17 +1018,17 @@ static void caam_rsa_set_priv_key_form(struct caam_rsa_ctx *ctx, return; free_dq: - kzfree(rsa_key->dq); + kfree_sensitive(rsa_key->dq); free_dp: - kzfree(rsa_key->dp); + kfree_sensitive(rsa_key->dp); free_tmp2: - kzfree(rsa_key->tmp2); + kfree_sensitive(rsa_key->tmp2); free_tmp1: - kzfree(rsa_key->tmp1); + kfree_sensitive(rsa_key->tmp1); free_q: - kzfree(rsa_key->q); + kfree_sensitive(rsa_key->q); free_p: - kzfree(rsa_key->p); + kfree_sensitive(rsa_key->p); } static int caam_rsa_set_priv_key(struct crypto_akcipher *tfm, const void *key, diff --git a/drivers/crypto/cavium/cpt/cptvf_main.c b/drivers/crypto/cavium/cpt/cptvf_main.c index 0f72e9abdefe6..a15245992cf99 100644 --- a/drivers/crypto/cavium/cpt/cptvf_main.c +++ b/drivers/crypto/cavium/cpt/cptvf_main.c @@ -74,7 +74,7 @@ static void cleanup_worker_threads(struct cpt_vf *cptvf) for (i = 0; i < cptvf->nr_queues; i++) tasklet_kill(&cwqe_info->vq_wqe[i].twork); - kzfree(cwqe_info); + kfree_sensitive(cwqe_info); cptvf->wqe_info = NULL; } @@ -88,7 +88,7 @@ static void free_pending_queues(struct pending_qinfo *pqinfo) continue; /* free single queue */ - kzfree((queue->head)); + kfree_sensitive((queue->head)); queue->front = 0; queue->rear = 0; @@ -189,7 +189,7 @@ static void free_command_queues(struct cpt_vf *cptvf, chunk->head = NULL; chunk->dma_addr = 0; hlist_del(&chunk->nextchunk); - kzfree(chunk); + kfree_sensitive(chunk); } queue->nchunks = 0; diff --git a/drivers/crypto/cavium/cpt/cptvf_reqmanager.c b/drivers/crypto/cavium/cpt/cptvf_reqmanager.c index 3878b01e19e19..dc5fda5227198 100644 --- a/drivers/crypto/cavium/cpt/cptvf_reqmanager.c +++ b/drivers/crypto/cavium/cpt/cptvf_reqmanager.c @@ -305,12 +305,12 @@ static void do_request_cleanup(struct cpt_vf *cptvf, } } - kzfree(info->scatter_components); - kzfree(info->gather_components); - kzfree(info->out_buffer); - kzfree(info->in_buffer); - kzfree((void *)info->completion_addr); - kzfree(info); + kfree_sensitive(info->scatter_components); + kfree_sensitive(info->gather_components); + kfree_sensitive(info->out_buffer); + kfree_sensitive(info->in_buffer); + kfree_sensitive((void *)info->completion_addr); + kfree_sensitive(info); } static void do_post_process(struct cpt_vf *cptvf, struct cpt_info_buffer *info) diff --git a/drivers/crypto/cavium/nitrox/nitrox_lib.c b/drivers/crypto/cavium/nitrox/nitrox_lib.c index 5cbc64b851b99..a5cdc2b48bd6c 100644 --- a/drivers/crypto/cavium/nitrox/nitrox_lib.c +++ b/drivers/crypto/cavium/nitrox/nitrox_lib.c @@ -90,7 +90,7 @@ static void nitrox_free_aqm_queues(struct nitrox_device *ndev) for (i = 0; i < ndev->nr_queues; i++) { nitrox_cmdq_cleanup(ndev->aqmq[i]); - kzfree(ndev->aqmq[i]); + kfree_sensitive(ndev->aqmq[i]); ndev->aqmq[i] = NULL; } } @@ -122,7 +122,7 @@ static int nitrox_alloc_aqm_queues(struct nitrox_device *ndev) err = nitrox_cmdq_init(cmdq, AQM_Q_ALIGN_BYTES); if (err) { - kzfree(cmdq); + kfree_sensitive(cmdq); goto aqmq_fail; } ndev->aqmq[i] = cmdq; diff --git a/drivers/crypto/cavium/zip/zip_crypto.c b/drivers/crypto/cavium/zip/zip_crypto.c index 4985bc812b0e0..7df71fcebe8f2 100644 --- a/drivers/crypto/cavium/zip/zip_crypto.c +++ b/drivers/crypto/cavium/zip/zip_crypto.c @@ -260,7 +260,7 @@ void *zip_alloc_scomp_ctx_deflate(struct crypto_scomp *tfm) ret = zip_ctx_init(zip_ctx, 0); if (ret) { - kzfree(zip_ctx); + kfree_sensitive(zip_ctx); return ERR_PTR(ret); } @@ -279,7 +279,7 @@ void *zip_alloc_scomp_ctx_lzs(struct crypto_scomp *tfm) ret = zip_ctx_init(zip_ctx, 1); if (ret) { - kzfree(zip_ctx); + kfree_sensitive(zip_ctx); return ERR_PTR(ret); } @@ -291,7 +291,7 @@ void zip_free_scomp_ctx(struct crypto_scomp *tfm, void *ctx) struct zip_kernel_ctx *zip_ctx = ctx; zip_ctx_exit(zip_ctx); - kzfree(zip_ctx); + kfree_sensitive(zip_ctx); } int zip_scomp_compress(struct crypto_scomp *tfm, diff --git a/drivers/crypto/ccp/ccp-crypto-rsa.c b/drivers/crypto/ccp/ccp-crypto-rsa.c index 649c91d604015..1223ac70aea28 100644 --- a/drivers/crypto/ccp/ccp-crypto-rsa.c +++ b/drivers/crypto/ccp/ccp-crypto-rsa.c @@ -112,13 +112,13 @@ static int ccp_check_key_length(unsigned int len) static void ccp_rsa_free_key_bufs(struct ccp_ctx *ctx) { /* Clean up old key data */ - kzfree(ctx->u.rsa.e_buf); + kfree_sensitive(ctx->u.rsa.e_buf); ctx->u.rsa.e_buf = NULL; ctx->u.rsa.e_len = 0; - kzfree(ctx->u.rsa.n_buf); + kfree_sensitive(ctx->u.rsa.n_buf); ctx->u.rsa.n_buf = NULL; ctx->u.rsa.n_len = 0; - kzfree(ctx->u.rsa.d_buf); + kfree_sensitive(ctx->u.rsa.d_buf); ctx->u.rsa.d_buf = NULL; ctx->u.rsa.d_len = 0; } diff --git a/drivers/crypto/ccree/cc_aead.c b/drivers/crypto/ccree/cc_aead.c index 1cf51edbc4b9f..35794c7271fb6 100644 --- a/drivers/crypto/ccree/cc_aead.c +++ b/drivers/crypto/ccree/cc_aead.c @@ -448,7 +448,7 @@ static int cc_get_plain_hmac_key(struct crypto_aead *tfm, const u8 *authkey, if (dma_mapping_error(dev, key_dma_addr)) { dev_err(dev, "Mapping key va=0x%p len=%u for DMA failed\n", key, keylen); - kzfree(key); + kfree_sensitive(key); return -ENOMEM; } if (keylen > blocksize) { @@ -533,7 +533,7 @@ static int cc_get_plain_hmac_key(struct crypto_aead *tfm, const u8 *authkey, if (key_dma_addr) dma_unmap_single(dev, key_dma_addr, keylen, DMA_TO_DEVICE); - kzfree(key); + kfree_sensitive(key); return rc; } diff --git a/drivers/crypto/ccree/cc_buffer_mgr.c b/drivers/crypto/ccree/cc_buffer_mgr.c index b2bd093e70139..a5e041d9d2cf1 100644 --- a/drivers/crypto/ccree/cc_buffer_mgr.c +++ b/drivers/crypto/ccree/cc_buffer_mgr.c @@ -488,7 +488,7 @@ void cc_unmap_aead_request(struct device *dev, struct aead_request *req) if (areq_ctx->gen_ctx.iv_dma_addr) { dma_unmap_single(dev, areq_ctx->gen_ctx.iv_dma_addr, hw_iv_size, DMA_BIDIRECTIONAL); - kzfree(areq_ctx->gen_ctx.iv); + kfree_sensitive(areq_ctx->gen_ctx.iv); } /* Release pool */ @@ -559,7 +559,7 @@ static int cc_aead_chain_iv(struct cc_drvdata *drvdata, if (dma_mapping_error(dev, areq_ctx->gen_ctx.iv_dma_addr)) { dev_err(dev, "Mapping iv %u B at va=%pK for DMA failed\n", hw_iv_size, req->iv); - kzfree(areq_ctx->gen_ctx.iv); + kfree_sensitive(areq_ctx->gen_ctx.iv); areq_ctx->gen_ctx.iv = NULL; rc = -ENOMEM; goto chain_iv_exit; diff --git a/drivers/crypto/ccree/cc_cipher.c b/drivers/crypto/ccree/cc_cipher.c index 076669dc1035b..d77ae981b64b2 100644 --- a/drivers/crypto/ccree/cc_cipher.c +++ b/drivers/crypto/ccree/cc_cipher.c @@ -257,7 +257,7 @@ static void cc_cipher_exit(struct crypto_tfm *tfm) &ctx_p->user.key_dma_addr); /* Free key buffer in context */ - kzfree(ctx_p->user.key); + kfree_sensitive(ctx_p->user.key); dev_dbg(dev, "Free key buffer in context. key=@%p\n", ctx_p->user.key); } @@ -881,7 +881,7 @@ static void cc_cipher_complete(struct device *dev, void *cc_req, int err) /* Not a BACKLOG notification */ cc_unmap_cipher_request(dev, req_ctx, ivsize, src, dst); memcpy(req->iv, req_ctx->iv, ivsize); - kzfree(req_ctx->iv); + kfree_sensitive(req_ctx->iv); } skcipher_request_complete(req, err); @@ -994,7 +994,7 @@ static int cc_cipher_process(struct skcipher_request *req, exit_process: if (rc != -EINPROGRESS && rc != -EBUSY) { - kzfree(req_ctx->iv); + kfree_sensitive(req_ctx->iv); } return rc; diff --git a/drivers/crypto/ccree/cc_hash.c b/drivers/crypto/ccree/cc_hash.c index d5310783af159..683c9a430e11b 100644 --- a/drivers/crypto/ccree/cc_hash.c +++ b/drivers/crypto/ccree/cc_hash.c @@ -764,7 +764,7 @@ static int cc_hash_setkey(struct crypto_ahash *ahash, const u8 *key, if (dma_mapping_error(dev, ctx->key_params.key_dma_addr)) { dev_err(dev, "Mapping key va=0x%p len=%u for DMA failed\n", ctx->key_params.key, keylen); - kzfree(ctx->key_params.key); + kfree_sensitive(ctx->key_params.key); return -ENOMEM; } dev_dbg(dev, "mapping key-buffer: key_dma_addr=%pad keylen=%u\n", @@ -913,7 +913,7 @@ out: &ctx->key_params.key_dma_addr, ctx->key_params.keylen); } - kzfree(ctx->key_params.key); + kfree_sensitive(ctx->key_params.key); return rc; } @@ -950,7 +950,7 @@ static int cc_xcbc_setkey(struct crypto_ahash *ahash, if (dma_mapping_error(dev, ctx->key_params.key_dma_addr)) { dev_err(dev, "Mapping key va=0x%p len=%u for DMA failed\n", key, keylen); - kzfree(ctx->key_params.key); + kfree_sensitive(ctx->key_params.key); return -ENOMEM; } dev_dbg(dev, "mapping key-buffer: key_dma_addr=%pad keylen=%u\n", @@ -999,7 +999,7 @@ static int cc_xcbc_setkey(struct crypto_ahash *ahash, dev_dbg(dev, "Unmapped key-buffer: key_dma_addr=%pad keylen=%u\n", &ctx->key_params.key_dma_addr, ctx->key_params.keylen); - kzfree(ctx->key_params.key); + kfree_sensitive(ctx->key_params.key); return rc; } diff --git a/drivers/crypto/ccree/cc_request_mgr.c b/drivers/crypto/ccree/cc_request_mgr.c index 1d7649ecf44ec..33fb27745d522 100644 --- a/drivers/crypto/ccree/cc_request_mgr.c +++ b/drivers/crypto/ccree/cc_request_mgr.c @@ -107,7 +107,7 @@ void cc_req_mgr_fini(struct cc_drvdata *drvdata) /* Kill tasklet */ tasklet_kill(&req_mgr_h->comptask); #endif - kzfree(req_mgr_h); + kfree_sensitive(req_mgr_h); drvdata->request_mgr_handle = NULL; } diff --git a/drivers/crypto/marvell/cesa/hash.c b/drivers/crypto/marvell/cesa/hash.c index bd0bd9ffd6e90..f2a2fc1111641 100644 --- a/drivers/crypto/marvell/cesa/hash.c +++ b/drivers/crypto/marvell/cesa/hash.c @@ -1157,7 +1157,7 @@ static int mv_cesa_ahmac_pad_init(struct ahash_request *req, } /* Set the memory region to 0 to avoid any leak. */ - kzfree(keydup); + kfree_sensitive(keydup); if (ret) return ret; diff --git a/drivers/crypto/marvell/octeontx/otx_cptvf_main.c b/drivers/crypto/marvell/octeontx/otx_cptvf_main.c index ce3168327a39c..228fe8e47e0ed 100644 --- a/drivers/crypto/marvell/octeontx/otx_cptvf_main.c +++ b/drivers/crypto/marvell/octeontx/otx_cptvf_main.c @@ -68,7 +68,7 @@ static void cleanup_worker_threads(struct otx_cptvf *cptvf) for (i = 0; i < cptvf->num_queues; i++) tasklet_kill(&cwqe_info->vq_wqe[i].twork); - kzfree(cwqe_info); + kfree_sensitive(cwqe_info); cptvf->wqe_info = NULL; } @@ -82,7 +82,7 @@ static void free_pending_queues(struct otx_cpt_pending_qinfo *pqinfo) continue; /* free single queue */ - kzfree((queue->head)); + kfree_sensitive((queue->head)); queue->front = 0; queue->rear = 0; queue->qlen = 0; @@ -176,7 +176,7 @@ static void free_command_queues(struct otx_cptvf *cptvf, chunk->head = NULL; chunk->dma_addr = 0; list_del(&chunk->nextchunk); - kzfree(chunk); + kfree_sensitive(chunk); } queue->num_chunks = 0; queue->idx = 0; diff --git a/drivers/crypto/marvell/octeontx/otx_cptvf_reqmgr.h b/drivers/crypto/marvell/octeontx/otx_cptvf_reqmgr.h index d912fe0c532d4..a02d059fb652d 100644 --- a/drivers/crypto/marvell/octeontx/otx_cptvf_reqmgr.h +++ b/drivers/crypto/marvell/octeontx/otx_cptvf_reqmgr.h @@ -215,7 +215,7 @@ static inline void do_request_cleanup(struct pci_dev *pdev, DMA_BIDIRECTIONAL); } } - kzfree(info); + kfree_sensitive(info); } struct otx_cptvf_wqe; diff --git a/drivers/crypto/nx/nx.c b/drivers/crypto/nx/nx.c index f03c238f5a31c..40882d6d52c18 100644 --- a/drivers/crypto/nx/nx.c +++ b/drivers/crypto/nx/nx.c @@ -746,7 +746,7 @@ void nx_crypto_ctx_exit(struct crypto_tfm *tfm) { struct nx_crypto_ctx *nx_ctx = crypto_tfm_ctx(tfm); - kzfree(nx_ctx->kmem); + kfree_sensitive(nx_ctx->kmem); nx_ctx->csbcpb = NULL; nx_ctx->csbcpb_aead = NULL; nx_ctx->in_sg = NULL; @@ -762,7 +762,7 @@ void nx_crypto_ctx_aead_exit(struct crypto_aead *tfm) { struct nx_crypto_ctx *nx_ctx = crypto_aead_ctx(tfm); - kzfree(nx_ctx->kmem); + kfree_sensitive(nx_ctx->kmem); } static int nx_probe(struct vio_dev *viodev, const struct vio_device_id *id) diff --git a/drivers/crypto/virtio/virtio_crypto_algs.c b/drivers/crypto/virtio/virtio_crypto_algs.c index b2601958282e0..583c0b535d13b 100644 --- a/drivers/crypto/virtio/virtio_crypto_algs.c +++ b/drivers/crypto/virtio/virtio_crypto_algs.c @@ -167,7 +167,7 @@ static int virtio_crypto_alg_skcipher_init_session( num_in, vcrypto, GFP_ATOMIC); if (err < 0) { spin_unlock(&vcrypto->ctrl_lock); - kzfree(cipher_key); + kfree_sensitive(cipher_key); return err; } virtqueue_kick(vcrypto->ctrl_vq); @@ -184,7 +184,7 @@ static int virtio_crypto_alg_skcipher_init_session( spin_unlock(&vcrypto->ctrl_lock); pr_err("virtio_crypto: Create session failed status: %u\n", le32_to_cpu(vcrypto->input.status)); - kzfree(cipher_key); + kfree_sensitive(cipher_key); return -EINVAL; } @@ -197,7 +197,7 @@ static int virtio_crypto_alg_skcipher_init_session( spin_unlock(&vcrypto->ctrl_lock); - kzfree(cipher_key); + kfree_sensitive(cipher_key); return 0; } @@ -472,9 +472,9 @@ __virtio_crypto_skcipher_do_req(struct virtio_crypto_sym_request *vc_sym_req, return 0; free_iv: - kzfree(iv); + kfree_sensitive(iv); free: - kzfree(req_data); + kfree_sensitive(req_data); kfree(sgs); return err; } @@ -583,7 +583,7 @@ static void virtio_crypto_skcipher_finalize_req( scatterwalk_map_and_copy(req->iv, req->dst, req->cryptlen - AES_BLOCK_SIZE, AES_BLOCK_SIZE, 0); - kzfree(vc_sym_req->iv); + kfree_sensitive(vc_sym_req->iv); virtcrypto_clear_request(&vc_sym_req->base); crypto_finalize_skcipher_request(vc_sym_req->base.dataq->engine, diff --git a/drivers/crypto/virtio/virtio_crypto_core.c b/drivers/crypto/virtio/virtio_crypto_core.c index 77e744eaedd02..0c66d6193ca2f 100644 --- a/drivers/crypto/virtio/virtio_crypto_core.c +++ b/drivers/crypto/virtio/virtio_crypto_core.c @@ -17,7 +17,7 @@ void virtcrypto_clear_request(struct virtio_crypto_request *vc_req) { if (vc_req) { - kzfree(vc_req->req_data); + kfree_sensitive(vc_req->req_data); kfree(vc_req->sgs); } } diff --git a/drivers/md/dm-crypt.c b/drivers/md/dm-crypt.c index b437a14c4942a..37dcc52cf21dc 100644 --- a/drivers/md/dm-crypt.c +++ b/drivers/md/dm-crypt.c @@ -407,7 +407,7 @@ static void crypt_iv_lmk_dtr(struct crypt_config *cc) crypto_free_shash(lmk->hash_tfm); lmk->hash_tfm = NULL; - kzfree(lmk->seed); + kfree_sensitive(lmk->seed); lmk->seed = NULL; } @@ -558,9 +558,9 @@ static void crypt_iv_tcw_dtr(struct crypt_config *cc) { struct iv_tcw_private *tcw = &cc->iv_gen_private.tcw; - kzfree(tcw->iv_seed); + kfree_sensitive(tcw->iv_seed); tcw->iv_seed = NULL; - kzfree(tcw->whitening); + kfree_sensitive(tcw->whitening); tcw->whitening = NULL; if (tcw->crc32_tfm && !IS_ERR(tcw->crc32_tfm)) @@ -994,8 +994,8 @@ static int crypt_iv_elephant(struct crypt_config *cc, struct dm_crypt_request *d kunmap_atomic(data); out: - kzfree(ks); - kzfree(es); + kfree_sensitive(ks); + kfree_sensitive(es); skcipher_request_free(req); return r; } @@ -2294,7 +2294,7 @@ static int crypt_set_keyring_key(struct crypt_config *cc, const char *key_string key = request_key(type, key_desc + 1, NULL); if (IS_ERR(key)) { - kzfree(new_key_string); + kfree_sensitive(new_key_string); return PTR_ERR(key); } @@ -2304,7 +2304,7 @@ static int crypt_set_keyring_key(struct crypt_config *cc, const char *key_string if (ret < 0) { up_read(&key->sem); key_put(key); - kzfree(new_key_string); + kfree_sensitive(new_key_string); return ret; } @@ -2318,10 +2318,10 @@ static int crypt_set_keyring_key(struct crypt_config *cc, const char *key_string if (!ret) { set_bit(DM_CRYPT_KEY_VALID, &cc->flags); - kzfree(cc->key_string); + kfree_sensitive(cc->key_string); cc->key_string = new_key_string; } else - kzfree(new_key_string); + kfree_sensitive(new_key_string); return ret; } @@ -2382,7 +2382,7 @@ static int crypt_set_key(struct crypt_config *cc, char *key) clear_bit(DM_CRYPT_KEY_VALID, &cc->flags); /* wipe references to any kernel keyring key */ - kzfree(cc->key_string); + kfree_sensitive(cc->key_string); cc->key_string = NULL; /* Decode key from its hex representation. */ @@ -2414,7 +2414,7 @@ static int crypt_wipe_key(struct crypt_config *cc) return r; } - kzfree(cc->key_string); + kfree_sensitive(cc->key_string); cc->key_string = NULL; r = crypt_setkey(cc); memset(&cc->key, 0, cc->key_size * sizeof(u8)); @@ -2493,15 +2493,15 @@ static void crypt_dtr(struct dm_target *ti) if (cc->dev) dm_put_device(ti, cc->dev); - kzfree(cc->cipher_string); - kzfree(cc->key_string); - kzfree(cc->cipher_auth); - kzfree(cc->authenc_key); + kfree_sensitive(cc->cipher_string); + kfree_sensitive(cc->key_string); + kfree_sensitive(cc->cipher_auth); + kfree_sensitive(cc->authenc_key); mutex_destroy(&cc->bio_alloc_lock); /* Must zero key material before freeing */ - kzfree(cc); + kfree_sensitive(cc); spin_lock(&dm_crypt_clients_lock); WARN_ON(!dm_crypt_clients_n); diff --git a/drivers/md/dm-integrity.c b/drivers/md/dm-integrity.c index 5da3eb661e502..8c8d940e532ef 100644 --- a/drivers/md/dm-integrity.c +++ b/drivers/md/dm-integrity.c @@ -3405,8 +3405,8 @@ static struct scatterlist **dm_integrity_alloc_journal_scatterlist(struct dm_int static void free_alg(struct alg_spec *a) { - kzfree(a->alg_string); - kzfree(a->key); + kfree_sensitive(a->alg_string); + kfree_sensitive(a->key); memset(a, 0, sizeof *a); } @@ -4337,7 +4337,7 @@ static void dm_integrity_dtr(struct dm_target *ti) for (i = 0; i < ic->journal_sections; i++) { struct skcipher_request *req = ic->sk_requests[i]; if (req) { - kzfree(req->iv); + kfree_sensitive(req->iv); skcipher_request_free(req); } } diff --git a/drivers/misc/ibmvmc.c b/drivers/misc/ibmvmc.c index c0d139c265055..2d778d0f011e6 100644 --- a/drivers/misc/ibmvmc.c +++ b/drivers/misc/ibmvmc.c @@ -286,7 +286,7 @@ static void *alloc_dma_buffer(struct vio_dev *vdev, size_t size, if (dma_mapping_error(&vdev->dev, *dma_handle)) { *dma_handle = 0; - kzfree(buffer); + kfree_sensitive(buffer); return NULL; } @@ -310,7 +310,7 @@ static void free_dma_buffer(struct vio_dev *vdev, size_t size, void *vaddr, dma_unmap_single(&vdev->dev, dma_handle, size, DMA_BIDIRECTIONAL); /* deallocate memory */ - kzfree(vaddr); + kfree_sensitive(vaddr); } /** @@ -883,7 +883,7 @@ static int ibmvmc_close(struct inode *inode, struct file *file) spin_unlock_irqrestore(&hmc->lock, flags); } - kzfree(session); + kfree_sensitive(session); return rc; } diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_mbx.c b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_mbx.c index 0874ae47cb034..3ab6db2588d31 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_mbx.c +++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_mbx.c @@ -137,7 +137,7 @@ static void hclge_free_vector_ring_chain(struct hnae3_ring_chain_node *head) while (chain) { chain_tmp = chain->next; - kzfree(chain); + kfree_sensitive(chain); chain = chain_tmp; } } diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_ipsec.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_ipsec.c index 6516980965a2e..eca73526ac86b 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_ipsec.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_ipsec.c @@ -960,9 +960,9 @@ int ixgbe_ipsec_vf_add_sa(struct ixgbe_adapter *adapter, u32 *msgbuf, u32 vf) return 0; err_aead: - kzfree(xs->aead); + kfree_sensitive(xs->aead); err_xs: - kzfree(xs); + kfree_sensitive(xs); err_out: msgbuf[1] = err; return err; @@ -1047,7 +1047,7 @@ int ixgbe_ipsec_vf_del_sa(struct ixgbe_adapter *adapter, u32 *msgbuf, u32 vf) ixgbe_ipsec_del_sa(xs); /* remove the xs that was made-up in the add request */ - kzfree(xs); + kfree_sensitive(xs); return 0; } diff --git a/drivers/net/ppp/ppp_mppe.c b/drivers/net/ppp/ppp_mppe.c index de3b57d09d0cb..208f6e24f37c4 100644 --- a/drivers/net/ppp/ppp_mppe.c +++ b/drivers/net/ppp/ppp_mppe.c @@ -222,7 +222,7 @@ out_free: kfree(state->sha1_digest); if (state->sha1) { crypto_free_shash(state->sha1->tfm); - kzfree(state->sha1); + kfree_sensitive(state->sha1); } kfree(state); out: @@ -238,8 +238,8 @@ static void mppe_free(void *arg) if (state) { kfree(state->sha1_digest); crypto_free_shash(state->sha1->tfm); - kzfree(state->sha1); - kzfree(state); + kfree_sensitive(state->sha1); + kfree_sensitive(state); } } diff --git a/drivers/net/wireguard/noise.c b/drivers/net/wireguard/noise.c index 201a22681945f..3dd3b76790d05 100644 --- a/drivers/net/wireguard/noise.c +++ b/drivers/net/wireguard/noise.c @@ -114,7 +114,7 @@ static struct noise_keypair *keypair_create(struct wg_peer *peer) static void keypair_free_rcu(struct rcu_head *rcu) { - kzfree(container_of(rcu, struct noise_keypair, rcu)); + kfree_sensitive(container_of(rcu, struct noise_keypair, rcu)); } static void keypair_free_kref(struct kref *kref) @@ -821,7 +821,7 @@ bool wg_noise_handshake_begin_session(struct noise_handshake *handshake, handshake->entry.peer->device->index_hashtable, &handshake->entry, &new_keypair->entry); } else { - kzfree(new_keypair); + kfree_sensitive(new_keypair); } rcu_read_unlock_bh(); diff --git a/drivers/net/wireguard/peer.c b/drivers/net/wireguard/peer.c index 1d634bd3038f0..b3b6370e6b959 100644 --- a/drivers/net/wireguard/peer.c +++ b/drivers/net/wireguard/peer.c @@ -203,7 +203,7 @@ static void rcu_release(struct rcu_head *rcu) /* The final zeroing takes care of clearing any remaining handshake key * material and other potentially sensitive information. */ - kzfree(peer); + kfree_sensitive(peer); } static void kref_release(struct kref *refcount) diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/rx.c b/drivers/net/wireless/intel/iwlwifi/pcie/rx.c index 24cb1b1f21f0e..9463c108aa966 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/rx.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/rx.c @@ -1369,7 +1369,7 @@ static void iwl_pcie_rx_handle_rb(struct iwl_trans *trans, &rxcb, rxq->id); if (reclaim) { - kzfree(txq->entries[cmd_index].free_buf); + kfree_sensitive(txq->entries[cmd_index].free_buf); txq->entries[cmd_index].free_buf = NULL; } diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/tx-gen2.c b/drivers/net/wireless/intel/iwlwifi/pcie/tx-gen2.c index 7fc7542535d84..606bef2ecc7b2 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/tx-gen2.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/tx-gen2.c @@ -1026,7 +1026,7 @@ static int iwl_pcie_gen2_enqueue_hcmd(struct iwl_trans *trans, BUILD_BUG_ON(IWL_TFH_NUM_TBS > sizeof(out_meta->tbs) * BITS_PER_BYTE); out_meta->flags = cmd->flags; if (WARN_ON_ONCE(txq->entries[idx].free_buf)) - kzfree(txq->entries[idx].free_buf); + kfree_sensitive(txq->entries[idx].free_buf); txq->entries[idx].free_buf = dup_buf; trace_iwlwifi_dev_hcmd(trans->dev, cmd, cmd_size, &out_cmd->hdr_wide); @@ -1257,8 +1257,8 @@ static void iwl_pcie_gen2_txq_free(struct iwl_trans *trans, int txq_id) /* De-alloc array of command/tx buffers */ if (txq_id == trans->txqs.cmd.q_id) for (i = 0; i < txq->n_window; i++) { - kzfree(txq->entries[i].cmd); - kzfree(txq->entries[i].free_buf); + kfree_sensitive(txq->entries[i].cmd); + kfree_sensitive(txq->entries[i].free_buf); } del_timer_sync(&txq->stuck_timer); diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/tx.c b/drivers/net/wireless/intel/iwlwifi/pcie/tx.c index 5c6c3fa0d29f3..eb396c06b7fb4 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/tx.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/tx.c @@ -721,8 +721,8 @@ static void iwl_pcie_txq_free(struct iwl_trans *trans, int txq_id) /* De-alloc array of command/tx buffers */ if (txq_id == trans->txqs.cmd.q_id) for (i = 0; i < txq->n_window; i++) { - kzfree(txq->entries[i].cmd); - kzfree(txq->entries[i].free_buf); + kfree_sensitive(txq->entries[i].cmd); + kfree_sensitive(txq->entries[i].free_buf); } /* De-alloc circular buffer of TFDs */ @@ -1765,7 +1765,7 @@ static int iwl_pcie_enqueue_hcmd(struct iwl_trans *trans, BUILD_BUG_ON(IWL_TFH_NUM_TBS > sizeof(out_meta->tbs) * BITS_PER_BYTE); out_meta->flags = cmd->flags; if (WARN_ON_ONCE(txq->entries[idx].free_buf)) - kzfree(txq->entries[idx].free_buf); + kfree_sensitive(txq->entries[idx].free_buf); txq->entries[idx].free_buf = dup_buf; trace_iwlwifi_dev_hcmd(trans->dev, cmd, cmd_size, &out_cmd->hdr_wide); diff --git a/drivers/net/wireless/intersil/orinoco/wext.c b/drivers/net/wireless/intersil/orinoco/wext.c index 1d4dae422106c..7b6c4ae8ddb35 100644 --- a/drivers/net/wireless/intersil/orinoco/wext.c +++ b/drivers/net/wireless/intersil/orinoco/wext.c @@ -31,8 +31,8 @@ static int orinoco_set_key(struct orinoco_private *priv, int index, enum orinoco_alg alg, const u8 *key, int key_len, const u8 *seq, int seq_len) { - kzfree(priv->keys[index].key); - kzfree(priv->keys[index].seq); + kfree_sensitive(priv->keys[index].key); + kfree_sensitive(priv->keys[index].seq); if (key_len) { priv->keys[index].key = kzalloc(key_len, GFP_ATOMIC); diff --git a/drivers/s390/crypto/ap_bus.h b/drivers/s390/crypto/ap_bus.h index 1a1d5e3c8d459..1ea046324e8f6 100644 --- a/drivers/s390/crypto/ap_bus.h +++ b/drivers/s390/crypto/ap_bus.h @@ -219,8 +219,8 @@ static inline void ap_init_message(struct ap_message *ap_msg) */ static inline void ap_release_message(struct ap_message *ap_msg) { - kzfree(ap_msg->msg); - kzfree(ap_msg->private); + kfree_sensitive(ap_msg->msg); + kfree_sensitive(ap_msg->private); } /* diff --git a/drivers/staging/ks7010/ks_hostif.c b/drivers/staging/ks7010/ks_hostif.c index b10a92ae2067b..eaaf6a5440a91 100644 --- a/drivers/staging/ks7010/ks_hostif.c +++ b/drivers/staging/ks7010/ks_hostif.c @@ -245,7 +245,7 @@ michael_mic(u8 *key, u8 *data, unsigned int len, u8 priority, u8 *result) ret = crypto_shash_finup(desc, data + 12, len - 12, result); err_free_desc: - kzfree(desc); + kfree_sensitive(desc); err_free_tfm: crypto_free_shash(tfm); diff --git a/drivers/staging/rtl8723bs/core/rtw_security.c b/drivers/staging/rtl8723bs/core/rtw_security.c index 0f15c96183a0c..7f74e1d05b3ad 100644 --- a/drivers/staging/rtl8723bs/core/rtw_security.c +++ b/drivers/staging/rtl8723bs/core/rtw_security.c @@ -2251,7 +2251,7 @@ static void gf_mulx(u8 *pad) static void aes_encrypt_deinit(void *ctx) { - kzfree(ctx); + kfree_sensitive(ctx); } diff --git a/drivers/staging/wlan-ng/p80211netdev.c b/drivers/staging/wlan-ng/p80211netdev.c index b809c0015c0ca..7b091c5a29845 100644 --- a/drivers/staging/wlan-ng/p80211netdev.c +++ b/drivers/staging/wlan-ng/p80211netdev.c @@ -429,7 +429,7 @@ static netdev_tx_t p80211knetdev_hard_start_xmit(struct sk_buff *skb, failed: /* Free up the WEP buffer if it's not the same as the skb */ if ((p80211_wep.data) && (p80211_wep.data != skb->data)) - kzfree(p80211_wep.data); + kfree_sensitive(p80211_wep.data); /* we always free the skb here, never in a lower level. */ if (!result) diff --git a/drivers/target/iscsi/iscsi_target_auth.c b/drivers/target/iscsi/iscsi_target_auth.c index 0e54627d9aa8a..62d912b79c612 100644 --- a/drivers/target/iscsi/iscsi_target_auth.c +++ b/drivers/target/iscsi/iscsi_target_auth.c @@ -484,7 +484,7 @@ static int chap_server_compute_hash( pr_debug("[server] Sending CHAP_R=0x%s\n", response); auth_ret = 0; out: - kzfree(desc); + kfree_sensitive(desc); if (tfm) crypto_free_shash(tfm); kfree(initiatorchg); diff --git a/fs/cifs/cifsencrypt.c b/fs/cifs/cifsencrypt.c index 874a551f339c7..9daa256f69d41 100644 --- a/fs/cifs/cifsencrypt.c +++ b/fs/cifs/cifsencrypt.c @@ -797,7 +797,7 @@ calc_seckey(struct cifs_ses *ses) ses->auth_key.len = CIFS_SESS_KEY_SIZE; memzero_explicit(sec_key, CIFS_SESS_KEY_SIZE); - kzfree(ctx_arc4); + kfree_sensitive(ctx_arc4); return 0; } diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c index a61abde09ffe1..889fee586d94f 100644 --- a/fs/cifs/connect.c +++ b/fs/cifs/connect.c @@ -2182,7 +2182,7 @@ cifs_parse_mount_options(const char *mountdata, const char *devname, tmp_end++; if (!(tmp_end < end && tmp_end[1] == delim)) { /* No it is not. Set the password to NULL */ - kzfree(vol->password); + kfree_sensitive(vol->password); vol->password = NULL; break; } @@ -2220,7 +2220,7 @@ cifs_parse_mount_options(const char *mountdata, const char *devname, options = end; } - kzfree(vol->password); + kfree_sensitive(vol->password); /* Now build new password string */ temp_len = strlen(value); vol->password = kzalloc(temp_len+1, GFP_KERNEL); @@ -3198,7 +3198,7 @@ cifs_set_cifscreds(struct smb_vol *vol, struct cifs_ses *ses) rc = -ENOMEM; kfree(vol->username); vol->username = NULL; - kzfree(vol->password); + kfree_sensitive(vol->password); vol->password = NULL; goto out_key_put; } @@ -4219,7 +4219,7 @@ void cifs_cleanup_volume_info_contents(struct smb_vol *volume_info) { kfree(volume_info->username); - kzfree(volume_info->password); + kfree_sensitive(volume_info->password); kfree(volume_info->UNC); kfree(volume_info->domainname); kfree(volume_info->iocharset); @@ -5345,7 +5345,7 @@ cifs_construct_tcon(struct cifs_sb_info *cifs_sb, kuid_t fsuid) out: kfree(vol_info->username); - kzfree(vol_info->password); + kfree_sensitive(vol_info->password); kfree(vol_info); return tcon; diff --git a/fs/cifs/dfs_cache.c b/fs/cifs/dfs_cache.c index df81c718d2fae..7a8d41493aad5 100644 --- a/fs/cifs/dfs_cache.c +++ b/fs/cifs/dfs_cache.c @@ -1131,7 +1131,7 @@ err_free_domainname: err_free_unc: kfree(new->UNC); err_free_password: - kzfree(new->password); + kfree_sensitive(new->password); err_free_username: kfree(new->username); kfree(new); diff --git a/fs/cifs/misc.c b/fs/cifs/misc.c index e44d049142d04..3e3772446c715 100644 --- a/fs/cifs/misc.c +++ b/fs/cifs/misc.c @@ -103,12 +103,12 @@ sesInfoFree(struct cifs_ses *buf_to_free) kfree(buf_to_free->serverOS); kfree(buf_to_free->serverDomain); kfree(buf_to_free->serverNOS); - kzfree(buf_to_free->password); + kfree_sensitive(buf_to_free->password); kfree(buf_to_free->user_name); kfree(buf_to_free->domainName); - kzfree(buf_to_free->auth_key.response); + kfree_sensitive(buf_to_free->auth_key.response); kfree(buf_to_free->iface_list); - kzfree(buf_to_free); + kfree_sensitive(buf_to_free); } struct cifs_tcon * @@ -148,7 +148,7 @@ tconInfoFree(struct cifs_tcon *buf_to_free) } atomic_dec(&tconInfoAllocCount); kfree(buf_to_free->nativeFileSystem); - kzfree(buf_to_free->password); + kfree_sensitive(buf_to_free->password); kfree(buf_to_free->crfid.fid); #ifdef CONFIG_CIFS_DFS_UPCALL kfree(buf_to_free->dfs_path); diff --git a/fs/crypto/inline_crypt.c b/fs/crypto/inline_crypt.c index b6b8574caa13c..faa25541ccb68 100644 --- a/fs/crypto/inline_crypt.c +++ b/fs/crypto/inline_crypt.c @@ -16,6 +16,7 @@ #include #include #include +#include #include "fscrypt_private.h" @@ -187,7 +188,7 @@ int fscrypt_prepare_inline_crypt_key(struct fscrypt_prepared_key *prep_key, fail: for (i = 0; i < queue_refs; i++) blk_put_queue(blk_key->devs[i]); - kzfree(blk_key); + kfree_sensitive(blk_key); return err; } @@ -201,7 +202,7 @@ void fscrypt_destroy_inline_crypt_key(struct fscrypt_prepared_key *prep_key) blk_crypto_evict_key(blk_key->devs[i], &blk_key->base); blk_put_queue(blk_key->devs[i]); } - kzfree(blk_key); + kfree_sensitive(blk_key); } } diff --git a/fs/crypto/keyring.c b/fs/crypto/keyring.c index 71d56f8e28708..e74f239c44280 100644 --- a/fs/crypto/keyring.c +++ b/fs/crypto/keyring.c @@ -51,7 +51,7 @@ static void free_master_key(struct fscrypt_master_key *mk) } key_put(mk->mk_users); - kzfree(mk); + kfree_sensitive(mk); } static inline bool valid_key_spec(const struct fscrypt_key_specifier *spec) @@ -531,7 +531,7 @@ static int fscrypt_provisioning_key_preparse(struct key_preparsed_payload *prep) static void fscrypt_provisioning_key_free_preparse( struct key_preparsed_payload *prep) { - kzfree(prep->payload.data[0]); + kfree_sensitive(prep->payload.data[0]); } static void fscrypt_provisioning_key_describe(const struct key *key, @@ -548,7 +548,7 @@ static void fscrypt_provisioning_key_describe(const struct key *key, static void fscrypt_provisioning_key_destroy(struct key *key) { - kzfree(key->payload.data[0]); + kfree_sensitive(key->payload.data[0]); } static struct key_type key_type_fscrypt_provisioning = { diff --git a/fs/crypto/keysetup_v1.c b/fs/crypto/keysetup_v1.c index e4e707fb1100e..a3cb52572b05c 100644 --- a/fs/crypto/keysetup_v1.c +++ b/fs/crypto/keysetup_v1.c @@ -155,7 +155,7 @@ static void free_direct_key(struct fscrypt_direct_key *dk) { if (dk) { fscrypt_destroy_prepared_key(&dk->dk_key); - kzfree(dk); + kfree_sensitive(dk); } } @@ -283,7 +283,7 @@ static int setup_v1_file_key_derived(struct fscrypt_info *ci, err = fscrypt_set_per_file_enc_key(ci, derived_key); out: - kzfree(derived_key); + kfree_sensitive(derived_key); return err; } diff --git a/fs/ecryptfs/keystore.c b/fs/ecryptfs/keystore.c index af3eb02bbca1d..f6a17d259db74 100644 --- a/fs/ecryptfs/keystore.c +++ b/fs/ecryptfs/keystore.c @@ -838,7 +838,7 @@ ecryptfs_write_tag_70_packet(char *dest, size_t *remaining_bytes, out_release_free_unlock: crypto_free_shash(s->hash_tfm); out_free_unlock: - kzfree(s->block_aligned_filename); + kfree_sensitive(s->block_aligned_filename); out_unlock: mutex_unlock(s->tfm_mutex); out: @@ -847,7 +847,7 @@ out: key_put(auth_tok_key); } skcipher_request_free(s->skcipher_req); - kzfree(s->hash_desc); + kfree_sensitive(s->hash_desc); kfree(s); return rc; } diff --git a/fs/ecryptfs/messaging.c b/fs/ecryptfs/messaging.c index 8646ba76def34..c0dfd9647627a 100644 --- a/fs/ecryptfs/messaging.c +++ b/fs/ecryptfs/messaging.c @@ -175,7 +175,7 @@ int ecryptfs_exorcise_daemon(struct ecryptfs_daemon *daemon) } hlist_del(&daemon->euid_chain); mutex_unlock(&daemon->mux); - kzfree(daemon); + kfree_sensitive(daemon); out: return rc; } diff --git a/include/crypto/aead.h b/include/crypto/aead.h index 62c68550aab61..c32a6f5664e9a 100644 --- a/include/crypto/aead.h +++ b/include/crypto/aead.h @@ -425,7 +425,7 @@ static inline struct aead_request *aead_request_alloc(struct crypto_aead *tfm, */ static inline void aead_request_free(struct aead_request *req) { - kzfree(req); + kfree_sensitive(req); } /** diff --git a/include/crypto/akcipher.h b/include/crypto/akcipher.h index 6924b091adec1..1d3aa252cabaf 100644 --- a/include/crypto/akcipher.h +++ b/include/crypto/akcipher.h @@ -207,7 +207,7 @@ static inline struct akcipher_request *akcipher_request_alloc( */ static inline void akcipher_request_free(struct akcipher_request *req) { - kzfree(req); + kfree_sensitive(req); } /** diff --git a/include/crypto/gf128mul.h b/include/crypto/gf128mul.h index fa0a63d298dc4..81330c6446f6a 100644 --- a/include/crypto/gf128mul.h +++ b/include/crypto/gf128mul.h @@ -230,7 +230,7 @@ void gf128mul_4k_bbe(be128 *a, const struct gf128mul_4k *t); void gf128mul_x8_ble(le128 *r, const le128 *x); static inline void gf128mul_free_4k(struct gf128mul_4k *t) { - kzfree(t); + kfree_sensitive(t); } diff --git a/include/crypto/hash.h b/include/crypto/hash.h index 19ce91f2359f5..0d1b403888c99 100644 --- a/include/crypto/hash.h +++ b/include/crypto/hash.h @@ -606,7 +606,7 @@ static inline struct ahash_request *ahash_request_alloc( */ static inline void ahash_request_free(struct ahash_request *req) { - kzfree(req); + kfree_sensitive(req); } static inline void ahash_request_zero(struct ahash_request *req) diff --git a/include/crypto/internal/acompress.h b/include/crypto/internal/acompress.h index cf478681b53e7..cfc47e18820fb 100644 --- a/include/crypto/internal/acompress.h +++ b/include/crypto/internal/acompress.h @@ -46,7 +46,7 @@ static inline struct acomp_req *__acomp_request_alloc(struct crypto_acomp *tfm) static inline void __acomp_request_free(struct acomp_req *req) { - kzfree(req); + kfree_sensitive(req); } /** diff --git a/include/crypto/kpp.h b/include/crypto/kpp.h index cd9a9b500624b..88b591215d5c8 100644 --- a/include/crypto/kpp.h +++ b/include/crypto/kpp.h @@ -187,7 +187,7 @@ static inline struct kpp_request *kpp_request_alloc(struct crypto_kpp *tfm, */ static inline void kpp_request_free(struct kpp_request *req) { - kzfree(req); + kfree_sensitive(req); } /** diff --git a/include/crypto/skcipher.h b/include/crypto/skcipher.h index 5663f71198b37..6a733b171a5d0 100644 --- a/include/crypto/skcipher.h +++ b/include/crypto/skcipher.h @@ -508,7 +508,7 @@ static inline struct skcipher_request *skcipher_request_alloc( */ static inline void skcipher_request_free(struct skcipher_request *req) { - kzfree(req); + kfree_sensitive(req); } static inline void skcipher_request_zero(struct skcipher_request *req) diff --git a/include/linux/slab.h b/include/linux/slab.h index 6d454886bcafb..0884d82c55ee5 100644 --- a/include/linux/slab.h +++ b/include/linux/slab.h @@ -186,10 +186,12 @@ void memcg_deactivate_kmem_caches(struct mem_cgroup *, struct mem_cgroup *); */ void * __must_check krealloc(const void *, size_t, gfp_t); void kfree(const void *); -void kzfree(const void *); +void kfree_sensitive(const void *); size_t __ksize(const void *); size_t ksize(const void *); +#define kzfree(x) kfree_sensitive(x) /* For backward compatibility */ + #ifdef CONFIG_HAVE_HARDENED_USERCOPY_ALLOCATOR void __check_heap_object(const void *ptr, unsigned long n, struct page *page, bool to_user); diff --git a/lib/mpi/mpiutil.c b/lib/mpi/mpiutil.c index 20ed0f7667871..4cd2b335cb7f5 100644 --- a/lib/mpi/mpiutil.c +++ b/lib/mpi/mpiutil.c @@ -69,7 +69,7 @@ void mpi_free_limb_space(mpi_ptr_t a) if (!a) return; - kzfree(a); + kfree_sensitive(a); } void mpi_assign_limb_space(MPI a, mpi_ptr_t ap, unsigned nlimbs) @@ -95,7 +95,7 @@ int mpi_resize(MPI a, unsigned nlimbs) if (!p) return -ENOMEM; memcpy(p, a->d, a->alloced * sizeof(mpi_limb_t)); - kzfree(a->d); + kfree_sensitive(a->d); a->d = p; } else { a->d = kcalloc(nlimbs, sizeof(mpi_limb_t), GFP_KERNEL); @@ -112,7 +112,7 @@ void mpi_free(MPI a) return; if (a->flags & 4) - kzfree(a->d); + kfree_sensitive(a->d); else mpi_free_limb_space(a->d); diff --git a/lib/test_kasan.c b/lib/test_kasan.c index dc2c6a51d11a0..e4d9a86b174b4 100644 --- a/lib/test_kasan.c +++ b/lib/test_kasan.c @@ -766,15 +766,15 @@ static noinline void __init kmalloc_double_kzfree(void) char *ptr; size_t size = 16; - pr_info("double-free (kzfree)\n"); + pr_info("double-free (kfree_sensitive)\n"); ptr = kmalloc(size, GFP_KERNEL); if (!ptr) { pr_err("Allocation failed\n"); return; } - kzfree(ptr); - kzfree(ptr); + kfree_sensitive(ptr); + kfree_sensitive(ptr); } #ifdef CONFIG_KASAN_VMALLOC diff --git a/mm/slab_common.c b/mm/slab_common.c index fe8b68482670d..f47a097bb4b8e 100644 --- a/mm/slab_common.c +++ b/mm/slab_common.c @@ -1729,17 +1729,17 @@ void *krealloc(const void *p, size_t new_size, gfp_t flags) EXPORT_SYMBOL(krealloc); /** - * kzfree - like kfree but zero memory + * kfree_sensitive - Clear sensitive information in memory before freeing * @p: object to free memory of * * The memory of the object @p points to is zeroed before freed. - * If @p is %NULL, kzfree() does nothing. + * If @p is %NULL, kfree_sensitive() does nothing. * * Note: this function zeroes the whole allocated buffer which can be a good * deal bigger than the requested buffer size passed to kmalloc(). So be * careful when using this function in performance sensitive code. */ -void kzfree(const void *p) +void kfree_sensitive(const void *p) { size_t ks; void *mem = (void *)p; @@ -1750,7 +1750,7 @@ void kzfree(const void *p) memzero_explicit(mem, ks); kfree(mem); } -EXPORT_SYMBOL(kzfree); +EXPORT_SYMBOL(kfree_sensitive); /** * ksize - get the actual amount of memory allocated for a given object diff --git a/net/atm/mpoa_caches.c b/net/atm/mpoa_caches.c index 3286f9d527d3b..f7a2f0e411057 100644 --- a/net/atm/mpoa_caches.c +++ b/net/atm/mpoa_caches.c @@ -180,7 +180,7 @@ static int cache_hit(in_cache_entry *entry, struct mpoa_client *mpc) static void in_cache_put(in_cache_entry *entry) { if (refcount_dec_and_test(&entry->use)) { - kzfree(entry); + kfree_sensitive(entry); } } @@ -415,7 +415,7 @@ static eg_cache_entry *eg_cache_get_by_src_ip(__be32 ipaddr, static void eg_cache_put(eg_cache_entry *entry) { if (refcount_dec_and_test(&entry->use)) { - kzfree(entry); + kfree_sensitive(entry); } } diff --git a/net/bluetooth/ecdh_helper.c b/net/bluetooth/ecdh_helper.c index 2155ce8028774..3226fe02e8754 100644 --- a/net/bluetooth/ecdh_helper.c +++ b/net/bluetooth/ecdh_helper.c @@ -104,7 +104,7 @@ int compute_ecdh_secret(struct crypto_kpp *tfm, const u8 public_key[64], free_all: kpp_request_free(req); free_tmp: - kzfree(tmp); + kfree_sensitive(tmp); return err; } @@ -151,9 +151,9 @@ int set_ecdh_privkey(struct crypto_kpp *tfm, const u8 private_key[32]) err = crypto_kpp_set_secret(tfm, buf, buf_len); /* fall through */ free_all: - kzfree(buf); + kfree_sensitive(buf); free_tmp: - kzfree(tmp); + kfree_sensitive(tmp); return err; } diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c index 433227f96c732..bf4bef13d9354 100644 --- a/net/bluetooth/smp.c +++ b/net/bluetooth/smp.c @@ -753,9 +753,9 @@ static void smp_chan_destroy(struct l2cap_conn *conn) complete = test_bit(SMP_FLAG_COMPLETE, &smp->flags); mgmt_smp_complete(hcon, complete); - kzfree(smp->csrk); - kzfree(smp->slave_csrk); - kzfree(smp->link_key); + kfree_sensitive(smp->csrk); + kfree_sensitive(smp->slave_csrk); + kfree_sensitive(smp->link_key); crypto_free_shash(smp->tfm_cmac); crypto_free_kpp(smp->tfm_ecdh); @@ -789,7 +789,7 @@ static void smp_chan_destroy(struct l2cap_conn *conn) } chan->data = NULL; - kzfree(smp); + kfree_sensitive(smp); hci_conn_drop(hcon); } @@ -1156,7 +1156,7 @@ static void sc_generate_link_key(struct smp_chan *smp) const u8 salt[16] = { 0x31, 0x70, 0x6d, 0x74 }; if (smp_h7(smp->tfm_cmac, smp->tk, salt, smp->link_key)) { - kzfree(smp->link_key); + kfree_sensitive(smp->link_key); smp->link_key = NULL; return; } @@ -1165,14 +1165,14 @@ static void sc_generate_link_key(struct smp_chan *smp) const u8 tmp1[4] = { 0x31, 0x70, 0x6d, 0x74 }; if (smp_h6(smp->tfm_cmac, smp->tk, tmp1, smp->link_key)) { - kzfree(smp->link_key); + kfree_sensitive(smp->link_key); smp->link_key = NULL; return; } } if (smp_h6(smp->tfm_cmac, smp->link_key, lebr, smp->link_key)) { - kzfree(smp->link_key); + kfree_sensitive(smp->link_key); smp->link_key = NULL; return; } @@ -1407,7 +1407,7 @@ static struct smp_chan *smp_chan_create(struct l2cap_conn *conn) free_shash: crypto_free_shash(smp->tfm_cmac); zfree_smp: - kzfree(smp); + kfree_sensitive(smp); return NULL; } @@ -3278,7 +3278,7 @@ static struct l2cap_chan *smp_add_cid(struct hci_dev *hdev, u16 cid) tfm_cmac = crypto_alloc_shash("cmac(aes)", 0, 0); if (IS_ERR(tfm_cmac)) { BT_ERR("Unable to create CMAC crypto context"); - kzfree(smp); + kfree_sensitive(smp); return ERR_CAST(tfm_cmac); } @@ -3286,7 +3286,7 @@ static struct l2cap_chan *smp_add_cid(struct hci_dev *hdev, u16 cid) if (IS_ERR(tfm_ecdh)) { BT_ERR("Unable to create ECDH crypto context"); crypto_free_shash(tfm_cmac); - kzfree(smp); + kfree_sensitive(smp); return ERR_CAST(tfm_ecdh); } @@ -3300,7 +3300,7 @@ create_chan: if (smp) { crypto_free_shash(smp->tfm_cmac); crypto_free_kpp(smp->tfm_ecdh); - kzfree(smp); + kfree_sensitive(smp); } return ERR_PTR(-ENOMEM); } @@ -3347,7 +3347,7 @@ static void smp_del_chan(struct l2cap_chan *chan) chan->data = NULL; crypto_free_shash(smp->tfm_cmac); crypto_free_kpp(smp->tfm_ecdh); - kzfree(smp); + kfree_sensitive(smp); } l2cap_chan_put(chan); diff --git a/net/core/sock.c b/net/core/sock.c index 49cd5ffe673ef..9d8b15bfd1925 100644 --- a/net/core/sock.c +++ b/net/core/sock.c @@ -2257,7 +2257,7 @@ static inline void __sock_kfree_s(struct sock *sk, void *mem, int size, if (WARN_ON_ONCE(!mem)) return; if (nullify) - kzfree(mem); + kfree_sensitive(mem); else kfree(mem); atomic_sub(size, &sk->sk_omem_alloc); diff --git a/net/ipv4/tcp_fastopen.c b/net/ipv4/tcp_fastopen.c index 19ad9586c7201..c1a54f3d58f5c 100644 --- a/net/ipv4/tcp_fastopen.c +++ b/net/ipv4/tcp_fastopen.c @@ -38,7 +38,7 @@ static void tcp_fastopen_ctx_free(struct rcu_head *head) struct tcp_fastopen_context *ctx = container_of(head, struct tcp_fastopen_context, rcu); - kzfree(ctx); + kfree_sensitive(ctx); } void tcp_fastopen_destroy_cipher(struct sock *sk) diff --git a/net/mac80211/aead_api.c b/net/mac80211/aead_api.c index c5fe95e49c681..d7b3d905d5353 100644 --- a/net/mac80211/aead_api.c +++ b/net/mac80211/aead_api.c @@ -41,7 +41,7 @@ int aead_encrypt(struct crypto_aead *tfm, u8 *b_0, u8 *aad, size_t aad_len, aead_request_set_ad(aead_req, sg[0].length); crypto_aead_encrypt(aead_req); - kzfree(aead_req); + kfree_sensitive(aead_req); return 0; } @@ -76,7 +76,7 @@ int aead_decrypt(struct crypto_aead *tfm, u8 *b_0, u8 *aad, size_t aad_len, aead_request_set_ad(aead_req, sg[0].length); err = crypto_aead_decrypt(aead_req); - kzfree(aead_req); + kfree_sensitive(aead_req); return err; } diff --git a/net/mac80211/aes_gmac.c b/net/mac80211/aes_gmac.c index 16ba09cb5defb..6f3b3a0cc10a4 100644 --- a/net/mac80211/aes_gmac.c +++ b/net/mac80211/aes_gmac.c @@ -60,7 +60,7 @@ int ieee80211_aes_gmac(struct crypto_aead *tfm, const u8 *aad, u8 *nonce, aead_request_set_ad(aead_req, GMAC_AAD_LEN + data_len); crypto_aead_encrypt(aead_req); - kzfree(aead_req); + kfree_sensitive(aead_req); return 0; } diff --git a/net/mac80211/key.c b/net/mac80211/key.c index 9c28880048787..2df636c32432f 100644 --- a/net/mac80211/key.c +++ b/net/mac80211/key.c @@ -732,7 +732,7 @@ static void ieee80211_key_free_common(struct ieee80211_key *key) ieee80211_aes_gcm_key_free(key->u.gcmp.tfm); break; } - kzfree(key); + kfree_sensitive(key); } static void __ieee80211_key_destroy(struct ieee80211_key *key, diff --git a/net/mac802154/llsec.c b/net/mac802154/llsec.c index c079ee69d3d03..585d33144c33f 100644 --- a/net/mac802154/llsec.c +++ b/net/mac802154/llsec.c @@ -49,7 +49,7 @@ void mac802154_llsec_destroy(struct mac802154_llsec *sec) msl = container_of(sl, struct mac802154_llsec_seclevel, level); list_del(&sl->list); - kzfree(msl); + kfree_sensitive(msl); } list_for_each_entry_safe(dev, dn, &sec->table.devices, list) { @@ -66,7 +66,7 @@ void mac802154_llsec_destroy(struct mac802154_llsec *sec) mkey = container_of(key->key, struct mac802154_llsec_key, key); list_del(&key->list); llsec_key_put(mkey); - kzfree(key); + kfree_sensitive(key); } } @@ -155,7 +155,7 @@ err_tfm: if (key->tfm[i]) crypto_free_aead(key->tfm[i]); - kzfree(key); + kfree_sensitive(key); return NULL; } @@ -170,7 +170,7 @@ static void llsec_key_release(struct kref *ref) crypto_free_aead(key->tfm[i]); crypto_free_sync_skcipher(key->tfm0); - kzfree(key); + kfree_sensitive(key); } static struct mac802154_llsec_key* @@ -261,7 +261,7 @@ int mac802154_llsec_key_add(struct mac802154_llsec *sec, return 0; fail: - kzfree(new); + kfree_sensitive(new); return -ENOMEM; } @@ -341,10 +341,10 @@ static void llsec_dev_free(struct mac802154_llsec_device *dev) devkey); list_del(&pos->list); - kzfree(devkey); + kfree_sensitive(devkey); } - kzfree(dev); + kfree_sensitive(dev); } int mac802154_llsec_dev_add(struct mac802154_llsec *sec, @@ -682,7 +682,7 @@ llsec_do_encrypt_auth(struct sk_buff *skb, const struct mac802154_llsec *sec, rc = crypto_aead_encrypt(req); - kzfree(req); + kfree_sensitive(req); return rc; } @@ -886,7 +886,7 @@ llsec_do_decrypt_auth(struct sk_buff *skb, const struct mac802154_llsec *sec, rc = crypto_aead_decrypt(req); - kzfree(req); + kfree_sensitive(req); skb_trim(skb, skb->len - authlen); return rc; @@ -926,7 +926,7 @@ llsec_update_devkey_record(struct mac802154_llsec_device *dev, if (!devkey) list_add_rcu(&next->devkey.list, &dev->dev.keys); else - kzfree(next); + kfree_sensitive(next); spin_unlock_bh(&dev->lock); } diff --git a/net/sctp/auth.c b/net/sctp/auth.c index 83e97e8892e05..9e289c770574f 100644 --- a/net/sctp/auth.c +++ b/net/sctp/auth.c @@ -49,7 +49,7 @@ void sctp_auth_key_put(struct sctp_auth_bytes *key) return; if (refcount_dec_and_test(&key->refcnt)) { - kzfree(key); + kfree_sensitive(key); SCTP_DBG_OBJCNT_DEC(keys); } } diff --git a/net/sunrpc/auth_gss/gss_krb5_crypto.c b/net/sunrpc/auth_gss/gss_krb5_crypto.c index e7180da1fc6a1..794fb30018809 100644 --- a/net/sunrpc/auth_gss/gss_krb5_crypto.c +++ b/net/sunrpc/auth_gss/gss_krb5_crypto.c @@ -1003,7 +1003,7 @@ krb5_rc4_setup_seq_key(struct krb5_ctx *kctx, err = 0; out_err: - kzfree(desc); + kfree_sensitive(desc); crypto_free_shash(hmac); dprintk("%s: returning %d\n", __func__, err); return err; @@ -1079,7 +1079,7 @@ krb5_rc4_setup_enc_key(struct krb5_ctx *kctx, err = 0; out_err: - kzfree(desc); + kfree_sensitive(desc); crypto_free_shash(hmac); dprintk("%s: returning %d\n", __func__, err); return err; diff --git a/net/sunrpc/auth_gss/gss_krb5_keys.c b/net/sunrpc/auth_gss/gss_krb5_keys.c index 3b7f721c023bb..726c076950c04 100644 --- a/net/sunrpc/auth_gss/gss_krb5_keys.c +++ b/net/sunrpc/auth_gss/gss_krb5_keys.c @@ -228,11 +228,11 @@ u32 krb5_derive_key(const struct gss_krb5_enctype *gk5e, ret = 0; err_free_raw: - kzfree(rawkey); + kfree_sensitive(rawkey); err_free_out: - kzfree(outblockdata); + kfree_sensitive(outblockdata); err_free_in: - kzfree(inblockdata); + kfree_sensitive(inblockdata); err_free_cipher: crypto_free_sync_skcipher(cipher); err_return: diff --git a/net/sunrpc/auth_gss/gss_krb5_mech.c b/net/sunrpc/auth_gss/gss_krb5_mech.c index 75b3c2e9e8f86..a84a5b2894844 100644 --- a/net/sunrpc/auth_gss/gss_krb5_mech.c +++ b/net/sunrpc/auth_gss/gss_krb5_mech.c @@ -443,7 +443,7 @@ context_derive_keys_rc4(struct krb5_ctx *ctx) desc->tfm = hmac; err = crypto_shash_digest(desc, sigkeyconstant, slen, ctx->cksum); - kzfree(desc); + kfree_sensitive(desc); if (err) goto out_err_free_hmac; /* diff --git a/net/tipc/crypto.c b/net/tipc/crypto.c index c8c47fc726536..001bcb0f2480d 100644 --- a/net/tipc/crypto.c +++ b/net/tipc/crypto.c @@ -441,7 +441,7 @@ static int tipc_aead_init(struct tipc_aead **aead, struct tipc_aead_key *ukey, /* Allocate per-cpu TFM entry pointer */ tmp->tfm_entry = alloc_percpu(struct tipc_tfm *); if (!tmp->tfm_entry) { - kzfree(tmp); + kfree_sensitive(tmp); return -ENOMEM; } @@ -491,7 +491,7 @@ static int tipc_aead_init(struct tipc_aead **aead, struct tipc_aead_key *ukey, /* Not any TFM is allocated? */ if (!tfm_cnt) { free_percpu(tmp->tfm_entry); - kzfree(tmp); + kfree_sensitive(tmp); return err; } @@ -545,7 +545,7 @@ static int tipc_aead_clone(struct tipc_aead **dst, struct tipc_aead *src) aead->tfm_entry = alloc_percpu_gfp(struct tipc_tfm *, GFP_ATOMIC); if (unlikely(!aead->tfm_entry)) { - kzfree(aead); + kfree_sensitive(aead); return -ENOMEM; } @@ -1352,7 +1352,7 @@ int tipc_crypto_start(struct tipc_crypto **crypto, struct net *net, /* Allocate statistic structure */ c->stats = alloc_percpu_gfp(struct tipc_crypto_stats, GFP_ATOMIC); if (!c->stats) { - kzfree(c); + kfree_sensitive(c); return -ENOMEM; } @@ -1408,7 +1408,7 @@ void tipc_crypto_stop(struct tipc_crypto **crypto) free_percpu(c->stats); *crypto = NULL; - kzfree(c); + kfree_sensitive(c); } void tipc_crypto_timeout(struct tipc_crypto *rx) diff --git a/net/wireless/core.c b/net/wireless/core.c index 1971d7e6eb550..354b0ccbdc240 100644 --- a/net/wireless/core.c +++ b/net/wireless/core.c @@ -1125,7 +1125,7 @@ static void __cfg80211_unregister_wdev(struct wireless_dev *wdev, bool sync) } #ifdef CONFIG_CFG80211_WEXT - kzfree(wdev->wext.keys); + kfree_sensitive(wdev->wext.keys); wdev->wext.keys = NULL; #endif /* only initialized if we have a netdev */ diff --git a/net/wireless/ibss.c b/net/wireless/ibss.c index ae8fe66a9bb82..a0621bb76d8e9 100644 --- a/net/wireless/ibss.c +++ b/net/wireless/ibss.c @@ -127,7 +127,7 @@ int __cfg80211_join_ibss(struct cfg80211_registered_device *rdev, return -EINVAL; if (WARN_ON(wdev->connect_keys)) - kzfree(wdev->connect_keys); + kfree_sensitive(wdev->connect_keys); wdev->connect_keys = connkeys; wdev->ibss_fixed = params->channel_fixed; @@ -161,7 +161,7 @@ static void __cfg80211_clear_ibss(struct net_device *dev, bool nowext) ASSERT_WDEV_LOCK(wdev); - kzfree(wdev->connect_keys); + kfree_sensitive(wdev->connect_keys); wdev->connect_keys = NULL; rdev_set_qos_map(rdev, dev, NULL); diff --git a/net/wireless/lib80211_crypt_tkip.c b/net/wireless/lib80211_crypt_tkip.c index f5e842ba7673a..1b4d6c87a5c5d 100644 --- a/net/wireless/lib80211_crypt_tkip.c +++ b/net/wireless/lib80211_crypt_tkip.c @@ -131,7 +131,7 @@ static void lib80211_tkip_deinit(void *priv) crypto_free_shash(_priv->tx_tfm_michael); crypto_free_shash(_priv->rx_tfm_michael); } - kzfree(priv); + kfree_sensitive(priv); } static inline u16 RotR1(u16 val) diff --git a/net/wireless/lib80211_crypt_wep.c b/net/wireless/lib80211_crypt_wep.c index dafc6f3571dbc..6ab9957b8f969 100644 --- a/net/wireless/lib80211_crypt_wep.c +++ b/net/wireless/lib80211_crypt_wep.c @@ -56,7 +56,7 @@ static void *lib80211_wep_init(int keyidx) static void lib80211_wep_deinit(void *priv) { - kzfree(priv); + kfree_sensitive(priv); } /* Add WEP IV/key info to a frame that has at least 4 bytes of headroom */ diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 814e23d3ce7c9..c04fc6cf65838 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -9836,7 +9836,7 @@ static int nl80211_join_ibss(struct sk_buff *skb, struct genl_info *info) if ((ibss.chandef.width != NL80211_CHAN_WIDTH_20_NOHT) && no_ht) { - kzfree(connkeys); + kfree_sensitive(connkeys); return -EINVAL; } } @@ -9848,7 +9848,7 @@ static int nl80211_join_ibss(struct sk_buff *skb, struct genl_info *info) int r = validate_pae_over_nl80211(rdev, info); if (r < 0) { - kzfree(connkeys); + kfree_sensitive(connkeys); return r; } @@ -9861,7 +9861,7 @@ static int nl80211_join_ibss(struct sk_buff *skb, struct genl_info *info) wdev_lock(dev->ieee80211_ptr); err = __cfg80211_join_ibss(rdev, dev, &ibss, connkeys); if (err) - kzfree(connkeys); + kfree_sensitive(connkeys); else if (info->attrs[NL80211_ATTR_SOCKET_OWNER]) dev->ieee80211_ptr->conn_owner_nlportid = info->snd_portid; wdev_unlock(dev->ieee80211_ptr); @@ -10289,7 +10289,7 @@ static int nl80211_connect(struct sk_buff *skb, struct genl_info *info) if (info->attrs[NL80211_ATTR_HT_CAPABILITY]) { if (!info->attrs[NL80211_ATTR_HT_CAPABILITY_MASK]) { - kzfree(connkeys); + kfree_sensitive(connkeys); return -EINVAL; } memcpy(&connect.ht_capa, @@ -10307,7 +10307,7 @@ static int nl80211_connect(struct sk_buff *skb, struct genl_info *info) if (info->attrs[NL80211_ATTR_VHT_CAPABILITY]) { if (!info->attrs[NL80211_ATTR_VHT_CAPABILITY_MASK]) { - kzfree(connkeys); + kfree_sensitive(connkeys); return -EINVAL; } memcpy(&connect.vht_capa, @@ -10321,7 +10321,7 @@ static int nl80211_connect(struct sk_buff *skb, struct genl_info *info) (rdev->wiphy.features & NL80211_FEATURE_QUIET)) && !wiphy_ext_feature_isset(&rdev->wiphy, NL80211_EXT_FEATURE_RRM)) { - kzfree(connkeys); + kfree_sensitive(connkeys); return -EINVAL; } connect.flags |= ASSOC_REQ_USE_RRM; @@ -10329,21 +10329,21 @@ static int nl80211_connect(struct sk_buff *skb, struct genl_info *info) connect.pbss = nla_get_flag(info->attrs[NL80211_ATTR_PBSS]); if (connect.pbss && !rdev->wiphy.bands[NL80211_BAND_60GHZ]) { - kzfree(connkeys); + kfree_sensitive(connkeys); return -EOPNOTSUPP; } if (info->attrs[NL80211_ATTR_BSS_SELECT]) { /* bss selection makes no sense if bssid is set */ if (connect.bssid) { - kzfree(connkeys); + kfree_sensitive(connkeys); return -EINVAL; } err = parse_bss_select(info->attrs[NL80211_ATTR_BSS_SELECT], wiphy, &connect.bss_select); if (err) { - kzfree(connkeys); + kfree_sensitive(connkeys); return err; } } @@ -10373,13 +10373,13 @@ static int nl80211_connect(struct sk_buff *skb, struct genl_info *info) info->attrs[NL80211_ATTR_FILS_ERP_REALM] || info->attrs[NL80211_ATTR_FILS_ERP_NEXT_SEQ_NUM] || info->attrs[NL80211_ATTR_FILS_ERP_RRK]) { - kzfree(connkeys); + kfree_sensitive(connkeys); return -EINVAL; } if (nla_get_flag(info->attrs[NL80211_ATTR_EXTERNAL_AUTH_SUPPORT])) { if (!info->attrs[NL80211_ATTR_SOCKET_OWNER]) { - kzfree(connkeys); + kfree_sensitive(connkeys); GENL_SET_ERR_MSG(info, "external auth requires connection ownership"); return -EINVAL; @@ -10392,7 +10392,7 @@ static int nl80211_connect(struct sk_buff *skb, struct genl_info *info) err = cfg80211_connect(rdev, dev, &connect, connkeys, connect.prev_bssid); if (err) - kzfree(connkeys); + kfree_sensitive(connkeys); if (!err && info->attrs[NL80211_ATTR_SOCKET_OWNER]) { dev->ieee80211_ptr->conn_owner_nlportid = info->snd_portid; diff --git a/net/wireless/sme.c b/net/wireless/sme.c index 15595cf401de5..985f3c23f0545 100644 --- a/net/wireless/sme.c +++ b/net/wireless/sme.c @@ -742,7 +742,7 @@ void __cfg80211_connect_result(struct net_device *dev, } if (cr->status != WLAN_STATUS_SUCCESS) { - kzfree(wdev->connect_keys); + kfree_sensitive(wdev->connect_keys); wdev->connect_keys = NULL; wdev->ssid_len = 0; wdev->conn_owner_nlportid = 0; @@ -1098,7 +1098,7 @@ void __cfg80211_disconnected(struct net_device *dev, const u8 *ie, wdev->current_bss = NULL; wdev->ssid_len = 0; wdev->conn_owner_nlportid = 0; - kzfree(wdev->connect_keys); + kfree_sensitive(wdev->connect_keys); wdev->connect_keys = NULL; nl80211_send_disconnected(rdev, dev, reason, ie, ie_len, from_ap); @@ -1281,7 +1281,7 @@ int cfg80211_disconnect(struct cfg80211_registered_device *rdev, ASSERT_WDEV_LOCK(wdev); - kzfree(wdev->connect_keys); + kfree_sensitive(wdev->connect_keys); wdev->connect_keys = NULL; wdev->conn_owner_nlportid = 0; diff --git a/net/wireless/util.c b/net/wireless/util.c index 26a977343c3b2..dfad1c0f57adb 100644 --- a/net/wireless/util.c +++ b/net/wireless/util.c @@ -871,7 +871,7 @@ void cfg80211_upload_connect_keys(struct wireless_dev *wdev) } } - kzfree(wdev->connect_keys); + kfree_sensitive(wdev->connect_keys); wdev->connect_keys = NULL; } diff --git a/net/wireless/wext-sme.c b/net/wireless/wext-sme.c index 73fd0eae08caa..73df23570d43a 100644 --- a/net/wireless/wext-sme.c +++ b/net/wireless/wext-sme.c @@ -57,7 +57,7 @@ int cfg80211_mgd_wext_connect(struct cfg80211_registered_device *rdev, err = cfg80211_connect(rdev, wdev->netdev, &wdev->wext.connect, ck, prev_bssid); if (err) - kzfree(ck); + kfree_sensitive(ck); return err; } diff --git a/scripts/coccinelle/free/devm_free.cocci b/scripts/coccinelle/free/devm_free.cocci index 3357bf4dbd7c0..da80050b91ff6 100644 --- a/scripts/coccinelle/free/devm_free.cocci +++ b/scripts/coccinelle/free/devm_free.cocci @@ -89,7 +89,7 @@ position p; ( kfree@p(x) | - kzfree@p(x) + kfree_sensitive@p(x) | krealloc@p(x, ...) | @@ -112,7 +112,7 @@ position p != safe.p; ( * kfree@p(x) | -* kzfree@p(x) +* kfree_sensitive@p(x) | * krealloc@p(x, ...) | diff --git a/scripts/coccinelle/free/ifnullfree.cocci b/scripts/coccinelle/free/ifnullfree.cocci index b3290c4ee239e..2045391e36a07 100644 --- a/scripts/coccinelle/free/ifnullfree.cocci +++ b/scripts/coccinelle/free/ifnullfree.cocci @@ -21,7 +21,7 @@ expression E; ( kfree(E); | - kzfree(E); + kfree_sensitive(E); | debugfs_remove(E); | @@ -42,7 +42,7 @@ position p; @@ * if (E != NULL) -* \(kfree@p\|kzfree@p\|debugfs_remove@p\|debugfs_remove_recursive@p\| +* \(kfree@p\|kfree_sensitive@p\|debugfs_remove@p\|debugfs_remove_recursive@p\| * usb_free_urb@p\|kmem_cache_destroy@p\|mempool_destroy@p\| * dma_pool_destroy@p\)(E); diff --git a/scripts/coccinelle/free/kfree.cocci b/scripts/coccinelle/free/kfree.cocci index e9d50e718e465..168568386034a 100644 --- a/scripts/coccinelle/free/kfree.cocci +++ b/scripts/coccinelle/free/kfree.cocci @@ -24,7 +24,7 @@ position p1; ( * kfree@p1(E) | -* kzfree@p1(E) +* kfree_sensitive@p1(E) ) @print expression@ @@ -68,7 +68,7 @@ while (1) { ... ( * kfree@ok(E) | -* kzfree@ok(E) +* kfree_sensitive@ok(E) ) ... when != break; when != goto l; @@ -86,7 +86,7 @@ position free.p1!=loop.ok,p2!={print.p,sz.p}; ( * kfree@p1(E,...) | -* kzfree@p1(E,...) +* kfree_sensitive@p1(E,...) ) ... ( diff --git a/scripts/coccinelle/free/kfreeaddr.cocci b/scripts/coccinelle/free/kfreeaddr.cocci index cfaf308328d8a..142af6337a044 100644 --- a/scripts/coccinelle/free/kfreeaddr.cocci +++ b/scripts/coccinelle/free/kfreeaddr.cocci @@ -20,7 +20,7 @@ position p; ( * kfree@p(&e->f) | -* kzfree@p(&e->f) +* kfree_sensitive@p(&e->f) ) @script:python depends on org@ diff --git a/security/apparmor/domain.c b/security/apparmor/domain.c index 1c898055a4764..7b0e13ce7dc7b 100644 --- a/security/apparmor/domain.c +++ b/security/apparmor/domain.c @@ -40,8 +40,8 @@ void aa_free_domain_entries(struct aa_domain *domain) return; for (i = 0; i < domain->size; i++) - kzfree(domain->table[i]); - kzfree(domain->table); + kfree_sensitive(domain->table[i]); + kfree_sensitive(domain->table); domain->table = NULL; } } diff --git a/security/apparmor/include/file.h b/security/apparmor/include/file.h index aff26fc714074..d4f8948517d99 100644 --- a/security/apparmor/include/file.h +++ b/security/apparmor/include/file.h @@ -72,7 +72,7 @@ static inline void aa_free_file_ctx(struct aa_file_ctx *ctx) { if (ctx) { aa_put_label(rcu_access_pointer(ctx->label)); - kzfree(ctx); + kfree_sensitive(ctx); } } diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c index af4f50fda9e30..4c010c9a6af1d 100644 --- a/security/apparmor/policy.c +++ b/security/apparmor/policy.c @@ -187,9 +187,9 @@ static void aa_free_data(void *ptr, void *arg) { struct aa_data *data = ptr; - kzfree(data->data); - kzfree(data->key); - kzfree(data); + kfree_sensitive(data->data); + kfree_sensitive(data->key); + kfree_sensitive(data); } /** @@ -217,19 +217,19 @@ void aa_free_profile(struct aa_profile *profile) aa_put_profile(rcu_access_pointer(profile->parent)); aa_put_ns(profile->ns); - kzfree(profile->rename); + kfree_sensitive(profile->rename); aa_free_file_rules(&profile->file); aa_free_cap_rules(&profile->caps); aa_free_rlimit_rules(&profile->rlimits); for (i = 0; i < profile->xattr_count; i++) - kzfree(profile->xattrs[i]); - kzfree(profile->xattrs); + kfree_sensitive(profile->xattrs[i]); + kfree_sensitive(profile->xattrs); for (i = 0; i < profile->secmark_count; i++) - kzfree(profile->secmark[i].label); - kzfree(profile->secmark); - kzfree(profile->dirname); + kfree_sensitive(profile->secmark[i].label); + kfree_sensitive(profile->secmark); + kfree_sensitive(profile->dirname); aa_put_dfa(profile->xmatch); aa_put_dfa(profile->policy.dfa); @@ -237,14 +237,14 @@ void aa_free_profile(struct aa_profile *profile) rht = profile->data; profile->data = NULL; rhashtable_free_and_destroy(rht, aa_free_data, NULL); - kzfree(rht); + kfree_sensitive(rht); } - kzfree(profile->hash); + kfree_sensitive(profile->hash); aa_put_loaddata(profile->rawdata); aa_label_destroy(&profile->label); - kzfree(profile); + kfree_sensitive(profile); } /** diff --git a/security/apparmor/policy_ns.c b/security/apparmor/policy_ns.c index d7ef540027a57..70921d95fb406 100644 --- a/security/apparmor/policy_ns.c +++ b/security/apparmor/policy_ns.c @@ -121,9 +121,9 @@ static struct aa_ns *alloc_ns(const char *prefix, const char *name) return ns; fail_unconfined: - kzfree(ns->base.hname); + kfree_sensitive(ns->base.hname); fail_ns: - kzfree(ns); + kfree_sensitive(ns); return NULL; } @@ -145,7 +145,7 @@ void aa_free_ns(struct aa_ns *ns) ns->unconfined->ns = NULL; aa_free_profile(ns->unconfined); - kzfree(ns); + kfree_sensitive(ns); } /** diff --git a/security/apparmor/policy_unpack.c b/security/apparmor/policy_unpack.c index b67322abcc330..dc345ac932053 100644 --- a/security/apparmor/policy_unpack.c +++ b/security/apparmor/policy_unpack.c @@ -163,10 +163,10 @@ static void do_loaddata_free(struct work_struct *work) aa_put_ns(ns); } - kzfree(d->hash); - kzfree(d->name); + kfree_sensitive(d->hash); + kfree_sensitive(d->name); kvfree(d->data); - kzfree(d); + kfree_sensitive(d); } void aa_loaddata_kref(struct kref *kref) @@ -894,7 +894,7 @@ static struct aa_profile *unpack_profile(struct aa_ext *e, char **ns_name) while (unpack_strdup(e, &key, NULL)) { data = kzalloc(sizeof(*data), GFP_KERNEL); if (!data) { - kzfree(key); + kfree_sensitive(key); goto fail; } @@ -902,8 +902,8 @@ static struct aa_profile *unpack_profile(struct aa_ext *e, char **ns_name) data->size = unpack_blob(e, &data->data, NULL); data->data = kvmemdup(data->data, data->size); if (data->size && !data->data) { - kzfree(data->key); - kzfree(data); + kfree_sensitive(data->key); + kfree_sensitive(data); goto fail; } @@ -1037,7 +1037,7 @@ void aa_load_ent_free(struct aa_load_ent *ent) aa_put_profile(ent->old); aa_put_profile(ent->new); kfree(ent->ns_name); - kzfree(ent); + kfree_sensitive(ent); } } diff --git a/security/keys/big_key.c b/security/keys/big_key.c index dd708e8f13c09..691347dea3c17 100644 --- a/security/keys/big_key.c +++ b/security/keys/big_key.c @@ -138,7 +138,7 @@ int big_key_preparse(struct key_preparsed_payload *prep) err_fput: fput(file); err_enckey: - kzfree(enckey); + kfree_sensitive(enckey); error: memzero_explicit(buf, enclen); kvfree(buf); @@ -155,7 +155,7 @@ void big_key_free_preparse(struct key_preparsed_payload *prep) path_put(path); } - kzfree(prep->payload.data[big_key_data]); + kfree_sensitive(prep->payload.data[big_key_data]); } /* @@ -187,7 +187,7 @@ void big_key_destroy(struct key *key) path->mnt = NULL; path->dentry = NULL; } - kzfree(key->payload.data[big_key_data]); + kfree_sensitive(key->payload.data[big_key_data]); key->payload.data[big_key_data] = NULL; } diff --git a/security/keys/dh.c b/security/keys/dh.c index c4c629bb1c038..1abfa70ed6e10 100644 --- a/security/keys/dh.c +++ b/security/keys/dh.c @@ -58,9 +58,9 @@ error: static void dh_free_data(struct dh *dh) { - kzfree(dh->key); - kzfree(dh->p); - kzfree(dh->g); + kfree_sensitive(dh->key); + kfree_sensitive(dh->p); + kfree_sensitive(dh->g); } struct dh_completion { @@ -126,7 +126,7 @@ static void kdf_dealloc(struct kdf_sdesc *sdesc) if (sdesc->shash.tfm) crypto_free_shash(sdesc->shash.tfm); - kzfree(sdesc); + kfree_sensitive(sdesc); } /* @@ -220,7 +220,7 @@ static int keyctl_dh_compute_kdf(struct kdf_sdesc *sdesc, ret = -EFAULT; err: - kzfree(outbuf); + kfree_sensitive(outbuf); return ret; } @@ -395,11 +395,11 @@ long __keyctl_dh_compute(struct keyctl_dh_params __user *params, out6: kpp_request_free(req); out5: - kzfree(outbuf); + kfree_sensitive(outbuf); out4: crypto_free_kpp(tfm); out3: - kzfree(secret); + kfree_sensitive(secret); out2: dh_free_data(&dh_inputs); out1: diff --git a/security/keys/encrypted-keys/encrypted.c b/security/keys/encrypted-keys/encrypted.c index 14cf81d1a30b1..deebbf14eeca1 100644 --- a/security/keys/encrypted-keys/encrypted.c +++ b/security/keys/encrypted-keys/encrypted.c @@ -370,7 +370,7 @@ static int get_derived_key(u8 *derived_key, enum derived_key_type key_type, master_keylen); ret = crypto_shash_tfm_digest(hash_tfm, derived_buf, derived_buf_len, derived_key); - kzfree(derived_buf); + kfree_sensitive(derived_buf); return ret; } @@ -812,13 +812,13 @@ static int encrypted_instantiate(struct key *key, ret = encrypted_init(epayload, key->description, format, master_desc, decrypted_datalen, hex_encoded_iv); if (ret < 0) { - kzfree(epayload); + kfree_sensitive(epayload); goto out; } rcu_assign_keypointer(key, epayload); out: - kzfree(datablob); + kfree_sensitive(datablob); return ret; } @@ -827,7 +827,7 @@ static void encrypted_rcu_free(struct rcu_head *rcu) struct encrypted_key_payload *epayload; epayload = container_of(rcu, struct encrypted_key_payload, rcu); - kzfree(epayload); + kfree_sensitive(epayload); } /* @@ -885,7 +885,7 @@ static int encrypted_update(struct key *key, struct key_preparsed_payload *prep) rcu_assign_keypointer(key, new_epayload); call_rcu(&epayload->rcu, encrypted_rcu_free); out: - kzfree(buf); + kfree_sensitive(buf); return ret; } @@ -946,7 +946,7 @@ static long encrypted_read(const struct key *key, char *buffer, memzero_explicit(derived_key, sizeof(derived_key)); memcpy(buffer, ascii_buf, asciiblob_len); - kzfree(ascii_buf); + kfree_sensitive(ascii_buf); return asciiblob_len; out: @@ -961,7 +961,7 @@ out: */ static void encrypted_destroy(struct key *key) { - kzfree(key->payload.data[0]); + kfree_sensitive(key->payload.data[0]); } struct key_type key_type_encrypted = { diff --git a/security/keys/trusted-keys/trusted_tpm1.c b/security/keys/trusted-keys/trusted_tpm1.c index 8001ab07e63b1..b9fe02e5f84f0 100644 --- a/security/keys/trusted-keys/trusted_tpm1.c +++ b/security/keys/trusted-keys/trusted_tpm1.c @@ -68,7 +68,7 @@ static int TSS_sha1(const unsigned char *data, unsigned int datalen, } ret = crypto_shash_digest(&sdesc->shash, data, datalen, digest); - kzfree(sdesc); + kfree_sensitive(sdesc); return ret; } @@ -112,7 +112,7 @@ static int TSS_rawhmac(unsigned char *digest, const unsigned char *key, if (!ret) ret = crypto_shash_final(&sdesc->shash, digest); out: - kzfree(sdesc); + kfree_sensitive(sdesc); return ret; } @@ -166,7 +166,7 @@ int TSS_authhmac(unsigned char *digest, const unsigned char *key, paramdigest, TPM_NONCE_SIZE, h1, TPM_NONCE_SIZE, h2, 1, &c, 0, 0); out: - kzfree(sdesc); + kfree_sensitive(sdesc); return ret; } EXPORT_SYMBOL_GPL(TSS_authhmac); @@ -251,7 +251,7 @@ int TSS_checkhmac1(unsigned char *buffer, if (memcmp(testhmac, authdata, SHA1_DIGEST_SIZE)) ret = -EINVAL; out: - kzfree(sdesc); + kfree_sensitive(sdesc); return ret; } EXPORT_SYMBOL_GPL(TSS_checkhmac1); @@ -353,7 +353,7 @@ static int TSS_checkhmac2(unsigned char *buffer, if (memcmp(testhmac2, authdata2, SHA1_DIGEST_SIZE)) ret = -EINVAL; out: - kzfree(sdesc); + kfree_sensitive(sdesc); return ret; } @@ -563,7 +563,7 @@ static int tpm_seal(struct tpm_buf *tb, uint16_t keytype, *bloblen = storedsize; } out: - kzfree(td); + kfree_sensitive(td); return ret; } @@ -1031,12 +1031,12 @@ static int trusted_instantiate(struct key *key, if (!ret && options->pcrlock) ret = pcrlock(options->pcrlock); out: - kzfree(datablob); - kzfree(options); + kfree_sensitive(datablob); + kfree_sensitive(options); if (!ret) rcu_assign_keypointer(key, payload); else - kzfree(payload); + kfree_sensitive(payload); return ret; } @@ -1045,7 +1045,7 @@ static void trusted_rcu_free(struct rcu_head *rcu) struct trusted_key_payload *p; p = container_of(rcu, struct trusted_key_payload, rcu); - kzfree(p); + kfree_sensitive(p); } /* @@ -1087,13 +1087,13 @@ static int trusted_update(struct key *key, struct key_preparsed_payload *prep) ret = datablob_parse(datablob, new_p, new_o); if (ret != Opt_update) { ret = -EINVAL; - kzfree(new_p); + kfree_sensitive(new_p); goto out; } if (!new_o->keyhandle) { ret = -EINVAL; - kzfree(new_p); + kfree_sensitive(new_p); goto out; } @@ -1107,22 +1107,22 @@ static int trusted_update(struct key *key, struct key_preparsed_payload *prep) ret = key_seal(new_p, new_o); if (ret < 0) { pr_info("trusted_key: key_seal failed (%d)\n", ret); - kzfree(new_p); + kfree_sensitive(new_p); goto out; } if (new_o->pcrlock) { ret = pcrlock(new_o->pcrlock); if (ret < 0) { pr_info("trusted_key: pcrlock failed (%d)\n", ret); - kzfree(new_p); + kfree_sensitive(new_p); goto out; } } rcu_assign_keypointer(key, new_p); call_rcu(&p->rcu, trusted_rcu_free); out: - kzfree(datablob); - kzfree(new_o); + kfree_sensitive(datablob); + kfree_sensitive(new_o); return ret; } @@ -1154,7 +1154,7 @@ static long trusted_read(const struct key *key, char *buffer, */ static void trusted_destroy(struct key *key) { - kzfree(key->payload.data[0]); + kfree_sensitive(key->payload.data[0]); } struct key_type key_type_trusted = { diff --git a/security/keys/user_defined.c b/security/keys/user_defined.c index 07d4287e90848..749e2a4dcb13e 100644 --- a/security/keys/user_defined.c +++ b/security/keys/user_defined.c @@ -82,7 +82,7 @@ EXPORT_SYMBOL_GPL(user_preparse); */ void user_free_preparse(struct key_preparsed_payload *prep) { - kzfree(prep->payload.data[0]); + kfree_sensitive(prep->payload.data[0]); } EXPORT_SYMBOL_GPL(user_free_preparse); @@ -91,7 +91,7 @@ static void user_free_payload_rcu(struct rcu_head *head) struct user_key_payload *payload; payload = container_of(head, struct user_key_payload, rcu); - kzfree(payload); + kfree_sensitive(payload); } /* @@ -147,7 +147,7 @@ void user_destroy(struct key *key) { struct user_key_payload *upayload = key->payload.data[0]; - kzfree(upayload); + kfree_sensitive(upayload); } EXPORT_SYMBOL_GPL(user_destroy); -- GitLab From fa9ba3aa89f9f1c003b5f5cde893bbbc140c7223 Mon Sep 17 00:00:00 2001 From: William Kucharski Date: Thu, 6 Aug 2020 23:18:17 -0700 Subject: [PATCH 1332/1476] mm: ksize() should silently accept a NULL pointer Other mm routines such as kfree() and kzfree() silently do the right thing if passed a NULL pointer, so ksize() should do the same. Signed-off-by: William Kucharski Signed-off-by: Andrew Morton Reviewed-by: Matthew Wilcox (Oracle) Reviewed-by: David Hildenbrand Cc: Christoph Lameter Cc: Pekka Enberg Cc: David Rientjes Cc: Joonsoo Kim Link: http://lkml.kernel.org/r/20200616225409.4670-1-william.kucharski@oracle.com Signed-off-by: Linus Torvalds --- mm/slab_common.c | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/mm/slab_common.c b/mm/slab_common.c index f47a097bb4b8e..e493203b50024 100644 --- a/mm/slab_common.c +++ b/mm/slab_common.c @@ -1681,10 +1681,9 @@ static __always_inline void *__do_krealloc(const void *p, size_t new_size, gfp_t flags) { void *ret; - size_t ks = 0; + size_t ks; - if (p) - ks = ksize(p); + ks = ksize(p); if (ks >= new_size) { p = kasan_krealloc((void *)p, new_size, flags); @@ -1744,10 +1743,9 @@ void kfree_sensitive(const void *p) size_t ks; void *mem = (void *)p; - if (unlikely(ZERO_OR_NULL_PTR(mem))) - return; ks = ksize(mem); - memzero_explicit(mem, ks); + if (ks) + memzero_explicit(mem, ks); kfree(mem); } EXPORT_SYMBOL(kfree_sensitive); @@ -1770,8 +1768,6 @@ size_t ksize(const void *objp) { size_t size; - if (WARN_ON_ONCE(!objp)) - return 0; /* * We need to check that the pointed to object is valid, and only then * unpoison the shadow memory below. We use __kasan_check_read(), to @@ -1785,7 +1781,7 @@ size_t ksize(const void *objp) * We want to perform the check before __ksize(), to avoid potentially * crashing in __ksize() due to accessing invalid metadata. */ - if (unlikely(objp == ZERO_SIZE_PTR) || !__kasan_check_read(objp, 1)) + if (unlikely(ZERO_OR_NULL_PTR(objp)) || !__kasan_check_read(objp, 1)) return 0; size = __ksize(objp); -- GitLab From 3404be67bf73515babd74acd8525d09dafe4234d Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Thu, 6 Aug 2020 23:18:20 -0700 Subject: [PATCH 1333/1476] mm/slab: expand CONFIG_SLAB_FREELIST_HARDENED to include SLAB Patch series "mm: Expand CONFIG_SLAB_FREELIST_HARDENED to include SLAB" In reviewing Vlastimil Babka's latest slub debug series, I realized[1] that several checks under CONFIG_SLAB_FREELIST_HARDENED weren't being applied to SLAB. Fix this by expanding the Kconfig coverage, and adding a simple double-free test for SLAB. This patch (of 2): Include SLAB caches when performing kmem_cache pointer verification. A defense against such corruption[1] should be applied to all the allocators. With this added, the "SLAB_FREE_CROSS" and "SLAB_FREE_PAGE" LKDTM tests now pass on SLAB: lkdtm: Performing direct entry SLAB_FREE_CROSS lkdtm: Attempting cross-cache slab free ... ------------[ cut here ]------------ cache_from_obj: Wrong slab cache. lkdtm-heap-b but object is from lkdtm-heap-a WARNING: CPU: 2 PID: 2195 at mm/slab.h:530 kmem_cache_free+0x8d/0x1d0 ... lkdtm: Performing direct entry SLAB_FREE_PAGE lkdtm: Attempting non-Slab slab free ... ------------[ cut here ]------------ virt_to_cache: Object is not a Slab page! WARNING: CPU: 1 PID: 2202 at mm/slab.h:489 kmem_cache_free+0x196/0x1d0 Additionally clean up neighboring Kconfig entries for clarity, readability, and redundant option removal. [1] https://github.com/ThomasKing2014/slides/raw/master/Building%20universal%20Android%20rooting%20with%20a%20type%20confusion%20vulnerability.pdf Fixes: 598a0717a816 ("mm/slab: validate cache membership under freelist hardening") Signed-off-by: Kees Cook Signed-off-by: Andrew Morton Acked-by: Vlastimil Babka Cc: Alexander Popov Cc: Christoph Lameter Cc: David Rientjes Cc: Jann Horn Cc: Joonsoo Kim Cc: Matthew Garrett Cc: Pekka Enberg Cc: Roman Gushchin Cc: Vijayanand Jitta Cc: Vinayak Menon Link: http://lkml.kernel.org/r/20200625215548.389774-1-keescook@chromium.org Link: http://lkml.kernel.org/r/20200625215548.389774-2-keescook@chromium.org Signed-off-by: Linus Torvalds --- init/Kconfig | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/init/Kconfig b/init/Kconfig index 9082ed33a9cde..d6a0b31b13dc9 100644 --- a/init/Kconfig +++ b/init/Kconfig @@ -1913,9 +1913,8 @@ config SLAB_MERGE_DEFAULT command line. config SLAB_FREELIST_RANDOM - default n + bool "Randomize slab freelist" depends on SLAB || SLUB - bool "SLAB freelist randomization" help Randomizes the freelist order used on creating new pages. This security feature reduces the predictability of the kernel slab @@ -1923,12 +1922,14 @@ config SLAB_FREELIST_RANDOM config SLAB_FREELIST_HARDENED bool "Harden slab freelist metadata" - depends on SLUB + depends on SLAB || SLUB help Many kernel heap attacks try to target slab cache metadata and other infrastructure. This options makes minor performance sacrifices to harden the kernel slab allocator against common - freelist exploit methods. + freelist exploit methods. Some slab implementations have more + sanity-checking than others. This option is most effective with + CONFIG_SLUB. config SHUFFLE_PAGE_ALLOCATOR bool "Page allocator randomization" -- GitLab From dabc3e291d56e3125113101bc6d53d1a1738294d Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Thu, 6 Aug 2020 23:18:24 -0700 Subject: [PATCH 1334/1476] mm/slab: add naive detection of double free Similar to commit ce6fa91b9363 ("mm/slub.c: add a naive detection of double free or corruption"), add a very cheap double-free check for SLAB under CONFIG_SLAB_FREELIST_HARDENED. With this added, the "SLAB_FREE_DOUBLE" LKDTM test passes under SLAB: lkdtm: Performing direct entry SLAB_FREE_DOUBLE lkdtm: Attempting double slab free ... ------------[ cut here ]------------ WARNING: CPU: 2 PID: 2193 at mm/slab.c:757 ___cache _free+0x325/0x390 [keescook@chromium.org: fix misplaced __free_one()] Link: http://lkml.kernel.org/r/202006261306.0D82A2B@keescook Link: https://lore.kernel.org/lkml/7ff248c7-d447-340c-a8e2-8c02972aca70@infradead.org Signed-off-by: Kees Cook Signed-off-by: Andrew Morton Acked-by: Vlastimil Babka Acked-by: Randy Dunlap [build tested] Cc: Roman Gushchin Cc: Christoph Lameter Cc: Alexander Popov Cc: Pekka Enberg Cc: David Rientjes Cc: Joonsoo Kim Cc: Vinayak Menon Cc: Matthew Garrett Cc: Jann Horn Cc: Vijayanand Jitta Link: http://lkml.kernel.org/r/20200625215548.389774-3-keescook@chromium.org Signed-off-by: Linus Torvalds --- mm/slab.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/mm/slab.c b/mm/slab.c index 9350062ffc1ad..77e90f9de9c0c 100644 --- a/mm/slab.c +++ b/mm/slab.c @@ -588,6 +588,16 @@ static int transfer_objects(struct array_cache *to, return nr; } +/* &alien->lock must be held by alien callers. */ +static __always_inline void __free_one(struct array_cache *ac, void *objp) +{ + /* Avoid trivial double-free. */ + if (IS_ENABLED(CONFIG_SLAB_FREELIST_HARDENED) && + WARN_ON_ONCE(ac->avail > 0 && ac->entry[ac->avail - 1] == objp)) + return; + ac->entry[ac->avail++] = objp; +} + #ifndef CONFIG_NUMA #define drain_alien_cache(cachep, alien) do { } while (0) @@ -767,7 +777,7 @@ static int __cache_free_alien(struct kmem_cache *cachep, void *objp, STATS_INC_ACOVERFLOW(cachep); __drain_alien_cache(cachep, ac, page_node, &list); } - ac->entry[ac->avail++] = objp; + __free_one(ac, objp); spin_unlock(&alien->lock); slabs_destroy(cachep, &list); } else { @@ -3466,7 +3476,7 @@ void ___cache_free(struct kmem_cache *cachep, void *objp, } } - ac->entry[ac->avail++] = objp; + __free_one(ac, objp); } /** -- GitLab From 444050990db4abdf0c85ca6050542b888ba316a1 Mon Sep 17 00:00:00 2001 From: Long Li Date: Thu, 6 Aug 2020 23:18:28 -0700 Subject: [PATCH 1335/1476] mm, slab: check GFP_SLAB_BUG_MASK before alloc_pages in kmalloc_order kmalloc cannot allocate memory from HIGHMEM. Allocating large amounts of memory currently bypasses the check and will simply leak the memory when page_address() returns NULL. To fix this, factor the GFP_SLAB_BUG_MASK check out of slab & slub, and call it from kmalloc_order() as well. In order to make the code clear, the warning message is put in one place. Signed-off-by: Long Li Signed-off-by: Andrew Morton Reviewed-by: Matthew Wilcox (Oracle) Reviewed-by: Pekka Enberg Acked-by: David Rientjes Cc: Christoph Lameter Cc: Joonsoo Kim Link: http://lkml.kernel.org/r/20200704035027.GA62481@lilong Signed-off-by: Linus Torvalds --- mm/slab.c | 10 +++------- mm/slab.h | 1 + mm/slab_common.c | 17 +++++++++++++++++ mm/slub.c | 9 ++------- 4 files changed, 23 insertions(+), 14 deletions(-) diff --git a/mm/slab.c b/mm/slab.c index 77e90f9de9c0c..0781a2384441c 100644 --- a/mm/slab.c +++ b/mm/slab.c @@ -2589,13 +2589,9 @@ static struct page *cache_grow_begin(struct kmem_cache *cachep, * Be lazy and only check for valid flags here, keeping it out of the * critical path in kmem_cache_alloc(). */ - if (unlikely(flags & GFP_SLAB_BUG_MASK)) { - gfp_t invalid_mask = flags & GFP_SLAB_BUG_MASK; - flags &= ~GFP_SLAB_BUG_MASK; - pr_warn("Unexpected gfp: %#x (%pGg). Fixing up to gfp: %#x (%pGg). Fix your code!\n", - invalid_mask, &invalid_mask, flags, &flags); - dump_stack(); - } + if (unlikely(flags & GFP_SLAB_BUG_MASK)) + flags = kmalloc_fix_flags(flags); + WARN_ON_ONCE(cachep->ctor && (flags & __GFP_ZERO)); local_flags = flags & (GFP_CONSTRAINT_MASK|GFP_RECLAIM_MASK); diff --git a/mm/slab.h b/mm/slab.h index 74f7e09a7cfd1..58ed025a0b23b 100644 --- a/mm/slab.h +++ b/mm/slab.h @@ -152,6 +152,7 @@ void create_kmalloc_caches(slab_flags_t); struct kmem_cache *kmalloc_slab(size_t, gfp_t); #endif +gfp_t kmalloc_fix_flags(gfp_t flags); /* Functions provided by the slab allocators */ int __kmem_cache_create(struct kmem_cache *, slab_flags_t flags); diff --git a/mm/slab_common.c b/mm/slab_common.c index e493203b50024..616ec8a0d91a5 100644 --- a/mm/slab_common.c +++ b/mm/slab_common.c @@ -26,6 +26,8 @@ #define CREATE_TRACE_POINTS #include +#include "internal.h" + #include "slab.h" enum slab_state slab_state; @@ -1332,6 +1334,18 @@ void __init create_kmalloc_caches(slab_flags_t flags) } #endif /* !CONFIG_SLOB */ +gfp_t kmalloc_fix_flags(gfp_t flags) +{ + gfp_t invalid_mask = flags & GFP_SLAB_BUG_MASK; + + flags &= ~GFP_SLAB_BUG_MASK; + pr_warn("Unexpected gfp: %#x (%pGg). Fixing up to gfp: %#x (%pGg). Fix your code!\n", + invalid_mask, &invalid_mask, flags, &flags); + dump_stack(); + + return flags; +} + /* * To avoid unnecessary overhead, we pass through large allocation requests * directly to the page allocator. We use __GFP_COMP, because we will need to @@ -1342,6 +1356,9 @@ void *kmalloc_order(size_t size, gfp_t flags, unsigned int order) void *ret = NULL; struct page *page; + if (unlikely(flags & GFP_SLAB_BUG_MASK)) + flags = kmalloc_fix_flags(flags); + flags |= __GFP_COMP; page = alloc_pages(flags, order); if (likely(page)) { diff --git a/mm/slub.c b/mm/slub.c index f226d66408eec..7f0764b6a3dfc 100644 --- a/mm/slub.c +++ b/mm/slub.c @@ -1745,13 +1745,8 @@ out: static struct page *new_slab(struct kmem_cache *s, gfp_t flags, int node) { - if (unlikely(flags & GFP_SLAB_BUG_MASK)) { - gfp_t invalid_mask = flags & GFP_SLAB_BUG_MASK; - flags &= ~GFP_SLAB_BUG_MASK; - pr_warn("Unexpected gfp: %#x (%pGg). Fixing up to gfp: %#x (%pGg). Fix your code!\n", - invalid_mask, &invalid_mask, flags, &flags); - dump_stack(); - } + if (unlikely(flags & GFP_SLAB_BUG_MASK)) + flags = kmalloc_fix_flags(flags); return allocate_slab(s, flags & (GFP_RECLAIM_MASK | GFP_CONSTRAINT_MASK), node); -- GitLab From 221503e1281f8a7d67eacbb6dc99db95acdf806c Mon Sep 17 00:00:00 2001 From: Xiao Yang Date: Thu, 6 Aug 2020 23:18:31 -0700 Subject: [PATCH 1336/1476] mm/slab.c: update outdated kmem_list3 in a comment kmem_list3 has been renamed to kmem_cache_node long long ago so update it. References: 6744f087ba2a ("slab: Common name for the per node structures") ce8eb6c424c7 ("slab: Rename list3/l3 to node") Signed-off-by: Xiao Yang Signed-off-by: Andrew Morton Reviewed-by: Pekka Enberg Cc: Christoph Lameter Cc: David Rientjes Cc: Joonsoo Kim Link: http://lkml.kernel.org/r/20200722033355.26908-1-yangx.jy@cn.fujitsu.com Signed-off-by: Linus Torvalds --- mm/slab.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mm/slab.c b/mm/slab.c index 0781a2384441c..390e249aaa8d3 100644 --- a/mm/slab.c +++ b/mm/slab.c @@ -1060,7 +1060,7 @@ int slab_prepare_cpu(unsigned int cpu) * offline. * * Even if all the cpus of a node are down, we don't free the - * kmem_list3 of any cache. This to avoid a race between cpu_down, and + * kmem_cache_node of any cache. This to avoid a race between cpu_down, and * a kmalloc allocation from another cpu for memory from the node of * the cpu going down. The list3 structure is usually allocated from * kmem_cache_create() and gets destroyed at kmem_cache_destroy(). -- GitLab From e17f1dfba37b84b574ae91e809ae3804fe5b29b9 Mon Sep 17 00:00:00 2001 From: Vlastimil Babka Date: Thu, 6 Aug 2020 23:18:35 -0700 Subject: [PATCH 1337/1476] mm, slub: extend slub_debug syntax for multiple blocks Patch series "slub_debug fixes and improvements". The slub_debug kernel boot parameter can either apply a single set of options to all caches or a list of caches. There is a use case where debugging is applied for all caches and then disabled at runtime for specific caches, for performance and memory consumption reasons [1]. As runtime changes are dangerous, extend the boot parameter syntax so that multiple blocks of either global or slab-specific options can be specified, with blocks delimited by ';'. This will also support the use case of [1] without runtime changes. For details see the updated Documentation/vm/slub.rst [1] https://lore.kernel.org/r/1383cd32-1ddc-4dac-b5f8-9c42282fa81c@codeaurora.org [weiyongjun1@huawei.com: make parse_slub_debug_flags() static] Link: http://lkml.kernel.org/r/20200702150522.4940-1-weiyongjun1@huawei.com Signed-off-by: Vlastimil Babka Signed-off-by: Andrew Morton Reviewed-by: Kees Cook Cc: Vlastimil Babka Cc: Christoph Lameter Cc: Jann Horn Cc: Roman Gushchin Cc: Vijayanand Jitta Cc: Pekka Enberg Cc: David Rientjes Cc: Joonsoo Kim Link: http://lkml.kernel.org/r/20200610163135.17364-2-vbabka@suse.cz Signed-off-by: Linus Torvalds --- .../admin-guide/kernel-parameters.txt | 2 +- Documentation/vm/slub.rst | 18 ++ mm/slub.c | 177 +++++++++++++----- 3 files changed, 145 insertions(+), 52 deletions(-) diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt index ef66b3c45ba22..c955522696892 100644 --- a/Documentation/admin-guide/kernel-parameters.txt +++ b/Documentation/admin-guide/kernel-parameters.txt @@ -4689,7 +4689,7 @@ fragmentation. Defaults to 1 for systems with more than 32MB of RAM, 0 otherwise. - slub_debug[=options[,slabs]] [MM, SLUB] + slub_debug[=options[,slabs][;[options[,slabs]]...] [MM, SLUB] Enabling slub_debug allows one to determine the culprit if slab objects become corrupted. Enabling slub_debug can create guard zones around objects and diff --git a/Documentation/vm/slub.rst b/Documentation/vm/slub.rst index 4eee598555c99..cfccb258cf42b 100644 --- a/Documentation/vm/slub.rst +++ b/Documentation/vm/slub.rst @@ -41,6 +41,11 @@ slub_debug=,,,... Enable options only for select slabs (no spaces after a comma) +Multiple blocks of options for all slabs or selected slabs can be given, with +blocks of options delimited by ';'. The last of "all slabs" blocks is applied +to all slabs except those that match one of the "select slabs" block. Options +of the first "select slabs" blocks that matches the slab's name are applied. + Possible debug options are:: F Sanity checks on (enables SLAB_DEBUG_CONSISTENCY_CHECKS @@ -83,6 +88,19 @@ switch off debugging for such caches by default, use:: slub_debug=O +You can apply different options to different list of slab names, using blocks +of options. This will enable red zoning for dentry and user tracking for +kmalloc. All other slabs will not get any debugging enabled:: + + slub_debug=Z,dentry;U,kmalloc-* + +You can also enable options (e.g. sanity checks and poisoning) for all caches +except some that are deemed too performance critical and don't need to be +debugged by specifying global debug options followed by a list of slab names +with "-" as options:: + + slub_debug=FZ;-,zs_handle,zspage + In case you forgot to enable debugging on the kernel command line: It is possible to enable debugging manually when the kernel is up. Look at the contents of:: diff --git a/mm/slub.c b/mm/slub.c index 7f0764b6a3dfc..829985d7c7c52 100644 --- a/mm/slub.c +++ b/mm/slub.c @@ -499,7 +499,7 @@ static slab_flags_t slub_debug = DEBUG_DEFAULT_FLAGS; static slab_flags_t slub_debug; #endif -static char *slub_debug_slabs; +static char *slub_debug_string; static int disable_higher_order_debug; /* @@ -1262,68 +1262,132 @@ out: return ret; } -static int __init setup_slub_debug(char *str) +/* + * Parse a block of slub_debug options. Blocks are delimited by ';' + * + * @str: start of block + * @flags: returns parsed flags, or DEBUG_DEFAULT_FLAGS if none specified + * @slabs: return start of list of slabs, or NULL when there's no list + * @init: assume this is initial parsing and not per-kmem-create parsing + * + * returns the start of next block if there's any, or NULL + */ +static char * +parse_slub_debug_flags(char *str, slab_flags_t *flags, char **slabs, bool init) { - slub_debug = DEBUG_DEFAULT_FLAGS; - if (*str++ != '=' || !*str) - /* - * No options specified. Switch on full debugging. - */ - goto out; + bool higher_order_disable = false; - if (*str == ',') + /* Skip any completely empty blocks */ + while (*str && *str == ';') + str++; + + if (*str == ',') { /* * No options but restriction on slabs. This means full * debugging for slabs matching a pattern. */ + *flags = DEBUG_DEFAULT_FLAGS; goto check_slabs; + } + *flags = 0; - slub_debug = 0; - if (*str == '-') - /* - * Switch off all debugging measures. - */ - goto out; - - /* - * Determine which debug features should be switched on - */ - for (; *str && *str != ','; str++) { + /* Determine which debug features should be switched on */ + for (; *str && *str != ',' && *str != ';'; str++) { switch (tolower(*str)) { + case '-': + *flags = 0; + break; case 'f': - slub_debug |= SLAB_CONSISTENCY_CHECKS; + *flags |= SLAB_CONSISTENCY_CHECKS; break; case 'z': - slub_debug |= SLAB_RED_ZONE; + *flags |= SLAB_RED_ZONE; break; case 'p': - slub_debug |= SLAB_POISON; + *flags |= SLAB_POISON; break; case 'u': - slub_debug |= SLAB_STORE_USER; + *flags |= SLAB_STORE_USER; break; case 't': - slub_debug |= SLAB_TRACE; + *flags |= SLAB_TRACE; break; case 'a': - slub_debug |= SLAB_FAILSLAB; + *flags |= SLAB_FAILSLAB; break; case 'o': /* * Avoid enabling debugging on caches if its minimum * order would increase as a result. */ - disable_higher_order_debug = 1; + higher_order_disable = true; break; default: - pr_err("slub_debug option '%c' unknown. skipped\n", - *str); + if (init) + pr_err("slub_debug option '%c' unknown. skipped\n", *str); } } - check_slabs: if (*str == ',') - slub_debug_slabs = str + 1; + *slabs = ++str; + else + *slabs = NULL; + + /* Skip over the slab list */ + while (*str && *str != ';') + str++; + + /* Skip any completely empty blocks */ + while (*str && *str == ';') + str++; + + if (init && higher_order_disable) + disable_higher_order_debug = 1; + + if (*str) + return str; + else + return NULL; +} + +static int __init setup_slub_debug(char *str) +{ + slab_flags_t flags; + char *saved_str; + char *slab_list; + bool global_slub_debug_changed = false; + bool slab_list_specified = false; + + slub_debug = DEBUG_DEFAULT_FLAGS; + if (*str++ != '=' || !*str) + /* + * No options specified. Switch on full debugging. + */ + goto out; + + saved_str = str; + while (str) { + str = parse_slub_debug_flags(str, &flags, &slab_list, true); + + if (!slab_list) { + slub_debug = flags; + global_slub_debug_changed = true; + } else { + slab_list_specified = true; + } + } + + /* + * For backwards compatibility, a single list of flags with list of + * slabs means debugging is only enabled for those slabs, so the global + * slub_debug should be 0. We can extended that to multiple lists as + * long as there is no option specifying flags without a slab list. + */ + if (slab_list_specified) { + if (!global_slub_debug_changed) + slub_debug = 0; + slub_debug_string = saved_str; + } out: if ((static_branch_unlikely(&init_on_alloc) || static_branch_unlikely(&init_on_free)) && @@ -1352,36 +1416,47 @@ slab_flags_t kmem_cache_flags(unsigned int object_size, { char *iter; size_t len; + char *next_block; + slab_flags_t block_flags; /* If slub_debug = 0, it folds into the if conditional. */ - if (!slub_debug_slabs) + if (!slub_debug_string) return flags | slub_debug; len = strlen(name); - iter = slub_debug_slabs; - while (*iter) { - char *end, *glob; - size_t cmplen; - - end = strchrnul(iter, ','); + next_block = slub_debug_string; + /* Go through all blocks of debug options, see if any matches our slab's name */ + while (next_block) { + next_block = parse_slub_debug_flags(next_block, &block_flags, &iter, false); + if (!iter) + continue; + /* Found a block that has a slab list, search it */ + while (*iter) { + char *end, *glob; + size_t cmplen; + + end = strchrnul(iter, ','); + if (next_block && next_block < end) + end = next_block - 1; + + glob = strnchr(iter, end - iter, '*'); + if (glob) + cmplen = glob - iter; + else + cmplen = max_t(size_t, len, (end - iter)); - glob = strnchr(iter, end - iter, '*'); - if (glob) - cmplen = glob - iter; - else - cmplen = max_t(size_t, len, (end - iter)); + if (!strncmp(name, iter, cmplen)) { + flags |= block_flags; + return flags; + } - if (!strncmp(name, iter, cmplen)) { - flags |= slub_debug; - break; + if (!*end || *end == ';') + break; + iter = end + 1; } - - if (!*end) - break; - iter = end + 1; } - return flags; + return slub_debug; } #else /* !CONFIG_SLUB_DEBUG */ static inline void setup_object_debug(struct kmem_cache *s, -- GitLab From ad38b5b1131e2a0e5c46724847da2e1eba31fb68 Mon Sep 17 00:00:00 2001 From: Vlastimil Babka Date: Thu, 6 Aug 2020 23:18:38 -0700 Subject: [PATCH 1338/1476] mm, slub: make some slub_debug related attributes read-only SLUB_DEBUG creates several files under /sys/kernel/slab// that can be read to check if the respective debugging options are enabled for given cache. The options can be also toggled at runtime by writing into the files. Some of those, namely red_zone, poison, and store_user can be toggled only when no objects yet exist in the cache. Vijayanand reports [1] that there is a problem with freelist randomization if changing the debugging option's state results in different number of objects per page, and the random sequence cache needs thus needs to be recomputed. However, another problem is that the check for "no objects yet exist in the cache" is racy, as noted by Jann [2] and fixing that would add overhead or otherwise complicate the allocation/freeing paths. Thus it would be much simpler just to remove the runtime toggling support. The documentation describes it's "In case you forgot to enable debugging on the kernel command line", but the neccessity of having no objects limits its usefulness anyway for many caches. Vijayanand describes an use case [3] where debugging is enabled for all but zram caches for memory overhead reasons, and using the runtime toggles was the only way to achieve such configuration. After the previous patch it's now possible to do that directly from the kernel boot option, so we can remove the dangerous runtime toggles by making the /sys attribute files read-only. While updating it, also improve the documentation of the debugging /sys files. [1] https://lkml.kernel.org/r/1580379523-32272-1-git-send-email-vjitta@codeaurora.org [2] https://lore.kernel.org/r/CAG48ez31PP--h6_FzVyfJ4H86QYczAFPdxtJHUEEan+7VJETAQ@mail.gmail.com [3] https://lore.kernel.org/r/1383cd32-1ddc-4dac-b5f8-9c42282fa81c@codeaurora.org Reported-by: Vijayanand Jitta Reported-by: Jann Horn Signed-off-by: Vlastimil Babka Signed-off-by: Andrew Morton Reviewed-by: Kees Cook Acked-by: Roman Gushchin Cc: Christoph Lameter Cc: David Rientjes Cc: Joonsoo Kim Cc: Pekka Enberg Link: http://lkml.kernel.org/r/20200610163135.17364-3-vbabka@suse.cz Signed-off-by: Linus Torvalds --- Documentation/vm/slub.rst | 28 ++++++++++++++---------- mm/slub.c | 46 +++------------------------------------ 2 files changed, 20 insertions(+), 54 deletions(-) diff --git a/Documentation/vm/slub.rst b/Documentation/vm/slub.rst index cfccb258cf42b..36241dfba0243 100644 --- a/Documentation/vm/slub.rst +++ b/Documentation/vm/slub.rst @@ -101,20 +101,26 @@ with "-" as options:: slub_debug=FZ;-,zs_handle,zspage -In case you forgot to enable debugging on the kernel command line: It is -possible to enable debugging manually when the kernel is up. Look at the -contents of:: +The state of each debug option for a slab can be found in the respective files +under:: /sys/kernel/slab// -Look at the writable files. Writing 1 to them will enable the -corresponding debug option. All options can be set on a slab that does -not contain objects. If the slab already contains objects then sanity checks -and tracing may only be enabled. The other options may cause the realignment -of objects. - -Careful with tracing: It may spew out lots of information and never stop if -used on the wrong slab. +If the file contains 1, the option is enabled, 0 means disabled. The debug +options from the ``slub_debug`` parameter translate to the following files:: + + F sanity_checks + Z red_zone + P poison + U store_user + T trace + A failslab + +The sanity_checks, trace and failslab files are writable, so writing 1 or 0 +will enable or disable the option at runtime. The writes to trace and failslab +may return -EINVAL if the cache is subject to slab merging. Careful with +tracing: It may spew out lots of information and never stop if used on the +wrong slab. Slab merging ============ diff --git a/mm/slub.c b/mm/slub.c index 829985d7c7c52..fd0b196fdaaf6 100644 --- a/mm/slub.c +++ b/mm/slub.c @@ -5335,61 +5335,21 @@ static ssize_t red_zone_show(struct kmem_cache *s, char *buf) return sprintf(buf, "%d\n", !!(s->flags & SLAB_RED_ZONE)); } -static ssize_t red_zone_store(struct kmem_cache *s, - const char *buf, size_t length) -{ - if (any_slab_objects(s)) - return -EBUSY; - - s->flags &= ~SLAB_RED_ZONE; - if (buf[0] == '1') { - s->flags |= SLAB_RED_ZONE; - } - calculate_sizes(s, -1); - return length; -} -SLAB_ATTR(red_zone); +SLAB_ATTR_RO(red_zone); static ssize_t poison_show(struct kmem_cache *s, char *buf) { return sprintf(buf, "%d\n", !!(s->flags & SLAB_POISON)); } -static ssize_t poison_store(struct kmem_cache *s, - const char *buf, size_t length) -{ - if (any_slab_objects(s)) - return -EBUSY; - - s->flags &= ~SLAB_POISON; - if (buf[0] == '1') { - s->flags |= SLAB_POISON; - } - calculate_sizes(s, -1); - return length; -} -SLAB_ATTR(poison); +SLAB_ATTR_RO(poison); static ssize_t store_user_show(struct kmem_cache *s, char *buf) { return sprintf(buf, "%d\n", !!(s->flags & SLAB_STORE_USER)); } -static ssize_t store_user_store(struct kmem_cache *s, - const char *buf, size_t length) -{ - if (any_slab_objects(s)) - return -EBUSY; - - s->flags &= ~SLAB_STORE_USER; - if (buf[0] == '1') { - s->flags &= ~__CMPXCHG_DOUBLE; - s->flags |= SLAB_STORE_USER; - } - calculate_sizes(s, -1); - return length; -} -SLAB_ATTR(store_user); +SLAB_ATTR_RO(store_user); static ssize_t validate_show(struct kmem_cache *s, char *buf) { -- GitLab From 32a6f409b6935d175464f26d31250ea13fda6b66 Mon Sep 17 00:00:00 2001 From: Vlastimil Babka Date: Thu, 6 Aug 2020 23:18:41 -0700 Subject: [PATCH 1339/1476] mm, slub: remove runtime allocation order changes SLUB allows runtime changing of page allocation order by writing into the /sys/kernel/slab//order file. Jann has reported [1] that this interface allows the order to be set too small, leading to crashes. While it's possible to fix the immediate issue, closer inspection reveals potential races. Storing the new order calls calculate_sizes() which non-atomically updates a lot of kmem_cache fields while the cache is still in use. Unexpected behavior might occur even if the fields are set to the same value as they were. This could be fixed by splitting out the part of calculate_sizes() that depends on forced_order, so that we only update kmem_cache.oo field. This could still race with init_cache_random_seq(), shuffle_freelist(), allocate_slab(). Perhaps it's possible to audit and e.g. add some READ_ONCE/WRITE_ONCE accesses, it might be easier just to remove the runtime order changes, which is what this patch does. If there are valid usecases for per-cache order setting, we could e.g. extend the boot parameters to do that. [1] https://lore.kernel.org/r/CAG48ez31PP--h6_FzVyfJ4H86QYczAFPdxtJHUEEan+7VJETAQ@mail.gmail.com Reported-by: Jann Horn Signed-off-by: Vlastimil Babka Signed-off-by: Andrew Morton Reviewed-by: Kees Cook Acked-by: Christoph Lameter Acked-by: Roman Gushchin Cc: Vijayanand Jitta Cc: David Rientjes Cc: Joonsoo Kim Cc: Pekka Enberg Link: http://lkml.kernel.org/r/20200610163135.17364-4-vbabka@suse.cz Signed-off-by: Linus Torvalds --- mm/slub.c | 19 +------------------ 1 file changed, 1 insertion(+), 18 deletions(-) diff --git a/mm/slub.c b/mm/slub.c index fd0b196fdaaf6..f03b1073bdcfc 100644 --- a/mm/slub.c +++ b/mm/slub.c @@ -5095,28 +5095,11 @@ static ssize_t objs_per_slab_show(struct kmem_cache *s, char *buf) } SLAB_ATTR_RO(objs_per_slab); -static ssize_t order_store(struct kmem_cache *s, - const char *buf, size_t length) -{ - unsigned int order; - int err; - - err = kstrtouint(buf, 10, &order); - if (err) - return err; - - if (order > slub_max_order || order < slub_min_order) - return -EINVAL; - - calculate_sizes(s, order); - return length; -} - static ssize_t order_show(struct kmem_cache *s, char *buf) { return sprintf(buf, "%u\n", oo_order(s->oo)); } -SLAB_ATTR(order); +SLAB_ATTR_RO(order); static ssize_t min_partial_show(struct kmem_cache *s, char *buf) { -- GitLab From 060807f841ac94d3826ce6fa3b4f3831cd0c015b Mon Sep 17 00:00:00 2001 From: Vlastimil Babka Date: Thu, 6 Aug 2020 23:18:45 -0700 Subject: [PATCH 1340/1476] mm, slub: make remaining slub_debug related attributes read-only SLUB_DEBUG creates several files under /sys/kernel/slab// that can be read to check if the respective debugging options are enabled for given cache. Some options, namely sanity_checks, trace, and failslab can be also enabled and disabled at runtime by writing into the files. The runtime toggling is racy. Some options disable __CMPXCHG_DOUBLE when enabled, which means that in case of concurrent allocations, some can still use __CMPXCHG_DOUBLE and some not, leading to potential corruption. The s->flags field is also not updated or checked atomically. The simplest solution is to remove the runtime toggling. The extended slub_debug boot parameter syntax introduced by earlier patch should allow to fine-tune the debugging configuration during boot with same granularity. Signed-off-by: Vlastimil Babka Signed-off-by: Andrew Morton Reviewed-by: Kees Cook Acked-by: Roman Gushchin Cc: Christoph Lameter Cc: Jann Horn Cc: Vijayanand Jitta Cc: David Rientjes Cc: Joonsoo Kim Cc: Pekka Enberg Link: http://lkml.kernel.org/r/20200610163135.17364-5-vbabka@suse.cz Signed-off-by: Linus Torvalds --- Documentation/vm/slub.rst | 7 ++--- mm/slub.c | 62 ++------------------------------------- 2 files changed, 5 insertions(+), 64 deletions(-) diff --git a/Documentation/vm/slub.rst b/Documentation/vm/slub.rst index 36241dfba0243..289d231cee972 100644 --- a/Documentation/vm/slub.rst +++ b/Documentation/vm/slub.rst @@ -116,11 +116,8 @@ options from the ``slub_debug`` parameter translate to the following files:: T trace A failslab -The sanity_checks, trace and failslab files are writable, so writing 1 or 0 -will enable or disable the option at runtime. The writes to trace and failslab -may return -EINVAL if the cache is subject to slab merging. Careful with -tracing: It may spew out lots of information and never stop if used on the -wrong slab. +Careful with tracing: It may spew out lots of information and never stop if +used on the wrong slab. Slab merging ============ diff --git a/mm/slub.c b/mm/slub.c index f03b1073bdcfc..5848b7c3575aa 100644 --- a/mm/slub.c +++ b/mm/slub.c @@ -5040,20 +5040,6 @@ static ssize_t show_slab_objects(struct kmem_cache *s, return x + sprintf(buf + x, "\n"); } -#ifdef CONFIG_SLUB_DEBUG -static int any_slab_objects(struct kmem_cache *s) -{ - int node; - struct kmem_cache_node *n; - - for_each_kmem_cache_node(s, node, n) - if (atomic_long_read(&n->total_objects)) - return 1; - - return 0; -} -#endif - #define to_slab_attr(n) container_of(n, struct slab_attribute, attr) #define to_slab(n) container_of(n, struct kmem_cache, kobj) @@ -5275,43 +5261,13 @@ static ssize_t sanity_checks_show(struct kmem_cache *s, char *buf) { return sprintf(buf, "%d\n", !!(s->flags & SLAB_CONSISTENCY_CHECKS)); } - -static ssize_t sanity_checks_store(struct kmem_cache *s, - const char *buf, size_t length) -{ - s->flags &= ~SLAB_CONSISTENCY_CHECKS; - if (buf[0] == '1') { - s->flags &= ~__CMPXCHG_DOUBLE; - s->flags |= SLAB_CONSISTENCY_CHECKS; - } - return length; -} -SLAB_ATTR(sanity_checks); +SLAB_ATTR_RO(sanity_checks); static ssize_t trace_show(struct kmem_cache *s, char *buf) { return sprintf(buf, "%d\n", !!(s->flags & SLAB_TRACE)); } - -static ssize_t trace_store(struct kmem_cache *s, const char *buf, - size_t length) -{ - /* - * Tracing a merged cache is going to give confusing results - * as well as cause other issues like converting a mergeable - * cache into an umergeable one. - */ - if (s->refcount > 1) - return -EINVAL; - - s->flags &= ~SLAB_TRACE; - if (buf[0] == '1') { - s->flags &= ~__CMPXCHG_DOUBLE; - s->flags |= SLAB_TRACE; - } - return length; -} -SLAB_ATTR(trace); +SLAB_ATTR_RO(trace); static ssize_t red_zone_show(struct kmem_cache *s, char *buf) { @@ -5375,19 +5331,7 @@ static ssize_t failslab_show(struct kmem_cache *s, char *buf) { return sprintf(buf, "%d\n", !!(s->flags & SLAB_FAILSLAB)); } - -static ssize_t failslab_store(struct kmem_cache *s, const char *buf, - size_t length) -{ - if (s->refcount > 1) - return -EINVAL; - - s->flags &= ~SLAB_FAILSLAB; - if (buf[0] == '1') - s->flags |= SLAB_FAILSLAB; - return length; -} -SLAB_ATTR(failslab); +SLAB_ATTR_RO(failslab); #endif static ssize_t shrink_show(struct kmem_cache *s, char *buf) -- GitLab From 8f58119ac49c03c5a0879353661c586b0436c250 Mon Sep 17 00:00:00 2001 From: Vlastimil Babka Date: Thu, 6 Aug 2020 23:18:48 -0700 Subject: [PATCH 1341/1476] mm, slub: make reclaim_account attribute read-only The attribute reflects the SLAB_RECLAIM_ACCOUNT cache flag. It's not clear why this attribute was writable in the first place, as it's tied to how the cache is used by its creator, it's not a user tunable. Furthermore: - it affects slab merging, but that's not being checked while toggled - if affects whether __GFP_RECLAIMABLE flag is used to allocate page, but the runtime toggle doesn't update allocflags - it affects cache_vmstat_idx() so runtime toggling might lead to incosistency of NR_SLAB_RECLAIMABLE and NR_SLAB_UNRECLAIMABLE Thus make it read-only. Signed-off-by: Vlastimil Babka Signed-off-by: Andrew Morton Reviewed-by: Kees Cook Acked-by: Roman Gushchin Cc: Christoph Lameter Cc: Jann Horn Cc: Vijayanand Jitta Cc: David Rientjes Cc: Joonsoo Kim Cc: Pekka Enberg Link: http://lkml.kernel.org/r/20200610163135.17364-6-vbabka@suse.cz Signed-off-by: Linus Torvalds --- mm/slub.c | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/mm/slub.c b/mm/slub.c index 5848b7c3575aa..617cf1fff1285 100644 --- a/mm/slub.c +++ b/mm/slub.c @@ -5207,16 +5207,7 @@ static ssize_t reclaim_account_show(struct kmem_cache *s, char *buf) { return sprintf(buf, "%d\n", !!(s->flags & SLAB_RECLAIM_ACCOUNT)); } - -static ssize_t reclaim_account_store(struct kmem_cache *s, - const char *buf, size_t length) -{ - s->flags &= ~SLAB_RECLAIM_ACCOUNT; - if (buf[0] == '1') - s->flags |= SLAB_RECLAIM_ACCOUNT; - return length; -} -SLAB_ATTR(reclaim_account); +SLAB_ATTR_RO(reclaim_account); static ssize_t hwcache_align_show(struct kmem_cache *s, char *buf) { -- GitLab From ca0cab65ea2b8c1527dc48c8dfd38ae055f5f241 Mon Sep 17 00:00:00 2001 From: Vlastimil Babka Date: Thu, 6 Aug 2020 23:18:51 -0700 Subject: [PATCH 1342/1476] mm, slub: introduce static key for slub_debug() One advantage of CONFIG_SLUB_DEBUG is that a generic distro kernel can be built with the option enabled, but it's inactive until simply enabled on boot, without rebuilding the kernel. With a static key, we can further eliminate the overhead of checking whether a cache has a particular debug flag enabled if we know that there are no such caches (slub_debug was not enabled during boot). We use the same mechanism also for e.g. page_owner, debug_pagealloc or kmemcg functionality. This patch introduces the static key and makes the general check for per-cache debug flags kmem_cache_debug() use it. This benefits several call sites, including (slow path but still rather frequent) __slab_free(). The next patches will add more uses. Signed-off-by: Vlastimil Babka Signed-off-by: Andrew Morton Reviewed-by: Kees Cook Acked-by: Roman Gushchin Acked-by: Christoph Lameter Cc: Jann Horn Cc: Vijayanand Jitta Cc: David Rientjes Cc: Joonsoo Kim Cc: Pekka Enberg Link: http://lkml.kernel.org/r/20200610163135.17364-7-vbabka@suse.cz Signed-off-by: Linus Torvalds --- mm/slub.c | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/mm/slub.c b/mm/slub.c index 617cf1fff1285..8adab4c5296d7 100644 --- a/mm/slub.c +++ b/mm/slub.c @@ -114,13 +114,21 @@ * the fast path and disables lockless freelists. */ +#ifdef CONFIG_SLUB_DEBUG +#ifdef CONFIG_SLUB_DEBUG_ON +DEFINE_STATIC_KEY_TRUE(slub_debug_enabled); +#else +DEFINE_STATIC_KEY_FALSE(slub_debug_enabled); +#endif +#endif + static inline int kmem_cache_debug(struct kmem_cache *s) { #ifdef CONFIG_SLUB_DEBUG - return unlikely(s->flags & SLAB_DEBUG_FLAGS); -#else - return 0; + if (static_branch_unlikely(&slub_debug_enabled)) + return s->flags & SLAB_DEBUG_FLAGS; #endif + return 0; } void *fixup_red_left(struct kmem_cache *s, void *p) @@ -1389,6 +1397,8 @@ static int __init setup_slub_debug(char *str) slub_debug_string = saved_str; } out: + if (slub_debug != 0 || slub_debug_string) + static_branch_enable(&slub_debug_enabled); if ((static_branch_unlikely(&init_on_alloc) || static_branch_unlikely(&init_on_free)) && (slub_debug & SLAB_POISON)) -- GitLab From 59052e89fc89e3e6bef0151052e093566e446851 Mon Sep 17 00:00:00 2001 From: Vlastimil Babka Date: Thu, 6 Aug 2020 23:18:55 -0700 Subject: [PATCH 1343/1476] mm, slub: introduce kmem_cache_debug_flags() There are few places that call kmem_cache_debug(s) (which tests if any of debug flags are enabled for a cache) immediately followed by a test for a specific flag. The compiler can probably eliminate the extra check, but we can make the code nicer by introducing kmem_cache_debug_flags() that works like kmem_cache_debug() (including the static key check) but tests for specific flag(s). The next patches will add more users. [vbabka@suse.cz: change return from int to bool, per Kees. Add VM_WARN_ON_ONCE() for invalid flags, per Roman] Link: http://lkml.kernel.org/r/949b90ed-e0f0-07d7-4d21-e30ec0958a7c@suse.cz Signed-off-by: Vlastimil Babka Signed-off-by: Andrew Morton Acked-by: Roman Gushchin Acked-by: Christoph Lameter Acked-by: Kees Cook Cc: Jann Horn Cc: Vijayanand Jitta Cc: David Rientjes Cc: Joonsoo Kim Cc: Pekka Enberg Link: http://lkml.kernel.org/r/20200610163135.17364-8-vbabka@suse.cz Signed-off-by: Linus Torvalds --- mm/slub.c | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/mm/slub.c b/mm/slub.c index 8adab4c5296d7..97074631a2d19 100644 --- a/mm/slub.c +++ b/mm/slub.c @@ -122,18 +122,29 @@ DEFINE_STATIC_KEY_FALSE(slub_debug_enabled); #endif #endif -static inline int kmem_cache_debug(struct kmem_cache *s) +/* + * Returns true if any of the specified slub_debug flags is enabled for the + * cache. Use only for flags parsed by setup_slub_debug() as it also enables + * the static key. + */ +static inline bool kmem_cache_debug_flags(struct kmem_cache *s, slab_flags_t flags) { + VM_WARN_ON_ONCE(!(flags & SLAB_DEBUG_FLAGS)); #ifdef CONFIG_SLUB_DEBUG if (static_branch_unlikely(&slub_debug_enabled)) - return s->flags & SLAB_DEBUG_FLAGS; + return s->flags & flags; #endif - return 0; + return false; +} + +static inline bool kmem_cache_debug(struct kmem_cache *s) +{ + return kmem_cache_debug_flags(s, SLAB_DEBUG_FLAGS); } void *fixup_red_left(struct kmem_cache *s, void *p) { - if (kmem_cache_debug(s) && s->flags & SLAB_RED_ZONE) + if (kmem_cache_debug_flags(s, SLAB_RED_ZONE)) p += s->red_left_pad; return p; @@ -4060,7 +4071,7 @@ void __check_heap_object(const void *ptr, unsigned long n, struct page *page, offset = (ptr - page_address(page)) % s->size; /* Adjust for redzone and reject if within the redzone. */ - if (kmem_cache_debug(s) && s->flags & SLAB_RED_ZONE) { + if (kmem_cache_debug_flags(s, SLAB_RED_ZONE)) { if (offset < s->red_left_pad) usercopy_abort("SLUB object in left red zone", s->name, to_user, offset, n); -- GitLab From 8fc8d6664247a6e65cba000789e2e85e2288f6f7 Mon Sep 17 00:00:00 2001 From: Vlastimil Babka Date: Thu, 6 Aug 2020 23:18:58 -0700 Subject: [PATCH 1344/1476] mm, slub: extend checks guarded by slub_debug static key There are few more places in SLUB that could benefit from reduced overhead of the static key introduced by a previous patch: - setup_object_debug() called on each object in newly allocated slab page - setup_page_debug() called on newly allocated slab page - __free_slab() called on freed slab page Signed-off-by: Vlastimil Babka Signed-off-by: Andrew Morton Acked-by: Roman Gushchin Acked-by: Christoph Lameter Cc: Jann Horn Cc: Kees Cook Cc: Vijayanand Jitta Cc: David Rientjes Cc: Joonsoo Kim Cc: Pekka Enberg Link: http://lkml.kernel.org/r/20200610163135.17364-9-vbabka@suse.cz Signed-off-by: Linus Torvalds --- mm/slub.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/mm/slub.c b/mm/slub.c index 97074631a2d19..0b80a84098366 100644 --- a/mm/slub.c +++ b/mm/slub.c @@ -1131,7 +1131,7 @@ static inline void dec_slabs_node(struct kmem_cache *s, int node, int objects) static void setup_object_debug(struct kmem_cache *s, struct page *page, void *object) { - if (!(s->flags & (SLAB_STORE_USER|SLAB_RED_ZONE|__OBJECT_POISON))) + if (!kmem_cache_debug_flags(s, SLAB_STORE_USER|SLAB_RED_ZONE|__OBJECT_POISON)) return; init_object(s, object, SLUB_RED_INACTIVE); @@ -1141,7 +1141,7 @@ static void setup_object_debug(struct kmem_cache *s, struct page *page, static void setup_page_debug(struct kmem_cache *s, struct page *page, void *addr) { - if (!(s->flags & SLAB_POISON)) + if (!kmem_cache_debug_flags(s, SLAB_POISON)) return; metadata_access_enable(); @@ -1853,7 +1853,7 @@ static void __free_slab(struct kmem_cache *s, struct page *page) int order = compound_order(page); int pages = 1 << order; - if (s->flags & SLAB_CONSISTENCY_CHECKS) { + if (kmem_cache_debug_flags(s, SLAB_CONSISTENCY_CHECKS)) { void *p; slab_pad_check(s, page); -- GitLab From d3c58f24be1bf10fa9e11977080a2398ddcd8361 Mon Sep 17 00:00:00 2001 From: Vlastimil Babka Date: Thu, 6 Aug 2020 23:19:01 -0700 Subject: [PATCH 1345/1476] mm, slab/slub: move and improve cache_from_obj() The function cache_from_obj() was added by commit b9ce5ef49f00 ("sl[au]b: always get the cache from its page in kmem_cache_free()") to support kmemcg, where per-memcg cache can be different from the root one, so we can't use the kmem_cache pointer given to kmem_cache_free(). Prior to that commit, SLUB already had debugging check+warning that could be enabled to compare the given kmem_cache pointer to one referenced by the slab page where the object-to-be-freed resides. This check was moved to cache_from_obj(). Later the check was also enabled for SLAB_FREELIST_HARDENED configs by commit 598a0717a816 ("mm/slab: validate cache membership under freelist hardening"). These checks and warnings can be useful especially for the debugging, which can be improved. Commit 598a0717a816 changed the pr_err() with WARN_ON_ONCE() to WARN_ONCE() so only the first hit is now reported, others are silent. This patch changes it to WARN() so that all errors are reported. It's also useful to print SLUB allocation/free tracking info for the offending object, if tracking is enabled. We could export the SLUB print_tracking() function and provide an empty one for SLAB, or realize that both the debugging and hardening cases in cache_from_obj() are only supported by SLUB anyway. So this patch moves cache_from_obj() from slab.h to separate instances in slab.c and slub.c, where the SLAB version only does the kmemcg lookup and even could be completely removed once the kmemcg rework [1] is merged. The SLUB version can thus easily use the print_tracking() function. It can also use the kmem_cache_debug_flags() static key check for improved performance in kernels without the hardening and with debugging not enabled on boot. [1] https://lore.kernel.org/r/20200608230654.828134-18-guro@fb.com Signed-off-by: Vlastimil Babka Signed-off-by: Andrew Morton Cc: Christoph Lameter Cc: Jann Horn Cc: Kees Cook Cc: Vijayanand Jitta Cc: David Rientjes Cc: Joonsoo Kim Cc: Pekka Enberg Link: http://lkml.kernel.org/r/20200610163135.17364-10-vbabka@suse.cz Signed-off-by: Linus Torvalds --- mm/slab.c | 8 ++++++++ mm/slab.h | 23 ----------------------- mm/slub.c | 21 +++++++++++++++++++++ 3 files changed, 29 insertions(+), 23 deletions(-) diff --git a/mm/slab.c b/mm/slab.c index 390e249aaa8d3..af3d3887b9b81 100644 --- a/mm/slab.c +++ b/mm/slab.c @@ -3678,6 +3678,14 @@ void *__kmalloc_track_caller(size_t size, gfp_t flags, unsigned long caller) } EXPORT_SYMBOL(__kmalloc_track_caller); +static inline struct kmem_cache *cache_from_obj(struct kmem_cache *s, void *x) +{ + if (memcg_kmem_enabled()) + return virt_to_cache(x); + else + return s; +} + /** * kmem_cache_free - Deallocate an object * @cachep: The cache the allocation was from. diff --git a/mm/slab.h b/mm/slab.h index 58ed025a0b23b..a21c78eccee77 100644 --- a/mm/slab.h +++ b/mm/slab.h @@ -504,29 +504,6 @@ static __always_inline void uncharge_slab_page(struct page *page, int order, memcg_uncharge_slab(page, order, s); } -static inline struct kmem_cache *cache_from_obj(struct kmem_cache *s, void *x) -{ - struct kmem_cache *cachep; - - /* - * When kmemcg is not being used, both assignments should return the - * same value. but we don't want to pay the assignment price in that - * case. If it is not compiled in, the compiler should be smart enough - * to not do even the assignment. In that case, slab_equal_or_root - * will also be a constant. - */ - if (!memcg_kmem_enabled() && - !IS_ENABLED(CONFIG_SLAB_FREELIST_HARDENED) && - !unlikely(s->flags & SLAB_CONSISTENCY_CHECKS)) - return s; - - cachep = virt_to_cache(x); - WARN_ONCE(cachep && !slab_equal_or_root(cachep, s), - "%s: Wrong slab cache. %s but object is from %s\n", - __func__, s->name, cachep->name); - return cachep; -} - static inline size_t slab_ksize(const struct kmem_cache *s) { #ifndef CONFIG_SLUB diff --git a/mm/slub.c b/mm/slub.c index 0b80a84098366..6a99a3c945cbe 100644 --- a/mm/slub.c +++ b/mm/slub.c @@ -1525,6 +1525,10 @@ static bool freelist_corrupted(struct kmem_cache *s, struct page *page, { return false; } + +static void print_tracking(struct kmem_cache *s, void *object) +{ +} #endif /* CONFIG_SLUB_DEBUG */ /* @@ -3171,6 +3175,23 @@ void ___cache_free(struct kmem_cache *cache, void *x, unsigned long addr) } #endif +static inline struct kmem_cache *cache_from_obj(struct kmem_cache *s, void *x) +{ + struct kmem_cache *cachep; + + if (!IS_ENABLED(CONFIG_SLAB_FREELIST_HARDENED) && + !memcg_kmem_enabled() && + !kmem_cache_debug_flags(s, SLAB_CONSISTENCY_CHECKS)) + return s; + + cachep = virt_to_cache(x); + if (WARN(cachep && !slab_equal_or_root(cachep, s), + "%s: Wrong slab cache. %s but object is from %s\n", + __func__, s->name, cachep->name)) + print_tracking(cachep, x); + return cachep; +} + void kmem_cache_free(struct kmem_cache *s, void *x) { s = cache_from_obj(s, x); -- GitLab From e42f174e43e47b623d9dbf814521c4961000c962 Mon Sep 17 00:00:00 2001 From: Vlastimil Babka Date: Thu, 6 Aug 2020 23:19:05 -0700 Subject: [PATCH 1346/1476] mm, slab/slub: improve error reporting and overhead of cache_from_obj() cache_from_obj() was added by commit b9ce5ef49f00 ("sl[au]b: always get the cache from its page in kmem_cache_free()") to support kmemcg, where per-memcg cache can be different from the root one, so we can't use the kmem_cache pointer given to kmem_cache_free(). Prior to that commit, SLUB already had debugging check+warning that could be enabled to compare the given kmem_cache pointer to one referenced by the slab page where the object-to-be-freed resides. This check was moved to cache_from_obj(). Later the check was also enabled for SLAB_FREELIST_HARDENED configs by commit 598a0717a816 ("mm/slab: validate cache membership under freelist hardening"). These checks and warnings can be useful especially for the debugging, which can be improved. Commit 598a0717a816 changed the pr_err() with WARN_ON_ONCE() to WARN_ONCE() so only the first hit is now reported, others are silent. This patch changes it to WARN() so that all errors are reported. It's also useful to print SLUB allocation/free tracking info for the offending object, if tracking is enabled. Thus, export the SLUB print_tracking() function and provide an empty one for SLAB. For SLUB we can also benefit from the static key check in kmem_cache_debug_flags(), but we need to move this function to slab.h and declare the static key there. [1] https://lore.kernel.org/r/20200608230654.828134-18-guro@fb.com [vbabka@suse.cz: avoid bogus WARN()] Link: https://lore.kernel.org/r/20200623090213.GW5535@shao2-debian Link: http://lkml.kernel.org/r/b33e0fa7-cd28-4788-9e54-5927846329ef@suse.cz Signed-off-by: Vlastimil Babka Signed-off-by: Andrew Morton Acked-by: Kees Cook Acked-by: Roman Gushchin Cc: Christoph Lameter Cc: Pekka Enberg Cc: David Rientjes Cc: Joonsoo Kim Cc: Matthew Garrett Cc: Jann Horn Cc: Vijayanand Jitta Cc: Vinayak Menon Link: http://lkml.kernel.org/r/afeda7ac-748b-33d8-a905-56b708148ad5@suse.cz Signed-off-by: Linus Torvalds --- mm/slab.c | 8 -------- mm/slab.h | 45 +++++++++++++++++++++++++++++++++++++++++++++ mm/slub.c | 38 +------------------------------------- 3 files changed, 46 insertions(+), 45 deletions(-) diff --git a/mm/slab.c b/mm/slab.c index af3d3887b9b81..390e249aaa8d3 100644 --- a/mm/slab.c +++ b/mm/slab.c @@ -3678,14 +3678,6 @@ void *__kmalloc_track_caller(size_t size, gfp_t flags, unsigned long caller) } EXPORT_SYMBOL(__kmalloc_track_caller); -static inline struct kmem_cache *cache_from_obj(struct kmem_cache *s, void *x) -{ - if (memcg_kmem_enabled()) - return virt_to_cache(x); - else - return s; -} - /** * kmem_cache_free - Deallocate an object * @cachep: The cache the allocation was from. diff --git a/mm/slab.h b/mm/slab.h index a21c78eccee77..fceb4341ba910 100644 --- a/mm/slab.h +++ b/mm/slab.h @@ -276,6 +276,34 @@ static inline int cache_vmstat_idx(struct kmem_cache *s) NR_SLAB_RECLAIMABLE : NR_SLAB_UNRECLAIMABLE; } +#ifdef CONFIG_SLUB_DEBUG +#ifdef CONFIG_SLUB_DEBUG_ON +DECLARE_STATIC_KEY_TRUE(slub_debug_enabled); +#else +DECLARE_STATIC_KEY_FALSE(slub_debug_enabled); +#endif +extern void print_tracking(struct kmem_cache *s, void *object); +#else +static inline void print_tracking(struct kmem_cache *s, void *object) +{ +} +#endif + +/* + * Returns true if any of the specified slub_debug flags is enabled for the + * cache. Use only for flags parsed by setup_slub_debug() as it also enables + * the static key. + */ +static inline bool kmem_cache_debug_flags(struct kmem_cache *s, slab_flags_t flags) +{ +#ifdef CONFIG_SLUB_DEBUG + VM_WARN_ON_ONCE(!(flags & SLAB_DEBUG_FLAGS)); + if (static_branch_unlikely(&slub_debug_enabled)) + return s->flags & flags; +#endif + return false; +} + #ifdef CONFIG_MEMCG_KMEM /* List of all root caches. */ @@ -504,6 +532,23 @@ static __always_inline void uncharge_slab_page(struct page *page, int order, memcg_uncharge_slab(page, order, s); } +static inline struct kmem_cache *cache_from_obj(struct kmem_cache *s, void *x) +{ + struct kmem_cache *cachep; + + if (!IS_ENABLED(CONFIG_SLAB_FREELIST_HARDENED) && + !memcg_kmem_enabled() && + !kmem_cache_debug_flags(s, SLAB_CONSISTENCY_CHECKS)) + return s; + + cachep = virt_to_cache(x); + if (WARN(cachep && !slab_equal_or_root(cachep, s), + "%s: Wrong slab cache. %s but object is from %s\n", + __func__, s->name, cachep->name)) + print_tracking(cachep, x); + return cachep; +} + static inline size_t slab_ksize(const struct kmem_cache *s) { #ifndef CONFIG_SLUB diff --git a/mm/slub.c b/mm/slub.c index 6a99a3c945cbe..629a58b56728a 100644 --- a/mm/slub.c +++ b/mm/slub.c @@ -122,21 +122,6 @@ DEFINE_STATIC_KEY_FALSE(slub_debug_enabled); #endif #endif -/* - * Returns true if any of the specified slub_debug flags is enabled for the - * cache. Use only for flags parsed by setup_slub_debug() as it also enables - * the static key. - */ -static inline bool kmem_cache_debug_flags(struct kmem_cache *s, slab_flags_t flags) -{ - VM_WARN_ON_ONCE(!(flags & SLAB_DEBUG_FLAGS)); -#ifdef CONFIG_SLUB_DEBUG - if (static_branch_unlikely(&slub_debug_enabled)) - return s->flags & flags; -#endif - return false; -} - static inline bool kmem_cache_debug(struct kmem_cache *s) { return kmem_cache_debug_flags(s, SLAB_DEBUG_FLAGS); @@ -653,7 +638,7 @@ static void print_track(const char *s, struct track *t, unsigned long pr_time) #endif } -static void print_tracking(struct kmem_cache *s, void *object) +void print_tracking(struct kmem_cache *s, void *object) { unsigned long pr_time = jiffies; if (!(s->flags & SLAB_STORE_USER)) @@ -1525,10 +1510,6 @@ static bool freelist_corrupted(struct kmem_cache *s, struct page *page, { return false; } - -static void print_tracking(struct kmem_cache *s, void *object) -{ -} #endif /* CONFIG_SLUB_DEBUG */ /* @@ -3175,23 +3156,6 @@ void ___cache_free(struct kmem_cache *cache, void *x, unsigned long addr) } #endif -static inline struct kmem_cache *cache_from_obj(struct kmem_cache *s, void *x) -{ - struct kmem_cache *cachep; - - if (!IS_ENABLED(CONFIG_SLAB_FREELIST_HARDENED) && - !memcg_kmem_enabled() && - !kmem_cache_debug_flags(s, SLAB_CONSISTENCY_CHECKS)) - return s; - - cachep = virt_to_cache(x); - if (WARN(cachep && !slab_equal_or_root(cachep, s), - "%s: Wrong slab cache. %s but object is from %s\n", - __func__, s->name, cachep->name)) - print_tracking(cachep, x); - return cachep; -} - void kmem_cache_free(struct kmem_cache *s, void *x) { s = cache_from_obj(s, x); -- GitLab From b3cb9fc3aeaf9d52967c87d54a98f035e96279dc Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Thu, 6 Aug 2020 23:19:09 -0700 Subject: [PATCH 1347/1476] mm/slub.c: drop lockdep_assert_held() from put_map() There is no point in using lockdep_assert_held() unlock that is about to be unlocked. It works only with lockdep and lockdep will complain if spin_unlock() is used on a lock that has not been locked. Remove superfluous lockdep_assert_held(). Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Andrew Morton Cc: Yu Zhao Cc: Christopher Lameter Cc: Pekka Enberg Cc: David Rientjes Cc: Joonsoo Kim Cc: Thomas Gleixner Link: http://lkml.kernel.org/r/20200618201234.795692-2-bigeasy@linutronix.de Signed-off-by: Linus Torvalds --- mm/slub.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/mm/slub.c b/mm/slub.c index 629a58b56728a..e5fc31c37b37a 100644 --- a/mm/slub.c +++ b/mm/slub.c @@ -473,8 +473,6 @@ static unsigned long *get_map(struct kmem_cache *s, struct page *page) static void put_map(unsigned long *map) __releases(&object_map_lock) { VM_BUG_ON(map != object_map); - lockdep_assert_held(&object_map_lock); - spin_unlock(&object_map_lock); } -- GitLab From cfbe1636c3585c1e032bfac512fb8be903fbc913 Mon Sep 17 00:00:00 2001 From: Marco Elver Date: Thu, 6 Aug 2020 23:19:12 -0700 Subject: [PATCH 1348/1476] mm, kcsan: instrument SLAB/SLUB free with "ASSERT_EXCLUSIVE_ACCESS" Provide the necessary KCSAN checks to assist with debugging racy use-after-frees. While KASAN is more reliable at generally catching such use-after-frees (due to its use of a quarantine), it can be difficult to debug racy use-after-frees. If a reliable reproducer exists, KCSAN can assist in debugging such issues. Note: ASSERT_EXCLUSIVE_ACCESS is a convenience wrapper if the size is simply sizeof(var). Instead, here we just use __kcsan_check_access() explicitly to pass the correct size. Signed-off-by: Marco Elver Signed-off-by: Andrew Morton Cc: Alexander Potapenko Cc: Andrey Konovalov Cc: Christoph Lameter Cc: Pekka Enberg Cc: David Rientjes Cc: Joonsoo Kim Link: http://lkml.kernel.org/r/20200623072653.114563-1-elver@google.com Signed-off-by: Linus Torvalds --- mm/slab.c | 5 +++++ mm/slub.c | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/mm/slab.c b/mm/slab.c index 390e249aaa8d3..fa31cbb76124b 100644 --- a/mm/slab.c +++ b/mm/slab.c @@ -3432,6 +3432,11 @@ static __always_inline void __cache_free(struct kmem_cache *cachep, void *objp, if (kasan_slab_free(cachep, objp, _RET_IP_)) return; + /* Use KCSAN to help debug racy use-after-free. */ + if (!(cachep->flags & SLAB_TYPESAFE_BY_RCU)) + __kcsan_check_access(objp, cachep->object_size, + KCSAN_ACCESS_WRITE | KCSAN_ACCESS_ASSERT); + ___cache_free(cachep, objp, caller); } diff --git a/mm/slub.c b/mm/slub.c index e5fc31c37b37a..ae39eb392396f 100644 --- a/mm/slub.c +++ b/mm/slub.c @@ -1549,6 +1549,11 @@ static __always_inline bool slab_free_hook(struct kmem_cache *s, void *x) if (!(s->flags & SLAB_DEBUG_OBJECTS)) debug_check_no_obj_freed(x, s->object_size); + /* Use KCSAN to help debug racy use-after-free. */ + if (!(s->flags & SLAB_TYPESAFE_BY_RCU)) + __kcsan_check_access(x, s->object_size, + KCSAN_ACCESS_WRITE | KCSAN_ACCESS_ASSERT); + /* KASAN might put x into memory quarantine, delaying its reuse */ return kasan_slab_free(s, x, _RET_IP_); } -- GitLab From 05289402d71736bd275902372e4b25664aeac6b8 Mon Sep 17 00:00:00 2001 From: Anshuman Khandual Date: Thu, 6 Aug 2020 23:19:16 -0700 Subject: [PATCH 1349/1476] mm/debug_vm_pgtable: add tests validating arch helpers for core MM features Patch series "mm/debug_vm_pgtable: Add some more tests", v5. This series adds some more arch page table helper validation tests which are related to core and advanced memory functions. This also creates a documentation, enlisting expected semantics for all page table helpers as suggested by Mike Rapoport previously (https://lkml.org/lkml/2020/1/30/40). There are many TRANSPARENT_HUGEPAGE and ARCH_HAS_TRANSPARENT_HUGEPAGE_PUD ifdefs scattered across the test. But consolidating all the fallback stubs is not very straight forward because ARCH_HAS_TRANSPARENT_HUGEPAGE_PUD is not explicitly dependent on ARCH_HAS_TRANSPARENT_HUGEPAGE. Tested on arm64, x86 platforms but only build tested on all other enabled platforms through ARCH_HAS_DEBUG_VM_PGTABLE i.e powerpc, arc, s390. The following failure on arm64 still exists which was mentioned previously. It will be fixed with the upcoming THP migration on arm64 enablement series. WARNING .... mm/debug_vm_pgtable.c:860 debug_vm_pgtable+0x940/0xa54 WARN_ON(!pmd_present(pmd_mkinvalid(pmd_mkhuge(pmd)))) This patch (of 4): This adds new tests validating arch page table helpers for these following core memory features. These tests create and test specific mapping types at various page table levels. 1. SPECIAL mapping 2. PROTNONE mapping 3. DEVMAP mapping 4. SOFTDIRTY mapping 5. SWAP mapping 6. MIGRATION mapping 7. HUGETLB mapping 8. THP mapping Suggested-by: Catalin Marinas Signed-off-by: Anshuman Khandual Signed-off-by: Andrew Morton Tested-by: Vineet Gupta [arc] Reviewed-by: Zi Yan Cc: Mike Rapoport Cc: Vineet Gupta Cc: Catalin Marinas Cc: Will Deacon Cc: Benjamin Herrenschmidt Cc: Paul Mackerras Cc: Michael Ellerman Cc: Heiko Carstens Cc: Vasily Gorbik Cc: Christian Borntraeger Cc: Thomas Gleixner Cc: Ingo Molnar Cc: Borislav Petkov Cc: "H. Peter Anvin" Cc: Kirill A. Shutemov Cc: Paul Walmsley Cc: Palmer Dabbelt Cc: Jonathan Corbet Cc: Mike Rapoport Cc: Steven Price Link: http://lkml.kernel.org/r/1594610587-4172-1-git-send-email-anshuman.khandual@arm.com Link: http://lkml.kernel.org/r/1593996516-7186-1-git-send-email-anshuman.khandual@arm.com Link: http://lkml.kernel.org/r/1593996516-7186-2-git-send-email-anshuman.khandual@arm.com Signed-off-by: Linus Torvalds --- mm/debug_vm_pgtable.c | 302 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 301 insertions(+), 1 deletion(-) diff --git a/mm/debug_vm_pgtable.c b/mm/debug_vm_pgtable.c index d315ff544f053..f772d97a3a9f0 100644 --- a/mm/debug_vm_pgtable.c +++ b/mm/debug_vm_pgtable.c @@ -282,6 +282,278 @@ static void __init pmd_populate_tests(struct mm_struct *mm, pmd_t *pmdp, WARN_ON(pmd_bad(pmd)); } +static void __init pte_special_tests(unsigned long pfn, pgprot_t prot) +{ + pte_t pte = pfn_pte(pfn, prot); + + if (!IS_ENABLED(CONFIG_ARCH_HAS_PTE_SPECIAL)) + return; + + WARN_ON(!pte_special(pte_mkspecial(pte))); +} + +static void __init pte_protnone_tests(unsigned long pfn, pgprot_t prot) +{ + pte_t pte = pfn_pte(pfn, prot); + + if (!IS_ENABLED(CONFIG_NUMA_BALANCING)) + return; + + WARN_ON(!pte_protnone(pte)); + WARN_ON(!pte_present(pte)); +} + +#ifdef CONFIG_TRANSPARENT_HUGEPAGE +static void __init pmd_protnone_tests(unsigned long pfn, pgprot_t prot) +{ + pmd_t pmd = pmd_mkhuge(pfn_pmd(pfn, prot)); + + if (!IS_ENABLED(CONFIG_NUMA_BALANCING)) + return; + + WARN_ON(!pmd_protnone(pmd)); + WARN_ON(!pmd_present(pmd)); +} +#else /* !CONFIG_TRANSPARENT_HUGEPAGE */ +static void __init pmd_protnone_tests(unsigned long pfn, pgprot_t prot) { } +#endif /* CONFIG_TRANSPARENT_HUGEPAGE */ + +#ifdef CONFIG_ARCH_HAS_PTE_DEVMAP +static void __init pte_devmap_tests(unsigned long pfn, pgprot_t prot) +{ + pte_t pte = pfn_pte(pfn, prot); + + WARN_ON(!pte_devmap(pte_mkdevmap(pte))); +} + +#ifdef CONFIG_TRANSPARENT_HUGEPAGE +static void __init pmd_devmap_tests(unsigned long pfn, pgprot_t prot) +{ + pmd_t pmd = pfn_pmd(pfn, prot); + + WARN_ON(!pmd_devmap(pmd_mkdevmap(pmd))); +} + +#ifdef CONFIG_HAVE_ARCH_TRANSPARENT_HUGEPAGE_PUD +static void __init pud_devmap_tests(unsigned long pfn, pgprot_t prot) +{ + pud_t pud = pfn_pud(pfn, prot); + + WARN_ON(!pud_devmap(pud_mkdevmap(pud))); +} +#else /* !CONFIG_HAVE_ARCH_TRANSPARENT_HUGEPAGE_PUD */ +static void __init pud_devmap_tests(unsigned long pfn, pgprot_t prot) { } +#endif /* CONFIG_HAVE_ARCH_TRANSPARENT_HUGEPAGE_PUD */ +#else /* CONFIG_TRANSPARENT_HUGEPAGE */ +static void __init pmd_devmap_tests(unsigned long pfn, pgprot_t prot) { } +static void __init pud_devmap_tests(unsigned long pfn, pgprot_t prot) { } +#endif /* CONFIG_TRANSPARENT_HUGEPAGE */ +#else +static void __init pte_devmap_tests(unsigned long pfn, pgprot_t prot) { } +static void __init pmd_devmap_tests(unsigned long pfn, pgprot_t prot) { } +static void __init pud_devmap_tests(unsigned long pfn, pgprot_t prot) { } +#endif /* CONFIG_ARCH_HAS_PTE_DEVMAP */ + +static void __init pte_soft_dirty_tests(unsigned long pfn, pgprot_t prot) +{ + pte_t pte = pfn_pte(pfn, prot); + + if (!IS_ENABLED(CONFIG_MEM_SOFT_DIRTY)) + return; + + WARN_ON(!pte_soft_dirty(pte_mksoft_dirty(pte))); + WARN_ON(pte_soft_dirty(pte_clear_soft_dirty(pte))); +} + +static void __init pte_swap_soft_dirty_tests(unsigned long pfn, pgprot_t prot) +{ + pte_t pte = pfn_pte(pfn, prot); + + if (!IS_ENABLED(CONFIG_MEM_SOFT_DIRTY)) + return; + + WARN_ON(!pte_swp_soft_dirty(pte_swp_mksoft_dirty(pte))); + WARN_ON(pte_swp_soft_dirty(pte_swp_clear_soft_dirty(pte))); +} + +#ifdef CONFIG_TRANSPARENT_HUGEPAGE +static void __init pmd_soft_dirty_tests(unsigned long pfn, pgprot_t prot) +{ + pmd_t pmd = pfn_pmd(pfn, prot); + + if (!IS_ENABLED(CONFIG_MEM_SOFT_DIRTY)) + return; + + WARN_ON(!pmd_soft_dirty(pmd_mksoft_dirty(pmd))); + WARN_ON(pmd_soft_dirty(pmd_clear_soft_dirty(pmd))); +} + +static void __init pmd_swap_soft_dirty_tests(unsigned long pfn, pgprot_t prot) +{ + pmd_t pmd = pfn_pmd(pfn, prot); + + if (!IS_ENABLED(CONFIG_MEM_SOFT_DIRTY) || + !IS_ENABLED(CONFIG_ARCH_ENABLE_THP_MIGRATION)) + return; + + WARN_ON(!pmd_swp_soft_dirty(pmd_swp_mksoft_dirty(pmd))); + WARN_ON(pmd_swp_soft_dirty(pmd_swp_clear_soft_dirty(pmd))); +} +#else /* !CONFIG_ARCH_HAS_PTE_DEVMAP */ +static void __init pmd_soft_dirty_tests(unsigned long pfn, pgprot_t prot) { } +static void __init pmd_swap_soft_dirty_tests(unsigned long pfn, pgprot_t prot) +{ +} +#endif /* CONFIG_ARCH_HAS_PTE_DEVMAP */ + +static void __init pte_swap_tests(unsigned long pfn, pgprot_t prot) +{ + swp_entry_t swp; + pte_t pte; + + pte = pfn_pte(pfn, prot); + swp = __pte_to_swp_entry(pte); + pte = __swp_entry_to_pte(swp); + WARN_ON(pfn != pte_pfn(pte)); +} + +#ifdef CONFIG_ARCH_ENABLE_THP_MIGRATION +static void __init pmd_swap_tests(unsigned long pfn, pgprot_t prot) +{ + swp_entry_t swp; + pmd_t pmd; + + pmd = pfn_pmd(pfn, prot); + swp = __pmd_to_swp_entry(pmd); + pmd = __swp_entry_to_pmd(swp); + WARN_ON(pfn != pmd_pfn(pmd)); +} +#else /* !CONFIG_ARCH_ENABLE_THP_MIGRATION */ +static void __init pmd_swap_tests(unsigned long pfn, pgprot_t prot) { } +#endif /* CONFIG_ARCH_ENABLE_THP_MIGRATION */ + +static void __init swap_migration_tests(void) +{ + struct page *page; + swp_entry_t swp; + + if (!IS_ENABLED(CONFIG_MIGRATION)) + return; + /* + * swap_migration_tests() requires a dedicated page as it needs to + * be locked before creating a migration entry from it. Locking the + * page that actually maps kernel text ('start_kernel') can be real + * problematic. Lets allocate a dedicated page explicitly for this + * purpose that will be freed subsequently. + */ + page = alloc_page(GFP_KERNEL); + if (!page) { + pr_err("page allocation failed\n"); + return; + } + + /* + * make_migration_entry() expects given page to be + * locked, otherwise it stumbles upon a BUG_ON(). + */ + __SetPageLocked(page); + swp = make_migration_entry(page, 1); + WARN_ON(!is_migration_entry(swp)); + WARN_ON(!is_write_migration_entry(swp)); + + make_migration_entry_read(&swp); + WARN_ON(!is_migration_entry(swp)); + WARN_ON(is_write_migration_entry(swp)); + + swp = make_migration_entry(page, 0); + WARN_ON(!is_migration_entry(swp)); + WARN_ON(is_write_migration_entry(swp)); + __ClearPageLocked(page); + __free_page(page); +} + +#ifdef CONFIG_HUGETLB_PAGE +static void __init hugetlb_basic_tests(unsigned long pfn, pgprot_t prot) +{ + struct page *page; + pte_t pte; + + /* + * Accessing the page associated with the pfn is safe here, + * as it was previously derived from a real kernel symbol. + */ + page = pfn_to_page(pfn); + pte = mk_huge_pte(page, prot); + + WARN_ON(!huge_pte_dirty(huge_pte_mkdirty(pte))); + WARN_ON(!huge_pte_write(huge_pte_mkwrite(huge_pte_wrprotect(pte)))); + WARN_ON(huge_pte_write(huge_pte_wrprotect(huge_pte_mkwrite(pte)))); + +#ifdef CONFIG_ARCH_WANT_GENERAL_HUGETLB + pte = pfn_pte(pfn, prot); + + WARN_ON(!pte_huge(pte_mkhuge(pte))); +#endif /* CONFIG_ARCH_WANT_GENERAL_HUGETLB */ +} +#else /* !CONFIG_HUGETLB_PAGE */ +static void __init hugetlb_basic_tests(unsigned long pfn, pgprot_t prot) { } +#endif /* CONFIG_HUGETLB_PAGE */ + +#ifdef CONFIG_TRANSPARENT_HUGEPAGE +static void __init pmd_thp_tests(unsigned long pfn, pgprot_t prot) +{ + pmd_t pmd; + + if (!has_transparent_hugepage()) + return; + + /* + * pmd_trans_huge() and pmd_present() must return positive after + * MMU invalidation with pmd_mkinvalid(). This behavior is an + * optimization for transparent huge page. pmd_trans_huge() must + * be true if pmd_page() returns a valid THP to avoid taking the + * pmd_lock when others walk over non transhuge pmds (i.e. there + * are no THP allocated). Especially when splitting a THP and + * removing the present bit from the pmd, pmd_trans_huge() still + * needs to return true. pmd_present() should be true whenever + * pmd_trans_huge() returns true. + */ + pmd = pfn_pmd(pfn, prot); + WARN_ON(!pmd_trans_huge(pmd_mkhuge(pmd))); + +#ifndef __HAVE_ARCH_PMDP_INVALIDATE + WARN_ON(!pmd_trans_huge(pmd_mkinvalid(pmd_mkhuge(pmd)))); + WARN_ON(!pmd_present(pmd_mkinvalid(pmd_mkhuge(pmd)))); +#endif /* __HAVE_ARCH_PMDP_INVALIDATE */ +} + +#ifdef CONFIG_HAVE_ARCH_TRANSPARENT_HUGEPAGE_PUD +static void __init pud_thp_tests(unsigned long pfn, pgprot_t prot) +{ + pud_t pud; + + if (!has_transparent_hugepage()) + return; + + pud = pfn_pud(pfn, prot); + WARN_ON(!pud_trans_huge(pud_mkhuge(pud))); + + /* + * pud_mkinvalid() has been dropped for now. Enable back + * these tests when it comes back with a modified pud_present(). + * + * WARN_ON(!pud_trans_huge(pud_mkinvalid(pud_mkhuge(pud)))); + * WARN_ON(!pud_present(pud_mkinvalid(pud_mkhuge(pud)))); + */ +} +#else /* !CONFIG_HAVE_ARCH_TRANSPARENT_HUGEPAGE_PUD */ +static void __init pud_thp_tests(unsigned long pfn, pgprot_t prot) { } +#endif /* CONFIG_HAVE_ARCH_TRANSPARENT_HUGEPAGE_PUD */ +#else /* !CONFIG_TRANSPARENT_HUGEPAGE */ +static void __init pmd_thp_tests(unsigned long pfn, pgprot_t prot) { } +static void __init pud_thp_tests(unsigned long pfn, pgprot_t prot) { } +#endif /* CONFIG_TRANSPARENT_HUGEPAGE */ + static unsigned long __init get_random_vaddr(void) { unsigned long random_vaddr, random_pages, total_user_pages; @@ -303,7 +575,7 @@ static int __init debug_vm_pgtable(void) pmd_t *pmdp, *saved_pmdp, pmd; pte_t *ptep; pgtable_t saved_ptep; - pgprot_t prot; + pgprot_t prot, protnone; phys_addr_t paddr; unsigned long vaddr, pte_aligned, pmd_aligned; unsigned long pud_aligned, p4d_aligned, pgd_aligned; @@ -318,6 +590,12 @@ static int __init debug_vm_pgtable(void) return 1; } + /* + * __P000 (or even __S000) will help create page table entries with + * PROT_NONE permission as required for pxx_protnone_tests(). + */ + protnone = __P000; + /* * PFN for mapping at PTE level is determined from a standard kernel * text symbol. But pfns for higher page table levels are derived by @@ -373,6 +651,28 @@ static int __init debug_vm_pgtable(void) p4d_populate_tests(mm, p4dp, saved_pudp); pgd_populate_tests(mm, pgdp, saved_p4dp); + pte_special_tests(pte_aligned, prot); + pte_protnone_tests(pte_aligned, protnone); + pmd_protnone_tests(pmd_aligned, protnone); + + pte_devmap_tests(pte_aligned, prot); + pmd_devmap_tests(pmd_aligned, prot); + pud_devmap_tests(pud_aligned, prot); + + pte_soft_dirty_tests(pte_aligned, prot); + pmd_soft_dirty_tests(pmd_aligned, prot); + pte_swap_soft_dirty_tests(pte_aligned, prot); + pmd_swap_soft_dirty_tests(pmd_aligned, prot); + + pte_swap_tests(pte_aligned, prot); + pmd_swap_tests(pmd_aligned, prot); + + swap_migration_tests(); + hugetlb_basic_tests(pte_aligned, prot); + + pmd_thp_tests(pmd_aligned, prot); + pud_thp_tests(pud_aligned, prot); + p4d_free(mm, saved_p4dp); pud_free(mm, saved_pudp); pmd_free(mm, saved_pmdp); -- GitLab From a5c3b9ffb0f404ba90622ea4a25c06035dd01249 Mon Sep 17 00:00:00 2001 From: Anshuman Khandual Date: Thu, 6 Aug 2020 23:19:20 -0700 Subject: [PATCH 1350/1476] mm/debug_vm_pgtable: add tests validating advanced arch page table helpers This adds new tests validating for these following arch advanced page table helpers. These tests create and test specific mapping types at various page table levels. 1. pxxp_set_wrprotect() 2. pxxp_get_and_clear() 3. pxxp_set_access_flags() 4. pxxp_get_and_clear_full() 5. pxxp_test_and_clear_young() 6. pxx_leaf() 7. pxx_set_huge() 8. pxx_(clear|mk)_savedwrite() 9. huge_pxxp_xxx() [anshuman.khandual@arm.com: drop RANDOM_ORVALUE from hugetlb_advanced_tests()] Link: http://lkml.kernel.org/r/1594610587-4172-3-git-send-email-anshuman.khandual@arm.com Suggested-by: Catalin Marinas Signed-off-by: Anshuman Khandual Signed-off-by: Andrew Morton Tested-by: Vineet Gupta [arc] Reviewed-by: Zi Yan Cc: Mike Rapoport Cc: Vineet Gupta Cc: Catalin Marinas Cc: Will Deacon Cc: Benjamin Herrenschmidt Cc: Paul Mackerras Cc: Michael Ellerman Cc: Heiko Carstens Cc: Vasily Gorbik Cc: Christian Borntraeger Cc: Thomas Gleixner Cc: Ingo Molnar Cc: Borislav Petkov Cc: "H. Peter Anvin" Cc: Kirill A. Shutemov Cc: Paul Walmsley Cc: Palmer Dabbelt Cc: Jonathan Corbet Cc: Mike Rapoport Cc: Steven Price Link: http://lkml.kernel.org/r/1593996516-7186-3-git-send-email-anshuman.khandual@arm.com Signed-off-by: Linus Torvalds --- mm/debug_vm_pgtable.c | 312 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 312 insertions(+) diff --git a/mm/debug_vm_pgtable.c b/mm/debug_vm_pgtable.c index f772d97a3a9f0..2ef347f8ebb55 100644 --- a/mm/debug_vm_pgtable.c +++ b/mm/debug_vm_pgtable.c @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include @@ -28,6 +29,7 @@ #include #include #include +#include #define VMFLAGS (VM_READ|VM_WRITE|VM_EXEC) @@ -55,6 +57,55 @@ static void __init pte_basic_tests(unsigned long pfn, pgprot_t prot) WARN_ON(pte_write(pte_wrprotect(pte_mkwrite(pte)))); } +static void __init pte_advanced_tests(struct mm_struct *mm, + struct vm_area_struct *vma, pte_t *ptep, + unsigned long pfn, unsigned long vaddr, + pgprot_t prot) +{ + pte_t pte = pfn_pte(pfn, prot); + + pte = pfn_pte(pfn, prot); + set_pte_at(mm, vaddr, ptep, pte); + ptep_set_wrprotect(mm, vaddr, ptep); + pte = ptep_get(ptep); + WARN_ON(pte_write(pte)); + + pte = pfn_pte(pfn, prot); + set_pte_at(mm, vaddr, ptep, pte); + ptep_get_and_clear(mm, vaddr, ptep); + pte = ptep_get(ptep); + WARN_ON(!pte_none(pte)); + + pte = pfn_pte(pfn, prot); + pte = pte_wrprotect(pte); + pte = pte_mkclean(pte); + set_pte_at(mm, vaddr, ptep, pte); + pte = pte_mkwrite(pte); + pte = pte_mkdirty(pte); + ptep_set_access_flags(vma, vaddr, ptep, pte, 1); + pte = ptep_get(ptep); + WARN_ON(!(pte_write(pte) && pte_dirty(pte))); + + pte = pfn_pte(pfn, prot); + set_pte_at(mm, vaddr, ptep, pte); + ptep_get_and_clear_full(mm, vaddr, ptep, 1); + pte = ptep_get(ptep); + WARN_ON(!pte_none(pte)); + + pte = pte_mkyoung(pte); + set_pte_at(mm, vaddr, ptep, pte); + ptep_test_and_clear_young(vma, vaddr, ptep); + pte = ptep_get(ptep); + WARN_ON(pte_young(pte)); +} + +static void __init pte_savedwrite_tests(unsigned long pfn, pgprot_t prot) +{ + pte_t pte = pfn_pte(pfn, prot); + + WARN_ON(!pte_savedwrite(pte_mk_savedwrite(pte_clear_savedwrite(pte)))); + WARN_ON(pte_savedwrite(pte_clear_savedwrite(pte_mk_savedwrite(pte)))); +} #ifdef CONFIG_TRANSPARENT_HUGEPAGE static void __init pmd_basic_tests(unsigned long pfn, pgprot_t prot) { @@ -77,6 +128,90 @@ static void __init pmd_basic_tests(unsigned long pfn, pgprot_t prot) WARN_ON(!pmd_bad(pmd_mkhuge(pmd))); } +static void __init pmd_advanced_tests(struct mm_struct *mm, + struct vm_area_struct *vma, pmd_t *pmdp, + unsigned long pfn, unsigned long vaddr, + pgprot_t prot) +{ + pmd_t pmd = pfn_pmd(pfn, prot); + + if (!has_transparent_hugepage()) + return; + + /* Align the address wrt HPAGE_PMD_SIZE */ + vaddr = (vaddr & HPAGE_PMD_MASK) + HPAGE_PMD_SIZE; + + pmd = pfn_pmd(pfn, prot); + set_pmd_at(mm, vaddr, pmdp, pmd); + pmdp_set_wrprotect(mm, vaddr, pmdp); + pmd = READ_ONCE(*pmdp); + WARN_ON(pmd_write(pmd)); + + pmd = pfn_pmd(pfn, prot); + set_pmd_at(mm, vaddr, pmdp, pmd); + pmdp_huge_get_and_clear(mm, vaddr, pmdp); + pmd = READ_ONCE(*pmdp); + WARN_ON(!pmd_none(pmd)); + + pmd = pfn_pmd(pfn, prot); + pmd = pmd_wrprotect(pmd); + pmd = pmd_mkclean(pmd); + set_pmd_at(mm, vaddr, pmdp, pmd); + pmd = pmd_mkwrite(pmd); + pmd = pmd_mkdirty(pmd); + pmdp_set_access_flags(vma, vaddr, pmdp, pmd, 1); + pmd = READ_ONCE(*pmdp); + WARN_ON(!(pmd_write(pmd) && pmd_dirty(pmd))); + + pmd = pmd_mkhuge(pfn_pmd(pfn, prot)); + set_pmd_at(mm, vaddr, pmdp, pmd); + pmdp_huge_get_and_clear_full(vma, vaddr, pmdp, 1); + pmd = READ_ONCE(*pmdp); + WARN_ON(!pmd_none(pmd)); + + pmd = pmd_mkyoung(pmd); + set_pmd_at(mm, vaddr, pmdp, pmd); + pmdp_test_and_clear_young(vma, vaddr, pmdp); + pmd = READ_ONCE(*pmdp); + WARN_ON(pmd_young(pmd)); +} + +static void __init pmd_leaf_tests(unsigned long pfn, pgprot_t prot) +{ + pmd_t pmd = pfn_pmd(pfn, prot); + + /* + * PMD based THP is a leaf entry. + */ + pmd = pmd_mkhuge(pmd); + WARN_ON(!pmd_leaf(pmd)); +} + +static void __init pmd_huge_tests(pmd_t *pmdp, unsigned long pfn, pgprot_t prot) +{ + pmd_t pmd; + + if (!IS_ENABLED(CONFIG_HAVE_ARCH_HUGE_VMAP)) + return; + /* + * X86 defined pmd_set_huge() verifies that the given + * PMD is not a populated non-leaf entry. + */ + WRITE_ONCE(*pmdp, __pmd(0)); + WARN_ON(!pmd_set_huge(pmdp, __pfn_to_phys(pfn), prot)); + WARN_ON(!pmd_clear_huge(pmdp)); + pmd = READ_ONCE(*pmdp); + WARN_ON(!pmd_none(pmd)); +} + +static void __init pmd_savedwrite_tests(unsigned long pfn, pgprot_t prot) +{ + pmd_t pmd = pfn_pmd(pfn, prot); + + WARN_ON(!pmd_savedwrite(pmd_mk_savedwrite(pmd_clear_savedwrite(pmd)))); + WARN_ON(pmd_savedwrite(pmd_clear_savedwrite(pmd_mk_savedwrite(pmd)))); +} + #ifdef CONFIG_HAVE_ARCH_TRANSPARENT_HUGEPAGE_PUD static void __init pud_basic_tests(unsigned long pfn, pgprot_t prot) { @@ -100,12 +235,119 @@ static void __init pud_basic_tests(unsigned long pfn, pgprot_t prot) */ WARN_ON(!pud_bad(pud_mkhuge(pud))); } + +static void __init pud_advanced_tests(struct mm_struct *mm, + struct vm_area_struct *vma, pud_t *pudp, + unsigned long pfn, unsigned long vaddr, + pgprot_t prot) +{ + pud_t pud = pfn_pud(pfn, prot); + + if (!has_transparent_hugepage()) + return; + + /* Align the address wrt HPAGE_PUD_SIZE */ + vaddr = (vaddr & HPAGE_PUD_MASK) + HPAGE_PUD_SIZE; + + set_pud_at(mm, vaddr, pudp, pud); + pudp_set_wrprotect(mm, vaddr, pudp); + pud = READ_ONCE(*pudp); + WARN_ON(pud_write(pud)); + +#ifndef __PAGETABLE_PMD_FOLDED + pud = pfn_pud(pfn, prot); + set_pud_at(mm, vaddr, pudp, pud); + pudp_huge_get_and_clear(mm, vaddr, pudp); + pud = READ_ONCE(*pudp); + WARN_ON(!pud_none(pud)); + + pud = pfn_pud(pfn, prot); + set_pud_at(mm, vaddr, pudp, pud); + pudp_huge_get_and_clear_full(mm, vaddr, pudp, 1); + pud = READ_ONCE(*pudp); + WARN_ON(!pud_none(pud)); +#endif /* __PAGETABLE_PMD_FOLDED */ + pud = pfn_pud(pfn, prot); + pud = pud_wrprotect(pud); + pud = pud_mkclean(pud); + set_pud_at(mm, vaddr, pudp, pud); + pud = pud_mkwrite(pud); + pud = pud_mkdirty(pud); + pudp_set_access_flags(vma, vaddr, pudp, pud, 1); + pud = READ_ONCE(*pudp); + WARN_ON(!(pud_write(pud) && pud_dirty(pud))); + + pud = pud_mkyoung(pud); + set_pud_at(mm, vaddr, pudp, pud); + pudp_test_and_clear_young(vma, vaddr, pudp); + pud = READ_ONCE(*pudp); + WARN_ON(pud_young(pud)); +} + +static void __init pud_leaf_tests(unsigned long pfn, pgprot_t prot) +{ + pud_t pud = pfn_pud(pfn, prot); + + /* + * PUD based THP is a leaf entry. + */ + pud = pud_mkhuge(pud); + WARN_ON(!pud_leaf(pud)); +} + +static void __init pud_huge_tests(pud_t *pudp, unsigned long pfn, pgprot_t prot) +{ + pud_t pud; + + if (!IS_ENABLED(CONFIG_HAVE_ARCH_HUGE_VMAP)) + return; + /* + * X86 defined pud_set_huge() verifies that the given + * PUD is not a populated non-leaf entry. + */ + WRITE_ONCE(*pudp, __pud(0)); + WARN_ON(!pud_set_huge(pudp, __pfn_to_phys(pfn), prot)); + WARN_ON(!pud_clear_huge(pudp)); + pud = READ_ONCE(*pudp); + WARN_ON(!pud_none(pud)); +} #else /* !CONFIG_HAVE_ARCH_TRANSPARENT_HUGEPAGE_PUD */ static void __init pud_basic_tests(unsigned long pfn, pgprot_t prot) { } +static void __init pud_advanced_tests(struct mm_struct *mm, + struct vm_area_struct *vma, pud_t *pudp, + unsigned long pfn, unsigned long vaddr, + pgprot_t prot) +{ +} +static void __init pud_leaf_tests(unsigned long pfn, pgprot_t prot) { } +static void __init pud_huge_tests(pud_t *pudp, unsigned long pfn, pgprot_t prot) +{ +} #endif /* CONFIG_HAVE_ARCH_TRANSPARENT_HUGEPAGE_PUD */ #else /* !CONFIG_TRANSPARENT_HUGEPAGE */ static void __init pmd_basic_tests(unsigned long pfn, pgprot_t prot) { } static void __init pud_basic_tests(unsigned long pfn, pgprot_t prot) { } +static void __init pmd_advanced_tests(struct mm_struct *mm, + struct vm_area_struct *vma, pmd_t *pmdp, + unsigned long pfn, unsigned long vaddr, + pgprot_t prot) +{ +} +static void __init pud_advanced_tests(struct mm_struct *mm, + struct vm_area_struct *vma, pud_t *pudp, + unsigned long pfn, unsigned long vaddr, + pgprot_t prot) +{ +} +static void __init pmd_leaf_tests(unsigned long pfn, pgprot_t prot) { } +static void __init pud_leaf_tests(unsigned long pfn, pgprot_t prot) { } +static void __init pmd_huge_tests(pmd_t *pmdp, unsigned long pfn, pgprot_t prot) +{ +} +static void __init pud_huge_tests(pud_t *pudp, unsigned long pfn, pgprot_t prot) +{ +} +static void __init pmd_savedwrite_tests(unsigned long pfn, pgprot_t prot) { } #endif /* CONFIG_TRANSPARENT_HUGEPAGE */ static void __init p4d_basic_tests(unsigned long pfn, pgprot_t prot) @@ -495,8 +737,56 @@ static void __init hugetlb_basic_tests(unsigned long pfn, pgprot_t prot) WARN_ON(!pte_huge(pte_mkhuge(pte))); #endif /* CONFIG_ARCH_WANT_GENERAL_HUGETLB */ } + +static void __init hugetlb_advanced_tests(struct mm_struct *mm, + struct vm_area_struct *vma, + pte_t *ptep, unsigned long pfn, + unsigned long vaddr, pgprot_t prot) +{ + struct page *page = pfn_to_page(pfn); + pte_t pte = ptep_get(ptep); + unsigned long paddr = __pfn_to_phys(pfn) & PMD_MASK; + + pte = pte_mkhuge(mk_pte(pfn_to_page(PHYS_PFN(paddr)), prot)); + set_huge_pte_at(mm, vaddr, ptep, pte); + barrier(); + WARN_ON(!pte_same(pte, huge_ptep_get(ptep))); + huge_pte_clear(mm, vaddr, ptep, PMD_SIZE); + pte = huge_ptep_get(ptep); + WARN_ON(!huge_pte_none(pte)); + + pte = mk_huge_pte(page, prot); + set_huge_pte_at(mm, vaddr, ptep, pte); + barrier(); + huge_ptep_set_wrprotect(mm, vaddr, ptep); + pte = huge_ptep_get(ptep); + WARN_ON(huge_pte_write(pte)); + + pte = mk_huge_pte(page, prot); + set_huge_pte_at(mm, vaddr, ptep, pte); + barrier(); + huge_ptep_get_and_clear(mm, vaddr, ptep); + pte = huge_ptep_get(ptep); + WARN_ON(!huge_pte_none(pte)); + + pte = mk_huge_pte(page, prot); + pte = huge_pte_wrprotect(pte); + set_huge_pte_at(mm, vaddr, ptep, pte); + barrier(); + pte = huge_pte_mkwrite(pte); + pte = huge_pte_mkdirty(pte); + huge_ptep_set_access_flags(vma, vaddr, ptep, pte, 1); + pte = huge_ptep_get(ptep); + WARN_ON(!(huge_pte_write(pte) && huge_pte_dirty(pte))); +} #else /* !CONFIG_HUGETLB_PAGE */ static void __init hugetlb_basic_tests(unsigned long pfn, pgprot_t prot) { } +static void __init hugetlb_advanced_tests(struct mm_struct *mm, + struct vm_area_struct *vma, + pte_t *ptep, unsigned long pfn, + unsigned long vaddr, pgprot_t prot) +{ +} #endif /* CONFIG_HUGETLB_PAGE */ #ifdef CONFIG_TRANSPARENT_HUGEPAGE @@ -568,6 +858,7 @@ static unsigned long __init get_random_vaddr(void) static int __init debug_vm_pgtable(void) { + struct vm_area_struct *vma; struct mm_struct *mm; pgd_t *pgdp; p4d_t *p4dp, *saved_p4dp; @@ -596,6 +887,12 @@ static int __init debug_vm_pgtable(void) */ protnone = __P000; + vma = vm_area_alloc(mm); + if (!vma) { + pr_err("vma allocation failed\n"); + return 1; + } + /* * PFN for mapping at PTE level is determined from a standard kernel * text symbol. But pfns for higher page table levels are derived by @@ -644,6 +941,20 @@ static int __init debug_vm_pgtable(void) p4d_clear_tests(mm, p4dp); pgd_clear_tests(mm, pgdp); + pte_advanced_tests(mm, vma, ptep, pte_aligned, vaddr, prot); + pmd_advanced_tests(mm, vma, pmdp, pmd_aligned, vaddr, prot); + pud_advanced_tests(mm, vma, pudp, pud_aligned, vaddr, prot); + hugetlb_advanced_tests(mm, vma, ptep, pte_aligned, vaddr, prot); + + pmd_leaf_tests(pmd_aligned, prot); + pud_leaf_tests(pud_aligned, prot); + + pmd_huge_tests(pmdp, pmd_aligned, prot); + pud_huge_tests(pudp, pud_aligned, prot); + + pte_savedwrite_tests(pte_aligned, prot); + pmd_savedwrite_tests(pmd_aligned, prot); + pte_unmap_unlock(ptep, ptl); pmd_populate_tests(mm, pmdp, saved_ptep); @@ -678,6 +989,7 @@ static int __init debug_vm_pgtable(void) pmd_free(mm, saved_pmdp); pte_free(mm, saved_ptep); + vm_area_free(vma); mm_dec_nr_puds(mm); mm_dec_nr_pmds(mm); mm_dec_nr_ptes(mm); -- GitLab From 6315df41afccf12a468ba0b17188d60b26009bc2 Mon Sep 17 00:00:00 2001 From: Anshuman Khandual Date: Thu, 6 Aug 2020 23:19:25 -0700 Subject: [PATCH 1351/1476] mm/debug_vm_pgtable: add debug prints for individual tests This adds debug print information that enlists all tests getting executed on a given platform. With dynamic debug enabled, the following information will be splashed during boot. For compactness purpose, dropped both time stamp and prefix (i.e debug_vm_pgtable) from this sample output. [debug_vm_pgtable ]: Validating architecture page table helpers [pte_basic_tests ]: Validating PTE basic [pmd_basic_tests ]: Validating PMD basic [p4d_basic_tests ]: Validating P4D basic [pgd_basic_tests ]: Validating PGD basic [pte_clear_tests ]: Validating PTE clear [pmd_clear_tests ]: Validating PMD clear [pte_advanced_tests ]: Validating PTE advanced [pmd_advanced_tests ]: Validating PMD advanced [hugetlb_advanced_tests]: Validating HugeTLB advanced [pmd_leaf_tests ]: Validating PMD leaf [pmd_huge_tests ]: Validating PMD huge [pte_savedwrite_tests ]: Validating PTE saved write [pmd_savedwrite_tests ]: Validating PMD saved write [pmd_populate_tests ]: Validating PMD populate [pte_special_tests ]: Validating PTE special [pte_protnone_tests ]: Validating PTE protnone [pmd_protnone_tests ]: Validating PMD protnone [pte_devmap_tests ]: Validating PTE devmap [pmd_devmap_tests ]: Validating PMD devmap [pte_swap_tests ]: Validating PTE swap [swap_migration_tests ]: Validating swap migration [hugetlb_basic_tests ]: Validating HugeTLB basic [pmd_thp_tests ]: Validating PMD based THP Signed-off-by: Anshuman Khandual Signed-off-by: Andrew Morton Tested-by: Vineet Gupta [arc] Reviewed-by: Zi Yan Cc: Catalin Marinas Cc: Mike Rapoport Cc: Vineet Gupta Cc: Will Deacon Cc: Benjamin Herrenschmidt Cc: Paul Mackerras Cc: Michael Ellerman Cc: Heiko Carstens Cc: Vasily Gorbik Cc: Christian Borntraeger Cc: Thomas Gleixner Cc: Ingo Molnar Cc: Borislav Petkov Cc: "H. Peter Anvin" Cc: Kirill A. Shutemov Cc: Paul Walmsley Cc: Palmer Dabbelt Cc: Jonathan Corbet Cc: Mike Rapoport Link: http://lkml.kernel.org/r/1593996516-7186-4-git-send-email-anshuman.khandual@arm.com Signed-off-by: Linus Torvalds --- mm/debug_vm_pgtable.c | 46 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 45 insertions(+), 1 deletion(-) diff --git a/mm/debug_vm_pgtable.c b/mm/debug_vm_pgtable.c index 2ef347f8ebb55..8d389d071968e 100644 --- a/mm/debug_vm_pgtable.c +++ b/mm/debug_vm_pgtable.c @@ -8,7 +8,7 @@ * * Author: Anshuman Khandual */ -#define pr_fmt(fmt) "debug_vm_pgtable: %s: " fmt, __func__ +#define pr_fmt(fmt) "debug_vm_pgtable: [%-25s]: " fmt, __func__ #include #include @@ -48,6 +48,7 @@ static void __init pte_basic_tests(unsigned long pfn, pgprot_t prot) { pte_t pte = pfn_pte(pfn, prot); + pr_debug("Validating PTE basic\n"); WARN_ON(!pte_same(pte, pte)); WARN_ON(!pte_young(pte_mkyoung(pte_mkold(pte)))); WARN_ON(!pte_dirty(pte_mkdirty(pte_mkclean(pte)))); @@ -64,6 +65,7 @@ static void __init pte_advanced_tests(struct mm_struct *mm, { pte_t pte = pfn_pte(pfn, prot); + pr_debug("Validating PTE advanced\n"); pte = pfn_pte(pfn, prot); set_pte_at(mm, vaddr, ptep, pte); ptep_set_wrprotect(mm, vaddr, ptep); @@ -103,6 +105,7 @@ static void __init pte_savedwrite_tests(unsigned long pfn, pgprot_t prot) { pte_t pte = pfn_pte(pfn, prot); + pr_debug("Validating PTE saved write\n"); WARN_ON(!pte_savedwrite(pte_mk_savedwrite(pte_clear_savedwrite(pte)))); WARN_ON(pte_savedwrite(pte_clear_savedwrite(pte_mk_savedwrite(pte)))); } @@ -114,6 +117,7 @@ static void __init pmd_basic_tests(unsigned long pfn, pgprot_t prot) if (!has_transparent_hugepage()) return; + pr_debug("Validating PMD basic\n"); WARN_ON(!pmd_same(pmd, pmd)); WARN_ON(!pmd_young(pmd_mkyoung(pmd_mkold(pmd)))); WARN_ON(!pmd_dirty(pmd_mkdirty(pmd_mkclean(pmd)))); @@ -138,6 +142,7 @@ static void __init pmd_advanced_tests(struct mm_struct *mm, if (!has_transparent_hugepage()) return; + pr_debug("Validating PMD advanced\n"); /* Align the address wrt HPAGE_PMD_SIZE */ vaddr = (vaddr & HPAGE_PMD_MASK) + HPAGE_PMD_SIZE; @@ -180,6 +185,7 @@ static void __init pmd_leaf_tests(unsigned long pfn, pgprot_t prot) { pmd_t pmd = pfn_pmd(pfn, prot); + pr_debug("Validating PMD leaf\n"); /* * PMD based THP is a leaf entry. */ @@ -193,6 +199,8 @@ static void __init pmd_huge_tests(pmd_t *pmdp, unsigned long pfn, pgprot_t prot) if (!IS_ENABLED(CONFIG_HAVE_ARCH_HUGE_VMAP)) return; + + pr_debug("Validating PMD huge\n"); /* * X86 defined pmd_set_huge() verifies that the given * PMD is not a populated non-leaf entry. @@ -208,6 +216,7 @@ static void __init pmd_savedwrite_tests(unsigned long pfn, pgprot_t prot) { pmd_t pmd = pfn_pmd(pfn, prot); + pr_debug("Validating PMD saved write\n"); WARN_ON(!pmd_savedwrite(pmd_mk_savedwrite(pmd_clear_savedwrite(pmd)))); WARN_ON(pmd_savedwrite(pmd_clear_savedwrite(pmd_mk_savedwrite(pmd)))); } @@ -220,6 +229,7 @@ static void __init pud_basic_tests(unsigned long pfn, pgprot_t prot) if (!has_transparent_hugepage()) return; + pr_debug("Validating PUD basic\n"); WARN_ON(!pud_same(pud, pud)); WARN_ON(!pud_young(pud_mkyoung(pud_mkold(pud)))); WARN_ON(!pud_write(pud_mkwrite(pud_wrprotect(pud)))); @@ -246,6 +256,7 @@ static void __init pud_advanced_tests(struct mm_struct *mm, if (!has_transparent_hugepage()) return; + pr_debug("Validating PUD advanced\n"); /* Align the address wrt HPAGE_PUD_SIZE */ vaddr = (vaddr & HPAGE_PUD_MASK) + HPAGE_PUD_SIZE; @@ -288,6 +299,7 @@ static void __init pud_leaf_tests(unsigned long pfn, pgprot_t prot) { pud_t pud = pfn_pud(pfn, prot); + pr_debug("Validating PUD leaf\n"); /* * PUD based THP is a leaf entry. */ @@ -301,6 +313,8 @@ static void __init pud_huge_tests(pud_t *pudp, unsigned long pfn, pgprot_t prot) if (!IS_ENABLED(CONFIG_HAVE_ARCH_HUGE_VMAP)) return; + + pr_debug("Validating PUD huge\n"); /* * X86 defined pud_set_huge() verifies that the given * PUD is not a populated non-leaf entry. @@ -354,6 +368,7 @@ static void __init p4d_basic_tests(unsigned long pfn, pgprot_t prot) { p4d_t p4d; + pr_debug("Validating P4D basic\n"); memset(&p4d, RANDOM_NZVALUE, sizeof(p4d_t)); WARN_ON(!p4d_same(p4d, p4d)); } @@ -362,6 +377,7 @@ static void __init pgd_basic_tests(unsigned long pfn, pgprot_t prot) { pgd_t pgd; + pr_debug("Validating PGD basic\n"); memset(&pgd, RANDOM_NZVALUE, sizeof(pgd_t)); WARN_ON(!pgd_same(pgd, pgd)); } @@ -374,6 +390,7 @@ static void __init pud_clear_tests(struct mm_struct *mm, pud_t *pudp) if (mm_pmd_folded(mm)) return; + pr_debug("Validating PUD clear\n"); pud = __pud(pud_val(pud) | RANDOM_ORVALUE); WRITE_ONCE(*pudp, pud); pud_clear(pudp); @@ -388,6 +405,8 @@ static void __init pud_populate_tests(struct mm_struct *mm, pud_t *pudp, if (mm_pmd_folded(mm)) return; + + pr_debug("Validating PUD populate\n"); /* * This entry points to next level page table page. * Hence this must not qualify as pud_bad(). @@ -414,6 +433,7 @@ static void __init p4d_clear_tests(struct mm_struct *mm, p4d_t *p4dp) if (mm_pud_folded(mm)) return; + pr_debug("Validating P4D clear\n"); p4d = __p4d(p4d_val(p4d) | RANDOM_ORVALUE); WRITE_ONCE(*p4dp, p4d); p4d_clear(p4dp); @@ -429,6 +449,7 @@ static void __init p4d_populate_tests(struct mm_struct *mm, p4d_t *p4dp, if (mm_pud_folded(mm)) return; + pr_debug("Validating P4D populate\n"); /* * This entry points to next level page table page. * Hence this must not qualify as p4d_bad(). @@ -447,6 +468,7 @@ static void __init pgd_clear_tests(struct mm_struct *mm, pgd_t *pgdp) if (mm_p4d_folded(mm)) return; + pr_debug("Validating PGD clear\n"); pgd = __pgd(pgd_val(pgd) | RANDOM_ORVALUE); WRITE_ONCE(*pgdp, pgd); pgd_clear(pgdp); @@ -462,6 +484,7 @@ static void __init pgd_populate_tests(struct mm_struct *mm, pgd_t *pgdp, if (mm_p4d_folded(mm)) return; + pr_debug("Validating PGD populate\n"); /* * This entry points to next level page table page. * Hence this must not qualify as pgd_bad(). @@ -490,6 +513,7 @@ static void __init pte_clear_tests(struct mm_struct *mm, pte_t *ptep, { pte_t pte = ptep_get(ptep); + pr_debug("Validating PTE clear\n"); pte = __pte(pte_val(pte) | RANDOM_ORVALUE); set_pte_at(mm, vaddr, ptep, pte); barrier(); @@ -502,6 +526,7 @@ static void __init pmd_clear_tests(struct mm_struct *mm, pmd_t *pmdp) { pmd_t pmd = READ_ONCE(*pmdp); + pr_debug("Validating PMD clear\n"); pmd = __pmd(pmd_val(pmd) | RANDOM_ORVALUE); WRITE_ONCE(*pmdp, pmd); pmd_clear(pmdp); @@ -514,6 +539,7 @@ static void __init pmd_populate_tests(struct mm_struct *mm, pmd_t *pmdp, { pmd_t pmd; + pr_debug("Validating PMD populate\n"); /* * This entry points to next level page table page. * Hence this must not qualify as pmd_bad(). @@ -531,6 +557,7 @@ static void __init pte_special_tests(unsigned long pfn, pgprot_t prot) if (!IS_ENABLED(CONFIG_ARCH_HAS_PTE_SPECIAL)) return; + pr_debug("Validating PTE special\n"); WARN_ON(!pte_special(pte_mkspecial(pte))); } @@ -541,6 +568,7 @@ static void __init pte_protnone_tests(unsigned long pfn, pgprot_t prot) if (!IS_ENABLED(CONFIG_NUMA_BALANCING)) return; + pr_debug("Validating PTE protnone\n"); WARN_ON(!pte_protnone(pte)); WARN_ON(!pte_present(pte)); } @@ -553,6 +581,7 @@ static void __init pmd_protnone_tests(unsigned long pfn, pgprot_t prot) if (!IS_ENABLED(CONFIG_NUMA_BALANCING)) return; + pr_debug("Validating PMD protnone\n"); WARN_ON(!pmd_protnone(pmd)); WARN_ON(!pmd_present(pmd)); } @@ -565,6 +594,7 @@ static void __init pte_devmap_tests(unsigned long pfn, pgprot_t prot) { pte_t pte = pfn_pte(pfn, prot); + pr_debug("Validating PTE devmap\n"); WARN_ON(!pte_devmap(pte_mkdevmap(pte))); } @@ -573,6 +603,7 @@ static void __init pmd_devmap_tests(unsigned long pfn, pgprot_t prot) { pmd_t pmd = pfn_pmd(pfn, prot); + pr_debug("Validating PMD devmap\n"); WARN_ON(!pmd_devmap(pmd_mkdevmap(pmd))); } @@ -581,6 +612,7 @@ static void __init pud_devmap_tests(unsigned long pfn, pgprot_t prot) { pud_t pud = pfn_pud(pfn, prot); + pr_debug("Validating PUD devmap\n"); WARN_ON(!pud_devmap(pud_mkdevmap(pud))); } #else /* !CONFIG_HAVE_ARCH_TRANSPARENT_HUGEPAGE_PUD */ @@ -603,6 +635,7 @@ static void __init pte_soft_dirty_tests(unsigned long pfn, pgprot_t prot) if (!IS_ENABLED(CONFIG_MEM_SOFT_DIRTY)) return; + pr_debug("Validating PTE soft dirty\n"); WARN_ON(!pte_soft_dirty(pte_mksoft_dirty(pte))); WARN_ON(pte_soft_dirty(pte_clear_soft_dirty(pte))); } @@ -614,6 +647,7 @@ static void __init pte_swap_soft_dirty_tests(unsigned long pfn, pgprot_t prot) if (!IS_ENABLED(CONFIG_MEM_SOFT_DIRTY)) return; + pr_debug("Validating PTE swap soft dirty\n"); WARN_ON(!pte_swp_soft_dirty(pte_swp_mksoft_dirty(pte))); WARN_ON(pte_swp_soft_dirty(pte_swp_clear_soft_dirty(pte))); } @@ -626,6 +660,7 @@ static void __init pmd_soft_dirty_tests(unsigned long pfn, pgprot_t prot) if (!IS_ENABLED(CONFIG_MEM_SOFT_DIRTY)) return; + pr_debug("Validating PMD soft dirty\n"); WARN_ON(!pmd_soft_dirty(pmd_mksoft_dirty(pmd))); WARN_ON(pmd_soft_dirty(pmd_clear_soft_dirty(pmd))); } @@ -638,6 +673,7 @@ static void __init pmd_swap_soft_dirty_tests(unsigned long pfn, pgprot_t prot) !IS_ENABLED(CONFIG_ARCH_ENABLE_THP_MIGRATION)) return; + pr_debug("Validating PMD swap soft dirty\n"); WARN_ON(!pmd_swp_soft_dirty(pmd_swp_mksoft_dirty(pmd))); WARN_ON(pmd_swp_soft_dirty(pmd_swp_clear_soft_dirty(pmd))); } @@ -653,6 +689,7 @@ static void __init pte_swap_tests(unsigned long pfn, pgprot_t prot) swp_entry_t swp; pte_t pte; + pr_debug("Validating PTE swap\n"); pte = pfn_pte(pfn, prot); swp = __pte_to_swp_entry(pte); pte = __swp_entry_to_pte(swp); @@ -665,6 +702,7 @@ static void __init pmd_swap_tests(unsigned long pfn, pgprot_t prot) swp_entry_t swp; pmd_t pmd; + pr_debug("Validating PMD swap\n"); pmd = pfn_pmd(pfn, prot); swp = __pmd_to_swp_entry(pmd); pmd = __swp_entry_to_pmd(swp); @@ -681,6 +719,8 @@ static void __init swap_migration_tests(void) if (!IS_ENABLED(CONFIG_MIGRATION)) return; + + pr_debug("Validating swap migration\n"); /* * swap_migration_tests() requires a dedicated page as it needs to * be locked before creating a migration entry from it. Locking the @@ -720,6 +760,7 @@ static void __init hugetlb_basic_tests(unsigned long pfn, pgprot_t prot) struct page *page; pte_t pte; + pr_debug("Validating HugeTLB basic\n"); /* * Accessing the page associated with the pfn is safe here, * as it was previously derived from a real kernel symbol. @@ -747,6 +788,7 @@ static void __init hugetlb_advanced_tests(struct mm_struct *mm, pte_t pte = ptep_get(ptep); unsigned long paddr = __pfn_to_phys(pfn) & PMD_MASK; + pr_debug("Validating HugeTLB advanced\n"); pte = pte_mkhuge(mk_pte(pfn_to_page(PHYS_PFN(paddr)), prot)); set_huge_pte_at(mm, vaddr, ptep, pte); barrier(); @@ -797,6 +839,7 @@ static void __init pmd_thp_tests(unsigned long pfn, pgprot_t prot) if (!has_transparent_hugepage()) return; + pr_debug("Validating PMD based THP\n"); /* * pmd_trans_huge() and pmd_present() must return positive after * MMU invalidation with pmd_mkinvalid(). This behavior is an @@ -825,6 +868,7 @@ static void __init pud_thp_tests(unsigned long pfn, pgprot_t prot) if (!has_transparent_hugepage()) return; + pr_debug("Validating PUD based THP\n"); pud = pfn_pud(pfn, prot); WARN_ON(!pud_trans_huge(pud_mkhuge(pud))); -- GitLab From b1d00007f2126e045c465daf754df43ba74ddcdd Mon Sep 17 00:00:00 2001 From: Anshuman Khandual Date: Thu, 6 Aug 2020 23:19:28 -0700 Subject: [PATCH 1352/1476] Documentation/mm: add descriptions for arch page table helpers This adds a specific description file for all arch page table helpers which is in sync with the semantics being tested via CONFIG_DEBUG_VM_PGTABLE. All future changes either to these descriptions here or the debug test should always remain in sync. [anshuman.khandual@arm.com: fold in Mike's patch for the rst document, fix typos in the rst document] Link: http://lkml.kernel.org/r/1594610587-4172-5-git-send-email-anshuman.khandual@arm.com Suggested-by: Mike Rapoport Signed-off-by: Anshuman Khandual Signed-off-by: Andrew Morton Acked-by: Mike Rapoport Cc: Jonathan Corbet Cc: Mike Rapoport Cc: Vineet Gupta Cc: Catalin Marinas Cc: Will Deacon Cc: Benjamin Herrenschmidt Cc: Paul Mackerras Cc: Michael Ellerman Cc: Heiko Carstens Cc: Vasily Gorbik Cc: Christian Borntraeger Cc: Thomas Gleixner Cc: Ingo Molnar Cc: Borislav Petkov Cc: "H. Peter Anvin" Cc: Kirill A. Shutemov Cc: Paul Walmsley Cc: Palmer Dabbelt Cc: Zi Yan Link: http://lkml.kernel.org/r/1593996516-7186-5-git-send-email-anshuman.khandual@arm.com Signed-off-by: Linus Torvalds --- Documentation/vm/arch_pgtable_helpers.rst | 258 ++++++++++++++++++++++ mm/debug_vm_pgtable.c | 6 + 2 files changed, 264 insertions(+) create mode 100644 Documentation/vm/arch_pgtable_helpers.rst diff --git a/Documentation/vm/arch_pgtable_helpers.rst b/Documentation/vm/arch_pgtable_helpers.rst new file mode 100644 index 0000000000000..f3591ee3aaa89 --- /dev/null +++ b/Documentation/vm/arch_pgtable_helpers.rst @@ -0,0 +1,258 @@ +.. SPDX-License-Identifier: GPL-2.0 + +.. _arch_page_table_helpers: + +=============================== +Architecture Page Table Helpers +=============================== + +Generic MM expects architectures (with MMU) to provide helpers to create, access +and modify page table entries at various level for different memory functions. +These page table helpers need to conform to a common semantics across platforms. +Following tables describe the expected semantics which can also be tested during +boot via CONFIG_DEBUG_VM_PGTABLE option. All future changes in here or the debug +test need to be in sync. + +====================== +PTE Page Table Helpers +====================== + ++---------------------------+--------------------------------------------------+ +| pte_same | Tests whether both PTE entries are the same | ++---------------------------+--------------------------------------------------+ +| pte_bad | Tests a non-table mapped PTE | ++---------------------------+--------------------------------------------------+ +| pte_present | Tests a valid mapped PTE | ++---------------------------+--------------------------------------------------+ +| pte_young | Tests a young PTE | ++---------------------------+--------------------------------------------------+ +| pte_dirty | Tests a dirty PTE | ++---------------------------+--------------------------------------------------+ +| pte_write | Tests a writable PTE | ++---------------------------+--------------------------------------------------+ +| pte_special | Tests a special PTE | ++---------------------------+--------------------------------------------------+ +| pte_protnone | Tests a PROT_NONE PTE | ++---------------------------+--------------------------------------------------+ +| pte_devmap | Tests a ZONE_DEVICE mapped PTE | ++---------------------------+--------------------------------------------------+ +| pte_soft_dirty | Tests a soft dirty PTE | ++---------------------------+--------------------------------------------------+ +| pte_swp_soft_dirty | Tests a soft dirty swapped PTE | ++---------------------------+--------------------------------------------------+ +| pte_mkyoung | Creates a young PTE | ++---------------------------+--------------------------------------------------+ +| pte_mkold | Creates an old PTE | ++---------------------------+--------------------------------------------------+ +| pte_mkdirty | Creates a dirty PTE | ++---------------------------+--------------------------------------------------+ +| pte_mkclean | Creates a clean PTE | ++---------------------------+--------------------------------------------------+ +| pte_mkwrite | Creates a writable PTE | ++---------------------------+--------------------------------------------------+ +| pte_mkwrprotect | Creates a write protected PTE | ++---------------------------+--------------------------------------------------+ +| pte_mkspecial | Creates a special PTE | ++---------------------------+--------------------------------------------------+ +| pte_mkdevmap | Creates a ZONE_DEVICE mapped PTE | ++---------------------------+--------------------------------------------------+ +| pte_mksoft_dirty | Creates a soft dirty PTE | ++---------------------------+--------------------------------------------------+ +| pte_clear_soft_dirty | Clears a soft dirty PTE | ++---------------------------+--------------------------------------------------+ +| pte_swp_mksoft_dirty | Creates a soft dirty swapped PTE | ++---------------------------+--------------------------------------------------+ +| pte_swp_clear_soft_dirty | Clears a soft dirty swapped PTE | ++---------------------------+--------------------------------------------------+ +| pte_mknotpresent | Invalidates a mapped PTE | ++---------------------------+--------------------------------------------------+ +| ptep_get_and_clear | Clears a PTE | ++---------------------------+--------------------------------------------------+ +| ptep_get_and_clear_full | Clears a PTE | ++---------------------------+--------------------------------------------------+ +| ptep_test_and_clear_young | Clears young from a PTE | ++---------------------------+--------------------------------------------------+ +| ptep_set_wrprotect | Converts into a write protected PTE | ++---------------------------+--------------------------------------------------+ +| ptep_set_access_flags | Converts into a more permissive PTE | ++---------------------------+--------------------------------------------------+ + +====================== +PMD Page Table Helpers +====================== + ++---------------------------+--------------------------------------------------+ +| pmd_same | Tests whether both PMD entries are the same | ++---------------------------+--------------------------------------------------+ +| pmd_bad | Tests a non-table mapped PMD | ++---------------------------+--------------------------------------------------+ +| pmd_leaf | Tests a leaf mapped PMD | ++---------------------------+--------------------------------------------------+ +| pmd_huge | Tests a HugeTLB mapped PMD | ++---------------------------+--------------------------------------------------+ +| pmd_trans_huge | Tests a Transparent Huge Page (THP) at PMD | ++---------------------------+--------------------------------------------------+ +| pmd_present | Tests a valid mapped PMD | ++---------------------------+--------------------------------------------------+ +| pmd_young | Tests a young PMD | ++---------------------------+--------------------------------------------------+ +| pmd_dirty | Tests a dirty PMD | ++---------------------------+--------------------------------------------------+ +| pmd_write | Tests a writable PMD | ++---------------------------+--------------------------------------------------+ +| pmd_special | Tests a special PMD | ++---------------------------+--------------------------------------------------+ +| pmd_protnone | Tests a PROT_NONE PMD | ++---------------------------+--------------------------------------------------+ +| pmd_devmap | Tests a ZONE_DEVICE mapped PMD | ++---------------------------+--------------------------------------------------+ +| pmd_soft_dirty | Tests a soft dirty PMD | ++---------------------------+--------------------------------------------------+ +| pmd_swp_soft_dirty | Tests a soft dirty swapped PMD | ++---------------------------+--------------------------------------------------+ +| pmd_mkyoung | Creates a young PMD | ++---------------------------+--------------------------------------------------+ +| pmd_mkold | Creates an old PMD | ++---------------------------+--------------------------------------------------+ +| pmd_mkdirty | Creates a dirty PMD | ++---------------------------+--------------------------------------------------+ +| pmd_mkclean | Creates a clean PMD | ++---------------------------+--------------------------------------------------+ +| pmd_mkwrite | Creates a writable PMD | ++---------------------------+--------------------------------------------------+ +| pmd_mkwrprotect | Creates a write protected PMD | ++---------------------------+--------------------------------------------------+ +| pmd_mkspecial | Creates a special PMD | ++---------------------------+--------------------------------------------------+ +| pmd_mkdevmap | Creates a ZONE_DEVICE mapped PMD | ++---------------------------+--------------------------------------------------+ +| pmd_mksoft_dirty | Creates a soft dirty PMD | ++---------------------------+--------------------------------------------------+ +| pmd_clear_soft_dirty | Clears a soft dirty PMD | ++---------------------------+--------------------------------------------------+ +| pmd_swp_mksoft_dirty | Creates a soft dirty swapped PMD | ++---------------------------+--------------------------------------------------+ +| pmd_swp_clear_soft_dirty | Clears a soft dirty swapped PMD | ++---------------------------+--------------------------------------------------+ +| pmd_mkinvalid | Invalidates a mapped PMD [1] | ++---------------------------+--------------------------------------------------+ +| pmd_set_huge | Creates a PMD huge mapping | ++---------------------------+--------------------------------------------------+ +| pmd_clear_huge | Clears a PMD huge mapping | ++---------------------------+--------------------------------------------------+ +| pmdp_get_and_clear | Clears a PMD | ++---------------------------+--------------------------------------------------+ +| pmdp_get_and_clear_full | Clears a PMD | ++---------------------------+--------------------------------------------------+ +| pmdp_test_and_clear_young | Clears young from a PMD | ++---------------------------+--------------------------------------------------+ +| pmdp_set_wrprotect | Converts into a write protected PMD | ++---------------------------+--------------------------------------------------+ +| pmdp_set_access_flags | Converts into a more permissive PMD | ++---------------------------+--------------------------------------------------+ + +====================== +PUD Page Table Helpers +====================== + ++---------------------------+--------------------------------------------------+ +| pud_same | Tests whether both PUD entries are the same | ++---------------------------+--------------------------------------------------+ +| pud_bad | Tests a non-table mapped PUD | ++---------------------------+--------------------------------------------------+ +| pud_leaf | Tests a leaf mapped PUD | ++---------------------------+--------------------------------------------------+ +| pud_huge | Tests a HugeTLB mapped PUD | ++---------------------------+--------------------------------------------------+ +| pud_trans_huge | Tests a Transparent Huge Page (THP) at PUD | ++---------------------------+--------------------------------------------------+ +| pud_present | Tests a valid mapped PUD | ++---------------------------+--------------------------------------------------+ +| pud_young | Tests a young PUD | ++---------------------------+--------------------------------------------------+ +| pud_dirty | Tests a dirty PUD | ++---------------------------+--------------------------------------------------+ +| pud_write | Tests a writable PUD | ++---------------------------+--------------------------------------------------+ +| pud_devmap | Tests a ZONE_DEVICE mapped PUD | ++---------------------------+--------------------------------------------------+ +| pud_mkyoung | Creates a young PUD | ++---------------------------+--------------------------------------------------+ +| pud_mkold | Creates an old PUD | ++---------------------------+--------------------------------------------------+ +| pud_mkdirty | Creates a dirty PUD | ++---------------------------+--------------------------------------------------+ +| pud_mkclean | Creates a clean PUD | ++---------------------------+--------------------------------------------------+ +| pud_mkwrite | Creates a writable PUD | ++---------------------------+--------------------------------------------------+ +| pud_mkwrprotect | Creates a write protected PUD | ++---------------------------+--------------------------------------------------+ +| pud_mkdevmap | Creates a ZONE_DEVICE mapped PUD | ++---------------------------+--------------------------------------------------+ +| pud_mkinvalid | Invalidates a mapped PUD [1] | ++---------------------------+--------------------------------------------------+ +| pud_set_huge | Creates a PUD huge mapping | ++---------------------------+--------------------------------------------------+ +| pud_clear_huge | Clears a PUD huge mapping | ++---------------------------+--------------------------------------------------+ +| pudp_get_and_clear | Clears a PUD | ++---------------------------+--------------------------------------------------+ +| pudp_get_and_clear_full | Clears a PUD | ++---------------------------+--------------------------------------------------+ +| pudp_test_and_clear_young | Clears young from a PUD | ++---------------------------+--------------------------------------------------+ +| pudp_set_wrprotect | Converts into a write protected PUD | ++---------------------------+--------------------------------------------------+ +| pudp_set_access_flags | Converts into a more permissive PUD | ++---------------------------+--------------------------------------------------+ + +========================== +HugeTLB Page Table Helpers +========================== + ++---------------------------+--------------------------------------------------+ +| pte_huge | Tests a HugeTLB | ++---------------------------+--------------------------------------------------+ +| pte_mkhuge | Creates a HugeTLB | ++---------------------------+--------------------------------------------------+ +| huge_pte_dirty | Tests a dirty HugeTLB | ++---------------------------+--------------------------------------------------+ +| huge_pte_write | Tests a writable HugeTLB | ++---------------------------+--------------------------------------------------+ +| huge_pte_mkdirty | Creates a dirty HugeTLB | ++---------------------------+--------------------------------------------------+ +| huge_pte_mkwrite | Creates a writable HugeTLB | ++---------------------------+--------------------------------------------------+ +| huge_pte_mkwrprotect | Creates a write protected HugeTLB | ++---------------------------+--------------------------------------------------+ +| huge_ptep_get_and_clear | Clears a HugeTLB | ++---------------------------+--------------------------------------------------+ +| huge_ptep_set_wrprotect | Converts into a write protected HugeTLB | ++---------------------------+--------------------------------------------------+ +| huge_ptep_set_access_flags | Converts into a more permissive HugeTLB | ++---------------------------+--------------------------------------------------+ + +======================== +SWAP Page Table Helpers +======================== + ++---------------------------+--------------------------------------------------+ +| __pte_to_swp_entry | Creates a swapped entry (arch) from a mapped PTE | ++---------------------------+--------------------------------------------------+ +| __swp_to_pte_entry | Creates a mapped PTE from a swapped entry (arch) | ++---------------------------+--------------------------------------------------+ +| __pmd_to_swp_entry | Creates a swapped entry (arch) from a mapped PMD | ++---------------------------+--------------------------------------------------+ +| __swp_to_pmd_entry | Creates a mapped PMD from a swapped entry (arch) | ++---------------------------+--------------------------------------------------+ +| is_migration_entry | Tests a migration (read or write) swapped entry | ++---------------------------+--------------------------------------------------+ +| is_write_migration_entry | Tests a write migration swapped entry | ++---------------------------+--------------------------------------------------+ +| make_migration_entry_read | Converts into read migration swapped entry | ++---------------------------+--------------------------------------------------+ +| make_migration_entry | Creates a migration swapped entry (read or write)| ++---------------------------+--------------------------------------------------+ + +[1] https://lore.kernel.org/linux-mm/20181017020930.GN30832@redhat.com/ diff --git a/mm/debug_vm_pgtable.c b/mm/debug_vm_pgtable.c index 8d389d071968e..086309fb9b6fb 100644 --- a/mm/debug_vm_pgtable.c +++ b/mm/debug_vm_pgtable.c @@ -31,6 +31,12 @@ #include #include +/* + * Please refer Documentation/vm/arch_pgtable_helpers.rst for the semantics + * expectations that are being validated here. All future changes in here + * or the documentation need to be in sync. + */ + #define VMFLAGS (VM_READ|VM_WRITE|VM_EXEC) /* -- GitLab From e1ab96f8cc713c673df4ab806bd90918e5ab6c98 Mon Sep 17 00:00:00 2001 From: "Matthew Wilcox (Oracle)" Date: Thu, 6 Aug 2020 23:19:32 -0700 Subject: [PATCH 1353/1476] mm/debug: handle page->mapping better in dump_page Patch series "Improvements for dump_page()", v2. Here's a sample dump of a pagecache tail page with all of the patches applied: page:000000006d1c49ca refcount:6 mapcount:0 mapping:00000000136b8d90 index:0x109 pfn:0x6c645 head:000000008bd38076 order:2 compound_mapcount:0 compound_pincount:0 aops:xfs_address_space_operations ino:800042 dentry name:"fd" flags: 0x4000000000012014(uptodate|lru|private|head) raw: 4000000000000000 ffffd46ac1b19101 ffffffff00000202 dead000000000004 raw: 0000000000000001 0000000000000000 00000000ffffffff 0000000000000000 head: 4000000000012014 ffffd46ac1b1bbc8 ffffd46ac1b1bc08 ffff91976f659560 head: 0000000000000108 ffff919773220680 00000006ffffffff 0000000000000000 page dumped because: testing This patch (of 6): If we can't call page_mapping() to get the page mapping, handle the anon/ksm/movable bits correctly. [akpm@linux-foundation.org: augmented code comment from John] Link: http://lkml.kernel.org/r/15cff11a-6762-8a6a-3f0e-dd227280cd6f@nvidia.com Signed-off-by: Matthew Wilcox (Oracle) Signed-off-by: Andrew Morton Reviewed-by: John Hubbard Acked-by: Mike Rapoport Acked-by: Vlastimil Babka Cc: William Kucharski Cc: "Kirill A. Shutemov" Link: http://lkml.kernel.org/r/20200709202117.7216-1-willy@infradead.org Link: http://lkml.kernel.org/r/20200709202117.7216-2-willy@infradead.org Signed-off-by: Linus Torvalds --- mm/debug.c | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/mm/debug.c b/mm/debug.c index 4f376514744db..567b80b0b9e4a 100644 --- a/mm/debug.c +++ b/mm/debug.c @@ -69,8 +69,19 @@ void __dump_page(struct page *page, const char *reason) } if (page < head || (page >= head + MAX_ORDER_NR_PAGES)) { - /* Corrupt page, cannot call page_mapping */ - mapping = page->mapping; + /* + * Corrupt page, so we cannot call page_mapping. Instead, do a + * safe subset of the steps that page_mapping() does. Caution: + * this will be misleading for tail pages, PageSwapCache pages, + * and potentially other situations. (See the page_mapping() + * implementation for what's missing here.) + */ + unsigned long tmp = (unsigned long)page->mapping; + + if (tmp & PAGE_MAPPING_ANON) + mapping = NULL; + else + mapping = (void *)(tmp & ~PAGE_MAPPING_FLAGS); head = page; compound = false; } else { -- GitLab From 452b557c9500d27903a20c450ca8d2919c3742be Mon Sep 17 00:00:00 2001 From: "Matthew Wilcox (Oracle)" Date: Thu, 6 Aug 2020 23:19:35 -0700 Subject: [PATCH 1354/1476] mm/debug: dump compound page information on a second line Simplify both the implementation and the output by splitting all the compound page information onto a second line. Reported-by: John Hubbard Signed-off-by: Matthew Wilcox (Oracle) Signed-off-by: Andrew Morton Tested-by: John Hubbard Reviewed-by: John Hubbard Acked-by: Mike Rapoport Acked-by: Vlastimil Babka Cc: "Kirill A. Shutemov" Cc: William Kucharski Link: http://lkml.kernel.org/r/20200709202117.7216-3-willy@infradead.org Signed-off-by: Linus Torvalds --- mm/debug.c | 30 ++++++++++++------------------ 1 file changed, 12 insertions(+), 18 deletions(-) diff --git a/mm/debug.c b/mm/debug.c index 567b80b0b9e4a..b0ebf83eec899 100644 --- a/mm/debug.c +++ b/mm/debug.c @@ -95,27 +95,21 @@ void __dump_page(struct page *page, const char *reason) */ mapcount = PageSlab(head) ? 0 : page_mapcount(page); - if (compound) + pr_warn("page:%px refcount:%d mapcount:%d mapping:%p index:%#lx\n", + page, page_ref_count(head), mapcount, mapping, + page_to_pgoff(page)); + if (compound) { if (hpage_pincount_available(page)) { - pr_warn("page:%px refcount:%d mapcount:%d mapping:%p " - "index:%#lx head:%px order:%u " - "compound_mapcount:%d compound_pincount:%d\n", - page, page_ref_count(head), mapcount, - mapping, page_to_pgoff(page), head, - compound_order(head), compound_mapcount(page), - compound_pincount(page)); + pr_warn("head:%px order:%u compound_mapcount:%d compound_pincount:%d\n", + head, compound_order(head), + compound_mapcount(head), + compound_pincount(head)); } else { - pr_warn("page:%px refcount:%d mapcount:%d mapping:%p " - "index:%#lx head:%px order:%u " - "compound_mapcount:%d\n", - page, page_ref_count(head), mapcount, - mapping, page_to_pgoff(page), head, - compound_order(head), compound_mapcount(page)); + pr_warn("head:%px order:%u compound_mapcount:%d\n", + head, compound_order(head), + compound_mapcount(head)); } - else - pr_warn("page:%px refcount:%d mapcount:%d mapping:%p index:%#lx\n", - page, page_ref_count(page), mapcount, - mapping, page_to_pgoff(page)); + } if (PageKsm(page)) type = "ksm "; else if (PageAnon(page)) -- GitLab From 0b93d59e90d4403dd7c6bccaf27bd5bc91396866 Mon Sep 17 00:00:00 2001 From: "Matthew Wilcox (Oracle)" Date: Thu, 6 Aug 2020 23:19:39 -0700 Subject: [PATCH 1355/1476] mm/debug: print head flags in dump_page Tail page flags contain very little useful information. Print the head page's flags instead. While the flags will contain "head" for tail pages, this should not be too confusing as the previous line starts with the word "head:" and so the flags should be interpreted as belonging to the head page. Signed-off-by: Matthew Wilcox (Oracle) Signed-off-by: Andrew Morton Reviewed-by: John Hubbard Acked-by: Mike Rapoport Acked-by: Vlastimil Babka Cc: "Kirill A. Shutemov" Cc: William Kucharski Link: http://lkml.kernel.org/r/20200709202117.7216-4-willy@infradead.org Signed-off-by: Linus Torvalds --- mm/debug.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mm/debug.c b/mm/debug.c index b0ebf83eec899..27837c18027ca 100644 --- a/mm/debug.c +++ b/mm/debug.c @@ -168,7 +168,7 @@ void __dump_page(struct page *page, const char *reason) out_mapping: BUILD_BUG_ON(ARRAY_SIZE(pageflag_names) != __NR_PAGEFLAGS + 1); - pr_warn("%sflags: %#lx(%pGp)%s\n", type, page->flags, &page->flags, + pr_warn("%sflags: %#lx(%pGp)%s\n", type, head->flags, &head->flags, page_cma ? " CMA" : ""); hex_only: -- GitLab From 9ad3826575abd1c096cf678a87dd860395c46d78 Mon Sep 17 00:00:00 2001 From: "Matthew Wilcox (Oracle)" Date: Thu, 6 Aug 2020 23:19:42 -0700 Subject: [PATCH 1356/1476] mm/debug: switch dump_page to get_kernel_nofault This is simpler to use than copy_from_kernel_nofault(). Also make some of the related error messages less verbose. Signed-off-by: Matthew Wilcox (Oracle) Signed-off-by: Andrew Morton Acked-by: Mike Rapoport Acked-by: Vlastimil Babka Cc: John Hubbard Cc: "Kirill A. Shutemov" Cc: William Kucharski Link: http://lkml.kernel.org/r/20200709202117.7216-5-willy@infradead.org Signed-off-by: Linus Torvalds --- mm/debug.c | 36 ++++++++++++++++-------------------- 1 file changed, 16 insertions(+), 20 deletions(-) diff --git a/mm/debug.c b/mm/debug.c index 27837c18027ca..4c62ee5f0ae60 100644 --- a/mm/debug.c +++ b/mm/debug.c @@ -115,54 +115,50 @@ void __dump_page(struct page *page, const char *reason) else if (PageAnon(page)) type = "anon "; else if (mapping) { - const struct inode *host; + struct inode *host; const struct address_space_operations *a_ops; - const struct hlist_node *dentry_first; - const struct dentry *dentry_ptr; + struct hlist_node *dentry_first; + struct dentry *dentry_ptr; struct dentry dentry; /* * mapping can be invalid pointer and we don't want to crash * accessing it, so probe everything depending on it carefully */ - if (copy_from_kernel_nofault(&host, &mapping->host, - sizeof(struct inode *)) || - copy_from_kernel_nofault(&a_ops, &mapping->a_ops, - sizeof(struct address_space_operations *))) { - pr_warn("failed to read mapping->host or a_ops, mapping not a valid kernel address?\n"); + if (get_kernel_nofault(host, &mapping->host) || + get_kernel_nofault(a_ops, &mapping->a_ops)) { + pr_warn("failed to read mapping contents, not a valid kernel address?\n"); goto out_mapping; } if (!host) { - pr_warn("mapping->a_ops:%ps\n", a_ops); + pr_warn("aops:%ps\n", a_ops); goto out_mapping; } - if (copy_from_kernel_nofault(&dentry_first, - &host->i_dentry.first, sizeof(struct hlist_node *))) { - pr_warn("mapping->a_ops:%ps with invalid mapping->host inode address %px\n", - a_ops, host); + if (get_kernel_nofault(dentry_first, &host->i_dentry.first)) { + pr_warn("aops:%ps with invalid host inode %px\n", + a_ops, host); goto out_mapping; } if (!dentry_first) { - pr_warn("mapping->a_ops:%ps\n", a_ops); + pr_warn("aops:%ps\n", a_ops); goto out_mapping; } dentry_ptr = container_of(dentry_first, struct dentry, d_u.d_alias); - if (copy_from_kernel_nofault(&dentry, dentry_ptr, - sizeof(struct dentry))) { - pr_warn("mapping->aops:%ps with invalid mapping->host->i_dentry.first %px\n", - a_ops, dentry_ptr); + if (get_kernel_nofault(dentry, dentry_ptr)) { + pr_warn("aops:%ps with invalid dentry %px\n", a_ops, + dentry_ptr); } else { /* * if dentry is corrupted, the %pd handler may still * crash, but it's unlikely that we reach here with a * corrupted struct page */ - pr_warn("mapping->aops:%ps dentry name:\"%pd\"\n", - a_ops, &dentry); + pr_warn("aops:%ps dentry name:\"%pd\"\n", a_ops, + &dentry); } } out_mapping: -- GitLab From 9bdaf2cc5eb570a875c6871ef7bee86a8c5be2d7 Mon Sep 17 00:00:00 2001 From: "Matthew Wilcox (Oracle)" Date: Thu, 6 Aug 2020 23:19:45 -0700 Subject: [PATCH 1357/1476] mm/debug: print the inode number in dump_page The inode number helps correlate this page with debug messages elsewhere in the kernel. Signed-off-by: Matthew Wilcox (Oracle) Signed-off-by: Andrew Morton Reviewed-by: John Hubbard Acked-by: Mike Rapoport Cc: "Kirill A. Shutemov" Cc: Vlastimil Babka Cc: William Kucharski Link: http://lkml.kernel.org/r/20200709202117.7216-6-willy@infradead.org Signed-off-by: Linus Torvalds --- mm/debug.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/mm/debug.c b/mm/debug.c index 4c62ee5f0ae60..c4dbe4def83da 100644 --- a/mm/debug.c +++ b/mm/debug.c @@ -143,7 +143,7 @@ void __dump_page(struct page *page, const char *reason) } if (!dentry_first) { - pr_warn("aops:%ps\n", a_ops); + pr_warn("aops:%ps ino:%lx\n", a_ops, host->i_ino); goto out_mapping; } @@ -157,8 +157,8 @@ void __dump_page(struct page *page, const char *reason) * crash, but it's unlikely that we reach here with a * corrupted struct page */ - pr_warn("aops:%ps dentry name:\"%pd\"\n", a_ops, - &dentry); + pr_warn("aops:%ps ino:%lx dentry name:\"%pd\"\n", + a_ops, host->i_ino, &dentry); } } out_mapping: -- GitLab From 54a75157d97976f35e63e37e31e39bda622cbf39 Mon Sep 17 00:00:00 2001 From: "Matthew Wilcox (Oracle)" Date: Thu, 6 Aug 2020 23:19:48 -0700 Subject: [PATCH 1358/1476] mm/debug: print hashed address of struct page The actual address of the struct page isn't particularly helpful, while the hashed address helps match with other messages elsewhere. Add the PFN that the page refers to in order to help diagnose problems where the page is improperly aligned for the purpose. Signed-off-by: Matthew Wilcox (Oracle) Signed-off-by: Andrew Morton Reviewed-by: John Hubbard Acked-by: Mike Rapoport Cc: "Kirill A. Shutemov" Cc: Vlastimil Babka Cc: William Kucharski Link: http://lkml.kernel.org/r/20200709202117.7216-7-willy@infradead.org Signed-off-by: Linus Torvalds --- mm/debug.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/mm/debug.c b/mm/debug.c index c4dbe4def83da..8f569db9a5146 100644 --- a/mm/debug.c +++ b/mm/debug.c @@ -95,17 +95,17 @@ void __dump_page(struct page *page, const char *reason) */ mapcount = PageSlab(head) ? 0 : page_mapcount(page); - pr_warn("page:%px refcount:%d mapcount:%d mapping:%p index:%#lx\n", + pr_warn("page:%p refcount:%d mapcount:%d mapping:%p index:%#lx pfn:%#lx\n", page, page_ref_count(head), mapcount, mapping, - page_to_pgoff(page)); + page_to_pgoff(page), page_to_pfn(page)); if (compound) { if (hpage_pincount_available(page)) { - pr_warn("head:%px order:%u compound_mapcount:%d compound_pincount:%d\n", + pr_warn("head:%p order:%u compound_mapcount:%d compound_pincount:%d\n", head, compound_order(head), compound_mapcount(head), compound_pincount(head)); } else { - pr_warn("head:%px order:%u compound_mapcount:%d\n", + pr_warn("head:%p order:%u compound_mapcount:%d\n", head, compound_order(head), compound_mapcount(head)); } -- GitLab From 6dc5ea16c86f753951f53085aa04df49cf17cb50 Mon Sep 17 00:00:00 2001 From: John Hubbard Date: Thu, 6 Aug 2020 23:19:51 -0700 Subject: [PATCH 1359/1476] mm, dump_page: do not crash with bad compound_mapcount() If a compound page is being split while dump_page() is being run on that page, we can end up calling compound_mapcount() on a page that is no longer compound. This leads to a crash (already seen at least once in the field), due to the VM_BUG_ON_PAGE() assertion inside compound_mapcount(). (The above is from Matthew Wilcox's analysis of Qian Cai's bug report.) A similar problem is possible, via compound_pincount() instead of compound_mapcount(). In order to avoid this kind of crash, make dump_page() slightly more robust, by providing a pair of simpler routines that don't contain assertions: head_mapcount() and head_pincount(). For debug tools, we don't want to go *too* far in this direction, but this is a simple small fix, and the crash has already been seen, so it's a good trade-off. Reported-by: Qian Cai Suggested-by: Matthew Wilcox Signed-off-by: John Hubbard Signed-off-by: Andrew Morton Acked-by: Vlastimil Babka Cc: Kirill A. Shutemov Cc: Mike Rapoport Cc: William Kucharski Link: http://lkml.kernel.org/r/20200804214807.169256-1-jhubbard@nvidia.com Signed-off-by: Linus Torvalds --- include/linux/mm.h | 14 ++++++++++++-- mm/debug.c | 6 +++--- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/include/linux/mm.h b/include/linux/mm.h index dc7b87310c103..303a47a9769d6 100644 --- a/include/linux/mm.h +++ b/include/linux/mm.h @@ -779,6 +779,11 @@ static inline void *kvcalloc(size_t n, size_t size, gfp_t flags) extern void kvfree(const void *addr); extern void kvfree_sensitive(const void *addr, size_t len); +static inline int head_mapcount(struct page *head) +{ + return atomic_read(compound_mapcount_ptr(head)) + 1; +} + /* * Mapcount of compound page as a whole, does not include mapped sub-pages. * @@ -788,7 +793,7 @@ static inline int compound_mapcount(struct page *page) { VM_BUG_ON_PAGE(!PageCompound(page), page); page = compound_head(page); - return atomic_read(compound_mapcount_ptr(page)) + 1; + return head_mapcount(page); } /* @@ -901,11 +906,16 @@ static inline bool hpage_pincount_available(struct page *page) return PageCompound(page) && compound_order(page) > 1; } +static inline int head_pincount(struct page *head) +{ + return atomic_read(compound_pincount_ptr(head)); +} + static inline int compound_pincount(struct page *page) { VM_BUG_ON_PAGE(!hpage_pincount_available(page), page); page = compound_head(page); - return atomic_read(compound_pincount_ptr(page)); + return head_pincount(page); } static inline void set_compound_order(struct page *page, unsigned int order) diff --git a/mm/debug.c b/mm/debug.c index 8f569db9a5146..ca8d1cacdecc8 100644 --- a/mm/debug.c +++ b/mm/debug.c @@ -102,12 +102,12 @@ void __dump_page(struct page *page, const char *reason) if (hpage_pincount_available(page)) { pr_warn("head:%p order:%u compound_mapcount:%d compound_pincount:%d\n", head, compound_order(head), - compound_mapcount(head), - compound_pincount(head)); + head_mapcount(head), + head_pincount(head)); } else { pr_warn("head:%p order:%u compound_mapcount:%d\n", head, compound_order(head), - compound_mapcount(head)); + head_mapcount(head)); } } if (PageKsm(page)) -- GitLab From b9306a796cad8a65f4d21779524e73fe0745bae1 Mon Sep 17 00:00:00 2001 From: Yang Shi Date: Thu, 6 Aug 2020 23:19:55 -0700 Subject: [PATCH 1360/1476] mm: filemap: clear idle flag for writes Since commit bbddabe2e436aa ("mm: filemap: only do access activations on reads"), mark_page_accessed() is called for reads only. But the idle flag is cleared by mark_page_accessed() so the idle flag won't get cleared if the page is write accessed only. Basically idle page tracking is used to estimate workingset size of workload, noticeable size of workingset might be missed if the idle flag is not maintained correctly. It seems good enough to just clear idle flag for write operations. Fixes: bbddabe2e436 ("mm: filemap: only do access activations on reads") Reported-by: Gang Deng Signed-off-by: Yang Shi Signed-off-by: Andrew Morton Reviewed-by: Shakeel Butt Cc: Johannes Weiner Cc: Rik van Riel Link: http://lkml.kernel.org/r/1593020612-13051-1-git-send-email-yang.shi@linux.alibaba.com Signed-off-by: Linus Torvalds --- mm/filemap.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/mm/filemap.c b/mm/filemap.c index 9f131f1cfde3f..4c8e64d104bda 100644 --- a/mm/filemap.c +++ b/mm/filemap.c @@ -41,6 +41,7 @@ #include #include #include +#include #include "internal.h" #define CREATE_TRACE_POINTS @@ -1689,6 +1690,11 @@ repeat: if (fgp_flags & FGP_ACCESSED) mark_page_accessed(page); + else if (fgp_flags & FGP_WRITE) { + /* Clear idle flag for buffer write */ + if (page_is_idle(page)) + clear_page_idle(page); + } no_page: if (!page && (fgp_flags & FGP_CREAT)) { -- GitLab From 605cad8343721c4a53e80f87d5b1161baf1a7d2b Mon Sep 17 00:00:00 2001 From: Yang Shi Date: Thu, 6 Aug 2020 23:19:58 -0700 Subject: [PATCH 1361/1476] mm: filemap: add missing FGP_ flags in kerneldoc comment for pagecache_get_page FGP_{WRITE|NOFS|NOWAIT} were missed in pagecache_get_page's kerneldoc comment. Signed-off-by: Yang Shi Signed-off-by: Andrew Morton Cc: Gang Deng Cc: Shakeel Butt Cc: Johannes Weiner Cc: Rik van Riel Link: http://lkml.kernel.org/r/1593031747-4249-1-git-send-email-yang.shi@linux.alibaba.com Signed-off-by: Linus Torvalds --- mm/filemap.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/mm/filemap.c b/mm/filemap.c index 4c8e64d104bda..f2bb5ff0293da 100644 --- a/mm/filemap.c +++ b/mm/filemap.c @@ -1649,6 +1649,9 @@ EXPORT_SYMBOL(find_lock_entry); * * %FGP_FOR_MMAP - The caller wants to do its own locking dance if the * page is already in cache. If the page was allocated, unlock it before * returning so the caller can do the same dance. + * * %FGP_WRITE - The page will be written + * * %FGP_NOFS - __GFP_FS will get cleared in gfp mask + * * %FGP_NOWAIT - Don't get blocked by page lock * * If %FGP_LOCK or %FGP_CREAT are specified then the function may sleep even * if the %GFP flags specified for %FGP_CREAT are atomic. -- GitLab From 0a36f7f85e69ce248a89634b2d386600bc4c637b Mon Sep 17 00:00:00 2001 From: Tang Yizhou Date: Thu, 6 Aug 2020 23:20:01 -0700 Subject: [PATCH 1362/1476] mm/gup.c: fix the comment of return value for populate_vma_page_range() The return value of populate_vma_page_range() is consistent with __get_user_pages(), and so is the function comment of return value. Signed-off-by: Tang Yizhou Signed-off-by: Andrew Morton Reviewed-by: Andrew Morton Reviewed-by: Ira Weiny Link: http://lkml.kernel.org/r/20200720034303.29920-1-tangyizhou@huawei.com Signed-off-by: Linus Torvalds --- mm/gup.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/mm/gup.c b/mm/gup.c index 6f47697f8fb0b..d8a33dd1430d9 100644 --- a/mm/gup.c +++ b/mm/gup.c @@ -1404,7 +1404,8 @@ retry: * * This takes care of mlocking the pages too if VM_LOCKED is set. * - * return 0 on success, negative error code on error. + * Return either number of pages pinned in the vma, or a negative error + * code on error. * * vma->vm_mm->mmap_lock must be held. * -- GitLab From f90eae2a0f8ec43081c2d590004da9dd55a64791 Mon Sep 17 00:00:00 2001 From: Zhen Lei Date: Thu, 6 Aug 2020 23:20:05 -0700 Subject: [PATCH 1363/1476] mm/swap_slots.c: simplify alloc_swap_slot_cache() Patch series "clean up some functions in mm/swap_slots.c". When I studied the code of mm/swap_slots.c, I found some places can be improved. This patch (of 3): Both "slots" and "slots_ret" are only need to be freed when cache already allocated. Make them closer, seems more clear. No functional change. Signed-off-by: Zhen Lei Signed-off-by: Andrew Morton Acked-by: Tim Chen Link: http://lkml.kernel.org/r/20200430061143.450-1-thunder.leizhen@huawei.com Link: http://lkml.kernel.org/r/20200430061143.450-2-thunder.leizhen@huawei.com Signed-off-by: Linus Torvalds --- mm/swap_slots.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/mm/swap_slots.c b/mm/swap_slots.c index 0975adc722532..01609b5f0c550 100644 --- a/mm/swap_slots.c +++ b/mm/swap_slots.c @@ -136,9 +136,16 @@ static int alloc_swap_slot_cache(unsigned int cpu) mutex_lock(&swap_slots_cache_mutex); cache = &per_cpu(swp_slots, cpu); - if (cache->slots || cache->slots_ret) + if (cache->slots || cache->slots_ret) { /* cache already allocated */ - goto out; + mutex_unlock(&swap_slots_cache_mutex); + + kvfree(slots); + kvfree(slots_ret); + + return 0; + } + if (!cache->lock_initialized) { mutex_init(&cache->alloc_lock); spin_lock_init(&cache->free_lock); @@ -155,15 +162,8 @@ static int alloc_swap_slot_cache(unsigned int cpu) */ mb(); cache->slots = slots; - slots = NULL; cache->slots_ret = slots_ret; - slots_ret = NULL; -out: mutex_unlock(&swap_slots_cache_mutex); - if (slots) - kvfree(slots); - if (slots_ret) - kvfree(slots_ret); return 0; } -- GitLab From d69a9575f57ef742df599f1186dcbfb28af0f846 Mon Sep 17 00:00:00 2001 From: Zhen Lei Date: Thu, 6 Aug 2020 23:20:08 -0700 Subject: [PATCH 1364/1476] mm/swap_slots.c: simplify enable_swap_slots_cache() Whether swap_slot_cache_initialized is true or false, __reenable_swap_slots_cache() is always called. To make this meaning clear, leave only one call to __reenable_swap_slots_cache(). This also make it clearer what extra needs be done when swap_slot_cache_initialized is false. No functional change. Signed-off-by: Zhen Lei Signed-off-by: Andrew Morton Acked-by: Tim Chen Link: http://lkml.kernel.org/r/20200430061143.450-3-thunder.leizhen@huawei.com Signed-off-by: Linus Torvalds --- mm/swap_slots.c | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/mm/swap_slots.c b/mm/swap_slots.c index 01609b5f0c550..b40394473a3cd 100644 --- a/mm/swap_slots.c +++ b/mm/swap_slots.c @@ -240,21 +240,19 @@ static int free_slot_cache(unsigned int cpu) int enable_swap_slots_cache(void) { - int ret = 0; - mutex_lock(&swap_slots_cache_enable_mutex); - if (swap_slot_cache_initialized) { - __reenable_swap_slots_cache(); - goto out_unlock; - } + if (!swap_slot_cache_initialized) { + int ret; - ret = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "swap_slots_cache", - alloc_swap_slot_cache, free_slot_cache); - if (WARN_ONCE(ret < 0, "Cache allocation failed (%s), operating " - "without swap slots cache.\n", __func__)) - goto out_unlock; + ret = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "swap_slots_cache", + alloc_swap_slot_cache, free_slot_cache); + if (WARN_ONCE(ret < 0, "Cache allocation failed (%s), operating " + "without swap slots cache.\n", __func__)) + goto out_unlock; + + swap_slot_cache_initialized = true; + } - swap_slot_cache_initialized = true; __reenable_swap_slots_cache(); out_unlock: mutex_unlock(&swap_slots_cache_enable_mutex); -- GitLab From e0f3ebba97f190ce988829ccc5b527e8eef40e01 Mon Sep 17 00:00:00 2001 From: Zhen Lei Date: Thu, 6 Aug 2020 23:20:11 -0700 Subject: [PATCH 1365/1476] mm/swap_slots.c: remove redundant check for swap_slot_cache_initialized Because enable_swap_slots_cache can only become true in enable_swap_slots_cache(), and depends on swap_slot_cache_initialized is true before. That means, when enable_swap_slots_cache is true, swap_slot_cache_initialized is true also. So the condition: "swap_slot_cache_enabled && swap_slot_cache_initialized" can be reduced to "swap_slot_cache_enabled" And in mathematics: "!swap_slot_cache_enabled || !swap_slot_cache_initialized" is equal to "!(swap_slot_cache_enabled && swap_slot_cache_initialized)" So no functional change. Signed-off-by: Zhen Lei Signed-off-by: Andrew Morton Acked-by: Tim Chen Link: http://lkml.kernel.org/r/20200430061143.450-4-thunder.leizhen@huawei.com Signed-off-by: Linus Torvalds --- mm/swap_slots.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/mm/swap_slots.c b/mm/swap_slots.c index b40394473a3cd..3e6453573a898 100644 --- a/mm/swap_slots.c +++ b/mm/swap_slots.c @@ -46,8 +46,7 @@ static void __drain_swap_slots_cache(unsigned int type); static void deactivate_swap_slots_cache(void); static void reactivate_swap_slots_cache(void); -#define use_swap_slot_cache (swap_slot_cache_active && \ - swap_slot_cache_enabled && swap_slot_cache_initialized) +#define use_swap_slot_cache (swap_slot_cache_active && swap_slot_cache_enabled) #define SLOTS_CACHE 0x1 #define SLOTS_CACHE_RET 0x2 @@ -94,7 +93,7 @@ static bool check_cache_active(void) { long pages; - if (!swap_slot_cache_enabled || !swap_slot_cache_initialized) + if (!swap_slot_cache_enabled) return false; pages = get_nr_swap_pages(); -- GitLab From 27ec4878df7bdffa4667f41d3830520597679a74 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Thu, 6 Aug 2020 23:20:14 -0700 Subject: [PATCH 1366/1476] mm: swap: fix kerneldoc of swap_vma_readahead() Fix W=1 compile warnings (invalid kerneldoc): mm/swap_state.c:742: warning: Function parameter or member 'fentry' not described in 'swap_vma_readahead' mm/swap_state.c:742: warning: Excess function parameter 'entry' description in 'swap_vma_readahead' Signed-off-by: Krzysztof Kozlowski Signed-off-by: Andrew Morton Reviewed-by: Andrew Morton Link: http://lkml.kernel.org/r/20200728171109.28687-2-krzk@kernel.org Signed-off-by: Linus Torvalds --- mm/swap_state.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mm/swap_state.c b/mm/swap_state.c index 05889e8e3c97d..e82f4f8b1f63d 100644 --- a/mm/swap_state.c +++ b/mm/swap_state.c @@ -725,7 +725,7 @@ static void swap_ra_info(struct vm_fault *vmf, /** * swap_vma_readahead - swap in pages in hope we need them soon - * @entry: swap entry of this memory + * @fentry: swap entry of this memory * @gfp_mask: memory allocation flags * @vmf: fault information * -- GitLab From 0f190a7ab78878f9e6c6930fc0f5f92c1250b57d Mon Sep 17 00:00:00 2001 From: Xianting Tian Date: Thu, 6 Aug 2020 23:20:17 -0700 Subject: [PATCH 1367/1476] mm/page_io.c: use blk_io_schedule() for avoiding task hung in sync io swap_readpage() does the sync io for one page, the io is not big, normally, the io can be finished quickly, but it may take long time or wait forever in case of io failure or discard. This patch uses blk_io_schedule() instead of io_schedule() to avoid task hung and crash (when set /proc/sys/kernel/hung_task_panic) when the above exception occurs. This is similar to the hung task avoidance in submit_bio_wait(), blk_execute_rq() and __blkdev_direct_IO(). Signed-off-by: Xianting Tian Signed-off-by: Andrew Morton Reviewed-by: Andrew Morton Cc: Ming Lei Cc: Bart Van Assche Cc: Hannes Reinecke Cc: Jens Axboe Cc: Hugh Dickins Link: http://lkml.kernel.org/r/1596461807-21087-1-git-send-email-xianting_tian@126.com Signed-off-by: Linus Torvalds --- mm/page_io.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mm/page_io.c b/mm/page_io.c index ccda767900885..9e362567d454c 100644 --- a/mm/page_io.c +++ b/mm/page_io.c @@ -441,7 +441,7 @@ int swap_readpage(struct page *page, bool synchronous) break; if (!blk_poll(disk->queue, qc, true)) - io_schedule(); + blk_io_schedule(); } __set_current_state(TASK_RUNNING); bio_put(bio); -- GitLab From e809d5f0b5c912fe981dce738f3283b2010665f0 Mon Sep 17 00:00:00 2001 From: Chris Down Date: Thu, 6 Aug 2020 23:20:20 -0700 Subject: [PATCH 1368/1476] tmpfs: per-superblock i_ino support Patch series "tmpfs: inode: Reduce risk of inum overflow", v7. In Facebook production we are seeing heavy i_ino wraparounds on tmpfs. On affected tiers, in excess of 10% of hosts show multiple files with different content and the same inode number, with some servers even having as many as 150 duplicated inode numbers with differing file content. This causes actual, tangible problems in production. For example, we have complaints from those working on remote caches that their application is reporting cache corruptions because it uses (device, inodenum) to establish the identity of a particular cache object, but because it's not unique any more, the application refuses to continue and reports cache corruption. Even worse, sometimes applications may not even detect the corruption but may continue anyway, causing phantom and hard to debug behaviour. In general, userspace applications expect that (device, inodenum) should be enough to be uniquely point to one inode, which seems fair enough. One might also need to check the generation, but in this case: 1. That's not currently exposed to userspace (ioctl(...FS_IOC_GETVERSION...) returns ENOTTY on tmpfs); 2. Even with generation, there shouldn't be two live inodes with the same inode number on one device. In order to mitigate this, we take a two-pronged approach: 1. Moving inum generation from being global to per-sb for tmpfs. This itself allows some reduction in i_ino churn. This works on both 64- and 32- bit machines. 2. Adding inode{64,32} for tmpfs. This fix is supported on machines with 64-bit ino_t only: we allow users to mount tmpfs with a new inode64 option that uses the full width of ino_t, or CONFIG_TMPFS_INODE64. You can see how this compares to previous related patches which didn't implement this per-superblock: - https://patchwork.kernel.org/patch/11254001/ - https://patchwork.kernel.org/patch/11023915/ This patch (of 2): get_next_ino has a number of problems: - It uses and returns a uint, which is susceptible to become overflowed if a lot of volatile inodes that use get_next_ino are created. - It's global, with no specificity per-sb or even per-filesystem. This means it's not that difficult to cause inode number wraparounds on a single device, which can result in having multiple distinct inodes with the same inode number. This patch adds a per-superblock counter that mitigates the second case. This design also allows us to later have a specific i_ino size per-device, for example, allowing users to choose whether to use 32- or 64-bit inodes for each tmpfs mount. This is implemented in the next commit. For internal shmem mounts which may be less tolerant to spinlock delays, we implement a percpu batching scheme which only takes the stat_lock at each batch boundary. Signed-off-by: Chris Down Signed-off-by: Andrew Morton Acked-by: Hugh Dickins Cc: Amir Goldstein Cc: Al Viro Cc: Matthew Wilcox Cc: Jeff Layton Cc: Johannes Weiner Cc: Tejun Heo Link: http://lkml.kernel.org/r/cover.1594661218.git.chris@chrisdown.name Link: http://lkml.kernel.org/r/1986b9d63b986f08ec07a4aa4b2275e718e47d8a.1594661218.git.chris@chrisdown.name Signed-off-by: Linus Torvalds --- include/linux/fs.h | 15 +++++++++ include/linux/shmem_fs.h | 2 ++ mm/shmem.c | 66 +++++++++++++++++++++++++++++++++++++--- 3 files changed, 78 insertions(+), 5 deletions(-) diff --git a/include/linux/fs.h b/include/linux/fs.h index 488c3ef93601c..b1c3a14f12e82 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -2946,6 +2946,21 @@ extern void discard_new_inode(struct inode *); extern unsigned int get_next_ino(void); extern void evict_inodes(struct super_block *sb); +/* + * Userspace may rely on the the inode number being non-zero. For example, glibc + * simply ignores files with zero i_ino in unlink() and other places. + * + * As an additional complication, if userspace was compiled with + * _FILE_OFFSET_BITS=32 on a 64-bit kernel we'll only end up reading out the + * lower 32 bits, so we need to check that those aren't zero explicitly. With + * _FILE_OFFSET_BITS=64, this may cause some harmless false-negatives, but + * better safe than sorry. + */ +static inline bool is_zero_ino(ino_t ino) +{ + return (u32)ino == 0; +} + extern void __iget(struct inode * inode); extern void iget_failed(struct inode *); extern void clear_inode(struct inode *); diff --git a/include/linux/shmem_fs.h b/include/linux/shmem_fs.h index 7a35a69012215..eb628696ec665 100644 --- a/include/linux/shmem_fs.h +++ b/include/linux/shmem_fs.h @@ -36,6 +36,8 @@ struct shmem_sb_info { unsigned char huge; /* Whether to try for hugepages */ kuid_t uid; /* Mount uid for root directory */ kgid_t gid; /* Mount gid for root directory */ + ino_t next_ino; /* The next per-sb inode number to use */ + ino_t __percpu *ino_batch; /* The next per-cpu inode number to use */ struct mempolicy *mpol; /* default memory policy for mappings */ spinlock_t shrinklist_lock; /* Protects shrinklist */ struct list_head shrinklist; /* List of shinkable inodes */ diff --git a/mm/shmem.c b/mm/shmem.c index b2abca3f7f33f..585a82d87a92f 100644 --- a/mm/shmem.c +++ b/mm/shmem.c @@ -260,18 +260,67 @@ bool vma_is_shmem(struct vm_area_struct *vma) static LIST_HEAD(shmem_swaplist); static DEFINE_MUTEX(shmem_swaplist_mutex); -static int shmem_reserve_inode(struct super_block *sb) +/* + * shmem_reserve_inode() performs bookkeeping to reserve a shmem inode, and + * produces a novel ino for the newly allocated inode. + * + * It may also be called when making a hard link to permit the space needed by + * each dentry. However, in that case, no new inode number is needed since that + * internally draws from another pool of inode numbers (currently global + * get_next_ino()). This case is indicated by passing NULL as inop. + */ +#define SHMEM_INO_BATCH 1024 +static int shmem_reserve_inode(struct super_block *sb, ino_t *inop) { struct shmem_sb_info *sbinfo = SHMEM_SB(sb); - if (sbinfo->max_inodes) { + ino_t ino; + + if (!(sb->s_flags & SB_KERNMOUNT)) { spin_lock(&sbinfo->stat_lock); if (!sbinfo->free_inodes) { spin_unlock(&sbinfo->stat_lock); return -ENOSPC; } sbinfo->free_inodes--; + if (inop) { + ino = sbinfo->next_ino++; + if (unlikely(is_zero_ino(ino))) + ino = sbinfo->next_ino++; + if (unlikely(ino > UINT_MAX)) { + /* + * Emulate get_next_ino uint wraparound for + * compatibility + */ + ino = 1; + } + *inop = ino; + } spin_unlock(&sbinfo->stat_lock); + } else if (inop) { + /* + * __shmem_file_setup, one of our callers, is lock-free: it + * doesn't hold stat_lock in shmem_reserve_inode since + * max_inodes is always 0, and is called from potentially + * unknown contexts. As such, use a per-cpu batched allocator + * which doesn't require the per-sb stat_lock unless we are at + * the batch boundary. + */ + ino_t *next_ino; + next_ino = per_cpu_ptr(sbinfo->ino_batch, get_cpu()); + ino = *next_ino; + if (unlikely(ino % SHMEM_INO_BATCH == 0)) { + spin_lock(&sbinfo->stat_lock); + ino = sbinfo->next_ino; + sbinfo->next_ino += SHMEM_INO_BATCH; + spin_unlock(&sbinfo->stat_lock); + if (unlikely(is_zero_ino(ino))) + ino++; + } + *inop = ino; + *next_ino = ++ino; + put_cpu(); } + return 0; } @@ -2222,13 +2271,14 @@ static struct inode *shmem_get_inode(struct super_block *sb, const struct inode struct inode *inode; struct shmem_inode_info *info; struct shmem_sb_info *sbinfo = SHMEM_SB(sb); + ino_t ino; - if (shmem_reserve_inode(sb)) + if (shmem_reserve_inode(sb, &ino)) return NULL; inode = new_inode(sb); if (inode) { - inode->i_ino = get_next_ino(); + inode->i_ino = ino; inode_init_owner(inode, dir, mode); inode->i_blocks = 0; inode->i_atime = inode->i_mtime = inode->i_ctime = current_time(inode); @@ -2932,7 +2982,7 @@ static int shmem_link(struct dentry *old_dentry, struct inode *dir, struct dentr * first link must skip that, to get the accounting right. */ if (inode->i_nlink) { - ret = shmem_reserve_inode(inode->i_sb); + ret = shmem_reserve_inode(inode->i_sb, NULL); if (ret) goto out; } @@ -3584,6 +3634,7 @@ static void shmem_put_super(struct super_block *sb) { struct shmem_sb_info *sbinfo = SHMEM_SB(sb); + free_percpu(sbinfo->ino_batch); percpu_counter_destroy(&sbinfo->used_blocks); mpol_put(sbinfo->mpol); kfree(sbinfo); @@ -3626,6 +3677,11 @@ static int shmem_fill_super(struct super_block *sb, struct fs_context *fc) #endif sbinfo->max_blocks = ctx->blocks; sbinfo->free_inodes = sbinfo->max_inodes = ctx->inodes; + if (sb->s_flags & SB_KERNMOUNT) { + sbinfo->ino_batch = alloc_percpu(ino_t); + if (!sbinfo->ino_batch) + goto failed; + } sbinfo->uid = ctx->uid; sbinfo->gid = ctx->gid; sbinfo->mode = ctx->mode; -- GitLab From ea3271f7196c65ae5d3e1c7b3f733892c017dbd6 Mon Sep 17 00:00:00 2001 From: Chris Down Date: Thu, 6 Aug 2020 23:20:25 -0700 Subject: [PATCH 1369/1476] tmpfs: support 64-bit inums per-sb The default is still set to inode32 for backwards compatibility, but system administrators can opt in to the new 64-bit inode numbers by either: 1. Passing inode64 on the command line when mounting, or 2. Configuring the kernel with CONFIG_TMPFS_INODE64=y The inode64 and inode32 names are used based on existing precedent from XFS. [hughd@google.com: Kconfig fixes] Link: http://lkml.kernel.org/r/alpine.LSU.2.11.2008011928010.13320@eggly.anvils Signed-off-by: Chris Down Signed-off-by: Hugh Dickins Signed-off-by: Andrew Morton Reviewed-by: Amir Goldstein Acked-by: Hugh Dickins Cc: Al Viro Cc: Matthew Wilcox Cc: Jeff Layton Cc: Johannes Weiner Cc: Tejun Heo Link: http://lkml.kernel.org/r/8b23758d0c66b5e2263e08baf9c4b6a7565cbd8f.1594661218.git.chris@chrisdown.name Signed-off-by: Linus Torvalds --- Documentation/filesystems/tmpfs.rst | 18 ++++++++ fs/Kconfig | 21 ++++++++++ include/linux/shmem_fs.h | 1 + mm/shmem.c | 65 ++++++++++++++++++++++++++++- 4 files changed, 103 insertions(+), 2 deletions(-) diff --git a/Documentation/filesystems/tmpfs.rst b/Documentation/filesystems/tmpfs.rst index 4e95929301a5b..c44f8b1d3cabf 100644 --- a/Documentation/filesystems/tmpfs.rst +++ b/Documentation/filesystems/tmpfs.rst @@ -150,6 +150,22 @@ These options do not have any effect on remount. You can change these parameters with chmod(1), chown(1) and chgrp(1) on a mounted filesystem. +tmpfs has a mount option to select whether it will wrap at 32- or 64-bit inode +numbers: + +======= ======================== +inode64 Use 64-bit inode numbers +inode32 Use 32-bit inode numbers +======= ======================== + +On a 32-bit kernel, inode32 is implicit, and inode64 is refused at mount time. +On a 64-bit kernel, CONFIG_TMPFS_INODE64 sets the default. inode64 avoids the +possibility of multiple files with the same inode number on a single device; +but risks glibc failing with EOVERFLOW once 33-bit inode numbers are reached - +if a long-lived tmpfs is accessed by 32-bit applications so ancient that +opening a file larger than 2GiB fails with EINVAL. + + So 'mount -t tmpfs -o size=10G,nr_inodes=10k,mode=700 tmpfs /mytmpfs' will give you tmpfs instance on /mytmpfs which can allocate 10GB RAM/SWAP in 10240 inodes and it is only accessible by root. @@ -161,3 +177,5 @@ RAM/SWAP in 10240 inodes and it is only accessible by root. Hugh Dickins, 4 June 2007 :Updated: KOSAKI Motohiro, 16 Mar 2010 +:Updated: + Chris Down, 13 July 2020 diff --git a/fs/Kconfig b/fs/Kconfig index a88aa3af73c11..aa4c122823018 100644 --- a/fs/Kconfig +++ b/fs/Kconfig @@ -201,6 +201,27 @@ config TMPFS_XATTR If unsure, say N. +config TMPFS_INODE64 + bool "Use 64-bit ino_t by default in tmpfs" + depends on TMPFS && 64BIT + default n + help + tmpfs has historically used only inode numbers as wide as an unsigned + int. In some cases this can cause wraparound, potentially resulting + in multiple files with the same inode number on a single device. This + option makes tmpfs use the full width of ino_t by default, without + needing to specify the inode64 option when mounting. + + But if a long-lived tmpfs is to be accessed by 32-bit applications so + ancient that opening a file larger than 2GiB fails with EINVAL, then + the INODE64 config option and inode64 mount option risk operations + failing with EOVERFLOW once 33-bit inode numbers are reached. + + To override this configured default, use the inode32 or inode64 + option when mounting. + + If unsure, say N. + config HUGETLBFS bool "HugeTLB file system support" depends on X86 || IA64 || SPARC64 || (S390 && 64BIT) || \ diff --git a/include/linux/shmem_fs.h b/include/linux/shmem_fs.h index eb628696ec665..a5a5d1d4d7b1d 100644 --- a/include/linux/shmem_fs.h +++ b/include/linux/shmem_fs.h @@ -36,6 +36,7 @@ struct shmem_sb_info { unsigned char huge; /* Whether to try for hugepages */ kuid_t uid; /* Mount uid for root directory */ kgid_t gid; /* Mount gid for root directory */ + bool full_inums; /* If i_ino should be uint or ino_t */ ino_t next_ino; /* The next per-sb inode number to use */ ino_t __percpu *ino_batch; /* The next per-cpu inode number to use */ struct mempolicy *mpol; /* default memory policy for mappings */ diff --git a/mm/shmem.c b/mm/shmem.c index 585a82d87a92f..c5c281893bb8c 100644 --- a/mm/shmem.c +++ b/mm/shmem.c @@ -114,11 +114,13 @@ struct shmem_options { kuid_t uid; kgid_t gid; umode_t mode; + bool full_inums; int huge; int seen; #define SHMEM_SEEN_BLOCKS 1 #define SHMEM_SEEN_INODES 2 #define SHMEM_SEEN_HUGE 4 +#define SHMEM_SEEN_INUMS 8 }; #ifdef CONFIG_TMPFS @@ -286,12 +288,17 @@ static int shmem_reserve_inode(struct super_block *sb, ino_t *inop) ino = sbinfo->next_ino++; if (unlikely(is_zero_ino(ino))) ino = sbinfo->next_ino++; - if (unlikely(ino > UINT_MAX)) { + if (unlikely(!sbinfo->full_inums && + ino > UINT_MAX)) { /* * Emulate get_next_ino uint wraparound for * compatibility */ - ino = 1; + if (IS_ENABLED(CONFIG_64BIT)) + pr_warn("%s: inode number overflow on device %d, consider using inode64 mount option\n", + __func__, MINOR(sb->s_dev)); + sbinfo->next_ino = 1; + ino = sbinfo->next_ino++; } *inop = ino; } @@ -304,6 +311,10 @@ static int shmem_reserve_inode(struct super_block *sb, ino_t *inop) * unknown contexts. As such, use a per-cpu batched allocator * which doesn't require the per-sb stat_lock unless we are at * the batch boundary. + * + * We don't need to worry about inode{32,64} since SB_KERNMOUNT + * shmem mounts are not exposed to userspace, so we don't need + * to worry about things like glibc compatibility. */ ino_t *next_ino; next_ino = per_cpu_ptr(sbinfo->ino_batch, get_cpu()); @@ -3397,6 +3408,8 @@ enum shmem_param { Opt_nr_inodes, Opt_size, Opt_uid, + Opt_inode32, + Opt_inode64, }; static const struct constant_table shmem_param_enums_huge[] = { @@ -3416,6 +3429,8 @@ const struct fs_parameter_spec shmem_fs_parameters[] = { fsparam_string("nr_inodes", Opt_nr_inodes), fsparam_string("size", Opt_size), fsparam_u32 ("uid", Opt_uid), + fsparam_flag ("inode32", Opt_inode32), + fsparam_flag ("inode64", Opt_inode64), {} }; @@ -3487,6 +3502,18 @@ static int shmem_parse_one(struct fs_context *fc, struct fs_parameter *param) break; } goto unsupported_parameter; + case Opt_inode32: + ctx->full_inums = false; + ctx->seen |= SHMEM_SEEN_INUMS; + break; + case Opt_inode64: + if (sizeof(ino_t) < 8) { + return invalfc(fc, + "Cannot use inode64 with <64bit inums in kernel\n"); + } + ctx->full_inums = true; + ctx->seen |= SHMEM_SEEN_INUMS; + break; } return 0; @@ -3578,8 +3605,16 @@ static int shmem_reconfigure(struct fs_context *fc) } } + if ((ctx->seen & SHMEM_SEEN_INUMS) && !ctx->full_inums && + sbinfo->next_ino > UINT_MAX) { + err = "Current inum too high to switch to 32-bit inums"; + goto out; + } + if (ctx->seen & SHMEM_SEEN_HUGE) sbinfo->huge = ctx->huge; + if (ctx->seen & SHMEM_SEEN_INUMS) + sbinfo->full_inums = ctx->full_inums; if (ctx->seen & SHMEM_SEEN_BLOCKS) sbinfo->max_blocks = ctx->blocks; if (ctx->seen & SHMEM_SEEN_INODES) { @@ -3619,6 +3654,29 @@ static int shmem_show_options(struct seq_file *seq, struct dentry *root) if (!gid_eq(sbinfo->gid, GLOBAL_ROOT_GID)) seq_printf(seq, ",gid=%u", from_kgid_munged(&init_user_ns, sbinfo->gid)); + + /* + * Showing inode{64,32} might be useful even if it's the system default, + * since then people don't have to resort to checking both here and + * /proc/config.gz to confirm 64-bit inums were successfully applied + * (which may not even exist if IKCONFIG_PROC isn't enabled). + * + * We hide it when inode64 isn't the default and we are using 32-bit + * inodes, since that probably just means the feature isn't even under + * consideration. + * + * As such: + * + * +-----------------+-----------------+ + * | TMPFS_INODE64=y | TMPFS_INODE64=n | + * +------------------+-----------------+-----------------+ + * | full_inums=true | show | show | + * | full_inums=false | show | hide | + * +------------------+-----------------+-----------------+ + * + */ + if (IS_ENABLED(CONFIG_TMPFS_INODE64) || sbinfo->full_inums) + seq_printf(seq, ",inode%d", (sbinfo->full_inums ? 64 : 32)); #ifdef CONFIG_TRANSPARENT_HUGEPAGE /* Rightly or wrongly, show huge mount option unmasked by shmem_huge */ if (sbinfo->huge) @@ -3667,6 +3725,8 @@ static int shmem_fill_super(struct super_block *sb, struct fs_context *fc) ctx->blocks = shmem_default_max_blocks(); if (!(ctx->seen & SHMEM_SEEN_INODES)) ctx->inodes = shmem_default_max_inodes(); + if (!(ctx->seen & SHMEM_SEEN_INUMS)) + ctx->full_inums = IS_ENABLED(CONFIG_TMPFS_INODE64); } else { sb->s_flags |= SB_NOUSER; } @@ -3684,6 +3744,7 @@ static int shmem_fill_super(struct super_block *sb, struct fs_context *fc) } sbinfo->uid = ctx->uid; sbinfo->gid = ctx->gid; + sbinfo->full_inums = ctx->full_inums; sbinfo->mode = ctx->mode; sbinfo->huge = ctx->huge; sbinfo->mpol = ctx->mpol; -- GitLab From d648bcc7fe65f09ecd19091f68395dfb3b7a87c8 Mon Sep 17 00:00:00 2001 From: Roman Gushchin Date: Thu, 6 Aug 2020 23:20:28 -0700 Subject: [PATCH 1370/1476] mm: kmem: make memcg_kmem_enabled() irreversible Historically the kernel memory accounting was an opt-in feature, which could be enabled for individual cgroups. But now it's not true, and it's on by default both on cgroup v1 and cgroup v2. And as long as a user has at least one non-root memory cgroup, the kernel memory accounting is on. So in most setups it's either always on (if memory cgroups are in use and kmem accounting is not disabled), either always off (otherwise). memcg_kmem_enabled() is used in many places to guard the kernel memory accounting code. If memcg_kmem_enabled() can reverse from returning true to returning false (as now), we can't rely on it on release paths and have to check if it was on before. If we'll make memcg_kmem_enabled() irreversible (always returning true after returning it for the first time), it'll make the general logic more simple and robust. It also will allow to guard some checks which otherwise would stay unguarded. Reported-by: Naresh Kamboju Signed-off-by: Roman Gushchin Signed-off-by: Andrew Morton Tested-by: Naresh Kamboju Reviewed-by: Shakeel Butt Acked-by: Vlastimil Babka Acked-by: Michal Hocko Link: http://lkml.kernel.org/r/20200702180926.1330769-1-guro@fb.com Signed-off-by: Linus Torvalds --- mm/memcontrol.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/mm/memcontrol.c b/mm/memcontrol.c index 8cc617ede7e2e..24892a14cc75c 100644 --- a/mm/memcontrol.c +++ b/mm/memcontrol.c @@ -3416,7 +3416,8 @@ static int memcg_online_kmem(struct mem_cgroup *memcg) if (memcg_id < 0) return memcg_id; - static_branch_inc(&memcg_kmem_enabled_key); + static_branch_enable(&memcg_kmem_enabled_key); + /* * A memory cgroup is considered kmem-online as soon as it gets * kmemcg_id. Setting the id after enabling static branching will @@ -3486,11 +3487,6 @@ static void memcg_free_kmem(struct mem_cgroup *memcg) /* css_alloc() failed, offlining didn't happen */ if (unlikely(memcg->kmem_state == KMEM_ONLINE)) memcg_offline_kmem(memcg); - - if (memcg->kmem_state == KMEM_ALLOCATED) { - WARN_ON(!list_empty(&memcg->kmem_caches)); - static_branch_dec(&memcg_kmem_enabled_key); - } } #else static int memcg_online_kmem(struct mem_cgroup *memcg) -- GitLab From eedc4e5a142cc33fbb54f8d72b929a0e123c48c4 Mon Sep 17 00:00:00 2001 From: Roman Gushchin Date: Thu, 6 Aug 2020 23:20:32 -0700 Subject: [PATCH 1371/1476] mm: memcg: factor out memcg- and lruvec-level changes out of __mod_lruvec_state() Patch series "The new cgroup slab memory controller", v7. The patchset moves the accounting from the page level to the object level. It allows to share slab pages between memory cgroups. This leads to a significant win in the slab utilization (up to 45%) and the corresponding drop in the total kernel memory footprint. The reduced number of unmovable slab pages should also have a positive effect on the memory fragmentation. The patchset makes the slab accounting code simpler: there is no more need in the complicated dynamic creation and destruction of per-cgroup slab caches, all memory cgroups use a global set of shared slab caches. The lifetime of slab caches is not more connected to the lifetime of memory cgroups. The more precise accounting does require more CPU, however in practice the difference seems to be negligible. We've been using the new slab controller in Facebook production for several months with different workloads and haven't seen any noticeable regressions. What we've seen were memory savings in order of 1 GB per host (it varied heavily depending on the actual workload, size of RAM, number of CPUs, memory pressure, etc). The third version of the patchset added yet another step towards the simplification of the code: sharing of slab caches between accounted and non-accounted allocations. It comes with significant upsides (most noticeable, a complete elimination of dynamic slab caches creation) but not without some regression risks, so this change sits on top of the patchset and is not completely merged in. So in the unlikely event of a noticeable performance regression it can be reverted separately. The slab memory accounting works in exactly the same way for SLAB and SLUB. With both allocators the new controller shows significant memory savings, with SLUB the difference is bigger. On my 16-core desktop machine running Fedora 32 the size of the slab memory measured after the start of the system was lower by 58% and 38% with SLUB and SLAB correspondingly. As an estimation of a potential CPU overhead, below are results of slab_bulk_test01 test, kindly provided by Jesper D. Brouer. He also helped with the evaluation of results. The test can be found here: https://github.com/netoptimizer/prototype-kernel/ The smallest number in each row should be picked for a comparison. SLUB-patched - bulk-API - SLUB-patched : bulk_quick_reuse objects=1 : 187 - 90 - 224 cycles(tsc) - SLUB-patched : bulk_quick_reuse objects=2 : 110 - 53 - 133 cycles(tsc) - SLUB-patched : bulk_quick_reuse objects=3 : 88 - 95 - 42 cycles(tsc) - SLUB-patched : bulk_quick_reuse objects=4 : 91 - 85 - 36 cycles(tsc) - SLUB-patched : bulk_quick_reuse objects=8 : 32 - 66 - 32 cycles(tsc) SLUB-original - bulk-API - SLUB-original: bulk_quick_reuse objects=1 : 87 - 87 - 142 cycles(tsc) - SLUB-original: bulk_quick_reuse objects=2 : 52 - 53 - 53 cycles(tsc) - SLUB-original: bulk_quick_reuse objects=3 : 42 - 42 - 91 cycles(tsc) - SLUB-original: bulk_quick_reuse objects=4 : 91 - 37 - 37 cycles(tsc) - SLUB-original: bulk_quick_reuse objects=8 : 31 - 79 - 76 cycles(tsc) SLAB-patched - bulk-API - SLAB-patched : bulk_quick_reuse objects=1 : 67 - 67 - 140 cycles(tsc) - SLAB-patched : bulk_quick_reuse objects=2 : 55 - 46 - 46 cycles(tsc) - SLAB-patched : bulk_quick_reuse objects=3 : 93 - 94 - 39 cycles(tsc) - SLAB-patched : bulk_quick_reuse objects=4 : 35 - 88 - 85 cycles(tsc) - SLAB-patched : bulk_quick_reuse objects=8 : 30 - 30 - 30 cycles(tsc) SLAB-original- bulk-API - SLAB-original: bulk_quick_reuse objects=1 : 143 - 136 - 67 cycles(tsc) - SLAB-original: bulk_quick_reuse objects=2 : 45 - 46 - 46 cycles(tsc) - SLAB-original: bulk_quick_reuse objects=3 : 38 - 39 - 39 cycles(tsc) - SLAB-original: bulk_quick_reuse objects=4 : 35 - 87 - 87 cycles(tsc) - SLAB-original: bulk_quick_reuse objects=8 : 29 - 66 - 30 cycles(tsc) This patch (of 19): To convert memcg and lruvec slab counters to bytes there must be a way to change these counters without touching node counters. Factor out __mod_memcg_lruvec_state() out of __mod_lruvec_state(). Signed-off-by: Roman Gushchin Signed-off-by: Andrew Morton Reviewed-by: Vlastimil Babka Reviewed-by: Shakeel Butt Acked-by: Johannes Weiner Cc: Christoph Lameter Cc: Michal Hocko Cc: Tejun Heo Link: http://lkml.kernel.org/r/20200623174037.3951353-1-guro@fb.com Link: http://lkml.kernel.org/r/20200623174037.3951353-2-guro@fb.com Signed-off-by: Linus Torvalds --- include/linux/memcontrol.h | 17 +++++++++++++++ mm/memcontrol.c | 43 +++++++++++++++++++++----------------- 2 files changed, 41 insertions(+), 19 deletions(-) diff --git a/include/linux/memcontrol.h b/include/linux/memcontrol.h index e77197a62809f..b250f8197710c 100644 --- a/include/linux/memcontrol.h +++ b/include/linux/memcontrol.h @@ -679,11 +679,23 @@ static inline unsigned long lruvec_page_state_local(struct lruvec *lruvec, return x; } +void __mod_memcg_lruvec_state(struct lruvec *lruvec, enum node_stat_item idx, + int val); void __mod_lruvec_state(struct lruvec *lruvec, enum node_stat_item idx, int val); void __mod_lruvec_slab_state(void *p, enum node_stat_item idx, int val); void mod_memcg_obj_state(void *p, int idx, int val); +static inline void mod_memcg_lruvec_state(struct lruvec *lruvec, + enum node_stat_item idx, int val) +{ + unsigned long flags; + + local_irq_save(flags); + __mod_memcg_lruvec_state(lruvec, idx, val); + local_irq_restore(flags); +} + static inline void mod_lruvec_state(struct lruvec *lruvec, enum node_stat_item idx, int val) { @@ -1057,6 +1069,11 @@ static inline unsigned long lruvec_page_state_local(struct lruvec *lruvec, return node_page_state(lruvec_pgdat(lruvec), idx); } +static inline void __mod_memcg_lruvec_state(struct lruvec *lruvec, + enum node_stat_item idx, int val) +{ +} + static inline void __mod_lruvec_state(struct lruvec *lruvec, enum node_stat_item idx, int val) { diff --git a/mm/memcontrol.c b/mm/memcontrol.c index 24892a14cc75c..5863ceb310fbf 100644 --- a/mm/memcontrol.c +++ b/mm/memcontrol.c @@ -713,30 +713,13 @@ parent_nodeinfo(struct mem_cgroup_per_node *pn, int nid) return mem_cgroup_nodeinfo(parent, nid); } -/** - * __mod_lruvec_state - update lruvec memory statistics - * @lruvec: the lruvec - * @idx: the stat item - * @val: delta to add to the counter, can be negative - * - * The lruvec is the intersection of the NUMA node and a cgroup. This - * function updates the all three counters that are affected by a - * change of state at this level: per-node, per-cgroup, per-lruvec. - */ -void __mod_lruvec_state(struct lruvec *lruvec, enum node_stat_item idx, - int val) +void __mod_memcg_lruvec_state(struct lruvec *lruvec, enum node_stat_item idx, + int val) { - pg_data_t *pgdat = lruvec_pgdat(lruvec); struct mem_cgroup_per_node *pn; struct mem_cgroup *memcg; long x; - /* Update node */ - __mod_node_page_state(pgdat, idx, val); - - if (mem_cgroup_disabled()) - return; - pn = container_of(lruvec, struct mem_cgroup_per_node, lruvec); memcg = pn->memcg; @@ -748,6 +731,7 @@ void __mod_lruvec_state(struct lruvec *lruvec, enum node_stat_item idx, x = val + __this_cpu_read(pn->lruvec_stat_cpu->count[idx]); if (unlikely(abs(x) > MEMCG_CHARGE_BATCH)) { + pg_data_t *pgdat = lruvec_pgdat(lruvec); struct mem_cgroup_per_node *pi; for (pi = pn; pi; pi = parent_nodeinfo(pi, pgdat->node_id)) @@ -757,6 +741,27 @@ void __mod_lruvec_state(struct lruvec *lruvec, enum node_stat_item idx, __this_cpu_write(pn->lruvec_stat_cpu->count[idx], x); } +/** + * __mod_lruvec_state - update lruvec memory statistics + * @lruvec: the lruvec + * @idx: the stat item + * @val: delta to add to the counter, can be negative + * + * The lruvec is the intersection of the NUMA node and a cgroup. This + * function updates the all three counters that are affected by a + * change of state at this level: per-node, per-cgroup, per-lruvec. + */ +void __mod_lruvec_state(struct lruvec *lruvec, enum node_stat_item idx, + int val) +{ + /* Update node */ + __mod_node_page_state(lruvec_pgdat(lruvec), idx, val); + + /* Update memcg and lruvec */ + if (!mem_cgroup_disabled()) + __mod_memcg_lruvec_state(lruvec, idx, val); +} + void __mod_lruvec_slab_state(void *p, enum node_stat_item idx, int val) { pg_data_t *pgdat = page_pgdat(virt_to_page(p)); -- GitLab From ea426c2a7de8e575108b7cecd3374e0c15a9f25e Mon Sep 17 00:00:00 2001 From: Roman Gushchin Date: Thu, 6 Aug 2020 23:20:35 -0700 Subject: [PATCH 1372/1476] mm: memcg: prepare for byte-sized vmstat items To implement per-object slab memory accounting, we need to convert slab vmstat counters to bytes. Actually, out of 4 levels of counters: global, per-node, per-memcg and per-lruvec only two last levels will require byte-sized counters. It's because global and per-node counters will be counting the number of slab pages, and per-memcg and per-lruvec will be counting the amount of memory taken by charged slab objects. Converting all vmstat counters to bytes or even all slab counters to bytes would introduce an additional overhead. So instead let's store global and per-node counters in pages, and memcg and lruvec counters in bytes. To make the API clean all access helpers (both on the read and write sides) are dealing with bytes. To avoid back-and-forth conversions a new flavor of read-side helpers is introduced, which always returns values in pages: node_page_state_pages() and global_node_page_state_pages(). Actually new helpers are just reading raw values. Old helpers are simple wrappers, which will complain on an attempt to read byte value, because at the moment no one actually needs bytes. Thanks to Johannes Weiner for the idea of having the byte-sized API on top of the page-sized internal storage. Signed-off-by: Roman Gushchin Signed-off-by: Andrew Morton Reviewed-by: Vlastimil Babka Reviewed-by: Shakeel Butt Acked-by: Johannes Weiner Cc: Christoph Lameter Cc: Michal Hocko Cc: Tejun Heo Link: http://lkml.kernel.org/r/20200623174037.3951353-3-guro@fb.com Signed-off-by: Linus Torvalds --- drivers/base/node.c | 2 +- include/linux/mmzone.h | 10 ++++++++++ include/linux/vmstat.h | 14 +++++++++++++- mm/memcontrol.c | 14 ++++++++++---- mm/vmstat.c | 30 ++++++++++++++++++++++++++---- 5 files changed, 60 insertions(+), 10 deletions(-) diff --git a/drivers/base/node.c b/drivers/base/node.c index 5b02f69769e86..e21e313592970 100644 --- a/drivers/base/node.c +++ b/drivers/base/node.c @@ -513,7 +513,7 @@ static ssize_t node_read_vmstat(struct device *dev, for (i = 0; i < NR_VM_NODE_STAT_ITEMS; i++) n += sprintf(buf+n, "%s %lu\n", node_stat_name(i), - node_page_state(pgdat, i)); + node_page_state_pages(pgdat, i)); return n; } diff --git a/include/linux/mmzone.h b/include/linux/mmzone.h index f6f884970511d..f16306e15b986 100644 --- a/include/linux/mmzone.h +++ b/include/linux/mmzone.h @@ -206,6 +206,16 @@ enum node_stat_item { NR_VM_NODE_STAT_ITEMS }; +/* + * Returns true if the value is measured in bytes (most vmstat values are + * measured in pages). This defines the API part, the internal representation + * might be different. + */ +static __always_inline bool vmstat_item_in_bytes(int idx) +{ + return false; +} + /* * We do arithmetic on the LRU lists in various places in the code, * so it is important to keep the active lists LRU_ACTIVE higher in diff --git a/include/linux/vmstat.h b/include/linux/vmstat.h index aa961088c5515..91220ace31da9 100644 --- a/include/linux/vmstat.h +++ b/include/linux/vmstat.h @@ -8,6 +8,7 @@ #include #include #include +#include extern int sysctl_stat_interval; @@ -192,7 +193,8 @@ static inline unsigned long global_zone_page_state(enum zone_stat_item item) return x; } -static inline unsigned long global_node_page_state(enum node_stat_item item) +static inline +unsigned long global_node_page_state_pages(enum node_stat_item item) { long x = atomic_long_read(&vm_node_stat[item]); #ifdef CONFIG_SMP @@ -202,6 +204,13 @@ static inline unsigned long global_node_page_state(enum node_stat_item item) return x; } +static inline unsigned long global_node_page_state(enum node_stat_item item) +{ + VM_WARN_ON_ONCE(vmstat_item_in_bytes(item)); + + return global_node_page_state_pages(item); +} + static inline unsigned long zone_page_state(struct zone *zone, enum zone_stat_item item) { @@ -242,9 +251,12 @@ extern unsigned long sum_zone_node_page_state(int node, extern unsigned long sum_zone_numa_state(int node, enum numa_stat_item item); extern unsigned long node_page_state(struct pglist_data *pgdat, enum node_stat_item item); +extern unsigned long node_page_state_pages(struct pglist_data *pgdat, + enum node_stat_item item); #else #define sum_zone_node_page_state(node, item) global_zone_page_state(item) #define node_page_state(node, item) global_node_page_state(item) +#define node_page_state_pages(node, item) global_node_page_state_pages(item) #endif /* CONFIG_NUMA */ #ifdef CONFIG_SMP diff --git a/mm/memcontrol.c b/mm/memcontrol.c index 5863ceb310fbf..61ae6658d59fa 100644 --- a/mm/memcontrol.c +++ b/mm/memcontrol.c @@ -681,13 +681,16 @@ mem_cgroup_largest_soft_limit_node(struct mem_cgroup_tree_per_node *mctz) */ void __mod_memcg_state(struct mem_cgroup *memcg, int idx, int val) { - long x; + long x, threshold = MEMCG_CHARGE_BATCH; if (mem_cgroup_disabled()) return; + if (vmstat_item_in_bytes(idx)) + threshold <<= PAGE_SHIFT; + x = val + __this_cpu_read(memcg->vmstats_percpu->stat[idx]); - if (unlikely(abs(x) > MEMCG_CHARGE_BATCH)) { + if (unlikely(abs(x) > threshold)) { struct mem_cgroup *mi; /* @@ -718,7 +721,7 @@ void __mod_memcg_lruvec_state(struct lruvec *lruvec, enum node_stat_item idx, { struct mem_cgroup_per_node *pn; struct mem_cgroup *memcg; - long x; + long x, threshold = MEMCG_CHARGE_BATCH; pn = container_of(lruvec, struct mem_cgroup_per_node, lruvec); memcg = pn->memcg; @@ -729,8 +732,11 @@ void __mod_memcg_lruvec_state(struct lruvec *lruvec, enum node_stat_item idx, /* Update lruvec */ __this_cpu_add(pn->lruvec_stat_local->count[idx], val); + if (vmstat_item_in_bytes(idx)) + threshold <<= PAGE_SHIFT; + x = val + __this_cpu_read(pn->lruvec_stat_cpu->count[idx]); - if (unlikely(abs(x) > MEMCG_CHARGE_BATCH)) { + if (unlikely(abs(x) > threshold)) { pg_data_t *pgdat = lruvec_pgdat(lruvec); struct mem_cgroup_per_node *pi; diff --git a/mm/vmstat.c b/mm/vmstat.c index 3fb23a21f6dd6..b171a76bfe833 100644 --- a/mm/vmstat.c +++ b/mm/vmstat.c @@ -341,6 +341,11 @@ void __mod_node_page_state(struct pglist_data *pgdat, enum node_stat_item item, long x; long t; + if (vmstat_item_in_bytes(item)) { + VM_WARN_ON_ONCE(delta & (PAGE_SIZE - 1)); + delta >>= PAGE_SHIFT; + } + x = delta + __this_cpu_read(*p); t = __this_cpu_read(pcp->stat_threshold); @@ -398,6 +403,8 @@ void __inc_node_state(struct pglist_data *pgdat, enum node_stat_item item) s8 __percpu *p = pcp->vm_node_stat_diff + item; s8 v, t; + VM_WARN_ON_ONCE(vmstat_item_in_bytes(item)); + v = __this_cpu_inc_return(*p); t = __this_cpu_read(pcp->stat_threshold); if (unlikely(v > t)) { @@ -442,6 +449,8 @@ void __dec_node_state(struct pglist_data *pgdat, enum node_stat_item item) s8 __percpu *p = pcp->vm_node_stat_diff + item; s8 v, t; + VM_WARN_ON_ONCE(vmstat_item_in_bytes(item)); + v = __this_cpu_dec_return(*p); t = __this_cpu_read(pcp->stat_threshold); if (unlikely(v < - t)) { @@ -541,6 +550,11 @@ static inline void mod_node_state(struct pglist_data *pgdat, s8 __percpu *p = pcp->vm_node_stat_diff + item; long o, n, t, z; + if (vmstat_item_in_bytes(item)) { + VM_WARN_ON_ONCE(delta & (PAGE_SIZE - 1)); + delta >>= PAGE_SHIFT; + } + do { z = 0; /* overflow to node counters */ @@ -989,8 +1003,8 @@ unsigned long sum_zone_numa_state(int node, /* * Determine the per node value of a stat item. */ -unsigned long node_page_state(struct pglist_data *pgdat, - enum node_stat_item item) +unsigned long node_page_state_pages(struct pglist_data *pgdat, + enum node_stat_item item) { long x = atomic_long_read(&pgdat->vm_stat[item]); #ifdef CONFIG_SMP @@ -999,6 +1013,14 @@ unsigned long node_page_state(struct pglist_data *pgdat, #endif return x; } + +unsigned long node_page_state(struct pglist_data *pgdat, + enum node_stat_item item) +{ + VM_WARN_ON_ONCE(vmstat_item_in_bytes(item)); + + return node_page_state_pages(pgdat, item); +} #endif #ifdef CONFIG_COMPACTION @@ -1577,7 +1599,7 @@ static void zoneinfo_show_print(struct seq_file *m, pg_data_t *pgdat, seq_printf(m, "\n per-node stats"); for (i = 0; i < NR_VM_NODE_STAT_ITEMS; i++) { seq_printf(m, "\n %-12s %lu", node_stat_name(i), - node_page_state(pgdat, i)); + node_page_state_pages(pgdat, i)); } } seq_printf(m, @@ -1698,7 +1720,7 @@ static void *vmstat_start(struct seq_file *m, loff_t *pos) #endif for (i = 0; i < NR_VM_NODE_STAT_ITEMS; i++) - v[i] = global_node_page_state(i); + v[i] = global_node_page_state_pages(i); v += NR_VM_NODE_STAT_ITEMS; global_dirty_limits(v + NR_DIRTY_BG_THRESHOLD, -- GitLab From d42f3245c7e299e017213fa028c319316bcdb7f4 Mon Sep 17 00:00:00 2001 From: Roman Gushchin Date: Thu, 6 Aug 2020 23:20:39 -0700 Subject: [PATCH 1373/1476] mm: memcg: convert vmstat slab counters to bytes In order to prepare for per-object slab memory accounting, convert NR_SLAB_RECLAIMABLE and NR_SLAB_UNRECLAIMABLE vmstat items to bytes. To make it obvious, rename them to NR_SLAB_RECLAIMABLE_B and NR_SLAB_UNRECLAIMABLE_B (similar to NR_KERNEL_STACK_KB). Internally global and per-node counters are stored in pages, however memcg and lruvec counters are stored in bytes. This scheme may look weird, but only for now. As soon as slab pages will be shared between multiple cgroups, global and node counters will reflect the total number of slab pages. However memcg and lruvec counters will be used for per-memcg slab memory tracking, which will take separate kernel objects in the account. Keeping global and node counters in pages helps to avoid additional overhead. The size of slab memory shouldn't exceed 4Gb on 32-bit machines, so it will fit into atomic_long_t we use for vmstats. Signed-off-by: Roman Gushchin Signed-off-by: Andrew Morton Reviewed-by: Shakeel Butt Acked-by: Johannes Weiner Acked-by: Vlastimil Babka Cc: Christoph Lameter Cc: Michal Hocko Cc: Tejun Heo Link: http://lkml.kernel.org/r/20200623174037.3951353-4-guro@fb.com Signed-off-by: Linus Torvalds --- drivers/base/node.c | 4 ++-- fs/proc/meminfo.c | 4 ++-- include/linux/mmzone.h | 16 +++++++++++++--- kernel/power/snapshot.c | 2 +- mm/memcontrol.c | 11 ++++------- mm/oom_kill.c | 2 +- mm/page_alloc.c | 8 ++++---- mm/slab.h | 15 ++++++++------- mm/slab_common.c | 4 ++-- mm/slob.c | 12 ++++++------ mm/slub.c | 8 ++++---- mm/vmscan.c | 3 ++- mm/workingset.c | 6 ++++-- 13 files changed, 53 insertions(+), 42 deletions(-) diff --git a/drivers/base/node.c b/drivers/base/node.c index e21e313592970..0cf13e31603c9 100644 --- a/drivers/base/node.c +++ b/drivers/base/node.c @@ -368,8 +368,8 @@ static ssize_t node_read_meminfo(struct device *dev, unsigned long sreclaimable, sunreclaimable; si_meminfo_node(&i, nid); - sreclaimable = node_page_state(pgdat, NR_SLAB_RECLAIMABLE); - sunreclaimable = node_page_state(pgdat, NR_SLAB_UNRECLAIMABLE); + sreclaimable = node_page_state_pages(pgdat, NR_SLAB_RECLAIMABLE_B); + sunreclaimable = node_page_state_pages(pgdat, NR_SLAB_UNRECLAIMABLE_B); n = sprintf(buf, "Node %d MemTotal: %8lu kB\n" "Node %d MemFree: %8lu kB\n" diff --git a/fs/proc/meminfo.c b/fs/proc/meminfo.c index e9a6841fc25bc..38ea95fd919a5 100644 --- a/fs/proc/meminfo.c +++ b/fs/proc/meminfo.c @@ -52,8 +52,8 @@ static int meminfo_proc_show(struct seq_file *m, void *v) pages[lru] = global_node_page_state(NR_LRU_BASE + lru); available = si_mem_available(); - sreclaimable = global_node_page_state(NR_SLAB_RECLAIMABLE); - sunreclaim = global_node_page_state(NR_SLAB_UNRECLAIMABLE); + sreclaimable = global_node_page_state_pages(NR_SLAB_RECLAIMABLE_B); + sunreclaim = global_node_page_state_pages(NR_SLAB_UNRECLAIMABLE_B); show_val_kb(m, "MemTotal: ", i.totalram); show_val_kb(m, "MemFree: ", i.freeram); diff --git a/include/linux/mmzone.h b/include/linux/mmzone.h index f16306e15b986..b79100edd2285 100644 --- a/include/linux/mmzone.h +++ b/include/linux/mmzone.h @@ -174,8 +174,8 @@ enum node_stat_item { NR_INACTIVE_FILE, /* " " " " " */ NR_ACTIVE_FILE, /* " " " " " */ NR_UNEVICTABLE, /* " " " " " */ - NR_SLAB_RECLAIMABLE, - NR_SLAB_UNRECLAIMABLE, + NR_SLAB_RECLAIMABLE_B, + NR_SLAB_UNRECLAIMABLE_B, NR_ISOLATED_ANON, /* Temporary isolated pages from anon lru */ NR_ISOLATED_FILE, /* Temporary isolated pages from file lru */ WORKINGSET_NODES, @@ -213,7 +213,17 @@ enum node_stat_item { */ static __always_inline bool vmstat_item_in_bytes(int idx) { - return false; + /* + * Global and per-node slab counters track slab pages. + * It's expected that changes are multiples of PAGE_SIZE. + * Internally values are stored in pages. + * + * Per-memcg and per-lruvec counters track memory, consumed + * by individual slab objects. These counters are actually + * byte-precise. + */ + return (idx == NR_SLAB_RECLAIMABLE_B || + idx == NR_SLAB_UNRECLAIMABLE_B); } /* diff --git a/kernel/power/snapshot.c b/kernel/power/snapshot.c index cef154261fe2f..d25749bce7cf8 100644 --- a/kernel/power/snapshot.c +++ b/kernel/power/snapshot.c @@ -1663,7 +1663,7 @@ static unsigned long minimum_image_size(unsigned long saveable) { unsigned long size; - size = global_node_page_state(NR_SLAB_RECLAIMABLE) + size = global_node_page_state_pages(NR_SLAB_RECLAIMABLE_B) + global_node_page_state(NR_ACTIVE_ANON) + global_node_page_state(NR_INACTIVE_ANON) + global_node_page_state(NR_ACTIVE_FILE) diff --git a/mm/memcontrol.c b/mm/memcontrol.c index 61ae6658d59fa..328b7e7bf9ab1 100644 --- a/mm/memcontrol.c +++ b/mm/memcontrol.c @@ -1391,9 +1391,8 @@ static char *memory_stat_format(struct mem_cgroup *memcg) (u64)memcg_page_state(memcg, MEMCG_KERNEL_STACK_KB) * 1024); seq_buf_printf(&s, "slab %llu\n", - (u64)(memcg_page_state(memcg, NR_SLAB_RECLAIMABLE) + - memcg_page_state(memcg, NR_SLAB_UNRECLAIMABLE)) * - PAGE_SIZE); + (u64)(memcg_page_state(memcg, NR_SLAB_RECLAIMABLE_B) + + memcg_page_state(memcg, NR_SLAB_UNRECLAIMABLE_B))); seq_buf_printf(&s, "sock %llu\n", (u64)memcg_page_state(memcg, MEMCG_SOCK) * PAGE_SIZE); @@ -1423,11 +1422,9 @@ static char *memory_stat_format(struct mem_cgroup *memcg) PAGE_SIZE); seq_buf_printf(&s, "slab_reclaimable %llu\n", - (u64)memcg_page_state(memcg, NR_SLAB_RECLAIMABLE) * - PAGE_SIZE); + (u64)memcg_page_state(memcg, NR_SLAB_RECLAIMABLE_B)); seq_buf_printf(&s, "slab_unreclaimable %llu\n", - (u64)memcg_page_state(memcg, NR_SLAB_UNRECLAIMABLE) * - PAGE_SIZE); + (u64)memcg_page_state(memcg, NR_SLAB_UNRECLAIMABLE_B)); /* Accumulated memory events */ diff --git a/mm/oom_kill.c b/mm/oom_kill.c index 6e94962893ee8..d30ce75f23fb7 100644 --- a/mm/oom_kill.c +++ b/mm/oom_kill.c @@ -184,7 +184,7 @@ static bool is_dump_unreclaim_slabs(void) global_node_page_state(NR_ISOLATED_FILE) + global_node_page_state(NR_UNEVICTABLE); - return (global_node_page_state(NR_SLAB_UNRECLAIMABLE) > nr_lru); + return (global_node_page_state_pages(NR_SLAB_UNRECLAIMABLE_B) > nr_lru); } /** diff --git a/mm/page_alloc.c b/mm/page_alloc.c index 901a21f61d68a..f9ad093814d25 100644 --- a/mm/page_alloc.c +++ b/mm/page_alloc.c @@ -5220,8 +5220,8 @@ long si_mem_available(void) * items that are in use, and cannot be freed. Cap this estimate at the * low watermark. */ - reclaimable = global_node_page_state(NR_SLAB_RECLAIMABLE) + - global_node_page_state(NR_KERNEL_MISC_RECLAIMABLE); + reclaimable = global_node_page_state_pages(NR_SLAB_RECLAIMABLE_B) + + global_node_page_state(NR_KERNEL_MISC_RECLAIMABLE); available += reclaimable - min(reclaimable / 2, wmark_low); if (available < 0) @@ -5364,8 +5364,8 @@ void show_free_areas(unsigned int filter, nodemask_t *nodemask) global_node_page_state(NR_UNEVICTABLE), global_node_page_state(NR_FILE_DIRTY), global_node_page_state(NR_WRITEBACK), - global_node_page_state(NR_SLAB_RECLAIMABLE), - global_node_page_state(NR_SLAB_UNRECLAIMABLE), + global_node_page_state_pages(NR_SLAB_RECLAIMABLE_B), + global_node_page_state_pages(NR_SLAB_UNRECLAIMABLE_B), global_node_page_state(NR_FILE_MAPPED), global_node_page_state(NR_SHMEM), global_zone_page_state(NR_PAGETABLE), diff --git a/mm/slab.h b/mm/slab.h index fceb4341ba910..09be3ca6fe878 100644 --- a/mm/slab.h +++ b/mm/slab.h @@ -273,7 +273,7 @@ int __kmem_cache_alloc_bulk(struct kmem_cache *, gfp_t, size_t, void **); static inline int cache_vmstat_idx(struct kmem_cache *s) { return (s->flags & SLAB_RECLAIM_ACCOUNT) ? - NR_SLAB_RECLAIMABLE : NR_SLAB_UNRECLAIMABLE; + NR_SLAB_RECLAIMABLE_B : NR_SLAB_UNRECLAIMABLE_B; } #ifdef CONFIG_SLUB_DEBUG @@ -390,7 +390,7 @@ static __always_inline int memcg_charge_slab(struct page *page, if (unlikely(!memcg || mem_cgroup_is_root(memcg))) { mod_node_page_state(page_pgdat(page), cache_vmstat_idx(s), - nr_pages); + nr_pages << PAGE_SHIFT); percpu_ref_get_many(&s->memcg_params.refcnt, nr_pages); return 0; } @@ -400,7 +400,7 @@ static __always_inline int memcg_charge_slab(struct page *page, goto out; lruvec = mem_cgroup_lruvec(memcg, page_pgdat(page)); - mod_lruvec_state(lruvec, cache_vmstat_idx(s), nr_pages); + mod_lruvec_state(lruvec, cache_vmstat_idx(s), nr_pages << PAGE_SHIFT); /* transer try_charge() page references to kmem_cache */ percpu_ref_get_many(&s->memcg_params.refcnt, nr_pages); @@ -425,11 +425,12 @@ static __always_inline void memcg_uncharge_slab(struct page *page, int order, memcg = READ_ONCE(s->memcg_params.memcg); if (likely(!mem_cgroup_is_root(memcg))) { lruvec = mem_cgroup_lruvec(memcg, page_pgdat(page)); - mod_lruvec_state(lruvec, cache_vmstat_idx(s), -nr_pages); + mod_lruvec_state(lruvec, cache_vmstat_idx(s), + -(nr_pages << PAGE_SHIFT)); memcg_kmem_uncharge(memcg, nr_pages); } else { mod_node_page_state(page_pgdat(page), cache_vmstat_idx(s), - -nr_pages); + -(nr_pages << PAGE_SHIFT)); } rcu_read_unlock(); @@ -513,7 +514,7 @@ static __always_inline int charge_slab_page(struct page *page, { if (is_root_cache(s)) { mod_node_page_state(page_pgdat(page), cache_vmstat_idx(s), - 1 << order); + PAGE_SIZE << order); return 0; } @@ -525,7 +526,7 @@ static __always_inline void uncharge_slab_page(struct page *page, int order, { if (is_root_cache(s)) { mod_node_page_state(page_pgdat(page), cache_vmstat_idx(s), - -(1 << order)); + -(PAGE_SIZE << order)); return; } diff --git a/mm/slab_common.c b/mm/slab_common.c index 616ec8a0d91a5..a73f168b10353 100644 --- a/mm/slab_common.c +++ b/mm/slab_common.c @@ -1363,8 +1363,8 @@ void *kmalloc_order(size_t size, gfp_t flags, unsigned int order) page = alloc_pages(flags, order); if (likely(page)) { ret = page_address(page); - mod_node_page_state(page_pgdat(page), NR_SLAB_UNRECLAIMABLE, - 1 << order); + mod_node_page_state(page_pgdat(page), NR_SLAB_UNRECLAIMABLE_B, + PAGE_SIZE << order); } ret = kasan_kmalloc_large(ret, size, flags); /* As ret might get tagged, call kmemleak hook after KASAN. */ diff --git a/mm/slob.c b/mm/slob.c index ac2aecfbc7a82..7cc9805c80911 100644 --- a/mm/slob.c +++ b/mm/slob.c @@ -202,8 +202,8 @@ static void *slob_new_pages(gfp_t gfp, int order, int node) if (!page) return NULL; - mod_node_page_state(page_pgdat(page), NR_SLAB_UNRECLAIMABLE, - 1 << order); + mod_node_page_state(page_pgdat(page), NR_SLAB_UNRECLAIMABLE_B, + PAGE_SIZE << order); return page_address(page); } @@ -214,8 +214,8 @@ static void slob_free_pages(void *b, int order) if (current->reclaim_state) current->reclaim_state->reclaimed_slab += 1 << order; - mod_node_page_state(page_pgdat(sp), NR_SLAB_UNRECLAIMABLE, - -(1 << order)); + mod_node_page_state(page_pgdat(sp), NR_SLAB_UNRECLAIMABLE_B, + -(PAGE_SIZE << order)); __free_pages(sp, order); } @@ -552,8 +552,8 @@ void kfree(const void *block) slob_free(m, *m + align); } else { unsigned int order = compound_order(sp); - mod_node_page_state(page_pgdat(sp), NR_SLAB_UNRECLAIMABLE, - -(1 << order)); + mod_node_page_state(page_pgdat(sp), NR_SLAB_UNRECLAIMABLE_B, + -(PAGE_SIZE << order)); __free_pages(sp, order); } diff --git a/mm/slub.c b/mm/slub.c index ae39eb392396f..2d73d677f7acd 100644 --- a/mm/slub.c +++ b/mm/slub.c @@ -3991,8 +3991,8 @@ static void *kmalloc_large_node(size_t size, gfp_t flags, int node) page = alloc_pages_node(node, flags, order); if (page) { ptr = page_address(page); - mod_node_page_state(page_pgdat(page), NR_SLAB_UNRECLAIMABLE, - 1 << order); + mod_node_page_state(page_pgdat(page), NR_SLAB_UNRECLAIMABLE_B, + PAGE_SIZE << order); } return kmalloc_large_node_hook(ptr, size, flags); @@ -4123,8 +4123,8 @@ void kfree(const void *x) BUG_ON(!PageCompound(page)); kfree_hook(object); - mod_node_page_state(page_pgdat(page), NR_SLAB_UNRECLAIMABLE, - -(1 << order)); + mod_node_page_state(page_pgdat(page), NR_SLAB_UNRECLAIMABLE_B, + -(PAGE_SIZE << order)); __free_pages(page, order); return; } diff --git a/mm/vmscan.c b/mm/vmscan.c index 749d239c62b2b..2ac43664aba4b 100644 --- a/mm/vmscan.c +++ b/mm/vmscan.c @@ -4222,7 +4222,8 @@ int node_reclaim(struct pglist_data *pgdat, gfp_t gfp_mask, unsigned int order) * unmapped file backed pages. */ if (node_pagecache_reclaimable(pgdat) <= pgdat->min_unmapped_pages && - node_page_state(pgdat, NR_SLAB_RECLAIMABLE) <= pgdat->min_slab_pages) + node_page_state_pages(pgdat, NR_SLAB_RECLAIMABLE_B) <= + pgdat->min_slab_pages) return NODE_RECLAIM_FULL; /* diff --git a/mm/workingset.c b/mm/workingset.c index 50b7937bab32f..b199726924ddd 100644 --- a/mm/workingset.c +++ b/mm/workingset.c @@ -486,8 +486,10 @@ static unsigned long count_shadow_nodes(struct shrinker *shrinker, for (pages = 0, i = 0; i < NR_LRU_LISTS; i++) pages += lruvec_page_state_local(lruvec, NR_LRU_BASE + i); - pages += lruvec_page_state_local(lruvec, NR_SLAB_RECLAIMABLE); - pages += lruvec_page_state_local(lruvec, NR_SLAB_UNRECLAIMABLE); + pages += lruvec_page_state_local( + lruvec, NR_SLAB_RECLAIMABLE_B) >> PAGE_SHIFT; + pages += lruvec_page_state_local( + lruvec, NR_SLAB_UNRECLAIMABLE_B) >> PAGE_SHIFT; } else #endif pages = node_present_pages(sc->nid); -- GitLab From 4138fdfc8b5db5a7a4b9b50c69d475fb2ac351b7 Mon Sep 17 00:00:00 2001 From: Roman Gushchin Date: Thu, 6 Aug 2020 23:20:42 -0700 Subject: [PATCH 1374/1476] mm: slub: implement SLUB version of obj_to_index() This commit implements SLUB version of the obj_to_index() function, which will be required to calculate the offset of obj_cgroup in the obj_cgroups vector to store/obtain the objcg ownership data. To make it faster, let's repeat the SLAB's trick introduced by commit 6a2d7a955d8d ("SLAB: use a multiply instead of a divide in obj_to_index()") and avoid an expensive division. Vlastimil Babka noticed, that SLUB does have already a similar function called slab_index(), which is defined only if SLUB_DEBUG is enabled. The function does a similar math, but with a division, and it also takes a page address instead of a page pointer. Let's remove slab_index() and replace it with the new helper __obj_to_index(), which takes a page address. obj_to_index() will be a simple wrapper taking a page pointer and passing page_address(page) into __obj_to_index(). Signed-off-by: Roman Gushchin Signed-off-by: Andrew Morton Reviewed-by: Vlastimil Babka Reviewed-by: Shakeel Butt Acked-by: Johannes Weiner Cc: Christoph Lameter Cc: Michal Hocko Cc: Tejun Heo Link: http://lkml.kernel.org/r/20200623174037.3951353-5-guro@fb.com Signed-off-by: Linus Torvalds --- include/linux/slub_def.h | 16 ++++++++++++++++ mm/slub.c | 15 +++++---------- 2 files changed, 21 insertions(+), 10 deletions(-) diff --git a/include/linux/slub_def.h b/include/linux/slub_def.h index d2153789bd9f9..30e91c83d4012 100644 --- a/include/linux/slub_def.h +++ b/include/linux/slub_def.h @@ -8,6 +8,7 @@ * (C) 2007 SGI, Christoph Lameter */ #include +#include enum stat_item { ALLOC_FASTPATH, /* Allocation from cpu slab */ @@ -86,6 +87,7 @@ struct kmem_cache { unsigned long min_partial; unsigned int size; /* The size of an object including metadata */ unsigned int object_size;/* The size of an object without metadata */ + struct reciprocal_value reciprocal_size; unsigned int offset; /* Free pointer offset */ #ifdef CONFIG_SLUB_CPU_PARTIAL /* Number of per cpu partial objects to keep around */ @@ -182,4 +184,18 @@ static inline void *nearest_obj(struct kmem_cache *cache, struct page *page, return result; } +/* Determine object index from a given position */ +static inline unsigned int __obj_to_index(const struct kmem_cache *cache, + void *addr, void *obj) +{ + return reciprocal_divide(kasan_reset_tag(obj) - addr, + cache->reciprocal_size); +} + +static inline unsigned int obj_to_index(const struct kmem_cache *cache, + const struct page *page, void *obj) +{ + return __obj_to_index(cache, page_address(page), obj); +} + #endif /* _LINUX_SLUB_DEF_H */ diff --git a/mm/slub.c b/mm/slub.c index 2d73d677f7acd..2a3075538f268 100644 --- a/mm/slub.c +++ b/mm/slub.c @@ -317,12 +317,6 @@ static inline void set_freepointer(struct kmem_cache *s, void *object, void *fp) __p < (__addr) + (__objects) * (__s)->size; \ __p += (__s)->size) -/* Determine object index from a given position */ -static inline unsigned int slab_index(void *p, struct kmem_cache *s, void *addr) -{ - return (kasan_reset_tag(p) - addr) / s->size; -} - static inline unsigned int order_objects(unsigned int order, unsigned int size) { return ((unsigned int)PAGE_SIZE << order) / size; @@ -465,7 +459,7 @@ static unsigned long *get_map(struct kmem_cache *s, struct page *page) bitmap_zero(object_map, page->objects); for (p = page->freelist; p; p = get_freepointer(s, p)) - set_bit(slab_index(p, s, addr), object_map); + set_bit(__obj_to_index(s, addr, p), object_map); return object_map; } @@ -3754,6 +3748,7 @@ static int calculate_sizes(struct kmem_cache *s, int forced_order) */ size = ALIGN(size, s->align); s->size = size; + s->reciprocal_size = reciprocal_value(size); if (forced_order >= 0) order = forced_order; else @@ -3858,7 +3853,7 @@ static void list_slab_objects(struct kmem_cache *s, struct page *page, map = get_map(s, page); for_each_object(p, s, addr, page->objects) { - if (!test_bit(slab_index(p, s, addr), map)) { + if (!test_bit(__obj_to_index(s, addr, p), map)) { pr_err("INFO: Object 0x%p @offset=%tu\n", p, p - addr); print_tracking(s, p); } @@ -4574,7 +4569,7 @@ static void validate_slab(struct kmem_cache *s, struct page *page) /* Now we know that a valid freelist exists */ map = get_map(s, page); for_each_object(p, s, addr, page->objects) { - u8 val = test_bit(slab_index(p, s, addr), map) ? + u8 val = test_bit(__obj_to_index(s, addr, p), map) ? SLUB_RED_INACTIVE : SLUB_RED_ACTIVE; if (!check_object(s, page, p, val)) @@ -4765,7 +4760,7 @@ static void process_slab(struct loc_track *t, struct kmem_cache *s, map = get_map(s, page); for_each_object(p, s, addr, page->objects) - if (!test_bit(slab_index(p, s, addr), map)) + if (!test_bit(__obj_to_index(s, addr, p), map)) add_location(t, s, get_track(s, p, alloc)); put_map(map); } -- GitLab From 1a3e1f40962c445b997151a542314f3c6097f8c3 Mon Sep 17 00:00:00 2001 From: Johannes Weiner Date: Thu, 6 Aug 2020 23:20:45 -0700 Subject: [PATCH 1375/1476] mm: memcontrol: decouple reference counting from page accounting The reference counting of a memcg is currently coupled directly to how many 4k pages are charged to it. This doesn't work well with Roman's new slab controller, which maintains pools of objects and doesn't want to keep an extra balance sheet for the pages backing those objects. This unusual refcounting design (reference counts usually track pointers to an object) is only for historical reasons: memcg used to not take any css references and simply stalled offlining until all charges had been reparented and the page counters had dropped to zero. When we got rid of the reparenting requirement, the simple mechanical translation was to take a reference for every charge. More historical context can be found in commit e8ea14cc6ead ("mm: memcontrol: take a css reference for each charged page"), commit 64f219938941 ("mm: memcontrol: remove obsolete kmemcg pinning tricks") and commit b2052564e66d ("mm: memcontrol: continue cache reclaim from offlined groups"). The new slab controller exposes the limitations in this scheme, so let's switch it to a more idiomatic reference counting model based on actual kernel pointers to the memcg: - The per-cpu stock holds a reference to the memcg its caching - User pages hold a reference for their page->mem_cgroup. Transparent huge pages will no longer acquire tail references in advance, we'll get them if needed during the split. - Kernel pages hold a reference for their page->mem_cgroup - Pages allocated in the root cgroup will acquire and release css references for simplicity. css_get() and css_put() optimize that. - The current memcg_charge_slab() already hacked around the per-charge references; this change gets rid of that as well. - tcp accounting will handle reference in mem_cgroup_sk_{alloc,free} Roman: 1) Rebased on top of the current mm tree: added css_get() in mem_cgroup_charge(), dropped mem_cgroup_try_charge() part 2) I've reformatted commit references in the commit log to make checkpatch.pl happy. [hughd@google.com: remove css_put_many() from __mem_cgroup_clear_mc()] Link: http://lkml.kernel.org/r/alpine.LSU.2.11.2007302011450.2347@eggly.anvils Signed-off-by: Johannes Weiner Signed-off-by: Roman Gushchin Signed-off-by: Hugh Dickins Signed-off-by: Andrew Morton Reviewed-by: Shakeel Butt Acked-by: Roman Gushchin Acked-by: Michal Hocko Cc: Christoph Lameter Cc: Tejun Heo Cc: Vlastimil Babka Link: http://lkml.kernel.org/r/20200623174037.3951353-6-guro@fb.com Signed-off-by: Linus Torvalds --- mm/memcontrol.c | 39 +++++++++++++++++++++------------------ mm/slab.h | 2 -- 2 files changed, 21 insertions(+), 20 deletions(-) diff --git a/mm/memcontrol.c b/mm/memcontrol.c index 328b7e7bf9ab1..4f9a3f55db71c 100644 --- a/mm/memcontrol.c +++ b/mm/memcontrol.c @@ -2094,13 +2094,17 @@ static void drain_stock(struct memcg_stock_pcp *stock) { struct mem_cgroup *old = stock->cached; + if (!old) + return; + if (stock->nr_pages) { page_counter_uncharge(&old->memory, stock->nr_pages); if (do_memsw_account()) page_counter_uncharge(&old->memsw, stock->nr_pages); - css_put_many(&old->css, stock->nr_pages); stock->nr_pages = 0; } + + css_put(&old->css); stock->cached = NULL; } @@ -2136,6 +2140,7 @@ static void refill_stock(struct mem_cgroup *memcg, unsigned int nr_pages) stock = this_cpu_ptr(&memcg_stock); if (stock->cached != memcg) { /* reset if necessary */ drain_stock(stock); + css_get(&memcg->css); stock->cached = memcg; } stock->nr_pages += nr_pages; @@ -2594,12 +2599,10 @@ force: page_counter_charge(&memcg->memory, nr_pages); if (do_memsw_account()) page_counter_charge(&memcg->memsw, nr_pages); - css_get_many(&memcg->css, nr_pages); return 0; done_restock: - css_get_many(&memcg->css, batch); if (batch > nr_pages) refill_stock(memcg, batch - nr_pages); @@ -2657,8 +2660,6 @@ static void cancel_charge(struct mem_cgroup *memcg, unsigned int nr_pages) page_counter_uncharge(&memcg->memory, nr_pages); if (do_memsw_account()) page_counter_uncharge(&memcg->memsw, nr_pages); - - css_put_many(&memcg->css, nr_pages); } #endif @@ -2966,6 +2967,7 @@ int __memcg_kmem_charge_page(struct page *page, gfp_t gfp, int order) if (!ret) { page->mem_cgroup = memcg; __SetPageKmemcg(page); + return 0; } } css_put(&memcg->css); @@ -2988,12 +2990,11 @@ void __memcg_kmem_uncharge_page(struct page *page, int order) VM_BUG_ON_PAGE(mem_cgroup_is_root(memcg), page); __memcg_kmem_uncharge(memcg, nr_pages); page->mem_cgroup = NULL; + css_put(&memcg->css); /* slab pages do not have PageKmemcg flag set */ if (PageKmemcg(page)) __ClearPageKmemcg(page); - - css_put_many(&memcg->css, nr_pages); } #endif /* CONFIG_MEMCG_KMEM */ @@ -3005,13 +3006,16 @@ void __memcg_kmem_uncharge_page(struct page *page, int order) */ void mem_cgroup_split_huge_fixup(struct page *head) { + struct mem_cgroup *memcg = head->mem_cgroup; int i; if (mem_cgroup_disabled()) return; - for (i = 1; i < HPAGE_PMD_NR; i++) - head[i].mem_cgroup = head->mem_cgroup; + for (i = 1; i < HPAGE_PMD_NR; i++) { + css_get(&memcg->css); + head[i].mem_cgroup = memcg; + } } #endif /* CONFIG_TRANSPARENT_HUGEPAGE */ @@ -5452,7 +5456,10 @@ static int mem_cgroup_move_account(struct page *page, */ smp_mb(); - page->mem_cgroup = to; /* caller should have done css_get */ + css_get(&to->css); + css_put(&from->css); + + page->mem_cgroup = to; __unlock_page_memcg(from); @@ -5673,8 +5680,6 @@ static void __mem_cgroup_clear_mc(void) if (!mem_cgroup_is_root(mc.to)) page_counter_uncharge(&mc.to->memory, mc.moved_swap); - css_put_many(&mc.to->css, mc.moved_swap); - mc.moved_swap = 0; } memcg_oom_recover(from); @@ -6502,6 +6507,7 @@ int mem_cgroup_charge(struct page *page, struct mm_struct *mm, gfp_t gfp_mask) if (ret) goto out_put; + css_get(&memcg->css); commit_charge(page, memcg); local_irq_disable(); @@ -6556,9 +6562,6 @@ static void uncharge_batch(const struct uncharge_gather *ug) __this_cpu_add(ug->memcg->vmstats_percpu->nr_page_events, ug->nr_pages); memcg_check_events(ug->memcg, ug->dummy_page); local_irq_restore(flags); - - if (!mem_cgroup_is_root(ug->memcg)) - css_put_many(&ug->memcg->css, ug->nr_pages); } static void uncharge_page(struct page *page, struct uncharge_gather *ug) @@ -6596,6 +6599,7 @@ static void uncharge_page(struct page *page, struct uncharge_gather *ug) ug->dummy_page = page; page->mem_cgroup = NULL; + css_put(&ug->memcg->css); } static void uncharge_list(struct list_head *page_list) @@ -6701,8 +6705,8 @@ void mem_cgroup_migrate(struct page *oldpage, struct page *newpage) page_counter_charge(&memcg->memory, nr_pages); if (do_memsw_account()) page_counter_charge(&memcg->memsw, nr_pages); - css_get_many(&memcg->css, nr_pages); + css_get(&memcg->css); commit_charge(newpage, memcg); local_irq_save(flags); @@ -6939,8 +6943,7 @@ void mem_cgroup_swapout(struct page *page, swp_entry_t entry) mem_cgroup_charge_statistics(memcg, page, -nr_entries); memcg_check_events(memcg, page); - if (!mem_cgroup_is_root(memcg)) - css_put_many(&memcg->css, nr_entries); + css_put(&memcg->css); } /** diff --git a/mm/slab.h b/mm/slab.h index 09be3ca6fe878..161ca34acb01d 100644 --- a/mm/slab.h +++ b/mm/slab.h @@ -402,9 +402,7 @@ static __always_inline int memcg_charge_slab(struct page *page, lruvec = mem_cgroup_lruvec(memcg, page_pgdat(page)); mod_lruvec_state(lruvec, cache_vmstat_idx(s), nr_pages << PAGE_SHIFT); - /* transer try_charge() page references to kmem_cache */ percpu_ref_get_many(&s->memcg_params.refcnt, nr_pages); - css_put_many(&memcg->css, nr_pages); out: css_put(&memcg->css); return ret; -- GitLab From bf4f059954dcb221384b2f784677e19a13cd4bdb Mon Sep 17 00:00:00 2001 From: Roman Gushchin Date: Thu, 6 Aug 2020 23:20:49 -0700 Subject: [PATCH 1376/1476] mm: memcg/slab: obj_cgroup API Obj_cgroup API provides an ability to account sub-page sized kernel objects, which potentially outlive the original memory cgroup. The top-level API consists of the following functions: bool obj_cgroup_tryget(struct obj_cgroup *objcg); void obj_cgroup_get(struct obj_cgroup *objcg); void obj_cgroup_put(struct obj_cgroup *objcg); int obj_cgroup_charge(struct obj_cgroup *objcg, gfp_t gfp, size_t size); void obj_cgroup_uncharge(struct obj_cgroup *objcg, size_t size); struct mem_cgroup *obj_cgroup_memcg(struct obj_cgroup *objcg); struct obj_cgroup *get_obj_cgroup_from_current(void); Object cgroup is basically a pointer to a memory cgroup with a per-cpu reference counter. It substitutes a memory cgroup in places where it's necessary to charge a custom amount of bytes instead of pages. All charged memory rounded down to pages is charged to the corresponding memory cgroup using __memcg_kmem_charge(). It implements reparenting: on memcg offlining it's getting reattached to the parent memory cgroup. Each online memory cgroup has an associated active object cgroup to handle new allocations and the list of all attached object cgroups. On offlining of a cgroup this list is reparented and for each object cgroup in the list the memcg pointer is swapped to the parent memory cgroup. It prevents long-living objects from pinning the original memory cgroup in the memory. The implementation is based on byte-sized per-cpu stocks. A sub-page sized leftover is stored in an atomic field, which is a part of obj_cgroup object. So on cgroup offlining the leftover is automatically reparented. memcg->objcg is rcu protected. objcg->memcg is a raw pointer, which is always pointing at a memory cgroup, but can be atomically swapped to the parent memory cgroup. So a user must ensure the lifetime of the cgroup, e.g. grab rcu_read_lock or css_set_lock. Suggested-by: Johannes Weiner Signed-off-by: Roman Gushchin Signed-off-by: Andrew Morton Reviewed-by: Shakeel Butt Cc: Christoph Lameter Cc: Michal Hocko Cc: Tejun Heo Cc: Vlastimil Babka Link: http://lkml.kernel.org/r/20200623174037.3951353-7-guro@fb.com Signed-off-by: Linus Torvalds --- include/linux/memcontrol.h | 51 +++++++ mm/memcontrol.c | 288 ++++++++++++++++++++++++++++++++++++- 2 files changed, 338 insertions(+), 1 deletion(-) diff --git a/include/linux/memcontrol.h b/include/linux/memcontrol.h index b250f8197710c..f2f9d5d6b7d1a 100644 --- a/include/linux/memcontrol.h +++ b/include/linux/memcontrol.h @@ -23,6 +23,7 @@ #include struct mem_cgroup; +struct obj_cgroup; struct page; struct mm_struct; struct kmem_cache; @@ -192,6 +193,22 @@ struct memcg_cgwb_frn { struct wb_completion done; /* tracks in-flight foreign writebacks */ }; +/* + * Bucket for arbitrarily byte-sized objects charged to a memory + * cgroup. The bucket can be reparented in one piece when the cgroup + * is destroyed, without having to round up the individual references + * of all live memory objects in the wild. + */ +struct obj_cgroup { + struct percpu_ref refcnt; + struct mem_cgroup *memcg; + atomic_t nr_charged_bytes; + union { + struct list_head list; + struct rcu_head rcu; + }; +}; + /* * The memory controller data structure. The memory controller controls both * page cache and RSS per cgroup. We would eventually like to provide @@ -301,6 +318,8 @@ struct mem_cgroup { int kmemcg_id; enum memcg_kmem_state kmem_state; struct list_head kmem_caches; + struct obj_cgroup __rcu *objcg; + struct list_head objcg_list; /* list of inherited objcgs */ #endif #ifdef CONFIG_CGROUP_WRITEBACK @@ -416,6 +435,33 @@ struct mem_cgroup *mem_cgroup_from_css(struct cgroup_subsys_state *css){ return css ? container_of(css, struct mem_cgroup, css) : NULL; } +static inline bool obj_cgroup_tryget(struct obj_cgroup *objcg) +{ + return percpu_ref_tryget(&objcg->refcnt); +} + +static inline void obj_cgroup_get(struct obj_cgroup *objcg) +{ + percpu_ref_get(&objcg->refcnt); +} + +static inline void obj_cgroup_put(struct obj_cgroup *objcg) +{ + percpu_ref_put(&objcg->refcnt); +} + +/* + * After the initialization objcg->memcg is always pointing at + * a valid memcg, but can be atomically swapped to the parent memcg. + * + * The caller must ensure that the returned memcg won't be released: + * e.g. acquire the rcu_read_lock or css_set_lock. + */ +static inline struct mem_cgroup *obj_cgroup_memcg(struct obj_cgroup *objcg) +{ + return READ_ONCE(objcg->memcg); +} + static inline void mem_cgroup_put(struct mem_cgroup *memcg) { if (memcg) @@ -1368,6 +1414,11 @@ void __memcg_kmem_uncharge(struct mem_cgroup *memcg, unsigned int nr_pages); int __memcg_kmem_charge_page(struct page *page, gfp_t gfp, int order); void __memcg_kmem_uncharge_page(struct page *page, int order); +struct obj_cgroup *get_obj_cgroup_from_current(void); + +int obj_cgroup_charge(struct obj_cgroup *objcg, gfp_t gfp, size_t size); +void obj_cgroup_uncharge(struct obj_cgroup *objcg, size_t size); + extern struct static_key_false memcg_kmem_enabled_key; extern struct workqueue_struct *memcg_kmem_cache_wq; diff --git a/mm/memcontrol.c b/mm/memcontrol.c index 4f9a3f55db71c..1cc784556e057 100644 --- a/mm/memcontrol.c +++ b/mm/memcontrol.c @@ -257,6 +257,98 @@ struct cgroup_subsys_state *vmpressure_to_css(struct vmpressure *vmpr) } #ifdef CONFIG_MEMCG_KMEM +extern spinlock_t css_set_lock; + +static void obj_cgroup_release(struct percpu_ref *ref) +{ + struct obj_cgroup *objcg = container_of(ref, struct obj_cgroup, refcnt); + struct mem_cgroup *memcg; + unsigned int nr_bytes; + unsigned int nr_pages; + unsigned long flags; + + /* + * At this point all allocated objects are freed, and + * objcg->nr_charged_bytes can't have an arbitrary byte value. + * However, it can be PAGE_SIZE or (x * PAGE_SIZE). + * + * The following sequence can lead to it: + * 1) CPU0: objcg == stock->cached_objcg + * 2) CPU1: we do a small allocation (e.g. 92 bytes), + * PAGE_SIZE bytes are charged + * 3) CPU1: a process from another memcg is allocating something, + * the stock if flushed, + * objcg->nr_charged_bytes = PAGE_SIZE - 92 + * 5) CPU0: we do release this object, + * 92 bytes are added to stock->nr_bytes + * 6) CPU0: stock is flushed, + * 92 bytes are added to objcg->nr_charged_bytes + * + * In the result, nr_charged_bytes == PAGE_SIZE. + * This page will be uncharged in obj_cgroup_release(). + */ + nr_bytes = atomic_read(&objcg->nr_charged_bytes); + WARN_ON_ONCE(nr_bytes & (PAGE_SIZE - 1)); + nr_pages = nr_bytes >> PAGE_SHIFT; + + spin_lock_irqsave(&css_set_lock, flags); + memcg = obj_cgroup_memcg(objcg); + if (nr_pages) + __memcg_kmem_uncharge(memcg, nr_pages); + list_del(&objcg->list); + mem_cgroup_put(memcg); + spin_unlock_irqrestore(&css_set_lock, flags); + + percpu_ref_exit(ref); + kfree_rcu(objcg, rcu); +} + +static struct obj_cgroup *obj_cgroup_alloc(void) +{ + struct obj_cgroup *objcg; + int ret; + + objcg = kzalloc(sizeof(struct obj_cgroup), GFP_KERNEL); + if (!objcg) + return NULL; + + ret = percpu_ref_init(&objcg->refcnt, obj_cgroup_release, 0, + GFP_KERNEL); + if (ret) { + kfree(objcg); + return NULL; + } + INIT_LIST_HEAD(&objcg->list); + return objcg; +} + +static void memcg_reparent_objcgs(struct mem_cgroup *memcg, + struct mem_cgroup *parent) +{ + struct obj_cgroup *objcg, *iter; + + objcg = rcu_replace_pointer(memcg->objcg, NULL, true); + + spin_lock_irq(&css_set_lock); + + /* Move active objcg to the parent's list */ + xchg(&objcg->memcg, parent); + css_get(&parent->css); + list_add(&objcg->list, &parent->objcg_list); + + /* Move already reparented objcgs to the parent's list */ + list_for_each_entry(iter, &memcg->objcg_list, list) { + css_get(&parent->css); + xchg(&iter->memcg, parent); + css_put(&memcg->css); + } + list_splice(&memcg->objcg_list, &parent->objcg_list); + + spin_unlock_irq(&css_set_lock); + + percpu_ref_kill(&objcg->refcnt); +} + /* * This will be the memcg's index in each cache's ->memcg_params.memcg_caches. * The main reason for not using cgroup id for this: @@ -2047,6 +2139,12 @@ EXPORT_SYMBOL(unlock_page_memcg); struct memcg_stock_pcp { struct mem_cgroup *cached; /* this never be root cgroup */ unsigned int nr_pages; + +#ifdef CONFIG_MEMCG_KMEM + struct obj_cgroup *cached_objcg; + unsigned int nr_bytes; +#endif + struct work_struct work; unsigned long flags; #define FLUSHING_CACHED_CHARGE 0 @@ -2054,6 +2152,22 @@ struct memcg_stock_pcp { static DEFINE_PER_CPU(struct memcg_stock_pcp, memcg_stock); static DEFINE_MUTEX(percpu_charge_mutex); +#ifdef CONFIG_MEMCG_KMEM +static void drain_obj_stock(struct memcg_stock_pcp *stock); +static bool obj_stock_flush_required(struct memcg_stock_pcp *stock, + struct mem_cgroup *root_memcg); + +#else +static inline void drain_obj_stock(struct memcg_stock_pcp *stock) +{ +} +static bool obj_stock_flush_required(struct memcg_stock_pcp *stock, + struct mem_cgroup *root_memcg) +{ + return false; +} +#endif + /** * consume_stock: Try to consume stocked charge on this cpu. * @memcg: memcg to consume from. @@ -2120,6 +2234,7 @@ static void drain_local_stock(struct work_struct *dummy) local_irq_save(flags); stock = this_cpu_ptr(&memcg_stock); + drain_obj_stock(stock); drain_stock(stock); clear_bit(FLUSHING_CACHED_CHARGE, &stock->flags); @@ -2179,6 +2294,8 @@ static void drain_all_stock(struct mem_cgroup *root_memcg) if (memcg && stock->nr_pages && mem_cgroup_is_descendant(memcg, root_memcg)) flush = true; + if (obj_stock_flush_required(stock, root_memcg)) + flush = true; rcu_read_unlock(); if (flush && @@ -2705,6 +2822,30 @@ struct mem_cgroup *mem_cgroup_from_obj(void *p) return page->mem_cgroup; } +__always_inline struct obj_cgroup *get_obj_cgroup_from_current(void) +{ + struct obj_cgroup *objcg = NULL; + struct mem_cgroup *memcg; + + if (unlikely(!current->mm && !current->active_memcg)) + return NULL; + + rcu_read_lock(); + if (unlikely(current->active_memcg)) + memcg = rcu_dereference(current->active_memcg); + else + memcg = mem_cgroup_from_task(current); + + for (; memcg != root_mem_cgroup; memcg = parent_mem_cgroup(memcg)) { + objcg = rcu_dereference(memcg->objcg); + if (objcg && obj_cgroup_tryget(objcg)) + break; + } + rcu_read_unlock(); + + return objcg; +} + static int memcg_alloc_cache_id(void) { int id, size; @@ -2996,6 +3137,140 @@ void __memcg_kmem_uncharge_page(struct page *page, int order) if (PageKmemcg(page)) __ClearPageKmemcg(page); } + +static bool consume_obj_stock(struct obj_cgroup *objcg, unsigned int nr_bytes) +{ + struct memcg_stock_pcp *stock; + unsigned long flags; + bool ret = false; + + local_irq_save(flags); + + stock = this_cpu_ptr(&memcg_stock); + if (objcg == stock->cached_objcg && stock->nr_bytes >= nr_bytes) { + stock->nr_bytes -= nr_bytes; + ret = true; + } + + local_irq_restore(flags); + + return ret; +} + +static void drain_obj_stock(struct memcg_stock_pcp *stock) +{ + struct obj_cgroup *old = stock->cached_objcg; + + if (!old) + return; + + if (stock->nr_bytes) { + unsigned int nr_pages = stock->nr_bytes >> PAGE_SHIFT; + unsigned int nr_bytes = stock->nr_bytes & (PAGE_SIZE - 1); + + if (nr_pages) { + rcu_read_lock(); + __memcg_kmem_uncharge(obj_cgroup_memcg(old), nr_pages); + rcu_read_unlock(); + } + + /* + * The leftover is flushed to the centralized per-memcg value. + * On the next attempt to refill obj stock it will be moved + * to a per-cpu stock (probably, on an other CPU), see + * refill_obj_stock(). + * + * How often it's flushed is a trade-off between the memory + * limit enforcement accuracy and potential CPU contention, + * so it might be changed in the future. + */ + atomic_add(nr_bytes, &old->nr_charged_bytes); + stock->nr_bytes = 0; + } + + obj_cgroup_put(old); + stock->cached_objcg = NULL; +} + +static bool obj_stock_flush_required(struct memcg_stock_pcp *stock, + struct mem_cgroup *root_memcg) +{ + struct mem_cgroup *memcg; + + if (stock->cached_objcg) { + memcg = obj_cgroup_memcg(stock->cached_objcg); + if (memcg && mem_cgroup_is_descendant(memcg, root_memcg)) + return true; + } + + return false; +} + +static void refill_obj_stock(struct obj_cgroup *objcg, unsigned int nr_bytes) +{ + struct memcg_stock_pcp *stock; + unsigned long flags; + + local_irq_save(flags); + + stock = this_cpu_ptr(&memcg_stock); + if (stock->cached_objcg != objcg) { /* reset if necessary */ + drain_obj_stock(stock); + obj_cgroup_get(objcg); + stock->cached_objcg = objcg; + stock->nr_bytes = atomic_xchg(&objcg->nr_charged_bytes, 0); + } + stock->nr_bytes += nr_bytes; + + if (stock->nr_bytes > PAGE_SIZE) + drain_obj_stock(stock); + + local_irq_restore(flags); +} + +int obj_cgroup_charge(struct obj_cgroup *objcg, gfp_t gfp, size_t size) +{ + struct mem_cgroup *memcg; + unsigned int nr_pages, nr_bytes; + int ret; + + if (consume_obj_stock(objcg, size)) + return 0; + + /* + * In theory, memcg->nr_charged_bytes can have enough + * pre-charged bytes to satisfy the allocation. However, + * flushing memcg->nr_charged_bytes requires two atomic + * operations, and memcg->nr_charged_bytes can't be big, + * so it's better to ignore it and try grab some new pages. + * memcg->nr_charged_bytes will be flushed in + * refill_obj_stock(), called from this function or + * independently later. + */ + rcu_read_lock(); + memcg = obj_cgroup_memcg(objcg); + css_get(&memcg->css); + rcu_read_unlock(); + + nr_pages = size >> PAGE_SHIFT; + nr_bytes = size & (PAGE_SIZE - 1); + + if (nr_bytes) + nr_pages += 1; + + ret = __memcg_kmem_charge(memcg, gfp, nr_pages); + if (!ret && nr_bytes) + refill_obj_stock(objcg, PAGE_SIZE - nr_bytes); + + css_put(&memcg->css); + return ret; +} + +void obj_cgroup_uncharge(struct obj_cgroup *objcg, size_t size) +{ + refill_obj_stock(objcg, size); +} + #endif /* CONFIG_MEMCG_KMEM */ #ifdef CONFIG_TRANSPARENT_HUGEPAGE @@ -3416,6 +3691,7 @@ static void memcg_flush_percpu_vmevents(struct mem_cgroup *memcg) #ifdef CONFIG_MEMCG_KMEM static int memcg_online_kmem(struct mem_cgroup *memcg) { + struct obj_cgroup *objcg; int memcg_id; if (cgroup_memory_nokmem) @@ -3428,6 +3704,14 @@ static int memcg_online_kmem(struct mem_cgroup *memcg) if (memcg_id < 0) return memcg_id; + objcg = obj_cgroup_alloc(); + if (!objcg) { + memcg_free_cache_id(memcg_id); + return -ENOMEM; + } + objcg->memcg = memcg; + rcu_assign_pointer(memcg->objcg, objcg); + static_branch_enable(&memcg_kmem_enabled_key); /* @@ -3464,9 +3748,10 @@ static void memcg_offline_kmem(struct mem_cgroup *memcg) parent = root_mem_cgroup; /* - * Deactivate and reparent kmem_caches. + * Deactivate and reparent kmem_caches and objcgs. */ memcg_deactivate_kmem_caches(memcg, parent); + memcg_reparent_objcgs(memcg, parent); kmemcg_id = memcg->kmemcg_id; BUG_ON(kmemcg_id < 0); @@ -5030,6 +5315,7 @@ static struct mem_cgroup *mem_cgroup_alloc(void) memcg->socket_pressure = jiffies; #ifdef CONFIG_MEMCG_KMEM memcg->kmemcg_id = -1; + INIT_LIST_HEAD(&memcg->objcg_list); #endif #ifdef CONFIG_CGROUP_WRITEBACK INIT_LIST_HEAD(&memcg->cgwb_list); -- GitLab From 286e04b8ed7a04279ae277f0f024430246ea5eec Mon Sep 17 00:00:00 2001 From: Roman Gushchin Date: Thu, 6 Aug 2020 23:20:52 -0700 Subject: [PATCH 1377/1476] mm: memcg/slab: allocate obj_cgroups for non-root slab pages Allocate and release memory to store obj_cgroup pointers for each non-root slab page. Reuse page->mem_cgroup pointer to store a pointer to the allocated space. This commit temporarily increases the memory footprint of the kernel memory accounting. To store obj_cgroup pointers we'll need a place for an objcg_pointer for each allocated object. However, the following patches in the series will enable sharing of slab pages between memory cgroups, which will dramatically increase the total slab utilization. And the final memory footprint will be significantly smaller than before. To distinguish between obj_cgroups and memcg pointers in case when it's not obvious which one is used (as in page_cgroup_ino()), let's always set the lowest bit in the obj_cgroup case. The original obj_cgroups pointer is marked to be ignored by kmemleak, which otherwise would report a memory leak for each allocated vector. Signed-off-by: Roman Gushchin Signed-off-by: Andrew Morton Reviewed-by: Vlastimil Babka Reviewed-by: Shakeel Butt Cc: Christoph Lameter Cc: Johannes Weiner Cc: Michal Hocko Cc: Tejun Heo Link: http://lkml.kernel.org/r/20200623174037.3951353-8-guro@fb.com Signed-off-by: Linus Torvalds --- include/linux/mm_types.h | 5 +++- include/linux/slab_def.h | 6 +++++ include/linux/slub_def.h | 5 ++++ mm/memcontrol.c | 17 ++++++++++--- mm/slab.h | 52 ++++++++++++++++++++++++++++++++++++++++ 5 files changed, 81 insertions(+), 4 deletions(-) diff --git a/include/linux/mm_types.h b/include/linux/mm_types.h index 64ede5f150dc5..0277fbab7c93b 100644 --- a/include/linux/mm_types.h +++ b/include/linux/mm_types.h @@ -198,7 +198,10 @@ struct page { atomic_t _refcount; #ifdef CONFIG_MEMCG - struct mem_cgroup *mem_cgroup; + union { + struct mem_cgroup *mem_cgroup; + struct obj_cgroup **obj_cgroups; + }; #endif /* diff --git a/include/linux/slab_def.h b/include/linux/slab_def.h index abc7de77b9881..ccda7b9669a51 100644 --- a/include/linux/slab_def.h +++ b/include/linux/slab_def.h @@ -114,4 +114,10 @@ static inline unsigned int obj_to_index(const struct kmem_cache *cache, return reciprocal_divide(offset, cache->reciprocal_buffer_size); } +static inline int objs_per_slab_page(const struct kmem_cache *cache, + const struct page *page) +{ + return cache->num; +} + #endif /* _LINUX_SLAB_DEF_H */ diff --git a/include/linux/slub_def.h b/include/linux/slub_def.h index 30e91c83d4012..f87302dcfe8c4 100644 --- a/include/linux/slub_def.h +++ b/include/linux/slub_def.h @@ -198,4 +198,9 @@ static inline unsigned int obj_to_index(const struct kmem_cache *cache, return __obj_to_index(cache, page_address(page), obj); } +static inline int objs_per_slab_page(const struct kmem_cache *cache, + const struct page *page) +{ + return page->objects; +} #endif /* _LINUX_SLUB_DEF_H */ diff --git a/mm/memcontrol.c b/mm/memcontrol.c index 1cc784556e057..e6cd4c0d44d16 100644 --- a/mm/memcontrol.c +++ b/mm/memcontrol.c @@ -569,10 +569,21 @@ ino_t page_cgroup_ino(struct page *page) unsigned long ino = 0; rcu_read_lock(); - if (PageSlab(page) && !PageTail(page)) + if (PageSlab(page) && !PageTail(page)) { memcg = memcg_from_slab_page(page); - else - memcg = READ_ONCE(page->mem_cgroup); + } else { + memcg = page->mem_cgroup; + + /* + * The lowest bit set means that memcg isn't a valid + * memcg pointer, but a obj_cgroups pointer. + * In this case the page is shared and doesn't belong + * to any specific memory cgroup. + */ + if ((unsigned long) memcg & 0x1UL) + memcg = NULL; + } + while (memcg && !(memcg->css.flags & CSS_ONLINE)) memcg = parent_mem_cgroup(memcg); if (memcg) diff --git a/mm/slab.h b/mm/slab.h index 161ca34acb01d..9f78e3167f195 100644 --- a/mm/slab.h +++ b/mm/slab.h @@ -109,6 +109,7 @@ struct memcg_cache_params { #include #include #include +#include /* * State of the slab allocator. @@ -348,6 +349,18 @@ static inline struct kmem_cache *memcg_root_cache(struct kmem_cache *s) return s->memcg_params.root_cache; } +static inline struct obj_cgroup **page_obj_cgroups(struct page *page) +{ + /* + * page->mem_cgroup and page->obj_cgroups are sharing the same + * space. To distinguish between them in case we don't know for sure + * that the page is a slab page (e.g. page_cgroup_ino()), let's + * always set the lowest bit of obj_cgroups. + */ + return (struct obj_cgroup **) + ((unsigned long)page->obj_cgroups & ~0x1UL); +} + /* * Expects a pointer to a slab page. Please note, that PageSlab() check * isn't sufficient, as it returns true also for tail compound slab pages, @@ -435,6 +448,28 @@ static __always_inline void memcg_uncharge_slab(struct page *page, int order, percpu_ref_put_many(&s->memcg_params.refcnt, nr_pages); } +static inline int memcg_alloc_page_obj_cgroups(struct page *page, + struct kmem_cache *s, gfp_t gfp) +{ + unsigned int objects = objs_per_slab_page(s, page); + void *vec; + + vec = kcalloc_node(objects, sizeof(struct obj_cgroup *), gfp, + page_to_nid(page)); + if (!vec) + return -ENOMEM; + + kmemleak_not_leak(vec); + page->obj_cgroups = (struct obj_cgroup **) ((unsigned long)vec | 0x1UL); + return 0; +} + +static inline void memcg_free_page_obj_cgroups(struct page *page) +{ + kfree(page_obj_cgroups(page)); + page->obj_cgroups = NULL; +} + extern void slab_init_memcg_params(struct kmem_cache *); extern void memcg_link_cache(struct kmem_cache *s, struct mem_cgroup *memcg); @@ -484,6 +519,16 @@ static inline void memcg_uncharge_slab(struct page *page, int order, { } +static inline int memcg_alloc_page_obj_cgroups(struct page *page, + struct kmem_cache *s, gfp_t gfp) +{ + return 0; +} + +static inline void memcg_free_page_obj_cgroups(struct page *page) +{ +} + static inline void slab_init_memcg_params(struct kmem_cache *s) { } @@ -510,12 +555,18 @@ static __always_inline int charge_slab_page(struct page *page, gfp_t gfp, int order, struct kmem_cache *s) { + int ret; + if (is_root_cache(s)) { mod_node_page_state(page_pgdat(page), cache_vmstat_idx(s), PAGE_SIZE << order); return 0; } + ret = memcg_alloc_page_obj_cgroups(page, s, gfp); + if (ret) + return ret; + return memcg_charge_slab(page, gfp, order, s); } @@ -528,6 +579,7 @@ static __always_inline void uncharge_slab_page(struct page *page, int order, return; } + memcg_free_page_obj_cgroups(page); memcg_uncharge_slab(page, order, s); } -- GitLab From 964d4bd370d559d9bd8e4abc139e85d2753956fb Mon Sep 17 00:00:00 2001 From: Roman Gushchin Date: Thu, 6 Aug 2020 23:20:56 -0700 Subject: [PATCH 1378/1476] mm: memcg/slab: save obj_cgroup for non-root slab objects Store the obj_cgroup pointer in the corresponding place of page->obj_cgroups for each allocated non-root slab object. Make sure that each allocated object holds a reference to obj_cgroup. Objcg pointer is obtained from the memcg->objcg dereferencing in memcg_kmem_get_cache() and passed from pre_alloc_hook to post_alloc_hook. Then in case of successful allocation(s) it's getting stored in the page->obj_cgroups vector. The objcg obtaining part look a bit bulky now, but it will be simplified by next commits in the series. Signed-off-by: Roman Gushchin Signed-off-by: Andrew Morton Reviewed-by: Vlastimil Babka Reviewed-by: Shakeel Butt Cc: Christoph Lameter Cc: Johannes Weiner Cc: Michal Hocko Cc: Tejun Heo Link: http://lkml.kernel.org/r/20200623174037.3951353-9-guro@fb.com Signed-off-by: Linus Torvalds --- include/linux/memcontrol.h | 3 +- mm/memcontrol.c | 14 +++++++-- mm/slab.c | 18 +++++++----- mm/slab.h | 60 ++++++++++++++++++++++++++++++++++---- mm/slub.c | 14 +++++---- 5 files changed, 88 insertions(+), 21 deletions(-) diff --git a/include/linux/memcontrol.h b/include/linux/memcontrol.h index f2f9d5d6b7d1a..b845e908e76e3 100644 --- a/include/linux/memcontrol.h +++ b/include/linux/memcontrol.h @@ -1404,7 +1404,8 @@ static inline void memcg_set_shrinker_bit(struct mem_cgroup *memcg, } #endif -struct kmem_cache *memcg_kmem_get_cache(struct kmem_cache *cachep); +struct kmem_cache *memcg_kmem_get_cache(struct kmem_cache *cachep, + struct obj_cgroup **objcgp); void memcg_kmem_put_cache(struct kmem_cache *cachep); #ifdef CONFIG_MEMCG_KMEM diff --git a/mm/memcontrol.c b/mm/memcontrol.c index e6cd4c0d44d16..ab96a120e6308 100644 --- a/mm/memcontrol.c +++ b/mm/memcontrol.c @@ -2973,7 +2973,8 @@ static inline bool memcg_kmem_bypass(void) * done with it, memcg_kmem_put_cache() must be called to release the * reference. */ -struct kmem_cache *memcg_kmem_get_cache(struct kmem_cache *cachep) +struct kmem_cache *memcg_kmem_get_cache(struct kmem_cache *cachep, + struct obj_cgroup **objcgp) { struct mem_cgroup *memcg; struct kmem_cache *memcg_cachep; @@ -3029,8 +3030,17 @@ struct kmem_cache *memcg_kmem_get_cache(struct kmem_cache *cachep) */ if (unlikely(!memcg_cachep)) memcg_schedule_kmem_cache_create(memcg, cachep); - else if (percpu_ref_tryget(&memcg_cachep->memcg_params.refcnt)) + else if (percpu_ref_tryget(&memcg_cachep->memcg_params.refcnt)) { + struct obj_cgroup *objcg = rcu_dereference(memcg->objcg); + + if (!objcg || !obj_cgroup_tryget(objcg)) { + percpu_ref_put(&memcg_cachep->memcg_params.refcnt); + goto out_unlock; + } + + *objcgp = objcg; cachep = memcg_cachep; + } out_unlock: rcu_read_unlock(); return cachep; diff --git a/mm/slab.c b/mm/slab.c index fa31cbb76124b..1e90b67735aa5 100644 --- a/mm/slab.c +++ b/mm/slab.c @@ -3228,9 +3228,10 @@ slab_alloc_node(struct kmem_cache *cachep, gfp_t flags, int nodeid, unsigned long save_flags; void *ptr; int slab_node = numa_mem_id(); + struct obj_cgroup *objcg = NULL; flags &= gfp_allowed_mask; - cachep = slab_pre_alloc_hook(cachep, flags); + cachep = slab_pre_alloc_hook(cachep, &objcg, 1, flags); if (unlikely(!cachep)) return NULL; @@ -3266,7 +3267,7 @@ slab_alloc_node(struct kmem_cache *cachep, gfp_t flags, int nodeid, if (unlikely(slab_want_init_on_alloc(flags, cachep)) && ptr) memset(ptr, 0, cachep->object_size); - slab_post_alloc_hook(cachep, flags, 1, &ptr); + slab_post_alloc_hook(cachep, objcg, flags, 1, &ptr); return ptr; } @@ -3307,9 +3308,10 @@ slab_alloc(struct kmem_cache *cachep, gfp_t flags, unsigned long caller) { unsigned long save_flags; void *objp; + struct obj_cgroup *objcg = NULL; flags &= gfp_allowed_mask; - cachep = slab_pre_alloc_hook(cachep, flags); + cachep = slab_pre_alloc_hook(cachep, &objcg, 1, flags); if (unlikely(!cachep)) return NULL; @@ -3323,7 +3325,7 @@ slab_alloc(struct kmem_cache *cachep, gfp_t flags, unsigned long caller) if (unlikely(slab_want_init_on_alloc(flags, cachep)) && objp) memset(objp, 0, cachep->object_size); - slab_post_alloc_hook(cachep, flags, 1, &objp); + slab_post_alloc_hook(cachep, objcg, flags, 1, &objp); return objp; } @@ -3450,6 +3452,7 @@ void ___cache_free(struct kmem_cache *cachep, void *objp, memset(objp, 0, cachep->object_size); kmemleak_free_recursive(objp, cachep->flags); objp = cache_free_debugcheck(cachep, objp, caller); + memcg_slab_free_hook(cachep, virt_to_head_page(objp), objp); /* * Skip calling cache_free_alien() when the platform is not numa. @@ -3515,8 +3518,9 @@ int kmem_cache_alloc_bulk(struct kmem_cache *s, gfp_t flags, size_t size, void **p) { size_t i; + struct obj_cgroup *objcg = NULL; - s = slab_pre_alloc_hook(s, flags); + s = slab_pre_alloc_hook(s, &objcg, size, flags); if (!s) return 0; @@ -3539,13 +3543,13 @@ int kmem_cache_alloc_bulk(struct kmem_cache *s, gfp_t flags, size_t size, for (i = 0; i < size; i++) memset(p[i], 0, s->object_size); - slab_post_alloc_hook(s, flags, size, p); + slab_post_alloc_hook(s, objcg, flags, size, p); /* FIXME: Trace call missing. Christoph would like a bulk variant */ return size; error: local_irq_enable(); cache_alloc_debugcheck_after_bulk(s, flags, i, p, _RET_IP_); - slab_post_alloc_hook(s, flags, i, p); + slab_post_alloc_hook(s, objcg, flags, i, p); __kmem_cache_free_bulk(s, i, p); return 0; } diff --git a/mm/slab.h b/mm/slab.h index 9f78e3167f195..d4f9fc93546f8 100644 --- a/mm/slab.h +++ b/mm/slab.h @@ -470,6 +470,41 @@ static inline void memcg_free_page_obj_cgroups(struct page *page) page->obj_cgroups = NULL; } +static inline void memcg_slab_post_alloc_hook(struct kmem_cache *s, + struct obj_cgroup *objcg, + size_t size, void **p) +{ + struct page *page; + unsigned long off; + size_t i; + + for (i = 0; i < size; i++) { + if (likely(p[i])) { + page = virt_to_head_page(p[i]); + off = obj_to_index(s, page, p[i]); + obj_cgroup_get(objcg); + page_obj_cgroups(page)[off] = objcg; + } + } + obj_cgroup_put(objcg); + memcg_kmem_put_cache(s); +} + +static inline void memcg_slab_free_hook(struct kmem_cache *s, struct page *page, + void *p) +{ + struct obj_cgroup *objcg; + unsigned int off; + + if (!memcg_kmem_enabled() || is_root_cache(s)) + return; + + off = obj_to_index(s, page, p); + objcg = page_obj_cgroups(page)[off]; + page_obj_cgroups(page)[off] = NULL; + obj_cgroup_put(objcg); +} + extern void slab_init_memcg_params(struct kmem_cache *); extern void memcg_link_cache(struct kmem_cache *s, struct mem_cgroup *memcg); @@ -529,6 +564,17 @@ static inline void memcg_free_page_obj_cgroups(struct page *page) { } +static inline void memcg_slab_post_alloc_hook(struct kmem_cache *s, + struct obj_cgroup *objcg, + size_t size, void **p) +{ +} + +static inline void memcg_slab_free_hook(struct kmem_cache *s, struct page *page, + void *p) +{ +} + static inline void slab_init_memcg_params(struct kmem_cache *s) { } @@ -631,7 +677,8 @@ static inline size_t slab_ksize(const struct kmem_cache *s) } static inline struct kmem_cache *slab_pre_alloc_hook(struct kmem_cache *s, - gfp_t flags) + struct obj_cgroup **objcgp, + size_t size, gfp_t flags) { flags &= gfp_allowed_mask; @@ -645,13 +692,14 @@ static inline struct kmem_cache *slab_pre_alloc_hook(struct kmem_cache *s, if (memcg_kmem_enabled() && ((flags & __GFP_ACCOUNT) || (s->flags & SLAB_ACCOUNT))) - return memcg_kmem_get_cache(s); + return memcg_kmem_get_cache(s, objcgp); return s; } -static inline void slab_post_alloc_hook(struct kmem_cache *s, gfp_t flags, - size_t size, void **p) +static inline void slab_post_alloc_hook(struct kmem_cache *s, + struct obj_cgroup *objcg, + gfp_t flags, size_t size, void **p) { size_t i; @@ -663,8 +711,8 @@ static inline void slab_post_alloc_hook(struct kmem_cache *s, gfp_t flags, s->flags, flags); } - if (memcg_kmem_enabled()) - memcg_kmem_put_cache(s); + if (memcg_kmem_enabled() && !is_root_cache(s)) + memcg_slab_post_alloc_hook(s, objcg, size, p); } #ifndef CONFIG_SLOB diff --git a/mm/slub.c b/mm/slub.c index 2a3075538f268..47e63b1100d49 100644 --- a/mm/slub.c +++ b/mm/slub.c @@ -2817,8 +2817,9 @@ static __always_inline void *slab_alloc_node(struct kmem_cache *s, struct kmem_cache_cpu *c; struct page *page; unsigned long tid; + struct obj_cgroup *objcg = NULL; - s = slab_pre_alloc_hook(s, gfpflags); + s = slab_pre_alloc_hook(s, &objcg, 1, gfpflags); if (!s) return NULL; redo: @@ -2894,7 +2895,7 @@ redo: if (unlikely(slab_want_init_on_alloc(gfpflags, s)) && object) memset(object, 0, s->object_size); - slab_post_alloc_hook(s, gfpflags, 1, &object); + slab_post_alloc_hook(s, objcg, gfpflags, 1, &object); return object; } @@ -3099,6 +3100,8 @@ static __always_inline void do_slab_free(struct kmem_cache *s, void *tail_obj = tail ? : head; struct kmem_cache_cpu *c; unsigned long tid; + + memcg_slab_free_hook(s, page, head); redo: /* * Determine the currently cpus per cpu slab. @@ -3278,9 +3281,10 @@ int kmem_cache_alloc_bulk(struct kmem_cache *s, gfp_t flags, size_t size, { struct kmem_cache_cpu *c; int i; + struct obj_cgroup *objcg = NULL; /* memcg and kmem_cache debug support */ - s = slab_pre_alloc_hook(s, flags); + s = slab_pre_alloc_hook(s, &objcg, size, flags); if (unlikely(!s)) return false; /* @@ -3334,11 +3338,11 @@ int kmem_cache_alloc_bulk(struct kmem_cache *s, gfp_t flags, size_t size, } /* memcg and kmem_cache debug support */ - slab_post_alloc_hook(s, flags, size, p); + slab_post_alloc_hook(s, objcg, flags, size, p); return i; error: local_irq_enable(); - slab_post_alloc_hook(s, flags, i, p); + slab_post_alloc_hook(s, objcg, flags, i, p); __kmem_cache_free_bulk(s, i, p); return 0; } -- GitLab From f2fe7b09a52bc898ec030d4fa6f78725439c7c2c Mon Sep 17 00:00:00 2001 From: Roman Gushchin Date: Thu, 6 Aug 2020 23:20:59 -0700 Subject: [PATCH 1379/1476] mm: memcg/slab: charge individual slab objects instead of pages Switch to per-object accounting of non-root slab objects. Charging is performed using obj_cgroup API in the pre_alloc hook. Obj_cgroup is charged with the size of the object and the size of metadata: as now it's the size of an obj_cgroup pointer. If the amount of memory has been charged successfully, the actual allocation code is executed. Otherwise, -ENOMEM is returned. In the post_alloc hook if the actual allocation succeeded, corresponding vmstats are bumped and the obj_cgroup pointer is saved. Otherwise, the charge is canceled. On the free path obj_cgroup pointer is obtained and used to uncharge the size of the releasing object. Memcg and lruvec counters are now representing only memory used by active slab objects and do not include the free space. The free space is shared and doesn't belong to any specific cgroup. Global per-node slab vmstats are still modified from (un)charge_slab_page() functions. The idea is to keep all slab pages accounted as slab pages on system level. Signed-off-by: Roman Gushchin Signed-off-by: Andrew Morton Reviewed-by: Vlastimil Babka Reviewed-by: Shakeel Butt Cc: Christoph Lameter Cc: Johannes Weiner Cc: Michal Hocko Cc: Tejun Heo Link: http://lkml.kernel.org/r/20200623174037.3951353-10-guro@fb.com Signed-off-by: Linus Torvalds --- mm/slab.h | 174 ++++++++++++++++++++++++------------------------------ 1 file changed, 78 insertions(+), 96 deletions(-) diff --git a/mm/slab.h b/mm/slab.h index d4f9fc93546f8..1f067e8bc3778 100644 --- a/mm/slab.h +++ b/mm/slab.h @@ -382,72 +382,6 @@ static inline struct mem_cgroup *memcg_from_slab_page(struct page *page) return NULL; } -/* - * Charge the slab page belonging to the non-root kmem_cache. - * Can be called for non-root kmem_caches only. - */ -static __always_inline int memcg_charge_slab(struct page *page, - gfp_t gfp, int order, - struct kmem_cache *s) -{ - int nr_pages = 1 << order; - struct mem_cgroup *memcg; - struct lruvec *lruvec; - int ret; - - rcu_read_lock(); - memcg = READ_ONCE(s->memcg_params.memcg); - while (memcg && !css_tryget_online(&memcg->css)) - memcg = parent_mem_cgroup(memcg); - rcu_read_unlock(); - - if (unlikely(!memcg || mem_cgroup_is_root(memcg))) { - mod_node_page_state(page_pgdat(page), cache_vmstat_idx(s), - nr_pages << PAGE_SHIFT); - percpu_ref_get_many(&s->memcg_params.refcnt, nr_pages); - return 0; - } - - ret = memcg_kmem_charge(memcg, gfp, nr_pages); - if (ret) - goto out; - - lruvec = mem_cgroup_lruvec(memcg, page_pgdat(page)); - mod_lruvec_state(lruvec, cache_vmstat_idx(s), nr_pages << PAGE_SHIFT); - - percpu_ref_get_many(&s->memcg_params.refcnt, nr_pages); -out: - css_put(&memcg->css); - return ret; -} - -/* - * Uncharge a slab page belonging to a non-root kmem_cache. - * Can be called for non-root kmem_caches only. - */ -static __always_inline void memcg_uncharge_slab(struct page *page, int order, - struct kmem_cache *s) -{ - int nr_pages = 1 << order; - struct mem_cgroup *memcg; - struct lruvec *lruvec; - - rcu_read_lock(); - memcg = READ_ONCE(s->memcg_params.memcg); - if (likely(!mem_cgroup_is_root(memcg))) { - lruvec = mem_cgroup_lruvec(memcg, page_pgdat(page)); - mod_lruvec_state(lruvec, cache_vmstat_idx(s), - -(nr_pages << PAGE_SHIFT)); - memcg_kmem_uncharge(memcg, nr_pages); - } else { - mod_node_page_state(page_pgdat(page), cache_vmstat_idx(s), - -(nr_pages << PAGE_SHIFT)); - } - rcu_read_unlock(); - - percpu_ref_put_many(&s->memcg_params.refcnt, nr_pages); -} - static inline int memcg_alloc_page_obj_cgroups(struct page *page, struct kmem_cache *s, gfp_t gfp) { @@ -470,6 +404,48 @@ static inline void memcg_free_page_obj_cgroups(struct page *page) page->obj_cgroups = NULL; } +static inline size_t obj_full_size(struct kmem_cache *s) +{ + /* + * For each accounted object there is an extra space which is used + * to store obj_cgroup membership. Charge it too. + */ + return s->size + sizeof(struct obj_cgroup *); +} + +static inline struct kmem_cache *memcg_slab_pre_alloc_hook(struct kmem_cache *s, + struct obj_cgroup **objcgp, + size_t objects, gfp_t flags) +{ + struct kmem_cache *cachep; + + cachep = memcg_kmem_get_cache(s, objcgp); + if (is_root_cache(cachep)) + return s; + + if (obj_cgroup_charge(*objcgp, flags, objects * obj_full_size(s))) { + obj_cgroup_put(*objcgp); + memcg_kmem_put_cache(cachep); + cachep = NULL; + } + + return cachep; +} + +static inline void mod_objcg_state(struct obj_cgroup *objcg, + struct pglist_data *pgdat, + int idx, int nr) +{ + struct mem_cgroup *memcg; + struct lruvec *lruvec; + + rcu_read_lock(); + memcg = obj_cgroup_memcg(objcg); + lruvec = mem_cgroup_lruvec(memcg, pgdat); + mod_memcg_lruvec_state(lruvec, idx, nr); + rcu_read_unlock(); +} + static inline void memcg_slab_post_alloc_hook(struct kmem_cache *s, struct obj_cgroup *objcg, size_t size, void **p) @@ -484,6 +460,10 @@ static inline void memcg_slab_post_alloc_hook(struct kmem_cache *s, off = obj_to_index(s, page, p[i]); obj_cgroup_get(objcg); page_obj_cgroups(page)[off] = objcg; + mod_objcg_state(objcg, page_pgdat(page), + cache_vmstat_idx(s), obj_full_size(s)); + } else { + obj_cgroup_uncharge(objcg, obj_full_size(s)); } } obj_cgroup_put(objcg); @@ -502,6 +482,11 @@ static inline void memcg_slab_free_hook(struct kmem_cache *s, struct page *page, off = obj_to_index(s, page, p); objcg = page_obj_cgroups(page)[off]; page_obj_cgroups(page)[off] = NULL; + + obj_cgroup_uncharge(objcg, obj_full_size(s)); + mod_objcg_state(objcg, page_pgdat(page), cache_vmstat_idx(s), + -obj_full_size(s)); + obj_cgroup_put(objcg); } @@ -543,17 +528,6 @@ static inline struct mem_cgroup *memcg_from_slab_page(struct page *page) return NULL; } -static inline int memcg_charge_slab(struct page *page, gfp_t gfp, int order, - struct kmem_cache *s) -{ - return 0; -} - -static inline void memcg_uncharge_slab(struct page *page, int order, - struct kmem_cache *s) -{ -} - static inline int memcg_alloc_page_obj_cgroups(struct page *page, struct kmem_cache *s, gfp_t gfp) { @@ -564,6 +538,13 @@ static inline void memcg_free_page_obj_cgroups(struct page *page) { } +static inline struct kmem_cache *memcg_slab_pre_alloc_hook(struct kmem_cache *s, + struct obj_cgroup **objcgp, + size_t objects, gfp_t flags) +{ + return NULL; +} + static inline void memcg_slab_post_alloc_hook(struct kmem_cache *s, struct obj_cgroup *objcg, size_t size, void **p) @@ -601,32 +582,33 @@ static __always_inline int charge_slab_page(struct page *page, gfp_t gfp, int order, struct kmem_cache *s) { - int ret; - - if (is_root_cache(s)) { - mod_node_page_state(page_pgdat(page), cache_vmstat_idx(s), - PAGE_SIZE << order); - return 0; - } +#ifdef CONFIG_MEMCG_KMEM + if (memcg_kmem_enabled() && !is_root_cache(s)) { + int ret; - ret = memcg_alloc_page_obj_cgroups(page, s, gfp); - if (ret) - return ret; + ret = memcg_alloc_page_obj_cgroups(page, s, gfp); + if (ret) + return ret; - return memcg_charge_slab(page, gfp, order, s); + percpu_ref_get_many(&s->memcg_params.refcnt, 1 << order); + } +#endif + mod_node_page_state(page_pgdat(page), cache_vmstat_idx(s), + PAGE_SIZE << order); + return 0; } static __always_inline void uncharge_slab_page(struct page *page, int order, struct kmem_cache *s) { - if (is_root_cache(s)) { - mod_node_page_state(page_pgdat(page), cache_vmstat_idx(s), - -(PAGE_SIZE << order)); - return; +#ifdef CONFIG_MEMCG_KMEM + if (memcg_kmem_enabled() && !is_root_cache(s)) { + memcg_free_page_obj_cgroups(page); + percpu_ref_put_many(&s->memcg_params.refcnt, 1 << order); } - - memcg_free_page_obj_cgroups(page); - memcg_uncharge_slab(page, order, s); +#endif + mod_node_page_state(page_pgdat(page), cache_vmstat_idx(s), + -(PAGE_SIZE << order)); } static inline struct kmem_cache *cache_from_obj(struct kmem_cache *s, void *x) @@ -692,7 +674,7 @@ static inline struct kmem_cache *slab_pre_alloc_hook(struct kmem_cache *s, if (memcg_kmem_enabled() && ((flags & __GFP_ACCOUNT) || (s->flags & SLAB_ACCOUNT))) - return memcg_kmem_get_cache(s, objcgp); + return memcg_slab_pre_alloc_hook(s, objcgp, size, flags); return s; } -- GitLab From 4330a26bc4527f1d8918c398ebc983574f761cca Mon Sep 17 00:00:00 2001 From: Roman Gushchin Date: Thu, 6 Aug 2020 23:21:03 -0700 Subject: [PATCH 1380/1476] mm: memcg/slab: deprecate memory.kmem.slabinfo Deprecate memory.kmem.slabinfo. An empty file will be presented if corresponding config options are enabled. The interface is implementation dependent, isn't present in cgroup v2, and is generally useful only for core mm debugging purposes. In other words, it doesn't provide any value for the absolute majority of users. A drgn-based replacement can be found in tools/cgroup/memcg_slabinfo.py. It does support cgroup v1 and v2, mimics memory.kmem.slabinfo output and also allows to get any additional information without a need to recompile the kernel. If a drgn-based solution is too slow for a task, a bpf-based tracing tool can be used, which can easily keep track of all slab allocations belonging to a memory cgroup. Signed-off-by: Roman Gushchin Signed-off-by: Andrew Morton Reviewed-by: Vlastimil Babka Reviewed-by: Shakeel Butt Acked-by: Johannes Weiner Cc: Christoph Lameter Cc: Michal Hocko Cc: Tejun Heo Link: http://lkml.kernel.org/r/20200623174037.3951353-11-guro@fb.com Signed-off-by: Linus Torvalds --- mm/memcontrol.c | 3 --- mm/slab_common.c | 31 ++++--------------------------- 2 files changed, 4 insertions(+), 30 deletions(-) diff --git a/mm/memcontrol.c b/mm/memcontrol.c index ab96a120e6308..0356e05bc6e6f 100644 --- a/mm/memcontrol.c +++ b/mm/memcontrol.c @@ -5114,9 +5114,6 @@ static struct cftype mem_cgroup_legacy_files[] = { (defined(CONFIG_SLAB) || defined(CONFIG_SLUB_DEBUG)) { .name = "kmem.slabinfo", - .seq_start = memcg_slab_start, - .seq_next = memcg_slab_next, - .seq_stop = memcg_slab_stop, .seq_show = memcg_slab_show, }, #endif diff --git a/mm/slab_common.c b/mm/slab_common.c index a73f168b10353..f86431d0de739 100644 --- a/mm/slab_common.c +++ b/mm/slab_common.c @@ -1561,35 +1561,12 @@ void dump_unreclaimable_slab(void) } #if defined(CONFIG_MEMCG_KMEM) -void *memcg_slab_start(struct seq_file *m, loff_t *pos) -{ - struct mem_cgroup *memcg = mem_cgroup_from_seq(m); - - mutex_lock(&slab_mutex); - return seq_list_start(&memcg->kmem_caches, *pos); -} - -void *memcg_slab_next(struct seq_file *m, void *p, loff_t *pos) -{ - struct mem_cgroup *memcg = mem_cgroup_from_seq(m); - - return seq_list_next(p, &memcg->kmem_caches, pos); -} - -void memcg_slab_stop(struct seq_file *m, void *p) -{ - mutex_unlock(&slab_mutex); -} - int memcg_slab_show(struct seq_file *m, void *p) { - struct kmem_cache *s = list_entry(p, struct kmem_cache, - memcg_params.kmem_caches_node); - struct mem_cgroup *memcg = mem_cgroup_from_seq(m); - - if (p == memcg->kmem_caches.next) - print_slabinfo_header(m); - cache_show(s, m); + /* + * Deprecated. + * Please, take a look at tools/cgroup/slabinfo.py . + */ return 0; } #endif -- GitLab From 0f876e4dc55db5fafef774917fd66e1373c0f390 Mon Sep 17 00:00:00 2001 From: Roman Gushchin Date: Thu, 6 Aug 2020 23:21:06 -0700 Subject: [PATCH 1381/1476] mm: memcg/slab: move memcg_kmem_bypass() to memcontrol.h To make the memcg_kmem_bypass() function available outside of the memcontrol.c, let's move it to memcontrol.h. The function is small and nicely fits into static inline sort of functions. It will be used from the slab code. Signed-off-by: Roman Gushchin Signed-off-by: Andrew Morton Reviewed-by: Vlastimil Babka Reviewed-by: Shakeel Butt Cc: Christoph Lameter Cc: Johannes Weiner Cc: Michal Hocko Cc: Tejun Heo Link: http://lkml.kernel.org/r/20200623174037.3951353-12-guro@fb.com Signed-off-by: Linus Torvalds --- include/linux/memcontrol.h | 12 ++++++++++++ mm/memcontrol.c | 12 ------------ 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/include/linux/memcontrol.h b/include/linux/memcontrol.h index b845e908e76e3..83e2858aecf25 100644 --- a/include/linux/memcontrol.h +++ b/include/linux/memcontrol.h @@ -1440,6 +1440,18 @@ static inline bool memcg_kmem_enabled(void) return static_branch_unlikely(&memcg_kmem_enabled_key); } +static inline bool memcg_kmem_bypass(void) +{ + if (in_interrupt()) + return true; + + /* Allow remote memcg charging in kthread contexts. */ + if ((!current->mm || (current->flags & PF_KTHREAD)) && + !current->active_memcg) + return true; + return false; +} + static inline int memcg_kmem_charge_page(struct page *page, gfp_t gfp, int order) { diff --git a/mm/memcontrol.c b/mm/memcontrol.c index 0356e05bc6e6f..5cb2a588cc107 100644 --- a/mm/memcontrol.c +++ b/mm/memcontrol.c @@ -2945,18 +2945,6 @@ static void memcg_schedule_kmem_cache_create(struct mem_cgroup *memcg, queue_work(memcg_kmem_cache_wq, &cw->work); } -static inline bool memcg_kmem_bypass(void) -{ - if (in_interrupt()) - return true; - - /* Allow remote memcg charging in kthread contexts. */ - if ((!current->mm || (current->flags & PF_KTHREAD)) && - !current->active_memcg) - return true; - return false; -} - /** * memcg_kmem_get_cache: select the correct per-memcg cache for allocation * @cachep: the original global kmem cache -- GitLab From 9855609bde03e2472b99a95e869d29ee1e78a751 Mon Sep 17 00:00:00 2001 From: Roman Gushchin Date: Thu, 6 Aug 2020 23:21:10 -0700 Subject: [PATCH 1382/1476] mm: memcg/slab: use a single set of kmem_caches for all accounted allocations This is fairly big but mostly red patch, which makes all accounted slab allocations use a single set of kmem_caches instead of creating a separate set for each memory cgroup. Because the number of non-root kmem_caches is now capped by the number of root kmem_caches, there is no need to shrink or destroy them prematurely. They can be perfectly destroyed together with their root counterparts. This allows to dramatically simplify the management of non-root kmem_caches and delete a ton of code. This patch performs the following changes: 1) introduces memcg_params.memcg_cache pointer to represent the kmem_cache which will be used for all non-root allocations 2) reuses the existing memcg kmem_cache creation mechanism to create memcg kmem_cache on the first allocation attempt 3) memcg kmem_caches are named -memcg, e.g. dentry-memcg 4) simplifies memcg_kmem_get_cache() to just return memcg kmem_cache or schedule it's creation and return the root cache 5) removes almost all non-root kmem_cache management code (separate refcounter, reparenting, shrinking, etc) 6) makes slab debugfs to display root_mem_cgroup css id and never show :dead and :deact flags in the memcg_slabinfo attribute. Following patches in the series will simplify the kmem_cache creation. Signed-off-by: Roman Gushchin Signed-off-by: Andrew Morton Reviewed-by: Vlastimil Babka Reviewed-by: Shakeel Butt Cc: Christoph Lameter Cc: Johannes Weiner Cc: Michal Hocko Cc: Tejun Heo Link: http://lkml.kernel.org/r/20200623174037.3951353-13-guro@fb.com Signed-off-by: Linus Torvalds --- include/linux/memcontrol.h | 5 +- include/linux/slab.h | 5 +- mm/memcontrol.c | 163 +++---------- mm/slab.c | 16 +- mm/slab.h | 146 ++++-------- mm/slab_common.c | 459 ++++--------------------------------- mm/slub.c | 38 +-- 7 files changed, 134 insertions(+), 698 deletions(-) diff --git a/include/linux/memcontrol.h b/include/linux/memcontrol.h index 83e2858aecf25..11fd18b3d6c6a 100644 --- a/include/linux/memcontrol.h +++ b/include/linux/memcontrol.h @@ -317,7 +317,6 @@ struct mem_cgroup { /* Index in the kmem_cache->memcg_params.memcg_caches array */ int kmemcg_id; enum memcg_kmem_state kmem_state; - struct list_head kmem_caches; struct obj_cgroup __rcu *objcg; struct list_head objcg_list; /* list of inherited objcgs */ #endif @@ -1404,9 +1403,7 @@ static inline void memcg_set_shrinker_bit(struct mem_cgroup *memcg, } #endif -struct kmem_cache *memcg_kmem_get_cache(struct kmem_cache *cachep, - struct obj_cgroup **objcgp); -void memcg_kmem_put_cache(struct kmem_cache *cachep); +struct kmem_cache *memcg_kmem_get_cache(struct kmem_cache *cachep); #ifdef CONFIG_MEMCG_KMEM int __memcg_kmem_charge(struct mem_cgroup *memcg, gfp_t gfp, diff --git a/include/linux/slab.h b/include/linux/slab.h index 0884d82c55ee5..8b1f91e320f9c 100644 --- a/include/linux/slab.h +++ b/include/linux/slab.h @@ -155,8 +155,7 @@ struct kmem_cache *kmem_cache_create_usercopy(const char *name, void kmem_cache_destroy(struct kmem_cache *); int kmem_cache_shrink(struct kmem_cache *); -void memcg_create_kmem_cache(struct mem_cgroup *, struct kmem_cache *); -void memcg_deactivate_kmem_caches(struct mem_cgroup *, struct mem_cgroup *); +void memcg_create_kmem_cache(struct kmem_cache *cachep); /* * Please use this macro to create slab caches. Simply specify the @@ -580,8 +579,6 @@ static __always_inline void *kmalloc_node(size_t size, gfp_t flags, int node) return __kmalloc_node(size, flags, node); } -int memcg_update_all_caches(int num_memcgs); - /** * kmalloc_array - allocate memory for an array. * @n: number of elements. diff --git a/mm/memcontrol.c b/mm/memcontrol.c index 5cb2a588cc107..874704c4a48a6 100644 --- a/mm/memcontrol.c +++ b/mm/memcontrol.c @@ -350,7 +350,7 @@ static void memcg_reparent_objcgs(struct mem_cgroup *memcg, } /* - * This will be the memcg's index in each cache's ->memcg_params.memcg_caches. + * This will be used as a shrinker list's index. * The main reason for not using cgroup id for this: * this works better in sparse environments, where we have a lot of memcgs, * but only a few kmem-limited. Or also, if we have, for instance, 200 @@ -569,20 +569,16 @@ ino_t page_cgroup_ino(struct page *page) unsigned long ino = 0; rcu_read_lock(); - if (PageSlab(page) && !PageTail(page)) { - memcg = memcg_from_slab_page(page); - } else { - memcg = page->mem_cgroup; + memcg = page->mem_cgroup; - /* - * The lowest bit set means that memcg isn't a valid - * memcg pointer, but a obj_cgroups pointer. - * In this case the page is shared and doesn't belong - * to any specific memory cgroup. - */ - if ((unsigned long) memcg & 0x1UL) - memcg = NULL; - } + /* + * The lowest bit set means that memcg isn't a valid + * memcg pointer, but a obj_cgroups pointer. + * In this case the page is shared and doesn't belong + * to any specific memory cgroup. + */ + if ((unsigned long) memcg & 0x1UL) + memcg = NULL; while (memcg && !(memcg->css.flags & CSS_ONLINE)) memcg = parent_mem_cgroup(memcg); @@ -2822,12 +2818,18 @@ struct mem_cgroup *mem_cgroup_from_obj(void *p) page = virt_to_head_page(p); /* - * Slab pages don't have page->mem_cgroup set because corresponding - * kmem caches can be reparented during the lifetime. That's why - * memcg_from_slab_page() should be used instead. + * Slab objects are accounted individually, not per-page. + * Memcg membership data for each individual object is saved in + * the page->obj_cgroups. */ - if (PageSlab(page)) - return memcg_from_slab_page(page); + if (page_has_obj_cgroups(page)) { + struct obj_cgroup *objcg; + unsigned int off; + + off = obj_to_index(page->slab_cache, page, p); + objcg = page_obj_cgroups(page)[off]; + return obj_cgroup_memcg(objcg); + } /* All other pages use page->mem_cgroup */ return page->mem_cgroup; @@ -2882,9 +2884,7 @@ static int memcg_alloc_cache_id(void) else if (size > MEMCG_CACHES_MAX_SIZE) size = MEMCG_CACHES_MAX_SIZE; - err = memcg_update_all_caches(size); - if (!err) - err = memcg_update_all_list_lrus(size); + err = memcg_update_all_list_lrus(size); if (!err) memcg_nr_cache_ids = size; @@ -2903,7 +2903,6 @@ static void memcg_free_cache_id(int id) } struct memcg_kmem_cache_create_work { - struct mem_cgroup *memcg; struct kmem_cache *cachep; struct work_struct work; }; @@ -2912,33 +2911,24 @@ static void memcg_kmem_cache_create_func(struct work_struct *w) { struct memcg_kmem_cache_create_work *cw = container_of(w, struct memcg_kmem_cache_create_work, work); - struct mem_cgroup *memcg = cw->memcg; struct kmem_cache *cachep = cw->cachep; - memcg_create_kmem_cache(memcg, cachep); + memcg_create_kmem_cache(cachep); - css_put(&memcg->css); kfree(cw); } /* * Enqueue the creation of a per-memcg kmem_cache. */ -static void memcg_schedule_kmem_cache_create(struct mem_cgroup *memcg, - struct kmem_cache *cachep) +static void memcg_schedule_kmem_cache_create(struct kmem_cache *cachep) { struct memcg_kmem_cache_create_work *cw; - if (!css_tryget_online(&memcg->css)) - return; - cw = kmalloc(sizeof(*cw), GFP_NOWAIT | __GFP_NOWARN); - if (!cw) { - css_put(&memcg->css); + if (!cw) return; - } - cw->memcg = memcg; cw->cachep = cachep; INIT_WORK(&cw->work, memcg_kmem_cache_create_func); @@ -2946,102 +2936,26 @@ static void memcg_schedule_kmem_cache_create(struct mem_cgroup *memcg, } /** - * memcg_kmem_get_cache: select the correct per-memcg cache for allocation + * memcg_kmem_get_cache: select memcg or root cache for allocation * @cachep: the original global kmem cache * * Return the kmem_cache we're supposed to use for a slab allocation. - * We try to use the current memcg's version of the cache. * * If the cache does not exist yet, if we are the first user of it, we * create it asynchronously in a workqueue and let the current allocation * go through with the original cache. - * - * This function takes a reference to the cache it returns to assure it - * won't get destroyed while we are working with it. Once the caller is - * done with it, memcg_kmem_put_cache() must be called to release the - * reference. */ -struct kmem_cache *memcg_kmem_get_cache(struct kmem_cache *cachep, - struct obj_cgroup **objcgp) +struct kmem_cache *memcg_kmem_get_cache(struct kmem_cache *cachep) { - struct mem_cgroup *memcg; struct kmem_cache *memcg_cachep; - struct memcg_cache_array *arr; - int kmemcg_id; - VM_BUG_ON(!is_root_cache(cachep)); - - if (memcg_kmem_bypass()) + memcg_cachep = READ_ONCE(cachep->memcg_params.memcg_cache); + if (unlikely(!memcg_cachep)) { + memcg_schedule_kmem_cache_create(cachep); return cachep; - - rcu_read_lock(); - - if (unlikely(current->active_memcg)) - memcg = current->active_memcg; - else - memcg = mem_cgroup_from_task(current); - - if (!memcg || memcg == root_mem_cgroup) - goto out_unlock; - - kmemcg_id = READ_ONCE(memcg->kmemcg_id); - if (kmemcg_id < 0) - goto out_unlock; - - arr = rcu_dereference(cachep->memcg_params.memcg_caches); - - /* - * Make sure we will access the up-to-date value. The code updating - * memcg_caches issues a write barrier to match the data dependency - * barrier inside READ_ONCE() (see memcg_create_kmem_cache()). - */ - memcg_cachep = READ_ONCE(arr->entries[kmemcg_id]); - - /* - * If we are in a safe context (can wait, and not in interrupt - * context), we could be be predictable and return right away. - * This would guarantee that the allocation being performed - * already belongs in the new cache. - * - * However, there are some clashes that can arrive from locking. - * For instance, because we acquire the slab_mutex while doing - * memcg_create_kmem_cache, this means no further allocation - * could happen with the slab_mutex held. So it's better to - * defer everything. - * - * If the memcg is dying or memcg_cache is about to be released, - * don't bother creating new kmem_caches. Because memcg_cachep - * is ZEROed as the fist step of kmem offlining, we don't need - * percpu_ref_tryget_live() here. css_tryget_online() check in - * memcg_schedule_kmem_cache_create() will prevent us from - * creation of a new kmem_cache. - */ - if (unlikely(!memcg_cachep)) - memcg_schedule_kmem_cache_create(memcg, cachep); - else if (percpu_ref_tryget(&memcg_cachep->memcg_params.refcnt)) { - struct obj_cgroup *objcg = rcu_dereference(memcg->objcg); - - if (!objcg || !obj_cgroup_tryget(objcg)) { - percpu_ref_put(&memcg_cachep->memcg_params.refcnt); - goto out_unlock; - } - - *objcgp = objcg; - cachep = memcg_cachep; } -out_unlock: - rcu_read_unlock(); - return cachep; -} -/** - * memcg_kmem_put_cache: drop reference taken by memcg_kmem_get_cache - * @cachep: the cache returned by memcg_kmem_get_cache - */ -void memcg_kmem_put_cache(struct kmem_cache *cachep) -{ - if (!is_root_cache(cachep)) - percpu_ref_put(&cachep->memcg_params.refcnt); + return memcg_cachep; } /** @@ -3731,7 +3645,6 @@ static int memcg_online_kmem(struct mem_cgroup *memcg) */ memcg->kmemcg_id = memcg_id; memcg->kmem_state = KMEM_ONLINE; - INIT_LIST_HEAD(&memcg->kmem_caches); return 0; } @@ -3744,22 +3657,13 @@ static void memcg_offline_kmem(struct mem_cgroup *memcg) if (memcg->kmem_state != KMEM_ONLINE) return; - /* - * Clear the online state before clearing memcg_caches array - * entries. The slab_mutex in memcg_deactivate_kmem_caches() - * guarantees that no cache will be created for this cgroup - * after we are done (see memcg_create_kmem_cache()). - */ + memcg->kmem_state = KMEM_ALLOCATED; parent = parent_mem_cgroup(memcg); if (!parent) parent = root_mem_cgroup; - /* - * Deactivate and reparent kmem_caches and objcgs. - */ - memcg_deactivate_kmem_caches(memcg, parent); memcg_reparent_objcgs(memcg, parent); kmemcg_id = memcg->kmemcg_id; @@ -5384,9 +5288,6 @@ mem_cgroup_css_alloc(struct cgroup_subsys_state *parent_css) /* The following stuff does not apply to the root */ if (!parent) { -#ifdef CONFIG_MEMCG_KMEM - INIT_LIST_HEAD(&memcg->kmem_caches); -#endif root_mem_cgroup = memcg; return &memcg->css; } diff --git a/mm/slab.c b/mm/slab.c index 1e90b67735aa5..0dd6956585dca 100644 --- a/mm/slab.c +++ b/mm/slab.c @@ -1249,7 +1249,7 @@ void __init kmem_cache_init(void) nr_node_ids * sizeof(struct kmem_cache_node *), SLAB_HWCACHE_ALIGN, 0, 0); list_add(&kmem_cache->list, &slab_caches); - memcg_link_cache(kmem_cache, NULL); + memcg_link_cache(kmem_cache); slab_state = PARTIAL; /* @@ -2253,17 +2253,6 @@ int __kmem_cache_shrink(struct kmem_cache *cachep) return (ret ? 1 : 0); } -#ifdef CONFIG_MEMCG -void __kmemcg_cache_deactivate(struct kmem_cache *cachep) -{ - __kmem_cache_shrink(cachep); -} - -void __kmemcg_cache_deactivate_after_rcu(struct kmem_cache *s) -{ -} -#endif - int __kmem_cache_shutdown(struct kmem_cache *cachep) { return __kmem_cache_shrink(cachep); @@ -3872,7 +3861,8 @@ static int do_tune_cpucache(struct kmem_cache *cachep, int limit, return ret; lockdep_assert_held(&slab_mutex); - for_each_memcg_cache(c, cachep) { + c = memcg_cache(cachep); + if (c) { /* return value determined by the root cache only */ __do_tune_cpucache(c, limit, batchcount, shared, gfp); } diff --git a/mm/slab.h b/mm/slab.h index 1f067e8bc3778..e716b80befc2f 100644 --- a/mm/slab.h +++ b/mm/slab.h @@ -32,66 +32,25 @@ struct kmem_cache { #else /* !CONFIG_SLOB */ -struct memcg_cache_array { - struct rcu_head rcu; - struct kmem_cache *entries[0]; -}; - /* * This is the main placeholder for memcg-related information in kmem caches. - * Both the root cache and the child caches will have it. For the root cache, - * this will hold a dynamically allocated array large enough to hold - * information about the currently limited memcgs in the system. To allow the - * array to be accessed without taking any locks, on relocation we free the old - * version only after a grace period. - * - * Root and child caches hold different metadata. + * Both the root cache and the child cache will have it. Some fields are used + * in both cases, other are specific to root caches. * * @root_cache: Common to root and child caches. NULL for root, pointer to * the root cache for children. * * The following fields are specific to root caches. * - * @memcg_caches: kmemcg ID indexed table of child caches. This table is - * used to index child cachces during allocation and cleared - * early during shutdown. - * - * @root_caches_node: List node for slab_root_caches list. - * - * @children: List of all child caches. While the child caches are also - * reachable through @memcg_caches, a child cache remains on - * this list until it is actually destroyed. - * - * The following fields are specific to child caches. - * - * @memcg: Pointer to the memcg this cache belongs to. - * - * @children_node: List node for @root_cache->children list. - * - * @kmem_caches_node: List node for @memcg->kmem_caches list. + * @memcg_cache: pointer to memcg kmem cache, used by all non-root memory + * cgroups. + * @root_caches_node: list node for slab_root_caches list. */ struct memcg_cache_params { struct kmem_cache *root_cache; - union { - struct { - struct memcg_cache_array __rcu *memcg_caches; - struct list_head __root_caches_node; - struct list_head children; - bool dying; - }; - struct { - struct mem_cgroup *memcg; - struct list_head children_node; - struct list_head kmem_caches_node; - struct percpu_ref refcnt; - - void (*work_fn)(struct kmem_cache *); - union { - struct rcu_head rcu_head; - struct work_struct work; - }; - }; - }; + + struct kmem_cache *memcg_cache; + struct list_head __root_caches_node; }; #endif /* CONFIG_SLOB */ @@ -236,8 +195,6 @@ bool __kmem_cache_empty(struct kmem_cache *); int __kmem_cache_shutdown(struct kmem_cache *); void __kmem_cache_release(struct kmem_cache *); int __kmem_cache_shrink(struct kmem_cache *); -void __kmemcg_cache_deactivate(struct kmem_cache *s); -void __kmemcg_cache_deactivate_after_rcu(struct kmem_cache *s); void slab_kmem_cache_release(struct kmem_cache *); void kmem_cache_shrink_all(struct kmem_cache *s); @@ -311,14 +268,6 @@ static inline bool kmem_cache_debug_flags(struct kmem_cache *s, slab_flags_t fla extern struct list_head slab_root_caches; #define root_caches_node memcg_params.__root_caches_node -/* - * Iterate over all memcg caches of the given root cache. The caller must hold - * slab_mutex. - */ -#define for_each_memcg_cache(iter, root) \ - list_for_each_entry(iter, &(root)->memcg_params.children, \ - memcg_params.children_node) - static inline bool is_root_cache(struct kmem_cache *s) { return !s->memcg_params.root_cache; @@ -349,6 +298,13 @@ static inline struct kmem_cache *memcg_root_cache(struct kmem_cache *s) return s->memcg_params.root_cache; } +static inline struct kmem_cache *memcg_cache(struct kmem_cache *s) +{ + if (is_root_cache(s)) + return s->memcg_params.memcg_cache; + return NULL; +} + static inline struct obj_cgroup **page_obj_cgroups(struct page *page) { /* @@ -361,25 +317,9 @@ static inline struct obj_cgroup **page_obj_cgroups(struct page *page) ((unsigned long)page->obj_cgroups & ~0x1UL); } -/* - * Expects a pointer to a slab page. Please note, that PageSlab() check - * isn't sufficient, as it returns true also for tail compound slab pages, - * which do not have slab_cache pointer set. - * So this function assumes that the page can pass PageSlab() && !PageTail() - * check. - * - * The kmem_cache can be reparented asynchronously. The caller must ensure - * the memcg lifetime, e.g. by taking rcu_read_lock() or cgroup_mutex. - */ -static inline struct mem_cgroup *memcg_from_slab_page(struct page *page) +static inline bool page_has_obj_cgroups(struct page *page) { - struct kmem_cache *s; - - s = READ_ONCE(page->slab_cache); - if (s && !is_root_cache(s)) - return READ_ONCE(s->memcg_params.memcg); - - return NULL; + return ((unsigned long)page->obj_cgroups & 0x1UL); } static inline int memcg_alloc_page_obj_cgroups(struct page *page, @@ -418,17 +358,25 @@ static inline struct kmem_cache *memcg_slab_pre_alloc_hook(struct kmem_cache *s, size_t objects, gfp_t flags) { struct kmem_cache *cachep; + struct obj_cgroup *objcg; + + if (memcg_kmem_bypass()) + return s; - cachep = memcg_kmem_get_cache(s, objcgp); + cachep = memcg_kmem_get_cache(s); if (is_root_cache(cachep)) return s; - if (obj_cgroup_charge(*objcgp, flags, objects * obj_full_size(s))) { - obj_cgroup_put(*objcgp); - memcg_kmem_put_cache(cachep); + objcg = get_obj_cgroup_from_current(); + if (!objcg) + return s; + + if (obj_cgroup_charge(objcg, flags, objects * obj_full_size(s))) { + obj_cgroup_put(objcg); cachep = NULL; } + *objcgp = objcg; return cachep; } @@ -467,7 +415,6 @@ static inline void memcg_slab_post_alloc_hook(struct kmem_cache *s, } } obj_cgroup_put(objcg); - memcg_kmem_put_cache(s); } static inline void memcg_slab_free_hook(struct kmem_cache *s, struct page *page, @@ -491,7 +438,7 @@ static inline void memcg_slab_free_hook(struct kmem_cache *s, struct page *page, } extern void slab_init_memcg_params(struct kmem_cache *); -extern void memcg_link_cache(struct kmem_cache *s, struct mem_cgroup *memcg); +extern void memcg_link_cache(struct kmem_cache *s); #else /* CONFIG_MEMCG_KMEM */ @@ -499,9 +446,6 @@ extern void memcg_link_cache(struct kmem_cache *s, struct mem_cgroup *memcg); #define slab_root_caches slab_caches #define root_caches_node list -#define for_each_memcg_cache(iter, root) \ - for ((void)(iter), (void)(root); 0; ) - static inline bool is_root_cache(struct kmem_cache *s) { return true; @@ -523,7 +467,17 @@ static inline struct kmem_cache *memcg_root_cache(struct kmem_cache *s) return s; } -static inline struct mem_cgroup *memcg_from_slab_page(struct page *page) +static inline struct kmem_cache *memcg_cache(struct kmem_cache *s) +{ + return NULL; +} + +static inline bool page_has_obj_cgroups(struct page *page) +{ + return false; +} + +static inline struct mem_cgroup *memcg_from_slab_obj(void *ptr) { return NULL; } @@ -560,8 +514,7 @@ static inline void slab_init_memcg_params(struct kmem_cache *s) { } -static inline void memcg_link_cache(struct kmem_cache *s, - struct mem_cgroup *memcg) +static inline void memcg_link_cache(struct kmem_cache *s) { } @@ -582,17 +535,14 @@ static __always_inline int charge_slab_page(struct page *page, gfp_t gfp, int order, struct kmem_cache *s) { -#ifdef CONFIG_MEMCG_KMEM if (memcg_kmem_enabled() && !is_root_cache(s)) { int ret; ret = memcg_alloc_page_obj_cgroups(page, s, gfp); if (ret) return ret; - - percpu_ref_get_many(&s->memcg_params.refcnt, 1 << order); } -#endif + mod_node_page_state(page_pgdat(page), cache_vmstat_idx(s), PAGE_SIZE << order); return 0; @@ -601,12 +551,9 @@ static __always_inline int charge_slab_page(struct page *page, static __always_inline void uncharge_slab_page(struct page *page, int order, struct kmem_cache *s) { -#ifdef CONFIG_MEMCG_KMEM - if (memcg_kmem_enabled() && !is_root_cache(s)) { + if (memcg_kmem_enabled() && !is_root_cache(s)) memcg_free_page_obj_cgroups(page); - percpu_ref_put_many(&s->memcg_params.refcnt, 1 << order); - } -#endif + mod_node_page_state(page_pgdat(page), cache_vmstat_idx(s), -(PAGE_SIZE << order)); } @@ -749,9 +696,6 @@ static inline struct kmem_cache_node *get_node(struct kmem_cache *s, int node) void *slab_start(struct seq_file *m, loff_t *pos); void *slab_next(struct seq_file *m, void *p, loff_t *pos); void slab_stop(struct seq_file *m, void *p); -void *memcg_slab_start(struct seq_file *m, loff_t *pos); -void *memcg_slab_next(struct seq_file *m, void *p, loff_t *pos); -void memcg_slab_stop(struct seq_file *m, void *p); int memcg_slab_show(struct seq_file *m, void *p); #if defined(CONFIG_SLAB) || defined(CONFIG_SLUB_DEBUG) diff --git a/mm/slab_common.c b/mm/slab_common.c index f86431d0de739..e752132eb64dc 100644 --- a/mm/slab_common.c +++ b/mm/slab_common.c @@ -133,141 +133,36 @@ int __kmem_cache_alloc_bulk(struct kmem_cache *s, gfp_t flags, size_t nr, #ifdef CONFIG_MEMCG_KMEM LIST_HEAD(slab_root_caches); -static DEFINE_SPINLOCK(memcg_kmem_wq_lock); - -static void kmemcg_cache_shutdown(struct percpu_ref *percpu_ref); void slab_init_memcg_params(struct kmem_cache *s) { s->memcg_params.root_cache = NULL; - RCU_INIT_POINTER(s->memcg_params.memcg_caches, NULL); - INIT_LIST_HEAD(&s->memcg_params.children); - s->memcg_params.dying = false; + s->memcg_params.memcg_cache = NULL; } -static int init_memcg_params(struct kmem_cache *s, - struct kmem_cache *root_cache) +static void init_memcg_params(struct kmem_cache *s, + struct kmem_cache *root_cache) { - struct memcg_cache_array *arr; - - if (root_cache) { - int ret = percpu_ref_init(&s->memcg_params.refcnt, - kmemcg_cache_shutdown, - 0, GFP_KERNEL); - if (ret) - return ret; - + if (root_cache) s->memcg_params.root_cache = root_cache; - INIT_LIST_HEAD(&s->memcg_params.children_node); - INIT_LIST_HEAD(&s->memcg_params.kmem_caches_node); - return 0; - } - - slab_init_memcg_params(s); - - if (!memcg_nr_cache_ids) - return 0; - - arr = kvzalloc(sizeof(struct memcg_cache_array) + - memcg_nr_cache_ids * sizeof(void *), - GFP_KERNEL); - if (!arr) - return -ENOMEM; - - RCU_INIT_POINTER(s->memcg_params.memcg_caches, arr); - return 0; -} - -static void destroy_memcg_params(struct kmem_cache *s) -{ - if (is_root_cache(s)) { - kvfree(rcu_access_pointer(s->memcg_params.memcg_caches)); - } else { - mem_cgroup_put(s->memcg_params.memcg); - WRITE_ONCE(s->memcg_params.memcg, NULL); - percpu_ref_exit(&s->memcg_params.refcnt); - } -} - -static void free_memcg_params(struct rcu_head *rcu) -{ - struct memcg_cache_array *old; - - old = container_of(rcu, struct memcg_cache_array, rcu); - kvfree(old); -} - -static int update_memcg_params(struct kmem_cache *s, int new_array_size) -{ - struct memcg_cache_array *old, *new; - - new = kvzalloc(sizeof(struct memcg_cache_array) + - new_array_size * sizeof(void *), GFP_KERNEL); - if (!new) - return -ENOMEM; - - old = rcu_dereference_protected(s->memcg_params.memcg_caches, - lockdep_is_held(&slab_mutex)); - if (old) - memcpy(new->entries, old->entries, - memcg_nr_cache_ids * sizeof(void *)); - - rcu_assign_pointer(s->memcg_params.memcg_caches, new); - if (old) - call_rcu(&old->rcu, free_memcg_params); - return 0; + else + slab_init_memcg_params(s); } -int memcg_update_all_caches(int num_memcgs) +void memcg_link_cache(struct kmem_cache *s) { - struct kmem_cache *s; - int ret = 0; - - mutex_lock(&slab_mutex); - list_for_each_entry(s, &slab_root_caches, root_caches_node) { - ret = update_memcg_params(s, num_memcgs); - /* - * Instead of freeing the memory, we'll just leave the caches - * up to this point in an updated state. - */ - if (ret) - break; - } - mutex_unlock(&slab_mutex); - return ret; -} - -void memcg_link_cache(struct kmem_cache *s, struct mem_cgroup *memcg) -{ - if (is_root_cache(s)) { + if (is_root_cache(s)) list_add(&s->root_caches_node, &slab_root_caches); - } else { - css_get(&memcg->css); - s->memcg_params.memcg = memcg; - list_add(&s->memcg_params.children_node, - &s->memcg_params.root_cache->memcg_params.children); - list_add(&s->memcg_params.kmem_caches_node, - &s->memcg_params.memcg->kmem_caches); - } } static void memcg_unlink_cache(struct kmem_cache *s) { - if (is_root_cache(s)) { + if (is_root_cache(s)) list_del(&s->root_caches_node); - } else { - list_del(&s->memcg_params.children_node); - list_del(&s->memcg_params.kmem_caches_node); - } } #else -static inline int init_memcg_params(struct kmem_cache *s, - struct kmem_cache *root_cache) -{ - return 0; -} - -static inline void destroy_memcg_params(struct kmem_cache *s) +static inline void init_memcg_params(struct kmem_cache *s, + struct kmem_cache *root_cache) { } @@ -328,14 +223,6 @@ int slab_unmergeable(struct kmem_cache *s) if (s->refcount < 0) return 1; -#ifdef CONFIG_MEMCG_KMEM - /* - * Skip the dying kmem_cache. - */ - if (s->memcg_params.dying) - return 1; -#endif - return 0; } @@ -390,7 +277,7 @@ static struct kmem_cache *create_cache(const char *name, unsigned int object_size, unsigned int align, slab_flags_t flags, unsigned int useroffset, unsigned int usersize, void (*ctor)(void *), - struct mem_cgroup *memcg, struct kmem_cache *root_cache) + struct kmem_cache *root_cache) { struct kmem_cache *s; int err; @@ -410,24 +297,20 @@ static struct kmem_cache *create_cache(const char *name, s->useroffset = useroffset; s->usersize = usersize; - err = init_memcg_params(s, root_cache); - if (err) - goto out_free_cache; - + init_memcg_params(s, root_cache); err = __kmem_cache_create(s, flags); if (err) goto out_free_cache; s->refcount = 1; list_add(&s->list, &slab_caches); - memcg_link_cache(s, memcg); + memcg_link_cache(s); out: if (err) return ERR_PTR(err); return s; out_free_cache: - destroy_memcg_params(s); kmem_cache_free(kmem_cache, s); goto out; } @@ -514,7 +397,7 @@ kmem_cache_create_usercopy(const char *name, s = create_cache(cache_name, size, calculate_alignment(flags, align, size), - flags, useroffset, usersize, ctor, NULL, NULL); + flags, useroffset, usersize, ctor, NULL); if (IS_ERR(s)) { err = PTR_ERR(s); kfree_const(cache_name); @@ -639,51 +522,27 @@ static int shutdown_cache(struct kmem_cache *s) #ifdef CONFIG_MEMCG_KMEM /* - * memcg_create_kmem_cache - Create a cache for a memory cgroup. - * @memcg: The memory cgroup the new cache is for. + * memcg_create_kmem_cache - Create a cache for non-root memory cgroups. * @root_cache: The parent of the new cache. * * This function attempts to create a kmem cache that will serve allocation - * requests going from @memcg to @root_cache. The new cache inherits properties - * from its parent. + * requests going all non-root memory cgroups to @root_cache. The new cache + * inherits properties from its parent. */ -void memcg_create_kmem_cache(struct mem_cgroup *memcg, - struct kmem_cache *root_cache) +void memcg_create_kmem_cache(struct kmem_cache *root_cache) { - static char memcg_name_buf[NAME_MAX + 1]; /* protected by slab_mutex */ - struct cgroup_subsys_state *css = &memcg->css; - struct memcg_cache_array *arr; struct kmem_cache *s = NULL; char *cache_name; - int idx; get_online_cpus(); get_online_mems(); mutex_lock(&slab_mutex); - /* - * The memory cgroup could have been offlined while the cache - * creation work was pending. - */ - if (memcg->kmem_state != KMEM_ONLINE) + if (root_cache->memcg_params.memcg_cache) goto out_unlock; - idx = memcg_cache_id(memcg); - arr = rcu_dereference_protected(root_cache->memcg_params.memcg_caches, - lockdep_is_held(&slab_mutex)); - - /* - * Since per-memcg caches are created asynchronously on first - * allocation (see memcg_kmem_get_cache()), several threads can try to - * create the same cache, but only one of them may succeed. - */ - if (arr->entries[idx]) - goto out_unlock; - - cgroup_name(css->cgroup, memcg_name_buf, sizeof(memcg_name_buf)); - cache_name = kasprintf(GFP_KERNEL, "%s(%llu:%s)", root_cache->name, - css->serial_nr, memcg_name_buf); + cache_name = kasprintf(GFP_KERNEL, "%s-memcg", root_cache->name); if (!cache_name) goto out_unlock; @@ -691,7 +550,7 @@ void memcg_create_kmem_cache(struct mem_cgroup *memcg, root_cache->align, root_cache->flags & CACHE_CREATE_MASK, root_cache->useroffset, root_cache->usersize, - root_cache->ctor, memcg, root_cache); + root_cache->ctor, root_cache); /* * If we could not create a memcg cache, do not complain, because * that's not critical at all as we can always proceed with the root @@ -708,7 +567,7 @@ void memcg_create_kmem_cache(struct mem_cgroup *memcg, * initialized. */ smp_wmb(); - arr->entries[idx] = s; + root_cache->memcg_params.memcg_cache = s; out_unlock: mutex_unlock(&slab_mutex); @@ -717,200 +576,18 @@ out_unlock: put_online_cpus(); } -static void kmemcg_workfn(struct work_struct *work) -{ - struct kmem_cache *s = container_of(work, struct kmem_cache, - memcg_params.work); - - get_online_cpus(); - get_online_mems(); - - mutex_lock(&slab_mutex); - s->memcg_params.work_fn(s); - mutex_unlock(&slab_mutex); - - put_online_mems(); - put_online_cpus(); -} - -static void kmemcg_rcufn(struct rcu_head *head) -{ - struct kmem_cache *s = container_of(head, struct kmem_cache, - memcg_params.rcu_head); - - /* - * We need to grab blocking locks. Bounce to ->work. The - * work item shares the space with the RCU head and can't be - * initialized earlier. - */ - INIT_WORK(&s->memcg_params.work, kmemcg_workfn); - queue_work(memcg_kmem_cache_wq, &s->memcg_params.work); -} - -static void kmemcg_cache_shutdown_fn(struct kmem_cache *s) -{ - WARN_ON(shutdown_cache(s)); -} - -static void kmemcg_cache_shutdown(struct percpu_ref *percpu_ref) -{ - struct kmem_cache *s = container_of(percpu_ref, struct kmem_cache, - memcg_params.refcnt); - unsigned long flags; - - spin_lock_irqsave(&memcg_kmem_wq_lock, flags); - if (s->memcg_params.root_cache->memcg_params.dying) - goto unlock; - - s->memcg_params.work_fn = kmemcg_cache_shutdown_fn; - INIT_WORK(&s->memcg_params.work, kmemcg_workfn); - queue_work(memcg_kmem_cache_wq, &s->memcg_params.work); - -unlock: - spin_unlock_irqrestore(&memcg_kmem_wq_lock, flags); -} - -static void kmemcg_cache_deactivate_after_rcu(struct kmem_cache *s) -{ - __kmemcg_cache_deactivate_after_rcu(s); - percpu_ref_kill(&s->memcg_params.refcnt); -} - -static void kmemcg_cache_deactivate(struct kmem_cache *s) -{ - if (WARN_ON_ONCE(is_root_cache(s))) - return; - - __kmemcg_cache_deactivate(s); - s->flags |= SLAB_DEACTIVATED; - - /* - * memcg_kmem_wq_lock is used to synchronize memcg_params.dying - * flag and make sure that no new kmem_cache deactivation tasks - * are queued (see flush_memcg_workqueue() ). - */ - spin_lock_irq(&memcg_kmem_wq_lock); - if (s->memcg_params.root_cache->memcg_params.dying) - goto unlock; - - s->memcg_params.work_fn = kmemcg_cache_deactivate_after_rcu; - call_rcu(&s->memcg_params.rcu_head, kmemcg_rcufn); -unlock: - spin_unlock_irq(&memcg_kmem_wq_lock); -} - -void memcg_deactivate_kmem_caches(struct mem_cgroup *memcg, - struct mem_cgroup *parent) -{ - int idx; - struct memcg_cache_array *arr; - struct kmem_cache *s, *c; - unsigned int nr_reparented; - - idx = memcg_cache_id(memcg); - - get_online_cpus(); - get_online_mems(); - - mutex_lock(&slab_mutex); - list_for_each_entry(s, &slab_root_caches, root_caches_node) { - arr = rcu_dereference_protected(s->memcg_params.memcg_caches, - lockdep_is_held(&slab_mutex)); - c = arr->entries[idx]; - if (!c) - continue; - - kmemcg_cache_deactivate(c); - arr->entries[idx] = NULL; - } - nr_reparented = 0; - list_for_each_entry(s, &memcg->kmem_caches, - memcg_params.kmem_caches_node) { - WRITE_ONCE(s->memcg_params.memcg, parent); - css_put(&memcg->css); - nr_reparented++; - } - if (nr_reparented) { - list_splice_init(&memcg->kmem_caches, - &parent->kmem_caches); - css_get_many(&parent->css, nr_reparented); - } - mutex_unlock(&slab_mutex); - - put_online_mems(); - put_online_cpus(); -} - static int shutdown_memcg_caches(struct kmem_cache *s) { - struct memcg_cache_array *arr; - struct kmem_cache *c, *c2; - LIST_HEAD(busy); - int i; - BUG_ON(!is_root_cache(s)); - /* - * First, shutdown active caches, i.e. caches that belong to online - * memory cgroups. - */ - arr = rcu_dereference_protected(s->memcg_params.memcg_caches, - lockdep_is_held(&slab_mutex)); - for_each_memcg_cache_index(i) { - c = arr->entries[i]; - if (!c) - continue; - if (shutdown_cache(c)) - /* - * The cache still has objects. Move it to a temporary - * list so as not to try to destroy it for a second - * time while iterating over inactive caches below. - */ - list_move(&c->memcg_params.children_node, &busy); - else - /* - * The cache is empty and will be destroyed soon. Clear - * the pointer to it in the memcg_caches array so that - * it will never be accessed even if the root cache - * stays alive. - */ - arr->entries[i] = NULL; - } - - /* - * Second, shutdown all caches left from memory cgroups that are now - * offline. - */ - list_for_each_entry_safe(c, c2, &s->memcg_params.children, - memcg_params.children_node) - shutdown_cache(c); - - list_splice(&busy, &s->memcg_params.children); + if (s->memcg_params.memcg_cache) + WARN_ON(shutdown_cache(s->memcg_params.memcg_cache)); - /* - * A cache being destroyed must be empty. In particular, this means - * that all per memcg caches attached to it must be empty too. - */ - if (!list_empty(&s->memcg_params.children)) - return -EBUSY; return 0; } -static void memcg_set_kmem_cache_dying(struct kmem_cache *s) -{ - spin_lock_irq(&memcg_kmem_wq_lock); - s->memcg_params.dying = true; - spin_unlock_irq(&memcg_kmem_wq_lock); -} - static void flush_memcg_workqueue(struct kmem_cache *s) { - /* - * SLAB and SLUB deactivate the kmem_caches through call_rcu. Make - * sure all registered rcu callbacks have been invoked. - */ - rcu_barrier(); - /* * SLAB and SLUB create memcg kmem_caches through workqueue and SLUB * deactivates the memcg kmem_caches through workqueue. Make sure all @@ -918,30 +595,21 @@ static void flush_memcg_workqueue(struct kmem_cache *s) */ if (likely(memcg_kmem_cache_wq)) flush_workqueue(memcg_kmem_cache_wq); - - /* - * If we're racing with children kmem_cache deactivation, it might - * take another rcu grace period to complete their destruction. - * At this moment the corresponding percpu_ref_kill() call should be - * done, but it might take another rcu grace period to complete - * switching to the atomic mode. - * Please, note that we check without grabbing the slab_mutex. It's safe - * because at this moment the children list can't grow. - */ - if (!list_empty(&s->memcg_params.children)) - rcu_barrier(); } #else static inline int shutdown_memcg_caches(struct kmem_cache *s) { return 0; } + +static inline void flush_memcg_workqueue(struct kmem_cache *s) +{ +} #endif /* CONFIG_MEMCG_KMEM */ void slab_kmem_cache_release(struct kmem_cache *s) { __kmem_cache_release(s); - destroy_memcg_params(s); kfree_const(s->name); kmem_cache_free(kmem_cache, s); } @@ -953,6 +621,8 @@ void kmem_cache_destroy(struct kmem_cache *s) if (unlikely(!s)) return; + flush_memcg_workqueue(s); + get_online_cpus(); get_online_mems(); @@ -962,22 +632,6 @@ void kmem_cache_destroy(struct kmem_cache *s) if (s->refcount) goto out_unlock; -#ifdef CONFIG_MEMCG_KMEM - memcg_set_kmem_cache_dying(s); - - mutex_unlock(&slab_mutex); - - put_online_mems(); - put_online_cpus(); - - flush_memcg_workqueue(s); - - get_online_cpus(); - get_online_mems(); - - mutex_lock(&slab_mutex); -#endif - err = shutdown_memcg_caches(s); if (!err) err = shutdown_cache(s); @@ -1019,7 +673,7 @@ int kmem_cache_shrink(struct kmem_cache *cachep) EXPORT_SYMBOL(kmem_cache_shrink); /** - * kmem_cache_shrink_all - shrink a cache and all memcg caches for root cache + * kmem_cache_shrink_all - shrink root and memcg caches * @s: The cache pointer */ void kmem_cache_shrink_all(struct kmem_cache *s) @@ -1036,21 +690,11 @@ void kmem_cache_shrink_all(struct kmem_cache *s) kasan_cache_shrink(s); __kmem_cache_shrink(s); - /* - * We have to take the slab_mutex to protect from the memcg list - * modification. - */ - mutex_lock(&slab_mutex); - for_each_memcg_cache(c, s) { - /* - * Don't need to shrink deactivated memcg caches. - */ - if (s->flags & SLAB_DEACTIVATED) - continue; + c = memcg_cache(s); + if (c) { kasan_cache_shrink(c); __kmem_cache_shrink(c); } - mutex_unlock(&slab_mutex); put_online_mems(); put_online_cpus(); } @@ -1105,7 +749,7 @@ struct kmem_cache *__init create_kmalloc_cache(const char *name, create_boot_cache(s, name, size, flags, useroffset, usersize); list_add(&s->list, &slab_caches); - memcg_link_cache(s, NULL); + memcg_link_cache(s); s->refcount = 1; return s; } @@ -1483,7 +1127,8 @@ memcg_accumulate_slabinfo(struct kmem_cache *s, struct slabinfo *info) if (!is_root_cache(s)) return; - for_each_memcg_cache(c, s) { + c = memcg_cache(s); + if (c) { memset(&sinfo, 0, sizeof(sinfo)); get_slabinfo(c, &sinfo); @@ -1614,7 +1259,7 @@ module_init(slab_proc_init); #if defined(CONFIG_DEBUG_FS) && defined(CONFIG_MEMCG_KMEM) /* - * Display information about kmem caches that have child memcg caches. + * Display information about kmem caches that have memcg cache. */ static int memcg_slabinfo_show(struct seq_file *m, void *unused) { @@ -1626,9 +1271,9 @@ static int memcg_slabinfo_show(struct seq_file *m, void *unused) seq_puts(m, " \n"); list_for_each_entry(s, &slab_root_caches, root_caches_node) { /* - * Skip kmem caches that don't have any memcg children. + * Skip kmem caches that don't have the memcg cache. */ - if (list_empty(&s->memcg_params.children)) + if (!s->memcg_params.memcg_cache) continue; memset(&sinfo, 0, sizeof(sinfo)); @@ -1637,23 +1282,13 @@ static int memcg_slabinfo_show(struct seq_file *m, void *unused) cache_name(s), sinfo.active_objs, sinfo.num_objs, sinfo.active_slabs, sinfo.num_slabs); - for_each_memcg_cache(c, s) { - struct cgroup_subsys_state *css; - char *status = ""; - - css = &c->memcg_params.memcg->css; - if (!(css->flags & CSS_ONLINE)) - status = ":dead"; - else if (c->flags & SLAB_DEACTIVATED) - status = ":deact"; - - memset(&sinfo, 0, sizeof(sinfo)); - get_slabinfo(c, &sinfo); - seq_printf(m, "%-17s %4d%-6s %6lu %6lu %6lu %6lu\n", - cache_name(c), css->id, status, - sinfo.active_objs, sinfo.num_objs, - sinfo.active_slabs, sinfo.num_slabs); - } + c = s->memcg_params.memcg_cache; + memset(&sinfo, 0, sizeof(sinfo)); + get_slabinfo(c, &sinfo); + seq_printf(m, "%-17s %4d %6lu %6lu %6lu %6lu\n", + cache_name(c), root_mem_cgroup->css.id, + sinfo.active_objs, sinfo.num_objs, + sinfo.active_slabs, sinfo.num_slabs); } mutex_unlock(&slab_mutex); return 0; diff --git a/mm/slub.c b/mm/slub.c index 47e63b1100d49..44a48a08a6919 100644 --- a/mm/slub.c +++ b/mm/slub.c @@ -4204,36 +4204,6 @@ int __kmem_cache_shrink(struct kmem_cache *s) return ret; } -#ifdef CONFIG_MEMCG -void __kmemcg_cache_deactivate_after_rcu(struct kmem_cache *s) -{ - /* - * Called with all the locks held after a sched RCU grace period. - * Even if @s becomes empty after shrinking, we can't know that @s - * doesn't have allocations already in-flight and thus can't - * destroy @s until the associated memcg is released. - * - * However, let's remove the sysfs files for empty caches here. - * Each cache has a lot of interface files which aren't - * particularly useful for empty draining caches; otherwise, we can - * easily end up with millions of unnecessary sysfs files on - * systems which have a lot of memory and transient cgroups. - */ - if (!__kmem_cache_shrink(s)) - sysfs_slab_remove(s); -} - -void __kmemcg_cache_deactivate(struct kmem_cache *s) -{ - /* - * Disable empty slabs caching. Used to avoid pinning offline - * memory cgroups by kmem pages that can be freed. - */ - slub_set_cpu_partial(s, 0); - s->min_partial = 0; -} -#endif /* CONFIG_MEMCG */ - static int slab_mem_going_offline_callback(void *arg) { struct kmem_cache *s; @@ -4390,7 +4360,7 @@ static struct kmem_cache * __init bootstrap(struct kmem_cache *static_cache) } slab_init_memcg_params(s); list_add(&s->list, &slab_caches); - memcg_link_cache(s, NULL); + memcg_link_cache(s); return s; } @@ -4458,7 +4428,8 @@ __kmem_cache_alias(const char *name, unsigned int size, unsigned int align, s->object_size = max(s->object_size, size); s->inuse = max(s->inuse, ALIGN(size, sizeof(void *))); - for_each_memcg_cache(c, s) { + c = memcg_cache(s); + if (c) { c->object_size = s->object_size; c->inuse = max(c->inuse, ALIGN(size, sizeof(void *))); } @@ -5591,7 +5562,8 @@ static ssize_t slab_attr_store(struct kobject *kobj, * directly either failed or succeeded, in which case we loop * through the descendants with best-effort propagation. */ - for_each_memcg_cache(c, s) + c = memcg_cache(s); + if (c) attribute->store(c, buf, len); mutex_unlock(&slab_mutex); } -- GitLab From d797b7d05405c519f7b62ea69a75cea1883863b2 Mon Sep 17 00:00:00 2001 From: Roman Gushchin Date: Thu, 6 Aug 2020 23:21:14 -0700 Subject: [PATCH 1383/1476] mm: memcg/slab: simplify memcg cache creation Because the number of non-root kmem_caches doesn't depend on the number of memory cgroups anymore and is generally not very big, there is no more need for a dedicated workqueue. Also, as there is no more need to pass any arguments to the memcg_create_kmem_cache() except the root kmem_cache, it's possible to just embed the work structure into the kmem_cache and avoid the dynamic allocation of the work structure. This will also simplify the synchronization: for each root kmem_cache there is only one work. So there will be no more concurrent attempts to create a non-root kmem_cache for a root kmem_cache: the second and all following attempts to queue the work will fail. On the kmem_cache destruction path there is no more need to call the expensive flush_workqueue() and wait for all pending works to be finished. Instead, cancel_work_sync() can be used to cancel/wait for only one work. Signed-off-by: Roman Gushchin Signed-off-by: Andrew Morton Reviewed-by: Vlastimil Babka Reviewed-by: Shakeel Butt Cc: Christoph Lameter Cc: Johannes Weiner Cc: Michal Hocko Cc: Tejun Heo Link: http://lkml.kernel.org/r/20200623174037.3951353-14-guro@fb.com Signed-off-by: Linus Torvalds --- include/linux/memcontrol.h | 1 - mm/memcontrol.c | 48 +------------------------------------- mm/slab.h | 2 ++ mm/slab_common.c | 22 +++++++++-------- 4 files changed, 15 insertions(+), 58 deletions(-) diff --git a/include/linux/memcontrol.h b/include/linux/memcontrol.h index 11fd18b3d6c6a..2ac84dcfc9e5a 100644 --- a/include/linux/memcontrol.h +++ b/include/linux/memcontrol.h @@ -1418,7 +1418,6 @@ int obj_cgroup_charge(struct obj_cgroup *objcg, gfp_t gfp, size_t size); void obj_cgroup_uncharge(struct obj_cgroup *objcg, size_t size); extern struct static_key_false memcg_kmem_enabled_key; -extern struct workqueue_struct *memcg_kmem_cache_wq; extern int memcg_nr_cache_ids; void memcg_get_cache_ids(void); diff --git a/mm/memcontrol.c b/mm/memcontrol.c index 874704c4a48a6..c713867e496d3 100644 --- a/mm/memcontrol.c +++ b/mm/memcontrol.c @@ -399,8 +399,6 @@ void memcg_put_cache_ids(void) */ DEFINE_STATIC_KEY_FALSE(memcg_kmem_enabled_key); EXPORT_SYMBOL(memcg_kmem_enabled_key); - -struct workqueue_struct *memcg_kmem_cache_wq; #endif static int memcg_shrinker_map_size; @@ -2902,39 +2900,6 @@ static void memcg_free_cache_id(int id) ida_simple_remove(&memcg_cache_ida, id); } -struct memcg_kmem_cache_create_work { - struct kmem_cache *cachep; - struct work_struct work; -}; - -static void memcg_kmem_cache_create_func(struct work_struct *w) -{ - struct memcg_kmem_cache_create_work *cw = - container_of(w, struct memcg_kmem_cache_create_work, work); - struct kmem_cache *cachep = cw->cachep; - - memcg_create_kmem_cache(cachep); - - kfree(cw); -} - -/* - * Enqueue the creation of a per-memcg kmem_cache. - */ -static void memcg_schedule_kmem_cache_create(struct kmem_cache *cachep) -{ - struct memcg_kmem_cache_create_work *cw; - - cw = kmalloc(sizeof(*cw), GFP_NOWAIT | __GFP_NOWARN); - if (!cw) - return; - - cw->cachep = cachep; - INIT_WORK(&cw->work, memcg_kmem_cache_create_func); - - queue_work(memcg_kmem_cache_wq, &cw->work); -} - /** * memcg_kmem_get_cache: select memcg or root cache for allocation * @cachep: the original global kmem cache @@ -2951,7 +2916,7 @@ struct kmem_cache *memcg_kmem_get_cache(struct kmem_cache *cachep) memcg_cachep = READ_ONCE(cachep->memcg_params.memcg_cache); if (unlikely(!memcg_cachep)) { - memcg_schedule_kmem_cache_create(cachep); + queue_work(system_wq, &cachep->memcg_params.work); return cachep; } @@ -7022,17 +6987,6 @@ static int __init mem_cgroup_init(void) { int cpu, node; -#ifdef CONFIG_MEMCG_KMEM - /* - * Kmem cache creation is mostly done with the slab_mutex held, - * so use a workqueue with limited concurrency to avoid stalling - * all worker threads in case lots of cgroups are created and - * destroyed simultaneously. - */ - memcg_kmem_cache_wq = alloc_workqueue("memcg_kmem_cache", 0, 1); - BUG_ON(!memcg_kmem_cache_wq); -#endif - cpuhp_setup_state_nocalls(CPUHP_MM_MEMCQ_DEAD, "mm/memctrl:dead", NULL, memcg_hotplug_cpu_dead); diff --git a/mm/slab.h b/mm/slab.h index e716b80befc2f..fd9fcdfb37899 100644 --- a/mm/slab.h +++ b/mm/slab.h @@ -45,12 +45,14 @@ struct kmem_cache { * @memcg_cache: pointer to memcg kmem cache, used by all non-root memory * cgroups. * @root_caches_node: list node for slab_root_caches list. + * @work: work struct used to create the non-root cache. */ struct memcg_cache_params { struct kmem_cache *root_cache; struct kmem_cache *memcg_cache; struct list_head __root_caches_node; + struct work_struct work; }; #endif /* CONFIG_SLOB */ diff --git a/mm/slab_common.c b/mm/slab_common.c index e752132eb64dc..b898698f6c8ac 100644 --- a/mm/slab_common.c +++ b/mm/slab_common.c @@ -134,10 +134,18 @@ int __kmem_cache_alloc_bulk(struct kmem_cache *s, gfp_t flags, size_t nr, LIST_HEAD(slab_root_caches); +static void memcg_kmem_cache_create_func(struct work_struct *work) +{ + struct kmem_cache *cachep = container_of(work, struct kmem_cache, + memcg_params.work); + memcg_create_kmem_cache(cachep); +} + void slab_init_memcg_params(struct kmem_cache *s) { s->memcg_params.root_cache = NULL; s->memcg_params.memcg_cache = NULL; + INIT_WORK(&s->memcg_params.work, memcg_kmem_cache_create_func); } static void init_memcg_params(struct kmem_cache *s, @@ -586,15 +594,9 @@ static int shutdown_memcg_caches(struct kmem_cache *s) return 0; } -static void flush_memcg_workqueue(struct kmem_cache *s) +static void cancel_memcg_cache_creation(struct kmem_cache *s) { - /* - * SLAB and SLUB create memcg kmem_caches through workqueue and SLUB - * deactivates the memcg kmem_caches through workqueue. Make sure all - * previous workitems on workqueue are processed. - */ - if (likely(memcg_kmem_cache_wq)) - flush_workqueue(memcg_kmem_cache_wq); + cancel_work_sync(&s->memcg_params.work); } #else static inline int shutdown_memcg_caches(struct kmem_cache *s) @@ -602,7 +604,7 @@ static inline int shutdown_memcg_caches(struct kmem_cache *s) return 0; } -static inline void flush_memcg_workqueue(struct kmem_cache *s) +static inline void cancel_memcg_cache_creation(struct kmem_cache *s) { } #endif /* CONFIG_MEMCG_KMEM */ @@ -621,7 +623,7 @@ void kmem_cache_destroy(struct kmem_cache *s) if (unlikely(!s)) return; - flush_memcg_workqueue(s); + cancel_memcg_cache_creation(s); get_online_cpus(); get_online_mems(); -- GitLab From 272911a4ad18c48f8bc449a5db945a54987dd687 Mon Sep 17 00:00:00 2001 From: Roman Gushchin Date: Thu, 6 Aug 2020 23:21:17 -0700 Subject: [PATCH 1384/1476] mm: memcg/slab: remove memcg_kmem_get_cache() The memcg_kmem_get_cache() function became really trivial, so let's just inline it into the single call point: memcg_slab_pre_alloc_hook(). It will make the code less bulky and can also help the compiler to generate a better code. Signed-off-by: Roman Gushchin Signed-off-by: Andrew Morton Reviewed-by: Vlastimil Babka Reviewed-by: Shakeel Butt Cc: Christoph Lameter Cc: Johannes Weiner Cc: Michal Hocko Cc: Tejun Heo Link: http://lkml.kernel.org/r/20200623174037.3951353-15-guro@fb.com Signed-off-by: Linus Torvalds --- include/linux/memcontrol.h | 2 -- mm/memcontrol.c | 25 +------------------------ mm/slab.h | 11 +++++++++-- mm/slab_common.c | 2 +- 4 files changed, 11 insertions(+), 29 deletions(-) diff --git a/include/linux/memcontrol.h b/include/linux/memcontrol.h index 2ac84dcfc9e5a..5a8b62d075e60 100644 --- a/include/linux/memcontrol.h +++ b/include/linux/memcontrol.h @@ -1403,8 +1403,6 @@ static inline void memcg_set_shrinker_bit(struct mem_cgroup *memcg, } #endif -struct kmem_cache *memcg_kmem_get_cache(struct kmem_cache *cachep); - #ifdef CONFIG_MEMCG_KMEM int __memcg_kmem_charge(struct mem_cgroup *memcg, gfp_t gfp, unsigned int nr_pages); diff --git a/mm/memcontrol.c b/mm/memcontrol.c index c713867e496d3..a8113b77b23a1 100644 --- a/mm/memcontrol.c +++ b/mm/memcontrol.c @@ -393,7 +393,7 @@ void memcg_put_cache_ids(void) /* * A lot of the calls to the cache allocation functions are expected to be - * inlined by the compiler. Since the calls to memcg_kmem_get_cache are + * inlined by the compiler. Since the calls to memcg_slab_pre_alloc_hook() are * conditional to this static branch, we'll have to allow modules that does * kmem_cache_alloc and the such to see this symbol as well */ @@ -2900,29 +2900,6 @@ static void memcg_free_cache_id(int id) ida_simple_remove(&memcg_cache_ida, id); } -/** - * memcg_kmem_get_cache: select memcg or root cache for allocation - * @cachep: the original global kmem cache - * - * Return the kmem_cache we're supposed to use for a slab allocation. - * - * If the cache does not exist yet, if we are the first user of it, we - * create it asynchronously in a workqueue and let the current allocation - * go through with the original cache. - */ -struct kmem_cache *memcg_kmem_get_cache(struct kmem_cache *cachep) -{ - struct kmem_cache *memcg_cachep; - - memcg_cachep = READ_ONCE(cachep->memcg_params.memcg_cache); - if (unlikely(!memcg_cachep)) { - queue_work(system_wq, &cachep->memcg_params.work); - return cachep; - } - - return memcg_cachep; -} - /** * __memcg_kmem_charge: charge a number of kernel pages to a memcg * @memcg: memory cgroup to charge diff --git a/mm/slab.h b/mm/slab.h index fd9fcdfb37899..342eac8529677 100644 --- a/mm/slab.h +++ b/mm/slab.h @@ -365,9 +365,16 @@ static inline struct kmem_cache *memcg_slab_pre_alloc_hook(struct kmem_cache *s, if (memcg_kmem_bypass()) return s; - cachep = memcg_kmem_get_cache(s); - if (is_root_cache(cachep)) + cachep = READ_ONCE(s->memcg_params.memcg_cache); + if (unlikely(!cachep)) { + /* + * If memcg cache does not exist yet, we schedule it's + * asynchronous creation and let the current allocation + * go through with the root cache. + */ + queue_work(system_wq, &s->memcg_params.work); return s; + } objcg = get_obj_cgroup_from_current(); if (!objcg) diff --git a/mm/slab_common.c b/mm/slab_common.c index b898698f6c8ac..de0a46cf974a7 100644 --- a/mm/slab_common.c +++ b/mm/slab_common.c @@ -570,7 +570,7 @@ void memcg_create_kmem_cache(struct kmem_cache *root_cache) } /* - * Since readers won't lock (see memcg_kmem_get_cache()), we need a + * Since readers won't lock (see memcg_slab_pre_alloc_hook()), we need a * barrier here to ensure nobody will see the kmem_cache partially * initialized. */ -- GitLab From c7094406fcb7cdf4fe1de8893f0613b75349773d Mon Sep 17 00:00:00 2001 From: Roman Gushchin Date: Thu, 6 Aug 2020 23:21:20 -0700 Subject: [PATCH 1385/1476] mm: memcg/slab: deprecate slab_root_caches Currently there are two lists of kmem_caches: 1) slab_caches, which contains all kmem_caches, 2) slab_root_caches, which contains only root kmem_caches. And there is some preprocessor magic to have a single list if CONFIG_MEMCG_KMEM isn't enabled. It was required earlier because the number of non-root kmem_caches was proportional to the number of memory cgroups and could reach really big values. Now, when it cannot exceed the number of root kmem_caches, there is really no reason to maintain two lists. We never iterate over the slab_root_caches list on any hot paths, so it's perfectly fine to iterate over slab_caches and filter out non-root kmem_caches. It allows to remove a lot of config-dependent code and two pointers from the kmem_cache structure. Signed-off-by: Roman Gushchin Signed-off-by: Andrew Morton Reviewed-by: Vlastimil Babka Reviewed-by: Shakeel Butt Cc: Christoph Lameter Cc: Johannes Weiner Cc: Michal Hocko Cc: Tejun Heo Link: http://lkml.kernel.org/r/20200623174037.3951353-16-guro@fb.com Signed-off-by: Linus Torvalds --- mm/slab.c | 1 - mm/slab.h | 17 ----------------- mm/slab_common.c | 37 ++++++++----------------------------- mm/slub.c | 1 - 4 files changed, 8 insertions(+), 48 deletions(-) diff --git a/mm/slab.c b/mm/slab.c index 0dd6956585dca..f40e5c95e11a2 100644 --- a/mm/slab.c +++ b/mm/slab.c @@ -1249,7 +1249,6 @@ void __init kmem_cache_init(void) nr_node_ids * sizeof(struct kmem_cache_node *), SLAB_HWCACHE_ALIGN, 0, 0); list_add(&kmem_cache->list, &slab_caches); - memcg_link_cache(kmem_cache); slab_state = PARTIAL; /* diff --git a/mm/slab.h b/mm/slab.h index 342eac8529677..7500a707121bb 100644 --- a/mm/slab.h +++ b/mm/slab.h @@ -44,14 +44,12 @@ struct kmem_cache { * * @memcg_cache: pointer to memcg kmem cache, used by all non-root memory * cgroups. - * @root_caches_node: list node for slab_root_caches list. * @work: work struct used to create the non-root cache. */ struct memcg_cache_params { struct kmem_cache *root_cache; struct kmem_cache *memcg_cache; - struct list_head __root_caches_node; struct work_struct work; }; #endif /* CONFIG_SLOB */ @@ -265,11 +263,6 @@ static inline bool kmem_cache_debug_flags(struct kmem_cache *s, slab_flags_t fla } #ifdef CONFIG_MEMCG_KMEM - -/* List of all root caches. */ -extern struct list_head slab_root_caches; -#define root_caches_node memcg_params.__root_caches_node - static inline bool is_root_cache(struct kmem_cache *s) { return !s->memcg_params.root_cache; @@ -447,14 +440,8 @@ static inline void memcg_slab_free_hook(struct kmem_cache *s, struct page *page, } extern void slab_init_memcg_params(struct kmem_cache *); -extern void memcg_link_cache(struct kmem_cache *s); #else /* CONFIG_MEMCG_KMEM */ - -/* If !memcg, all caches are root. */ -#define slab_root_caches slab_caches -#define root_caches_node list - static inline bool is_root_cache(struct kmem_cache *s) { return true; @@ -523,10 +510,6 @@ static inline void slab_init_memcg_params(struct kmem_cache *s) { } -static inline void memcg_link_cache(struct kmem_cache *s) -{ -} - #endif /* CONFIG_MEMCG_KMEM */ static inline struct kmem_cache *virt_to_cache(const void *obj) diff --git a/mm/slab_common.c b/mm/slab_common.c index de0a46cf974a7..a9f6ab452ce53 100644 --- a/mm/slab_common.c +++ b/mm/slab_common.c @@ -131,9 +131,6 @@ int __kmem_cache_alloc_bulk(struct kmem_cache *s, gfp_t flags, size_t nr, } #ifdef CONFIG_MEMCG_KMEM - -LIST_HEAD(slab_root_caches); - static void memcg_kmem_cache_create_func(struct work_struct *work) { struct kmem_cache *cachep = container_of(work, struct kmem_cache, @@ -156,27 +153,11 @@ static void init_memcg_params(struct kmem_cache *s, else slab_init_memcg_params(s); } - -void memcg_link_cache(struct kmem_cache *s) -{ - if (is_root_cache(s)) - list_add(&s->root_caches_node, &slab_root_caches); -} - -static void memcg_unlink_cache(struct kmem_cache *s) -{ - if (is_root_cache(s)) - list_del(&s->root_caches_node); -} #else static inline void init_memcg_params(struct kmem_cache *s, struct kmem_cache *root_cache) { } - -static inline void memcg_unlink_cache(struct kmem_cache *s) -{ -} #endif /* CONFIG_MEMCG_KMEM */ /* @@ -253,7 +234,7 @@ struct kmem_cache *find_mergeable(unsigned int size, unsigned int align, if (flags & SLAB_NEVER_MERGE) return NULL; - list_for_each_entry_reverse(s, &slab_root_caches, root_caches_node) { + list_for_each_entry_reverse(s, &slab_caches, list) { if (slab_unmergeable(s)) continue; @@ -312,7 +293,6 @@ static struct kmem_cache *create_cache(const char *name, s->refcount = 1; list_add(&s->list, &slab_caches); - memcg_link_cache(s); out: if (err) return ERR_PTR(err); @@ -507,7 +487,6 @@ static int shutdown_cache(struct kmem_cache *s) if (__kmem_cache_shutdown(s) != 0) return -EBUSY; - memcg_unlink_cache(s); list_del(&s->list); if (s->flags & SLAB_TYPESAFE_BY_RCU) { @@ -751,7 +730,6 @@ struct kmem_cache *__init create_kmalloc_cache(const char *name, create_boot_cache(s, name, size, flags, useroffset, usersize); list_add(&s->list, &slab_caches); - memcg_link_cache(s); s->refcount = 1; return s; } @@ -1107,12 +1085,12 @@ static void print_slabinfo_header(struct seq_file *m) void *slab_start(struct seq_file *m, loff_t *pos) { mutex_lock(&slab_mutex); - return seq_list_start(&slab_root_caches, *pos); + return seq_list_start(&slab_caches, *pos); } void *slab_next(struct seq_file *m, void *p, loff_t *pos) { - return seq_list_next(p, &slab_root_caches, pos); + return seq_list_next(p, &slab_caches, pos); } void slab_stop(struct seq_file *m, void *p) @@ -1165,11 +1143,12 @@ static void cache_show(struct kmem_cache *s, struct seq_file *m) static int slab_show(struct seq_file *m, void *p) { - struct kmem_cache *s = list_entry(p, struct kmem_cache, root_caches_node); + struct kmem_cache *s = list_entry(p, struct kmem_cache, list); - if (p == slab_root_caches.next) + if (p == slab_caches.next) print_slabinfo_header(m); - cache_show(s, m); + if (is_root_cache(s)) + cache_show(s, m); return 0; } @@ -1271,7 +1250,7 @@ static int memcg_slabinfo_show(struct seq_file *m, void *unused) mutex_lock(&slab_mutex); seq_puts(m, "# "); seq_puts(m, " \n"); - list_for_each_entry(s, &slab_root_caches, root_caches_node) { + list_for_each_entry(s, &slab_caches, list) { /* * Skip kmem caches that don't have the memcg cache. */ diff --git a/mm/slub.c b/mm/slub.c index 44a48a08a6919..9cd724fe37d89 100644 --- a/mm/slub.c +++ b/mm/slub.c @@ -4360,7 +4360,6 @@ static struct kmem_cache * __init bootstrap(struct kmem_cache *static_cache) } slab_init_memcg_params(s); list_add(&s->list, &slab_caches); - memcg_link_cache(s); return s; } -- GitLab From 15999eef7f25e2ea6a1c33f026166f472c5714e9 Mon Sep 17 00:00:00 2001 From: Roman Gushchin Date: Thu, 6 Aug 2020 23:21:24 -0700 Subject: [PATCH 1386/1476] mm: memcg/slab: remove redundant check in memcg_accumulate_slabinfo() memcg_accumulate_slabinfo() is never called with a non-root kmem_cache as a first argument, so the is_root_cache(s) check is redundant and can be removed without any functional change. Signed-off-by: Roman Gushchin Signed-off-by: Andrew Morton Reviewed-by: Vlastimil Babka Reviewed-by: Shakeel Butt Cc: Christoph Lameter Cc: Johannes Weiner Cc: Michal Hocko Cc: Tejun Heo Link: http://lkml.kernel.org/r/20200623174037.3951353-17-guro@fb.com Signed-off-by: Linus Torvalds --- mm/slab_common.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/mm/slab_common.c b/mm/slab_common.c index a9f6ab452ce53..ad67a03c592ab 100644 --- a/mm/slab_common.c +++ b/mm/slab_common.c @@ -1104,9 +1104,6 @@ memcg_accumulate_slabinfo(struct kmem_cache *s, struct slabinfo *info) struct kmem_cache *c; struct slabinfo sinfo; - if (!is_root_cache(s)) - return; - c = memcg_cache(s); if (c) { memset(&sinfo, 0, sizeof(sinfo)); -- GitLab From 10befea91b61c4e2c2d1df06a2e978d182fcf792 Mon Sep 17 00:00:00 2001 From: Roman Gushchin Date: Thu, 6 Aug 2020 23:21:27 -0700 Subject: [PATCH 1387/1476] mm: memcg/slab: use a single set of kmem_caches for all allocations Instead of having two sets of kmem_caches: one for system-wide and non-accounted allocations and the second one shared by all accounted allocations, we can use just one. The idea is simple: space for obj_cgroup metadata can be allocated on demand and filled only for accounted allocations. It allows to remove a bunch of code which is required to handle kmem_cache clones for accounted allocations. There is no more need to create them, accumulate statistics, propagate attributes, etc. It's a quite significant simplification. Also, because the total number of slab_caches is reduced almost twice (not all kmem_caches have a memcg clone), some additional memory savings are expected. On my devvm it additionally saves about 3.5% of slab memory. [guro@fb.com: fix build on MIPS] Link: http://lkml.kernel.org/r/20200717214810.3733082-1-guro@fb.com Suggested-by: Johannes Weiner Signed-off-by: Roman Gushchin Signed-off-by: Andrew Morton Reviewed-by: Vlastimil Babka Reviewed-by: Shakeel Butt Cc: Christoph Lameter Cc: Michal Hocko Cc: Tejun Heo Cc: Naresh Kamboju Link: http://lkml.kernel.org/r/20200623174037.3951353-18-guro@fb.com Signed-off-by: Linus Torvalds --- include/linux/slab.h | 2 - include/linux/slab_def.h | 3 - include/linux/slub_def.h | 10 -- mm/memcontrol.c | 25 ++++- mm/slab.c | 41 +------ mm/slab.h | 194 +++++++-------------------------- mm/slab_common.c | 230 +-------------------------------------- mm/slub.c | 163 +-------------------------- 8 files changed, 78 insertions(+), 590 deletions(-) diff --git a/include/linux/slab.h b/include/linux/slab.h index 8b1f91e320f9c..24df2393ec032 100644 --- a/include/linux/slab.h +++ b/include/linux/slab.h @@ -155,8 +155,6 @@ struct kmem_cache *kmem_cache_create_usercopy(const char *name, void kmem_cache_destroy(struct kmem_cache *); int kmem_cache_shrink(struct kmem_cache *); -void memcg_create_kmem_cache(struct kmem_cache *cachep); - /* * Please use this macro to create slab caches. Simply specify the * name of the structure and maybe some flags that are listed above. diff --git a/include/linux/slab_def.h b/include/linux/slab_def.h index ccda7b9669a51..9eb430c163c2b 100644 --- a/include/linux/slab_def.h +++ b/include/linux/slab_def.h @@ -72,9 +72,6 @@ struct kmem_cache { int obj_offset; #endif /* CONFIG_DEBUG_SLAB */ -#ifdef CONFIG_MEMCG - struct memcg_cache_params memcg_params; -#endif #ifdef CONFIG_KASAN struct kasan_cache kasan_info; #endif diff --git a/include/linux/slub_def.h b/include/linux/slub_def.h index f87302dcfe8c4..1be0ed5befa1c 100644 --- a/include/linux/slub_def.h +++ b/include/linux/slub_def.h @@ -108,17 +108,7 @@ struct kmem_cache { struct list_head list; /* List of slab caches */ #ifdef CONFIG_SYSFS struct kobject kobj; /* For sysfs */ - struct work_struct kobj_remove_work; #endif -#ifdef CONFIG_MEMCG - struct memcg_cache_params memcg_params; - /* For propagation, maximum size of a stored attr */ - unsigned int max_attr_size; -#ifdef CONFIG_SYSFS - struct kset *memcg_kset; -#endif -#endif - #ifdef CONFIG_SLAB_FREELIST_HARDENED unsigned long random; #endif diff --git a/mm/memcontrol.c b/mm/memcontrol.c index a8113b77b23a1..473f9b91d51f8 100644 --- a/mm/memcontrol.c +++ b/mm/memcontrol.c @@ -2800,6 +2800,26 @@ static void commit_charge(struct page *page, struct mem_cgroup *memcg) } #ifdef CONFIG_MEMCG_KMEM +int memcg_alloc_page_obj_cgroups(struct page *page, struct kmem_cache *s, + gfp_t gfp) +{ + unsigned int objects = objs_per_slab_page(s, page); + void *vec; + + vec = kcalloc_node(objects, sizeof(struct obj_cgroup *), gfp, + page_to_nid(page)); + if (!vec) + return -ENOMEM; + + if (cmpxchg(&page->obj_cgroups, NULL, + (struct obj_cgroup **) ((unsigned long)vec | 0x1UL))) + kfree(vec); + else + kmemleak_not_leak(vec); + + return 0; +} + /* * Returns a pointer to the memory cgroup to which the kernel object is charged. * @@ -2826,7 +2846,10 @@ struct mem_cgroup *mem_cgroup_from_obj(void *p) off = obj_to_index(page->slab_cache, page, p); objcg = page_obj_cgroups(page)[off]; - return obj_cgroup_memcg(objcg); + if (objcg) + return obj_cgroup_memcg(objcg); + + return NULL; } /* All other pages use page->mem_cgroup */ diff --git a/mm/slab.c b/mm/slab.c index f40e5c95e11a2..684ebe5b0c7a3 100644 --- a/mm/slab.c +++ b/mm/slab.c @@ -1379,11 +1379,7 @@ static struct page *kmem_getpages(struct kmem_cache *cachep, gfp_t flags, return NULL; } - if (charge_slab_page(page, flags, cachep->gfporder, cachep)) { - __free_pages(page, cachep->gfporder); - return NULL; - } - + charge_slab_page(page, flags, cachep->gfporder, cachep); __SetPageSlab(page); /* Record if ALLOC_NO_WATERMARKS was set when allocating the slab */ if (sk_memalloc_socks() && page_is_pfmemalloc(page)) @@ -3799,8 +3795,8 @@ fail: } /* Always called with the slab_mutex held */ -static int __do_tune_cpucache(struct kmem_cache *cachep, int limit, - int batchcount, int shared, gfp_t gfp) +static int do_tune_cpucache(struct kmem_cache *cachep, int limit, + int batchcount, int shared, gfp_t gfp) { struct array_cache __percpu *cpu_cache, *prev; int cpu; @@ -3845,30 +3841,6 @@ setup_node: return setup_kmem_cache_nodes(cachep, gfp); } -static int do_tune_cpucache(struct kmem_cache *cachep, int limit, - int batchcount, int shared, gfp_t gfp) -{ - int ret; - struct kmem_cache *c; - - ret = __do_tune_cpucache(cachep, limit, batchcount, shared, gfp); - - if (slab_state < FULL) - return ret; - - if ((ret < 0) || !is_root_cache(cachep)) - return ret; - - lockdep_assert_held(&slab_mutex); - c = memcg_cache(cachep); - if (c) { - /* return value determined by the root cache only */ - __do_tune_cpucache(c, limit, batchcount, shared, gfp); - } - - return ret; -} - /* Called with slab_mutex held always */ static int enable_cpucache(struct kmem_cache *cachep, gfp_t gfp) { @@ -3881,13 +3853,6 @@ static int enable_cpucache(struct kmem_cache *cachep, gfp_t gfp) if (err) goto end; - if (!is_root_cache(cachep)) { - struct kmem_cache *root = memcg_root_cache(cachep); - limit = root->limit; - shared = root->shared; - batchcount = root->batchcount; - } - if (limit && shared && batchcount) goto skip_setup; /* diff --git a/mm/slab.h b/mm/slab.h index 7500a707121bb..ec8e22ee65440 100644 --- a/mm/slab.h +++ b/mm/slab.h @@ -30,28 +30,6 @@ struct kmem_cache { struct list_head list; /* List of all slab caches on the system */ }; -#else /* !CONFIG_SLOB */ - -/* - * This is the main placeholder for memcg-related information in kmem caches. - * Both the root cache and the child cache will have it. Some fields are used - * in both cases, other are specific to root caches. - * - * @root_cache: Common to root and child caches. NULL for root, pointer to - * the root cache for children. - * - * The following fields are specific to root caches. - * - * @memcg_cache: pointer to memcg kmem cache, used by all non-root memory - * cgroups. - * @work: work struct used to create the non-root cache. - */ -struct memcg_cache_params { - struct kmem_cache *root_cache; - - struct kmem_cache *memcg_cache; - struct work_struct work; -}; #endif /* CONFIG_SLOB */ #ifdef CONFIG_SLAB @@ -196,7 +174,6 @@ int __kmem_cache_shutdown(struct kmem_cache *); void __kmem_cache_release(struct kmem_cache *); int __kmem_cache_shrink(struct kmem_cache *); void slab_kmem_cache_release(struct kmem_cache *); -void kmem_cache_shrink_all(struct kmem_cache *s); struct seq_file; struct file; @@ -263,43 +240,6 @@ static inline bool kmem_cache_debug_flags(struct kmem_cache *s, slab_flags_t fla } #ifdef CONFIG_MEMCG_KMEM -static inline bool is_root_cache(struct kmem_cache *s) -{ - return !s->memcg_params.root_cache; -} - -static inline bool slab_equal_or_root(struct kmem_cache *s, - struct kmem_cache *p) -{ - return p == s || p == s->memcg_params.root_cache; -} - -/* - * We use suffixes to the name in memcg because we can't have caches - * created in the system with the same name. But when we print them - * locally, better refer to them with the base name - */ -static inline const char *cache_name(struct kmem_cache *s) -{ - if (!is_root_cache(s)) - s = s->memcg_params.root_cache; - return s->name; -} - -static inline struct kmem_cache *memcg_root_cache(struct kmem_cache *s) -{ - if (is_root_cache(s)) - return s; - return s->memcg_params.root_cache; -} - -static inline struct kmem_cache *memcg_cache(struct kmem_cache *s) -{ - if (is_root_cache(s)) - return s->memcg_params.memcg_cache; - return NULL; -} - static inline struct obj_cgroup **page_obj_cgroups(struct page *page) { /* @@ -317,21 +257,8 @@ static inline bool page_has_obj_cgroups(struct page *page) return ((unsigned long)page->obj_cgroups & 0x1UL); } -static inline int memcg_alloc_page_obj_cgroups(struct page *page, - struct kmem_cache *s, gfp_t gfp) -{ - unsigned int objects = objs_per_slab_page(s, page); - void *vec; - - vec = kcalloc_node(objects, sizeof(struct obj_cgroup *), gfp, - page_to_nid(page)); - if (!vec) - return -ENOMEM; - - kmemleak_not_leak(vec); - page->obj_cgroups = (struct obj_cgroup **) ((unsigned long)vec | 0x1UL); - return 0; -} +int memcg_alloc_page_obj_cgroups(struct page *page, struct kmem_cache *s, + gfp_t gfp); static inline void memcg_free_page_obj_cgroups(struct page *page) { @@ -348,38 +275,25 @@ static inline size_t obj_full_size(struct kmem_cache *s) return s->size + sizeof(struct obj_cgroup *); } -static inline struct kmem_cache *memcg_slab_pre_alloc_hook(struct kmem_cache *s, - struct obj_cgroup **objcgp, - size_t objects, gfp_t flags) +static inline struct obj_cgroup *memcg_slab_pre_alloc_hook(struct kmem_cache *s, + size_t objects, + gfp_t flags) { - struct kmem_cache *cachep; struct obj_cgroup *objcg; if (memcg_kmem_bypass()) - return s; - - cachep = READ_ONCE(s->memcg_params.memcg_cache); - if (unlikely(!cachep)) { - /* - * If memcg cache does not exist yet, we schedule it's - * asynchronous creation and let the current allocation - * go through with the root cache. - */ - queue_work(system_wq, &s->memcg_params.work); - return s; - } + return NULL; objcg = get_obj_cgroup_from_current(); if (!objcg) - return s; + return NULL; if (obj_cgroup_charge(objcg, flags, objects * obj_full_size(s))) { obj_cgroup_put(objcg); - cachep = NULL; + return NULL; } - *objcgp = objcg; - return cachep; + return objcg; } static inline void mod_objcg_state(struct obj_cgroup *objcg, @@ -398,15 +312,27 @@ static inline void mod_objcg_state(struct obj_cgroup *objcg, static inline void memcg_slab_post_alloc_hook(struct kmem_cache *s, struct obj_cgroup *objcg, - size_t size, void **p) + gfp_t flags, size_t size, + void **p) { struct page *page; unsigned long off; size_t i; + if (!objcg) + return; + + flags &= ~__GFP_ACCOUNT; for (i = 0; i < size; i++) { if (likely(p[i])) { page = virt_to_head_page(p[i]); + + if (!page_has_obj_cgroups(page) && + memcg_alloc_page_obj_cgroups(page, s, flags)) { + obj_cgroup_uncharge(objcg, obj_full_size(s)); + continue; + } + off = obj_to_index(s, page, p[i]); obj_cgroup_get(objcg); page_obj_cgroups(page)[off] = objcg; @@ -425,13 +351,19 @@ static inline void memcg_slab_free_hook(struct kmem_cache *s, struct page *page, struct obj_cgroup *objcg; unsigned int off; - if (!memcg_kmem_enabled() || is_root_cache(s)) + if (!memcg_kmem_enabled()) + return; + + if (!page_has_obj_cgroups(page)) return; off = obj_to_index(s, page, p); objcg = page_obj_cgroups(page)[off]; page_obj_cgroups(page)[off] = NULL; + if (!objcg) + return; + obj_cgroup_uncharge(objcg, obj_full_size(s)); mod_objcg_state(objcg, page_pgdat(page), cache_vmstat_idx(s), -obj_full_size(s)); @@ -439,35 +371,7 @@ static inline void memcg_slab_free_hook(struct kmem_cache *s, struct page *page, obj_cgroup_put(objcg); } -extern void slab_init_memcg_params(struct kmem_cache *); - #else /* CONFIG_MEMCG_KMEM */ -static inline bool is_root_cache(struct kmem_cache *s) -{ - return true; -} - -static inline bool slab_equal_or_root(struct kmem_cache *s, - struct kmem_cache *p) -{ - return s == p; -} - -static inline const char *cache_name(struct kmem_cache *s) -{ - return s->name; -} - -static inline struct kmem_cache *memcg_root_cache(struct kmem_cache *s) -{ - return s; -} - -static inline struct kmem_cache *memcg_cache(struct kmem_cache *s) -{ - return NULL; -} - static inline bool page_has_obj_cgroups(struct page *page) { return false; @@ -488,16 +392,17 @@ static inline void memcg_free_page_obj_cgroups(struct page *page) { } -static inline struct kmem_cache *memcg_slab_pre_alloc_hook(struct kmem_cache *s, - struct obj_cgroup **objcgp, - size_t objects, gfp_t flags) +static inline struct obj_cgroup *memcg_slab_pre_alloc_hook(struct kmem_cache *s, + size_t objects, + gfp_t flags) { return NULL; } static inline void memcg_slab_post_alloc_hook(struct kmem_cache *s, struct obj_cgroup *objcg, - size_t size, void **p) + gfp_t flags, size_t size, + void **p) { } @@ -505,11 +410,6 @@ static inline void memcg_slab_free_hook(struct kmem_cache *s, struct page *page, void *p) { } - -static inline void slab_init_memcg_params(struct kmem_cache *s) -{ -} - #endif /* CONFIG_MEMCG_KMEM */ static inline struct kmem_cache *virt_to_cache(const void *obj) @@ -523,27 +423,18 @@ static inline struct kmem_cache *virt_to_cache(const void *obj) return page->slab_cache; } -static __always_inline int charge_slab_page(struct page *page, - gfp_t gfp, int order, - struct kmem_cache *s) +static __always_inline void charge_slab_page(struct page *page, + gfp_t gfp, int order, + struct kmem_cache *s) { - if (memcg_kmem_enabled() && !is_root_cache(s)) { - int ret; - - ret = memcg_alloc_page_obj_cgroups(page, s, gfp); - if (ret) - return ret; - } - mod_node_page_state(page_pgdat(page), cache_vmstat_idx(s), PAGE_SIZE << order); - return 0; } static __always_inline void uncharge_slab_page(struct page *page, int order, struct kmem_cache *s) { - if (memcg_kmem_enabled() && !is_root_cache(s)) + if (memcg_kmem_enabled()) memcg_free_page_obj_cgroups(page); mod_node_page_state(page_pgdat(page), cache_vmstat_idx(s), @@ -555,12 +446,11 @@ static inline struct kmem_cache *cache_from_obj(struct kmem_cache *s, void *x) struct kmem_cache *cachep; if (!IS_ENABLED(CONFIG_SLAB_FREELIST_HARDENED) && - !memcg_kmem_enabled() && !kmem_cache_debug_flags(s, SLAB_CONSISTENCY_CHECKS)) return s; cachep = virt_to_cache(x); - if (WARN(cachep && !slab_equal_or_root(cachep, s), + if (WARN(cachep && cachep != s, "%s: Wrong slab cache. %s but object is from %s\n", __func__, s->name, cachep->name)) print_tracking(cachep, x); @@ -613,7 +503,7 @@ static inline struct kmem_cache *slab_pre_alloc_hook(struct kmem_cache *s, if (memcg_kmem_enabled() && ((flags & __GFP_ACCOUNT) || (s->flags & SLAB_ACCOUNT))) - return memcg_slab_pre_alloc_hook(s, objcgp, size, flags); + *objcgp = memcg_slab_pre_alloc_hook(s, size, flags); return s; } @@ -632,8 +522,8 @@ static inline void slab_post_alloc_hook(struct kmem_cache *s, s->flags, flags); } - if (memcg_kmem_enabled() && !is_root_cache(s)) - memcg_slab_post_alloc_hook(s, objcg, size, p); + if (memcg_kmem_enabled()) + memcg_slab_post_alloc_hook(s, objcg, flags, size, p); } #ifndef CONFIG_SLOB diff --git a/mm/slab_common.c b/mm/slab_common.c index ad67a03c592ab..a513f32371556 100644 --- a/mm/slab_common.c +++ b/mm/slab_common.c @@ -130,36 +130,6 @@ int __kmem_cache_alloc_bulk(struct kmem_cache *s, gfp_t flags, size_t nr, return i; } -#ifdef CONFIG_MEMCG_KMEM -static void memcg_kmem_cache_create_func(struct work_struct *work) -{ - struct kmem_cache *cachep = container_of(work, struct kmem_cache, - memcg_params.work); - memcg_create_kmem_cache(cachep); -} - -void slab_init_memcg_params(struct kmem_cache *s) -{ - s->memcg_params.root_cache = NULL; - s->memcg_params.memcg_cache = NULL; - INIT_WORK(&s->memcg_params.work, memcg_kmem_cache_create_func); -} - -static void init_memcg_params(struct kmem_cache *s, - struct kmem_cache *root_cache) -{ - if (root_cache) - s->memcg_params.root_cache = root_cache; - else - slab_init_memcg_params(s); -} -#else -static inline void init_memcg_params(struct kmem_cache *s, - struct kmem_cache *root_cache) -{ -} -#endif /* CONFIG_MEMCG_KMEM */ - /* * Figure out what the alignment of the objects will be given a set of * flags, a user specified alignment and the size of the objects. @@ -197,9 +167,6 @@ int slab_unmergeable(struct kmem_cache *s) if (slab_nomerge || (s->flags & SLAB_NEVER_MERGE)) return 1; - if (!is_root_cache(s)) - return 1; - if (s->ctor) return 1; @@ -286,7 +253,6 @@ static struct kmem_cache *create_cache(const char *name, s->useroffset = useroffset; s->usersize = usersize; - init_memcg_params(s, root_cache); err = __kmem_cache_create(s, flags); if (err) goto out_free_cache; @@ -344,7 +310,6 @@ kmem_cache_create_usercopy(const char *name, get_online_cpus(); get_online_mems(); - memcg_get_cache_ids(); mutex_lock(&slab_mutex); @@ -394,7 +359,6 @@ kmem_cache_create_usercopy(const char *name, out_unlock: mutex_unlock(&slab_mutex); - memcg_put_cache_ids(); put_online_mems(); put_online_cpus(); @@ -507,87 +471,6 @@ static int shutdown_cache(struct kmem_cache *s) return 0; } -#ifdef CONFIG_MEMCG_KMEM -/* - * memcg_create_kmem_cache - Create a cache for non-root memory cgroups. - * @root_cache: The parent of the new cache. - * - * This function attempts to create a kmem cache that will serve allocation - * requests going all non-root memory cgroups to @root_cache. The new cache - * inherits properties from its parent. - */ -void memcg_create_kmem_cache(struct kmem_cache *root_cache) -{ - struct kmem_cache *s = NULL; - char *cache_name; - - get_online_cpus(); - get_online_mems(); - - mutex_lock(&slab_mutex); - - if (root_cache->memcg_params.memcg_cache) - goto out_unlock; - - cache_name = kasprintf(GFP_KERNEL, "%s-memcg", root_cache->name); - if (!cache_name) - goto out_unlock; - - s = create_cache(cache_name, root_cache->object_size, - root_cache->align, - root_cache->flags & CACHE_CREATE_MASK, - root_cache->useroffset, root_cache->usersize, - root_cache->ctor, root_cache); - /* - * If we could not create a memcg cache, do not complain, because - * that's not critical at all as we can always proceed with the root - * cache. - */ - if (IS_ERR(s)) { - kfree(cache_name); - goto out_unlock; - } - - /* - * Since readers won't lock (see memcg_slab_pre_alloc_hook()), we need a - * barrier here to ensure nobody will see the kmem_cache partially - * initialized. - */ - smp_wmb(); - root_cache->memcg_params.memcg_cache = s; - -out_unlock: - mutex_unlock(&slab_mutex); - - put_online_mems(); - put_online_cpus(); -} - -static int shutdown_memcg_caches(struct kmem_cache *s) -{ - BUG_ON(!is_root_cache(s)); - - if (s->memcg_params.memcg_cache) - WARN_ON(shutdown_cache(s->memcg_params.memcg_cache)); - - return 0; -} - -static void cancel_memcg_cache_creation(struct kmem_cache *s) -{ - cancel_work_sync(&s->memcg_params.work); -} -#else -static inline int shutdown_memcg_caches(struct kmem_cache *s) -{ - return 0; -} - -static inline void cancel_memcg_cache_creation(struct kmem_cache *s) -{ -} -#endif /* CONFIG_MEMCG_KMEM */ - void slab_kmem_cache_release(struct kmem_cache *s) { __kmem_cache_release(s); @@ -602,8 +485,6 @@ void kmem_cache_destroy(struct kmem_cache *s) if (unlikely(!s)) return; - cancel_memcg_cache_creation(s); - get_online_cpus(); get_online_mems(); @@ -613,10 +494,7 @@ void kmem_cache_destroy(struct kmem_cache *s) if (s->refcount) goto out_unlock; - err = shutdown_memcg_caches(s); - if (!err) - err = shutdown_cache(s); - + err = shutdown_cache(s); if (err) { pr_err("kmem_cache_destroy %s: Slab cache still has objects\n", s->name); @@ -653,33 +531,6 @@ int kmem_cache_shrink(struct kmem_cache *cachep) } EXPORT_SYMBOL(kmem_cache_shrink); -/** - * kmem_cache_shrink_all - shrink root and memcg caches - * @s: The cache pointer - */ -void kmem_cache_shrink_all(struct kmem_cache *s) -{ - struct kmem_cache *c; - - if (!IS_ENABLED(CONFIG_MEMCG_KMEM) || !is_root_cache(s)) { - kmem_cache_shrink(s); - return; - } - - get_online_cpus(); - get_online_mems(); - kasan_cache_shrink(s); - __kmem_cache_shrink(s); - - c = memcg_cache(s); - if (c) { - kasan_cache_shrink(c); - __kmem_cache_shrink(c); - } - put_online_mems(); - put_online_cpus(); -} - bool slab_is_available(void) { return slab_state >= UP; @@ -708,8 +559,6 @@ void __init create_boot_cache(struct kmem_cache *s, const char *name, s->useroffset = useroffset; s->usersize = usersize; - slab_init_memcg_params(s); - err = __kmem_cache_create(s, flags); if (err) @@ -1098,25 +947,6 @@ void slab_stop(struct seq_file *m, void *p) mutex_unlock(&slab_mutex); } -static void -memcg_accumulate_slabinfo(struct kmem_cache *s, struct slabinfo *info) -{ - struct kmem_cache *c; - struct slabinfo sinfo; - - c = memcg_cache(s); - if (c) { - memset(&sinfo, 0, sizeof(sinfo)); - get_slabinfo(c, &sinfo); - - info->active_slabs += sinfo.active_slabs; - info->num_slabs += sinfo.num_slabs; - info->shared_avail += sinfo.shared_avail; - info->active_objs += sinfo.active_objs; - info->num_objs += sinfo.num_objs; - } -} - static void cache_show(struct kmem_cache *s, struct seq_file *m) { struct slabinfo sinfo; @@ -1124,10 +954,8 @@ static void cache_show(struct kmem_cache *s, struct seq_file *m) memset(&sinfo, 0, sizeof(sinfo)); get_slabinfo(s, &sinfo); - memcg_accumulate_slabinfo(s, &sinfo); - seq_printf(m, "%-17s %6lu %6lu %6u %4u %4d", - cache_name(s), sinfo.active_objs, sinfo.num_objs, s->size, + s->name, sinfo.active_objs, sinfo.num_objs, s->size, sinfo.objects_per_slab, (1 << sinfo.cache_order)); seq_printf(m, " : tunables %4u %4u %4u", @@ -1144,8 +972,7 @@ static int slab_show(struct seq_file *m, void *p) if (p == slab_caches.next) print_slabinfo_header(m); - if (is_root_cache(s)) - cache_show(s, m); + cache_show(s, m); return 0; } @@ -1170,13 +997,13 @@ void dump_unreclaimable_slab(void) pr_info("Name Used Total\n"); list_for_each_entry_safe(s, s2, &slab_caches, list) { - if (!is_root_cache(s) || (s->flags & SLAB_RECLAIM_ACCOUNT)) + if (s->flags & SLAB_RECLAIM_ACCOUNT) continue; get_slabinfo(s, &sinfo); if (sinfo.num_objs > 0) - pr_info("%-17s %10luKB %10luKB\n", cache_name(s), + pr_info("%-17s %10luKB %10luKB\n", s->name, (sinfo.active_objs * s->size) / 1024, (sinfo.num_objs * s->size) / 1024); } @@ -1235,53 +1062,6 @@ static int __init slab_proc_init(void) } module_init(slab_proc_init); -#if defined(CONFIG_DEBUG_FS) && defined(CONFIG_MEMCG_KMEM) -/* - * Display information about kmem caches that have memcg cache. - */ -static int memcg_slabinfo_show(struct seq_file *m, void *unused) -{ - struct kmem_cache *s, *c; - struct slabinfo sinfo; - - mutex_lock(&slab_mutex); - seq_puts(m, "# "); - seq_puts(m, " \n"); - list_for_each_entry(s, &slab_caches, list) { - /* - * Skip kmem caches that don't have the memcg cache. - */ - if (!s->memcg_params.memcg_cache) - continue; - - memset(&sinfo, 0, sizeof(sinfo)); - get_slabinfo(s, &sinfo); - seq_printf(m, "%-17s root %6lu %6lu %6lu %6lu\n", - cache_name(s), sinfo.active_objs, sinfo.num_objs, - sinfo.active_slabs, sinfo.num_slabs); - - c = s->memcg_params.memcg_cache; - memset(&sinfo, 0, sizeof(sinfo)); - get_slabinfo(c, &sinfo); - seq_printf(m, "%-17s %4d %6lu %6lu %6lu %6lu\n", - cache_name(c), root_mem_cgroup->css.id, - sinfo.active_objs, sinfo.num_objs, - sinfo.active_slabs, sinfo.num_slabs); - } - mutex_unlock(&slab_mutex); - return 0; -} -DEFINE_SHOW_ATTRIBUTE(memcg_slabinfo); - -static int __init memcg_slabinfo_init(void) -{ - debugfs_create_file("memcg_slabinfo", S_IFREG | S_IRUGO, - NULL, NULL, &memcg_slabinfo_fops); - return 0; -} - -late_initcall(memcg_slabinfo_init); -#endif /* CONFIG_DEBUG_FS && CONFIG_MEMCG_KMEM */ #endif /* CONFIG_SLAB || CONFIG_SLUB_DEBUG */ static __always_inline void *__do_krealloc(const void *p, size_t new_size, diff --git a/mm/slub.c b/mm/slub.c index 9cd724fe37d89..eba8f57d5734b 100644 --- a/mm/slub.c +++ b/mm/slub.c @@ -218,14 +218,10 @@ enum track_item { TRACK_ALLOC, TRACK_FREE }; #ifdef CONFIG_SYSFS static int sysfs_slab_add(struct kmem_cache *); static int sysfs_slab_alias(struct kmem_cache *, const char *); -static void memcg_propagate_slab_attrs(struct kmem_cache *s); -static void sysfs_slab_remove(struct kmem_cache *s); #else static inline int sysfs_slab_add(struct kmem_cache *s) { return 0; } static inline int sysfs_slab_alias(struct kmem_cache *s, const char *p) { return 0; } -static inline void memcg_propagate_slab_attrs(struct kmem_cache *s) { } -static inline void sysfs_slab_remove(struct kmem_cache *s) { } #endif static inline void stat(const struct kmem_cache *s, enum stat_item si) @@ -1624,10 +1620,8 @@ static inline struct page *alloc_slab_page(struct kmem_cache *s, else page = __alloc_pages_node(node, flags, order); - if (page && charge_slab_page(page, flags, order, s)) { - __free_pages(page, order); - page = NULL; - } + if (page) + charge_slab_page(page, flags, order, s); return page; } @@ -3920,7 +3914,6 @@ int __kmem_cache_shutdown(struct kmem_cache *s) if (n->nr_partial || slabs_node(s, node)) return 1; } - sysfs_slab_remove(s); return 0; } @@ -4358,7 +4351,6 @@ static struct kmem_cache * __init bootstrap(struct kmem_cache *static_cache) p->slab_cache = s; #endif } - slab_init_memcg_params(s); list_add(&s->list, &slab_caches); return s; } @@ -4414,7 +4406,7 @@ struct kmem_cache * __kmem_cache_alias(const char *name, unsigned int size, unsigned int align, slab_flags_t flags, void (*ctor)(void *)) { - struct kmem_cache *s, *c; + struct kmem_cache *s; s = find_mergeable(size, align, flags, name, ctor); if (s) { @@ -4427,12 +4419,6 @@ __kmem_cache_alias(const char *name, unsigned int size, unsigned int align, s->object_size = max(s->object_size, size); s->inuse = max(s->inuse, ALIGN(size, sizeof(void *))); - c = memcg_cache(s); - if (c) { - c->object_size = s->object_size; - c->inuse = max(c->inuse, ALIGN(size, sizeof(void *))); - } - if (sysfs_slab_alias(s, name)) { s->refcount--; s = NULL; @@ -4454,7 +4440,6 @@ int __kmem_cache_create(struct kmem_cache *s, slab_flags_t flags) if (slab_state <= UP) return 0; - memcg_propagate_slab_attrs(s); err = sysfs_slab_add(s); if (err) __kmem_cache_release(s); @@ -5312,7 +5297,7 @@ static ssize_t shrink_store(struct kmem_cache *s, const char *buf, size_t length) { if (buf[0] == '1') - kmem_cache_shrink_all(s); + kmem_cache_shrink(s); else return -EINVAL; return length; @@ -5536,99 +5521,9 @@ static ssize_t slab_attr_store(struct kobject *kobj, return -EIO; err = attribute->store(s, buf, len); -#ifdef CONFIG_MEMCG - if (slab_state >= FULL && err >= 0 && is_root_cache(s)) { - struct kmem_cache *c; - - mutex_lock(&slab_mutex); - if (s->max_attr_size < len) - s->max_attr_size = len; - - /* - * This is a best effort propagation, so this function's return - * value will be determined by the parent cache only. This is - * basically because not all attributes will have a well - * defined semantics for rollbacks - most of the actions will - * have permanent effects. - * - * Returning the error value of any of the children that fail - * is not 100 % defined, in the sense that users seeing the - * error code won't be able to know anything about the state of - * the cache. - * - * Only returning the error code for the parent cache at least - * has well defined semantics. The cache being written to - * directly either failed or succeeded, in which case we loop - * through the descendants with best-effort propagation. - */ - c = memcg_cache(s); - if (c) - attribute->store(c, buf, len); - mutex_unlock(&slab_mutex); - } -#endif return err; } -static void memcg_propagate_slab_attrs(struct kmem_cache *s) -{ -#ifdef CONFIG_MEMCG - int i; - char *buffer = NULL; - struct kmem_cache *root_cache; - - if (is_root_cache(s)) - return; - - root_cache = s->memcg_params.root_cache; - - /* - * This mean this cache had no attribute written. Therefore, no point - * in copying default values around - */ - if (!root_cache->max_attr_size) - return; - - for (i = 0; i < ARRAY_SIZE(slab_attrs); i++) { - char mbuf[64]; - char *buf; - struct slab_attribute *attr = to_slab_attr(slab_attrs[i]); - ssize_t len; - - if (!attr || !attr->store || !attr->show) - continue; - - /* - * It is really bad that we have to allocate here, so we will - * do it only as a fallback. If we actually allocate, though, - * we can just use the allocated buffer until the end. - * - * Most of the slub attributes will tend to be very small in - * size, but sysfs allows buffers up to a page, so they can - * theoretically happen. - */ - if (buffer) - buf = buffer; - else if (root_cache->max_attr_size < ARRAY_SIZE(mbuf) && - !IS_ENABLED(CONFIG_SLUB_STATS)) - buf = mbuf; - else { - buffer = (char *) get_zeroed_page(GFP_KERNEL); - if (WARN_ON(!buffer)) - continue; - buf = buffer; - } - - len = attr->show(root_cache, buf); - if (len > 0) - attr->store(s, buf, len); - } - - if (buffer) - free_page((unsigned long)buffer); -#endif /* CONFIG_MEMCG */ -} - static void kmem_cache_release(struct kobject *k) { slab_kmem_cache_release(to_slab(k)); @@ -5648,10 +5543,6 @@ static struct kset *slab_kset; static inline struct kset *cache_kset(struct kmem_cache *s) { -#ifdef CONFIG_MEMCG - if (!is_root_cache(s)) - return s->memcg_params.root_cache->memcg_kset; -#endif return slab_kset; } @@ -5694,27 +5585,6 @@ static char *create_unique_id(struct kmem_cache *s) return name; } -static void sysfs_slab_remove_workfn(struct work_struct *work) -{ - struct kmem_cache *s = - container_of(work, struct kmem_cache, kobj_remove_work); - - if (!s->kobj.state_in_sysfs) - /* - * For a memcg cache, this may be called during - * deactivation and again on shutdown. Remove only once. - * A cache is never shut down before deactivation is - * complete, so no need to worry about synchronization. - */ - goto out; - -#ifdef CONFIG_MEMCG - kset_unregister(s->memcg_kset); -#endif -out: - kobject_put(&s->kobj); -} - static int sysfs_slab_add(struct kmem_cache *s) { int err; @@ -5722,8 +5592,6 @@ static int sysfs_slab_add(struct kmem_cache *s) struct kset *kset = cache_kset(s); int unmergeable = slab_unmergeable(s); - INIT_WORK(&s->kobj_remove_work, sysfs_slab_remove_workfn); - if (!kset) { kobject_init(&s->kobj, &slab_ktype); return 0; @@ -5760,16 +5628,6 @@ static int sysfs_slab_add(struct kmem_cache *s) if (err) goto out_del_kobj; -#ifdef CONFIG_MEMCG - if (is_root_cache(s) && memcg_sysfs_enabled) { - s->memcg_kset = kset_create_and_add("cgroup", NULL, &s->kobj); - if (!s->memcg_kset) { - err = -ENOMEM; - goto out_del_kobj; - } - } -#endif - if (!unmergeable) { /* Setup first alias */ sysfs_slab_alias(s, s->name); @@ -5783,19 +5641,6 @@ out_del_kobj: goto out; } -static void sysfs_slab_remove(struct kmem_cache *s) -{ - if (slab_state < FULL) - /* - * Sysfs has not been setup yet so no need to remove the - * cache from sysfs. - */ - return; - - kobject_get(&s->kobj); - schedule_work(&s->kobj_remove_work); -} - void sysfs_slab_unlink(struct kmem_cache *s) { if (slab_state >= FULL) -- GitLab From 933dc80ec262ed8b1cc2ab463d989e1f59ee05c4 Mon Sep 17 00:00:00 2001 From: Roman Gushchin Date: Thu, 6 Aug 2020 23:21:30 -0700 Subject: [PATCH 1388/1476] kselftests: cgroup: add kernel memory accounting tests Add some tests to cover the kernel memory accounting functionality. These are covering some issues (and changes) we had recently. 1) A test which allocates a lot of negative dentries, checks memcg slab statistics, creates memory pressure by setting memory.max to some low value and checks that some number of slabs was reclaimed. 2) A test which covers side effects of memcg destruction: it creates and destroys a large number of sub-cgroups, each containing a multi-threaded workload which allocates and releases some kernel memory. Then it checks that the charge ans memory.stats do add up on the parent level. 3) A test which reads /proc/kpagecgroup and implicitly checks that it doesn't crash the system. 4) A test which spawns a large number of threads and checks that the kernel stacks accounting works as expected. 5) A test which checks that living charged slab objects are not preventing the memory cgroup from being released after being deleted by a user. Signed-off-by: Roman Gushchin Signed-off-by: Andrew Morton Cc: Christoph Lameter Cc: Johannes Weiner Cc: Michal Hocko Cc: Shakeel Butt Cc: Tejun Heo Cc: Vlastimil Babka Link: http://lkml.kernel.org/r/20200623174037.3951353-19-guro@fb.com Signed-off-by: Linus Torvalds --- tools/testing/selftests/cgroup/.gitignore | 1 + tools/testing/selftests/cgroup/Makefile | 2 + tools/testing/selftests/cgroup/test_kmem.c | 382 +++++++++++++++++++++ 3 files changed, 385 insertions(+) create mode 100644 tools/testing/selftests/cgroup/test_kmem.c diff --git a/tools/testing/selftests/cgroup/.gitignore b/tools/testing/selftests/cgroup/.gitignore index aa6de65b08384..84cfcabea8387 100644 --- a/tools/testing/selftests/cgroup/.gitignore +++ b/tools/testing/selftests/cgroup/.gitignore @@ -2,3 +2,4 @@ test_memcontrol test_core test_freezer +test_kmem \ No newline at end of file diff --git a/tools/testing/selftests/cgroup/Makefile b/tools/testing/selftests/cgroup/Makefile index 967f268fde74e..f027d933595b3 100644 --- a/tools/testing/selftests/cgroup/Makefile +++ b/tools/testing/selftests/cgroup/Makefile @@ -6,11 +6,13 @@ all: TEST_FILES := with_stress.sh TEST_PROGS := test_stress.sh TEST_GEN_PROGS = test_memcontrol +TEST_GEN_PROGS += test_kmem TEST_GEN_PROGS += test_core TEST_GEN_PROGS += test_freezer include ../lib.mk $(OUTPUT)/test_memcontrol: cgroup_util.c ../clone3/clone3_selftests.h +$(OUTPUT)/test_kmem: cgroup_util.c ../clone3/clone3_selftests.h $(OUTPUT)/test_core: cgroup_util.c ../clone3/clone3_selftests.h $(OUTPUT)/test_freezer: cgroup_util.c ../clone3/clone3_selftests.h diff --git a/tools/testing/selftests/cgroup/test_kmem.c b/tools/testing/selftests/cgroup/test_kmem.c new file mode 100644 index 0000000000000..5224dae216e5f --- /dev/null +++ b/tools/testing/selftests/cgroup/test_kmem.c @@ -0,0 +1,382 @@ +// SPDX-License-Identifier: GPL-2.0 +#define _GNU_SOURCE + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../kselftest.h" +#include "cgroup_util.h" + + +static int alloc_dcache(const char *cgroup, void *arg) +{ + unsigned long i; + struct stat st; + char buf[128]; + + for (i = 0; i < (unsigned long)arg; i++) { + snprintf(buf, sizeof(buf), + "/something-non-existent-with-a-long-name-%64lu-%d", + i, getpid()); + stat(buf, &st); + } + + return 0; +} + +/* + * This test allocates 100000 of negative dentries with long names. + * Then it checks that "slab" in memory.stat is larger than 1M. + * Then it sets memory.high to 1M and checks that at least 1/2 + * of slab memory has been reclaimed. + */ +static int test_kmem_basic(const char *root) +{ + int ret = KSFT_FAIL; + char *cg = NULL; + long slab0, slab1, current; + + cg = cg_name(root, "kmem_basic_test"); + if (!cg) + goto cleanup; + + if (cg_create(cg)) + goto cleanup; + + if (cg_run(cg, alloc_dcache, (void *)100000)) + goto cleanup; + + slab0 = cg_read_key_long(cg, "memory.stat", "slab "); + if (slab0 < (1 << 20)) + goto cleanup; + + cg_write(cg, "memory.high", "1M"); + slab1 = cg_read_key_long(cg, "memory.stat", "slab "); + if (slab1 <= 0) + goto cleanup; + + current = cg_read_long(cg, "memory.current"); + if (current <= 0) + goto cleanup; + + if (slab1 < slab0 / 2 && current < slab0 / 2) + ret = KSFT_PASS; +cleanup: + cg_destroy(cg); + free(cg); + + return ret; +} + +static void *alloc_kmem_fn(void *arg) +{ + alloc_dcache(NULL, (void *)100); + return NULL; +} + +static int alloc_kmem_smp(const char *cgroup, void *arg) +{ + int nr_threads = 2 * get_nprocs(); + pthread_t *tinfo; + unsigned long i; + int ret = -1; + + tinfo = calloc(nr_threads, sizeof(pthread_t)); + if (tinfo == NULL) + return -1; + + for (i = 0; i < nr_threads; i++) { + if (pthread_create(&tinfo[i], NULL, &alloc_kmem_fn, + (void *)i)) { + free(tinfo); + return -1; + } + } + + for (i = 0; i < nr_threads; i++) { + ret = pthread_join(tinfo[i], NULL); + if (ret) + break; + } + + free(tinfo); + return ret; +} + +static int cg_run_in_subcgroups(const char *parent, + int (*fn)(const char *cgroup, void *arg), + void *arg, int times) +{ + char *child; + int i; + + for (i = 0; i < times; i++) { + child = cg_name_indexed(parent, "child", i); + if (!child) + return -1; + + if (cg_create(child)) { + cg_destroy(child); + free(child); + return -1; + } + + if (cg_run(child, fn, NULL)) { + cg_destroy(child); + free(child); + return -1; + } + + cg_destroy(child); + free(child); + } + + return 0; +} + +/* + * The test creates and destroys a large number of cgroups. In each cgroup it + * allocates some slab memory (mostly negative dentries) using 2 * NR_CPUS + * threads. Then it checks the sanity of numbers on the parent level: + * the total size of the cgroups should be roughly equal to + * anon + file + slab + kernel_stack. + */ +static int test_kmem_memcg_deletion(const char *root) +{ + long current, slab, anon, file, kernel_stack, sum; + int ret = KSFT_FAIL; + char *parent; + + parent = cg_name(root, "kmem_memcg_deletion_test"); + if (!parent) + goto cleanup; + + if (cg_create(parent)) + goto cleanup; + + if (cg_write(parent, "cgroup.subtree_control", "+memory")) + goto cleanup; + + if (cg_run_in_subcgroups(parent, alloc_kmem_smp, NULL, 100)) + goto cleanup; + + current = cg_read_long(parent, "memory.current"); + slab = cg_read_key_long(parent, "memory.stat", "slab "); + anon = cg_read_key_long(parent, "memory.stat", "anon "); + file = cg_read_key_long(parent, "memory.stat", "file "); + kernel_stack = cg_read_key_long(parent, "memory.stat", "kernel_stack "); + if (current < 0 || slab < 0 || anon < 0 || file < 0 || + kernel_stack < 0) + goto cleanup; + + sum = slab + anon + file + kernel_stack; + if (abs(sum - current) < 4096 * 32 * 2 * get_nprocs()) { + ret = KSFT_PASS; + } else { + printf("memory.current = %ld\n", current); + printf("slab + anon + file + kernel_stack = %ld\n", sum); + printf("slab = %ld\n", slab); + printf("anon = %ld\n", anon); + printf("file = %ld\n", file); + printf("kernel_stack = %ld\n", kernel_stack); + } + +cleanup: + cg_destroy(parent); + free(parent); + + return ret; +} + +/* + * The test reads the entire /proc/kpagecgroup. If the operation went + * successfully (and the kernel didn't panic), the test is treated as passed. + */ +static int test_kmem_proc_kpagecgroup(const char *root) +{ + unsigned long buf[128]; + int ret = KSFT_FAIL; + ssize_t len; + int fd; + + fd = open("/proc/kpagecgroup", O_RDONLY); + if (fd < 0) + return ret; + + do { + len = read(fd, buf, sizeof(buf)); + } while (len > 0); + + if (len == 0) + ret = KSFT_PASS; + + close(fd); + return ret; +} + +static void *pthread_wait_fn(void *arg) +{ + sleep(100); + return NULL; +} + +static int spawn_1000_threads(const char *cgroup, void *arg) +{ + int nr_threads = 1000; + pthread_t *tinfo; + unsigned long i; + long stack; + int ret = -1; + + tinfo = calloc(nr_threads, sizeof(pthread_t)); + if (tinfo == NULL) + return -1; + + for (i = 0; i < nr_threads; i++) { + if (pthread_create(&tinfo[i], NULL, &pthread_wait_fn, + (void *)i)) { + free(tinfo); + return(-1); + } + } + + stack = cg_read_key_long(cgroup, "memory.stat", "kernel_stack "); + if (stack >= 4096 * 1000) + ret = 0; + + free(tinfo); + return ret; +} + +/* + * The test spawns a process, which spawns 1000 threads. Then it checks + * that memory.stat's kernel_stack is at least 1000 pages large. + */ +static int test_kmem_kernel_stacks(const char *root) +{ + int ret = KSFT_FAIL; + char *cg = NULL; + + cg = cg_name(root, "kmem_kernel_stacks_test"); + if (!cg) + goto cleanup; + + if (cg_create(cg)) + goto cleanup; + + if (cg_run(cg, spawn_1000_threads, NULL)) + goto cleanup; + + ret = KSFT_PASS; +cleanup: + cg_destroy(cg); + free(cg); + + return ret; +} + +/* + * This test sequentionally creates 30 child cgroups, allocates some + * kernel memory in each of them, and deletes them. Then it checks + * that the number of dying cgroups on the parent level is 0. + */ +static int test_kmem_dead_cgroups(const char *root) +{ + int ret = KSFT_FAIL; + char *parent; + long dead; + int i; + + parent = cg_name(root, "kmem_dead_cgroups_test"); + if (!parent) + goto cleanup; + + if (cg_create(parent)) + goto cleanup; + + if (cg_write(parent, "cgroup.subtree_control", "+memory")) + goto cleanup; + + if (cg_run_in_subcgroups(parent, alloc_dcache, (void *)100, 30)) + goto cleanup; + + for (i = 0; i < 5; i++) { + dead = cg_read_key_long(parent, "cgroup.stat", + "nr_dying_descendants "); + if (dead == 0) { + ret = KSFT_PASS; + break; + } + /* + * Reclaiming cgroups might take some time, + * let's wait a bit and repeat. + */ + sleep(1); + } + +cleanup: + cg_destroy(parent); + free(parent); + + return ret; +} + +#define T(x) { x, #x } +struct kmem_test { + int (*fn)(const char *root); + const char *name; +} tests[] = { + T(test_kmem_basic), + T(test_kmem_memcg_deletion), + T(test_kmem_proc_kpagecgroup), + T(test_kmem_kernel_stacks), + T(test_kmem_dead_cgroups), +}; +#undef T + +int main(int argc, char **argv) +{ + char root[PATH_MAX]; + int i, ret = EXIT_SUCCESS; + + if (cg_find_unified_root(root, sizeof(root))) + ksft_exit_skip("cgroup v2 isn't mounted\n"); + + /* + * Check that memory controller is available: + * memory is listed in cgroup.controllers + */ + if (cg_read_strstr(root, "cgroup.controllers", "memory")) + ksft_exit_skip("memory controller isn't available\n"); + + if (cg_read_strstr(root, "cgroup.subtree_control", "memory")) + if (cg_write(root, "cgroup.subtree_control", "+memory")) + ksft_exit_skip("Failed to set memory controller\n"); + + for (i = 0; i < ARRAY_SIZE(tests); i++) { + switch (tests[i].fn(root)) { + case KSFT_PASS: + ksft_test_result_pass("%s\n", tests[i].name); + break; + case KSFT_SKIP: + ksft_test_result_skip("%s\n", tests[i].name); + break; + default: + ret = EXIT_FAILURE; + ksft_test_result_fail("%s\n", tests[i].name); + break; + } + } + + return ret; +} -- GitLab From fbc1ac9d09d70859eee24131d667e01e3986e368 Mon Sep 17 00:00:00 2001 From: Roman Gushchin Date: Thu, 6 Aug 2020 23:21:34 -0700 Subject: [PATCH 1389/1476] tools/cgroup: add memcg_slabinfo.py tool Add a drgn-based tool to display slab information for a given memcg. Can replace cgroup v1 memory.kmem.slabinfo interface on cgroup v2, but in a more flexiable way. Currently supports only SLUB configuration, but SLAB can be trivially added later. Output example: $ sudo ./tools/cgroup/memcg_slabinfo.py /sys/fs/cgroup/user.slice/user-111017.slice/user\@111017.service shmem_inode_cache 92 92 704 46 8 : tunables 0 0 0 : slabdata 2 2 0 eventpoll_pwq 56 56 72 56 1 : tunables 0 0 0 : slabdata 1 1 0 eventpoll_epi 32 32 128 32 1 : tunables 0 0 0 : slabdata 1 1 0 kmalloc-8 0 0 8 512 1 : tunables 0 0 0 : slabdata 0 0 0 kmalloc-96 0 0 96 42 1 : tunables 0 0 0 : slabdata 0 0 0 kmalloc-2048 0 0 2048 16 8 : tunables 0 0 0 : slabdata 0 0 0 kmalloc-64 128 128 64 64 1 : tunables 0 0 0 : slabdata 2 2 0 mm_struct 160 160 1024 32 8 : tunables 0 0 0 : slabdata 5 5 0 signal_cache 96 96 1024 32 8 : tunables 0 0 0 : slabdata 3 3 0 sighand_cache 45 45 2112 15 8 : tunables 0 0 0 : slabdata 3 3 0 files_cache 138 138 704 46 8 : tunables 0 0 0 : slabdata 3 3 0 task_delay_info 153 153 80 51 1 : tunables 0 0 0 : slabdata 3 3 0 task_struct 27 27 3520 9 8 : tunables 0 0 0 : slabdata 3 3 0 radix_tree_node 56 56 584 28 4 : tunables 0 0 0 : slabdata 2 2 0 btrfs_inode 140 140 1136 28 8 : tunables 0 0 0 : slabdata 5 5 0 kmalloc-1024 64 64 1024 32 8 : tunables 0 0 0 : slabdata 2 2 0 kmalloc-192 84 84 192 42 2 : tunables 0 0 0 : slabdata 2 2 0 inode_cache 54 54 600 27 4 : tunables 0 0 0 : slabdata 2 2 0 kmalloc-128 0 0 128 32 1 : tunables 0 0 0 : slabdata 0 0 0 kmalloc-512 32 32 512 32 4 : tunables 0 0 0 : slabdata 1 1 0 skbuff_head_cache 32 32 256 32 2 : tunables 0 0 0 : slabdata 1 1 0 sock_inode_cache 46 46 704 46 8 : tunables 0 0 0 : slabdata 1 1 0 cred_jar 378 378 192 42 2 : tunables 0 0 0 : slabdata 9 9 0 proc_inode_cache 96 96 672 24 4 : tunables 0 0 0 : slabdata 4 4 0 dentry 336 336 192 42 2 : tunables 0 0 0 : slabdata 8 8 0 filp 697 864 256 32 2 : tunables 0 0 0 : slabdata 27 27 0 anon_vma 644 644 88 46 1 : tunables 0 0 0 : slabdata 14 14 0 pid 1408 1408 64 64 1 : tunables 0 0 0 : slabdata 22 22 0 vm_area_struct 1200 1200 200 40 2 : tunables 0 0 0 : slabdata 30 30 0 Signed-off-by: Roman Gushchin Signed-off-by: Andrew Morton Acked-by: Tejun Heo Cc: Christoph Lameter Cc: Johannes Weiner Cc: Michal Hocko Cc: Shakeel Butt Cc: Vlastimil Babka Link: http://lkml.kernel.org/r/20200623174037.3951353-20-guro@fb.com Signed-off-by: Linus Torvalds --- tools/cgroup/memcg_slabinfo.py | 226 +++++++++++++++++++++++++++++++++ 1 file changed, 226 insertions(+) create mode 100644 tools/cgroup/memcg_slabinfo.py diff --git a/tools/cgroup/memcg_slabinfo.py b/tools/cgroup/memcg_slabinfo.py new file mode 100644 index 0000000000000..c4225ed63565a --- /dev/null +++ b/tools/cgroup/memcg_slabinfo.py @@ -0,0 +1,226 @@ +#!/usr/bin/env drgn +# +# Copyright (C) 2020 Roman Gushchin +# Copyright (C) 2020 Facebook + +from os import stat +import argparse +import sys + +from drgn.helpers.linux import list_for_each_entry, list_empty +from drgn.helpers.linux import for_each_page +from drgn.helpers.linux.cpumask import for_each_online_cpu +from drgn.helpers.linux.percpu import per_cpu_ptr +from drgn import container_of, FaultError, Object + + +DESC = """ +This is a drgn script to provide slab statistics for memory cgroups. +It supports cgroup v2 and v1 and can emulate memory.kmem.slabinfo +interface of cgroup v1. +For drgn, visit https://github.com/osandov/drgn. +""" + + +MEMCGS = {} + +OO_SHIFT = 16 +OO_MASK = ((1 << OO_SHIFT) - 1) + + +def err(s): + print('slabinfo.py: error: %s' % s, file=sys.stderr, flush=True) + sys.exit(1) + + +def find_memcg_ids(css=prog['root_mem_cgroup'].css, prefix=''): + if not list_empty(css.children.address_of_()): + for css in list_for_each_entry('struct cgroup_subsys_state', + css.children.address_of_(), + 'sibling'): + name = prefix + '/' + css.cgroup.kn.name.string_().decode('utf-8') + memcg = container_of(css, 'struct mem_cgroup', 'css') + MEMCGS[css.cgroup.kn.id.value_()] = memcg + find_memcg_ids(css, name) + + +def is_root_cache(s): + try: + return False if s.memcg_params.root_cache else True + except AttributeError: + return True + + +def cache_name(s): + if is_root_cache(s): + return s.name.string_().decode('utf-8') + else: + return s.memcg_params.root_cache.name.string_().decode('utf-8') + + +# SLUB + +def oo_order(s): + return s.oo.x >> OO_SHIFT + + +def oo_objects(s): + return s.oo.x & OO_MASK + + +def count_partial(n, fn): + nr_pages = 0 + for page in list_for_each_entry('struct page', n.partial.address_of_(), + 'lru'): + nr_pages += fn(page) + return nr_pages + + +def count_free(page): + return page.objects - page.inuse + + +def slub_get_slabinfo(s, cfg): + nr_slabs = 0 + nr_objs = 0 + nr_free = 0 + + for node in range(cfg['nr_nodes']): + n = s.node[node] + nr_slabs += n.nr_slabs.counter.value_() + nr_objs += n.total_objects.counter.value_() + nr_free += count_partial(n, count_free) + + return {'active_objs': nr_objs - nr_free, + 'num_objs': nr_objs, + 'active_slabs': nr_slabs, + 'num_slabs': nr_slabs, + 'objects_per_slab': oo_objects(s), + 'cache_order': oo_order(s), + 'limit': 0, + 'batchcount': 0, + 'shared': 0, + 'shared_avail': 0} + + +def cache_show(s, cfg, objs): + if cfg['allocator'] == 'SLUB': + sinfo = slub_get_slabinfo(s, cfg) + else: + err('SLAB isn\'t supported yet') + + if cfg['shared_slab_pages']: + sinfo['active_objs'] = objs + sinfo['num_objs'] = objs + + print('%-17s %6lu %6lu %6u %4u %4d' + ' : tunables %4u %4u %4u' + ' : slabdata %6lu %6lu %6lu' % ( + cache_name(s), sinfo['active_objs'], sinfo['num_objs'], + s.size, sinfo['objects_per_slab'], 1 << sinfo['cache_order'], + sinfo['limit'], sinfo['batchcount'], sinfo['shared'], + sinfo['active_slabs'], sinfo['num_slabs'], + sinfo['shared_avail'])) + + +def detect_kernel_config(): + cfg = {} + + cfg['nr_nodes'] = prog['nr_online_nodes'].value_() + + if prog.type('struct kmem_cache').members[1][1] == 'flags': + cfg['allocator'] = 'SLUB' + elif prog.type('struct kmem_cache').members[1][1] == 'batchcount': + cfg['allocator'] = 'SLAB' + else: + err('Can\'t determine the slab allocator') + + cfg['shared_slab_pages'] = False + try: + if prog.type('struct obj_cgroup'): + cfg['shared_slab_pages'] = True + except: + pass + + return cfg + + +def for_each_slab_page(prog): + PGSlab = 1 << prog.constant('PG_slab') + PGHead = 1 << prog.constant('PG_head') + + for page in for_each_page(prog): + try: + if page.flags.value_() & PGSlab: + yield page + except FaultError: + pass + + +def main(): + parser = argparse.ArgumentParser(description=DESC, + formatter_class= + argparse.RawTextHelpFormatter) + parser.add_argument('cgroup', metavar='CGROUP', + help='Target memory cgroup') + args = parser.parse_args() + + try: + cgroup_id = stat(args.cgroup).st_ino + find_memcg_ids() + memcg = MEMCGS[cgroup_id] + except KeyError: + err('Can\'t find the memory cgroup') + + cfg = detect_kernel_config() + + print('# name ' + ' : tunables ' + ' : slabdata ') + + if cfg['shared_slab_pages']: + obj_cgroups = set() + stats = {} + caches = {} + + # find memcg pointers belonging to the specified cgroup + obj_cgroups.add(memcg.objcg.value_()) + for ptr in list_for_each_entry('struct obj_cgroup', + memcg.objcg_list.address_of_(), + 'list'): + obj_cgroups.add(ptr.value_()) + + # look over all slab pages, belonging to non-root memcgs + # and look for objects belonging to the given memory cgroup + for page in for_each_slab_page(prog): + objcg_vec_raw = page.obj_cgroups.value_() + if objcg_vec_raw == 0: + continue + cache = page.slab_cache + if not cache: + continue + addr = cache.value_() + caches[addr] = cache + # clear the lowest bit to get the true obj_cgroups + objcg_vec = Object(prog, page.obj_cgroups.type_, + value=objcg_vec_raw & ~1) + + if addr not in stats: + stats[addr] = 0 + + for i in range(oo_objects(cache)): + if objcg_vec[i].value_() in obj_cgroups: + stats[addr] += 1 + + for addr in caches: + if stats[addr] > 0: + cache_show(caches[addr], cfg, stats[addr]) + + else: + for s in list_for_each_entry('struct kmem_cache', + memcg.kmem_caches.address_of_(), + 'memcg_params.kmem_caches_node'): + cache_show(s, cfg, None) + + +main() -- GitLab From 991e7673859ed41e7ba83c8c4e57afe8cfebe314 Mon Sep 17 00:00:00 2001 From: Shakeel Butt Date: Thu, 6 Aug 2020 23:21:37 -0700 Subject: [PATCH 1390/1476] mm: memcontrol: account kernel stack per node Currently the kernel stack is being accounted per-zone. There is no need to do that. In addition due to being per-zone, memcg has to keep a separate MEMCG_KERNEL_STACK_KB. Make the stat per-node and deprecate MEMCG_KERNEL_STACK_KB as memcg_stat_item is an extension of node_stat_item. In addition localize the kernel stack stats updates to account_kernel_stack(). Signed-off-by: Shakeel Butt Signed-off-by: Andrew Morton Reviewed-by: Roman Gushchin Cc: Johannes Weiner Cc: Michal Hocko Link: http://lkml.kernel.org/r/20200630161539.1759185-1-shakeelb@google.com Signed-off-by: Linus Torvalds --- drivers/base/node.c | 4 +-- fs/proc/meminfo.c | 4 +-- include/linux/memcontrol.h | 21 ++++++++++++++-- include/linux/mmzone.h | 8 +++--- kernel/fork.c | 51 +++++++++++--------------------------- kernel/scs.c | 2 +- mm/memcontrol.c | 2 +- mm/page_alloc.c | 16 ++++++------ mm/vmstat.c | 8 +++--- 9 files changed, 55 insertions(+), 61 deletions(-) diff --git a/drivers/base/node.c b/drivers/base/node.c index 0cf13e31603c9..508b80f6329b4 100644 --- a/drivers/base/node.c +++ b/drivers/base/node.c @@ -440,9 +440,9 @@ static ssize_t node_read_meminfo(struct device *dev, nid, K(node_page_state(pgdat, NR_FILE_MAPPED)), nid, K(node_page_state(pgdat, NR_ANON_MAPPED)), nid, K(i.sharedram), - nid, sum_zone_node_page_state(nid, NR_KERNEL_STACK_KB), + nid, node_page_state(pgdat, NR_KERNEL_STACK_KB), #ifdef CONFIG_SHADOW_CALL_STACK - nid, sum_zone_node_page_state(nid, NR_KERNEL_SCS_KB), + nid, node_page_state(pgdat, NR_KERNEL_SCS_KB), #endif nid, K(sum_zone_node_page_state(nid, NR_PAGETABLE)), nid, 0UL, diff --git a/fs/proc/meminfo.c b/fs/proc/meminfo.c index 38ea95fd919a5..2a4c58f70fb91 100644 --- a/fs/proc/meminfo.c +++ b/fs/proc/meminfo.c @@ -101,10 +101,10 @@ static int meminfo_proc_show(struct seq_file *m, void *v) show_val_kb(m, "SReclaimable: ", sreclaimable); show_val_kb(m, "SUnreclaim: ", sunreclaim); seq_printf(m, "KernelStack: %8lu kB\n", - global_zone_page_state(NR_KERNEL_STACK_KB)); + global_node_page_state(NR_KERNEL_STACK_KB)); #ifdef CONFIG_SHADOW_CALL_STACK seq_printf(m, "ShadowCallStack:%8lu kB\n", - global_zone_page_state(NR_KERNEL_SCS_KB)); + global_node_page_state(NR_KERNEL_SCS_KB)); #endif show_val_kb(m, "PageTables: ", global_zone_page_state(NR_PAGETABLE)); diff --git a/include/linux/memcontrol.h b/include/linux/memcontrol.h index 5a8b62d075e60..624400c27eba6 100644 --- a/include/linux/memcontrol.h +++ b/include/linux/memcontrol.h @@ -32,8 +32,6 @@ struct kmem_cache; enum memcg_stat_item { MEMCG_SWAP = NR_VM_NODE_STAT_ITEMS, MEMCG_SOCK, - /* XXX: why are these zone and not node counters? */ - MEMCG_KERNEL_STACK_KB, MEMCG_NR_STAT, }; @@ -729,8 +727,19 @@ void __mod_memcg_lruvec_state(struct lruvec *lruvec, enum node_stat_item idx, void __mod_lruvec_state(struct lruvec *lruvec, enum node_stat_item idx, int val); void __mod_lruvec_slab_state(void *p, enum node_stat_item idx, int val); + void mod_memcg_obj_state(void *p, int idx, int val); +static inline void mod_lruvec_slab_state(void *p, enum node_stat_item idx, + int val) +{ + unsigned long flags; + + local_irq_save(flags); + __mod_lruvec_slab_state(p, idx, val); + local_irq_restore(flags); +} + static inline void mod_memcg_lruvec_state(struct lruvec *lruvec, enum node_stat_item idx, int val) { @@ -1151,6 +1160,14 @@ static inline void __mod_lruvec_slab_state(void *p, enum node_stat_item idx, __mod_node_page_state(page_pgdat(page), idx, val); } +static inline void mod_lruvec_slab_state(void *p, enum node_stat_item idx, + int val) +{ + struct page *page = virt_to_head_page(p); + + mod_node_page_state(page_pgdat(page), idx, val); +} + static inline void mod_memcg_obj_state(void *p, int idx, int val) { } diff --git a/include/linux/mmzone.h b/include/linux/mmzone.h index b79100edd2285..a3bd54139a30e 100644 --- a/include/linux/mmzone.h +++ b/include/linux/mmzone.h @@ -155,10 +155,6 @@ enum zone_stat_item { NR_ZONE_WRITE_PENDING, /* Count of dirty, writeback and unstable pages */ NR_MLOCK, /* mlock()ed pages found and moved off LRU */ NR_PAGETABLE, /* used for pagetables */ - NR_KERNEL_STACK_KB, /* measured in KiB */ -#if IS_ENABLED(CONFIG_SHADOW_CALL_STACK) - NR_KERNEL_SCS_KB, /* measured in KiB */ -#endif /* Second 128 byte cacheline */ NR_BOUNCE, #if IS_ENABLED(CONFIG_ZSMALLOC) @@ -203,6 +199,10 @@ enum node_stat_item { NR_KERNEL_MISC_RECLAIMABLE, /* reclaimable non-slab kernel pages */ NR_FOLL_PIN_ACQUIRED, /* via: pin_user_page(), gup flag: FOLL_PIN */ NR_FOLL_PIN_RELEASED, /* pages returned via unpin_user_page() */ + NR_KERNEL_STACK_KB, /* measured in KiB */ +#if IS_ENABLED(CONFIG_SHADOW_CALL_STACK) + NR_KERNEL_SCS_KB, /* measured in KiB */ +#endif NR_VM_NODE_STAT_ITEMS }; diff --git a/kernel/fork.c b/kernel/fork.c index 76d3f3387554b..c7b4ce9d2647c 100644 --- a/kernel/fork.c +++ b/kernel/fork.c @@ -276,13 +276,8 @@ static inline void free_thread_stack(struct task_struct *tsk) if (vm) { int i; - for (i = 0; i < THREAD_SIZE / PAGE_SIZE; i++) { - mod_memcg_page_state(vm->pages[i], - MEMCG_KERNEL_STACK_KB, - -(int)(PAGE_SIZE / 1024)); - + for (i = 0; i < THREAD_SIZE / PAGE_SIZE; i++) memcg_kmem_uncharge_page(vm->pages[i], 0); - } for (i = 0; i < NR_CACHED_STACKS; i++) { if (this_cpu_cmpxchg(cached_stacks[i], @@ -382,31 +377,14 @@ static void account_kernel_stack(struct task_struct *tsk, int account) void *stack = task_stack_page(tsk); struct vm_struct *vm = task_stack_vm_area(tsk); - BUILD_BUG_ON(IS_ENABLED(CONFIG_VMAP_STACK) && PAGE_SIZE % 1024 != 0); - - if (vm) { - int i; - - BUG_ON(vm->nr_pages != THREAD_SIZE / PAGE_SIZE); - for (i = 0; i < THREAD_SIZE / PAGE_SIZE; i++) { - mod_zone_page_state(page_zone(vm->pages[i]), - NR_KERNEL_STACK_KB, - PAGE_SIZE / 1024 * account); - } - } else { - /* - * All stack pages are in the same zone and belong to the - * same memcg. - */ - struct page *first_page = virt_to_page(stack); - - mod_zone_page_state(page_zone(first_page), NR_KERNEL_STACK_KB, - THREAD_SIZE / 1024 * account); - - mod_memcg_obj_state(stack, MEMCG_KERNEL_STACK_KB, - account * (THREAD_SIZE / 1024)); - } + /* All stack pages are in the same node. */ + if (vm) + mod_lruvec_page_state(vm->pages[0], NR_KERNEL_STACK_KB, + account * (THREAD_SIZE / 1024)); + else + mod_lruvec_slab_state(stack, NR_KERNEL_STACK_KB, + account * (THREAD_SIZE / 1024)); } static int memcg_charge_kernel_stack(struct task_struct *tsk) @@ -415,24 +393,23 @@ static int memcg_charge_kernel_stack(struct task_struct *tsk) struct vm_struct *vm = task_stack_vm_area(tsk); int ret; + BUILD_BUG_ON(IS_ENABLED(CONFIG_VMAP_STACK) && PAGE_SIZE % 1024 != 0); + if (vm) { int i; + BUG_ON(vm->nr_pages != THREAD_SIZE / PAGE_SIZE); + for (i = 0; i < THREAD_SIZE / PAGE_SIZE; i++) { /* * If memcg_kmem_charge_page() fails, page->mem_cgroup - * pointer is NULL, and both memcg_kmem_uncharge_page() - * and mod_memcg_page_state() in free_thread_stack() - * will ignore this page. So it's safe. + * pointer is NULL, and memcg_kmem_uncharge_page() in + * free_thread_stack() will ignore this page. */ ret = memcg_kmem_charge_page(vm->pages[i], GFP_KERNEL, 0); if (ret) return ret; - - mod_memcg_page_state(vm->pages[i], - MEMCG_KERNEL_STACK_KB, - PAGE_SIZE / 1024); } } #endif diff --git a/kernel/scs.c b/kernel/scs.c index 5d4d9bbdec36c..4ff4a7ba0094f 100644 --- a/kernel/scs.c +++ b/kernel/scs.c @@ -17,7 +17,7 @@ static void __scs_account(void *s, int account) { struct page *scs_page = virt_to_page(s); - mod_zone_page_state(page_zone(scs_page), NR_KERNEL_SCS_KB, + mod_node_page_state(page_pgdat(scs_page), NR_KERNEL_SCS_KB, account * (SCS_SIZE / SZ_1K)); } diff --git a/mm/memcontrol.c b/mm/memcontrol.c index 473f9b91d51f8..a3e9633667694 100644 --- a/mm/memcontrol.c +++ b/mm/memcontrol.c @@ -1485,7 +1485,7 @@ static char *memory_stat_format(struct mem_cgroup *memcg) (u64)memcg_page_state(memcg, NR_FILE_PAGES) * PAGE_SIZE); seq_buf_printf(&s, "kernel_stack %llu\n", - (u64)memcg_page_state(memcg, MEMCG_KERNEL_STACK_KB) * + (u64)memcg_page_state(memcg, NR_KERNEL_STACK_KB) * 1024); seq_buf_printf(&s, "slab %llu\n", (u64)(memcg_page_state(memcg, NR_SLAB_RECLAIMABLE_B) + diff --git a/mm/page_alloc.c b/mm/page_alloc.c index f9ad093814d25..8d5d8526c2f33 100644 --- a/mm/page_alloc.c +++ b/mm/page_alloc.c @@ -5396,6 +5396,10 @@ void show_free_areas(unsigned int filter, nodemask_t *nodemask) " anon_thp: %lukB" #endif " writeback_tmp:%lukB" + " kernel_stack:%lukB" +#ifdef CONFIG_SHADOW_CALL_STACK + " shadow_call_stack:%lukB" +#endif " all_unreclaimable? %s" "\n", pgdat->node_id, @@ -5417,6 +5421,10 @@ void show_free_areas(unsigned int filter, nodemask_t *nodemask) K(node_page_state(pgdat, NR_ANON_THPS) * HPAGE_PMD_NR), #endif K(node_page_state(pgdat, NR_WRITEBACK_TEMP)), + node_page_state(pgdat, NR_KERNEL_STACK_KB), +#ifdef CONFIG_SHADOW_CALL_STACK + node_page_state(pgdat, NR_KERNEL_SCS_KB), +#endif pgdat->kswapd_failures >= MAX_RECLAIM_RETRIES ? "yes" : "no"); } @@ -5448,10 +5456,6 @@ void show_free_areas(unsigned int filter, nodemask_t *nodemask) " present:%lukB" " managed:%lukB" " mlocked:%lukB" - " kernel_stack:%lukB" -#ifdef CONFIG_SHADOW_CALL_STACK - " shadow_call_stack:%lukB" -#endif " pagetables:%lukB" " bounce:%lukB" " free_pcp:%lukB" @@ -5473,10 +5477,6 @@ void show_free_areas(unsigned int filter, nodemask_t *nodemask) K(zone->present_pages), K(zone_managed_pages(zone)), K(zone_page_state(zone, NR_MLOCK)), - zone_page_state(zone, NR_KERNEL_STACK_KB), -#ifdef CONFIG_SHADOW_CALL_STACK - zone_page_state(zone, NR_KERNEL_SCS_KB), -#endif K(zone_page_state(zone, NR_PAGETABLE)), K(zone_page_state(zone, NR_BOUNCE)), K(free_pcp), diff --git a/mm/vmstat.c b/mm/vmstat.c index b171a76bfe833..2b866cbab11db 100644 --- a/mm/vmstat.c +++ b/mm/vmstat.c @@ -1140,10 +1140,6 @@ const char * const vmstat_text[] = { "nr_zone_write_pending", "nr_mlock", "nr_page_table_pages", - "nr_kernel_stack", -#if IS_ENABLED(CONFIG_SHADOW_CALL_STACK) - "nr_shadow_call_stack", -#endif "nr_bounce", #if IS_ENABLED(CONFIG_ZSMALLOC) "nr_zspages", @@ -1194,6 +1190,10 @@ const char * const vmstat_text[] = { "nr_kernel_misc_reclaimable", "nr_foll_pin_acquired", "nr_foll_pin_released", + "nr_kernel_stack", +#if IS_ENABLED(CONFIG_SHADOW_CALL_STACK) + "nr_shadow_call_stack", +#endif /* enum writeback_stat_item counters */ "nr_dirty_threshold", -- GitLab From 849504809f86ef43b0b12617c0a71b6c6e61cd78 Mon Sep 17 00:00:00 2001 From: Roman Gushchin Date: Thu, 6 Aug 2020 23:21:40 -0700 Subject: [PATCH 1391/1476] mm: memcg/slab: remove unused argument by charge_slab_page() charge_slab_page() is not using the gfp argument anymore, remove it. Signed-off-by: Roman Gushchin Signed-off-by: Andrew Morton Reviewed-by: Shakeel Butt Acked-by: Vlastimil Babka Cc: Christoph Lameter Cc: Pekka Enberg Cc: David Rientjes Cc: Joonsoo Kim Cc: Johannes Weiner Cc: Michal Hocko Link: http://lkml.kernel.org/r/20200707173612.124425-1-guro@fb.com Signed-off-by: Linus Torvalds --- mm/slab.c | 2 +- mm/slab.h | 3 +-- mm/slub.c | 2 +- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/mm/slab.c b/mm/slab.c index 684ebe5b0c7a3..92e6f1228526f 100644 --- a/mm/slab.c +++ b/mm/slab.c @@ -1379,7 +1379,7 @@ static struct page *kmem_getpages(struct kmem_cache *cachep, gfp_t flags, return NULL; } - charge_slab_page(page, flags, cachep->gfporder, cachep); + charge_slab_page(page, cachep->gfporder, cachep); __SetPageSlab(page); /* Record if ALLOC_NO_WATERMARKS was set when allocating the slab */ if (sk_memalloc_socks() && page_is_pfmemalloc(page)) diff --git a/mm/slab.h b/mm/slab.h index ec8e22ee65440..853a4bd0ede57 100644 --- a/mm/slab.h +++ b/mm/slab.h @@ -423,8 +423,7 @@ static inline struct kmem_cache *virt_to_cache(const void *obj) return page->slab_cache; } -static __always_inline void charge_slab_page(struct page *page, - gfp_t gfp, int order, +static __always_inline void charge_slab_page(struct page *page, int order, struct kmem_cache *s) { mod_node_page_state(page_pgdat(page), cache_vmstat_idx(s), diff --git a/mm/slub.c b/mm/slub.c index eba8f57d5734b..9440c68ffc059 100644 --- a/mm/slub.c +++ b/mm/slub.c @@ -1621,7 +1621,7 @@ static inline struct page *alloc_slab_page(struct kmem_cache *s, page = __alloc_pages_node(node, flags, order); if (page) - charge_slab_page(page, flags, order, s); + charge_slab_page(page, order, s); return page; } -- GitLab From 74d555bed5d0f8a1264f1e083183f5409b1aa36a Mon Sep 17 00:00:00 2001 From: Roman Gushchin Date: Thu, 6 Aug 2020 23:21:44 -0700 Subject: [PATCH 1392/1476] mm: slab: rename (un)charge_slab_page() to (un)account_slab_page() charge_slab_page() and uncharge_slab_page() are not related anymore to memcg charging and uncharging. In order to make their names less confusing, let's rename them to account_slab_page() and unaccount_slab_page() respectively. Signed-off-by: Roman Gushchin Signed-off-by: Andrew Morton Reviewed-by: Shakeel Butt Acked-by: Vlastimil Babka Cc: Christoph Lameter Cc: David Rientjes Cc: Johannes Weiner Cc: Joonsoo Kim Cc: Michal Hocko Cc: Pekka Enberg Link: http://lkml.kernel.org/r/20200707173612.124425-2-guro@fb.com Signed-off-by: Linus Torvalds --- mm/slab.c | 4 ++-- mm/slab.h | 8 ++++---- mm/slub.c | 4 ++-- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/mm/slab.c b/mm/slab.c index 92e6f1228526f..3160dff6fd767 100644 --- a/mm/slab.c +++ b/mm/slab.c @@ -1379,7 +1379,7 @@ static struct page *kmem_getpages(struct kmem_cache *cachep, gfp_t flags, return NULL; } - charge_slab_page(page, cachep->gfporder, cachep); + account_slab_page(page, cachep->gfporder, cachep); __SetPageSlab(page); /* Record if ALLOC_NO_WATERMARKS was set when allocating the slab */ if (sk_memalloc_socks() && page_is_pfmemalloc(page)) @@ -1403,7 +1403,7 @@ static void kmem_freepages(struct kmem_cache *cachep, struct page *page) if (current->reclaim_state) current->reclaim_state->reclaimed_slab += 1 << order; - uncharge_slab_page(page, order, cachep); + unaccount_slab_page(page, order, cachep); __free_pages(page, order); } diff --git a/mm/slab.h b/mm/slab.h index 853a4bd0ede57..6cc323f1313af 100644 --- a/mm/slab.h +++ b/mm/slab.h @@ -423,15 +423,15 @@ static inline struct kmem_cache *virt_to_cache(const void *obj) return page->slab_cache; } -static __always_inline void charge_slab_page(struct page *page, int order, - struct kmem_cache *s) +static __always_inline void account_slab_page(struct page *page, int order, + struct kmem_cache *s) { mod_node_page_state(page_pgdat(page), cache_vmstat_idx(s), PAGE_SIZE << order); } -static __always_inline void uncharge_slab_page(struct page *page, int order, - struct kmem_cache *s) +static __always_inline void unaccount_slab_page(struct page *page, int order, + struct kmem_cache *s) { if (memcg_kmem_enabled()) memcg_free_page_obj_cgroups(page); diff --git a/mm/slub.c b/mm/slub.c index 9440c68ffc059..68c02b2eecd90 100644 --- a/mm/slub.c +++ b/mm/slub.c @@ -1621,7 +1621,7 @@ static inline struct page *alloc_slab_page(struct kmem_cache *s, page = __alloc_pages_node(node, flags, order); if (page) - charge_slab_page(page, order, s); + account_slab_page(page, order, s); return page; } @@ -1844,7 +1844,7 @@ static void __free_slab(struct kmem_cache *s, struct page *page) page->mapping = NULL; if (current->reclaim_state) current->reclaim_state->reclaimed_slab += pages; - uncharge_slab_page(page, order, s); + unaccount_slab_page(page, order, s); __free_pages(page, order); } -- GitLab From eda330e57b26df8fabce184736ae3d11e7a104bd Mon Sep 17 00:00:00 2001 From: Roman Gushchin Date: Thu, 6 Aug 2020 23:21:47 -0700 Subject: [PATCH 1393/1476] mm: kmem: switch to static_branch_likely() in memcg_kmem_enabled() Currently memcg_kmem_enabled() is optimized for the kernel memory accounting being off. It was so for a long time, and arguably the reason behind was that the kernel memory accounting was initially an opt-in feature. However, now it's on by default on both cgroup v1 and cgroup v2, and it's on for all cgroups. So let's switch over to static_branch_likely() to reflect this fact. Unlikely there is a significant performance difference, as the cost of a memory allocation and its accounting significantly exceeds the cost of a jump. However, the conversion makes the code look more logically. Signed-off-by: Roman Gushchin Signed-off-by: Andrew Morton Reviewed-by: Shakeel Butt Acked-by: Vlastimil Babka Cc: Johannes Weiner Cc: Michal Hocko Cc: Christoph Lameter Cc: David Rientjes Cc: Joonsoo Kim Cc: Pekka Enberg Link: http://lkml.kernel.org/r/20200707173612.124425-3-guro@fb.com Signed-off-by: Linus Torvalds --- include/linux/memcontrol.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/linux/memcontrol.h b/include/linux/memcontrol.h index 624400c27eba6..3f8ff6519c9d7 100644 --- a/include/linux/memcontrol.h +++ b/include/linux/memcontrol.h @@ -1448,7 +1448,7 @@ void memcg_put_cache_ids(void); static inline bool memcg_kmem_enabled(void) { - return static_branch_unlikely(&memcg_kmem_enabled_key); + return static_branch_likely(&memcg_kmem_enabled_key); } static inline bool memcg_kmem_bypass(void) -- GitLab From 536d3bf261a2fc3b05b3e91e7eef7383443015cf Mon Sep 17 00:00:00 2001 From: Roman Gushchin Date: Thu, 6 Aug 2020 23:21:51 -0700 Subject: [PATCH 1394/1476] mm: memcontrol: avoid workload stalls when lowering memory.high Memory.high limit is implemented in a way such that the kernel penalizes all threads which are allocating a memory over the limit. Forcing all threads into the synchronous reclaim and adding some artificial delays allows to slow down the memory consumption and potentially give some time for userspace oom handlers/resource control agents to react. It works nicely if the memory usage is hitting the limit from below, however it works sub-optimal if a user adjusts memory.high to a value way below the current memory usage. It basically forces all workload threads (doing any memory allocations) into the synchronous reclaim and sleep. This makes the workload completely unresponsive for a long period of time and can also lead to a system-wide contention on lru locks. It can happen even if the workload is not actually tight on memory and has, for example, a ton of cold pagecache. In the current implementation writing to memory.high causes an atomic update of page counter's high value followed by an attempt to reclaim enough memory to fit into the new limit. To fix the problem described above, all we need is to change the order of execution: try to push the memory usage under the limit first, and only then set the new high limit. Reported-by: Domas Mituzas Signed-off-by: Roman Gushchin Signed-off-by: Andrew Morton Reviewed-by: Shakeel Butt Acked-by: Michal Hocko Cc: Johannes Weiner Cc: Tejun Heo Cc: Chris Down Link: http://lkml.kernel.org/r/20200709194718.189231-1-guro@fb.com Signed-off-by: Linus Torvalds --- mm/memcontrol.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mm/memcontrol.c b/mm/memcontrol.c index a3e9633667694..bb0d3ac80ade8 100644 --- a/mm/memcontrol.c +++ b/mm/memcontrol.c @@ -6213,8 +6213,6 @@ static ssize_t memory_high_write(struct kernfs_open_file *of, if (err) return err; - page_counter_set_high(&memcg->memory, high); - for (;;) { unsigned long nr_pages = page_counter_read(&memcg->memory); unsigned long reclaimed; @@ -6238,6 +6236,8 @@ static ssize_t memory_high_write(struct kernfs_open_file *of, break; } + page_counter_set_high(&memcg->memory, high); + return nbytes; } -- GitLab From b3ff92916af3b458712110bb83976a23471c12fa Mon Sep 17 00:00:00 2001 From: Chris Down Date: Thu, 6 Aug 2020 23:21:54 -0700 Subject: [PATCH 1395/1476] mm, memcg: reclaim more aggressively before high allocator throttling Patch series "mm, memcg: reclaim harder before high throttling", v2. This patch (of 2): In Facebook production, we've seen cases where cgroups have been put into allocator throttling even when they appear to have a lot of slack file caches which should be trivially reclaimable. Looking more closely, the problem is that we only try a single cgroup reclaim walk for each return to usermode before calculating whether or not we should throttle. This single attempt doesn't produce enough pressure to shrink for cgroups with a rapidly growing amount of file caches prior to entering allocator throttling. As an example, we see that threads in an affected cgroup are stuck in allocator throttling: # for i in $(cat cgroup.threads); do > grep over_high "/proc/$i/stack" > done [<0>] mem_cgroup_handle_over_high+0x10b/0x150 [<0>] mem_cgroup_handle_over_high+0x10b/0x150 [<0>] mem_cgroup_handle_over_high+0x10b/0x150 ...however, there is no I/O pressure reported by PSI, despite a lot of slack file pages: # cat memory.pressure some avg10=78.50 avg60=84.99 avg300=84.53 total=5702440903 full avg10=78.50 avg60=84.99 avg300=84.53 total=5702116959 # cat io.pressure some avg10=0.00 avg60=0.00 avg300=0.00 total=78051391 full avg10=0.00 avg60=0.00 avg300=0.00 total=78049640 # grep _file memory.stat inactive_file 1370939392 active_file 661635072 This patch changes the behaviour to retry reclaim either until the current task goes below the 10ms grace period, or we are making no reclaim progress at all. In the latter case, we enter reclaim throttling as before. To a user, there's no intuitive reason for the reclaim behaviour to differ from hitting memory.high as part of a new allocation, as opposed to hitting memory.high because someone lowered its value. As such this also brings an added benefit: it unifies the reclaim behaviour between the two. There's precedent for this behaviour: we already do reclaim retries when writing to memory.{high,max}, in max reclaim, and in the page allocator itself. Signed-off-by: Chris Down Signed-off-by: Andrew Morton Reviewed-by: Shakeel Butt Acked-by: Johannes Weiner Cc: Tejun Heo Cc: Michal Hocko Cc: Roman Gushchin Link: http://lkml.kernel.org/r/cover.1594640214.git.chris@chrisdown.name Link: http://lkml.kernel.org/r/a4e23b59e9ef499b575ae73a8120ee089b7d3373.1594640214.git.chris@chrisdown.name Signed-off-by: Linus Torvalds --- mm/memcontrol.c | 42 +++++++++++++++++++++++++++++++++++++----- 1 file changed, 37 insertions(+), 5 deletions(-) diff --git a/mm/memcontrol.c b/mm/memcontrol.c index bb0d3ac80ade8..9b7a7d0592e54 100644 --- a/mm/memcontrol.c +++ b/mm/memcontrol.c @@ -73,6 +73,7 @@ EXPORT_SYMBOL(memory_cgrp_subsys); struct mem_cgroup *root_mem_cgroup __read_mostly; +/* The number of times we should retry reclaim failures before giving up. */ #define MEM_CGROUP_RECLAIM_RETRIES 5 /* Socket memory accounting disabled? */ @@ -2363,18 +2364,23 @@ static int memcg_hotplug_cpu_dead(unsigned int cpu) return 0; } -static void reclaim_high(struct mem_cgroup *memcg, - unsigned int nr_pages, - gfp_t gfp_mask) +static unsigned long reclaim_high(struct mem_cgroup *memcg, + unsigned int nr_pages, + gfp_t gfp_mask) { + unsigned long nr_reclaimed = 0; + do { if (page_counter_read(&memcg->memory) <= READ_ONCE(memcg->memory.high)) continue; memcg_memory_event(memcg, MEMCG_HIGH); - try_to_free_mem_cgroup_pages(memcg, nr_pages, gfp_mask, true); + nr_reclaimed += try_to_free_mem_cgroup_pages(memcg, nr_pages, + gfp_mask, true); } while ((memcg = parent_mem_cgroup(memcg)) && !mem_cgroup_is_root(memcg)); + + return nr_reclaimed; } static void high_work_func(struct work_struct *work) @@ -2530,16 +2536,32 @@ void mem_cgroup_handle_over_high(void) { unsigned long penalty_jiffies; unsigned long pflags; + unsigned long nr_reclaimed; unsigned int nr_pages = current->memcg_nr_pages_over_high; + int nr_retries = MEM_CGROUP_RECLAIM_RETRIES; struct mem_cgroup *memcg; + bool in_retry = false; if (likely(!nr_pages)) return; memcg = get_mem_cgroup_from_mm(current->mm); - reclaim_high(memcg, nr_pages, GFP_KERNEL); current->memcg_nr_pages_over_high = 0; +retry_reclaim: + /* + * The allocating task should reclaim at least the batch size, but for + * subsequent retries we only want to do what's necessary to prevent oom + * or breaching resource isolation. + * + * This is distinct from memory.max or page allocator behaviour because + * memory.high is currently batched, whereas memory.max and the page + * allocator run every time an allocation is made. + */ + nr_reclaimed = reclaim_high(memcg, + in_retry ? SWAP_CLUSTER_MAX : nr_pages, + GFP_KERNEL); + /* * memory.high is breached and reclaim is unable to keep up. Throttle * allocators proactively to slow down excessive growth. @@ -2566,6 +2588,16 @@ void mem_cgroup_handle_over_high(void) if (penalty_jiffies <= HZ / 100) goto out; + /* + * If reclaim is making forward progress but we're still over + * memory.high, we want to encourage that rather than doing allocator + * throttling. + */ + if (nr_reclaimed || nr_retries--) { + in_retry = true; + goto retry_reclaim; + } + /* * If we exit early, we're guaranteed to die (since * schedule_timeout_killable sets TASK_KILLABLE). This means we don't -- GitLab From d977aa939fcaff9000f1ba2cd2d516658fdd1ba8 Mon Sep 17 00:00:00 2001 From: Chris Down Date: Thu, 6 Aug 2020 23:21:58 -0700 Subject: [PATCH 1396/1476] mm, memcg: unify reclaim retry limits with page allocator Reclaim retries have been set to 5 since the beginning of time in commit 66e1707bc346 ("Memory controller: add per cgroup LRU and reclaim"). However, we now have a generally agreed-upon standard for page reclaim: MAX_RECLAIM_RETRIES (currently 16), added many years later in commit 0a0337e0d1d1 ("mm, oom: rework oom detection"). In the absence of a compelling reason to declare an OOM earlier in memcg context than page allocator context, it seems reasonable to supplant MEM_CGROUP_RECLAIM_RETRIES with MAX_RECLAIM_RETRIES, making the page allocator and memcg internals more similar in semantics when reclaim fails to produce results, avoiding premature OOMs or throttling. Signed-off-by: Chris Down Signed-off-by: Andrew Morton Reviewed-by: Shakeel Butt Acked-by: Michal Hocko Cc: Johannes Weiner Cc: Tejun Heo Cc: Roman Gushchin Link: http://lkml.kernel.org/r/da557856c9c7654308eaff4eedc1952a95e8df5f.1594640214.git.chris@chrisdown.name Signed-off-by: Linus Torvalds --- mm/memcontrol.c | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/mm/memcontrol.c b/mm/memcontrol.c index 9b7a7d0592e54..b4cb1bb61d46b 100644 --- a/mm/memcontrol.c +++ b/mm/memcontrol.c @@ -73,9 +73,6 @@ EXPORT_SYMBOL(memory_cgrp_subsys); struct mem_cgroup *root_mem_cgroup __read_mostly; -/* The number of times we should retry reclaim failures before giving up. */ -#define MEM_CGROUP_RECLAIM_RETRIES 5 - /* Socket memory accounting disabled? */ static bool cgroup_memory_nosocket; @@ -2538,7 +2535,7 @@ void mem_cgroup_handle_over_high(void) unsigned long pflags; unsigned long nr_reclaimed; unsigned int nr_pages = current->memcg_nr_pages_over_high; - int nr_retries = MEM_CGROUP_RECLAIM_RETRIES; + int nr_retries = MAX_RECLAIM_RETRIES; struct mem_cgroup *memcg; bool in_retry = false; @@ -2615,7 +2612,7 @@ static int try_charge(struct mem_cgroup *memcg, gfp_t gfp_mask, unsigned int nr_pages) { unsigned int batch = max(MEMCG_CHARGE_BATCH, nr_pages); - int nr_retries = MEM_CGROUP_RECLAIM_RETRIES; + int nr_retries = MAX_RECLAIM_RETRIES; struct mem_cgroup *mem_over_limit; struct page_counter *counter; unsigned long nr_reclaimed; @@ -2734,7 +2731,7 @@ retry: get_order(nr_pages * PAGE_SIZE)); switch (oom_status) { case OOM_SUCCESS: - nr_retries = MEM_CGROUP_RECLAIM_RETRIES; + nr_retries = MAX_RECLAIM_RETRIES; goto retry; case OOM_FAILED: goto force; @@ -3414,7 +3411,7 @@ static inline bool memcg_has_children(struct mem_cgroup *memcg) */ static int mem_cgroup_force_empty(struct mem_cgroup *memcg) { - int nr_retries = MEM_CGROUP_RECLAIM_RETRIES; + int nr_retries = MAX_RECLAIM_RETRIES; /* we call try-to-free pages for make this cgroup empty */ lru_add_drain_all(); @@ -6235,7 +6232,7 @@ static ssize_t memory_high_write(struct kernfs_open_file *of, char *buf, size_t nbytes, loff_t off) { struct mem_cgroup *memcg = mem_cgroup_from_css(of_css(of)); - unsigned int nr_retries = MEM_CGROUP_RECLAIM_RETRIES; + unsigned int nr_retries = MAX_RECLAIM_RETRIES; bool drained = false; unsigned long high; int err; @@ -6283,7 +6280,7 @@ static ssize_t memory_max_write(struct kernfs_open_file *of, char *buf, size_t nbytes, loff_t off) { struct mem_cgroup *memcg = mem_cgroup_from_css(of_css(of)); - unsigned int nr_reclaims = MEM_CGROUP_RECLAIM_RETRIES; + unsigned int nr_reclaims = MAX_RECLAIM_RETRIES; bool drained = false; unsigned long max; int err; -- GitLab From 22f7496f0b901249f23c5251eb8a10aae126b909 Mon Sep 17 00:00:00 2001 From: Yafang Shao Date: Thu, 6 Aug 2020 23:22:01 -0700 Subject: [PATCH 1397/1476] mm, memcg: avoid stale protection values when cgroup is above protection Patch series "mm, memcg: memory.{low,min} reclaim fix & cleanup", v4. This series contains a fix for a edge case in my earlier protection calculation patches, and a patch to make the area overall a little more robust to hopefully help avoid this in future. This patch (of 2): A cgroup can have both memory protection and a memory limit to isolate it from its siblings in both directions - for example, to prevent it from being shrunk below 2G under high pressure from outside, but also from growing beyond 4G under low pressure. Commit 9783aa9917f8 ("mm, memcg: proportional memory.{low,min} reclaim") implemented proportional scan pressure so that multiple siblings in excess of their protection settings don't get reclaimed equally but instead in accordance to their unprotected portion. During limit reclaim, this proportionality shouldn't apply of course: there is no competition, all pressure is from within the cgroup and should be applied as such. Reclaim should operate at full efficiency. However, mem_cgroup_protected() never expected anybody to look at the effective protection values when it indicated that the cgroup is above its protection. As a result, a query during limit reclaim may return stale protection values that were calculated by a previous reclaim cycle in which the cgroup did have siblings. When this happens, reclaim is unnecessarily hesitant and potentially slow to meet the desired limit. In theory this could lead to premature OOM kills, although it's not obvious this has occurred in practice. Workaround the problem by special casing reclaim roots in mem_cgroup_protection. These memcgs are never participating in the reclaim protection because the reclaim is internal. We have to ignore effective protection values for reclaim roots because mem_cgroup_protected might be called from racing reclaim contexts with different roots. Calculation is relying on root -> leaf tree traversal therefore top-down reclaim protection invariants should hold. The only exception is the reclaim root which should have effective protection set to 0 but that would be problematic for the following setup: Let's have global and A's reclaim in parallel: | A (low=2G, usage = 3G, max = 3G, children_low_usage = 1.5G) |\ | C (low = 1G, usage = 2.5G) B (low = 1G, usage = 0.5G) for A reclaim we have B.elow = B.low C.elow = C.low For the global reclaim A.elow = A.low B.elow = min(B.usage, B.low) because children_low_usage <= A.elow C.elow = min(C.usage, C.low) With the effective values resetting we have A reclaim A.elow = 0 B.elow = B.low C.elow = C.low and global reclaim could see the above and then B.elow = C.elow = 0 because children_low_usage > A.elow Which means that protected memcgs would get reclaimed. In future we would like to make mem_cgroup_protected more robust against racing reclaim contexts but that is likely more complex solution than this simple workaround. [hannes@cmpxchg.org - large part of the changelog] [mhocko@suse.com - workaround explanation] [chris@chrisdown.name - retitle] Fixes: 9783aa9917f8 ("mm, memcg: proportional memory.{low,min} reclaim") Signed-off-by: Yafang Shao Signed-off-by: Chris Down Signed-off-by: Andrew Morton Acked-by: Michal Hocko Acked-by: Johannes Weiner Acked-by: Chris Down Acked-by: Roman Gushchin Link: http://lkml.kernel.org/r/cover.1594638158.git.chris@chrisdown.name Link: http://lkml.kernel.org/r/044fb8ecffd001c7905d27c0c2ad998069fdc396.1594638158.git.chris@chrisdown.name Signed-off-by: Linus Torvalds --- include/linux/memcontrol.h | 42 ++++++++++++++++++++++++++++++++++++-- mm/memcontrol.c | 8 ++++++++ mm/vmscan.c | 3 ++- 3 files changed, 50 insertions(+), 3 deletions(-) diff --git a/include/linux/memcontrol.h b/include/linux/memcontrol.h index 3f8ff6519c9d7..0301b231fd023 100644 --- a/include/linux/memcontrol.h +++ b/include/linux/memcontrol.h @@ -355,12 +355,49 @@ static inline bool mem_cgroup_disabled(void) return !cgroup_subsys_enabled(memory_cgrp_subsys); } -static inline unsigned long mem_cgroup_protection(struct mem_cgroup *memcg, +static inline unsigned long mem_cgroup_protection(struct mem_cgroup *root, + struct mem_cgroup *memcg, bool in_low_reclaim) { if (mem_cgroup_disabled()) return 0; + /* + * There is no reclaim protection applied to a targeted reclaim. + * We are special casing this specific case here because + * mem_cgroup_protected calculation is not robust enough to keep + * the protection invariant for calculated effective values for + * parallel reclaimers with different reclaim target. This is + * especially a problem for tail memcgs (as they have pages on LRU) + * which would want to have effective values 0 for targeted reclaim + * but a different value for external reclaim. + * + * Example + * Let's have global and A's reclaim in parallel: + * | + * A (low=2G, usage = 3G, max = 3G, children_low_usage = 1.5G) + * |\ + * | C (low = 1G, usage = 2.5G) + * B (low = 1G, usage = 0.5G) + * + * For the global reclaim + * A.elow = A.low + * B.elow = min(B.usage, B.low) because children_low_usage <= A.elow + * C.elow = min(C.usage, C.low) + * + * With the effective values resetting we have A reclaim + * A.elow = 0 + * B.elow = B.low + * C.elow = C.low + * + * If the global reclaim races with A's reclaim then + * B.elow = C.elow = 0 because children_low_usage > A.elow) + * is possible and reclaiming B would be violating the protection. + * + */ + if (root == memcg) + return 0; + if (in_low_reclaim) return READ_ONCE(memcg->memory.emin); @@ -891,7 +928,8 @@ static inline void memcg_memory_event_mm(struct mm_struct *mm, { } -static inline unsigned long mem_cgroup_protection(struct mem_cgroup *memcg, +static inline unsigned long mem_cgroup_protection(struct mem_cgroup *root, + struct mem_cgroup *memcg, bool in_low_reclaim) { return 0; diff --git a/mm/memcontrol.c b/mm/memcontrol.c index b4cb1bb61d46b..c610617bb19a6 100644 --- a/mm/memcontrol.c +++ b/mm/memcontrol.c @@ -6605,6 +6605,14 @@ enum mem_cgroup_protection mem_cgroup_protected(struct mem_cgroup *root, if (!root) root = root_mem_cgroup; + + /* + * Effective values of the reclaim targets are ignored so they + * can be stale. Have a look at mem_cgroup_protection for more + * details. + * TODO: calculation should be more robust so that we do not need + * that special casing. + */ if (memcg == root) return MEMCG_PROT_NONE; diff --git a/mm/vmscan.c b/mm/vmscan.c index 2ac43664aba4b..9f0811d242551 100644 --- a/mm/vmscan.c +++ b/mm/vmscan.c @@ -2331,7 +2331,8 @@ out: unsigned long protection; lruvec_size = lruvec_lru_size(lruvec, lru, sc->reclaim_idx); - protection = mem_cgroup_protection(memcg, + protection = mem_cgroup_protection(sc->target_mem_cgroup, + memcg, sc->memcg_low_reclaim); if (protection) { -- GitLab From 45c7f7e1ef17f09fe70bad4b705ce43772153fd7 Mon Sep 17 00:00:00 2001 From: Chris Down Date: Thu, 6 Aug 2020 23:22:05 -0700 Subject: [PATCH 1398/1476] mm, memcg: decouple e{low,min} state mutations from protection checks mem_cgroup_protected currently is both used to set effective low and min and return a mem_cgroup_protection based on the result. As a user, this can be a little unexpected: it appears to be a simple predicate function, if not for the big warning in the comment above about the order in which it must be executed. This change makes it so that we separate the state mutations from the actual protection checks, which makes it more obvious where we need to be careful mutating internal state, and where we are simply checking and don't need to worry about that. [mhocko@suse.com - don't check protection on root memcgs] Suggested-by: Johannes Weiner Signed-off-by: Chris Down Signed-off-by: Andrew Morton Acked-by: Johannes Weiner Acked-by: Michal Hocko Cc: Roman Gushchin Cc: Yafang Shao Link: http://lkml.kernel.org/r/ff3f915097fcee9f6d7041c084ef92d16aaeb56a.1594638158.git.chris@chrisdown.name Signed-off-by: Linus Torvalds --- include/linux/memcontrol.h | 53 ++++++++++++++++++++++++++++++-------- mm/memcontrol.c | 28 +++++--------------- mm/vmscan.c | 17 +++--------- 3 files changed, 53 insertions(+), 45 deletions(-) diff --git a/include/linux/memcontrol.h b/include/linux/memcontrol.h index 0301b231fd023..1bb49b600310c 100644 --- a/include/linux/memcontrol.h +++ b/include/linux/memcontrol.h @@ -47,12 +47,6 @@ enum memcg_memory_event { MEMCG_NR_MEMORY_EVENTS, }; -enum mem_cgroup_protection { - MEMCG_PROT_NONE, - MEMCG_PROT_LOW, - MEMCG_PROT_MIN, -}; - struct mem_cgroup_reclaim_cookie { pg_data_t *pgdat; unsigned int generation; @@ -405,8 +399,36 @@ static inline unsigned long mem_cgroup_protection(struct mem_cgroup *root, READ_ONCE(memcg->memory.elow)); } -enum mem_cgroup_protection mem_cgroup_protected(struct mem_cgroup *root, - struct mem_cgroup *memcg); +void mem_cgroup_calculate_protection(struct mem_cgroup *root, + struct mem_cgroup *memcg); + +static inline bool mem_cgroup_supports_protection(struct mem_cgroup *memcg) +{ + /* + * The root memcg doesn't account charges, and doesn't support + * protection. + */ + return !mem_cgroup_disabled() && !mem_cgroup_is_root(memcg); + +} + +static inline bool mem_cgroup_below_low(struct mem_cgroup *memcg) +{ + if (!mem_cgroup_supports_protection(memcg)) + return false; + + return READ_ONCE(memcg->memory.elow) >= + page_counter_read(&memcg->memory); +} + +static inline bool mem_cgroup_below_min(struct mem_cgroup *memcg) +{ + if (!mem_cgroup_supports_protection(memcg)) + return false; + + return READ_ONCE(memcg->memory.emin) >= + page_counter_read(&memcg->memory); +} int mem_cgroup_charge(struct page *page, struct mm_struct *mm, gfp_t gfp_mask); @@ -935,10 +957,19 @@ static inline unsigned long mem_cgroup_protection(struct mem_cgroup *root, return 0; } -static inline enum mem_cgroup_protection mem_cgroup_protected( - struct mem_cgroup *root, struct mem_cgroup *memcg) +static inline void mem_cgroup_calculate_protection(struct mem_cgroup *root, + struct mem_cgroup *memcg) { - return MEMCG_PROT_NONE; +} + +static inline bool mem_cgroup_below_low(struct mem_cgroup *memcg) +{ + return false; +} + +static inline bool mem_cgroup_below_min(struct mem_cgroup *memcg) +{ + return false; } static inline int mem_cgroup_charge(struct page *page, struct mm_struct *mm, diff --git a/mm/memcontrol.c b/mm/memcontrol.c index c610617bb19a6..b30a52db6b2d3 100644 --- a/mm/memcontrol.c +++ b/mm/memcontrol.c @@ -6587,21 +6587,15 @@ static unsigned long effective_protection(unsigned long usage, * * WARNING: This function is not stateless! It can only be used as part * of a top-down tree iteration, not for isolated queries. - * - * Returns one of the following: - * MEMCG_PROT_NONE: cgroup memory is not protected - * MEMCG_PROT_LOW: cgroup memory is protected as long there is - * an unprotected supply of reclaimable memory from other cgroups. - * MEMCG_PROT_MIN: cgroup memory is protected */ -enum mem_cgroup_protection mem_cgroup_protected(struct mem_cgroup *root, - struct mem_cgroup *memcg) +void mem_cgroup_calculate_protection(struct mem_cgroup *root, + struct mem_cgroup *memcg) { unsigned long usage, parent_usage; struct mem_cgroup *parent; if (mem_cgroup_disabled()) - return MEMCG_PROT_NONE; + return; if (!root) root = root_mem_cgroup; @@ -6614,21 +6608,21 @@ enum mem_cgroup_protection mem_cgroup_protected(struct mem_cgroup *root, * that special casing. */ if (memcg == root) - return MEMCG_PROT_NONE; + return; usage = page_counter_read(&memcg->memory); if (!usage) - return MEMCG_PROT_NONE; + return; parent = parent_mem_cgroup(memcg); /* No parent means a non-hierarchical mode on v1 memcg */ if (!parent) - return MEMCG_PROT_NONE; + return; if (parent == root) { memcg->memory.emin = READ_ONCE(memcg->memory.min); memcg->memory.elow = READ_ONCE(memcg->memory.low); - goto out; + return; } parent_usage = page_counter_read(&parent->memory); @@ -6642,14 +6636,6 @@ enum mem_cgroup_protection mem_cgroup_protected(struct mem_cgroup *root, READ_ONCE(memcg->memory.low), READ_ONCE(parent->memory.elow), atomic_long_read(&parent->memory.children_low_usage))); - -out: - if (usage <= memcg->memory.emin) - return MEMCG_PROT_MIN; - else if (usage <= memcg->memory.elow) - return MEMCG_PROT_LOW; - else - return MEMCG_PROT_NONE; } /** diff --git a/mm/vmscan.c b/mm/vmscan.c index 9f0811d242551..5747867f0082f 100644 --- a/mm/vmscan.c +++ b/mm/vmscan.c @@ -2620,14 +2620,15 @@ static void shrink_node_memcgs(pg_data_t *pgdat, struct scan_control *sc) unsigned long reclaimed; unsigned long scanned; - switch (mem_cgroup_protected(target_memcg, memcg)) { - case MEMCG_PROT_MIN: + mem_cgroup_calculate_protection(target_memcg, memcg); + + if (mem_cgroup_below_min(memcg)) { /* * Hard protection. * If there is no reclaimable memory, OOM. */ continue; - case MEMCG_PROT_LOW: + } else if (mem_cgroup_below_low(memcg)) { /* * Soft protection. * Respect the protection only as long as @@ -2639,16 +2640,6 @@ static void shrink_node_memcgs(pg_data_t *pgdat, struct scan_control *sc) continue; } memcg_memory_event(memcg, MEMCG_LOW); - break; - case MEMCG_PROT_NONE: - /* - * All protection thresholds breached. We may - * still choose to vary the scan pressure - * applied based on by how much the cgroup in - * question has exceeded its protection - * thresholds (see get_scan_count). - */ - break; } reclaimed = sc->nr_reclaimed; -- GitLab From 1378b37d03e8147c67fde60caf0474ea879163d8 Mon Sep 17 00:00:00 2001 From: Yafang Shao Date: Thu, 6 Aug 2020 23:22:08 -0700 Subject: [PATCH 1399/1476] memcg, oom: check memcg margin for parallel oom Memcg oom killer invocation is synchronized by the global oom_lock and tasks are sleeping on the lock while somebody is selecting the victim or potentially race with the oom_reaper is releasing the victim's memory. This can result in a pointless oom killer invocation because a waiter might be racing with the oom_reaper P1 oom_reaper P2 oom_reap_task mutex_lock(oom_lock) out_of_memory # no victim because we have one already __oom_reap_task_mm mute_unlock(oom_lock) mutex_lock(oom_lock) set MMF_OOM_SKIP select_bad_process # finds a new victim The page allocator prevents from this race by trying to allocate after the lock can be acquired (in __alloc_pages_may_oom) which acts as a last minute check. Moreover page allocator simply doesn't block on the oom_lock and simply retries the whole reclaim process. Memcg oom killer should do the last minute check as well. Call mem_cgroup_margin to do that. Trylock on the oom_lock could be done as well but this doesn't seem to be necessary at this stage. [mhocko@kernel.org: commit log] Suggested-by: Michal Hocko Signed-off-by: Yafang Shao Signed-off-by: Andrew Morton Acked-by: Michal Hocko Acked-by: Chris Down Cc: Tetsuo Handa Cc: David Rientjes Cc: Johannes Weiner Link: http://lkml.kernel.org/r/1594735034-19190-1-git-send-email-laoar.shao@gmail.com Signed-off-by: Linus Torvalds --- mm/memcontrol.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/mm/memcontrol.c b/mm/memcontrol.c index b30a52db6b2d3..369d9e1d83330 100644 --- a/mm/memcontrol.c +++ b/mm/memcontrol.c @@ -1663,15 +1663,21 @@ static bool mem_cgroup_out_of_memory(struct mem_cgroup *memcg, gfp_t gfp_mask, .gfp_mask = gfp_mask, .order = order, }; - bool ret; + bool ret = true; if (mutex_lock_killable(&oom_lock)) return true; + + if (mem_cgroup_margin(memcg) >= (1 << order)) + goto unlock; + /* * A few threads which were not waiting at mutex_lock_killable() can * fail to bail out. Therefore, check again after holding oom_lock. */ ret = should_force_charge() || out_of_memory(&oc); + +unlock: mutex_unlock(&oom_lock); return ret; } -- GitLab From 19ce33acbbd03c371f2d4b533879c4755686f95f Mon Sep 17 00:00:00 2001 From: Johannes Weiner Date: Thu, 6 Aug 2020 23:22:12 -0700 Subject: [PATCH 1400/1476] mm: memcontrol: restore proper dirty throttling when memory.high changes Commit 8c8c383c04f6 ("mm: memcontrol: try harder to set a new memory.high") inadvertently removed a callback to recalculate the writeback cache size in light of a newly configured memory.high limit. Without letting the writeback cache know about a potentially heavily reduced limit, it may permit too many dirty pages, which can cause unnecessary reclaim latencies or even avoidable OOM situations. This was spotted while reading the code, it hasn't knowingly caused any problems in practice so far. Fixes: 8c8c383c04f6 ("mm: memcontrol: try harder to set a new memory.high") Signed-off-by: Johannes Weiner Signed-off-by: Andrew Morton Reviewed-by: Shakeel Butt Acked-by: Chris Down Acked-by: Michal Hocko Cc: Roman Gushchin Link: http://lkml.kernel.org/r/20200728135210.379885-1-hannes@cmpxchg.org Signed-off-by: Linus Torvalds --- mm/memcontrol.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/mm/memcontrol.c b/mm/memcontrol.c index 369d9e1d83330..856db3ba30938 100644 --- a/mm/memcontrol.c +++ b/mm/memcontrol.c @@ -6273,6 +6273,8 @@ static ssize_t memory_high_write(struct kernfs_open_file *of, page_counter_set_high(&memcg->memory, high); + memcg_wb_domain_size_changed(memcg); + return nbytes; } -- GitLab From e22c6ed90aa91abc08f107344428ebb8c2629e98 Mon Sep 17 00:00:00 2001 From: Johannes Weiner Date: Thu, 6 Aug 2020 23:22:15 -0700 Subject: [PATCH 1401/1476] mm: memcontrol: don't count limit-setting reclaim as memory pressure When an outside process lowers one of the memory limits of a cgroup (or uses the force_empty knob in cgroup1), direct reclaim is performed in the context of the write(), in order to directly enforce the new limit and have it being met by the time the write() returns. Currently, this reclaim activity is accounted as memory pressure in the cgroup that the writer(!) belongs to. This is unexpected. It specifically causes problems for senpai (https://github.com/facebookincubator/senpai), which is an agent that routinely adjusts the memory limits and performs associated reclaim work in tens or even hundreds of cgroups running on the host. The cgroup that senpai is running in itself will report elevated levels of memory pressure, even though it itself is under no memory shortage or any sort of distress. Move the psi annotation from the central cgroup reclaim function to callsites in the allocation context, and thereby no longer count any limit-setting reclaim as memory pressure. If the newly set limit causes the workload inside the cgroup into direct reclaim, that of course will continue to count as memory pressure. Signed-off-by: Johannes Weiner Signed-off-by: Andrew Morton Reviewed-by: Shakeel Butt Reviewed-by: Roman Gushchin Acked-by: Chris Down Acked-by: Michal Hocko Link: http://lkml.kernel.org/r/20200728135210.379885-2-hannes@cmpxchg.org Signed-off-by: Linus Torvalds --- mm/memcontrol.c | 11 ++++++++++- mm/vmscan.c | 6 ------ 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/mm/memcontrol.c b/mm/memcontrol.c index 856db3ba30938..8d9ceea7fe4d0 100644 --- a/mm/memcontrol.c +++ b/mm/memcontrol.c @@ -2374,12 +2374,18 @@ static unsigned long reclaim_high(struct mem_cgroup *memcg, unsigned long nr_reclaimed = 0; do { + unsigned long pflags; + if (page_counter_read(&memcg->memory) <= READ_ONCE(memcg->memory.high)) continue; + memcg_memory_event(memcg, MEMCG_HIGH); + + psi_memstall_enter(&pflags); nr_reclaimed += try_to_free_mem_cgroup_pages(memcg, nr_pages, gfp_mask, true); + psi_memstall_leave(&pflags); } while ((memcg = parent_mem_cgroup(memcg)) && !mem_cgroup_is_root(memcg)); @@ -2621,10 +2627,11 @@ static int try_charge(struct mem_cgroup *memcg, gfp_t gfp_mask, int nr_retries = MAX_RECLAIM_RETRIES; struct mem_cgroup *mem_over_limit; struct page_counter *counter; + enum oom_status oom_status; unsigned long nr_reclaimed; bool may_swap = true; bool drained = false; - enum oom_status oom_status; + unsigned long pflags; if (mem_cgroup_is_root(memcg)) return 0; @@ -2684,8 +2691,10 @@ retry: memcg_memory_event(mem_over_limit, MEMCG_MAX); + psi_memstall_enter(&pflags); nr_reclaimed = try_to_free_mem_cgroup_pages(mem_over_limit, nr_pages, gfp_mask, may_swap); + psi_memstall_leave(&pflags); if (mem_cgroup_margin(mem_over_limit) >= nr_pages) goto retry; diff --git a/mm/vmscan.c b/mm/vmscan.c index 5747867f0082f..23156c252e0a1 100644 --- a/mm/vmscan.c +++ b/mm/vmscan.c @@ -3310,7 +3310,6 @@ unsigned long try_to_free_mem_cgroup_pages(struct mem_cgroup *memcg, bool may_swap) { unsigned long nr_reclaimed; - unsigned long pflags; unsigned int noreclaim_flag; struct scan_control sc = { .nr_to_reclaim = max(nr_pages, SWAP_CLUSTER_MAX), @@ -3331,17 +3330,12 @@ unsigned long try_to_free_mem_cgroup_pages(struct mem_cgroup *memcg, struct zonelist *zonelist = node_zonelist(numa_node_id(), sc.gfp_mask); set_task_reclaim_state(current, &sc.reclaim_state); - trace_mm_vmscan_memcg_reclaim_begin(0, sc.gfp_mask); - - psi_memstall_enter(&pflags); noreclaim_flag = memalloc_noreclaim_save(); nr_reclaimed = do_try_to_free_pages(zonelist, &sc); memalloc_noreclaim_restore(noreclaim_flag); - psi_memstall_leave(&pflags); - trace_mm_vmscan_memcg_reclaim_end(nr_reclaimed); set_task_reclaim_state(current, NULL); -- GitLab From a6f23d14ec7d7d02220ad8bb2774be3322b9aeec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20Koutn=C3=BD?= Date: Thu, 6 Aug 2020 23:22:18 -0700 Subject: [PATCH 1402/1476] mm/page_counter.c: fix protection usage propagation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When workload runs in cgroups that aren't directly below root cgroup and their parent specifies reclaim protection, it may end up ineffective. The reason is that propagate_protected_usage() is not called in all hierarchy up. All the protected usage is incorrectly accumulated in the workload's parent. This means that siblings_low_usage is overestimated and effective protection underestimated. Even though it is transitional phenomenon (uncharge path does correct propagation and fixes the wrong children_low_usage), it can undermine the intended protection unexpectedly. We have noticed this problem while seeing a swap out in a descendant of a protected memcg (intermediate node) while the parent was conveniently under its protection limit and the memory pressure was external to that hierarchy. Michal has pinpointed this down to the wrong siblings_low_usage which led to the unwanted reclaim. The fix is simply updating children_low_usage in respective ancestors also in the charging path. Fixes: 230671533d64 ("mm: memory.low hierarchical behavior") Signed-off-by: Michal Koutný Signed-off-by: Michal Hocko Signed-off-by: Andrew Morton Acked-by: Michal Hocko Acked-by: Roman Gushchin Cc: Johannes Weiner Cc: Tejun Heo Cc: [4.18+] Link: http://lkml.kernel.org/r/20200803153231.15477-1-mhocko@kernel.org Signed-off-by: Linus Torvalds --- mm/page_counter.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/mm/page_counter.c b/mm/page_counter.c index c56db2d5e1592..b4663844c9b37 100644 --- a/mm/page_counter.c +++ b/mm/page_counter.c @@ -72,7 +72,7 @@ void page_counter_charge(struct page_counter *counter, unsigned long nr_pages) long new; new = atomic_long_add_return(nr_pages, &c->usage); - propagate_protected_usage(counter, new); + propagate_protected_usage(c, new); /* * This is indeed racy, but we can live with some * inaccuracy in the watermark. @@ -116,7 +116,7 @@ bool page_counter_try_charge(struct page_counter *counter, new = atomic_long_add_return(nr_pages, &c->usage); if (new > c->max) { atomic_long_sub(nr_pages, &c->usage); - propagate_protected_usage(counter, new); + propagate_protected_usage(c, new); /* * This is racy, but we can live with some * inaccuracy in the failcnt. @@ -125,7 +125,7 @@ bool page_counter_try_charge(struct page_counter *counter, *fail = c; goto failed; } - propagate_protected_usage(counter, new); + propagate_protected_usage(c, new); /* * Just like with failcnt, we can live with some * inaccuracy in the watermark. -- GitLab From 463b7a173d52f0b15473171fe895ab3a3d852a5e Mon Sep 17 00:00:00 2001 From: Ralph Campbell Date: Thu, 6 Aug 2020 23:22:21 -0700 Subject: [PATCH 1403/1476] mm: remove redundant check non_swap_entry() In zap_pte_range(), the check for non_swap_entry() and is_device_private_entry() is unnecessary since the latter is sufficient to determine if the page is a device private page. Remove the test for non_swap_entry() to simplify the code and for clarity. Signed-off-by: Ralph Campbell Signed-off-by: Andrew Morton Reviewed-by: Jason Gunthorpe Acked-by: David Hildenbrand Link: http://lkml.kernel.org/r/20200615175405.4613-1-rcampbell@nvidia.com Signed-off-by: Linus Torvalds --- mm/memory.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mm/memory.c b/mm/memory.c index 0da48f6586f81..f4f27b9d48c5b 100644 --- a/mm/memory.c +++ b/mm/memory.c @@ -1098,7 +1098,7 @@ again: } entry = pte_to_swp_entry(ptent); - if (non_swap_entry(entry) && is_device_private_entry(entry)) { + if (is_device_private_entry(entry)) { struct page *page = device_private_entry_to_page(entry); if (unlikely(details && details->check_mapping)) { -- GitLab From 0c4123e3fb82d6014d0a70b52eb38153f658541c Mon Sep 17 00:00:00 2001 From: Alex Zhang Date: Thu, 6 Aug 2020 23:22:24 -0700 Subject: [PATCH 1404/1476] mm/memory.c: make remap_pfn_range() reject unaligned addr This function implicitly assumes that the addr passed in is page aligned. A non page aligned addr could ultimately cause a kernel bug in remap_pte_range as the exit condition in the logic loop may never be satisfied. This patch documents the need for the requirement, as well as explicitly adds a check for it. Signed-off-by: Alex Zhang Signed-off-by: Andrew Morton Reviewed-by: Andrew Morton Link: http://lkml.kernel.org/r/20200617233512.177519-1-zhangalex@google.com Signed-off-by: Linus Torvalds --- mm/memory.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/mm/memory.c b/mm/memory.c index f4f27b9d48c5b..c39a13b09602c 100644 --- a/mm/memory.c +++ b/mm/memory.c @@ -2082,7 +2082,7 @@ static inline int remap_p4d_range(struct mm_struct *mm, pgd_t *pgd, /** * remap_pfn_range - remap kernel memory to userspace * @vma: user vma to map to - * @addr: target user address to start at + * @addr: target page aligned user address to start at * @pfn: page frame number of kernel physical memory address * @size: size of mapping area * @prot: page protection flags for this mapping @@ -2101,6 +2101,9 @@ int remap_pfn_range(struct vm_area_struct *vma, unsigned long addr, unsigned long remap_pfn = pfn; int err; + if (WARN_ON_ONCE(!PAGE_ALIGNED(addr))) + return -EINVAL; + /* * Physically remapped pages are special. Tell the * rest of the world about it: -- GitLab From ca15ca406f660ad5fab55b851d2b269ce915c88d Mon Sep 17 00:00:00 2001 From: Mike Rapoport Date: Thu, 6 Aug 2020 23:22:28 -0700 Subject: [PATCH 1405/1476] mm: remove unneeded includes of Patch series "mm: cleanup usage of " Most architectures have very similar versions of pXd_alloc_one() and pXd_free_one() for intermediate levels of page table. These patches add generic versions of these functions in and enable use of the generic functions where appropriate. In addition, functions declared and defined in headers are used mostly by core mm and early mm initialization in arch and there is no actual reason to have the included all over the place. The first patch in this series removes unneeded includes of In the end it didn't work out as neatly as I hoped and moving pXd_alloc_track() definitions to would require unnecessary changes to arches that have custom page table allocations, so I've decided to move lib/ioremap.c to mm/ and make pgalloc-track.h local to mm/. This patch (of 8): In most cases header is required only for allocations of page table memory. Most of the .c files that include that header do not use symbols declared in and do not require that header. As for the other header files that used to include , it is possible to move that include into the .c file that actually uses symbols from and drop the include from the header file. The process was somewhat automated using sed -i -E '/[<"]asm\/pgalloc\.h/d' \ $(grep -L -w -f /tmp/xx \ $(git grep -E -l '[<"]asm/pgalloc\.h')) where /tmp/xx contains all the symbols defined in arch/*/include/asm/pgalloc.h. [rppt@linux.ibm.com: fix powerpc warning] Signed-off-by: Mike Rapoport Signed-off-by: Andrew Morton Reviewed-by: Pekka Enberg Acked-by: Geert Uytterhoeven [m68k] Cc: Abdul Haleem Cc: Andy Lutomirski Cc: Arnd Bergmann Cc: Christophe Leroy Cc: Joerg Roedel Cc: Max Filippov Cc: Peter Zijlstra Cc: Satheesh Rajendran Cc: Stafford Horne Cc: Stephen Rothwell Cc: Steven Rostedt Cc: Joerg Roedel Cc: Matthew Wilcox Link: http://lkml.kernel.org/r/20200627143453.31835-1-rppt@kernel.org Link: http://lkml.kernel.org/r/20200627143453.31835-2-rppt@kernel.org Signed-off-by: Linus Torvalds --- arch/alpha/include/asm/tlbflush.h | 1 - arch/alpha/kernel/core_irongate.c | 1 - arch/alpha/kernel/core_marvel.c | 1 - arch/alpha/kernel/core_titan.c | 1 - arch/alpha/kernel/machvec_impl.h | 2 -- arch/alpha/kernel/smp.c | 1 - arch/alpha/mm/numa.c | 1 - arch/arc/mm/fault.c | 1 - arch/arc/mm/init.c | 1 - arch/arm/include/asm/tlb.h | 1 - arch/arm/kernel/machine_kexec.c | 1 - arch/arm/kernel/smp.c | 1 - arch/arm/kernel/suspend.c | 1 - arch/arm/mach-omap2/omap-mpuss-lowpower.c | 1 - arch/arm/mm/hugetlbpage.c | 1 - arch/arm/mm/mmu.c | 1 + arch/arm64/kernel/smp.c | 1 - arch/arm64/mm/hugetlbpage.c | 1 - arch/arm64/mm/ioremap.c | 1 - arch/arm64/mm/mmu.c | 1 + arch/csky/kernel/smp.c | 1 - arch/ia64/include/asm/tlb.h | 1 - arch/ia64/kernel/process.c | 1 - arch/ia64/kernel/smp.c | 1 - arch/ia64/kernel/smpboot.c | 1 - arch/ia64/mm/contig.c | 1 - arch/ia64/mm/discontig.c | 1 - arch/ia64/mm/hugetlbpage.c | 1 - arch/ia64/mm/tlb.c | 1 - arch/m68k/include/asm/mmu_context.h | 2 +- arch/m68k/kernel/dma.c | 2 +- arch/m68k/kernel/traps.c | 3 +-- arch/m68k/mm/cache.c | 2 +- arch/m68k/mm/fault.c | 1 - arch/m68k/mm/kmap.c | 2 +- arch/m68k/mm/mcfmmu.c | 1 + arch/m68k/mm/memory.c | 1 - arch/m68k/sun3x/dvma.c | 2 +- arch/microblaze/include/asm/tlbflush.h | 1 - arch/microblaze/kernel/process.c | 1 - arch/microblaze/kernel/signal.c | 1 - arch/mips/sgi-ip32/ip32-memory.c | 1 - arch/openrisc/include/asm/tlbflush.h | 1 - arch/openrisc/kernel/or32_ksyms.c | 1 - arch/parisc/include/asm/mmu_context.h | 1 - arch/parisc/kernel/cache.c | 1 - arch/parisc/kernel/pci-dma.c | 1 - arch/parisc/kernel/process.c | 1 - arch/parisc/kernel/signal.c | 1 - arch/parisc/kernel/smp.c | 1 - arch/parisc/mm/hugetlbpage.c | 1 - arch/parisc/mm/ioremap.c | 2 +- arch/powerpc/include/asm/tlb.h | 1 - arch/powerpc/mm/book3s64/hash_hugetlbpage.c | 1 - arch/powerpc/mm/book3s64/hash_pgtable.c | 1 - arch/powerpc/mm/book3s64/hash_tlb.c | 1 - arch/powerpc/mm/book3s64/radix_hugetlbpage.c | 1 - arch/powerpc/mm/init_32.c | 1 - arch/powerpc/mm/kasan/8xx.c | 1 - arch/powerpc/mm/kasan/book3s_32.c | 1 - arch/powerpc/mm/mem.c | 1 - arch/powerpc/mm/nohash/40x.c | 1 - arch/powerpc/mm/nohash/8xx.c | 1 - arch/powerpc/mm/nohash/fsl_booke.c | 1 - arch/powerpc/mm/nohash/kaslr_booke.c | 1 - arch/powerpc/mm/nohash/tlb.c | 1 + arch/powerpc/mm/pgtable.c | 1 - arch/powerpc/mm/pgtable_64.c | 1 - arch/powerpc/mm/ptdump/hashpagetable.c | 2 +- arch/powerpc/mm/ptdump/ptdump.c | 1 - arch/powerpc/platforms/pseries/cmm.c | 1 - arch/riscv/mm/fault.c | 1 - arch/s390/include/asm/tlb.h | 1 - arch/s390/include/asm/tlbflush.h | 1 - arch/s390/kernel/machine_kexec.c | 1 - arch/s390/kernel/ptrace.c | 1 - arch/s390/kvm/diag.c | 1 - arch/s390/kvm/priv.c | 1 - arch/s390/kvm/pv.c | 1 - arch/s390/mm/cmm.c | 1 - arch/s390/mm/mmap.c | 1 - arch/s390/mm/pgtable.c | 1 - arch/sh/kernel/idle.c | 1 - arch/sh/kernel/machine_kexec.c | 1 - arch/sh/mm/cache-sh3.c | 1 - arch/sh/mm/cache-sh7705.c | 1 - arch/sh/mm/hugetlbpage.c | 1 - arch/sh/mm/init.c | 1 + arch/sh/mm/ioremap_fixed.c | 1 - arch/sh/mm/tlb-sh3.c | 1 - arch/sparc/include/asm/ide.h | 1 - arch/sparc/include/asm/tlb_64.h | 1 - arch/sparc/kernel/leon_smp.c | 1 - arch/sparc/kernel/process_32.c | 1 - arch/sparc/kernel/signal_32.c | 1 - arch/sparc/kernel/smp_32.c | 1 - arch/sparc/kernel/smp_64.c | 1 + arch/sparc/kernel/sun4m_irq.c | 1 - arch/sparc/mm/highmem.c | 1 - arch/sparc/mm/io-unit.c | 1 - arch/sparc/mm/iommu.c | 1 - arch/sparc/mm/tlb.c | 1 - arch/x86/ia32/ia32_aout.c | 1 - arch/x86/include/asm/mmu_context.h | 1 - arch/x86/kernel/alternative.c | 1 + arch/x86/kernel/apic/apic.c | 1 - arch/x86/kernel/mpparse.c | 1 - arch/x86/kernel/traps.c | 1 - arch/x86/mm/fault.c | 1 - arch/x86/mm/hugetlbpage.c | 1 - arch/x86/mm/kaslr.c | 1 - arch/x86/mm/pgtable_32.c | 1 - arch/x86/mm/pti.c | 1 - arch/x86/platform/uv/bios_uv.c | 1 + arch/xtensa/kernel/xtensa_ksyms.c | 1 - arch/xtensa/mm/cache.c | 1 - arch/xtensa/mm/fault.c | 1 - drivers/block/xen-blkback/common.h | 1 - drivers/iommu/ipmmu-vmsa.c | 1 - drivers/xen/balloon.c | 1 - drivers/xen/privcmd.c | 1 - fs/binfmt_elf_fdpic.c | 1 - include/asm-generic/tlb.h | 1 - mm/hugetlb.c | 1 + mm/sparse.c | 1 - 125 files changed, 17 insertions(+), 118 deletions(-) diff --git a/arch/alpha/include/asm/tlbflush.h b/arch/alpha/include/asm/tlbflush.h index f8b492408f514..94dc37cf873a7 100644 --- a/arch/alpha/include/asm/tlbflush.h +++ b/arch/alpha/include/asm/tlbflush.h @@ -5,7 +5,6 @@ #include #include #include -#include #ifndef __EXTERN_INLINE #define __EXTERN_INLINE extern inline diff --git a/arch/alpha/kernel/core_irongate.c b/arch/alpha/kernel/core_irongate.c index a9fd133a7fb2a..72af1e72d8331 100644 --- a/arch/alpha/kernel/core_irongate.c +++ b/arch/alpha/kernel/core_irongate.c @@ -302,7 +302,6 @@ irongate_init_arch(void) #include #include #include -#include #define GET_PAGE_DIR_OFF(addr) (addr >> 22) #define GET_PAGE_DIR_IDX(addr) (GET_PAGE_DIR_OFF(addr)) diff --git a/arch/alpha/kernel/core_marvel.c b/arch/alpha/kernel/core_marvel.c index 1db9d0eb29221..4c80d992a6594 100644 --- a/arch/alpha/kernel/core_marvel.c +++ b/arch/alpha/kernel/core_marvel.c @@ -23,7 +23,6 @@ #include #include #include -#include #include #include diff --git a/arch/alpha/kernel/core_titan.c b/arch/alpha/kernel/core_titan.c index 2a2820fb1be63..77f5d68ed04bf 100644 --- a/arch/alpha/kernel/core_titan.c +++ b/arch/alpha/kernel/core_titan.c @@ -20,7 +20,6 @@ #include #include -#include #include #include diff --git a/arch/alpha/kernel/machvec_impl.h b/arch/alpha/kernel/machvec_impl.h index 38f045ec5cd29..393d5d6ca5d28 100644 --- a/arch/alpha/kernel/machvec_impl.h +++ b/arch/alpha/kernel/machvec_impl.h @@ -7,8 +7,6 @@ * This file has goodies to help simplify instantiation of machine vectors. */ -#include - /* Whee. These systems don't have an HAE: IRONGATE, MARVEL, POLARIS, TSUNAMI, TITAN, WILDFIRE Fix things up for the GENERIC kernel by defining the HAE address diff --git a/arch/alpha/kernel/smp.c b/arch/alpha/kernel/smp.c index 631cc17410d15..f4dd9f3f30010 100644 --- a/arch/alpha/kernel/smp.c +++ b/arch/alpha/kernel/smp.c @@ -36,7 +36,6 @@ #include #include -#include #include #include diff --git a/arch/alpha/mm/numa.c b/arch/alpha/mm/numa.c index 5ad6087de1d65..0636e254a22fe 100644 --- a/arch/alpha/mm/numa.c +++ b/arch/alpha/mm/numa.c @@ -17,7 +17,6 @@ #include #include -#include #include pg_data_t node_data[MAX_NUMNODES]; diff --git a/arch/arc/mm/fault.c b/arch/arc/mm/fault.c index 72f5405a7ec59..7287c793d1c9d 100644 --- a/arch/arc/mm/fault.c +++ b/arch/arc/mm/fault.c @@ -13,7 +13,6 @@ #include #include #include -#include #include /* diff --git a/arch/arc/mm/init.c b/arch/arc/mm/init.c index e7bdc2ac1c87c..f886ac69d8ada 100644 --- a/arch/arc/mm/init.c +++ b/arch/arc/mm/init.c @@ -14,7 +14,6 @@ #include #include #include -#include #include #include diff --git a/arch/arm/include/asm/tlb.h b/arch/arm/include/asm/tlb.h index 4d4e7b6aabff6..9415222b49ad3 100644 --- a/arch/arm/include/asm/tlb.h +++ b/arch/arm/include/asm/tlb.h @@ -27,7 +27,6 @@ #else /* !CONFIG_MMU */ #include -#include #include static inline void __tlb_remove_table(void *_table) diff --git a/arch/arm/kernel/machine_kexec.c b/arch/arm/kernel/machine_kexec.c index 974b6c64d3e6f..5d84ad333f050 100644 --- a/arch/arm/kernel/machine_kexec.c +++ b/arch/arm/kernel/machine_kexec.c @@ -11,7 +11,6 @@ #include #include #include -#include #include #include #include diff --git a/arch/arm/kernel/smp.c b/arch/arm/kernel/smp.c index 9a6432557871f..5d9da61eff622 100644 --- a/arch/arm/kernel/smp.c +++ b/arch/arm/kernel/smp.c @@ -37,7 +37,6 @@ #include #include #include -#include #include #include #include diff --git a/arch/arm/kernel/suspend.c b/arch/arm/kernel/suspend.c index d2c9338d74e81..24bd20564be77 100644 --- a/arch/arm/kernel/suspend.c +++ b/arch/arm/kernel/suspend.c @@ -7,7 +7,6 @@ #include #include #include -#include #include #include #include diff --git a/arch/arm/mach-omap2/omap-mpuss-lowpower.c b/arch/arm/mach-omap2/omap-mpuss-lowpower.c index 67fa28532a3a8..9fba98c2313ad 100644 --- a/arch/arm/mach-omap2/omap-mpuss-lowpower.c +++ b/arch/arm/mach-omap2/omap-mpuss-lowpower.c @@ -42,7 +42,6 @@ #include #include #include -#include #include #include #include diff --git a/arch/arm/mm/hugetlbpage.c b/arch/arm/mm/hugetlbpage.c index a1e5aace897a3..dd7a0277c5c09 100644 --- a/arch/arm/mm/hugetlbpage.c +++ b/arch/arm/mm/hugetlbpage.c @@ -17,7 +17,6 @@ #include #include #include -#include /* * On ARM, huge pages are backed by pmd's rather than pte's, so we do a lot diff --git a/arch/arm/mm/mmu.c b/arch/arm/mm/mmu.c index cc3c9a6a11130..c36f977b2ccb2 100644 --- a/arch/arm/mm/mmu.c +++ b/arch/arm/mm/mmu.c @@ -29,6 +29,7 @@ #include #include #include +#include #include #include diff --git a/arch/arm64/kernel/smp.c b/arch/arm64/kernel/smp.c index e43a8ff19f0f6..8059d50bc8cbc 100644 --- a/arch/arm64/kernel/smp.c +++ b/arch/arm64/kernel/smp.c @@ -43,7 +43,6 @@ #include #include #include -#include #include #include #include diff --git a/arch/arm64/mm/hugetlbpage.c b/arch/arm64/mm/hugetlbpage.c index aa421bf4956ed..55ecf6de9ff77 100644 --- a/arch/arm64/mm/hugetlbpage.c +++ b/arch/arm64/mm/hugetlbpage.c @@ -17,7 +17,6 @@ #include #include #include -#include /* * HugeTLB Support Matrix diff --git a/arch/arm64/mm/ioremap.c b/arch/arm64/mm/ioremap.c index 9be71bee902ca..b5e83c46b23e7 100644 --- a/arch/arm64/mm/ioremap.c +++ b/arch/arm64/mm/ioremap.c @@ -16,7 +16,6 @@ #include #include -#include static void __iomem *__ioremap_caller(phys_addr_t phys_addr, size_t size, pgprot_t prot, void *caller) diff --git a/arch/arm64/mm/mmu.c b/arch/arm64/mm/mmu.c index 1df25f26571d0..cafefb147a5ec 100644 --- a/arch/arm64/mm/mmu.c +++ b/arch/arm64/mm/mmu.c @@ -35,6 +35,7 @@ #include #include #include +#include #define NO_BLOCK_MAPPINGS BIT(0) #define NO_CONT_MAPPINGS BIT(1) diff --git a/arch/csky/kernel/smp.c b/arch/csky/kernel/smp.c index e7425e6b04193..041d0de6a1b67 100644 --- a/arch/csky/kernel/smp.c +++ b/arch/csky/kernel/smp.c @@ -23,7 +23,6 @@ #include #include #include -#include #ifdef CONFIG_CPU_HAS_FPU #include #endif diff --git a/arch/ia64/include/asm/tlb.h b/arch/ia64/include/asm/tlb.h index f1f257d632b37..8d9da6f08a62e 100644 --- a/arch/ia64/include/asm/tlb.h +++ b/arch/ia64/include/asm/tlb.h @@ -42,7 +42,6 @@ #include #include -#include #include #include diff --git a/arch/ia64/kernel/process.c b/arch/ia64/kernel/process.c index 7a4de9d994c5a..ec0b40f6e9c6e 100644 --- a/arch/ia64/kernel/process.c +++ b/arch/ia64/kernel/process.c @@ -40,7 +40,6 @@ #include #include #include -#include #include #include #include diff --git a/arch/ia64/kernel/smp.c b/arch/ia64/kernel/smp.c index bbfd421e6deb1..0e27420031219 100644 --- a/arch/ia64/kernel/smp.c +++ b/arch/ia64/kernel/smp.c @@ -39,7 +39,6 @@ #include #include #include -#include #include #include #include diff --git a/arch/ia64/kernel/smpboot.c b/arch/ia64/kernel/smpboot.c index 016683b743c26..c29c600d79674 100644 --- a/arch/ia64/kernel/smpboot.c +++ b/arch/ia64/kernel/smpboot.c @@ -49,7 +49,6 @@ #include #include #include -#include #include #include #include diff --git a/arch/ia64/mm/contig.c b/arch/ia64/mm/contig.c index d7d31c718d2dd..e30e360beef84 100644 --- a/arch/ia64/mm/contig.c +++ b/arch/ia64/mm/contig.c @@ -21,7 +21,6 @@ #include #include -#include #include #include diff --git a/arch/ia64/mm/discontig.c b/arch/ia64/mm/discontig.c index da810ca234dad..37f8b6875ac90 100644 --- a/arch/ia64/mm/discontig.c +++ b/arch/ia64/mm/discontig.c @@ -24,7 +24,6 @@ #include #include #include -#include #include #include #include diff --git a/arch/ia64/mm/hugetlbpage.c b/arch/ia64/mm/hugetlbpage.c index 32352a73df0c1..b331f94d20ac1 100644 --- a/arch/ia64/mm/hugetlbpage.c +++ b/arch/ia64/mm/hugetlbpage.c @@ -18,7 +18,6 @@ #include #include #include -#include #include #include diff --git a/arch/ia64/mm/tlb.c b/arch/ia64/mm/tlb.c index 71c19918e3875..135b5135cace2 100644 --- a/arch/ia64/mm/tlb.c +++ b/arch/ia64/mm/tlb.c @@ -27,7 +27,6 @@ #include #include -#include #include #include #include diff --git a/arch/m68k/include/asm/mmu_context.h b/arch/m68k/include/asm/mmu_context.h index cac9f289d1f60..993fd7e37069f 100644 --- a/arch/m68k/include/asm/mmu_context.h +++ b/arch/m68k/include/asm/mmu_context.h @@ -222,7 +222,7 @@ static inline void activate_mm(struct mm_struct *prev_mm, #include #include -#include +#include static inline int init_new_context(struct task_struct *tsk, struct mm_struct *mm) diff --git a/arch/m68k/kernel/dma.c b/arch/m68k/kernel/dma.c index 871a0e11da341..b1ca3522eccc2 100644 --- a/arch/m68k/kernel/dma.c +++ b/arch/m68k/kernel/dma.c @@ -15,7 +15,7 @@ #include #include -#include +#include #if defined(CONFIG_MMU) && !defined(CONFIG_COLDFIRE) void arch_dma_prep_coherent(struct page *page, size_t size) diff --git a/arch/m68k/kernel/traps.c b/arch/m68k/kernel/traps.c index df6fc782754f7..546e81935fe89 100644 --- a/arch/m68k/kernel/traps.c +++ b/arch/m68k/kernel/traps.c @@ -35,10 +35,9 @@ #include #include #include -#include #include #include - +#include static const char *vec_names[] = { [VEC_RESETSP] = "RESET SP", diff --git a/arch/m68k/mm/cache.c b/arch/m68k/mm/cache.c index 5ecb3310e8745..b486c0889eece 100644 --- a/arch/m68k/mm/cache.c +++ b/arch/m68k/mm/cache.c @@ -8,7 +8,7 @@ */ #include -#include +#include #include diff --git a/arch/m68k/mm/fault.c b/arch/m68k/mm/fault.c index a94a814ad6adb..508abb63da678 100644 --- a/arch/m68k/mm/fault.c +++ b/arch/m68k/mm/fault.c @@ -15,7 +15,6 @@ #include #include -#include extern void die_if_kernel(char *, struct pt_regs *, long); diff --git a/arch/m68k/mm/kmap.c b/arch/m68k/mm/kmap.c index 14d31d216cefe..1269d513b2217 100644 --- a/arch/m68k/mm/kmap.c +++ b/arch/m68k/mm/kmap.c @@ -19,8 +19,8 @@ #include #include #include -#include #include +#include #undef DEBUG diff --git a/arch/m68k/mm/mcfmmu.c b/arch/m68k/mm/mcfmmu.c index 7d04210d34f02..7068126cedc1e 100644 --- a/arch/m68k/mm/mcfmmu.c +++ b/arch/m68k/mm/mcfmmu.c @@ -20,6 +20,7 @@ #include #include #include +#include #define KMAPAREA(x) ((x >= VMALLOC_START) && (x < KMAP_END)) diff --git a/arch/m68k/mm/memory.c b/arch/m68k/mm/memory.c index 65e0c40719121..fe75aecfb238a 100644 --- a/arch/m68k/mm/memory.c +++ b/arch/m68k/mm/memory.c @@ -17,7 +17,6 @@ #include #include #include -#include #include #include diff --git a/arch/m68k/sun3x/dvma.c b/arch/m68k/sun3x/dvma.c index fef52d222d46b..08bb92113026a 100644 --- a/arch/m68k/sun3x/dvma.c +++ b/arch/m68k/sun3x/dvma.c @@ -22,7 +22,7 @@ #include #include #include -#include +#include /* IOMMU support */ diff --git a/arch/microblaze/include/asm/tlbflush.h b/arch/microblaze/include/asm/tlbflush.h index 6f8f5c77a0501..1200e2bf14bb1 100644 --- a/arch/microblaze/include/asm/tlbflush.h +++ b/arch/microblaze/include/asm/tlbflush.h @@ -15,7 +15,6 @@ #include /* For TASK_SIZE */ #include #include -#include extern void _tlbie(unsigned long address); extern void _tlbia(void); diff --git a/arch/microblaze/kernel/process.c b/arch/microblaze/kernel/process.c index 6cabeab9e2ba0..a9e46e525cd0a 100644 --- a/arch/microblaze/kernel/process.c +++ b/arch/microblaze/kernel/process.c @@ -18,7 +18,6 @@ #include #include #include -#include #include /* for USER_DS macros */ #include diff --git a/arch/microblaze/kernel/signal.c b/arch/microblaze/kernel/signal.c index bdd6d0c86e16f..65bf5fd8d4733 100644 --- a/arch/microblaze/kernel/signal.c +++ b/arch/microblaze/kernel/signal.c @@ -35,7 +35,6 @@ #include #include #include -#include #include #include #include diff --git a/arch/mips/sgi-ip32/ip32-memory.c b/arch/mips/sgi-ip32/ip32-memory.c index be1b2cfc4c3ed..62b956cc2d1d2 100644 --- a/arch/mips/sgi-ip32/ip32-memory.c +++ b/arch/mips/sgi-ip32/ip32-memory.c @@ -14,7 +14,6 @@ #include #include #include -#include extern void crime_init(void); diff --git a/arch/openrisc/include/asm/tlbflush.h b/arch/openrisc/include/asm/tlbflush.h index 4a4639c65cbb6..185dcd3731edd 100644 --- a/arch/openrisc/include/asm/tlbflush.h +++ b/arch/openrisc/include/asm/tlbflush.h @@ -17,7 +17,6 @@ #include #include -#include #include #include diff --git a/arch/openrisc/kernel/or32_ksyms.c b/arch/openrisc/kernel/or32_ksyms.c index 277ac7a557525..212e5f85004cc 100644 --- a/arch/openrisc/kernel/or32_ksyms.c +++ b/arch/openrisc/kernel/or32_ksyms.c @@ -26,7 +26,6 @@ #include #include #include -#include #define DECLARE_EXPORT(name) extern void name(void); EXPORT_SYMBOL(name) diff --git a/arch/parisc/include/asm/mmu_context.h b/arch/parisc/include/asm/mmu_context.h index 07b89c74abeb1..cb5f2f7304213 100644 --- a/arch/parisc/include/asm/mmu_context.h +++ b/arch/parisc/include/asm/mmu_context.h @@ -5,7 +5,6 @@ #include #include #include -#include #include static inline void enter_lazy_tlb(struct mm_struct *mm, struct task_struct *tsk) diff --git a/arch/parisc/kernel/cache.c b/arch/parisc/kernel/cache.c index 1eedfecc51371..b5e1d9f1b440b 100644 --- a/arch/parisc/kernel/cache.c +++ b/arch/parisc/kernel/cache.c @@ -24,7 +24,6 @@ #include #include #include -#include #include #include #include diff --git a/arch/parisc/kernel/pci-dma.c b/arch/parisc/kernel/pci-dma.c index 4f1596bb1936d..38c68e131bbe2 100644 --- a/arch/parisc/kernel/pci-dma.c +++ b/arch/parisc/kernel/pci-dma.c @@ -32,7 +32,6 @@ #include /* for DMA_CHUNK_SIZE */ #include #include /* get_order */ -#include #include #include /* for purge_tlb_*() macros */ diff --git a/arch/parisc/kernel/process.c b/arch/parisc/kernel/process.c index de6299ff15305..86ec30cc0e770 100644 --- a/arch/parisc/kernel/process.c +++ b/arch/parisc/kernel/process.c @@ -47,7 +47,6 @@ #include #include #include -#include #include #include diff --git a/arch/parisc/kernel/signal.c b/arch/parisc/kernel/signal.c index 02895a8f2c551..5df5d4cd5d4cf 100644 --- a/arch/parisc/kernel/signal.c +++ b/arch/parisc/kernel/signal.c @@ -30,7 +30,6 @@ #include #include #include -#include #include #include diff --git a/arch/parisc/kernel/smp.c b/arch/parisc/kernel/smp.c index f8a842ddd82de..6271139d2213a 100644 --- a/arch/parisc/kernel/smp.c +++ b/arch/parisc/kernel/smp.c @@ -39,7 +39,6 @@ #include /* for CPU_IRQ_REGION and friends */ #include #include -#include #include #include #include diff --git a/arch/parisc/mm/hugetlbpage.c b/arch/parisc/mm/hugetlbpage.c index 0e1e212f1c965..d7ba014a7fbb5 100644 --- a/arch/parisc/mm/hugetlbpage.c +++ b/arch/parisc/mm/hugetlbpage.c @@ -15,7 +15,6 @@ #include #include -#include #include #include #include diff --git a/arch/parisc/mm/ioremap.c b/arch/parisc/mm/ioremap.c index 6e7c005aa09b9..345ff0b664993 100644 --- a/arch/parisc/mm/ioremap.c +++ b/arch/parisc/mm/ioremap.c @@ -11,7 +11,7 @@ #include #include #include -#include +#include /* * Generic mapping function (not visible outside): diff --git a/arch/powerpc/include/asm/tlb.h b/arch/powerpc/include/asm/tlb.h index 862985cf51804..fbc6f3002f236 100644 --- a/arch/powerpc/include/asm/tlb.h +++ b/arch/powerpc/include/asm/tlb.h @@ -12,7 +12,6 @@ #ifndef __powerpc64__ #include #endif -#include #ifndef __powerpc64__ #include #include diff --git a/arch/powerpc/mm/book3s64/hash_hugetlbpage.c b/arch/powerpc/mm/book3s64/hash_hugetlbpage.c index 25acb9c5ee1b5..964467b3a776c 100644 --- a/arch/powerpc/mm/book3s64/hash_hugetlbpage.c +++ b/arch/powerpc/mm/book3s64/hash_hugetlbpage.c @@ -10,7 +10,6 @@ #include #include -#include #include #include diff --git a/arch/powerpc/mm/book3s64/hash_pgtable.c b/arch/powerpc/mm/book3s64/hash_pgtable.c index 2a99167afbaf3..fd9c7f91b0923 100644 --- a/arch/powerpc/mm/book3s64/hash_pgtable.c +++ b/arch/powerpc/mm/book3s64/hash_pgtable.c @@ -9,7 +9,6 @@ #include #include -#include #include #include #include diff --git a/arch/powerpc/mm/book3s64/hash_tlb.c b/arch/powerpc/mm/book3s64/hash_tlb.c index 0fbf3dc9f2c29..eb0bccaf221ea 100644 --- a/arch/powerpc/mm/book3s64/hash_tlb.c +++ b/arch/powerpc/mm/book3s64/hash_tlb.c @@ -21,7 +21,6 @@ #include #include #include -#include #include #include #include diff --git a/arch/powerpc/mm/book3s64/radix_hugetlbpage.c b/arch/powerpc/mm/book3s64/radix_hugetlbpage.c index c812b401b66ce..cb91071eef524 100644 --- a/arch/powerpc/mm/book3s64/radix_hugetlbpage.c +++ b/arch/powerpc/mm/book3s64/radix_hugetlbpage.c @@ -2,7 +2,6 @@ #include #include #include -#include #include #include #include diff --git a/arch/powerpc/mm/init_32.c b/arch/powerpc/mm/init_32.c index 5a5469eb3174f..7ea19dc4883b4 100644 --- a/arch/powerpc/mm/init_32.c +++ b/arch/powerpc/mm/init_32.c @@ -29,7 +29,6 @@ #include #include -#include #include #include #include diff --git a/arch/powerpc/mm/kasan/8xx.c b/arch/powerpc/mm/kasan/8xx.c index 569d98a41881b..2784224054f85 100644 --- a/arch/powerpc/mm/kasan/8xx.c +++ b/arch/powerpc/mm/kasan/8xx.c @@ -5,7 +5,6 @@ #include #include #include -#include static int __init kasan_init_shadow_8M(unsigned long k_start, unsigned long k_end, void *block) diff --git a/arch/powerpc/mm/kasan/book3s_32.c b/arch/powerpc/mm/kasan/book3s_32.c index a32b4640b9dea..202bd260a0095 100644 --- a/arch/powerpc/mm/kasan/book3s_32.c +++ b/arch/powerpc/mm/kasan/book3s_32.c @@ -4,7 +4,6 @@ #include #include -#include #include int __init kasan_init_region(void *start, size_t size) diff --git a/arch/powerpc/mm/mem.c b/arch/powerpc/mm/mem.c index c2c11eb8dcfca..ab12916ec1a7b 100644 --- a/arch/powerpc/mm/mem.c +++ b/arch/powerpc/mm/mem.c @@ -34,7 +34,6 @@ #include #include -#include #include #include #include diff --git a/arch/powerpc/mm/nohash/40x.c b/arch/powerpc/mm/nohash/40x.c index 13e74bc39ba5d..95751c322f6cd 100644 --- a/arch/powerpc/mm/nohash/40x.c +++ b/arch/powerpc/mm/nohash/40x.c @@ -32,7 +32,6 @@ #include #include -#include #include #include #include diff --git a/arch/powerpc/mm/nohash/8xx.c b/arch/powerpc/mm/nohash/8xx.c index 92e8929cbe3e1..d2b37146ae6c9 100644 --- a/arch/powerpc/mm/nohash/8xx.c +++ b/arch/powerpc/mm/nohash/8xx.c @@ -13,7 +13,6 @@ #include #include #include -#include #include diff --git a/arch/powerpc/mm/nohash/fsl_booke.c b/arch/powerpc/mm/nohash/fsl_booke.c index c06dfbb771f46..0c294827d6e51 100644 --- a/arch/powerpc/mm/nohash/fsl_booke.c +++ b/arch/powerpc/mm/nohash/fsl_booke.c @@ -37,7 +37,6 @@ #include #include -#include #include #include #include diff --git a/arch/powerpc/mm/nohash/kaslr_booke.c b/arch/powerpc/mm/nohash/kaslr_booke.c index bce0e5349978f..4c74e8a5482bf 100644 --- a/arch/powerpc/mm/nohash/kaslr_booke.c +++ b/arch/powerpc/mm/nohash/kaslr_booke.c @@ -15,7 +15,6 @@ #include #include #include -#include #include #include #include diff --git a/arch/powerpc/mm/nohash/tlb.c b/arch/powerpc/mm/nohash/tlb.c index 696f568253a0c..14514585db980 100644 --- a/arch/powerpc/mm/nohash/tlb.c +++ b/arch/powerpc/mm/nohash/tlb.c @@ -34,6 +34,7 @@ #include #include +#include #include #include #include diff --git a/arch/powerpc/mm/pgtable.c b/arch/powerpc/mm/pgtable.c index 1136257c3a99d..9c0547d77af35 100644 --- a/arch/powerpc/mm/pgtable.c +++ b/arch/powerpc/mm/pgtable.c @@ -23,7 +23,6 @@ #include #include #include -#include #include #include #include diff --git a/arch/powerpc/mm/pgtable_64.c b/arch/powerpc/mm/pgtable_64.c index bb43a8c04bee2..cc6e2f94517fc 100644 --- a/arch/powerpc/mm/pgtable_64.c +++ b/arch/powerpc/mm/pgtable_64.c @@ -31,7 +31,6 @@ #include #include -#include #include #include #include diff --git a/arch/powerpc/mm/ptdump/hashpagetable.c b/arch/powerpc/mm/ptdump/hashpagetable.c index a2c33efc7ce8d..ff4b05a9e7f07 100644 --- a/arch/powerpc/mm/ptdump/hashpagetable.c +++ b/arch/powerpc/mm/ptdump/hashpagetable.c @@ -17,10 +17,10 @@ #include #include #include -#include #include #include #include +#include struct pg_state { struct seq_file *seq; diff --git a/arch/powerpc/mm/ptdump/ptdump.c b/arch/powerpc/mm/ptdump/ptdump.c index de6e05ef871c1..f7ba13c41d13c 100644 --- a/arch/powerpc/mm/ptdump/ptdump.c +++ b/arch/powerpc/mm/ptdump/ptdump.c @@ -21,7 +21,6 @@ #include #include #include -#include #include #include diff --git a/arch/powerpc/platforms/pseries/cmm.c b/arch/powerpc/platforms/pseries/cmm.c index 9dba7e8808853..45a3a3022a85c 100644 --- a/arch/powerpc/platforms/pseries/cmm.c +++ b/arch/powerpc/platforms/pseries/cmm.c @@ -26,7 +26,6 @@ #include #include #include -#include #include #include #include diff --git a/arch/riscv/mm/fault.c b/arch/riscv/mm/fault.c index ae7b7fe24658a..5873835a3e6b7 100644 --- a/arch/riscv/mm/fault.c +++ b/arch/riscv/mm/fault.c @@ -14,7 +14,6 @@ #include #include -#include #include #include diff --git a/arch/s390/include/asm/tlb.h b/arch/s390/include/asm/tlb.h index aa406c05a3505..954fa8ca6cbda 100644 --- a/arch/s390/include/asm/tlb.h +++ b/arch/s390/include/asm/tlb.h @@ -36,7 +36,6 @@ static inline bool __tlb_remove_page_size(struct mmu_gather *tlb, #define p4d_free_tlb p4d_free_tlb #define pud_free_tlb pud_free_tlb -#include #include #include diff --git a/arch/s390/include/asm/tlbflush.h b/arch/s390/include/asm/tlbflush.h index 2204704840eaa..acce6a08a1faa 100644 --- a/arch/s390/include/asm/tlbflush.h +++ b/arch/s390/include/asm/tlbflush.h @@ -5,7 +5,6 @@ #include #include #include -#include /* * Flush all TLB entries on the local CPU. diff --git a/arch/s390/kernel/machine_kexec.c b/arch/s390/kernel/machine_kexec.c index 93c6b8932fbd4..d91989c7bd6a0 100644 --- a/arch/s390/kernel/machine_kexec.c +++ b/arch/s390/kernel/machine_kexec.c @@ -16,7 +16,6 @@ #include #include #include -#include #include #include #include diff --git a/arch/s390/kernel/ptrace.c b/arch/s390/kernel/ptrace.c index 3cc15c0662983..3c72a3b772536 100644 --- a/arch/s390/kernel/ptrace.c +++ b/arch/s390/kernel/ptrace.c @@ -25,7 +25,6 @@ #include #include #include -#include #include #include #include diff --git a/arch/s390/kvm/diag.c b/arch/s390/kvm/diag.c index 563429dece034..5b8ec1c447e14 100644 --- a/arch/s390/kvm/diag.c +++ b/arch/s390/kvm/diag.c @@ -10,7 +10,6 @@ #include #include -#include #include #include #include "kvm-s390.h" diff --git a/arch/s390/kvm/priv.c b/arch/s390/kvm/priv.c index 96ae368aa0a2c..2f721a923b549 100644 --- a/arch/s390/kvm/priv.c +++ b/arch/s390/kvm/priv.c @@ -22,7 +22,6 @@ #include #include #include -#include #include #include #include diff --git a/arch/s390/kvm/pv.c b/arch/s390/kvm/pv.c index 63e330109b63b..eb99e2f95ebed 100644 --- a/arch/s390/kvm/pv.c +++ b/arch/s390/kvm/pv.c @@ -9,7 +9,6 @@ #include #include #include -#include #include #include #include diff --git a/arch/s390/mm/cmm.c b/arch/s390/mm/cmm.c index 5c15ae3daf718..1141c8d5c0d03 100644 --- a/arch/s390/mm/cmm.c +++ b/arch/s390/mm/cmm.c @@ -21,7 +21,6 @@ #include #include -#include #include #ifdef CONFIG_CMM_IUCV diff --git a/arch/s390/mm/mmap.c b/arch/s390/mm/mmap.c index 1b78f630a9ca7..e54f928503c5c 100644 --- a/arch/s390/mm/mmap.c +++ b/arch/s390/mm/mmap.c @@ -17,7 +17,6 @@ #include #include #include -#include #include static unsigned long stack_maxrandom_size(void) diff --git a/arch/s390/mm/pgtable.c b/arch/s390/mm/pgtable.c index 2e0cc19f4cd70..0d25f743b270e 100644 --- a/arch/s390/mm/pgtable.c +++ b/arch/s390/mm/pgtable.c @@ -19,7 +19,6 @@ #include #include -#include #include #include #include diff --git a/arch/sh/kernel/idle.c b/arch/sh/kernel/idle.c index c20fc5487e051..0dc0f52f9bb8d 100644 --- a/arch/sh/kernel/idle.c +++ b/arch/sh/kernel/idle.c @@ -14,7 +14,6 @@ #include #include #include -#include #include #include diff --git a/arch/sh/kernel/machine_kexec.c b/arch/sh/kernel/machine_kexec.c index 4a98980b8a077..223c14f44af7e 100644 --- a/arch/sh/kernel/machine_kexec.c +++ b/arch/sh/kernel/machine_kexec.c @@ -14,7 +14,6 @@ #include #include #include -#include #include #include #include diff --git a/arch/sh/mm/cache-sh3.c b/arch/sh/mm/cache-sh3.c index 26f3bd43e8500..bc595982d396e 100644 --- a/arch/sh/mm/cache-sh3.c +++ b/arch/sh/mm/cache-sh3.c @@ -16,7 +16,6 @@ #include #include #include -#include #include #include diff --git a/arch/sh/mm/cache-sh7705.c b/arch/sh/mm/cache-sh7705.c index 48978293226cf..4c67b3d887750 100644 --- a/arch/sh/mm/cache-sh7705.c +++ b/arch/sh/mm/cache-sh7705.c @@ -20,7 +20,6 @@ #include #include #include -#include #include #include diff --git a/arch/sh/mm/hugetlbpage.c b/arch/sh/mm/hugetlbpage.c index acd5652a0de3d..220d7bc43d2bb 100644 --- a/arch/sh/mm/hugetlbpage.c +++ b/arch/sh/mm/hugetlbpage.c @@ -17,7 +17,6 @@ #include #include -#include #include #include #include diff --git a/arch/sh/mm/init.c b/arch/sh/mm/init.c index a70ba0fdd0b38..a86ce13f392c9 100644 --- a/arch/sh/mm/init.c +++ b/arch/sh/mm/init.c @@ -27,6 +27,7 @@ #include #include #include +#include #include pgd_t swapper_pg_dir[PTRS_PER_PGD]; diff --git a/arch/sh/mm/ioremap_fixed.c b/arch/sh/mm/ioremap_fixed.c index 07e744d75fa01..aab3f82856bb2 100644 --- a/arch/sh/mm/ioremap_fixed.c +++ b/arch/sh/mm/ioremap_fixed.c @@ -18,7 +18,6 @@ #include #include #include -#include #include #include #include diff --git a/arch/sh/mm/tlb-sh3.c b/arch/sh/mm/tlb-sh3.c index 869243518bb3a..fb400afc2a496 100644 --- a/arch/sh/mm/tlb-sh3.c +++ b/arch/sh/mm/tlb-sh3.c @@ -21,7 +21,6 @@ #include #include -#include #include #include diff --git a/arch/sparc/include/asm/ide.h b/arch/sparc/include/asm/ide.h index 499aa2e6e2768..904cc6cbc1550 100644 --- a/arch/sparc/include/asm/ide.h +++ b/arch/sparc/include/asm/ide.h @@ -13,7 +13,6 @@ #include #ifdef CONFIG_SPARC64 -#include #include #include #include diff --git a/arch/sparc/include/asm/tlb_64.h b/arch/sparc/include/asm/tlb_64.h index 6820d357581c3..e841cae544c2b 100644 --- a/arch/sparc/include/asm/tlb_64.h +++ b/arch/sparc/include/asm/tlb_64.h @@ -4,7 +4,6 @@ #include #include -#include #include #include diff --git a/arch/sparc/kernel/leon_smp.c b/arch/sparc/kernel/leon_smp.c index 41829c024f928..1eed26d423fb2 100644 --- a/arch/sparc/kernel/leon_smp.c +++ b/arch/sparc/kernel/leon_smp.c @@ -38,7 +38,6 @@ #include #include #include -#include #include #include #include diff --git a/arch/sparc/kernel/process_32.c b/arch/sparc/kernel/process_32.c index bd123f1de2e7c..3f519e1047b6a 100644 --- a/arch/sparc/kernel/process_32.c +++ b/arch/sparc/kernel/process_32.c @@ -34,7 +34,6 @@ #include #include #include -#include #include #include #include diff --git a/arch/sparc/kernel/signal_32.c b/arch/sparc/kernel/signal_32.c index 3b005b6c3e0fd..f1f8c8ebe641d 100644 --- a/arch/sparc/kernel/signal_32.c +++ b/arch/sparc/kernel/signal_32.c @@ -23,7 +23,6 @@ #include #include -#include #include /* flush_sig_insns */ #include diff --git a/arch/sparc/kernel/smp_32.c b/arch/sparc/kernel/smp_32.c index 76ce290c67cfd..50c127ab46d5b 100644 --- a/arch/sparc/kernel/smp_32.c +++ b/arch/sparc/kernel/smp_32.c @@ -29,7 +29,6 @@ #include #include -#include #include #include #include diff --git a/arch/sparc/kernel/smp_64.c b/arch/sparc/kernel/smp_64.c index 0085e28bf019a..e286e2badc8a4 100644 --- a/arch/sparc/kernel/smp_64.c +++ b/arch/sparc/kernel/smp_64.c @@ -47,6 +47,7 @@ #include #include #include +#include #include #include #include diff --git a/arch/sparc/kernel/sun4m_irq.c b/arch/sparc/kernel/sun4m_irq.c index 91b61f012d193..1079638986b52 100644 --- a/arch/sparc/kernel/sun4m_irq.c +++ b/arch/sparc/kernel/sun4m_irq.c @@ -16,7 +16,6 @@ #include #include -#include #include #include #include diff --git a/arch/sparc/mm/highmem.c b/arch/sparc/mm/highmem.c index d1fc9a7b7d78e..8f2a2afb048a2 100644 --- a/arch/sparc/mm/highmem.c +++ b/arch/sparc/mm/highmem.c @@ -29,7 +29,6 @@ #include #include -#include #include static pte_t *kmap_pte; diff --git a/arch/sparc/mm/io-unit.c b/arch/sparc/mm/io-unit.c index bfcc04bfce541..430a47a1b6aef 100644 --- a/arch/sparc/mm/io-unit.c +++ b/arch/sparc/mm/io-unit.c @@ -15,7 +15,6 @@ #include #include -#include #include #include #include diff --git a/arch/sparc/mm/iommu.c b/arch/sparc/mm/iommu.c index 35b002eb312eb..3a388b1c5d4bc 100644 --- a/arch/sparc/mm/iommu.c +++ b/arch/sparc/mm/iommu.c @@ -16,7 +16,6 @@ #include #include -#include #include #include #include diff --git a/arch/sparc/mm/tlb.c b/arch/sparc/mm/tlb.c index a32a16c186174..20ee147393332 100644 --- a/arch/sparc/mm/tlb.c +++ b/arch/sparc/mm/tlb.c @@ -10,7 +10,6 @@ #include #include -#include #include #include #include diff --git a/arch/x86/ia32/ia32_aout.c b/arch/x86/ia32/ia32_aout.c index 385d3d172ee14..ca8a657edf597 100644 --- a/arch/x86/ia32/ia32_aout.c +++ b/arch/x86/ia32/ia32_aout.c @@ -30,7 +30,6 @@ #include #include -#include #include #include #include diff --git a/arch/x86/include/asm/mmu_context.h b/arch/x86/include/asm/mmu_context.h index 47562147e70b8..d98016b837559 100644 --- a/arch/x86/include/asm/mmu_context.h +++ b/arch/x86/include/asm/mmu_context.h @@ -9,7 +9,6 @@ #include -#include #include #include #include diff --git a/arch/x86/kernel/alternative.c b/arch/x86/kernel/alternative.c index c826cddae1576..d1175533d125d 100644 --- a/arch/x86/kernel/alternative.c +++ b/arch/x86/kernel/alternative.c @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include diff --git a/arch/x86/kernel/apic/apic.c b/arch/x86/kernel/apic/apic.c index e0e2f020ec025..ccf726cc87b77 100644 --- a/arch/x86/kernel/apic/apic.c +++ b/arch/x86/kernel/apic/apic.c @@ -40,7 +40,6 @@ #include #include #include -#include #include #include #include diff --git a/arch/x86/kernel/mpparse.c b/arch/x86/kernel/mpparse.c index afac7ccce72f4..c27b82b62c8bd 100644 --- a/arch/x86/kernel/mpparse.c +++ b/arch/x86/kernel/mpparse.c @@ -22,7 +22,6 @@ #include #include #include -#include #include #include #include diff --git a/arch/x86/kernel/traps.c b/arch/x86/kernel/traps.c index 438fc554d48d5..1f66d2d1e998d 100644 --- a/arch/x86/kernel/traps.c +++ b/arch/x86/kernel/traps.c @@ -62,7 +62,6 @@ #ifdef CONFIG_X86_64 #include -#include #include #else #include diff --git a/arch/x86/mm/fault.c b/arch/x86/mm/fault.c index 5e5edd2ec893d..0c7643d9f7cb3 100644 --- a/arch/x86/mm/fault.c +++ b/arch/x86/mm/fault.c @@ -21,7 +21,6 @@ #include /* boot_cpu_has, ... */ #include /* dotraplinkage, ... */ -#include /* pgd_*(), ... */ #include /* VSYSCALL_ADDR */ #include /* emulate_vsyscall */ #include /* struct vm86 */ diff --git a/arch/x86/mm/hugetlbpage.c b/arch/x86/mm/hugetlbpage.c index cf57811427167..a0d023cb42921 100644 --- a/arch/x86/mm/hugetlbpage.c +++ b/arch/x86/mm/hugetlbpage.c @@ -17,7 +17,6 @@ #include #include #include -#include #include #if 0 /* This is just for testing */ diff --git a/arch/x86/mm/kaslr.c b/arch/x86/mm/kaslr.c index fb620fd9dae9d..6e6b39710e5fa 100644 --- a/arch/x86/mm/kaslr.c +++ b/arch/x86/mm/kaslr.c @@ -26,7 +26,6 @@ #include #include -#include #include #include diff --git a/arch/x86/mm/pgtable_32.c b/arch/x86/mm/pgtable_32.c index 1953685c2ddfe..c234634e26ba4 100644 --- a/arch/x86/mm/pgtable_32.c +++ b/arch/x86/mm/pgtable_32.c @@ -11,7 +11,6 @@ #include #include -#include #include #include #include diff --git a/arch/x86/mm/pti.c b/arch/x86/mm/pti.c index a8a924b3c3358..1aab92930569a 100644 --- a/arch/x86/mm/pti.c +++ b/arch/x86/mm/pti.c @@ -34,7 +34,6 @@ #include #include #include -#include #include #include #include diff --git a/arch/x86/platform/uv/bios_uv.c b/arch/x86/platform/uv/bios_uv.c index a6e5f2c1805d0..a2f447dffea6c 100644 --- a/arch/x86/platform/uv/bios_uv.c +++ b/arch/x86/platform/uv/bios_uv.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include diff --git a/arch/xtensa/kernel/xtensa_ksyms.c b/arch/xtensa/kernel/xtensa_ksyms.c index 24cf6972eacea..415fe7faa37f0 100644 --- a/arch/xtensa/kernel/xtensa_ksyms.c +++ b/arch/xtensa/kernel/xtensa_ksyms.c @@ -25,7 +25,6 @@ #include #include #include -#include #include #ifdef CONFIG_BLK_DEV_FD #include diff --git a/arch/xtensa/mm/cache.c b/arch/xtensa/mm/cache.c index 2369433b734af..5835406b3cecb 100644 --- a/arch/xtensa/mm/cache.c +++ b/arch/xtensa/mm/cache.c @@ -31,7 +31,6 @@ #include #include #include -#include /* * Note: diff --git a/arch/xtensa/mm/fault.c b/arch/xtensa/mm/fault.c index c4decc73fd86e..c128dcc7c85b4 100644 --- a/arch/xtensa/mm/fault.c +++ b/arch/xtensa/mm/fault.c @@ -20,7 +20,6 @@ #include #include #include -#include DEFINE_PER_CPU(unsigned long, asid_cache) = ASID_USER_FIRST; void bad_page_fault(struct pt_regs*, unsigned long, int); diff --git a/drivers/block/xen-blkback/common.h b/drivers/block/xen-blkback/common.h index a3eeccf3ac5f0..c6ea5d38c509a 100644 --- a/drivers/block/xen-blkback/common.h +++ b/drivers/block/xen-blkback/common.h @@ -36,7 +36,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/iommu/ipmmu-vmsa.c b/drivers/iommu/ipmmu-vmsa.c index 4c2972f3153b5..6de86e73dfc3f 100644 --- a/drivers/iommu/ipmmu-vmsa.c +++ b/drivers/iommu/ipmmu-vmsa.c @@ -28,7 +28,6 @@ #if defined(CONFIG_ARM) && !defined(CONFIG_IOMMU_DMA) #include -#include #else #define arm_iommu_create_mapping(...) NULL #define arm_iommu_attach_device(...) -ENODEV diff --git a/drivers/xen/balloon.c b/drivers/xen/balloon.c index 77c57568e5d7f..f5c838a92b019 100644 --- a/drivers/xen/balloon.c +++ b/drivers/xen/balloon.c @@ -58,7 +58,6 @@ #include #include -#include #include #include diff --git a/drivers/xen/privcmd.c b/drivers/xen/privcmd.c index a250d118144a0..5dfc59fd9f16e 100644 --- a/drivers/xen/privcmd.c +++ b/drivers/xen/privcmd.c @@ -25,7 +25,6 @@ #include #include -#include #include #include diff --git a/fs/binfmt_elf_fdpic.c b/fs/binfmt_elf_fdpic.c index 0f45521b237cd..cf306e0798fd0 100644 --- a/fs/binfmt_elf_fdpic.c +++ b/fs/binfmt_elf_fdpic.c @@ -38,7 +38,6 @@ #include #include -#include typedef char *elf_caddr_t; diff --git a/include/asm-generic/tlb.h b/include/asm-generic/tlb.h index ef75ec86f8654..6661ee1cff479 100644 --- a/include/asm-generic/tlb.h +++ b/include/asm-generic/tlb.h @@ -14,7 +14,6 @@ #include #include #include -#include #include #include diff --git a/mm/hugetlb.c b/mm/hugetlb.c index 590111ea6975d..27556d4d49fee 100644 --- a/mm/hugetlb.c +++ b/mm/hugetlb.c @@ -31,6 +31,7 @@ #include #include +#include #include #include diff --git a/mm/sparse.c b/mm/sparse.c index b2b9a3e346967..97179d27801af 100644 --- a/mm/sparse.c +++ b/mm/sparse.c @@ -16,7 +16,6 @@ #include "internal.h" #include -#include /* * Permanent SPARSEMEM data: -- GitLab From fc2a6b837db74aed1160ebcfe8dd94bb58141401 Mon Sep 17 00:00:00 2001 From: Mike Rapoport Date: Thu, 6 Aug 2020 23:22:32 -0700 Subject: [PATCH 1406/1476] opeinrisc: switch to generic version of pte allocation Replace pte_alloc_one(), pte_free() and pte_free_kernel() with the generic implementation. The only actual functional change is the addition of __GFP_ACCOUT for the allocation of the user page tables. The pte_alloc_one_kernel() is kept back because its implementation on openrisc is different than the generic one. Signed-off-by: Mike Rapoport Signed-off-by: Andrew Morton Reviewed-by: Pekka Enberg Acked-by: Stafford Horne Cc: Abdul Haleem Cc: Andy Lutomirski Cc: Arnd Bergmann Cc: Christophe Leroy Cc: Joerg Roedel Cc: Joerg Roedel Cc: Max Filippov Cc: Peter Zijlstra (Intel) Cc: Satheesh Rajendran Cc: Stephen Rothwell Cc: Steven Rostedt Cc: Geert Uytterhoeven Cc: Matthew Wilcox Link: http://lkml.kernel.org/r/20200627143453.31835-3-rppt@kernel.org Signed-off-by: Linus Torvalds --- arch/openrisc/include/asm/pgalloc.h | 33 +++-------------------------- 1 file changed, 3 insertions(+), 30 deletions(-) diff --git a/arch/openrisc/include/asm/pgalloc.h b/arch/openrisc/include/asm/pgalloc.h index da12a4c38c4b5..88820299ecc4a 100644 --- a/arch/openrisc/include/asm/pgalloc.h +++ b/arch/openrisc/include/asm/pgalloc.h @@ -20,6 +20,9 @@ #include #include +#define __HAVE_ARCH_PTE_ALLOC_ONE_KERNEL +#include + extern int mem_init_done; #define pmd_populate_kernel(mm, pmd, pte) \ @@ -61,38 +64,8 @@ extern inline pgd_t *pgd_alloc(struct mm_struct *mm) } #endif -static inline void pgd_free(struct mm_struct *mm, pgd_t *pgd) -{ - free_page((unsigned long)pgd); -} - extern pte_t *pte_alloc_one_kernel(struct mm_struct *mm); -static inline struct page *pte_alloc_one(struct mm_struct *mm) -{ - struct page *pte; - pte = alloc_pages(GFP_KERNEL, 0); - if (!pte) - return NULL; - clear_page(page_address(pte)); - if (!pgtable_pte_page_ctor(pte)) { - __free_page(pte); - return NULL; - } - return pte; -} - -static inline void pte_free_kernel(struct mm_struct *mm, pte_t *pte) -{ - free_page((unsigned long)pte); -} - -static inline void pte_free(struct mm_struct *mm, struct page *pte) -{ - pgtable_pte_page_dtor(pte); - __free_page(pte); -} - #define __pte_free_tlb(tlb, pte, addr) \ do { \ pgtable_pte_page_dtor(pte); \ -- GitLab From 7278914ca1fdb54ba101c8617fb86953b31348cd Mon Sep 17 00:00:00 2001 From: Mike Rapoport Date: Thu, 6 Aug 2020 23:22:36 -0700 Subject: [PATCH 1407/1476] xtensa: switch to generic version of pte allocation xtensa clears PTEs during allocation of the page tables and pte_clear() sets the PTE to a non-zero value. Splitting ptes_clear() helper out of pte_alloc_one() and pte_alloc_one_kernel() allows reuse of base generic allocation methods (__pte_alloc_one() and __pte_alloc_one_kernel()) and the common GFP mask for page table allocations. The pte_free() and pte_free_kernel() implementations on xtensa are identical to the generic ones and can be dropped. [jcmvbkbc@gmail.com: xtensa: fix closing endif comment] Link: http://lkml.kernel.org/r/20200721024751.1257-1-jcmvbkbc@gmail.com Signed-off-by: Mike Rapoport Signed-off-by: Max Filippov Signed-off-by: Andrew Morton Reviewed-by: Pekka Enberg Cc: Abdul Haleem Cc: Andy Lutomirski Cc: Arnd Bergmann Cc: Christophe Leroy Cc: Joerg Roedel Cc: Joerg Roedel Cc: Max Filippov Cc: Peter Zijlstra (Intel) Cc: Satheesh Rajendran Cc: Stafford Horne Cc: Stephen Rothwell Cc: Steven Rostedt Cc: Geert Uytterhoeven Cc: Matthew Wilcox Link: http://lkml.kernel.org/r/20200627143453.31835-4-rppt@kernel.org Signed-off-by: Linus Torvalds --- arch/xtensa/include/asm/pgalloc.h | 41 ++++++++++++++----------------- 1 file changed, 19 insertions(+), 22 deletions(-) diff --git a/arch/xtensa/include/asm/pgalloc.h b/arch/xtensa/include/asm/pgalloc.h index 1d38f0e755ba9..5553abed69330 100644 --- a/arch/xtensa/include/asm/pgalloc.h +++ b/arch/xtensa/include/asm/pgalloc.h @@ -8,9 +8,14 @@ #ifndef _XTENSA_PGALLOC_H #define _XTENSA_PGALLOC_H +#ifdef CONFIG_MMU #include #include +#define __HAVE_ARCH_PTE_ALLOC_ONE_KERNEL +#define __HAVE_ARCH_PTE_ALLOC_ONE +#include + /* * Allocating and freeing a pmd is trivial: the 1-entry pmd is * inside the pgd, so has no extra memory associated with it. @@ -33,45 +38,37 @@ static inline void pgd_free(struct mm_struct *mm, pgd_t *pgd) free_page((unsigned long)pgd); } +static inline void ptes_clear(pte_t *ptep) +{ + int i; + + for (i = 0; i < PTRS_PER_PTE; i++) + pte_clear(NULL, 0, ptep + i); +} + static inline pte_t *pte_alloc_one_kernel(struct mm_struct *mm) { pte_t *ptep; - int i; - ptep = (pte_t *)__get_free_page(GFP_KERNEL); + ptep = (pte_t *)__pte_alloc_one_kernel(mm); if (!ptep) return NULL; - for (i = 0; i < 1024; i++) - pte_clear(NULL, 0, ptep + i); + ptes_clear(ptep); return ptep; } static inline pgtable_t pte_alloc_one(struct mm_struct *mm) { - pte_t *pte; struct page *page; - pte = pte_alloc_one_kernel(mm); - if (!pte) - return NULL; - page = virt_to_page(pte); - if (!pgtable_pte_page_ctor(page)) { - __free_page(page); + page = __pte_alloc_one(mm, GFP_PGTABLE_USER); + if (!page) return NULL; - } + ptes_clear(page_address(page)); return page; } -static inline void pte_free_kernel(struct mm_struct *mm, pte_t *pte) -{ - free_page((unsigned long)pte); -} - -static inline void pte_free(struct mm_struct *mm, pgtable_t pte) -{ - pgtable_pte_page_dtor(pte); - __free_page(pte); -} #define pmd_pgtable(pmd) pmd_page(pmd) +#endif /* CONFIG_MMU */ #endif /* _XTENSA_PGALLOC_H */ -- GitLab From 1355c31eeb7ea61a7f2f2937d17cd4e343a6b5af Mon Sep 17 00:00:00 2001 From: Mike Rapoport Date: Thu, 6 Aug 2020 23:22:39 -0700 Subject: [PATCH 1408/1476] asm-generic: pgalloc: provide generic pmd_alloc_one() and pmd_free_one() For most architectures that support >2 levels of page tables, pmd_alloc_one() is a wrapper for __get_free_pages(), sometimes with __GFP_ZERO and sometimes followed by memset(0) instead. More elaborate versions on arm64 and x86 account memory for the user page tables and call to pgtable_pmd_page_ctor() as the part of PMD page initialization. Move the arm64 version to include/asm-generic/pgalloc.h and use the generic version on several architectures. The pgtable_pmd_page_ctor() is a NOP when ARCH_ENABLE_SPLIT_PMD_PTLOCK is not enabled, so there is no functional change for most architectures except of the addition of __GFP_ACCOUNT for allocation of user page tables. The pmd_free() is a wrapper for free_page() in all the cases, so no functional change here. Signed-off-by: Mike Rapoport Signed-off-by: Andrew Morton Reviewed-by: Pekka Enberg Cc: Matthew Wilcox Cc: Abdul Haleem Cc: Andy Lutomirski Cc: Arnd Bergmann Cc: Christophe Leroy Cc: Joerg Roedel Cc: Joerg Roedel Cc: Max Filippov Cc: Peter Zijlstra (Intel) Cc: Satheesh Rajendran Cc: Stafford Horne Cc: Stephen Rothwell Cc: Steven Rostedt Cc: Geert Uytterhoeven Link: http://lkml.kernel.org/r/20200627143453.31835-5-rppt@kernel.org Signed-off-by: Linus Torvalds --- arch/alpha/include/asm/pgalloc.h | 15 +--------- arch/arm/include/asm/pgalloc.h | 11 ------- arch/arm64/include/asm/pgalloc.h | 27 +---------------- arch/ia64/include/asm/pgalloc.h | 10 ------- arch/mips/include/asm/pgalloc.h | 8 ++---- arch/parisc/include/asm/pgalloc.h | 11 ++----- arch/riscv/include/asm/pgalloc.h | 13 +-------- arch/sh/include/asm/pgalloc.h | 3 ++ arch/um/include/asm/pgalloc.h | 8 +----- arch/um/include/asm/pgtable-3level.h | 3 -- arch/um/kernel/mem.c | 12 -------- arch/x86/include/asm/pgalloc.h | 26 +---------------- include/asm-generic/pgalloc.h | 43 ++++++++++++++++++++++++++++ 13 files changed, 55 insertions(+), 135 deletions(-) diff --git a/arch/alpha/include/asm/pgalloc.h b/arch/alpha/include/asm/pgalloc.h index a1a29f60934c5..4834cd52e9d0b 100644 --- a/arch/alpha/include/asm/pgalloc.h +++ b/arch/alpha/include/asm/pgalloc.h @@ -5,7 +5,7 @@ #include #include -#include /* for pte_{alloc,free}_one */ +#include /* * Allocate and free page tables. The xxx_kernel() versions are @@ -40,17 +40,4 @@ pgd_free(struct mm_struct *mm, pgd_t *pgd) free_page((unsigned long)pgd); } -static inline pmd_t * -pmd_alloc_one(struct mm_struct *mm, unsigned long address) -{ - pmd_t *ret = (pmd_t *)__get_free_page(GFP_PGTABLE_USER); - return ret; -} - -static inline void -pmd_free(struct mm_struct *mm, pmd_t *pmd) -{ - free_page((unsigned long)pmd); -} - #endif /* _ALPHA_PGALLOC_H */ diff --git a/arch/arm/include/asm/pgalloc.h b/arch/arm/include/asm/pgalloc.h index 069da393110cc..c5bdfd404ea58 100644 --- a/arch/arm/include/asm/pgalloc.h +++ b/arch/arm/include/asm/pgalloc.h @@ -22,17 +22,6 @@ #ifdef CONFIG_ARM_LPAE -static inline pmd_t *pmd_alloc_one(struct mm_struct *mm, unsigned long addr) -{ - return (pmd_t *)get_zeroed_page(GFP_KERNEL); -} - -static inline void pmd_free(struct mm_struct *mm, pmd_t *pmd) -{ - BUG_ON((unsigned long)pmd & (PAGE_SIZE-1)); - free_page((unsigned long)pmd); -} - static inline void pud_populate(struct mm_struct *mm, pud_t *pud, pmd_t *pmd) { set_pud(pud, __pud(__pa(pmd) | PMD_TYPE_TABLE)); diff --git a/arch/arm64/include/asm/pgalloc.h b/arch/arm64/include/asm/pgalloc.h index 58e93583ddb64..7246d0a662e1a 100644 --- a/arch/arm64/include/asm/pgalloc.h +++ b/arch/arm64/include/asm/pgalloc.h @@ -13,37 +13,12 @@ #include #include -#include /* for pte_{alloc,free}_one */ +#include #define PGD_SIZE (PTRS_PER_PGD * sizeof(pgd_t)) #if CONFIG_PGTABLE_LEVELS > 2 -static inline pmd_t *pmd_alloc_one(struct mm_struct *mm, unsigned long addr) -{ - gfp_t gfp = GFP_PGTABLE_USER; - struct page *page; - - if (mm == &init_mm) - gfp = GFP_PGTABLE_KERNEL; - - page = alloc_page(gfp); - if (!page) - return NULL; - if (!pgtable_pmd_page_ctor(page)) { - __free_page(page); - return NULL; - } - return page_address(page); -} - -static inline void pmd_free(struct mm_struct *mm, pmd_t *pmdp) -{ - BUG_ON((unsigned long)pmdp & (PAGE_SIZE-1)); - pgtable_pmd_page_dtor(virt_to_page(pmdp)); - free_page((unsigned long)pmdp); -} - static inline void __pud_populate(pud_t *pudp, phys_addr_t pmdp, pudval_t prot) { set_pud(pudp, __pud(__phys_to_pud_val(pmdp) | prot)); diff --git a/arch/ia64/include/asm/pgalloc.h b/arch/ia64/include/asm/pgalloc.h index 2a30503450995..5da1fc76477b1 100644 --- a/arch/ia64/include/asm/pgalloc.h +++ b/arch/ia64/include/asm/pgalloc.h @@ -59,16 +59,6 @@ pud_populate(struct mm_struct *mm, pud_t * pud_entry, pmd_t * pmd) pud_val(*pud_entry) = __pa(pmd); } -static inline pmd_t *pmd_alloc_one(struct mm_struct *mm, unsigned long addr) -{ - return (pmd_t *)__get_free_page(GFP_KERNEL | __GFP_ZERO); -} - -static inline void pmd_free(struct mm_struct *mm, pmd_t *pmd) -{ - free_page((unsigned long)pmd); -} - #define __pmd_free_tlb(tlb, pmd, address) pmd_free((tlb)->mm, pmd) static inline void diff --git a/arch/mips/include/asm/pgalloc.h b/arch/mips/include/asm/pgalloc.h index fa77cb71f303f..eed1b3e8c642b 100644 --- a/arch/mips/include/asm/pgalloc.h +++ b/arch/mips/include/asm/pgalloc.h @@ -13,7 +13,8 @@ #include #include -#include /* for pte_{alloc,free}_one */ +#define __HAVE_ARCH_PMD_ALLOC_ONE +#include static inline void pmd_populate_kernel(struct mm_struct *mm, pmd_t *pmd, pte_t *pte) @@ -70,11 +71,6 @@ static inline pmd_t *pmd_alloc_one(struct mm_struct *mm, unsigned long address) return pmd; } -static inline void pmd_free(struct mm_struct *mm, pmd_t *pmd) -{ - free_pages((unsigned long)pmd, PMD_ORDER); -} - #define __pmd_free_tlb(tlb, x, addr) pmd_free((tlb)->mm, x) #endif diff --git a/arch/parisc/include/asm/pgalloc.h b/arch/parisc/include/asm/pgalloc.h index 9ac74da256b87..689766b914ed0 100644 --- a/arch/parisc/include/asm/pgalloc.h +++ b/arch/parisc/include/asm/pgalloc.h @@ -10,7 +10,8 @@ #include -#include /* for pte_{alloc,free}_one */ +#define __HAVE_ARCH_PMD_FREE +#include /* Allocate the top level pgd (page directory) * @@ -65,14 +66,6 @@ static inline void pud_populate(struct mm_struct *mm, pud_t *pud, pmd_t *pmd) (__u32)(__pa((unsigned long)pmd) >> PxD_VALUE_SHIFT))); } -static inline pmd_t *pmd_alloc_one(struct mm_struct *mm, unsigned long address) -{ - pmd_t *pmd = (pmd_t *)__get_free_pages(GFP_KERNEL, PMD_ORDER); - if (pmd) - memset(pmd, 0, PAGE_SIZE< #ifdef CONFIG_MMU -#include /* for pte_{alloc,free}_one */ +#include static inline void pmd_populate_kernel(struct mm_struct *mm, pmd_t *pmd, pte_t *pte) @@ -62,17 +62,6 @@ static inline void pgd_free(struct mm_struct *mm, pgd_t *pgd) #ifndef __PAGETABLE_PMD_FOLDED -static inline pmd_t *pmd_alloc_one(struct mm_struct *mm, unsigned long addr) -{ - return (pmd_t *)__get_free_page( - GFP_KERNEL | __GFP_RETRY_MAYFAIL | __GFP_ZERO); -} - -static inline void pmd_free(struct mm_struct *mm, pmd_t *pmd) -{ - free_page((unsigned long)pmd); -} - #define __pmd_free_tlb(tlb, pmd, addr) pmd_free((tlb)->mm, pmd) #endif /* __PAGETABLE_PMD_FOLDED */ diff --git a/arch/sh/include/asm/pgalloc.h b/arch/sh/include/asm/pgalloc.h index d770da3f8b6fb..811afb5671019 100644 --- a/arch/sh/include/asm/pgalloc.h +++ b/arch/sh/include/asm/pgalloc.h @@ -3,6 +3,9 @@ #define __ASM_SH_PGALLOC_H #include + +#define __HAVE_ARCH_PMD_ALLOC_ONE +#define __HAVE_ARCH_PMD_FREE #include extern pgd_t *pgd_alloc(struct mm_struct *); diff --git a/arch/um/include/asm/pgalloc.h b/arch/um/include/asm/pgalloc.h index 881e76da19384..bdde433dbdec5 100644 --- a/arch/um/include/asm/pgalloc.h +++ b/arch/um/include/asm/pgalloc.h @@ -10,7 +10,7 @@ #include -#include /* for pte_{alloc,free}_one */ +#include #define pmd_populate_kernel(mm, pmd, pte) \ set_pmd(pmd, __pmd(_PAGE_TABLE + (unsigned long) __pa(pte))) @@ -34,12 +34,6 @@ do { \ } while (0) #ifdef CONFIG_3_LEVEL_PGTABLES - -static inline void pmd_free(struct mm_struct *mm, pmd_t *pmd) -{ - free_page((unsigned long)pmd); -} - #define __pmd_free_tlb(tlb,x, address) tlb_remove_page((tlb),virt_to_page(x)) #endif diff --git a/arch/um/include/asm/pgtable-3level.h b/arch/um/include/asm/pgtable-3level.h index 36f452957cef8..7e6a4180db9d3 100644 --- a/arch/um/include/asm/pgtable-3level.h +++ b/arch/um/include/asm/pgtable-3level.h @@ -78,9 +78,6 @@ static inline void pgd_mkuptodate(pgd_t pgd) { pgd_val(pgd) &= ~_PAGE_NEWPAGE; } #define set_pmd(pmdptr, pmdval) (*(pmdptr) = (pmdval)) #endif -struct mm_struct; -extern pmd_t *pmd_alloc_one(struct mm_struct *mm, unsigned long address); - static inline void pud_clear (pud_t *pud) { set_pud(pud, __pud(_PAGE_NEWPAGE)); diff --git a/arch/um/kernel/mem.c b/arch/um/kernel/mem.c index c2ff76c8981e0..a4accb14cbd5a 100644 --- a/arch/um/kernel/mem.c +++ b/arch/um/kernel/mem.c @@ -201,18 +201,6 @@ void pgd_free(struct mm_struct *mm, pgd_t *pgd) free_page((unsigned long) pgd); } -#ifdef CONFIG_3_LEVEL_PGTABLES -pmd_t *pmd_alloc_one(struct mm_struct *mm, unsigned long address) -{ - pmd_t *pmd = (pmd_t *) __get_free_page(GFP_KERNEL); - - if (pmd) - memset(pmd, 0, PAGE_SIZE); - - return pmd; -} -#endif - void *uml_kmalloc(int size, int flags) { return kmalloc(size, flags); diff --git a/arch/x86/include/asm/pgalloc.h b/arch/x86/include/asm/pgalloc.h index 29aa7859bdee4..25feaa117c402 100644 --- a/arch/x86/include/asm/pgalloc.h +++ b/arch/x86/include/asm/pgalloc.h @@ -7,7 +7,7 @@ #include #define __HAVE_ARCH_PTE_ALLOC_ONE -#include /* for pte_{alloc,free}_one */ +#include static inline int __paravirt_pgd_alloc(struct mm_struct *mm) { return 0; } @@ -86,30 +86,6 @@ static inline void pmd_populate(struct mm_struct *mm, pmd_t *pmd, #define pmd_pgtable(pmd) pmd_page(pmd) #if CONFIG_PGTABLE_LEVELS > 2 -static inline pmd_t *pmd_alloc_one(struct mm_struct *mm, unsigned long addr) -{ - struct page *page; - gfp_t gfp = GFP_KERNEL_ACCOUNT | __GFP_ZERO; - - if (mm == &init_mm) - gfp &= ~__GFP_ACCOUNT; - page = alloc_pages(gfp, 0); - if (!page) - return NULL; - if (!pgtable_pmd_page_ctor(page)) { - __free_pages(page, 0); - return NULL; - } - return (pmd_t *)page_address(page); -} - -static inline void pmd_free(struct mm_struct *mm, pmd_t *pmd) -{ - BUG_ON((unsigned long)pmd & (PAGE_SIZE-1)); - pgtable_pmd_page_dtor(virt_to_page(pmd)); - free_page((unsigned long)pmd); -} - extern void ___pmd_free_tlb(struct mmu_gather *tlb, pmd_t *pmd); static inline void __pmd_free_tlb(struct mmu_gather *tlb, pmd_t *pmd, diff --git a/include/asm-generic/pgalloc.h b/include/asm-generic/pgalloc.h index 73f7421413cbd..1bc027891a003 100644 --- a/include/asm-generic/pgalloc.h +++ b/include/asm-generic/pgalloc.h @@ -102,6 +102,49 @@ static inline void pte_free(struct mm_struct *mm, struct page *pte_page) __free_page(pte_page); } + +#if CONFIG_PGTABLE_LEVELS > 2 + +#ifndef __HAVE_ARCH_PMD_ALLOC_ONE +/** + * pmd_alloc_one - allocate a page for PMD-level page table + * @mm: the mm_struct of the current context + * + * Allocates a page and runs the pgtable_pmd_page_ctor(). + * Allocations use %GFP_PGTABLE_USER in user context and + * %GFP_PGTABLE_KERNEL in kernel context. + * + * Return: pointer to the allocated memory or %NULL on error + */ +static inline pmd_t *pmd_alloc_one(struct mm_struct *mm, unsigned long addr) +{ + struct page *page; + gfp_t gfp = GFP_PGTABLE_USER; + + if (mm == &init_mm) + gfp = GFP_PGTABLE_KERNEL; + page = alloc_pages(gfp, 0); + if (!page) + return NULL; + if (!pgtable_pmd_page_ctor(page)) { + __free_pages(page, 0); + return NULL; + } + return (pmd_t *)page_address(page); +} +#endif + +#ifndef __HAVE_ARCH_PMD_FREE +static inline void pmd_free(struct mm_struct *mm, pmd_t *pmd) +{ + BUG_ON((unsigned long)pmd & (PAGE_SIZE-1)); + pgtable_pmd_page_dtor(virt_to_page(pmd)); + free_page((unsigned long)pmd); +} +#endif + +#endif /* CONFIG_PGTABLE_LEVELS > 2 */ + #endif /* CONFIG_MMU */ #endif /* __ASM_GENERIC_PGALLOC_H */ -- GitLab From d9e8b929670b4f79e07cdbcb0fb4f162a561d5c6 Mon Sep 17 00:00:00 2001 From: Mike Rapoport Date: Thu, 6 Aug 2020 23:22:44 -0700 Subject: [PATCH 1409/1476] asm-generic: pgalloc: provide generic pud_alloc_one() and pud_free_one() Several architectures define pud_alloc_one() as a wrapper for __get_free_page() and pud_free() as a wrapper for free_page(). Provide a generic implementation in asm-generic/pgalloc.h and use it where appropriate. Signed-off-by: Mike Rapoport Signed-off-by: Andrew Morton Reviewed-by: Pekka Enberg Cc: Abdul Haleem Cc: Andy Lutomirski Cc: Arnd Bergmann Cc: Christophe Leroy Cc: Joerg Roedel Cc: Joerg Roedel Cc: Max Filippov Cc: Peter Zijlstra (Intel) Cc: Satheesh Rajendran Cc: Stafford Horne Cc: Stephen Rothwell Cc: Steven Rostedt Cc: Geert Uytterhoeven Cc: Matthew Wilcox Link: http://lkml.kernel.org/r/20200627143453.31835-6-rppt@kernel.org Signed-off-by: Linus Torvalds --- arch/arm64/include/asm/pgalloc.h | 11 ----------- arch/ia64/include/asm/pgalloc.h | 9 --------- arch/mips/include/asm/pgalloc.h | 6 +----- arch/x86/include/asm/pgalloc.h | 15 --------------- include/asm-generic/pgalloc.h | 30 ++++++++++++++++++++++++++++++ 5 files changed, 31 insertions(+), 40 deletions(-) diff --git a/arch/arm64/include/asm/pgalloc.h b/arch/arm64/include/asm/pgalloc.h index 7246d0a662e1a..0965945b595d0 100644 --- a/arch/arm64/include/asm/pgalloc.h +++ b/arch/arm64/include/asm/pgalloc.h @@ -37,17 +37,6 @@ static inline void __pud_populate(pud_t *pudp, phys_addr_t pmdp, pudval_t prot) #if CONFIG_PGTABLE_LEVELS > 3 -static inline pud_t *pud_alloc_one(struct mm_struct *mm, unsigned long addr) -{ - return (pud_t *)__get_free_page(GFP_PGTABLE_USER); -} - -static inline void pud_free(struct mm_struct *mm, pud_t *pudp) -{ - BUG_ON((unsigned long)pudp & (PAGE_SIZE-1)); - free_page((unsigned long)pudp); -} - static inline void __p4d_populate(p4d_t *p4dp, phys_addr_t pudp, p4dval_t prot) { set_p4d(p4dp, __p4d(__phys_to_p4d_val(pudp) | prot)); diff --git a/arch/ia64/include/asm/pgalloc.h b/arch/ia64/include/asm/pgalloc.h index 5da1fc76477b1..06f80358e20fd 100644 --- a/arch/ia64/include/asm/pgalloc.h +++ b/arch/ia64/include/asm/pgalloc.h @@ -41,15 +41,6 @@ p4d_populate(struct mm_struct *mm, p4d_t * p4d_entry, pud_t * pud) p4d_val(*p4d_entry) = __pa(pud); } -static inline pud_t *pud_alloc_one(struct mm_struct *mm, unsigned long addr) -{ - return (pud_t *)__get_free_page(GFP_KERNEL | __GFP_ZERO); -} - -static inline void pud_free(struct mm_struct *mm, pud_t *pud) -{ - free_page((unsigned long)pud); -} #define __pud_free_tlb(tlb, pud, address) pud_free((tlb)->mm, pud) #endif /* CONFIG_PGTABLE_LEVELS == 4 */ diff --git a/arch/mips/include/asm/pgalloc.h b/arch/mips/include/asm/pgalloc.h index eed1b3e8c642b..e5a840910ce07 100644 --- a/arch/mips/include/asm/pgalloc.h +++ b/arch/mips/include/asm/pgalloc.h @@ -14,6 +14,7 @@ #include #define __HAVE_ARCH_PMD_ALLOC_ONE +#define __HAVE_ARCH_PUD_ALLOC_ONE #include static inline void pmd_populate_kernel(struct mm_struct *mm, pmd_t *pmd, @@ -87,11 +88,6 @@ static inline pud_t *pud_alloc_one(struct mm_struct *mm, unsigned long address) return pud; } -static inline void pud_free(struct mm_struct *mm, pud_t *pud) -{ - free_pages((unsigned long)pud, PUD_ORDER); -} - static inline void p4d_populate(struct mm_struct *mm, p4d_t *p4d, pud_t *pud) { set_p4d(p4d, __p4d((unsigned long)pud)); diff --git a/arch/x86/include/asm/pgalloc.h b/arch/x86/include/asm/pgalloc.h index 25feaa117c402..3d1085a143470 100644 --- a/arch/x86/include/asm/pgalloc.h +++ b/arch/x86/include/asm/pgalloc.h @@ -123,21 +123,6 @@ static inline void p4d_populate_safe(struct mm_struct *mm, p4d_t *p4d, pud_t *pu set_p4d_safe(p4d, __p4d(_PAGE_TABLE | __pa(pud))); } -static inline pud_t *pud_alloc_one(struct mm_struct *mm, unsigned long addr) -{ - gfp_t gfp = GFP_KERNEL_ACCOUNT; - - if (mm == &init_mm) - gfp &= ~__GFP_ACCOUNT; - return (pud_t *)get_zeroed_page(gfp); -} - -static inline void pud_free(struct mm_struct *mm, pud_t *pud) -{ - BUG_ON((unsigned long)pud & (PAGE_SIZE-1)); - free_page((unsigned long)pud); -} - extern void ___pud_free_tlb(struct mmu_gather *tlb, pud_t *pud); static inline void __pud_free_tlb(struct mmu_gather *tlb, pud_t *pud, diff --git a/include/asm-generic/pgalloc.h b/include/asm-generic/pgalloc.h index 1bc027891a003..d361574aaadff 100644 --- a/include/asm-generic/pgalloc.h +++ b/include/asm-generic/pgalloc.h @@ -145,6 +145,36 @@ static inline void pmd_free(struct mm_struct *mm, pmd_t *pmd) #endif /* CONFIG_PGTABLE_LEVELS > 2 */ +#if CONFIG_PGTABLE_LEVELS > 3 + +#ifndef __HAVE_ARCH_PUD_FREE +/** + * pud_alloc_one - allocate a page for PUD-level page table + * @mm: the mm_struct of the current context + * + * Allocates a page using %GFP_PGTABLE_USER for user context and + * %GFP_PGTABLE_KERNEL for kernel context. + * + * Return: pointer to the allocated memory or %NULL on error + */ +static inline pud_t *pud_alloc_one(struct mm_struct *mm, unsigned long addr) +{ + gfp_t gfp = GFP_PGTABLE_USER; + + if (mm == &init_mm) + gfp = GFP_PGTABLE_KERNEL; + return (pud_t *)get_zeroed_page(gfp); +} +#endif + +static inline void pud_free(struct mm_struct *mm, pud_t *pud) +{ + BUG_ON((unsigned long)pud & (PAGE_SIZE-1)); + free_page((unsigned long)pud); +} + +#endif /* CONFIG_PGTABLE_LEVELS > 3 */ + #endif /* CONFIG_MMU */ #endif /* __ASM_GENERIC_PGALLOC_H */ -- GitLab From f9cb654cb550b7b87e8608b14fc3eca432429ffe Mon Sep 17 00:00:00 2001 From: Mike Rapoport Date: Thu, 6 Aug 2020 23:22:47 -0700 Subject: [PATCH 1410/1476] asm-generic: pgalloc: provide generic pgd_free() Most architectures define pgd_free() as a wrapper for free_page(). Provide a generic version in asm-generic/pgalloc.h and enable its use for most architectures. Signed-off-by: Mike Rapoport Signed-off-by: Andrew Morton Reviewed-by: Pekka Enberg Acked-by: Geert Uytterhoeven [m68k] Cc: Abdul Haleem Cc: Andy Lutomirski Cc: Arnd Bergmann Cc: Christophe Leroy Cc: Joerg Roedel Cc: Joerg Roedel Cc: Max Filippov Cc: Peter Zijlstra (Intel) Cc: Satheesh Rajendran Cc: Stafford Horne Cc: Stephen Rothwell Cc: Steven Rostedt Cc: Matthew Wilcox Link: http://lkml.kernel.org/r/20200627143453.31835-7-rppt@kernel.org Signed-off-by: Linus Torvalds --- arch/alpha/include/asm/pgalloc.h | 6 ------ arch/arm/include/asm/pgalloc.h | 1 + arch/arm64/include/asm/pgalloc.h | 1 + arch/csky/include/asm/pgalloc.h | 7 +------ arch/hexagon/include/asm/pgalloc.h | 7 +------ arch/ia64/include/asm/pgalloc.h | 5 ----- arch/m68k/include/asm/sun3_pgalloc.h | 7 +------ arch/microblaze/include/asm/pgalloc.h | 6 ------ arch/mips/include/asm/pgalloc.h | 5 ----- arch/nds32/mm/mm-nds32.c | 2 ++ arch/nios2/include/asm/pgalloc.h | 7 +------ arch/parisc/include/asm/pgalloc.h | 1 + arch/riscv/include/asm/pgalloc.h | 5 ----- arch/sh/include/asm/pgalloc.h | 1 + arch/um/include/asm/pgalloc.h | 1 - arch/um/kernel/mem.c | 5 ----- arch/x86/include/asm/pgalloc.h | 1 + arch/xtensa/include/asm/pgalloc.h | 5 ----- include/asm-generic/pgalloc.h | 7 +++++++ 19 files changed, 18 insertions(+), 62 deletions(-) diff --git a/arch/alpha/include/asm/pgalloc.h b/arch/alpha/include/asm/pgalloc.h index 4834cd52e9d0b..9c6a24fe493d5 100644 --- a/arch/alpha/include/asm/pgalloc.h +++ b/arch/alpha/include/asm/pgalloc.h @@ -34,10 +34,4 @@ pud_populate(struct mm_struct *mm, pud_t *pud, pmd_t *pmd) extern pgd_t *pgd_alloc(struct mm_struct *mm); -static inline void -pgd_free(struct mm_struct *mm, pgd_t *pgd) -{ - free_page((unsigned long)pgd); -} - #endif /* _ALPHA_PGALLOC_H */ diff --git a/arch/arm/include/asm/pgalloc.h b/arch/arm/include/asm/pgalloc.h index c5bdfd404ea58..15f4674715f84 100644 --- a/arch/arm/include/asm/pgalloc.h +++ b/arch/arm/include/asm/pgalloc.h @@ -65,6 +65,7 @@ static inline void clean_pte_table(pte_t *pte) #define __HAVE_ARCH_PTE_ALLOC_ONE_KERNEL #define __HAVE_ARCH_PTE_ALLOC_ONE +#define __HAVE_ARCH_PGD_FREE #include static inline pte_t * diff --git a/arch/arm64/include/asm/pgalloc.h b/arch/arm64/include/asm/pgalloc.h index 0965945b595d0..3c6a7f5988b12 100644 --- a/arch/arm64/include/asm/pgalloc.h +++ b/arch/arm64/include/asm/pgalloc.h @@ -13,6 +13,7 @@ #include #include +#define __HAVE_ARCH_PGD_FREE #include #define PGD_SIZE (PTRS_PER_PGD * sizeof(pgd_t)) diff --git a/arch/csky/include/asm/pgalloc.h b/arch/csky/include/asm/pgalloc.h index c7c1ed27e3480..d58d8146b729c 100644 --- a/arch/csky/include/asm/pgalloc.h +++ b/arch/csky/include/asm/pgalloc.h @@ -9,7 +9,7 @@ #include #define __HAVE_ARCH_PTE_ALLOC_ONE_KERNEL -#include /* for pte_{alloc,free}_one */ +#include static inline void pmd_populate_kernel(struct mm_struct *mm, pmd_t *pmd, pte_t *pte) @@ -42,11 +42,6 @@ static inline pte_t *pte_alloc_one_kernel(struct mm_struct *mm) return pte; } -static inline void pgd_free(struct mm_struct *mm, pgd_t *pgd) -{ - free_pages((unsigned long)pgd, PGD_ORDER); -} - static inline pgd_t *pgd_alloc(struct mm_struct *mm) { pgd_t *ret; diff --git a/arch/hexagon/include/asm/pgalloc.h b/arch/hexagon/include/asm/pgalloc.h index cc9be514a676a..f0c47e6a7427d 100644 --- a/arch/hexagon/include/asm/pgalloc.h +++ b/arch/hexagon/include/asm/pgalloc.h @@ -11,7 +11,7 @@ #include #include -#include /* for pte_{alloc,free}_one */ +#include extern unsigned long long kmap_generation; @@ -41,11 +41,6 @@ static inline pgd_t *pgd_alloc(struct mm_struct *mm) return pgd; } -static inline void pgd_free(struct mm_struct *mm, pgd_t *pgd) -{ - free_page((unsigned long) pgd); -} - static inline void pmd_populate(struct mm_struct *mm, pmd_t *pmd, pgtable_t pte) { diff --git a/arch/ia64/include/asm/pgalloc.h b/arch/ia64/include/asm/pgalloc.h index 06f80358e20fd..9601cfe83c940 100644 --- a/arch/ia64/include/asm/pgalloc.h +++ b/arch/ia64/include/asm/pgalloc.h @@ -29,11 +29,6 @@ static inline pgd_t *pgd_alloc(struct mm_struct *mm) return (pgd_t *)__get_free_page(GFP_KERNEL | __GFP_ZERO); } -static inline void pgd_free(struct mm_struct *mm, pgd_t *pgd) -{ - free_page((unsigned long)pgd); -} - #if CONFIG_PGTABLE_LEVELS == 4 static inline void p4d_populate(struct mm_struct *mm, p4d_t * p4d_entry, pud_t * pud) diff --git a/arch/m68k/include/asm/sun3_pgalloc.h b/arch/m68k/include/asm/sun3_pgalloc.h index 11b95dadf7c05..000f64869b919 100644 --- a/arch/m68k/include/asm/sun3_pgalloc.h +++ b/arch/m68k/include/asm/sun3_pgalloc.h @@ -13,7 +13,7 @@ #include -#include /* for pte_{alloc,free}_one */ +#include extern const char bad_pmd_string[]; @@ -40,11 +40,6 @@ static inline void pmd_populate(struct mm_struct *mm, pmd_t *pmd, pgtable_t page */ #define pmd_free(mm, x) do { } while (0) -static inline void pgd_free(struct mm_struct *mm, pgd_t *pgd) -{ - free_page((unsigned long) pgd); -} - static inline pgd_t * pgd_alloc(struct mm_struct *mm) { pgd_t *new_pgd; diff --git a/arch/microblaze/include/asm/pgalloc.h b/arch/microblaze/include/asm/pgalloc.h index ebb6b7939bb82..8839ce00ea051 100644 --- a/arch/microblaze/include/asm/pgalloc.h +++ b/arch/microblaze/include/asm/pgalloc.h @@ -28,12 +28,6 @@ static inline pgd_t *get_pgd(void) return (pgd_t *)__get_free_pages(GFP_KERNEL|__GFP_ZERO, 0); } -static inline void free_pgd(pgd_t *pgd) -{ - free_page((unsigned long)pgd); -} - -#define pgd_free(mm, pgd) free_pgd(pgd) #define pgd_alloc(mm) get_pgd() #define pmd_pgtable(pmd) pmd_page(pmd) diff --git a/arch/mips/include/asm/pgalloc.h b/arch/mips/include/asm/pgalloc.h index e5a840910ce07..8b18424b31208 100644 --- a/arch/mips/include/asm/pgalloc.h +++ b/arch/mips/include/asm/pgalloc.h @@ -49,11 +49,6 @@ static inline void pud_populate(struct mm_struct *mm, pud_t *pud, pmd_t *pmd) extern void pgd_init(unsigned long page); extern pgd_t *pgd_alloc(struct mm_struct *mm); -static inline void pgd_free(struct mm_struct *mm, pgd_t *pgd) -{ - free_pages((unsigned long)pgd, PGD_ORDER); -} - #define __pte_free_tlb(tlb,pte,address) \ do { \ pgtable_pte_page_dtor(pte); \ diff --git a/arch/nds32/mm/mm-nds32.c b/arch/nds32/mm/mm-nds32.c index 8503bee882d18..55bec50ccc033 100644 --- a/arch/nds32/mm/mm-nds32.c +++ b/arch/nds32/mm/mm-nds32.c @@ -2,6 +2,8 @@ // Copyright (C) 2005-2017 Andes Technology Corporation #include + +#define __HAVE_ARCH_PGD_FREE #include #define FIRST_KERNEL_PGD_NR (USER_PTRS_PER_PGD) diff --git a/arch/nios2/include/asm/pgalloc.h b/arch/nios2/include/asm/pgalloc.h index 0b146d773c85d..e6600d2a5ae05 100644 --- a/arch/nios2/include/asm/pgalloc.h +++ b/arch/nios2/include/asm/pgalloc.h @@ -12,7 +12,7 @@ #include -#include /* for pte_{alloc,free}_one */ +#include static inline void pmd_populate_kernel(struct mm_struct *mm, pmd_t *pmd, pte_t *pte) @@ -34,11 +34,6 @@ extern void pmd_init(unsigned long page, unsigned long pagetable); extern pgd_t *pgd_alloc(struct mm_struct *mm); -static inline void pgd_free(struct mm_struct *mm, pgd_t *pgd) -{ - free_pages((unsigned long)pgd, PGD_ORDER); -} - #define __pte_free_tlb(tlb, pte, addr) \ do { \ pgtable_pte_page_dtor(pte); \ diff --git a/arch/parisc/include/asm/pgalloc.h b/arch/parisc/include/asm/pgalloc.h index 689766b914ed0..cc7ecc2ef55d6 100644 --- a/arch/parisc/include/asm/pgalloc.h +++ b/arch/parisc/include/asm/pgalloc.h @@ -11,6 +11,7 @@ #include #define __HAVE_ARCH_PMD_FREE +#define __HAVE_ARCH_PGD_FREE #include /* Allocate the top level pgd (page directory) diff --git a/arch/riscv/include/asm/pgalloc.h b/arch/riscv/include/asm/pgalloc.h index 8d3135f05b8e2..23b1544e0ca5b 100644 --- a/arch/riscv/include/asm/pgalloc.h +++ b/arch/riscv/include/asm/pgalloc.h @@ -55,11 +55,6 @@ static inline pgd_t *pgd_alloc(struct mm_struct *mm) return pgd; } -static inline void pgd_free(struct mm_struct *mm, pgd_t *pgd) -{ - free_page((unsigned long)pgd); -} - #ifndef __PAGETABLE_PMD_FOLDED #define __pmd_free_tlb(tlb, pmd, addr) pmd_free((tlb)->mm, pmd) diff --git a/arch/sh/include/asm/pgalloc.h b/arch/sh/include/asm/pgalloc.h index 811afb5671019..0e6b0be25e33b 100644 --- a/arch/sh/include/asm/pgalloc.h +++ b/arch/sh/include/asm/pgalloc.h @@ -6,6 +6,7 @@ #define __HAVE_ARCH_PMD_ALLOC_ONE #define __HAVE_ARCH_PMD_FREE +#define __HAVE_ARCH_PGD_FREE #include extern pgd_t *pgd_alloc(struct mm_struct *); diff --git a/arch/um/include/asm/pgalloc.h b/arch/um/include/asm/pgalloc.h index bdde433dbdec5..5393e13e07e0a 100644 --- a/arch/um/include/asm/pgalloc.h +++ b/arch/um/include/asm/pgalloc.h @@ -25,7 +25,6 @@ * Allocate and free page tables. */ extern pgd_t *pgd_alloc(struct mm_struct *); -extern void pgd_free(struct mm_struct *mm, pgd_t *pgd); #define __pte_free_tlb(tlb,pte, address) \ do { \ diff --git a/arch/um/kernel/mem.c b/arch/um/kernel/mem.c index a4accb14cbd5a..9242dc91d7519 100644 --- a/arch/um/kernel/mem.c +++ b/arch/um/kernel/mem.c @@ -196,11 +196,6 @@ pgd_t *pgd_alloc(struct mm_struct *mm) return pgd; } -void pgd_free(struct mm_struct *mm, pgd_t *pgd) -{ - free_page((unsigned long) pgd); -} - void *uml_kmalloc(int size, int flags) { return kmalloc(size, flags); diff --git a/arch/x86/include/asm/pgalloc.h b/arch/x86/include/asm/pgalloc.h index 3d1085a143470..62ad61d6fefcb 100644 --- a/arch/x86/include/asm/pgalloc.h +++ b/arch/x86/include/asm/pgalloc.h @@ -7,6 +7,7 @@ #include #define __HAVE_ARCH_PTE_ALLOC_ONE +#define __HAVE_ARCH_PGD_FREE #include static inline int __paravirt_pgd_alloc(struct mm_struct *mm) { return 0; } diff --git a/arch/xtensa/include/asm/pgalloc.h b/arch/xtensa/include/asm/pgalloc.h index 5553abed69330..d3a22da4d2c91 100644 --- a/arch/xtensa/include/asm/pgalloc.h +++ b/arch/xtensa/include/asm/pgalloc.h @@ -33,11 +33,6 @@ pgd_alloc(struct mm_struct *mm) return (pgd_t*) __get_free_pages(GFP_KERNEL | __GFP_ZERO, PGD_ORDER); } -static inline void pgd_free(struct mm_struct *mm, pgd_t *pgd) -{ - free_page((unsigned long)pgd); -} - static inline void ptes_clear(pte_t *ptep) { int i; diff --git a/include/asm-generic/pgalloc.h b/include/asm-generic/pgalloc.h index d361574aaadff..6f44810921aa1 100644 --- a/include/asm-generic/pgalloc.h +++ b/include/asm-generic/pgalloc.h @@ -175,6 +175,13 @@ static inline void pud_free(struct mm_struct *mm, pud_t *pud) #endif /* CONFIG_PGTABLE_LEVELS > 3 */ +#ifndef __HAVE_ARCH_PGD_FREE +static inline void pgd_free(struct mm_struct *mm, pgd_t *pgd) +{ + free_page((unsigned long)pgd); +} +#endif + #endif /* CONFIG_MMU */ #endif /* __ASM_GENERIC_PGALLOC_H */ -- GitLab From ab05eabfa18a6a537a73408642177e4a803317dc Mon Sep 17 00:00:00 2001 From: Mike Rapoport Date: Thu, 6 Aug 2020 23:22:51 -0700 Subject: [PATCH 1411/1476] mm: move lib/ioremap.c to mm/ The functionality in lib/ioremap.c deals with pagetables, vmalloc and caches, so it naturally belongs to mm/ Moving it there will also allow declaring p?d_alloc_track functions in an header file inside mm/ rather than having those declarations in include/linux/mm.h Suggested-by: Andrew Morton Signed-off-by: Mike Rapoport Signed-off-by: Andrew Morton Reviewed-by: Pekka Enberg Cc: Abdul Haleem Cc: Andy Lutomirski Cc: Arnd Bergmann Cc: Christophe Leroy Cc: Joerg Roedel Cc: Joerg Roedel Cc: Max Filippov Cc: Peter Zijlstra (Intel) Cc: Satheesh Rajendran Cc: Stafford Horne Cc: Stephen Rothwell Cc: Steven Rostedt Cc: Geert Uytterhoeven Cc: Matthew Wilcox Link: http://lkml.kernel.org/r/20200627143453.31835-8-rppt@kernel.org Signed-off-by: Linus Torvalds --- lib/Makefile | 1 - mm/Makefile | 2 +- {lib => mm}/ioremap.c | 0 3 files changed, 1 insertion(+), 2 deletions(-) rename {lib => mm}/ioremap.c (100%) diff --git a/lib/Makefile b/lib/Makefile index 435f7f13b8aa2..f39962104036b 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -37,7 +37,6 @@ lib-y := ctype.o string.o vsprintf.o cmdline.o \ nmi_backtrace.o nodemask.o win_minmax.o memcat_p.o lib-$(CONFIG_PRINTK) += dump_stack.o -lib-$(CONFIG_MMU) += ioremap.o lib-$(CONFIG_SMP) += cpumask.o lib-y += kobject.o klist.o diff --git a/mm/Makefile b/mm/Makefile index 6e9d46b2efc9a..d5649f1c12c01 100644 --- a/mm/Makefile +++ b/mm/Makefile @@ -38,7 +38,7 @@ mmu-y := nommu.o mmu-$(CONFIG_MMU) := highmem.o memory.o mincore.o \ mlock.o mmap.o mmu_gather.o mprotect.o mremap.o \ msync.o page_vma_mapped.o pagewalk.o \ - pgtable-generic.o rmap.o vmalloc.o + pgtable-generic.o rmap.o vmalloc.o ioremap.o ifdef CONFIG_CROSS_MEMORY_ATTACH diff --git a/lib/ioremap.c b/mm/ioremap.c similarity index 100% rename from lib/ioremap.c rename to mm/ioremap.c -- GitLab From 2a681cfa5bb41e78e7bfafbb748b581374ce9b1d Mon Sep 17 00:00:00 2001 From: Joerg Roedel Date: Thu, 6 Aug 2020 23:22:55 -0700 Subject: [PATCH 1412/1476] mm: move p?d_alloc_track to separate header file The functions are only used in two source files, so there is no need for them to be in the global header. Move them to the new header and include it only where needed. Signed-off-by: Joerg Roedel Signed-off-by: Andrew Morton Reviewed-by: Pekka Enberg Cc: Peter Zijlstra (Intel) Cc: Andy Lutomirski Cc: Abdul Haleem Cc: Satheesh Rajendran Cc: Stephen Rothwell Cc: Steven Rostedt (VMware) Cc: Mike Rapoport Cc: Christophe Leroy Cc: Arnd Bergmann Cc: Max Filippov Cc: Stafford Horne Cc: Geert Uytterhoeven Cc: Matthew Wilcox Link: http://lkml.kernel.org/r/20200609120533.25867-1-joro@8bytes.org Signed-off-by: Linus Torvalds --- include/linux/mm.h | 45 ---------------------------------------- mm/ioremap.c | 2 ++ mm/pgalloc-track.h | 51 ++++++++++++++++++++++++++++++++++++++++++++++ mm/vmalloc.c | 1 + 4 files changed, 54 insertions(+), 45 deletions(-) create mode 100644 mm/pgalloc-track.h diff --git a/include/linux/mm.h b/include/linux/mm.h index 303a47a9769d6..2830f1c0fdc32 100644 --- a/include/linux/mm.h +++ b/include/linux/mm.h @@ -2103,51 +2103,11 @@ static inline pud_t *pud_alloc(struct mm_struct *mm, p4d_t *p4d, NULL : pud_offset(p4d, address); } -static inline p4d_t *p4d_alloc_track(struct mm_struct *mm, pgd_t *pgd, - unsigned long address, - pgtbl_mod_mask *mod_mask) - -{ - if (unlikely(pgd_none(*pgd))) { - if (__p4d_alloc(mm, pgd, address)) - return NULL; - *mod_mask |= PGTBL_PGD_MODIFIED; - } - - return p4d_offset(pgd, address); -} - -static inline pud_t *pud_alloc_track(struct mm_struct *mm, p4d_t *p4d, - unsigned long address, - pgtbl_mod_mask *mod_mask) -{ - if (unlikely(p4d_none(*p4d))) { - if (__pud_alloc(mm, p4d, address)) - return NULL; - *mod_mask |= PGTBL_P4D_MODIFIED; - } - - return pud_offset(p4d, address); -} - static inline pmd_t *pmd_alloc(struct mm_struct *mm, pud_t *pud, unsigned long address) { return (unlikely(pud_none(*pud)) && __pmd_alloc(mm, pud, address))? NULL: pmd_offset(pud, address); } - -static inline pmd_t *pmd_alloc_track(struct mm_struct *mm, pud_t *pud, - unsigned long address, - pgtbl_mod_mask *mod_mask) -{ - if (unlikely(pud_none(*pud))) { - if (__pmd_alloc(mm, pud, address)) - return NULL; - *mod_mask |= PGTBL_PUD_MODIFIED; - } - - return pmd_offset(pud, address); -} #endif /* CONFIG_MMU */ #if USE_SPLIT_PTE_PTLOCKS @@ -2263,11 +2223,6 @@ static inline void pgtable_pte_page_dtor(struct page *page) ((unlikely(pmd_none(*(pmd))) && __pte_alloc_kernel(pmd))? \ NULL: pte_offset_kernel(pmd, address)) -#define pte_alloc_kernel_track(pmd, address, mask) \ - ((unlikely(pmd_none(*(pmd))) && \ - (__pte_alloc_kernel(pmd) || ({*(mask)|=PGTBL_PMD_MODIFIED;0;})))?\ - NULL: pte_offset_kernel(pmd, address)) - #if USE_SPLIT_PMD_PTLOCKS static struct page *pmd_to_page(pmd_t *pmd) diff --git a/mm/ioremap.c b/mm/ioremap.c index 5ee3526f71b8e..5fa1ab41d1526 100644 --- a/mm/ioremap.c +++ b/mm/ioremap.c @@ -13,6 +13,8 @@ #include #include +#include "pgalloc-track.h" + #ifdef CONFIG_HAVE_ARCH_HUGE_VMAP static int __read_mostly ioremap_p4d_capable; static int __read_mostly ioremap_pud_capable; diff --git a/mm/pgalloc-track.h b/mm/pgalloc-track.h new file mode 100644 index 0000000000000..1dcc865029a2d --- /dev/null +++ b/mm/pgalloc-track.h @@ -0,0 +1,51 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _LINUX_PGALLLC_TRACK_H +#define _LINUX_PGALLLC_TRACK_H + +#if defined(CONFIG_MMU) +static inline p4d_t *p4d_alloc_track(struct mm_struct *mm, pgd_t *pgd, + unsigned long address, + pgtbl_mod_mask *mod_mask) +{ + if (unlikely(pgd_none(*pgd))) { + if (__p4d_alloc(mm, pgd, address)) + return NULL; + *mod_mask |= PGTBL_PGD_MODIFIED; + } + + return p4d_offset(pgd, address); +} + +static inline pud_t *pud_alloc_track(struct mm_struct *mm, p4d_t *p4d, + unsigned long address, + pgtbl_mod_mask *mod_mask) +{ + if (unlikely(p4d_none(*p4d))) { + if (__pud_alloc(mm, p4d, address)) + return NULL; + *mod_mask |= PGTBL_P4D_MODIFIED; + } + + return pud_offset(p4d, address); +} + +static inline pmd_t *pmd_alloc_track(struct mm_struct *mm, pud_t *pud, + unsigned long address, + pgtbl_mod_mask *mod_mask) +{ + if (unlikely(pud_none(*pud))) { + if (__pmd_alloc(mm, pud, address)) + return NULL; + *mod_mask |= PGTBL_PUD_MODIFIED; + } + + return pmd_offset(pud, address); +} +#endif /* CONFIG_MMU */ + +#define pte_alloc_kernel_track(pmd, address, mask) \ + ((unlikely(pmd_none(*(pmd))) && \ + (__pte_alloc_kernel(pmd) || ({*(mask)|=PGTBL_PMD_MODIFIED;0;})))?\ + NULL: pte_offset_kernel(pmd, address)) + +#endif /* _LINUX_PGALLLC_TRACK_H */ diff --git a/mm/vmalloc.c b/mm/vmalloc.c index 5a2b55c8dd9a7..5be3cf3b59dea 100644 --- a/mm/vmalloc.c +++ b/mm/vmalloc.c @@ -41,6 +41,7 @@ #include #include "internal.h" +#include "pgalloc-track.h" bool is_vmalloc_addr(const void *x) { -- GitLab From 7bba8f0ea428f8a904c022b86e584009a2f87357 Mon Sep 17 00:00:00 2001 From: Zhen Lei Date: Thu, 6 Aug 2020 23:22:59 -0700 Subject: [PATCH 1413/1476] mm/mmap: optimize a branch judgment in ksys_mmap_pgoff() Look at the pseudo code below. It's very clear that, the judgement "!is_file_hugepages(file)" at 3) is duplicated to the one at 1), we can use "else if" to avoid it. And the assignment "retval = -EINVAL" at 2) is only needed by the branch 3), because "retval" will be overwritten at 4). No functional change, but it can reduce the code size. Maybe more clearer? Before: text data bss dec hex filename 28733 1590 1 30324 7674 mm/mmap.o After: text data bss dec hex filename 28701 1590 1 30292 7654 mm/mmap.o ====pseudo code====: if (!(flags & MAP_ANONYMOUS)) { ... 1) if (is_file_hugepages(file)) len = ALIGN(len, huge_page_size(hstate_file(file))); 2) retval = -EINVAL; 3) if (unlikely(flags & MAP_HUGETLB && !is_file_hugepages(file))) goto out_fput; } else if (flags & MAP_HUGETLB) { ... } ... 4) retval = vm_mmap_pgoff(file, addr, len, prot, flags, pgoff); out_fput: ... return retval; Signed-off-by: Zhen Lei Signed-off-by: Andrew Morton Reviewed-by: Andrew Morton Link: http://lkml.kernel.org/r/20200705080112.1405-1-thunder.leizhen@huawei.com Signed-off-by: Linus Torvalds --- mm/mmap.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/mm/mmap.c b/mm/mmap.c index dcdab2675a21a..853ae3aebc3d3 100644 --- a/mm/mmap.c +++ b/mm/mmap.c @@ -1562,11 +1562,12 @@ unsigned long ksys_mmap_pgoff(unsigned long addr, unsigned long len, file = fget(fd); if (!file) return -EBADF; - if (is_file_hugepages(file)) + if (is_file_hugepages(file)) { len = ALIGN(len, huge_page_size(hstate_file(file))); - retval = -EINVAL; - if (unlikely(flags & MAP_HUGETLB && !is_file_hugepages(file))) + } else if (unlikely(flags & MAP_HUGETLB)) { + retval = -EINVAL; goto out_fput; + } } else if (flags & MAP_HUGETLB) { struct user_struct *user = NULL; struct hstate *hs; -- GitLab From 1455083c1d70f69028df0eb92b69fd24db277e4b Mon Sep 17 00:00:00 2001 From: Feng Tang Date: Thu, 6 Aug 2020 23:23:03 -0700 Subject: [PATCH 1414/1476] proc/meminfo: avoid open coded reading of vm_committed_as Patch series "make vm_committed_as_batch aware of vm overcommit policy", v6. When checking a performance change for will-it-scale scalability mmap test [1], we found very high lock contention for spinlock of percpu counter 'vm_committed_as': 94.14% 0.35% [kernel.kallsyms] [k] _raw_spin_lock_irqsave 48.21% _raw_spin_lock_irqsave;percpu_counter_add_batch;__vm_enough_memory;mmap_region;do_mmap; 45.91% _raw_spin_lock_irqsave;percpu_counter_add_batch;__do_munmap; Actually this heavy lock contention is not always necessary. The 'vm_committed_as' needs to be very precise when the strict OVERCOMMIT_NEVER policy is set, which requires a rather small batch number for the percpu counter. So keep 'batch' number unchanged for strict OVERCOMMIT_NEVER policy, and enlarge it for not-so-strict OVERCOMMIT_ALWAYS and OVERCOMMIT_GUESS policies. Benchmark with the same testcase in [1] shows 53% improvement on a 8C/16T desktop, and 2097%(20X) on a 4S/72C/144T server. And for that case, whether it shows improvements depends on if the test mmap size is bigger than the batch number computed. We tested 10+ platforms in 0day (server, desktop and laptop). If we lift it to 64X, 80%+ platforms show improvements, and for 16X lift, 1/3 of the platforms will show improvements. And generally it should help the mmap/unmap usage,as Michal Hocko mentioned: : I believe that there are non-synthetic worklaods which would benefit : from a larger batch. E.g. large in memory databases which do large : mmaps during startups from multiple threads. Note: There are some style complain from checkpatch for patch 4, as sysctl handler declaration follows the similar format of sibling functions [1] https://lore.kernel.org/lkml/20200305062138.GI5972@shao2-debian/ This patch (of 4): Use the existing vm_memory_committed() instead, which is also convenient for future change. Signed-off-by: Feng Tang Signed-off-by: Andrew Morton Acked-by: Michal Hocko Cc: Matthew Wilcox (Oracle) Cc: Johannes Weiner Cc: Mel Gorman Cc: Qian Cai Cc: Kees Cook Cc: Andi Kleen Cc: Tim Chen Cc: Dave Hansen Cc: Huang Ying Cc: Christoph Lameter Cc: Dennis Zhou Cc: Haiyang Zhang Cc: kernel test robot Cc: "K. Y. Srinivasan" Cc: Tejun Heo Link: http://lkml.kernel.org/r/1594389708-60781-1-git-send-email-feng.tang@intel.com Link: http://lkml.kernel.org/r/1594389708-60781-2-git-send-email-feng.tang@intel.com Signed-off-by: Linus Torvalds --- fs/proc/meminfo.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/proc/meminfo.c b/fs/proc/meminfo.c index 2a4c58f70fb91..887a5532e4499 100644 --- a/fs/proc/meminfo.c +++ b/fs/proc/meminfo.c @@ -41,7 +41,7 @@ static int meminfo_proc_show(struct seq_file *m, void *v) si_meminfo(&i); si_swapinfo(&i); - committed = percpu_counter_read_positive(&vm_committed_as); + committed = vm_memory_committed(); cached = global_node_page_state(NR_FILE_PAGES) - total_swapcache_pages() - i.bufferram; -- GitLab From 4e2ee51e82510813969eff9feff8e570a8a28c73 Mon Sep 17 00:00:00 2001 From: Feng Tang Date: Thu, 6 Aug 2020 23:23:07 -0700 Subject: [PATCH 1415/1476] mm/util.c: make vm_memory_committed() more accurate percpu_counter_sum_positive() will provide more accurate info. As with percpu_counter_read_positive(), in worst case the deviation could be 'batch * nr_cpus', which is totalram_pages/256 for now, and will be more when the batch gets enlarged. Its time cost is about 800 nanoseconds on a 2C/4T platform and 2~3 microseconds on a 2S/36C/72T Skylake server in normal case, and in worst case where vm_committed_as's spinlock is under severe contention, it costs 30~40 microseconds for the 2S/36C/72T Skylake sever, which should be fine for its only two users: /proc/meminfo and HyperV balloon driver's status trace per second. Signed-off-by: Feng Tang Signed-off-by: Andrew Morton Acked-by: Michal Hocko # for /proc/meminfo Cc: "K. Y. Srinivasan" Cc: Haiyang Zhang Cc: Matthew Wilcox (Oracle) Cc: Johannes Weiner Cc: Mel Gorman Cc: Qian Cai Cc: Andi Kleen Cc: Tim Chen Cc: Dave Hansen Cc: Huang Ying Cc: Christoph Lameter Cc: Dennis Zhou Cc: Kees Cook Cc: kernel test robot Cc: Tejun Heo Link: http://lkml.kernel.org/r/1592725000-73486-3-git-send-email-feng.tang@intel.com Link: http://lkml.kernel.org/r/1594389708-60781-3-git-send-email-feng.tang@intel.com Signed-off-by: Linus Torvalds --- mm/util.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/mm/util.c b/mm/util.c index c63c8e47be572..1c9d097d7e486 100644 --- a/mm/util.c +++ b/mm/util.c @@ -787,10 +787,15 @@ struct percpu_counter vm_committed_as ____cacheline_aligned_in_smp; * balancing memory across competing virtual machines that are hosted. * Several metrics drive this policy engine including the guest reported * memory commitment. + * + * The time cost of this is very low for small platforms, and for big + * platform like a 2S/36C/72T Skylake server, in worst case where + * vm_committed_as's spinlock is under severe contention, the time cost + * could be about 30~40 microseconds. */ unsigned long vm_memory_committed(void) { - return percpu_counter_read_positive(&vm_committed_as); + return percpu_counter_sum_positive(&vm_committed_as); } EXPORT_SYMBOL_GPL(vm_memory_committed); -- GitLab From 0a4954a850b0c4d0a5d18b1a55d6e5a653e362b5 Mon Sep 17 00:00:00 2001 From: Feng Tang Date: Thu, 6 Aug 2020 23:23:11 -0700 Subject: [PATCH 1416/1476] percpu_counter: add percpu_counter_sync() percpu_counter's accuracy is related to its batch size. For a percpu_counter with a big batch, its deviation could be big, so when the counter's batch is runtime changed to a smaller value for better accuracy, there could also be requirment to reduce the big deviation. So add a percpu-counter sync function to be run on each CPU. Reported-by: kernel test robot Signed-off-by: Feng Tang Signed-off-by: Andrew Morton Cc: Dennis Zhou Cc: Tejun Heo Cc: Christoph Lameter Cc: Michal Hocko Cc: Qian Cai Cc: Andi Kleen Cc: Huang Ying Cc: Dave Hansen Cc: Haiyang Zhang Cc: Johannes Weiner Cc: Kees Cook Cc: "K. Y. Srinivasan" Cc: Matthew Wilcox (Oracle) Cc: Mel Gorman Cc: Tim Chen Link: http://lkml.kernel.org/r/1594389708-60781-4-git-send-email-feng.tang@intel.com Signed-off-by: Linus Torvalds --- include/linux/percpu_counter.h | 4 ++++ lib/percpu_counter.c | 19 +++++++++++++++++++ 2 files changed, 23 insertions(+) diff --git a/include/linux/percpu_counter.h b/include/linux/percpu_counter.h index 0a4f54dd4737b..01861eebed79d 100644 --- a/include/linux/percpu_counter.h +++ b/include/linux/percpu_counter.h @@ -44,6 +44,7 @@ void percpu_counter_add_batch(struct percpu_counter *fbc, s64 amount, s32 batch); s64 __percpu_counter_sum(struct percpu_counter *fbc); int __percpu_counter_compare(struct percpu_counter *fbc, s64 rhs, s32 batch); +void percpu_counter_sync(struct percpu_counter *fbc); static inline int percpu_counter_compare(struct percpu_counter *fbc, s64 rhs) { @@ -172,6 +173,9 @@ static inline bool percpu_counter_initialized(struct percpu_counter *fbc) return true; } +static inline void percpu_counter_sync(struct percpu_counter *fbc) +{ +} #endif /* CONFIG_SMP */ static inline void percpu_counter_inc(struct percpu_counter *fbc) diff --git a/lib/percpu_counter.c b/lib/percpu_counter.c index a66595ba5543e..a2345de90e937 100644 --- a/lib/percpu_counter.c +++ b/lib/percpu_counter.c @@ -98,6 +98,25 @@ void percpu_counter_add_batch(struct percpu_counter *fbc, s64 amount, s32 batch) } EXPORT_SYMBOL(percpu_counter_add_batch); +/* + * For percpu_counter with a big batch, the devication of its count could + * be big, and there is requirement to reduce the deviation, like when the + * counter's batch could be runtime decreased to get a better accuracy, + * which can be achieved by running this sync function on each CPU. + */ +void percpu_counter_sync(struct percpu_counter *fbc) +{ + unsigned long flags; + s64 count; + + raw_spin_lock_irqsave(&fbc->lock, flags); + count = __this_cpu_read(*fbc->counters); + fbc->count += count; + __this_cpu_sub(*fbc->counters, count); + raw_spin_unlock_irqrestore(&fbc->lock, flags); +} +EXPORT_SYMBOL(percpu_counter_sync); + /* * Add up all the per-cpu counts, return the result. This is a more accurate * but much slower version of percpu_counter_read_positive() -- GitLab From 56f3547bfa4d361148aa748ccb86073bc57f5e6c Mon Sep 17 00:00:00 2001 From: Feng Tang Date: Thu, 6 Aug 2020 23:23:15 -0700 Subject: [PATCH 1417/1476] mm: adjust vm_committed_as_batch according to vm overcommit policy When checking a performance change for will-it-scale scalability mmap test [1], we found very high lock contention for spinlock of percpu counter 'vm_committed_as': 94.14% 0.35% [kernel.kallsyms] [k] _raw_spin_lock_irqsave 48.21% _raw_spin_lock_irqsave;percpu_counter_add_batch;__vm_enough_memory;mmap_region;do_mmap; 45.91% _raw_spin_lock_irqsave;percpu_counter_add_batch;__do_munmap; Actually this heavy lock contention is not always necessary. The 'vm_committed_as' needs to be very precise when the strict OVERCOMMIT_NEVER policy is set, which requires a rather small batch number for the percpu counter. So keep 'batch' number unchanged for strict OVERCOMMIT_NEVER policy, and lift it to 64X for OVERCOMMIT_ALWAYS and OVERCOMMIT_GUESS policies. Also add a sysctl handler to adjust it when the policy is reconfigured. Benchmark with the same testcase in [1] shows 53% improvement on a 8C/16T desktop, and 2097%(20X) on a 4S/72C/144T server. We tested with test platforms in 0day (server, desktop and laptop), and 80%+ platforms shows improvements with that test. And whether it shows improvements depends on if the test mmap size is bigger than the batch number computed. And if the lift is 16X, 1/3 of the platforms will show improvements, though it should help the mmap/unmap usage generally, as Michal Hocko mentioned: : I believe that there are non-synthetic worklaods which would benefit from : a larger batch. E.g. large in memory databases which do large mmaps : during startups from multiple threads. [1] https://lore.kernel.org/lkml/20200305062138.GI5972@shao2-debian/ Signed-off-by: Feng Tang Signed-off-by: Andrew Morton Acked-by: Michal Hocko Cc: Matthew Wilcox (Oracle) Cc: Johannes Weiner Cc: Mel Gorman Cc: Qian Cai Cc: Kees Cook Cc: Andi Kleen Cc: Tim Chen Cc: Dave Hansen Cc: Huang Ying Cc: Christoph Lameter Cc: Dennis Zhou Cc: Haiyang Zhang Cc: kernel test robot Cc: "K. Y. Srinivasan" Cc: Tejun Heo Link: http://lkml.kernel.org/r/1589611660-89854-4-git-send-email-feng.tang@intel.com Link: http://lkml.kernel.org/r/1592725000-73486-4-git-send-email-feng.tang@intel.com Link: http://lkml.kernel.org/r/1594389708-60781-5-git-send-email-feng.tang@intel.com Signed-off-by: Linus Torvalds --- include/linux/mm.h | 2 ++ include/linux/mman.h | 4 ++++ kernel/sysctl.c | 2 +- mm/mm_init.c | 22 ++++++++++++++++------ mm/util.c | 41 +++++++++++++++++++++++++++++++++++++++++ 5 files changed, 64 insertions(+), 7 deletions(-) diff --git a/include/linux/mm.h b/include/linux/mm.h index 2830f1c0fdc32..1c34705503953 100644 --- a/include/linux/mm.h +++ b/include/linux/mm.h @@ -206,6 +206,8 @@ int overcommit_ratio_handler(struct ctl_table *, int, void *, size_t *, loff_t *); int overcommit_kbytes_handler(struct ctl_table *, int, void *, size_t *, loff_t *); +int overcommit_policy_handler(struct ctl_table *, int, void *, size_t *, + loff_t *); #define nth_page(page,n) pfn_to_page(page_to_pfn((page)) + (n)) diff --git a/include/linux/mman.h b/include/linux/mman.h index 4b08e9c9c5386..6f34c33075f94 100644 --- a/include/linux/mman.h +++ b/include/linux/mman.h @@ -57,8 +57,12 @@ extern struct percpu_counter vm_committed_as; #ifdef CONFIG_SMP extern s32 vm_committed_as_batch; +extern void mm_compute_batch(int overcommit_policy); #else #define vm_committed_as_batch 0 +static inline void mm_compute_batch(int overcommit_policy) +{ +} #endif unsigned long vm_memory_committed(void); diff --git a/kernel/sysctl.c b/kernel/sysctl.c index 1b4d2dc270a59..f785de3caac0a 100644 --- a/kernel/sysctl.c +++ b/kernel/sysctl.c @@ -2671,7 +2671,7 @@ static struct ctl_table vm_table[] = { .data = &sysctl_overcommit_memory, .maxlen = sizeof(sysctl_overcommit_memory), .mode = 0644, - .proc_handler = proc_dointvec_minmax, + .proc_handler = overcommit_policy_handler, .extra1 = SYSCTL_ZERO, .extra2 = &two, }, diff --git a/mm/mm_init.c b/mm/mm_init.c index 435e5f794b3ba..b06a30fbedff7 100644 --- a/mm/mm_init.c +++ b/mm/mm_init.c @@ -13,6 +13,7 @@ #include #include #include +#include #include "internal.h" #ifdef CONFIG_DEBUG_MEMORY_INIT @@ -144,14 +145,23 @@ EXPORT_SYMBOL_GPL(mm_kobj); #ifdef CONFIG_SMP s32 vm_committed_as_batch = 32; -static void __meminit mm_compute_batch(void) +void mm_compute_batch(int overcommit_policy) { u64 memsized_batch; s32 nr = num_present_cpus(); s32 batch = max_t(s32, nr*2, 32); - - /* batch size set to 0.4% of (total memory/#cpus), or max int32 */ - memsized_batch = min_t(u64, (totalram_pages()/nr)/256, 0x7fffffff); + unsigned long ram_pages = totalram_pages(); + + /* + * For policy OVERCOMMIT_NEVER, set batch size to 0.4% of + * (total memory/#cpus), and lift it to 25% for other policies + * to easy the possible lock contention for percpu_counter + * vm_committed_as, while the max limit is INT_MAX + */ + if (overcommit_policy == OVERCOMMIT_NEVER) + memsized_batch = min_t(u64, ram_pages/nr/256, INT_MAX); + else + memsized_batch = min_t(u64, ram_pages/nr/4, INT_MAX); vm_committed_as_batch = max_t(s32, memsized_batch, batch); } @@ -162,7 +172,7 @@ static int __meminit mm_compute_batch_notifier(struct notifier_block *self, switch (action) { case MEM_ONLINE: case MEM_OFFLINE: - mm_compute_batch(); + mm_compute_batch(sysctl_overcommit_memory); default: break; } @@ -176,7 +186,7 @@ static struct notifier_block compute_batch_nb __meminitdata = { static int __init mm_compute_batch_init(void) { - mm_compute_batch(); + mm_compute_batch(sysctl_overcommit_memory); register_hotmemory_notifier(&compute_batch_nb); return 0; diff --git a/mm/util.c b/mm/util.c index 1c9d097d7e486..8d6280c05238a 100644 --- a/mm/util.c +++ b/mm/util.c @@ -746,6 +746,47 @@ int overcommit_ratio_handler(struct ctl_table *table, int write, void *buffer, return ret; } +static void sync_overcommit_as(struct work_struct *dummy) +{ + percpu_counter_sync(&vm_committed_as); +} + +int overcommit_policy_handler(struct ctl_table *table, int write, void *buffer, + size_t *lenp, loff_t *ppos) +{ + struct ctl_table t; + int new_policy; + int ret; + + /* + * The deviation of sync_overcommit_as could be big with loose policy + * like OVERCOMMIT_ALWAYS/OVERCOMMIT_GUESS. When changing policy to + * strict OVERCOMMIT_NEVER, we need to reduce the deviation to comply + * with the strict "NEVER", and to avoid possible race condtion (even + * though user usually won't too frequently do the switching to policy + * OVERCOMMIT_NEVER), the switch is done in the following order: + * 1. changing the batch + * 2. sync percpu count on each CPU + * 3. switch the policy + */ + if (write) { + t = *table; + t.data = &new_policy; + ret = proc_dointvec_minmax(&t, write, buffer, lenp, ppos); + if (ret) + return ret; + + mm_compute_batch(new_policy); + if (new_policy == OVERCOMMIT_NEVER) + schedule_on_each_cpu(sync_overcommit_as); + sysctl_overcommit_memory = new_policy; + } else { + ret = proc_dointvec_minmax(table, write, buffer, lenp, ppos); + } + + return ret; +} + int overcommit_kbytes_handler(struct ctl_table *table, int write, void *buffer, size_t *lenp, loff_t *ppos) { -- GitLab From 1d9cfee7535c213038a615f112c900c2d0ba8f54 Mon Sep 17 00:00:00 2001 From: Anshuman Khandual Date: Thu, 6 Aug 2020 23:23:19 -0700 Subject: [PATCH 1418/1476] mm/sparsemem: enable vmem_altmap support in vmemmap_populate_basepages() Patch series "arm64: Enable vmemmap mapping from device memory", v4. This series enables vmemmap backing memory allocation from device memory ranges on arm64. But before that, it enables vmemmap_populate_basepages() and vmemmap_alloc_block_buf() to accommodate struct vmem_altmap based alocation requests. This patch (of 3): vmemmap_populate_basepages() is used across platforms to allocate backing memory for vmemmap mapping. This is used as a standard default choice or as a fallback when intended huge pages allocation fails. This just creates entire vmemmap mapping with base pages (PAGE_SIZE). On arm64 platforms, vmemmap_populate_basepages() is called instead of the platform specific vmemmap_populate() when ARM64_SWAPPER_USES_SECTION_MAPS is not enabled as in case for ARM64_16K_PAGES and ARM64_64K_PAGES configs. At present vmemmap_populate_basepages() does not support allocating from driver defined struct vmem_altmap while trying to create vmemmap mapping for a device memory range. It prevents ARM64_16K_PAGES and ARM64_64K_PAGES configs on arm64 from supporting device memory with vmemap_altmap request. This enables vmem_altmap support in vmemmap_populate_basepages() unlocking device memory allocation for vmemap mapping on arm64 platforms with 16K or 64K base page configs. Each architecture should evaluate and decide on subscribing device memory based base page allocation through vmemmap_populate_basepages(). Hence lets keep it disabled on all archs in order to preserve the existing semantics. A subsequent patch enables it on arm64. Signed-off-by: Anshuman Khandual Signed-off-by: Andrew Morton Tested-by: Jia He Reviewed-by: David Hildenbrand Acked-by: Will Deacon Acked-by: Catalin Marinas Cc: Mark Rutland Cc: Paul Walmsley Cc: Palmer Dabbelt Cc: Tony Luck Cc: Fenghua Yu Cc: Dave Hansen Cc: Andy Lutomirski Cc: Peter Zijlstra Cc: Thomas Gleixner Cc: Ingo Molnar Cc: Mike Rapoport Cc: Michal Hocko Cc: "Matthew Wilcox (Oracle)" Cc: "Kirill A. Shutemov" Cc: Dan Williams Cc: Pavel Tatashin Cc: Benjamin Herrenschmidt Cc: Borislav Petkov Cc: "H. Peter Anvin" Cc: Hsin-Yi Wang Cc: Jonathan Corbet Cc: Michael Ellerman Cc: Paul Mackerras Cc: Robin Murphy Cc: Steve Capper Cc: Yu Zhao Link: http://lkml.kernel.org/r/1594004178-8861-1-git-send-email-anshuman.khandual@arm.com Link: http://lkml.kernel.org/r/1594004178-8861-2-git-send-email-anshuman.khandual@arm.com Signed-off-by: Linus Torvalds --- arch/arm64/mm/mmu.c | 2 +- arch/ia64/mm/discontig.c | 2 +- arch/riscv/mm/init.c | 2 +- arch/x86/mm/init_64.c | 6 +++--- include/linux/mm.h | 5 +++-- mm/sparse-vmemmap.c | 16 +++++++++++----- 6 files changed, 20 insertions(+), 13 deletions(-) diff --git a/arch/arm64/mm/mmu.c b/arch/arm64/mm/mmu.c index cafefb147a5ec..a32ddd021fe98 100644 --- a/arch/arm64/mm/mmu.c +++ b/arch/arm64/mm/mmu.c @@ -1070,7 +1070,7 @@ static void free_empty_tables(unsigned long addr, unsigned long end, int __meminit vmemmap_populate(unsigned long start, unsigned long end, int node, struct vmem_altmap *altmap) { - return vmemmap_populate_basepages(start, end, node); + return vmemmap_populate_basepages(start, end, node, NULL); } #else /* !ARM64_SWAPPER_USES_SECTION_MAPS */ int __meminit vmemmap_populate(unsigned long start, unsigned long end, int node, diff --git a/arch/ia64/mm/discontig.c b/arch/ia64/mm/discontig.c index 37f8b6875ac90..2ba2127335a7a 100644 --- a/arch/ia64/mm/discontig.c +++ b/arch/ia64/mm/discontig.c @@ -655,7 +655,7 @@ void arch_refresh_nodedata(int update_node, pg_data_t *update_pgdat) int __meminit vmemmap_populate(unsigned long start, unsigned long end, int node, struct vmem_altmap *altmap) { - return vmemmap_populate_basepages(start, end, node); + return vmemmap_populate_basepages(start, end, node, NULL); } void vmemmap_free(unsigned long start, unsigned long end, diff --git a/arch/riscv/mm/init.c b/arch/riscv/mm/init.c index 79e9d55bdf1ac..416e520d07d39 100644 --- a/arch/riscv/mm/init.c +++ b/arch/riscv/mm/init.c @@ -554,6 +554,6 @@ void __init paging_init(void) int __meminit vmemmap_populate(unsigned long start, unsigned long end, int node, struct vmem_altmap *altmap) { - return vmemmap_populate_basepages(start, end, node); + return vmemmap_populate_basepages(start, end, node, NULL); } #endif diff --git a/arch/x86/mm/init_64.c b/arch/x86/mm/init_64.c index 3f4e29a78f2ba..1acc5627b21c2 100644 --- a/arch/x86/mm/init_64.c +++ b/arch/x86/mm/init_64.c @@ -1545,7 +1545,7 @@ static int __meminit vmemmap_populate_hugepages(unsigned long start, vmemmap_verify((pte_t *)pmd, node, addr, next); continue; } - if (vmemmap_populate_basepages(addr, next, node)) + if (vmemmap_populate_basepages(addr, next, node, NULL)) return -ENOMEM; } return 0; @@ -1557,7 +1557,7 @@ int __meminit vmemmap_populate(unsigned long start, unsigned long end, int node, int err; if (end - start < PAGES_PER_SECTION * sizeof(struct page)) - err = vmemmap_populate_basepages(start, end, node); + err = vmemmap_populate_basepages(start, end, node, NULL); else if (boot_cpu_has(X86_FEATURE_PSE)) err = vmemmap_populate_hugepages(start, end, node, altmap); else if (altmap) { @@ -1565,7 +1565,7 @@ int __meminit vmemmap_populate(unsigned long start, unsigned long end, int node, __func__); err = -ENOMEM; } else - err = vmemmap_populate_basepages(start, end, node); + err = vmemmap_populate_basepages(start, end, node, NULL); if (!err) sync_global_pgds(start, end - 1); return err; diff --git a/include/linux/mm.h b/include/linux/mm.h index 1c34705503953..a7ff98738126b 100644 --- a/include/linux/mm.h +++ b/include/linux/mm.h @@ -2978,14 +2978,15 @@ pgd_t *vmemmap_pgd_populate(unsigned long addr, int node); p4d_t *vmemmap_p4d_populate(pgd_t *pgd, unsigned long addr, int node); pud_t *vmemmap_pud_populate(p4d_t *p4d, unsigned long addr, int node); pmd_t *vmemmap_pmd_populate(pud_t *pud, unsigned long addr, int node); -pte_t *vmemmap_pte_populate(pmd_t *pmd, unsigned long addr, int node); +pte_t *vmemmap_pte_populate(pmd_t *pmd, unsigned long addr, int node, + struct vmem_altmap *altmap); void *vmemmap_alloc_block(unsigned long size, int node); struct vmem_altmap; void *vmemmap_alloc_block_buf(unsigned long size, int node); void *altmap_alloc_block_buf(unsigned long size, struct vmem_altmap *altmap); void vmemmap_verify(pte_t *, int, unsigned long, unsigned long); int vmemmap_populate_basepages(unsigned long start, unsigned long end, - int node); + int node, struct vmem_altmap *altmap); int vmemmap_populate(unsigned long start, unsigned long end, int node, struct vmem_altmap *altmap); void vmemmap_populate_print_last(void); diff --git a/mm/sparse-vmemmap.c b/mm/sparse-vmemmap.c index 0db7738d76e9b..ceed10dec31e8 100644 --- a/mm/sparse-vmemmap.c +++ b/mm/sparse-vmemmap.c @@ -139,12 +139,18 @@ void __meminit vmemmap_verify(pte_t *pte, int node, start, end - 1); } -pte_t * __meminit vmemmap_pte_populate(pmd_t *pmd, unsigned long addr, int node) +pte_t * __meminit vmemmap_pte_populate(pmd_t *pmd, unsigned long addr, int node, + struct vmem_altmap *altmap) { pte_t *pte = pte_offset_kernel(pmd, addr); if (pte_none(*pte)) { pte_t entry; - void *p = vmemmap_alloc_block_buf(PAGE_SIZE, node); + void *p; + + if (altmap) + p = altmap_alloc_block_buf(PAGE_SIZE, altmap); + else + p = vmemmap_alloc_block_buf(PAGE_SIZE, node); if (!p) return NULL; entry = pfn_pte(__pa(p) >> PAGE_SHIFT, PAGE_KERNEL); @@ -212,8 +218,8 @@ pgd_t * __meminit vmemmap_pgd_populate(unsigned long addr, int node) return pgd; } -int __meminit vmemmap_populate_basepages(unsigned long start, - unsigned long end, int node) +int __meminit vmemmap_populate_basepages(unsigned long start, unsigned long end, + int node, struct vmem_altmap *altmap) { unsigned long addr = start; pgd_t *pgd; @@ -235,7 +241,7 @@ int __meminit vmemmap_populate_basepages(unsigned long start, pmd = vmemmap_pmd_populate(pud, addr, node); if (!pmd) return -ENOMEM; - pte = vmemmap_pte_populate(pmd, addr, node); + pte = vmemmap_pte_populate(pmd, addr, node, altmap); if (!pte) return -ENOMEM; vmemmap_verify(pte, node, addr, addr + PAGE_SIZE); -- GitLab From 56993b4e147e9f2ba91ac15ef9ae5ee0626a6850 Mon Sep 17 00:00:00 2001 From: Anshuman Khandual Date: Thu, 6 Aug 2020 23:23:24 -0700 Subject: [PATCH 1419/1476] mm/sparsemem: enable vmem_altmap support in vmemmap_alloc_block_buf() There are many instances where vmemap allocation is often switched between regular memory and device memory just based on whether altmap is available or not. vmemmap_alloc_block_buf() is used in various platforms to allocate vmemmap mappings. Lets also enable it to handle altmap based device memory allocation along with existing regular memory allocations. This will help in avoiding the altmap based allocation switch in many places. To summarize there are two different methods to call vmemmap_alloc_block_buf(). vmemmap_alloc_block_buf(size, node, NULL) /* Allocate from system RAM */ vmemmap_alloc_block_buf(size, node, altmap) /* Allocate from altmap */ This converts altmap_alloc_block_buf() into a static function, drops it's entry from the header and updates Documentation/vm/memory-model.rst. Suggested-by: Robin Murphy Signed-off-by: Anshuman Khandual Signed-off-by: Andrew Morton Tested-by: Jia He Reviewed-by: Catalin Marinas Cc: Jonathan Corbet Cc: Will Deacon Cc: Benjamin Herrenschmidt Cc: Paul Mackerras Cc: Michael Ellerman Cc: Dave Hansen Cc: Andy Lutomirski Cc: Peter Zijlstra Cc: Thomas Gleixner Cc: Ingo Molnar Cc: Borislav Petkov Cc: "H. Peter Anvin" Cc: Dan Williams Cc: David Hildenbrand Cc: Fenghua Yu Cc: Hsin-Yi Wang Cc: "Kirill A. Shutemov" Cc: Mark Rutland Cc: "Matthew Wilcox (Oracle)" Cc: Michal Hocko Cc: Mike Rapoport Cc: Palmer Dabbelt Cc: Paul Walmsley Cc: Pavel Tatashin Cc: Steve Capper Cc: Tony Luck Cc: Yu Zhao Link: http://lkml.kernel.org/r/1594004178-8861-3-git-send-email-anshuman.khandual@arm.com Signed-off-by: Linus Torvalds --- Documentation/vm/memory-model.rst | 2 +- arch/arm64/mm/mmu.c | 2 +- arch/powerpc/mm/init_64.c | 4 ++-- arch/x86/mm/init_64.c | 5 +---- include/linux/mm.h | 4 ++-- mm/sparse-vmemmap.c | 28 +++++++++++++--------------- 6 files changed, 20 insertions(+), 25 deletions(-) diff --git a/Documentation/vm/memory-model.rst b/Documentation/vm/memory-model.rst index cc65bc85d260f..2b898a27b3465 100644 --- a/Documentation/vm/memory-model.rst +++ b/Documentation/vm/memory-model.rst @@ -178,7 +178,7 @@ for persistent memory devices in pre-allocated storage on those devices. This storage is represented with :c:type:`struct vmem_altmap` that is eventually passed to vmemmap_populate() through a long chain of function calls. The vmemmap_populate() implementation may use the -`vmem_altmap` along with :c:func:`altmap_alloc_block_buf` helper to +`vmem_altmap` along with :c:func:`vmemmap_alloc_block_buf` helper to allocate memory map on the persistent memory device. ZONE_DEVICE diff --git a/arch/arm64/mm/mmu.c b/arch/arm64/mm/mmu.c index a32ddd021fe98..f3d9ff323c8f6 100644 --- a/arch/arm64/mm/mmu.c +++ b/arch/arm64/mm/mmu.c @@ -1102,7 +1102,7 @@ int __meminit vmemmap_populate(unsigned long start, unsigned long end, int node, if (pmd_none(READ_ONCE(*pmdp))) { void *p = NULL; - p = vmemmap_alloc_block_buf(PMD_SIZE, node); + p = vmemmap_alloc_block_buf(PMD_SIZE, node, NULL); if (!p) return -ENOMEM; diff --git a/arch/powerpc/mm/init_64.c b/arch/powerpc/mm/init_64.c index bc73abf0bc25e..3fd504d72c5e6 100644 --- a/arch/powerpc/mm/init_64.c +++ b/arch/powerpc/mm/init_64.c @@ -225,12 +225,12 @@ int __meminit vmemmap_populate(unsigned long start, unsigned long end, int node, * fall back to system memory if the altmap allocation fail. */ if (altmap && !altmap_cross_boundary(altmap, start, page_size)) { - p = altmap_alloc_block_buf(page_size, altmap); + p = vmemmap_alloc_block_buf(page_size, node, altmap); if (!p) pr_debug("altmap block allocation failed, falling back to system memory"); } if (!p) - p = vmemmap_alloc_block_buf(page_size, node); + p = vmemmap_alloc_block_buf(page_size, node, NULL); if (!p) return -ENOMEM; diff --git a/arch/x86/mm/init_64.c b/arch/x86/mm/init_64.c index 1acc5627b21c2..53e1d4f4ed9de 100644 --- a/arch/x86/mm/init_64.c +++ b/arch/x86/mm/init_64.c @@ -1515,10 +1515,7 @@ static int __meminit vmemmap_populate_hugepages(unsigned long start, if (pmd_none(*pmd)) { void *p; - if (altmap) - p = altmap_alloc_block_buf(PMD_SIZE, altmap); - else - p = vmemmap_alloc_block_buf(PMD_SIZE, node); + p = vmemmap_alloc_block_buf(PMD_SIZE, node, altmap); if (p) { pte_t entry; diff --git a/include/linux/mm.h b/include/linux/mm.h index a7ff98738126b..98e35df8d88e3 100644 --- a/include/linux/mm.h +++ b/include/linux/mm.h @@ -2982,8 +2982,8 @@ pte_t *vmemmap_pte_populate(pmd_t *pmd, unsigned long addr, int node, struct vmem_altmap *altmap); void *vmemmap_alloc_block(unsigned long size, int node); struct vmem_altmap; -void *vmemmap_alloc_block_buf(unsigned long size, int node); -void *altmap_alloc_block_buf(unsigned long size, struct vmem_altmap *altmap); +void *vmemmap_alloc_block_buf(unsigned long size, int node, + struct vmem_altmap *altmap); void vmemmap_verify(pte_t *, int, unsigned long, unsigned long); int vmemmap_populate_basepages(unsigned long start, unsigned long end, int node, struct vmem_altmap *altmap); diff --git a/mm/sparse-vmemmap.c b/mm/sparse-vmemmap.c index ceed10dec31e8..41eeac67723bc 100644 --- a/mm/sparse-vmemmap.c +++ b/mm/sparse-vmemmap.c @@ -69,11 +69,19 @@ void * __meminit vmemmap_alloc_block(unsigned long size, int node) __pa(MAX_DMA_ADDRESS)); } +static void * __meminit altmap_alloc_block_buf(unsigned long size, + struct vmem_altmap *altmap); + /* need to make sure size is all the same during early stage */ -void * __meminit vmemmap_alloc_block_buf(unsigned long size, int node) +void * __meminit vmemmap_alloc_block_buf(unsigned long size, int node, + struct vmem_altmap *altmap) { - void *ptr = sparse_buffer_alloc(size); + void *ptr; + + if (altmap) + return altmap_alloc_block_buf(size, altmap); + ptr = sparse_buffer_alloc(size); if (!ptr) ptr = vmemmap_alloc_block(size, node); return ptr; @@ -94,15 +102,8 @@ static unsigned long __meminit vmem_altmap_nr_free(struct vmem_altmap *altmap) return 0; } -/** - * altmap_alloc_block_buf - allocate pages from the device page map - * @altmap: device page map - * @size: size (in bytes) of the allocation - * - * Allocations are aligned to the size of the request. - */ -void * __meminit altmap_alloc_block_buf(unsigned long size, - struct vmem_altmap *altmap) +static void * __meminit altmap_alloc_block_buf(unsigned long size, + struct vmem_altmap *altmap) { unsigned long pfn, nr_pfns, nr_align; @@ -147,10 +148,7 @@ pte_t * __meminit vmemmap_pte_populate(pmd_t *pmd, unsigned long addr, int node, pte_t entry; void *p; - if (altmap) - p = altmap_alloc_block_buf(PAGE_SIZE, altmap); - else - p = vmemmap_alloc_block_buf(PAGE_SIZE, node); + p = vmemmap_alloc_block_buf(PAGE_SIZE, node, altmap); if (!p) return NULL; entry = pfn_pte(__pa(p) >> PAGE_SHIFT, PAGE_KERNEL); -- GitLab From eee07935bb52a03561d53ccc5cecf12f2faeb396 Mon Sep 17 00:00:00 2001 From: Anshuman Khandual Date: Thu, 6 Aug 2020 23:23:29 -0700 Subject: [PATCH 1420/1476] arm64/mm: enable vmem_altmap support for vmemmap mappings Device memory ranges when getting hot added into ZONE_DEVICE, might require their vmemmap mapping's backing memory to be allocated from their own range instead of consuming system memory. This prevents large system memory usage for potentially large device memory ranges. Device driver communicates this request via vmem_altmap structure. Architecture needs to take this request into account while creating and tearing down vemmmap mappings. This enables vmem_altmap support in vmemmap_populate() and vmemmap_free() which includes vmemmap_populate_basepages() used for ARM64_16K_PAGES and ARM64_64K_PAGES configs. Signed-off-by: Anshuman Khandual Signed-off-by: Andrew Morton Tested-by: Jia He Reviewed-by: Catalin Marinas Cc: Will Deacon Cc: Mark Rutland Cc: Steve Capper Cc: David Hildenbrand Cc: Yu Zhao Cc: Hsin-Yi Wang Cc: Thomas Gleixner Cc: Andy Lutomirski Cc: Benjamin Herrenschmidt Cc: Borislav Petkov Cc: Dan Williams Cc: Dave Hansen Cc: Fenghua Yu Cc: "H. Peter Anvin" Cc: Ingo Molnar Cc: Jonathan Corbet Cc: "Kirill A. Shutemov" Cc: "Matthew Wilcox (Oracle)" Cc: Michael Ellerman Cc: Michal Hocko Cc: Mike Rapoport Cc: Palmer Dabbelt Cc: Paul Mackerras Cc: Paul Walmsley Cc: Pavel Tatashin Cc: Peter Zijlstra Cc: Robin Murphy Cc: Tony Luck Link: http://lkml.kernel.org/r/1594004178-8861-4-git-send-email-anshuman.khandual@arm.com Signed-off-by: Linus Torvalds --- arch/arm64/mm/mmu.c | 58 +++++++++++++++++++++++++++++---------------- 1 file changed, 38 insertions(+), 20 deletions(-) diff --git a/arch/arm64/mm/mmu.c b/arch/arm64/mm/mmu.c index f3d9ff323c8f6..75df62fea1b68 100644 --- a/arch/arm64/mm/mmu.c +++ b/arch/arm64/mm/mmu.c @@ -761,15 +761,20 @@ int kern_addr_valid(unsigned long addr) } #ifdef CONFIG_MEMORY_HOTPLUG -static void free_hotplug_page_range(struct page *page, size_t size) +static void free_hotplug_page_range(struct page *page, size_t size, + struct vmem_altmap *altmap) { - WARN_ON(PageReserved(page)); - free_pages((unsigned long)page_address(page), get_order(size)); + if (altmap) { + vmem_altmap_free(altmap, size >> PAGE_SHIFT); + } else { + WARN_ON(PageReserved(page)); + free_pages((unsigned long)page_address(page), get_order(size)); + } } static void free_hotplug_pgtable_page(struct page *page) { - free_hotplug_page_range(page, PAGE_SIZE); + free_hotplug_page_range(page, PAGE_SIZE, NULL); } static bool pgtable_range_aligned(unsigned long start, unsigned long end, @@ -792,7 +797,8 @@ static bool pgtable_range_aligned(unsigned long start, unsigned long end, } static void unmap_hotplug_pte_range(pmd_t *pmdp, unsigned long addr, - unsigned long end, bool free_mapped) + unsigned long end, bool free_mapped, + struct vmem_altmap *altmap) { pte_t *ptep, pte; @@ -806,12 +812,14 @@ static void unmap_hotplug_pte_range(pmd_t *pmdp, unsigned long addr, pte_clear(&init_mm, addr, ptep); flush_tlb_kernel_range(addr, addr + PAGE_SIZE); if (free_mapped) - free_hotplug_page_range(pte_page(pte), PAGE_SIZE); + free_hotplug_page_range(pte_page(pte), + PAGE_SIZE, altmap); } while (addr += PAGE_SIZE, addr < end); } static void unmap_hotplug_pmd_range(pud_t *pudp, unsigned long addr, - unsigned long end, bool free_mapped) + unsigned long end, bool free_mapped, + struct vmem_altmap *altmap) { unsigned long next; pmd_t *pmdp, pmd; @@ -834,16 +842,17 @@ static void unmap_hotplug_pmd_range(pud_t *pudp, unsigned long addr, flush_tlb_kernel_range(addr, addr + PAGE_SIZE); if (free_mapped) free_hotplug_page_range(pmd_page(pmd), - PMD_SIZE); + PMD_SIZE, altmap); continue; } WARN_ON(!pmd_table(pmd)); - unmap_hotplug_pte_range(pmdp, addr, next, free_mapped); + unmap_hotplug_pte_range(pmdp, addr, next, free_mapped, altmap); } while (addr = next, addr < end); } static void unmap_hotplug_pud_range(p4d_t *p4dp, unsigned long addr, - unsigned long end, bool free_mapped) + unsigned long end, bool free_mapped, + struct vmem_altmap *altmap) { unsigned long next; pud_t *pudp, pud; @@ -866,16 +875,17 @@ static void unmap_hotplug_pud_range(p4d_t *p4dp, unsigned long addr, flush_tlb_kernel_range(addr, addr + PAGE_SIZE); if (free_mapped) free_hotplug_page_range(pud_page(pud), - PUD_SIZE); + PUD_SIZE, altmap); continue; } WARN_ON(!pud_table(pud)); - unmap_hotplug_pmd_range(pudp, addr, next, free_mapped); + unmap_hotplug_pmd_range(pudp, addr, next, free_mapped, altmap); } while (addr = next, addr < end); } static void unmap_hotplug_p4d_range(pgd_t *pgdp, unsigned long addr, - unsigned long end, bool free_mapped) + unsigned long end, bool free_mapped, + struct vmem_altmap *altmap) { unsigned long next; p4d_t *p4dp, p4d; @@ -888,16 +898,24 @@ static void unmap_hotplug_p4d_range(pgd_t *pgdp, unsigned long addr, continue; WARN_ON(!p4d_present(p4d)); - unmap_hotplug_pud_range(p4dp, addr, next, free_mapped); + unmap_hotplug_pud_range(p4dp, addr, next, free_mapped, altmap); } while (addr = next, addr < end); } static void unmap_hotplug_range(unsigned long addr, unsigned long end, - bool free_mapped) + bool free_mapped, struct vmem_altmap *altmap) { unsigned long next; pgd_t *pgdp, pgd; + /* + * altmap can only be used as vmemmap mapping backing memory. + * In case the backing memory itself is not being freed, then + * altmap is irrelevant. Warn about this inconsistency when + * encountered. + */ + WARN_ON(!free_mapped && altmap); + do { next = pgd_addr_end(addr, end); pgdp = pgd_offset_k(addr); @@ -906,7 +924,7 @@ static void unmap_hotplug_range(unsigned long addr, unsigned long end, continue; WARN_ON(!pgd_present(pgd)); - unmap_hotplug_p4d_range(pgdp, addr, next, free_mapped); + unmap_hotplug_p4d_range(pgdp, addr, next, free_mapped, altmap); } while (addr = next, addr < end); } @@ -1070,7 +1088,7 @@ static void free_empty_tables(unsigned long addr, unsigned long end, int __meminit vmemmap_populate(unsigned long start, unsigned long end, int node, struct vmem_altmap *altmap) { - return vmemmap_populate_basepages(start, end, node, NULL); + return vmemmap_populate_basepages(start, end, node, altmap); } #else /* !ARM64_SWAPPER_USES_SECTION_MAPS */ int __meminit vmemmap_populate(unsigned long start, unsigned long end, int node, @@ -1102,7 +1120,7 @@ int __meminit vmemmap_populate(unsigned long start, unsigned long end, int node, if (pmd_none(READ_ONCE(*pmdp))) { void *p = NULL; - p = vmemmap_alloc_block_buf(PMD_SIZE, node, NULL); + p = vmemmap_alloc_block_buf(PMD_SIZE, node, altmap); if (!p) return -ENOMEM; @@ -1120,7 +1138,7 @@ void vmemmap_free(unsigned long start, unsigned long end, #ifdef CONFIG_MEMORY_HOTPLUG WARN_ON((start < VMEMMAP_START) || (end > VMEMMAP_END)); - unmap_hotplug_range(start, end, true); + unmap_hotplug_range(start, end, true, altmap); free_empty_tables(start, end, VMEMMAP_START, VMEMMAP_END); #endif } @@ -1411,7 +1429,7 @@ static void __remove_pgd_mapping(pgd_t *pgdir, unsigned long start, u64 size) WARN_ON(pgdir != init_mm.pgd); WARN_ON((start < PAGE_OFFSET) || (end > PAGE_END)); - unmap_hotplug_range(start, end, false); + unmap_hotplug_range(start, end, false, NULL); free_empty_tables(start, end, PAGE_OFFSET, PAGE_END); } -- GitLab From d70cec8983241a6aafadf78e2d65bbafac87ab6a Mon Sep 17 00:00:00 2001 From: Miaohe Lin Date: Thu, 6 Aug 2020 23:23:34 -0700 Subject: [PATCH 1421/1476] mm: mmap: merge vma after call_mmap() if possible The vm_flags may be changed after call_mmap() because drivers may set some flags for their own purpose. As a result, we failed to merge the adjacent vma due to the different vm_flags as userspace can't pass in the same one. Try to merge vma after call_mmap() to fix this issue. Signed-off-by: Hongxiang Lou Signed-off-by: Miaohe Lin Signed-off-by: Andrew Morton Reviewed-by: Andrew Morton Link: http://lkml.kernel.org/r/1594954065-23733-1-git-send-email-linmiaohe@huawei.com Signed-off-by: Linus Torvalds --- mm/mmap.c | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/mm/mmap.c b/mm/mmap.c index 853ae3aebc3d3..ba39409cdb173 100644 --- a/mm/mmap.c +++ b/mm/mmap.c @@ -1690,7 +1690,7 @@ unsigned long mmap_region(struct file *file, unsigned long addr, struct list_head *uf) { struct mm_struct *mm = current->mm; - struct vm_area_struct *vma, *prev; + struct vm_area_struct *vma, *prev, *merge; int error; struct rb_node **rb_link, *rb_parent; unsigned long charged = 0; @@ -1774,6 +1774,25 @@ unsigned long mmap_region(struct file *file, unsigned long addr, if (error) goto unmap_and_free_vma; + /* If vm_flags changed after call_mmap(), we should try merge vma again + * as we may succeed this time. + */ + if (unlikely(vm_flags != vma->vm_flags && prev)) { + merge = vma_merge(mm, prev, vma->vm_start, vma->vm_end, vma->vm_flags, + NULL, vma->vm_file, vma->vm_pgoff, NULL, NULL_VM_UFFD_CTX); + if (merge) { + fput(file); + vm_area_free(vma); + vma = merge; + /* Update vm_flags and possible addr to pick up the change. We don't + * warn here if addr changed as the vma is not linked by vma_link(). + */ + addr = vma->vm_start; + vm_flags = vma->vm_flags; + goto unmap_writable; + } + } + /* Can addr have changed?? * * Answer: Yes, several device drivers can do it in their @@ -1796,6 +1815,7 @@ unsigned long mmap_region(struct file *file, unsigned long addr, vma_link(mm, vma, prev, rb_link, rb_parent); /* Once vma denies write, undo our temporary denial count */ if (file) { +unmap_writable: if (vm_flags & VM_SHARED) mapping_unmap_writable(file->f_mapping); if (vm_flags & VM_DENYWRITE) -- GitLab From 45e55300f11495ed58c53427da7f0d958800a30f Mon Sep 17 00:00:00 2001 From: Peter Collingbourne Date: Thu, 6 Aug 2020 23:23:37 -0700 Subject: [PATCH 1422/1476] mm: remove unnecessary wrapper function do_mmap_pgoff() The current split between do_mmap() and do_mmap_pgoff() was introduced in commit 1fcfd8db7f82 ("mm, mpx: add "vm_flags_t vm_flags" arg to do_mmap_pgoff()") to support MPX. The wrapper function do_mmap_pgoff() always passed 0 as the value of the vm_flags argument to do_mmap(). However, MPX support has subsequently been removed from the kernel and there were no more direct callers of do_mmap(); all calls were going via do_mmap_pgoff(). Simplify the code by removing do_mmap_pgoff() and changing all callers to directly call do_mmap(), which now no longer takes a vm_flags argument. Signed-off-by: Peter Collingbourne Signed-off-by: Andrew Morton Reviewed-by: Andrew Morton Reviewed-by: David Hildenbrand Link: http://lkml.kernel.org/r/20200727194109.1371462-1-pcc@google.com Signed-off-by: Linus Torvalds --- fs/aio.c | 6 +++--- fs/hugetlbfs/inode.c | 2 +- include/linux/fs.h | 2 +- include/linux/mm.h | 12 +----------- ipc/shm.c | 2 +- mm/mmap.c | 16 ++++++++-------- mm/nommu.c | 6 +++--- mm/shmem.c | 2 +- mm/util.c | 4 ++-- 9 files changed, 21 insertions(+), 31 deletions(-) diff --git a/fs/aio.c b/fs/aio.c index 91e7cc4a9f179..5736bff48e9e9 100644 --- a/fs/aio.c +++ b/fs/aio.c @@ -525,9 +525,9 @@ static int aio_setup_ring(struct kioctx *ctx, unsigned int nr_events) return -EINTR; } - ctx->mmap_base = do_mmap_pgoff(ctx->aio_ring_file, 0, ctx->mmap_size, - PROT_READ | PROT_WRITE, - MAP_SHARED, 0, &unused, NULL); + ctx->mmap_base = do_mmap(ctx->aio_ring_file, 0, ctx->mmap_size, + PROT_READ | PROT_WRITE, + MAP_SHARED, 0, &unused, NULL); mmap_write_unlock(mm); if (IS_ERR((void *)ctx->mmap_base)) { ctx->mmap_size = 0; diff --git a/fs/hugetlbfs/inode.c b/fs/hugetlbfs/inode.c index ef5313f9c78fe..523954d00dff6 100644 --- a/fs/hugetlbfs/inode.c +++ b/fs/hugetlbfs/inode.c @@ -140,7 +140,7 @@ static int hugetlbfs_file_mmap(struct file *file, struct vm_area_struct *vma) * already been checked by prepare_hugepage_range. If you add * any error returns here, do so after setting VM_HUGETLB, so * is_vm_hugetlb_page tests below unmap_region go the right - * way when do_mmap_pgoff unwinds (may be important on powerpc + * way when do_mmap unwinds (may be important on powerpc * and ia64). */ vma->vm_flags |= VM_HUGETLB | VM_DONTEXPAND; diff --git a/include/linux/fs.h b/include/linux/fs.h index b1c3a14f12e82..f5da33bcaaae0 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -528,7 +528,7 @@ static inline int mapping_mapped(struct address_space *mapping) /* * Might pages of this file have been modified in userspace? - * Note that i_mmap_writable counts all VM_SHARED vmas: do_mmap_pgoff + * Note that i_mmap_writable counts all VM_SHARED vmas: do_mmap * marks vma as VM_SHARED if it is shared, and the file was opened for * writing i.e. vma may be mprotected writable even if now readonly. * diff --git a/include/linux/mm.h b/include/linux/mm.h index 98e35df8d88e3..cf7e4605ff3f6 100644 --- a/include/linux/mm.h +++ b/include/linux/mm.h @@ -2546,23 +2546,13 @@ extern unsigned long mmap_region(struct file *file, unsigned long addr, struct list_head *uf); extern unsigned long do_mmap(struct file *file, unsigned long addr, unsigned long len, unsigned long prot, unsigned long flags, - vm_flags_t vm_flags, unsigned long pgoff, unsigned long *populate, - struct list_head *uf); + unsigned long pgoff, unsigned long *populate, struct list_head *uf); extern int __do_munmap(struct mm_struct *, unsigned long, size_t, struct list_head *uf, bool downgrade); extern int do_munmap(struct mm_struct *, unsigned long, size_t, struct list_head *uf); extern int do_madvise(unsigned long start, size_t len_in, int behavior); -static inline unsigned long -do_mmap_pgoff(struct file *file, unsigned long addr, - unsigned long len, unsigned long prot, unsigned long flags, - unsigned long pgoff, unsigned long *populate, - struct list_head *uf) -{ - return do_mmap(file, addr, len, prot, flags, 0, pgoff, populate, uf); -} - #ifdef CONFIG_MMU extern int __mm_populate(unsigned long addr, unsigned long len, int ignore_errors); diff --git a/ipc/shm.c b/ipc/shm.c index 0a6dd94afa217..bf38d7e2fbe9e 100644 --- a/ipc/shm.c +++ b/ipc/shm.c @@ -1558,7 +1558,7 @@ long do_shmat(int shmid, char __user *shmaddr, int shmflg, goto invalid; } - addr = do_mmap_pgoff(file, addr, size, prot, flags, 0, &populate, NULL); + addr = do_mmap(file, addr, size, prot, flags, 0, &populate, NULL); *raddr = addr; err = 0; if (IS_ERR_VALUE(addr)) diff --git a/mm/mmap.c b/mm/mmap.c index ba39409cdb173..40248d84ad5fb 100644 --- a/mm/mmap.c +++ b/mm/mmap.c @@ -1030,7 +1030,7 @@ static inline int is_mergeable_anon_vma(struct anon_vma *anon_vma1, * anon_vmas, nor if same anon_vma is assigned but offsets incompatible. * * We don't check here for the merged mmap wrapping around the end of pagecache - * indices (16TB on ia32) because do_mmap_pgoff() does not permit mmap's which + * indices (16TB on ia32) because do_mmap() does not permit mmap's which * wrap, nor mmaps which cover the final page at index -1UL. */ static int @@ -1365,11 +1365,11 @@ static inline bool file_mmap_ok(struct file *file, struct inode *inode, */ unsigned long do_mmap(struct file *file, unsigned long addr, unsigned long len, unsigned long prot, - unsigned long flags, vm_flags_t vm_flags, - unsigned long pgoff, unsigned long *populate, - struct list_head *uf) + unsigned long flags, unsigned long pgoff, + unsigned long *populate, struct list_head *uf) { struct mm_struct *mm = current->mm; + vm_flags_t vm_flags; int pkey = 0; *populate = 0; @@ -1431,7 +1431,7 @@ unsigned long do_mmap(struct file *file, unsigned long addr, * to. we assume access permissions have been handled by the open * of the memory object, so we don't do any here. */ - vm_flags |= calc_vm_prot_bits(prot, pkey) | calc_vm_flag_bits(flags) | + vm_flags = calc_vm_prot_bits(prot, pkey) | calc_vm_flag_bits(flags) | mm->def_flags | VM_MAYREAD | VM_MAYWRITE | VM_MAYEXEC; if (flags & MAP_LOCKED) @@ -2230,7 +2230,7 @@ get_unmapped_area(struct file *file, unsigned long addr, unsigned long len, /* * mmap_region() will call shmem_zero_setup() to create a file, * so use shmem's get_unmapped_area in case it can be huge. - * do_mmap_pgoff() will clear pgoff, so match alignment. + * do_mmap() will clear pgoff, so match alignment. */ pgoff = 0; get_area = shmem_get_unmapped_area; @@ -3003,7 +3003,7 @@ SYSCALL_DEFINE5(remap_file_pages, unsigned long, start, unsigned long, size, } file = get_file(vma->vm_file); - ret = do_mmap_pgoff(vma->vm_file, start, size, + ret = do_mmap(vma->vm_file, start, size, prot, flags, pgoff, &populate, NULL); fput(file); out: @@ -3223,7 +3223,7 @@ int insert_vm_struct(struct mm_struct *mm, struct vm_area_struct *vma) * By setting it to reflect the virtual start address of the * vma, merges and splits can happen in a seamless way, just * using the existing file pgoff checks and manipulations. - * Similarly in do_mmap_pgoff and in do_brk. + * Similarly in do_mmap and in do_brk. */ if (vma_is_anonymous(vma)) { BUG_ON(vma->anon_vma); diff --git a/mm/nommu.c b/mm/nommu.c index 314174817b04d..340ae7774c132 100644 --- a/mm/nommu.c +++ b/mm/nommu.c @@ -1078,7 +1078,6 @@ unsigned long do_mmap(struct file *file, unsigned long len, unsigned long prot, unsigned long flags, - vm_flags_t vm_flags, unsigned long pgoff, unsigned long *populate, struct list_head *uf) @@ -1086,6 +1085,7 @@ unsigned long do_mmap(struct file *file, struct vm_area_struct *vma; struct vm_region *region; struct rb_node *rb; + vm_flags_t vm_flags; unsigned long capabilities, result; int ret; @@ -1104,7 +1104,7 @@ unsigned long do_mmap(struct file *file, /* we've determined that we can make the mapping, now translate what we * now know into VMA flags */ - vm_flags |= determine_vm_flags(file, prot, flags, capabilities); + vm_flags = determine_vm_flags(file, prot, flags, capabilities); /* we're going to need to record the mapping */ region = kmem_cache_zalloc(vm_region_jar, GFP_KERNEL); @@ -1763,7 +1763,7 @@ EXPORT_SYMBOL_GPL(access_process_vm); * * Check the shared mappings on an inode on behalf of a shrinking truncate to * make sure that that any outstanding VMAs aren't broken and then shrink the - * vm_regions that extend that beyond so that do_mmap_pgoff() doesn't + * vm_regions that extend that beyond so that do_mmap() doesn't * automatically grant mappings that are too large. */ int nommu_shrink_inode_mappings(struct inode *inode, size_t size, diff --git a/mm/shmem.c b/mm/shmem.c index c5c281893bb8c..eb6b36d897224 100644 --- a/mm/shmem.c +++ b/mm/shmem.c @@ -4245,7 +4245,7 @@ EXPORT_SYMBOL_GPL(shmem_file_setup_with_mnt); /** * shmem_zero_setup - setup a shared anonymous mapping - * @vma: the vma to be mmapped is prepared by do_mmap_pgoff + * @vma: the vma to be mmapped is prepared by do_mmap */ int shmem_zero_setup(struct vm_area_struct *vma) { diff --git a/mm/util.c b/mm/util.c index 8d6280c05238a..5ef378a2a038b 100644 --- a/mm/util.c +++ b/mm/util.c @@ -503,8 +503,8 @@ unsigned long vm_mmap_pgoff(struct file *file, unsigned long addr, if (!ret) { if (mmap_write_lock_killable(mm)) return -EINTR; - ret = do_mmap_pgoff(file, addr, len, prot, flag, pgoff, - &populate, &uf); + ret = do_mmap(file, addr, len, prot, flag, pgoff, &populate, + &uf); mmap_write_unlock(mm); userfaultfd_unmap_complete(mm, &uf); if (populate) -- GitLab From b8aa9d9d95b3b4b60d42ac95f65d33a92527aef3 Mon Sep 17 00:00:00 2001 From: Wei Yang Date: Thu, 6 Aug 2020 23:23:40 -0700 Subject: [PATCH 1423/1476] mm/mremap: it is sure to have enough space when extent meets requirement Patch series "mm/mremap: cleanup move_page_tables() a little", v5. move_page_tables() tries to move page table by PMD or PTE. The root reason is if it tries to move PMD, both old and new range should be PMD aligned. But current code calculate old range and new range separately. This leads to some redundant check and calculation. This cleanup tries to consolidate the range check in one place to reduce some extra range handling. This patch (of 3): old_end is passed to these two functions to check whether there is enough space to do the move, while this check is done before invoking these functions. These two functions only would be invoked when extent meets the requirement and there is one check before invoking these functions: if (extent > old_end - old_addr) extent = old_end - old_addr; This implies (old_end - old_addr) won't fail the check in these two functions. Signed-off-by: Wei Yang Signed-off-by: Andrew Morton Tested-by: Dmitry Osipenko Acked-by: Kirill A. Shutemov Cc: Vlastimil Babka Cc: Yang Shi Cc: Thomas Hellstrom (VMware) Cc: Anshuman Khandual Cc: Sean Christopherson Cc: Wei Yang Cc: Peter Xu Cc: Aneesh Kumar K.V Cc: Matthew Wilcox Cc: Thomas Hellstrom Link: http://lkml.kernel.org/r/20200710092835.56368-1-richard.weiyang@linux.alibaba.com Link: http://lkml.kernel.org/r/20200710092835.56368-2-richard.weiyang@linux.alibaba.com Link: http://lkml.kernel.org/r/20200708095028.41706-1-richard.weiyang@linux.alibaba.com Link: http://lkml.kernel.org/r/20200708095028.41706-2-richard.weiyang@linux.alibaba.com Signed-off-by: Linus Torvalds --- include/linux/huge_mm.h | 2 +- mm/huge_memory.c | 7 ++----- mm/mremap.c | 10 ++++------ 3 files changed, 7 insertions(+), 12 deletions(-) diff --git a/include/linux/huge_mm.h b/include/linux/huge_mm.h index 71f20776b06ca..17c4c49751451 100644 --- a/include/linux/huge_mm.h +++ b/include/linux/huge_mm.h @@ -42,7 +42,7 @@ extern int mincore_huge_pmd(struct vm_area_struct *vma, pmd_t *pmd, unsigned long addr, unsigned long end, unsigned char *vec); extern bool move_huge_pmd(struct vm_area_struct *vma, unsigned long old_addr, - unsigned long new_addr, unsigned long old_end, + unsigned long new_addr, pmd_t *old_pmd, pmd_t *new_pmd); extern int change_huge_pmd(struct vm_area_struct *vma, pmd_t *pmd, unsigned long addr, pgprot_t newprot, diff --git a/mm/huge_memory.c b/mm/huge_memory.c index 78c84bee7e294..1e580fdad4d03 100644 --- a/mm/huge_memory.c +++ b/mm/huge_memory.c @@ -1722,17 +1722,14 @@ static pmd_t move_soft_dirty_pmd(pmd_t pmd) } bool move_huge_pmd(struct vm_area_struct *vma, unsigned long old_addr, - unsigned long new_addr, unsigned long old_end, - pmd_t *old_pmd, pmd_t *new_pmd) + unsigned long new_addr, pmd_t *old_pmd, pmd_t *new_pmd) { spinlock_t *old_ptl, *new_ptl; pmd_t pmd; struct mm_struct *mm = vma->vm_mm; bool force_flush = false; - if ((old_addr & ~HPAGE_PMD_MASK) || - (new_addr & ~HPAGE_PMD_MASK) || - old_end - old_addr < HPAGE_PMD_SIZE) + if ((old_addr & ~HPAGE_PMD_MASK) || (new_addr & ~HPAGE_PMD_MASK)) return false; /* diff --git a/mm/mremap.c b/mm/mremap.c index 6b153dc05fe48..4f434062d1540 100644 --- a/mm/mremap.c +++ b/mm/mremap.c @@ -193,15 +193,13 @@ static void move_ptes(struct vm_area_struct *vma, pmd_t *old_pmd, #ifdef CONFIG_HAVE_MOVE_PMD static bool move_normal_pmd(struct vm_area_struct *vma, unsigned long old_addr, - unsigned long new_addr, unsigned long old_end, - pmd_t *old_pmd, pmd_t *new_pmd) + unsigned long new_addr, pmd_t *old_pmd, pmd_t *new_pmd) { spinlock_t *old_ptl, *new_ptl; struct mm_struct *mm = vma->vm_mm; pmd_t pmd; - if ((old_addr & ~PMD_MASK) || (new_addr & ~PMD_MASK) - || old_end - old_addr < PMD_SIZE) + if ((old_addr & ~PMD_MASK) || (new_addr & ~PMD_MASK)) return false; /* @@ -292,7 +290,7 @@ unsigned long move_page_tables(struct vm_area_struct *vma, if (need_rmap_locks) take_rmap_locks(vma); moved = move_huge_pmd(vma, old_addr, new_addr, - old_end, old_pmd, new_pmd); + old_pmd, new_pmd); if (need_rmap_locks) drop_rmap_locks(vma); if (moved) @@ -312,7 +310,7 @@ unsigned long move_page_tables(struct vm_area_struct *vma, if (need_rmap_locks) take_rmap_locks(vma); moved = move_normal_pmd(vma, old_addr, new_addr, - old_end, old_pmd, new_pmd); + old_pmd, new_pmd); if (need_rmap_locks) drop_rmap_locks(vma); if (moved) -- GitLab From 9ad9718bfa41f6ae62fb2a7244357e99ac00d25a Mon Sep 17 00:00:00 2001 From: Wei Yang Date: Thu, 6 Aug 2020 23:23:44 -0700 Subject: [PATCH 1424/1476] mm/mremap: calculate extent in one place Page tables is moved on the base of PMD. This requires both source and destination range should meet the requirement. Current code works well since move_huge_pmd() and move_normal_pmd() would check old_addr and new_addr again. And then return to move_ptes() if the either of them is not aligned. Instead of calculating the extent separately, it is better to calculate in one place, so we know it is not necessary to try move pmd. By doing so, the logic seems a little clear. Signed-off-by: Wei Yang Signed-off-by: Andrew Morton Tested-by: Dmitry Osipenko Acked-by: Kirill A. Shutemov Cc: Aneesh Kumar K.V Cc: Anshuman Khandual Cc: Matthew Wilcox Cc: Peter Xu Cc: Sean Christopherson Cc: Thomas Hellstrom Cc: Thomas Hellstrom (VMware) Cc: Vlastimil Babka Cc: Yang Shi Link: http://lkml.kernel.org/r/20200708095028.41706-3-richard.weiyang@linux.alibaba.com Signed-off-by: Linus Torvalds --- mm/mremap.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/mm/mremap.c b/mm/mremap.c index 4f434062d1540..c3622bfcf6d9b 100644 --- a/mm/mremap.c +++ b/mm/mremap.c @@ -277,6 +277,9 @@ unsigned long move_page_tables(struct vm_area_struct *vma, extent = next - old_addr; if (extent > old_end - old_addr) extent = old_end - old_addr; + next = (new_addr + PMD_SIZE) & PMD_MASK; + if (extent > next - new_addr) + extent = next - new_addr; old_pmd = get_old_pmd(vma->vm_mm, old_addr); if (!old_pmd) continue; @@ -320,9 +323,6 @@ unsigned long move_page_tables(struct vm_area_struct *vma, if (pte_alloc(new_vma->vm_mm, new_pmd)) break; - next = (new_addr + PMD_SIZE) & PMD_MASK; - if (extent > next - new_addr) - extent = next - new_addr; move_ptes(vma, old_pmd, old_addr, old_addr + extent, new_vma, new_pmd, new_addr, need_rmap_locks); } -- GitLab From 349d9fbb0b0a6734bcac9c08c5cc21992da5d2e6 Mon Sep 17 00:00:00 2001 From: Wei Yang Date: Thu, 6 Aug 2020 23:23:48 -0700 Subject: [PATCH 1425/1476] mm/mremap: start addresses are properly aligned After previous cleanup, extent is the minimal step for both source and destination. This means when extent is HPAGE_PMD_SIZE or PMD_SIZE, old_addr and new_addr are properly aligned too. Since these two functions are only invoked in move_page_tables, it is safe to remove the check now. Signed-off-by: Wei Yang Signed-off-by: Andrew Morton Tested-by: Dmitry Osipenko Acked-by: Kirill A. Shutemov Cc: Aneesh Kumar K.V Cc: Anshuman Khandual Cc: Matthew Wilcox Cc: Peter Xu Cc: Sean Christopherson Cc: Thomas Hellstrom Cc: Thomas Hellstrom (VMware) Cc: Vlastimil Babka Cc: Yang Shi Link: http://lkml.kernel.org/r/20200708095028.41706-4-richard.weiyang@linux.alibaba.com Signed-off-by: Linus Torvalds --- mm/huge_memory.c | 3 --- mm/mremap.c | 3 --- 2 files changed, 6 deletions(-) diff --git a/mm/huge_memory.c b/mm/huge_memory.c index 1e580fdad4d03..462a7dbd63502 100644 --- a/mm/huge_memory.c +++ b/mm/huge_memory.c @@ -1729,9 +1729,6 @@ bool move_huge_pmd(struct vm_area_struct *vma, unsigned long old_addr, struct mm_struct *mm = vma->vm_mm; bool force_flush = false; - if ((old_addr & ~HPAGE_PMD_MASK) || (new_addr & ~HPAGE_PMD_MASK)) - return false; - /* * The destination pmd shouldn't be established, free_pgtables() * should have release it. diff --git a/mm/mremap.c b/mm/mremap.c index c3622bfcf6d9b..138abbae4f758 100644 --- a/mm/mremap.c +++ b/mm/mremap.c @@ -199,9 +199,6 @@ static bool move_normal_pmd(struct vm_area_struct *vma, unsigned long old_addr, struct mm_struct *mm = vma->vm_mm; pmd_t pmd; - if ((old_addr & ~PMD_MASK) || (new_addr & ~PMD_MASK)) - return false; - /* * The destination pmd shouldn't be established, free_pgtables() * should have released it. -- GitLab From 7a309195d11cde854eb75559fbd6b48f9e518f25 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ricardo=20Ca=C3=B1uelo?= Date: Thu, 6 Aug 2020 23:23:51 -0700 Subject: [PATCH 1426/1476] selftests: add mincore() tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a test suite for the mincore() syscall. It tests most of its use cases as well as its interface. Tests implemented: - basic interface test - behavior on anonymous mappings - behavior on anonymous mappings with huge tlb pages - file-backed mapping with a regular file - file-backed mapping with a tmpfs file Signed-off-by: Ricardo Cañuelo Signed-off-by: Andrew Morton Link: http://lkml.kernel.org/r/20200728100450.4065-1-ricardo.canuelo@collabora.com Signed-off-by: Linus Torvalds --- tools/testing/selftests/Makefile | 1 + tools/testing/selftests/mincore/.gitignore | 2 + tools/testing/selftests/mincore/Makefile | 6 + .../selftests/mincore/mincore_selftest.c | 361 ++++++++++++++++++ 4 files changed, 370 insertions(+) create mode 100644 tools/testing/selftests/mincore/.gitignore create mode 100644 tools/testing/selftests/mincore/Makefile create mode 100644 tools/testing/selftests/mincore/mincore_selftest.c diff --git a/tools/testing/selftests/Makefile b/tools/testing/selftests/Makefile index e03bc15ce7312..9018f45d631de 100644 --- a/tools/testing/selftests/Makefile +++ b/tools/testing/selftests/Makefile @@ -32,6 +32,7 @@ TARGETS += lkdtm TARGETS += membarrier TARGETS += memfd TARGETS += memory-hotplug +TARGETS += mincore TARGETS += mount TARGETS += mqueue TARGETS += net diff --git a/tools/testing/selftests/mincore/.gitignore b/tools/testing/selftests/mincore/.gitignore new file mode 100644 index 0000000000000..15c4dfc2df004 --- /dev/null +++ b/tools/testing/selftests/mincore/.gitignore @@ -0,0 +1,2 @@ +# SPDX-License-Identifier: GPL-2.0+ +mincore_selftest diff --git a/tools/testing/selftests/mincore/Makefile b/tools/testing/selftests/mincore/Makefile new file mode 100644 index 0000000000000..38c7db1e89265 --- /dev/null +++ b/tools/testing/selftests/mincore/Makefile @@ -0,0 +1,6 @@ +# SPDX-License-Identifier: GPL-2.0+ + +CFLAGS += -Wall + +TEST_GEN_PROGS := mincore_selftest +include ../lib.mk diff --git a/tools/testing/selftests/mincore/mincore_selftest.c b/tools/testing/selftests/mincore/mincore_selftest.c new file mode 100644 index 0000000000000..5a1e85ff5d32a --- /dev/null +++ b/tools/testing/selftests/mincore/mincore_selftest.c @@ -0,0 +1,361 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * kselftest suite for mincore(). + * + * Copyright (C) 2020 Collabora, Ltd. + */ + +#define _GNU_SOURCE + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../kselftest.h" +#include "../kselftest_harness.h" + +/* Default test file size: 4MB */ +#define MB (1UL << 20) +#define FILE_SIZE (4 * MB) + + +/* + * Tests the user interface. This test triggers most of the documented + * error conditions in mincore(). + */ +TEST(basic_interface) +{ + int retval; + int page_size; + unsigned char vec[1]; + char *addr; + + page_size = sysconf(_SC_PAGESIZE); + + /* Query a 0 byte sized range */ + retval = mincore(0, 0, vec); + EXPECT_EQ(0, retval); + + /* Addresses in the specified range are invalid or unmapped */ + errno = 0; + retval = mincore(NULL, page_size, vec); + EXPECT_EQ(-1, retval); + EXPECT_EQ(ENOMEM, errno); + + errno = 0; + addr = mmap(NULL, page_size, PROT_READ | PROT_WRITE, + MAP_SHARED | MAP_ANONYMOUS, -1, 0); + ASSERT_NE(MAP_FAILED, addr) { + TH_LOG("mmap error: %s", strerror(errno)); + } + + /* argument is not page-aligned */ + errno = 0; + retval = mincore(addr + 1, page_size, vec); + EXPECT_EQ(-1, retval); + EXPECT_EQ(EINVAL, errno); + + /* argument is too large */ + errno = 0; + retval = mincore(addr, -1, vec); + EXPECT_EQ(-1, retval); + EXPECT_EQ(ENOMEM, errno); + + /* argument points to an illegal address */ + errno = 0; + retval = mincore(addr, page_size, NULL); + EXPECT_EQ(-1, retval); + EXPECT_EQ(EFAULT, errno); + munmap(addr, page_size); +} + + +/* + * Test mincore() behavior on a private anonymous page mapping. + * Check that the page is not loaded into memory right after the mapping + * but after accessing it (on-demand allocation). + * Then free the page and check that it's not memory-resident. + */ +TEST(check_anonymous_locked_pages) +{ + unsigned char vec[1]; + char *addr; + int retval; + int page_size; + + page_size = sysconf(_SC_PAGESIZE); + + /* Map one page and check it's not memory-resident */ + errno = 0; + addr = mmap(NULL, page_size, PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + ASSERT_NE(MAP_FAILED, addr) { + TH_LOG("mmap error: %s", strerror(errno)); + } + retval = mincore(addr, page_size, vec); + ASSERT_EQ(0, retval); + ASSERT_EQ(0, vec[0]) { + TH_LOG("Page found in memory before use"); + } + + /* Touch the page and check again. It should now be in memory */ + addr[0] = 1; + mlock(addr, page_size); + retval = mincore(addr, page_size, vec); + ASSERT_EQ(0, retval); + ASSERT_EQ(1, vec[0]) { + TH_LOG("Page not found in memory after use"); + } + + /* + * It shouldn't be memory-resident after unlocking it and + * marking it as unneeded. + */ + munlock(addr, page_size); + madvise(addr, page_size, MADV_DONTNEED); + retval = mincore(addr, page_size, vec); + ASSERT_EQ(0, retval); + ASSERT_EQ(0, vec[0]) { + TH_LOG("Page in memory after being zapped"); + } + munmap(addr, page_size); +} + + +/* + * Check mincore() behavior on huge pages. + * This test will be skipped if the mapping fails (ie. if there are no + * huge pages available). + * + * Make sure the system has at least one free huge page, check + * "HugePages_Free" in /proc/meminfo. + * Increment /sys/kernel/mm/hugepages/hugepages-2048kB/nr_hugepages if + * needed. + */ +TEST(check_huge_pages) +{ + unsigned char vec[1]; + char *addr; + int retval; + int page_size; + + page_size = sysconf(_SC_PAGESIZE); + + errno = 0; + addr = mmap(NULL, page_size, PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANONYMOUS | MAP_HUGETLB, + -1, 0); + if (addr == MAP_FAILED) { + if (errno == ENOMEM) + SKIP(return, "No huge pages available."); + else + TH_LOG("mmap error: %s", strerror(errno)); + } + retval = mincore(addr, page_size, vec); + ASSERT_EQ(0, retval); + ASSERT_EQ(0, vec[0]) { + TH_LOG("Page found in memory before use"); + } + + addr[0] = 1; + mlock(addr, page_size); + retval = mincore(addr, page_size, vec); + ASSERT_EQ(0, retval); + ASSERT_EQ(1, vec[0]) { + TH_LOG("Page not found in memory after use"); + } + + munlock(addr, page_size); + munmap(addr, page_size); +} + + +/* + * Test mincore() behavior on a file-backed page. + * No pages should be loaded into memory right after the mapping. Then, + * accessing any address in the mapping range should load the page + * containing the address and a number of subsequent pages (readahead). + * + * The actual readahead settings depend on the test environment, so we + * can't make a lot of assumptions about that. This test covers the most + * general cases. + */ +TEST(check_file_mmap) +{ + unsigned char *vec; + int vec_size; + char *addr; + int retval; + int page_size; + int fd; + int i; + int ra_pages = 0; + + page_size = sysconf(_SC_PAGESIZE); + vec_size = FILE_SIZE / page_size; + if (FILE_SIZE % page_size) + vec_size++; + + vec = calloc(vec_size, sizeof(unsigned char)); + ASSERT_NE(NULL, vec) { + TH_LOG("Can't allocate array"); + } + + errno = 0; + fd = open(".", O_TMPFILE | O_RDWR, 0600); + ASSERT_NE(-1, fd) { + TH_LOG("Can't create temporary file: %s", + strerror(errno)); + } + errno = 0; + retval = fallocate(fd, 0, 0, FILE_SIZE); + ASSERT_EQ(0, retval) { + TH_LOG("Error allocating space for the temporary file: %s", + strerror(errno)); + } + + /* + * Map the whole file, the pages shouldn't be fetched yet. + */ + errno = 0; + addr = mmap(NULL, FILE_SIZE, PROT_READ | PROT_WRITE, + MAP_SHARED, fd, 0); + ASSERT_NE(MAP_FAILED, addr) { + TH_LOG("mmap error: %s", strerror(errno)); + } + retval = mincore(addr, FILE_SIZE, vec); + ASSERT_EQ(0, retval); + for (i = 0; i < vec_size; i++) { + ASSERT_EQ(0, vec[i]) { + TH_LOG("Unexpected page in memory"); + } + } + + /* + * Touch a page in the middle of the mapping. We expect the next + * few pages (the readahead window) to be populated too. + */ + addr[FILE_SIZE / 2] = 1; + retval = mincore(addr, FILE_SIZE, vec); + ASSERT_EQ(0, retval); + ASSERT_EQ(1, vec[FILE_SIZE / 2 / page_size]) { + TH_LOG("Page not found in memory after use"); + } + + i = FILE_SIZE / 2 / page_size + 1; + while (i < vec_size && vec[i]) { + ra_pages++; + i++; + } + EXPECT_GT(ra_pages, 0) { + TH_LOG("No read-ahead pages found in memory"); + } + + EXPECT_LT(i, vec_size) { + TH_LOG("Read-ahead pages reached the end of the file"); + } + /* + * End of the readahead window. The rest of the pages shouldn't + * be in memory. + */ + if (i < vec_size) { + while (i < vec_size && !vec[i]) + i++; + EXPECT_EQ(vec_size, i) { + TH_LOG("Unexpected page in memory beyond readahead window"); + } + } + + munmap(addr, FILE_SIZE); + close(fd); + free(vec); +} + + +/* + * Test mincore() behavior on a page backed by a tmpfs file. This test + * performs the same steps as the previous one. However, we don't expect + * any readahead in this case. + */ +TEST(check_tmpfs_mmap) +{ + unsigned char *vec; + int vec_size; + char *addr; + int retval; + int page_size; + int fd; + int i; + int ra_pages = 0; + + page_size = sysconf(_SC_PAGESIZE); + vec_size = FILE_SIZE / page_size; + if (FILE_SIZE % page_size) + vec_size++; + + vec = calloc(vec_size, sizeof(unsigned char)); + ASSERT_NE(NULL, vec) { + TH_LOG("Can't allocate array"); + } + + errno = 0; + fd = open("/dev/shm", O_TMPFILE | O_RDWR, 0600); + ASSERT_NE(-1, fd) { + TH_LOG("Can't create temporary file: %s", + strerror(errno)); + } + errno = 0; + retval = fallocate(fd, 0, 0, FILE_SIZE); + ASSERT_EQ(0, retval) { + TH_LOG("Error allocating space for the temporary file: %s", + strerror(errno)); + } + + /* + * Map the whole file, the pages shouldn't be fetched yet. + */ + errno = 0; + addr = mmap(NULL, FILE_SIZE, PROT_READ | PROT_WRITE, + MAP_SHARED, fd, 0); + ASSERT_NE(MAP_FAILED, addr) { + TH_LOG("mmap error: %s", strerror(errno)); + } + retval = mincore(addr, FILE_SIZE, vec); + ASSERT_EQ(0, retval); + for (i = 0; i < vec_size; i++) { + ASSERT_EQ(0, vec[i]) { + TH_LOG("Unexpected page in memory"); + } + } + + /* + * Touch a page in the middle of the mapping. We expect only + * that page to be fetched into memory. + */ + addr[FILE_SIZE / 2] = 1; + retval = mincore(addr, FILE_SIZE, vec); + ASSERT_EQ(0, retval); + ASSERT_EQ(1, vec[FILE_SIZE / 2 / page_size]) { + TH_LOG("Page not found in memory after use"); + } + + i = FILE_SIZE / 2 / page_size + 1; + while (i < vec_size && vec[i]) { + ra_pages++; + i++; + } + ASSERT_EQ(ra_pages, 0) { + TH_LOG("Read-ahead pages found in memory"); + } + + munmap(addr, FILE_SIZE); + close(fd); + free(vec); +} + +TEST_HARNESS_MAIN -- GitLab From ef69bc9f689de8380688be742f9b9df615d42429 Mon Sep 17 00:00:00 2001 From: Wei Yang Date: Thu, 6 Aug 2020 23:23:55 -0700 Subject: [PATCH 1427/1476] mm/sparse: never partially remove memmap for early section For early sections, its memmap is handled specially even sub-section is enabled. The memmap could only be populated as a whole. Quoted from the comment of section_activate(): * The early init code does not consider partially populated * initial sections, it simply assumes that memory will never be * referenced. If we hot-add memory into such a section then we * do not need to populate the memmap and can simply reuse what * is already there. While current section_deactivate() breaks this rule. When hot-remove a sub-section, section_deactivate() would depopulate its memmap. The consequence is if we hot-add this subsection again, its memmap never get proper populated. We can reproduce the case by following steps: 1. Hacking qemu to allow sub-section early section : diff --git a/hw/i386/pc.c b/hw/i386/pc.c : index 51b3050d01..c6a78d83c0 100644 : --- a/hw/i386/pc.c : +++ b/hw/i386/pc.c : @@ -1010,7 +1010,7 @@ void pc_memory_init(PCMachineState *pcms, : } : : machine->device_memory->base = : - ROUND_UP(0x100000000ULL + x86ms->above_4g_mem_size, 1 * GiB); : + 0x100000000ULL + x86ms->above_4g_mem_size; : : if (pcmc->enforce_aligned_dimm) { : /* size device region assuming 1G page max alignment per slot */ 2. Bootup qemu with PSE disabled and a sub-section aligned memory size Part of the qemu command would look like this: sudo x86_64-softmmu/qemu-system-x86_64 \ --enable-kvm -cpu host,pse=off \ -m 4160M,maxmem=20G,slots=1 \ -smp sockets=2,cores=16 \ -numa node,nodeid=0,cpus=0-1 -numa node,nodeid=1,cpus=2-3 \ -machine pc,nvdimm \ -nographic \ -object memory-backend-ram,id=mem0,size=8G \ -device nvdimm,id=vm0,memdev=mem0,node=0,addr=0x144000000,label-size=128k 3. Re-config a pmem device with sub-section size in guest ndctl create-namespace --force --reconfig=namespace0.0 --mode=devdax --size=16M Then you would see the following call trace: pmem0: detected capacity change from 0 to 16777216 BUG: unable to handle page fault for address: ffffec73c51000b4 #PF: supervisor write access in kernel mode #PF: error_code(0x0002) - not-present page PGD 81ff8067 P4D 81ff8067 PUD 81ff7067 PMD 1437cb067 PTE 0 Oops: 0002 [#1] SMP NOPTI CPU: 16 PID: 1348 Comm: ndctl Kdump: loaded Tainted: G W 5.8.0-rc2+ #24 Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS rel-1.13.0-0-gf21b5a4aeb02-prebuilt.qemu.4 RIP: 0010:memmap_init_zone+0x154/0x1c2 Code: 77 16 f6 40 10 02 74 10 48 03 48 08 48 89 cb 48 c1 eb 0c e9 3a ff ff ff 48 89 df 48 c1 e7 06 48f RSP: 0018:ffffbdc7011a39b0 EFLAGS: 00010282 RAX: ffffec73c5100088 RBX: 0000000000144002 RCX: 0000000000144000 RDX: 0000000000000004 RSI: 007ffe0000000000 RDI: ffffec73c5100080 RBP: 027ffe0000000000 R08: 0000000000000001 R09: ffff9f8d38f6d708 R10: ffffec73c0000000 R11: 0000000000000000 R12: 0000000000000004 R13: 0000000000000001 R14: 0000000000144200 R15: 0000000000000000 FS: 00007efe6b65d780(0000) GS:ffff9f8d3f780000(0000) knlGS:0000000000000000 CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 CR2: ffffec73c51000b4 CR3: 000000007d718000 CR4: 0000000000340ee0 Call Trace: move_pfn_range_to_zone+0x128/0x150 memremap_pages+0x4e4/0x5a0 devm_memremap_pages+0x1e/0x60 dev_dax_probe+0x69/0x160 [device_dax] really_probe+0x298/0x3c0 driver_probe_device+0xe1/0x150 ? driver_allows_async_probing+0x50/0x50 bus_for_each_drv+0x7e/0xc0 __device_attach+0xdf/0x160 bus_probe_device+0x8e/0xa0 device_add+0x3b9/0x740 __devm_create_dev_dax+0x127/0x1c0 __dax_pmem_probe+0x1f2/0x219 [dax_pmem_core] dax_pmem_probe+0xc/0x1b [dax_pmem] nvdimm_bus_probe+0x69/0x1c0 [libnvdimm] really_probe+0x147/0x3c0 driver_probe_device+0xe1/0x150 device_driver_attach+0x53/0x60 bind_store+0xd1/0x110 kernfs_fop_write+0xce/0x1b0 vfs_write+0xb6/0x1a0 ksys_write+0x5f/0xe0 do_syscall_64+0x4d/0x90 entry_SYSCALL_64_after_hwframe+0x44/0xa9 Fixes: ba72b4c8cf60 ("mm/sparsemem: support sub-section hotplug") Signed-off-by: Wei Yang Signed-off-by: Andrew Morton Acked-by: David Hildenbrand Cc: Oscar Salvador Cc: Dan Williams Link: http://lkml.kernel.org/r/20200625223534.18024-1-richard.weiyang@linux.alibaba.com Signed-off-by: Linus Torvalds --- mm/sparse.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/mm/sparse.c b/mm/sparse.c index 97179d27801af..1b5e0385f4195 100644 --- a/mm/sparse.c +++ b/mm/sparse.c @@ -824,10 +824,14 @@ static void section_deactivate(unsigned long pfn, unsigned long nr_pages, ms->section_mem_map &= ~SECTION_HAS_MEM_MAP; } - if (section_is_early && memmap) - free_map_bootmem(memmap); - else + /* + * The memmap of early sections is always fully populated. See + * section_activate() and pfn_valid() . + */ + if (!section_is_early) depopulate_section_memmap(pfn, nr_pages, altmap); + else if (memmap) + free_map_bootmem(memmap); if (empty) ms->section_mem_map = (unsigned long)NULL; -- GitLab From 6cda72047ea46272ecb9cc71acf1231cea07167a Mon Sep 17 00:00:00 2001 From: Wei Yang Date: Thu, 6 Aug 2020 23:23:59 -0700 Subject: [PATCH 1428/1476] mm/sparse: only sub-section aligned range would be populated There are two code path which invoke __populate_section_memmap() * sparse_init_nid() * sparse_add_section() For both case, we are sure the memory range is sub-section aligned. * we pass PAGES_PER_SECTION to sparse_init_nid() * we check range by check_pfn_span() before calling sparse_add_section() Also, the counterpart of __populate_section_memmap(), we don't do such calculation and check since the range is checked by check_pfn_span() in __remove_pages(). Clear the calculation and check to keep it simple and comply with its counterpart. Signed-off-by: Wei Yang Signed-off-by: Andrew Morton Acked-by: David Hildenbrand Link: http://lkml.kernel.org/r/20200703031828.14645-1-richard.weiyang@linux.alibaba.com Signed-off-by: Linus Torvalds --- mm/sparse-vmemmap.c | 20 ++++++-------------- 1 file changed, 6 insertions(+), 14 deletions(-) diff --git a/mm/sparse-vmemmap.c b/mm/sparse-vmemmap.c index 41eeac67723bc..16183d85a7d50 100644 --- a/mm/sparse-vmemmap.c +++ b/mm/sparse-vmemmap.c @@ -251,20 +251,12 @@ int __meminit vmemmap_populate_basepages(unsigned long start, unsigned long end, struct page * __meminit __populate_section_memmap(unsigned long pfn, unsigned long nr_pages, int nid, struct vmem_altmap *altmap) { - unsigned long start; - unsigned long end; - - /* - * The minimum granularity of memmap extensions is - * PAGES_PER_SUBSECTION as allocations are tracked in the - * 'subsection_map' bitmap of the section. - */ - end = ALIGN(pfn + nr_pages, PAGES_PER_SUBSECTION); - pfn &= PAGE_SUBSECTION_MASK; - nr_pages = end - pfn; - - start = (unsigned long) pfn_to_page(pfn); - end = start + nr_pages * sizeof(struct page); + unsigned long start = (unsigned long) pfn_to_page(pfn); + unsigned long end = start + nr_pages * sizeof(struct page); + + if (WARN_ON_ONCE(!IS_ALIGNED(pfn, PAGES_PER_SUBSECTION) || + !IS_ALIGNED(nr_pages, PAGES_PER_SUBSECTION))) + return NULL; if (vmemmap_populate(start, end, nid, altmap)) return NULL; -- GitLab From c89ab04febf97d2db8ca4ef8e2866fadc474351b Mon Sep 17 00:00:00 2001 From: Mike Rapoport Date: Thu, 6 Aug 2020 23:24:02 -0700 Subject: [PATCH 1429/1476] mm/sparse: cleanup the code surrounding memory_present() After removal of CONFIG_HAVE_MEMBLOCK_NODE_MAP we have two equivalent functions that call memory_present() for each region in memblock.memory: sparse_memory_present_with_active_regions() and membocks_present(). Moreover, all architectures have a call to either of these functions preceding the call to sparse_init() and in the most cases they are called one after the other. Mark the regions from memblock.memory as present during sparce_init() by making sparse_init() call memblocks_present(), make memblocks_present() and memory_present() functions static and remove redundant sparse_memory_present_with_active_regions() function. Also remove no longer required HAVE_MEMORY_PRESENT configuration option. Signed-off-by: Mike Rapoport Signed-off-by: Andrew Morton Link: http://lkml.kernel.org/r/20200712083130.22919-1-rppt@kernel.org Signed-off-by: Linus Torvalds --- Documentation/vm/memory-model.rst | 7 ++----- arch/arm/mm/init.c | 9 ++------- arch/arm64/mm/init.c | 6 ++---- arch/ia64/mm/discontig.c | 1 - arch/microblaze/mm/init.c | 3 --- arch/mips/kernel/setup.c | 8 -------- arch/mips/loongson64/numa.c | 1 - arch/mips/sgi-ip27/ip27-memory.c | 2 -- arch/parisc/mm/init.c | 5 ----- arch/powerpc/mm/mem.c | 2 -- arch/powerpc/mm/numa.c | 1 - arch/riscv/mm/init.c | 1 - arch/s390/mm/init.c | 1 - arch/sh/mm/init.c | 6 ------ arch/sh/mm/numa.c | 3 --- arch/sparc/mm/init_64.c | 1 - arch/x86/mm/init_32.c | 2 -- arch/x86/mm/init_64.c | 1 - include/linux/mm.h | 4 ---- include/linux/mmzone.h | 14 -------------- mm/Kconfig | 6 +----- mm/page_alloc.c | 16 ---------------- mm/sparse.c | 20 ++++++++++++-------- 23 files changed, 19 insertions(+), 101 deletions(-) diff --git a/Documentation/vm/memory-model.rst b/Documentation/vm/memory-model.rst index 2b898a27b3465..7694497345731 100644 --- a/Documentation/vm/memory-model.rst +++ b/Documentation/vm/memory-model.rst @@ -141,11 +141,8 @@ sections: `mem_section` objects and the number of rows is calculated to fit all the memory sections. -The architecture setup code should call :c:func:`memory_present` for -each active memory range or use :c:func:`memblocks_present` or -:c:func:`sparse_memory_present_with_active_regions` wrappers to -initialize the memory sections. Next, the actual memory maps should be -set up using :c:func:`sparse_init`. +The architecture setup code should call sparse_init() to +initialize the memory sections and the memory maps. With SPARSEMEM there are two possible ways to convert a PFN to the corresponding `struct page` - a "classic sparse" and "sparse diff --git a/arch/arm/mm/init.c b/arch/arm/mm/init.c index 01e18e43b1742..000c1b48e9734 100644 --- a/arch/arm/mm/init.c +++ b/arch/arm/mm/init.c @@ -243,13 +243,8 @@ void __init bootmem_init(void) (phys_addr_t)max_low_pfn << PAGE_SHIFT); /* - * Sparsemem tries to allocate bootmem in memory_present(), - * so must be done after the fixed reservations - */ - memblocks_present(); - - /* - * sparse_init() needs the bootmem allocator up and running. + * sparse_init() tries to allocate memory from memblock, so must be + * done after the fixed reservations */ sparse_init(); diff --git a/arch/arm64/mm/init.c b/arch/arm64/mm/init.c index f8c19c6c8e71c..481d22c32a2e7 100644 --- a/arch/arm64/mm/init.c +++ b/arch/arm64/mm/init.c @@ -430,11 +430,9 @@ void __init bootmem_init(void) #endif /* - * Sparsemem tries to allocate bootmem in memory_present(), so must be - * done after the fixed reservations. + * sparse_init() tries to allocate memory from memblock, so must be + * done after the fixed reservations */ - memblocks_present(); - sparse_init(); zone_sizes_init(min, max); diff --git a/arch/ia64/mm/discontig.c b/arch/ia64/mm/discontig.c index 2ba2127335a7a..dbe829fc52980 100644 --- a/arch/ia64/mm/discontig.c +++ b/arch/ia64/mm/discontig.c @@ -600,7 +600,6 @@ void __init paging_init(void) max_dma = virt_to_phys((void *) MAX_DMA_ADDRESS) >> PAGE_SHIFT; - sparse_memory_present_with_active_regions(MAX_NUMNODES); sparse_init(); #ifdef CONFIG_VIRTUAL_MEM_MAP diff --git a/arch/microblaze/mm/init.c b/arch/microblaze/mm/init.c index 521b59ba716c0..0880a003573d9 100644 --- a/arch/microblaze/mm/init.c +++ b/arch/microblaze/mm/init.c @@ -172,9 +172,6 @@ void __init setup_memory(void) &memblock.memory, 0); } - /* XXX need to clip this if using highmem? */ - sparse_memory_present_with_active_regions(0); - paging_init(); } diff --git a/arch/mips/kernel/setup.c b/arch/mips/kernel/setup.c index 588b21245e00b..bf5f5acab0a82 100644 --- a/arch/mips/kernel/setup.c +++ b/arch/mips/kernel/setup.c @@ -371,14 +371,6 @@ static void __init bootmem_init(void) #endif } - - /* - * In any case the added to the memblock memory regions - * (highmem/lowmem, available/reserved, etc) are considered - * as present, so inform sparsemem about them. - */ - memblocks_present(); - /* * Reserve initrd memory if needed. */ diff --git a/arch/mips/loongson64/numa.c b/arch/mips/loongson64/numa.c index 901f5be5ee761..ea8bb1bc667e4 100644 --- a/arch/mips/loongson64/numa.c +++ b/arch/mips/loongson64/numa.c @@ -220,7 +220,6 @@ static __init void prom_meminit(void) cpumask_clear(&__node_cpumask[node]); } } - memblocks_present(); max_low_pfn = PHYS_PFN(memblock_end_of_DRAM()); for (cpu = 0; cpu < loongson_sysconf.nr_cpus; cpu++) { diff --git a/arch/mips/sgi-ip27/ip27-memory.c b/arch/mips/sgi-ip27/ip27-memory.c index 1213215ea9654..d411e0a90a5be 100644 --- a/arch/mips/sgi-ip27/ip27-memory.c +++ b/arch/mips/sgi-ip27/ip27-memory.c @@ -402,8 +402,6 @@ void __init prom_meminit(void) } __node_data[node] = &null_node; } - - memblocks_present(); } void __init prom_free_prom_memory(void) diff --git a/arch/parisc/mm/init.c b/arch/parisc/mm/init.c index 39ea464c8bd92..4381b65ae1e04 100644 --- a/arch/parisc/mm/init.c +++ b/arch/parisc/mm/init.c @@ -689,11 +689,6 @@ void __init paging_init(void) flush_cache_all_local(); /* start with known state */ flush_tlb_all_local(NULL); - /* - * Mark all memblocks as present for sparsemem using - * memory_present() and then initialize sparsemem. - */ - memblocks_present(); sparse_init(); parisc_bootmem_free(); } diff --git a/arch/powerpc/mm/mem.c b/arch/powerpc/mm/mem.c index ab12916ec1a7b..ec68c9eeac0ed 100644 --- a/arch/powerpc/mm/mem.c +++ b/arch/powerpc/mm/mem.c @@ -183,8 +183,6 @@ void __init mem_topology_setup(void) void __init initmem_init(void) { - /* XXX need to clip this if using highmem? */ - sparse_memory_present_with_active_regions(0); sparse_init(); } diff --git a/arch/powerpc/mm/numa.c b/arch/powerpc/mm/numa.c index 9fcf2d1958300..03a81d65095ba 100644 --- a/arch/powerpc/mm/numa.c +++ b/arch/powerpc/mm/numa.c @@ -949,7 +949,6 @@ void __init initmem_init(void) get_pfn_range_for_nid(nid, &start_pfn, &end_pfn); setup_node_data(nid, start_pfn, end_pfn); - sparse_memory_present_with_active_regions(nid); } sparse_init(); diff --git a/arch/riscv/mm/init.c b/arch/riscv/mm/init.c index 416e520d07d39..f6e6286b3d15e 100644 --- a/arch/riscv/mm/init.c +++ b/arch/riscv/mm/init.c @@ -544,7 +544,6 @@ void mark_rodata_ro(void) void __init paging_init(void) { setup_vm_final(); - memblocks_present(); sparse_init(); setup_zero_page(); zone_sizes_init(); diff --git a/arch/s390/mm/init.c b/arch/s390/mm/init.c index 6dc7c3b60ef6e..0d282081dc1f8 100644 --- a/arch/s390/mm/init.c +++ b/arch/s390/mm/init.c @@ -115,7 +115,6 @@ void __init paging_init(void) __load_psw_mask(psw.mask); kasan_free_early_identity(); - sparse_memory_present_with_active_regions(MAX_NUMNODES); sparse_init(); zone_dma_bits = 31; memset(max_zone_pfns, 0, sizeof(max_zone_pfns)); diff --git a/arch/sh/mm/init.c b/arch/sh/mm/init.c index a86ce13f392c9..613de8096335a 100644 --- a/arch/sh/mm/init.c +++ b/arch/sh/mm/init.c @@ -241,12 +241,6 @@ static void __init do_init_bootmem(void) plat_mem_setup(); - for_each_memblock(memory, reg) { - int nid = memblock_get_region_node(reg); - - memory_present(nid, memblock_region_memory_base_pfn(reg), - memblock_region_memory_end_pfn(reg)); - } sparse_init(); } diff --git a/arch/sh/mm/numa.c b/arch/sh/mm/numa.c index f7e4439deb176..50f0dc1744d04 100644 --- a/arch/sh/mm/numa.c +++ b/arch/sh/mm/numa.c @@ -53,7 +53,4 @@ void __init setup_bootmem_node(int nid, unsigned long start, unsigned long end) /* It's up */ node_set_online(nid); - - /* Kick sparsemem */ - sparse_memory_present_with_active_regions(nid); } diff --git a/arch/sparc/mm/init_64.c b/arch/sparc/mm/init_64.c index 02e6e5e0f106e..fad6d31299042 100644 --- a/arch/sparc/mm/init_64.c +++ b/arch/sparc/mm/init_64.c @@ -1610,7 +1610,6 @@ static unsigned long __init bootmem_init(unsigned long phys_base) /* XXX cpu notifier XXX */ - sparse_memory_present_with_active_regions(MAX_NUMNODES); sparse_init(); return end_pfn; diff --git a/arch/x86/mm/init_32.c b/arch/x86/mm/init_32.c index 8b4afad84f4a4..4cb958419fb0f 100644 --- a/arch/x86/mm/init_32.c +++ b/arch/x86/mm/init_32.c @@ -678,7 +678,6 @@ void __init initmem_init(void) #endif memblock_set_node(0, PHYS_ADDR_MAX, &memblock.memory, 0); - sparse_memory_present_with_active_regions(0); #ifdef CONFIG_FLATMEM max_mapnr = IS_ENABLED(CONFIG_HIGHMEM) ? highend_pfn : max_low_pfn; @@ -718,7 +717,6 @@ void __init paging_init(void) * NOTE: at this point the bootmem allocator is fully available. */ olpc_dt_build_devicetree(); - sparse_memory_present_with_active_regions(MAX_NUMNODES); sparse_init(); zone_sizes_init(); } diff --git a/arch/x86/mm/init_64.c b/arch/x86/mm/init_64.c index 53e1d4f4ed9de..1c1209da55fef 100644 --- a/arch/x86/mm/init_64.c +++ b/arch/x86/mm/init_64.c @@ -817,7 +817,6 @@ void __init initmem_init(void) void __init paging_init(void) { - sparse_memory_present_with_active_regions(MAX_NUMNODES); sparse_init(); /* diff --git a/include/linux/mm.h b/include/linux/mm.h index cf7e4605ff3f6..392016ce58789 100644 --- a/include/linux/mm.h +++ b/include/linux/mm.h @@ -2382,9 +2382,6 @@ static inline unsigned long get_num_physpages(void) * for_each_valid_physical_page_range() * memblock_add_node(base, size, nid) * free_area_init(max_zone_pfns); - * - * sparse_memory_present_with_active_regions() calls memory_present() for - * each range when SPARSEMEM is enabled. */ void free_area_init(unsigned long *max_zone_pfn); unsigned long node_map_pfn_alignment(void); @@ -2395,7 +2392,6 @@ extern unsigned long absent_pages_in_range(unsigned long start_pfn, extern void get_pfn_range_for_nid(unsigned int nid, unsigned long *start_pfn, unsigned long *end_pfn); extern unsigned long find_min_pfn_with_active_regions(void); -extern void sparse_memory_present_with_active_regions(int nid); #ifndef CONFIG_NEED_MULTIPLE_NODES static inline int early_pfn_to_nid(unsigned long pfn) diff --git a/include/linux/mmzone.h b/include/linux/mmzone.h index a3bd54139a30e..2eef8afd3a0f6 100644 --- a/include/linux/mmzone.h +++ b/include/linux/mmzone.h @@ -839,18 +839,6 @@ static inline struct pglist_data *lruvec_pgdat(struct lruvec *lruvec) extern unsigned long lruvec_lru_size(struct lruvec *lruvec, enum lru_list lru, int zone_idx); -#ifdef CONFIG_HAVE_MEMORY_PRESENT -void memory_present(int nid, unsigned long start, unsigned long end); -#else -static inline void memory_present(int nid, unsigned long start, unsigned long end) {} -#endif - -#if defined(CONFIG_SPARSEMEM) -void memblocks_present(void); -#else -static inline void memblocks_present(void) {} -#endif - #ifdef CONFIG_HAVE_MEMORYLESS_NODES int local_memory_node(int node_id); #else @@ -1407,8 +1395,6 @@ struct mminit_pfnnid_cache { #define early_pfn_valid(pfn) (1) #endif -void memory_present(int nid, unsigned long start, unsigned long end); - /* * If it is possible to have holes within a MAX_ORDER_NR_PAGES, then we * need to check pfn validity within that MAX_ORDER_NR_PAGES block. diff --git a/mm/Kconfig b/mm/Kconfig index d41f3fa7e9236..6c974888f86f9 100644 --- a/mm/Kconfig +++ b/mm/Kconfig @@ -88,13 +88,9 @@ config NEED_MULTIPLE_NODES def_bool y depends on DISCONTIGMEM || NUMA -config HAVE_MEMORY_PRESENT - def_bool y - depends on ARCH_HAVE_MEMORY_PRESENT || SPARSEMEM - # # SPARSEMEM_EXTREME (which is the default) does some bootmem -# allocations when memory_present() is called. If this cannot +# allocations when sparse_init() is called. If this cannot # be done on your architecture, select this option. However, # statically allocating the mem_section[] array can potentially # consume vast quantities of .bss, so be careful. diff --git a/mm/page_alloc.c b/mm/page_alloc.c index 8d5d8526c2f33..f49de9e97bf24 100644 --- a/mm/page_alloc.c +++ b/mm/page_alloc.c @@ -6324,22 +6324,6 @@ void __meminit init_currently_empty_zone(struct zone *zone, zone->initialized = 1; } -/** - * sparse_memory_present_with_active_regions - Call memory_present for each active range - * @nid: The node to call memory_present for. If MAX_NUMNODES, all nodes will be used. - * - * If an architecture guarantees that all ranges registered contain no holes and may - * be freed, this function may be used instead of calling memory_present() manually. - */ -void __init sparse_memory_present_with_active_regions(int nid) -{ - unsigned long start_pfn, end_pfn; - int i, this_nid; - - for_each_mem_pfn_range(i, nid, &start_pfn, &end_pfn, &this_nid) - memory_present(this_nid, start_pfn, end_pfn); -} - /** * get_pfn_range_for_nid - Return the start and end page frames for a node * @nid: The nid to return the range for. If MAX_NUMNODES, the min and max PFN are returned. diff --git a/mm/sparse.c b/mm/sparse.c index 1b5e0385f4195..fcc3d176f1eaa 100644 --- a/mm/sparse.c +++ b/mm/sparse.c @@ -249,7 +249,7 @@ void __init subsection_map_init(unsigned long pfn, unsigned long nr_pages) #endif /* Record a memory area against a node. */ -void __init memory_present(int nid, unsigned long start, unsigned long end) +static void __init memory_present(int nid, unsigned long start, unsigned long end) { unsigned long pfn; @@ -285,11 +285,11 @@ void __init memory_present(int nid, unsigned long start, unsigned long end) } /* - * Mark all memblocks as present using memory_present(). This is a - * convenience function that is useful for a number of arches - * to mark all of the systems memory as present during initialization. + * Mark all memblocks as present using memory_present(). + * This is a convenience function that is useful to mark all of the systems + * memory as present during initialization. */ -void __init memblocks_present(void) +static void __init memblocks_present(void) { struct memblock_region *reg; @@ -574,9 +574,13 @@ failed: */ void __init sparse_init(void) { - unsigned long pnum_begin = first_present_section_nr(); - int nid_begin = sparse_early_nid(__nr_to_section(pnum_begin)); - unsigned long pnum_end, map_count = 1; + unsigned long pnum_end, pnum_begin, map_count = 1; + int nid_begin; + + memblocks_present(); + + pnum_begin = first_present_section_nr(); + nid_begin = sparse_early_nid(__nr_to_section(pnum_begin)); /* Setup pageblock_order for HUGETLB_PAGE_SIZE_VARIABLE */ set_pageblock_order(); -- GitLab From 0f14599c607d32512a1d37e6d2a2d1a867f16177 Mon Sep 17 00:00:00 2001 From: "Matthew Wilcox (Oracle)" Date: Thu, 6 Aug 2020 23:24:05 -0700 Subject: [PATCH 1430/1476] vmalloc: convert to XArray The radix tree of vmap blocks is simpler to express as an XArray. Reduces both the text and data sizes of the object file and eliminates a user of the radix tree preload API. Signed-off-by: Matthew Wilcox (Oracle) Signed-off-by: Andrew Morton Reviewed-by: William Kucharski Link: http://lkml.kernel.org/r/20200603171448.5894-1-willy@infradead.org Signed-off-by: Linus Torvalds --- mm/vmalloc.c | 40 +++++++++++----------------------------- 1 file changed, 11 insertions(+), 29 deletions(-) diff --git a/mm/vmalloc.c b/mm/vmalloc.c index 5be3cf3b59dea..d496856bd4086 100644 --- a/mm/vmalloc.c +++ b/mm/vmalloc.c @@ -25,7 +25,7 @@ #include #include #include -#include +#include #include #include #include @@ -1514,12 +1514,11 @@ struct vmap_block { static DEFINE_PER_CPU(struct vmap_block_queue, vmap_block_queue); /* - * Radix tree of vmap blocks, indexed by address, to quickly find a vmap block + * XArray of vmap blocks, indexed by address, to quickly find a vmap block * in the free path. Could get rid of this if we change the API to return a * "cookie" from alloc, to be passed to free. But no big deal yet. */ -static DEFINE_SPINLOCK(vmap_block_tree_lock); -static RADIX_TREE(vmap_block_tree, GFP_ATOMIC); +static DEFINE_XARRAY(vmap_blocks); /* * We should probably have a fallback mechanism to allocate virtual memory @@ -1576,13 +1575,6 @@ static void *new_vmap_block(unsigned int order, gfp_t gfp_mask) return ERR_CAST(va); } - err = radix_tree_preload(gfp_mask); - if (unlikely(err)) { - kfree(vb); - free_vmap_area(va); - return ERR_PTR(err); - } - vaddr = vmap_block_vaddr(va->va_start, 0); spin_lock_init(&vb->lock); vb->va = va; @@ -1595,11 +1587,12 @@ static void *new_vmap_block(unsigned int order, gfp_t gfp_mask) INIT_LIST_HEAD(&vb->free_list); vb_idx = addr_to_vb_idx(va->va_start); - spin_lock(&vmap_block_tree_lock); - err = radix_tree_insert(&vmap_block_tree, vb_idx, vb); - spin_unlock(&vmap_block_tree_lock); - BUG_ON(err); - radix_tree_preload_end(); + err = xa_insert(&vmap_blocks, vb_idx, vb, gfp_mask); + if (err) { + kfree(vb); + free_vmap_area(va); + return ERR_PTR(err); + } vbq = &get_cpu_var(vmap_block_queue); spin_lock(&vbq->lock); @@ -1613,12 +1606,8 @@ static void *new_vmap_block(unsigned int order, gfp_t gfp_mask) static void free_vmap_block(struct vmap_block *vb) { struct vmap_block *tmp; - unsigned long vb_idx; - vb_idx = addr_to_vb_idx(vb->va->va_start); - spin_lock(&vmap_block_tree_lock); - tmp = radix_tree_delete(&vmap_block_tree, vb_idx); - spin_unlock(&vmap_block_tree_lock); + tmp = xa_erase(&vmap_blocks, addr_to_vb_idx(vb->va->va_start)); BUG_ON(tmp != vb); free_vmap_area_noflush(vb->va); @@ -1724,7 +1713,6 @@ static void *vb_alloc(unsigned long size, gfp_t gfp_mask) static void vb_free(unsigned long addr, unsigned long size) { unsigned long offset; - unsigned long vb_idx; unsigned int order; struct vmap_block *vb; @@ -1734,14 +1722,8 @@ static void vb_free(unsigned long addr, unsigned long size) flush_cache_vunmap(addr, addr + size); order = get_order(size); - offset = (addr & (VMAP_BLOCK_SIZE - 1)) >> PAGE_SHIFT; - - vb_idx = addr_to_vb_idx(addr); - rcu_read_lock(); - vb = radix_tree_lookup(&vmap_block_tree, vb_idx); - rcu_read_unlock(); - BUG_ON(!vb); + vb = xa_load(&vmap_blocks, addr_to_vb_idx(addr)); unmap_kernel_range_noflush(addr, size); -- GitLab From 5dd7864094033a281aeffccaf9703468cbcfccfc Mon Sep 17 00:00:00 2001 From: "Uladzislau Rezki (Sony)" Date: Thu, 6 Aug 2020 23:24:09 -0700 Subject: [PATCH 1431/1476] mm/vmalloc: simplify merge_or_add_vmap_area() Currently when a VA is deallocated and is about to be placed back to the tree, it can be either: merged with next/prev neighbors or inserted if not coalesced. On those steps the tree can be populated several times. For example when both neighbors are merged. It can be avoided and simplified in fact. Therefore do it only once when VA points to final merged area, after all manipulations: merging/removing/inserting. Signed-off-by: Uladzislau Rezki (Sony) Signed-off-by: Andrew Morton Link: http://lkml.kernel.org/r/20200527205054.1696-1-urezki@gmail.com Signed-off-by: Linus Torvalds --- mm/vmalloc.c | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/mm/vmalloc.c b/mm/vmalloc.c index d496856bd4086..d0ec02308d7cb 100644 --- a/mm/vmalloc.c +++ b/mm/vmalloc.c @@ -797,9 +797,6 @@ merge_or_add_vmap_area(struct vmap_area *va, if (sibling->va_start == va->va_end) { sibling->va_start = va->va_start; - /* Check and update the tree if needed. */ - augment_tree_propagate_from(sibling); - /* Free vmap_area object. */ kmem_cache_free(vmap_area_cachep, va); @@ -819,14 +816,18 @@ merge_or_add_vmap_area(struct vmap_area *va, if (next->prev != head) { sibling = list_entry(next->prev, struct vmap_area, list); if (sibling->va_end == va->va_start) { - sibling->va_end = va->va_end; - - /* Check and update the tree if needed. */ - augment_tree_propagate_from(sibling); - + /* + * If both neighbors are coalesced, it is important + * to unlink the "next" node first, followed by merging + * with "previous" one. Otherwise the tree might not be + * fully populated if a sibling's augmented value is + * "normalized" because of rotation operations. + */ if (merged) unlink_va(va, root); + sibling->va_end = va->va_end; + /* Free vmap_area object. */ kmem_cache_free(vmap_area_cachep, va); @@ -837,11 +838,13 @@ merge_or_add_vmap_area(struct vmap_area *va, } insert: - if (!merged) { + if (!merged) link_va(va, root, parent, link, head); - augment_tree_propagate_from(va); - } + /* + * Last step is to check and update the tree. + */ + augment_tree_propagate_from(va); return va; } -- GitLab From da27c9ed17794b4741fe2858ce727ffac671877e Mon Sep 17 00:00:00 2001 From: "Uladzislau Rezki (Sony)" Date: Thu, 6 Aug 2020 23:24:12 -0700 Subject: [PATCH 1432/1476] mm/vmalloc: simplify augment_tree_propagate_check() This function is for debug purpose only. Currently it uses recursion for tree traversal, checking an augmented value of each node to find out if it is valid or not. The recursion can corrupt the stack because the tree can be huge if synthetic tests are applied. To prevent it, navigate the tree from bottom to upper levels using a regular list instead, because nodes are linked among each other also. It is faster and without recursion. Signed-off-by: Uladzislau Rezki (Sony) Signed-off-by: Andrew Morton Link: http://lkml.kernel.org/r/20200527205054.1696-2-urezki@gmail.com Signed-off-by: Linus Torvalds --- mm/vmalloc.c | 42 ++++++++---------------------------------- 1 file changed, 8 insertions(+), 34 deletions(-) diff --git a/mm/vmalloc.c b/mm/vmalloc.c index d0ec02308d7cb..740b7e4cac03e 100644 --- a/mm/vmalloc.c +++ b/mm/vmalloc.c @@ -633,43 +633,17 @@ unlink_va(struct vmap_area *va, struct rb_root *root) #if DEBUG_AUGMENT_PROPAGATE_CHECK static void -augment_tree_propagate_check(struct rb_node *n) +augment_tree_propagate_check(void) { struct vmap_area *va; - struct rb_node *node; - unsigned long size; - bool found = false; - - if (n == NULL) - return; - - va = rb_entry(n, struct vmap_area, rb_node); - size = va->subtree_max_size; - node = n; - - while (node) { - va = rb_entry(node, struct vmap_area, rb_node); - - if (get_subtree_max_size(node->rb_left) == size) { - node = node->rb_left; - } else { - if (va_size(va) == size) { - found = true; - break; - } + unsigned long computed_size; - node = node->rb_right; - } - } - - if (!found) { - va = rb_entry(n, struct vmap_area, rb_node); - pr_emerg("tree is corrupted: %lu, %lu\n", - va_size(va), va->subtree_max_size); + list_for_each_entry(va, &free_vmap_area_list, list) { + computed_size = compute_subtree_max_size(va); + if (computed_size != va->subtree_max_size) + pr_emerg("tree is corrupted: %lu, %lu\n", + va_size(va), va->subtree_max_size); } - - augment_tree_propagate_check(n->rb_left); - augment_tree_propagate_check(n->rb_right); } #endif @@ -724,7 +698,7 @@ augment_tree_propagate_from(struct vmap_area *va) } #if DEBUG_AUGMENT_PROPAGATE_CHECK - augment_tree_propagate_check(free_vmap_area_root.rb_node); + augment_tree_propagate_check(); #endif } -- GitLab From 15ae144f77027db35fad168f24a1b9469dcf70a7 Mon Sep 17 00:00:00 2001 From: "Uladzislau Rezki (Sony)" Date: Thu, 6 Aug 2020 23:24:15 -0700 Subject: [PATCH 1433/1476] mm/vmalloc: switch to "propagate()" callback An augment_tree_propagate_from() function uses its own implementation that populates a tree from the specified node toward a root node. On the other hand the RB_DECLARE_CALLBACKS_MAX macro provides the "propagate()" callback that does exactly the same. Having two similar functions does not make sense and is redundant. Reuse "built in" functionality to the macros. So the code size gets reduced. Signed-off-by: Uladzislau Rezki (Sony) Signed-off-by: Andrew Morton Link: http://lkml.kernel.org/r/20200527205054.1696-3-urezki@gmail.com Signed-off-by: Linus Torvalds --- mm/vmalloc.c | 25 ++++++------------------- 1 file changed, 6 insertions(+), 19 deletions(-) diff --git a/mm/vmalloc.c b/mm/vmalloc.c index 740b7e4cac03e..ce17dbbd4bec9 100644 --- a/mm/vmalloc.c +++ b/mm/vmalloc.c @@ -677,25 +677,12 @@ augment_tree_propagate_check(void) static __always_inline void augment_tree_propagate_from(struct vmap_area *va) { - struct rb_node *node = &va->rb_node; - unsigned long new_va_sub_max_size; - - while (node) { - va = rb_entry(node, struct vmap_area, rb_node); - new_va_sub_max_size = compute_subtree_max_size(va); - - /* - * If the newly calculated maximum available size of the - * subtree is equal to the current one, then it means that - * the tree is propagated correctly. So we have to stop at - * this point to save cycles. - */ - if (va->subtree_max_size == new_va_sub_max_size) - break; - - va->subtree_max_size = new_va_sub_max_size; - node = rb_parent(&va->rb_node); - } + /* + * Populate the tree from bottom towards the root until + * the calculated maximum available size of checked node + * is equal to its current one. + */ + free_vmap_area_rb_augment_cb_propagate(&va->rb_node, NULL); #if DEBUG_AUGMENT_PROPAGATE_CHECK augment_tree_propagate_check(); -- GitLab From d758ffe6b9073ac7a21405cfdf2f7685aef8bacd Mon Sep 17 00:00:00 2001 From: "Uladzislau Rezki (Sony)" Date: Thu, 6 Aug 2020 23:24:18 -0700 Subject: [PATCH 1434/1476] mm/vmalloc: update the header about KVA rework Reflect information about the author, date and year when the KVA rework was done. Signed-off-by: Uladzislau Rezki (Sony) Signed-off-by: Andrew Morton Reviewed-by: Andrew Morton Link: http://lkml.kernel.org/r/20200622195821.4796-1-urezki@gmail.com Signed-off-by: Linus Torvalds --- mm/vmalloc.c | 1 + 1 file changed, 1 insertion(+) diff --git a/mm/vmalloc.c b/mm/vmalloc.c index ce17dbbd4bec9..970cbc453bcda 100644 --- a/mm/vmalloc.c +++ b/mm/vmalloc.c @@ -7,6 +7,7 @@ * SMP-safe vmalloc/vfree/ioremap, Tigran Aivazian , May 2000 * Major rework to support vmap/vunmap, Christoph Hellwig, SGI, August 2002 * Numa awareness, Christoph Lameter, SGI, June 2005 + * Improving global KVA allocator, Uladzislau Rezki, Sony, May 2019 */ #include -- GitLab From 1a69a623d98397e3968a3bd6e9f650e8a5e8fdce Mon Sep 17 00:00:00 2001 From: Mike Rapoport Date: Thu, 6 Aug 2020 23:24:21 -0700 Subject: [PATCH 1435/1476] mm: vmalloc: remove redundant assignment in unmap_kernel_range_noflush() 'addr' is set to 'start' and then a few lines afterwards 'start' is set to 'addr'. Remove the second asignment. Fixes: 2ba3e6947aed ("mm/vmalloc: track which page-table levels were modified") Signed-off-by: Mike Rapoport Signed-off-by: Andrew Morton Reviewed-by: David Hildenbrand Cc: Joerg Roedel Link: http://lkml.kernel.org/r/20200707163226.374685-1-rppt@kernel.org Signed-off-by: Linus Torvalds --- mm/vmalloc.c | 1 - 1 file changed, 1 deletion(-) diff --git a/mm/vmalloc.c b/mm/vmalloc.c index 970cbc453bcda..3d7b94eb0ac0a 100644 --- a/mm/vmalloc.c +++ b/mm/vmalloc.c @@ -175,7 +175,6 @@ void unmap_kernel_range_noflush(unsigned long start, unsigned long size) pgtbl_mod_mask mask = 0; BUG_ON(addr >= end); - start = addr; pgd = pgd_offset_k(addr); do { next = pgd_addr_end(addr, end); -- GitLab From 9c801f61d0e9e19b685ceb6e5f7107ad29b7837e Mon Sep 17 00:00:00 2001 From: "Uladzislau Rezki (Sony)" Date: Thu, 6 Aug 2020 23:24:24 -0700 Subject: [PATCH 1436/1476] mm/vmalloc.c: remove BUG() from the find_va_links() Get rid of BUG() macro, that should be used only when a critical situation happens and a system is not able to function anymore. Replace it with WARN() macro instead, dump some extra information about start/end addresses of both VAs which overlap. Such overlap data can help to figure out what happened making further analysis easier. For example if both areas are identical it could mean a double free. A recovery process consists of declining all further steps regarding inserting of conflicting overlap range. In that sense find_va_links() now can return NULL, so its return value has to be checked by callers. Side effect of such process is it can leak memory, but it is better than just killing a machine for no good reason. Apart of that a debugging process can be done on alive system. Signed-off-by: Uladzislau Rezki (Sony) Signed-off-by: Andrew Morton Cc: Hillf Danton Cc: Michal Hocko Cc: Matthew Wilcox Cc: Oleksiy Avramchenko Cc: Steven Rostedt Link: http://lkml.kernel.org/r/20200711104531.12242-1-urezki@gmail.com Signed-off-by: Linus Torvalds --- mm/vmalloc.c | 41 ++++++++++++++++++++++++++++++++--------- 1 file changed, 32 insertions(+), 9 deletions(-) diff --git a/mm/vmalloc.c b/mm/vmalloc.c index 3d7b94eb0ac0a..b482d240f9a25 100644 --- a/mm/vmalloc.c +++ b/mm/vmalloc.c @@ -512,6 +512,10 @@ static struct vmap_area *__find_vmap_area(unsigned long addr) /* * This function returns back addresses of parent node * and its left or right link for further processing. + * + * Otherwise NULL is returned. In that case all further + * steps regarding inserting of conflicting overlap range + * have to be declined and actually considered as a bug. */ static __always_inline struct rb_node ** find_va_links(struct vmap_area *va, @@ -550,8 +554,12 @@ find_va_links(struct vmap_area *va, else if (va->va_end > tmp_va->va_start && va->va_start >= tmp_va->va_end) link = &(*link)->rb_right; - else - BUG(); + else { + WARN(1, "vmalloc bug: 0x%lx-0x%lx overlaps with 0x%lx-0x%lx\n", + va->va_start, va->va_end, tmp_va->va_start, tmp_va->va_end); + + return NULL; + } } while (*link); *parent = &tmp_va->rb_node; @@ -697,7 +705,8 @@ insert_vmap_area(struct vmap_area *va, struct rb_node *parent; link = find_va_links(va, root, NULL, &parent); - link_va(va, root, parent, link, head); + if (link) + link_va(va, root, parent, link, head); } static void @@ -713,8 +722,10 @@ insert_vmap_area_augment(struct vmap_area *va, else link = find_va_links(va, root, NULL, &parent); - link_va(va, root, parent, link, head); - augment_tree_propagate_from(va); + if (link) { + link_va(va, root, parent, link, head); + augment_tree_propagate_from(va); + } } /* @@ -722,6 +733,11 @@ insert_vmap_area_augment(struct vmap_area *va, * and next free blocks. If coalesce is not done a new * free area is inserted. If VA has been merged, it is * freed. + * + * Please note, it can return NULL in case of overlap + * ranges, followed by WARN() report. Despite it is a + * buggy behaviour, a system can be alive and keep + * ongoing. */ static __always_inline struct vmap_area * merge_or_add_vmap_area(struct vmap_area *va, @@ -738,6 +754,8 @@ merge_or_add_vmap_area(struct vmap_area *va, * inserted, unless it is merged with its sibling/siblings. */ link = find_va_links(va, root, NULL, &parent); + if (!link) + return NULL; /* * Get next node of VA to check if merging can be done. @@ -1346,6 +1364,9 @@ static bool __purge_vmap_area_lazy(unsigned long start, unsigned long end) va = merge_or_add_vmap_area(va, &free_vmap_area_root, &free_vmap_area_list); + if (!va) + continue; + if (is_vmalloc_or_module_addr((void *)orig_start)) kasan_release_vmalloc(orig_start, orig_end, va->va_start, va->va_end); @@ -3330,8 +3351,9 @@ recovery: orig_end = vas[area]->va_end; va = merge_or_add_vmap_area(vas[area], &free_vmap_area_root, &free_vmap_area_list); - kasan_release_vmalloc(orig_start, orig_end, - va->va_start, va->va_end); + if (va) + kasan_release_vmalloc(orig_start, orig_end, + va->va_start, va->va_end); vas[area] = NULL; } @@ -3379,8 +3401,9 @@ err_free_shadow: orig_end = vas[area]->va_end; va = merge_or_add_vmap_area(vas[area], &free_vmap_area_root, &free_vmap_area_list); - kasan_release_vmalloc(orig_start, orig_end, - va->va_start, va->va_end); + if (va) + kasan_release_vmalloc(orig_start, orig_end, + va->va_start, va->va_end); vas[area] = NULL; kfree(vms[area]); } -- GitLab From 7a3767f83f173f9df9af9eb04959abe2966d4cc8 Mon Sep 17 00:00:00 2001 From: Marco Elver Date: Thu, 6 Aug 2020 23:24:28 -0700 Subject: [PATCH 1437/1476] kasan: improve and simplify Kconfig.kasan Turn 'KASAN' into a menuconfig, to avoid cluttering its parent menu with the suboptions if enabled. Use 'if KASAN ... endif' instead of having to 'depend on KASAN' for each entry. Signed-off-by: Marco Elver Signed-off-by: Andrew Morton Reviewed-by: Andrey Konovalov Cc: Andrey Ryabinin Cc: Arnd Bergmann Cc: Daniel Axtens Cc: Dmitry Vyukov Cc: Nick Desaulniers Cc: Walter Wu Link: http://lkml.kernel.org/r/20200629104157.3242503-1-elver@google.com Signed-off-by: Linus Torvalds --- lib/Kconfig.kasan | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/lib/Kconfig.kasan b/lib/Kconfig.kasan index 34b84bcbd3d91..89053defc0d97 100644 --- a/lib/Kconfig.kasan +++ b/lib/Kconfig.kasan @@ -18,7 +18,7 @@ config CC_HAS_KASAN_SW_TAGS config CC_HAS_WORKING_NOSANITIZE_ADDRESS def_bool !CC_IS_GCC || GCC_VERSION >= 80300 -config KASAN +menuconfig KASAN bool "KASAN: runtime memory debugger" depends on (HAVE_ARCH_KASAN && CC_HAS_KASAN_GENERIC) || \ (HAVE_ARCH_KASAN_SW_TAGS && CC_HAS_KASAN_SW_TAGS) @@ -29,9 +29,10 @@ config KASAN designed to find out-of-bounds accesses and use-after-free bugs. See Documentation/dev-tools/kasan.rst for details. +if KASAN + choice prompt "KASAN mode" - depends on KASAN default KASAN_GENERIC help KASAN has two modes: generic KASAN (similar to userspace ASan, @@ -88,7 +89,6 @@ endchoice choice prompt "Instrumentation type" - depends on KASAN default KASAN_OUTLINE config KASAN_OUTLINE @@ -113,7 +113,6 @@ endchoice config KASAN_STACK_ENABLE bool "Enable stack instrumentation (unsafe)" if CC_IS_CLANG && !COMPILE_TEST - depends on KASAN help The LLVM stack address sanitizer has a know problem that causes excessive stack usage in a lot of functions, see @@ -134,7 +133,7 @@ config KASAN_STACK config KASAN_S390_4_LEVEL_PAGING bool "KASan: use 4-level paging" - depends on KASAN && S390 + depends on S390 help Compiling the kernel with KASan disables automatic 3-level vs 4-level paging selection. 3-level paging is used by default (up @@ -151,7 +150,7 @@ config KASAN_SW_TAGS_IDENTIFY config KASAN_VMALLOC bool "Back mappings in vmalloc space with real shadow memory" - depends on KASAN && HAVE_ARCH_KASAN_VMALLOC + depends on HAVE_ARCH_KASAN_VMALLOC help By default, the shadow region for vmalloc space is the read-only zero page. This means that KASAN cannot detect errors involving @@ -164,8 +163,10 @@ config KASAN_VMALLOC config TEST_KASAN tristate "Module for testing KASAN for bug detection" - depends on m && KASAN + depends on m help This is a test module doing various nasty things like out of bounds accesses, use after free. It is useful for testing kernel debugging features like KASAN. + +endif # KASAN -- GitLab From ac4766be5e2d103ebf5d3f2ff676f866f6a56d44 Mon Sep 17 00:00:00 2001 From: Marco Elver Date: Thu, 6 Aug 2020 23:24:31 -0700 Subject: [PATCH 1438/1476] kasan: update required compiler versions in documentation Updates the recently changed compiler requirements for KASAN. In particular, we require GCC >= 8.3.0, and add a note that Clang 11 supports OOB detection of globals. Fixes: 7b861a53e46b ("kasan: Bump required compiler version") Fixes: acf7b0bf7dcf ("kasan: Fix required compiler version") Signed-off-by: Marco Elver Signed-off-by: Andrew Morton Reviewed-by: Andrey Konovalov Cc: Dmitry Vyukov Cc: Andrey Ryabinin Cc: Nick Desaulniers Cc: Walter Wu Cc: Arnd Bergmann Cc: Daniel Axtens Link: http://lkml.kernel.org/r/20200629104157.3242503-2-elver@google.com Signed-off-by: Linus Torvalds --- Documentation/dev-tools/kasan.rst | 7 ++----- lib/Kconfig.kasan | 24 +++++++++++++++--------- 2 files changed, 17 insertions(+), 14 deletions(-) diff --git a/Documentation/dev-tools/kasan.rst b/Documentation/dev-tools/kasan.rst index c652d740735de..15a2a53e77b0e 100644 --- a/Documentation/dev-tools/kasan.rst +++ b/Documentation/dev-tools/kasan.rst @@ -13,11 +13,8 @@ KASAN uses compile-time instrumentation to insert validity checks before every memory access, and therefore requires a compiler version that supports that. Generic KASAN is supported in both GCC and Clang. With GCC it requires version -4.9.2 or later for basic support and version 5.0 or later for detection of -out-of-bounds accesses for stack and global variables and for inline -instrumentation mode (see the Usage section). With Clang it requires version -7.0.0 or later and it doesn't support detection of out-of-bounds accesses for -global variables yet. +8.3.0 or later. With Clang it requires version 7.0.0 or later, but detection of +out-of-bounds accesses for global variables is only supported since Clang 11. Tag-based KASAN is only supported in Clang and requires version 7.0.0 or later. diff --git a/lib/Kconfig.kasan b/lib/Kconfig.kasan index 89053defc0d97..047b53dbfd58b 100644 --- a/lib/Kconfig.kasan +++ b/lib/Kconfig.kasan @@ -40,6 +40,7 @@ choice software tag-based KASAN (a version based on software memory tagging, arm64 only, similar to userspace HWASan, enabled with CONFIG_KASAN_SW_TAGS). + Both generic and tag-based KASAN are strictly debugging features. config KASAN_GENERIC @@ -51,16 +52,18 @@ config KASAN_GENERIC select STACKDEPOT help Enables generic KASAN mode. - Supported in both GCC and Clang. With GCC it requires version 4.9.2 - or later for basic support and version 5.0 or later for detection of - out-of-bounds accesses for stack and global variables and for inline - instrumentation mode (CONFIG_KASAN_INLINE). With Clang it requires - version 3.7.0 or later and it doesn't support detection of - out-of-bounds accesses for global variables yet. + + This mode is supported in both GCC and Clang. With GCC it requires + version 8.3.0 or later. With Clang it requires version 7.0.0 or + later, but detection of out-of-bounds accesses for global variables + is supported only since Clang 11. + This mode consumes about 1/8th of available memory at kernel start and introduces an overhead of ~x1.5 for the rest of the allocations. The performance slowdown is ~x3. + For better error detection enable CONFIG_STACKTRACE. + Currently CONFIG_KASAN_GENERIC doesn't work with CONFIG_DEBUG_SLAB (the resulting kernel does not boot). @@ -73,15 +76,19 @@ config KASAN_SW_TAGS select STACKDEPOT help Enables software tag-based KASAN mode. + This mode requires Top Byte Ignore support by the CPU and therefore - is only supported for arm64. - This mode requires Clang version 7.0.0 or later. + is only supported for arm64. This mode requires Clang version 7.0.0 + or later. + This mode consumes about 1/16th of available memory at kernel start and introduces an overhead of ~20% for the rest of the allocations. This mode may potentially introduce problems relating to pointer casting and comparison, as it embeds tags into the top byte of each pointer. + For better error detection enable CONFIG_STACKTRACE. + Currently CONFIG_KASAN_SW_TAGS doesn't work with CONFIG_DEBUG_SLAB (the resulting kernel does not boot). @@ -107,7 +114,6 @@ config KASAN_INLINE memory accesses. This is faster than outline (in some workloads it gives about x2 boost over outline instrumentation), but make kernel's .text size much bigger. - For CONFIG_KASAN_GENERIC this requires GCC 5.0 or later. endchoice -- GitLab From 26e760c9a7c8ec31fa1a6bfbbce3f63f189ccef0 Mon Sep 17 00:00:00 2001 From: Walter Wu Date: Thu, 6 Aug 2020 23:24:35 -0700 Subject: [PATCH 1439/1476] rcu: kasan: record and print call_rcu() call stack Patch series "kasan: memorize and print call_rcu stack", v8. This patchset improves KASAN reports by making them to have call_rcu() call stack information. It is useful for programmers to solve use-after-free or double-free memory issue. The KASAN report was as follows(cleaned up slightly): BUG: KASAN: use-after-free in kasan_rcu_reclaim+0x58/0x60 Freed by task 0: kasan_save_stack+0x24/0x50 kasan_set_track+0x24/0x38 kasan_set_free_info+0x18/0x20 __kasan_slab_free+0x10c/0x170 kasan_slab_free+0x10/0x18 kfree+0x98/0x270 kasan_rcu_reclaim+0x1c/0x60 Last call_rcu(): kasan_save_stack+0x24/0x50 kasan_record_aux_stack+0xbc/0xd0 call_rcu+0x8c/0x580 kasan_rcu_uaf+0xf4/0xf8 Generic KASAN will record the last two call_rcu() call stacks and print up to 2 call_rcu() call stacks in KASAN report. it is only suitable for generic KASAN. This feature considers the size of struct kasan_alloc_meta and kasan_free_meta, we try to optimize the structure layout and size, lets it get better memory consumption. [1]https://bugzilla.kernel.org/show_bug.cgi?id=198437 [2]https://groups.google.com/forum/#!searchin/kasan-dev/better$20stack$20traces$20for$20rcu%7Csort:date/kasan-dev/KQsjT_88hDE/7rNUZprRBgAJ This patch (of 4): This feature will record the last two call_rcu() call stacks and prints up to 2 call_rcu() call stacks in KASAN report. When call_rcu() is called, we store the call_rcu() call stack into slub alloc meta-data, so that the KASAN report can print rcu stack. [1]https://bugzilla.kernel.org/show_bug.cgi?id=198437 [2]https://groups.google.com/forum/#!searchin/kasan-dev/better$20stack$20traces$20for$20rcu%7Csort:date/kasan-dev/KQsjT_88hDE/7rNUZprRBgAJ [walter-zh.wu@mediatek.com: build fix] Link: http://lkml.kernel.org/r/20200710162401.23816-1-walter-zh.wu@mediatek.com Suggested-by: Dmitry Vyukov Signed-off-by: Walter Wu Signed-off-by: Andrew Morton Tested-by: Dmitry Vyukov Reviewed-by: Dmitry Vyukov Reviewed-by: Andrey Konovalov Acked-by: Paul E. McKenney Cc: Andrey Ryabinin Cc: Alexander Potapenko Cc: Josh Triplett Cc: Mathieu Desnoyers Cc: Lai Jiangshan Cc: Joel Fernandes Cc: Jonathan Corbet Cc: Matthias Brugger Link: http://lkml.kernel.org/r/20200710162123.23713-1-walter-zh.wu@mediatek.com Link: http://lkml.kernel.org/r/20200601050847.1096-1-walter-zh.wu@mediatek.com Link: http://lkml.kernel.org/r/20200601050927.1153-1-walter-zh.wu@mediatek.com Signed-off-by: Linus Torvalds --- include/linux/kasan.h | 2 ++ kernel/rcu/tree.c | 2 ++ mm/kasan/common.c | 4 ++-- mm/kasan/generic.c | 21 +++++++++++++++++++++ mm/kasan/kasan.h | 9 +++++++++ mm/kasan/report.c | 28 +++++++++++++++++++++++----- 6 files changed, 59 insertions(+), 7 deletions(-) diff --git a/include/linux/kasan.h b/include/linux/kasan.h index 82522e996c760..18452e35e7b2a 100644 --- a/include/linux/kasan.h +++ b/include/linux/kasan.h @@ -174,11 +174,13 @@ static inline size_t kasan_metadata_size(struct kmem_cache *cache) { return 0; } void kasan_cache_shrink(struct kmem_cache *cache); void kasan_cache_shutdown(struct kmem_cache *cache); +void kasan_record_aux_stack(void *ptr); #else /* CONFIG_KASAN_GENERIC */ static inline void kasan_cache_shrink(struct kmem_cache *cache) {} static inline void kasan_cache_shutdown(struct kmem_cache *cache) {} +static inline void kasan_record_aux_stack(void *ptr) {} #endif /* CONFIG_KASAN_GENERIC */ diff --git a/kernel/rcu/tree.c b/kernel/rcu/tree.c index ac7198ed31976..8ce77d9ac716c 100644 --- a/kernel/rcu/tree.c +++ b/kernel/rcu/tree.c @@ -59,6 +59,7 @@ #include #include #include +#include #include "../time/tick-internal.h" #include "tree.h" @@ -2890,6 +2891,7 @@ __call_rcu(struct rcu_head *head, rcu_callback_t func) head->func = func; head->next = NULL; local_irq_save(flags); + kasan_record_aux_stack(head); rdp = this_cpu_ptr(&rcu_data); /* Add the callback to our list. */ diff --git a/mm/kasan/common.c b/mm/kasan/common.c index 757d4074fe286..ad24666f50e45 100644 --- a/mm/kasan/common.c +++ b/mm/kasan/common.c @@ -40,7 +40,7 @@ #include "kasan.h" #include "../slab.h" -static inline depot_stack_handle_t save_stack(gfp_t flags) +depot_stack_handle_t kasan_save_stack(gfp_t flags) { unsigned long entries[KASAN_STACK_DEPTH]; unsigned int nr_entries; @@ -53,7 +53,7 @@ static inline depot_stack_handle_t save_stack(gfp_t flags) static inline void set_track(struct kasan_track *track, gfp_t flags) { track->pid = current->pid; - track->stack = save_stack(flags); + track->stack = kasan_save_stack(flags); } void kasan_enable_current(void) diff --git a/mm/kasan/generic.c b/mm/kasan/generic.c index 098a7dbaced66..d70586393b045 100644 --- a/mm/kasan/generic.c +++ b/mm/kasan/generic.c @@ -324,3 +324,24 @@ DEFINE_ASAN_SET_SHADOW(f2); DEFINE_ASAN_SET_SHADOW(f3); DEFINE_ASAN_SET_SHADOW(f5); DEFINE_ASAN_SET_SHADOW(f8); + +void kasan_record_aux_stack(void *addr) +{ + struct page *page = kasan_addr_to_page(addr); + struct kmem_cache *cache; + struct kasan_alloc_meta *alloc_info; + void *object; + + if (!(page && PageSlab(page))) + return; + + cache = page->slab_cache; + object = nearest_obj(cache, page, addr); + alloc_info = get_alloc_info(cache, object); + + /* + * record the last two call_rcu() call stacks. + */ + alloc_info->aux_stack[1] = alloc_info->aux_stack[0]; + alloc_info->aux_stack[0] = kasan_save_stack(GFP_NOWAIT); +} diff --git a/mm/kasan/kasan.h b/mm/kasan/kasan.h index cfade6413528d..f89a195e336ae 100644 --- a/mm/kasan/kasan.h +++ b/mm/kasan/kasan.h @@ -104,6 +104,13 @@ struct kasan_track { struct kasan_alloc_meta { struct kasan_track alloc_track; +#ifdef CONFIG_KASAN_GENERIC + /* + * call_rcu() call stack is stored into struct kasan_alloc_meta. + * The free stack is stored into struct kasan_free_meta. + */ + depot_stack_handle_t aux_stack[2]; +#endif struct kasan_track free_track[KASAN_NR_FREE_STACKS]; #ifdef CONFIG_KASAN_SW_TAGS_IDENTIFY u8 free_pointer_tag[KASAN_NR_FREE_STACKS]; @@ -159,6 +166,8 @@ void kasan_report_invalid_free(void *object, unsigned long ip); struct page *kasan_addr_to_page(const void *addr); +depot_stack_handle_t kasan_save_stack(gfp_t flags); + #if defined(CONFIG_KASAN_GENERIC) && \ (defined(CONFIG_SLAB) || defined(CONFIG_SLUB)) void quarantine_put(struct kasan_free_meta *info, struct kmem_cache *cache); diff --git a/mm/kasan/report.c b/mm/kasan/report.c index 51ec45407a0b9..445a9d56eb13d 100644 --- a/mm/kasan/report.c +++ b/mm/kasan/report.c @@ -106,15 +106,20 @@ static void end_report(unsigned long *flags) kasan_enable_current(); } +static void print_stack(depot_stack_handle_t stack) +{ + unsigned long *entries; + unsigned int nr_entries; + + nr_entries = stack_depot_fetch(stack, &entries); + stack_trace_print(entries, nr_entries, 0); +} + static void print_track(struct kasan_track *track, const char *prefix) { pr_err("%s by task %u:\n", prefix, track->pid); if (track->stack) { - unsigned long *entries; - unsigned int nr_entries; - - nr_entries = stack_depot_fetch(track->stack, &entries); - stack_trace_print(entries, nr_entries, 0); + print_stack(track->stack); } else { pr_err("(stack is not available)\n"); } @@ -193,6 +198,19 @@ static void describe_object(struct kmem_cache *cache, void *object, free_track = kasan_get_free_track(cache, object, tag); print_track(free_track, "Freed"); pr_err("\n"); + +#ifdef CONFIG_KASAN_GENERIC + if (alloc_info->aux_stack[0]) { + pr_err("Last call_rcu():\n"); + print_stack(alloc_info->aux_stack[0]); + pr_err("\n"); + } + if (alloc_info->aux_stack[1]) { + pr_err("Second to last call_rcu():\n"); + print_stack(alloc_info->aux_stack[1]); + pr_err("\n"); + } +#endif } describe_object_addr(cache, object, addr); -- GitLab From e4b7818b9aa8fa8dfd5fc7bb98f0d4c16b50fd8b Mon Sep 17 00:00:00 2001 From: Walter Wu Date: Thu, 6 Aug 2020 23:24:39 -0700 Subject: [PATCH 1440/1476] kasan: record and print the free track Move free track from kasan_alloc_meta to kasan_free_meta in order to make struct kasan_alloc_meta and kasan_free_meta size are both 16 bytes. It is a good size because it is the minimal redzone size and a good number of alignment. For free track, we make some modifications as shown below: 1) Remove the free_track from struct kasan_alloc_meta. 2) Add the free_track into struct kasan_free_meta. 3) Add a macro KASAN_KMALLOC_FREETRACK in order to check whether it can print free stack in KASAN report. [1]https://bugzilla.kernel.org/show_bug.cgi?id=198437 [walter-zh.wu@mediatek.com: build fix] Link: http://lkml.kernel.org/r/20200710162440.23887-1-walter-zh.wu@mediatek.com Suggested-by: Dmitry Vyukov Co-developed-by: Dmitry Vyukov Signed-off-by: Walter Wu Signed-off-by: Andrew Morton Tested-by: Dmitry Vyukov Reviewed-by: Dmitry Vyukov Reviewed-by: Andrey Konovalov Cc: Andrey Ryabinin Cc: Alexander Potapenko Cc: Joel Fernandes Cc: Jonathan Corbet Cc: Josh Triplett Cc: Lai Jiangshan Cc: Mathieu Desnoyers Cc: Matthias Brugger Cc: "Paul E . McKenney" Link: http://lkml.kernel.org/r/20200601051022.1230-1-walter-zh.wu@mediatek.com Signed-off-by: Linus Torvalds --- mm/kasan/common.c | 22 ++-------------------- mm/kasan/generic.c | 22 ++++++++++++++++++++++ mm/kasan/generic_report.c | 1 + mm/kasan/kasan.h | 16 +++++++++++++--- mm/kasan/quarantine.c | 1 + mm/kasan/report.c | 26 ++++---------------------- mm/kasan/tags.c | 37 +++++++++++++++++++++++++++++++++++++ 7 files changed, 80 insertions(+), 45 deletions(-) diff --git a/mm/kasan/common.c b/mm/kasan/common.c index ad24666f50e45..f18067736f7c2 100644 --- a/mm/kasan/common.c +++ b/mm/kasan/common.c @@ -50,7 +50,7 @@ depot_stack_handle_t kasan_save_stack(gfp_t flags) return stack_depot_save(entries, nr_entries, flags); } -static inline void set_track(struct kasan_track *track, gfp_t flags) +void kasan_set_track(struct kasan_track *track, gfp_t flags) { track->pid = current->pid; track->stack = kasan_save_stack(flags); @@ -298,24 +298,6 @@ struct kasan_free_meta *get_free_info(struct kmem_cache *cache, return (void *)object + cache->kasan_info.free_meta_offset; } - -static void kasan_set_free_info(struct kmem_cache *cache, - void *object, u8 tag) -{ - struct kasan_alloc_meta *alloc_meta; - u8 idx = 0; - - alloc_meta = get_alloc_info(cache, object); - -#ifdef CONFIG_KASAN_SW_TAGS_IDENTIFY - idx = alloc_meta->free_track_idx; - alloc_meta->free_pointer_tag[idx] = tag; - alloc_meta->free_track_idx = (idx + 1) % KASAN_NR_FREE_STACKS; -#endif - - set_track(&alloc_meta->free_track[idx], GFP_NOWAIT); -} - void kasan_poison_slab(struct page *page) { unsigned long i; @@ -491,7 +473,7 @@ static void *__kasan_kmalloc(struct kmem_cache *cache, const void *object, KASAN_KMALLOC_REDZONE); if (cache->flags & SLAB_KASAN) - set_track(&get_alloc_info(cache, object)->alloc_track, flags); + kasan_set_track(&get_alloc_info(cache, object)->alloc_track, flags); return set_tag(object, tag); } diff --git a/mm/kasan/generic.c b/mm/kasan/generic.c index d70586393b045..248264b9cb76f 100644 --- a/mm/kasan/generic.c +++ b/mm/kasan/generic.c @@ -345,3 +345,25 @@ void kasan_record_aux_stack(void *addr) alloc_info->aux_stack[1] = alloc_info->aux_stack[0]; alloc_info->aux_stack[0] = kasan_save_stack(GFP_NOWAIT); } + +void kasan_set_free_info(struct kmem_cache *cache, + void *object, u8 tag) +{ + struct kasan_free_meta *free_meta; + + free_meta = get_free_info(cache, object); + kasan_set_track(&free_meta->free_track, GFP_NOWAIT); + + /* + * the object was freed and has free track set + */ + *(u8 *)kasan_mem_to_shadow(object) = KASAN_KMALLOC_FREETRACK; +} + +struct kasan_track *kasan_get_free_track(struct kmem_cache *cache, + void *object, u8 tag) +{ + if (*(u8 *)kasan_mem_to_shadow(object) != KASAN_KMALLOC_FREETRACK) + return NULL; + return &get_free_info(cache, object)->free_track; +} diff --git a/mm/kasan/generic_report.c b/mm/kasan/generic_report.c index e200acb2d2927..a38c7a9e192a6 100644 --- a/mm/kasan/generic_report.c +++ b/mm/kasan/generic_report.c @@ -80,6 +80,7 @@ static const char *get_shadow_bug_type(struct kasan_access_info *info) break; case KASAN_FREE_PAGE: case KASAN_KMALLOC_FREE: + case KASAN_KMALLOC_FREETRACK: bug_type = "use-after-free"; break; case KASAN_ALLOCA_LEFT: diff --git a/mm/kasan/kasan.h b/mm/kasan/kasan.h index f89a195e336ae..ac499456740f1 100644 --- a/mm/kasan/kasan.h +++ b/mm/kasan/kasan.h @@ -17,15 +17,17 @@ #define KASAN_PAGE_REDZONE 0xFE /* redzone for kmalloc_large allocations */ #define KASAN_KMALLOC_REDZONE 0xFC /* redzone inside slub object */ #define KASAN_KMALLOC_FREE 0xFB /* object was freed (kmem_cache_free/kfree) */ +#define KASAN_KMALLOC_FREETRACK 0xFA /* object was freed and has free track set */ #else #define KASAN_FREE_PAGE KASAN_TAG_INVALID #define KASAN_PAGE_REDZONE KASAN_TAG_INVALID #define KASAN_KMALLOC_REDZONE KASAN_TAG_INVALID #define KASAN_KMALLOC_FREE KASAN_TAG_INVALID +#define KASAN_KMALLOC_FREETRACK KASAN_TAG_INVALID #endif -#define KASAN_GLOBAL_REDZONE 0xFA /* redzone for global variable */ -#define KASAN_VMALLOC_INVALID 0xF9 /* unallocated space in vmapped page */ +#define KASAN_GLOBAL_REDZONE 0xF9 /* redzone for global variable */ +#define KASAN_VMALLOC_INVALID 0xF8 /* unallocated space in vmapped page */ /* * Stack redzone shadow values @@ -110,8 +112,9 @@ struct kasan_alloc_meta { * The free stack is stored into struct kasan_free_meta. */ depot_stack_handle_t aux_stack[2]; -#endif +#else struct kasan_track free_track[KASAN_NR_FREE_STACKS]; +#endif #ifdef CONFIG_KASAN_SW_TAGS_IDENTIFY u8 free_pointer_tag[KASAN_NR_FREE_STACKS]; u8 free_track_idx; @@ -126,6 +129,9 @@ struct kasan_free_meta { * Otherwise it might be used for the allocator freelist. */ struct qlist_node quarantine_link; +#ifdef CONFIG_KASAN_GENERIC + struct kasan_track free_track; +#endif }; struct kasan_alloc_meta *get_alloc_info(struct kmem_cache *cache, @@ -167,6 +173,10 @@ void kasan_report_invalid_free(void *object, unsigned long ip); struct page *kasan_addr_to_page(const void *addr); depot_stack_handle_t kasan_save_stack(gfp_t flags); +void kasan_set_track(struct kasan_track *track, gfp_t flags); +void kasan_set_free_info(struct kmem_cache *cache, void *object, u8 tag); +struct kasan_track *kasan_get_free_track(struct kmem_cache *cache, + void *object, u8 tag); #if defined(CONFIG_KASAN_GENERIC) && \ (defined(CONFIG_SLAB) || defined(CONFIG_SLUB)) diff --git a/mm/kasan/quarantine.c b/mm/kasan/quarantine.c index 978bc4a3eb51a..4c53758104496 100644 --- a/mm/kasan/quarantine.c +++ b/mm/kasan/quarantine.c @@ -145,6 +145,7 @@ static void qlink_free(struct qlist_node *qlink, struct kmem_cache *cache) if (IS_ENABLED(CONFIG_SLAB)) local_irq_save(flags); + *(u8 *)kasan_mem_to_shadow(object) = KASAN_KMALLOC_FREE; ___cache_free(cache, object, _THIS_IP_); if (IS_ENABLED(CONFIG_SLAB)) diff --git a/mm/kasan/report.c b/mm/kasan/report.c index 445a9d56eb13d..4f49fa6cd1aa7 100644 --- a/mm/kasan/report.c +++ b/mm/kasan/report.c @@ -165,26 +165,6 @@ static void describe_object_addr(struct kmem_cache *cache, void *object, (void *)(object_addr + cache->object_size)); } -static struct kasan_track *kasan_get_free_track(struct kmem_cache *cache, - void *object, u8 tag) -{ - struct kasan_alloc_meta *alloc_meta; - int i = 0; - - alloc_meta = get_alloc_info(cache, object); - -#ifdef CONFIG_KASAN_SW_TAGS_IDENTIFY - for (i = 0; i < KASAN_NR_FREE_STACKS; i++) { - if (alloc_meta->free_pointer_tag[i] == tag) - break; - } - if (i == KASAN_NR_FREE_STACKS) - i = alloc_meta->free_track_idx; -#endif - - return &alloc_meta->free_track[i]; -} - static void describe_object(struct kmem_cache *cache, void *object, const void *addr, u8 tag) { @@ -196,8 +176,10 @@ static void describe_object(struct kmem_cache *cache, void *object, print_track(&alloc_info->alloc_track, "Allocated"); pr_err("\n"); free_track = kasan_get_free_track(cache, object, tag); - print_track(free_track, "Freed"); - pr_err("\n"); + if (free_track) { + print_track(free_track, "Freed"); + pr_err("\n"); + } #ifdef CONFIG_KASAN_GENERIC if (alloc_info->aux_stack[0]) { diff --git a/mm/kasan/tags.c b/mm/kasan/tags.c index 8a959fdd30e35..e02a36a51f429 100644 --- a/mm/kasan/tags.c +++ b/mm/kasan/tags.c @@ -161,3 +161,40 @@ void __hwasan_tag_memory(unsigned long addr, u8 tag, unsigned long size) kasan_poison_shadow((void *)addr, size, tag); } EXPORT_SYMBOL(__hwasan_tag_memory); + +void kasan_set_free_info(struct kmem_cache *cache, + void *object, u8 tag) +{ + struct kasan_alloc_meta *alloc_meta; + u8 idx = 0; + + alloc_meta = get_alloc_info(cache, object); + +#ifdef CONFIG_KASAN_SW_TAGS_IDENTIFY + idx = alloc_meta->free_track_idx; + alloc_meta->free_pointer_tag[idx] = tag; + alloc_meta->free_track_idx = (idx + 1) % KASAN_NR_FREE_STACKS; +#endif + + kasan_set_track(&alloc_meta->free_track[idx], GFP_NOWAIT); +} + +struct kasan_track *kasan_get_free_track(struct kmem_cache *cache, + void *object, u8 tag) +{ + struct kasan_alloc_meta *alloc_meta; + int i = 0; + + alloc_meta = get_alloc_info(cache, object); + +#ifdef CONFIG_KASAN_SW_TAGS_IDENTIFY + for (i = 0; i < KASAN_NR_FREE_STACKS; i++) { + if (alloc_meta->free_pointer_tag[i] == tag) + break; + } + if (i == KASAN_NR_FREE_STACKS) + i = alloc_meta->free_track_idx; +#endif + + return &alloc_meta->free_track[i]; +} -- GitLab From 387d6e46681b0d5b64f5d13639f90e3df15e020d Mon Sep 17 00:00:00 2001 From: Walter Wu Date: Thu, 6 Aug 2020 23:24:42 -0700 Subject: [PATCH 1441/1476] kasan: add tests for call_rcu stack recording Test call_rcu() call stack recording and verify whether it correctly is printed in KASAN report. Signed-off-by: Walter Wu Signed-off-by: Andrew Morton Tested-by: Dmitry Vyukov Reviewed-by: Dmitry Vyukov Reviewed-by: Andrey Konovalov Cc: Andrey Ryabinin Cc: Alexander Potapenko Cc: Matthias Brugger Cc: Joel Fernandes Cc: Jonathan Corbet Cc: Josh Triplett Cc: Lai Jiangshan Cc: Mathieu Desnoyers Cc: "Paul E . McKenney" Link: http://lkml.kernel.org/r/20200601051045.1294-1-walter-zh.wu@mediatek.com Signed-off-by: Linus Torvalds --- lib/test_kasan.c | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/lib/test_kasan.c b/lib/test_kasan.c index e4d9a86b174b4..7f95f85421b22 100644 --- a/lib/test_kasan.c +++ b/lib/test_kasan.c @@ -801,6 +801,35 @@ static noinline void __init vmalloc_oob(void) static void __init vmalloc_oob(void) {} #endif +static struct kasan_rcu_info { + int i; + struct rcu_head rcu; +} *global_rcu_ptr; + +static noinline void __init kasan_rcu_reclaim(struct rcu_head *rp) +{ + struct kasan_rcu_info *fp = container_of(rp, + struct kasan_rcu_info, rcu); + + kfree(fp); + fp->i = 1; +} + +static noinline void __init kasan_rcu_uaf(void) +{ + struct kasan_rcu_info *ptr; + + pr_info("use-after-free in kasan_rcu_reclaim\n"); + ptr = kmalloc(sizeof(struct kasan_rcu_info), GFP_KERNEL); + if (!ptr) { + pr_err("Allocation failed\n"); + return; + } + + global_rcu_ptr = rcu_dereference_protected(ptr, NULL); + call_rcu(&global_rcu_ptr->rcu, kasan_rcu_reclaim); +} + static int __init kmalloc_tests_init(void) { /* @@ -848,6 +877,7 @@ static int __init kmalloc_tests_init(void) kasan_bitops(); kmalloc_double_kzfree(); vmalloc_oob(); + kasan_rcu_uaf(); kasan_restore_multi_shot(multishot); -- GitLab From 9793b62663f54e369a3986543b296992a8a0589c Mon Sep 17 00:00:00 2001 From: Walter Wu Date: Thu, 6 Aug 2020 23:24:46 -0700 Subject: [PATCH 1442/1476] kasan: update documentation for generic kasan Generic KASAN will support to record the last two call_rcu() call stacks and print them in KASAN report. So that need to update documentation. Signed-off-by: Walter Wu Signed-off-by: Andrew Morton Tested-by: Dmitry Vyukov Reviewed-by: Dmitry Vyukov Reviewed-by: Andrey Konovalov Cc: Andrey Ryabinin Cc: Alexander Potapenko Cc: Jonathan Corbet Cc: Matthias Brugger Cc: "Paul E . McKenney" Cc: Josh Triplett Cc: Mathieu Desnoyers Cc: Lai Jiangshan Cc: Joel Fernandes Link: http://lkml.kernel.org/r/20200601051111.1359-1-walter-zh.wu@mediatek.com Signed-off-by: Linus Torvalds --- Documentation/dev-tools/kasan.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Documentation/dev-tools/kasan.rst b/Documentation/dev-tools/kasan.rst index 15a2a53e77b0e..38fd5681fade4 100644 --- a/Documentation/dev-tools/kasan.rst +++ b/Documentation/dev-tools/kasan.rst @@ -190,6 +190,9 @@ function calls GCC directly inserts the code to check the shadow memory. This option significantly enlarges kernel but it gives x1.1-x2 performance boost over outline instrumented kernel. +Generic KASAN prints up to 2 call_rcu() call stacks in reports, the last one +and the second to last. + Software tag-based KASAN ~~~~~~~~~~~~~~~~~~~~~~~~ -- GitLab From c0e16ab3b5887e86cd45b95e28cf66498b161ee1 Mon Sep 17 00:00:00 2001 From: Vincenzo Frascino Date: Thu, 6 Aug 2020 23:24:50 -0700 Subject: [PATCH 1443/1476] kasan: remove kasan_unpoison_stack_above_sp_to() kasan_unpoison_stack_above_sp_to() is defined in kasan code but never used. The function was introduced as part of the commit: commit 9f7d416c36124667 ("kprobes: Unpoison stack in jprobe_return() for KASAN") ... where it was necessary because x86's jprobe_return() would leave stale shadow on the stack, and was an oddity in that regard. Since then, jprobes were removed entirely, and as of commit: commit 80006dbee674f9fa ("kprobes/x86: Remove jprobe implementation") ... there have been no callers of this function. Remove the declaration and the implementation. Signed-off-by: Vincenzo Frascino Signed-off-by: Andrew Morton Reviewed-by: Mark Rutland Reviewed-by: Andrey Konovalov Cc: Andrey Ryabinin Cc: Alexander Potapenko Cc: Dmitry Vyukov Link: http://lkml.kernel.org/r/20200706143505.23299-1-vincenzo.frascino@arm.com Signed-off-by: Linus Torvalds --- include/linux/kasan.h | 2 -- mm/kasan/common.c | 15 --------------- 2 files changed, 17 deletions(-) diff --git a/include/linux/kasan.h b/include/linux/kasan.h index 18452e35e7b2a..087fba34b2090 100644 --- a/include/linux/kasan.h +++ b/include/linux/kasan.h @@ -38,7 +38,6 @@ extern void kasan_disable_current(void); void kasan_unpoison_shadow(const void *address, size_t size); void kasan_unpoison_task_stack(struct task_struct *task); -void kasan_unpoison_stack_above_sp_to(const void *watermark); void kasan_alloc_pages(struct page *page, unsigned int order); void kasan_free_pages(struct page *page, unsigned int order); @@ -101,7 +100,6 @@ void kasan_restore_multi_shot(bool enabled); static inline void kasan_unpoison_shadow(const void *address, size_t size) {} static inline void kasan_unpoison_task_stack(struct task_struct *task) {} -static inline void kasan_unpoison_stack_above_sp_to(const void *watermark) {} static inline void kasan_enable_current(void) {} static inline void kasan_disable_current(void) {} diff --git a/mm/kasan/common.c b/mm/kasan/common.c index f18067736f7c2..950fd372a07ec 100644 --- a/mm/kasan/common.c +++ b/mm/kasan/common.c @@ -180,21 +180,6 @@ asmlinkage void kasan_unpoison_task_stack_below(const void *watermark) kasan_unpoison_shadow(base, watermark - base); } -/* - * Clear all poison for the region between the current SP and a provided - * watermark value, as is sometimes required prior to hand-crafted asm function - * returns in the middle of functions. - */ -void kasan_unpoison_stack_above_sp_to(const void *watermark) -{ - const void *sp = __builtin_frame_address(0); - size_t size = watermark - sp; - - if (WARN_ON(sp > watermark)) - return; - kasan_unpoison_shadow(sp, size); -} - void kasan_alloc_pages(struct page *page, unsigned int order) { u8 tag; -- GitLab From f33a01492a24a276e0bc1c932bcefdb8c1125159 Mon Sep 17 00:00:00 2001 From: Walter Wu Date: Thu, 6 Aug 2020 23:24:54 -0700 Subject: [PATCH 1444/1476] lib/test_kasan.c: fix KASAN unit tests for tag-based KASAN We use tag-based KASAN, then KASAN unit tests don't detect out-of-bounds memory access. They need to be fixed. With tag-based KASAN, the state of each 16 aligned bytes of memory is encoded in one shadow byte and the shadow value is tag of pointer, so we need to read next shadow byte, the shadow value is not equal to tag value of pointer, so that tag-based KASAN will detect out-of-bounds memory access. [walter-zh.wu@mediatek.com: use KASAN_SHADOW_SCALE_SIZE instead of 13] Link: http://lkml.kernel.org/r/20200708132524.11688-1-walter-zh.wu@mediatek.com Suggested-by: Dmitry Vyukov Signed-off-by: Walter Wu Signed-off-by: Andrew Morton Reviewed-by: Andrey Konovalov Acked-by: Dmitry Vyukov Cc: Andrey Ryabinin Cc: Alexander Potapenko Cc: Matthias Brugger Link: http://lkml.kernel.org/r/20200706115039.16750-1-walter-zh.wu@mediatek.com Signed-off-by: Linus Torvalds --- lib/test_kasan.c | 49 +++++++++++++++++++++++++++++++----------------- 1 file changed, 32 insertions(+), 17 deletions(-) diff --git a/lib/test_kasan.c b/lib/test_kasan.c index 7f95f85421b22..f362f26629381 100644 --- a/lib/test_kasan.c +++ b/lib/test_kasan.c @@ -23,6 +23,10 @@ #include +#include "../mm/kasan/kasan.h" + +#define OOB_TAG_OFF (IS_ENABLED(CONFIG_KASAN_GENERIC) ? 0 : KASAN_SHADOW_SCALE_SIZE) + /* * We assign some test results to these globals to make sure the tests * are not eliminated as dead code. @@ -48,7 +52,8 @@ static noinline void __init kmalloc_oob_right(void) return; } - ptr[size] = 'x'; + ptr[size + OOB_TAG_OFF] = 'x'; + kfree(ptr); } @@ -100,7 +105,8 @@ static noinline void __init kmalloc_pagealloc_oob_right(void) return; } - ptr[size] = 0; + ptr[size + OOB_TAG_OFF] = 0; + kfree(ptr); } @@ -170,7 +176,8 @@ static noinline void __init kmalloc_oob_krealloc_more(void) return; } - ptr2[size2] = 'x'; + ptr2[size2 + OOB_TAG_OFF] = 'x'; + kfree(ptr2); } @@ -188,7 +195,9 @@ static noinline void __init kmalloc_oob_krealloc_less(void) kfree(ptr1); return; } - ptr2[size2] = 'x'; + + ptr2[size2 + OOB_TAG_OFF] = 'x'; + kfree(ptr2); } @@ -224,7 +233,8 @@ static noinline void __init kmalloc_oob_memset_2(void) return; } - memset(ptr+7, 0, 2); + memset(ptr + 7 + OOB_TAG_OFF, 0, 2); + kfree(ptr); } @@ -240,7 +250,8 @@ static noinline void __init kmalloc_oob_memset_4(void) return; } - memset(ptr+5, 0, 4); + memset(ptr + 5 + OOB_TAG_OFF, 0, 4); + kfree(ptr); } @@ -257,7 +268,8 @@ static noinline void __init kmalloc_oob_memset_8(void) return; } - memset(ptr+1, 0, 8); + memset(ptr + 1 + OOB_TAG_OFF, 0, 8); + kfree(ptr); } @@ -273,7 +285,8 @@ static noinline void __init kmalloc_oob_memset_16(void) return; } - memset(ptr+1, 0, 16); + memset(ptr + 1 + OOB_TAG_OFF, 0, 16); + kfree(ptr); } @@ -289,7 +302,8 @@ static noinline void __init kmalloc_oob_in_memset(void) return; } - memset(ptr, 0, size+5); + memset(ptr, 0, size + 5 + OOB_TAG_OFF); + kfree(ptr); } @@ -423,7 +437,8 @@ static noinline void __init kmem_cache_oob(void) return; } - *p = p[size]; + *p = p[size + OOB_TAG_OFF]; + kmem_cache_free(cache, p); kmem_cache_destroy(cache); } @@ -520,25 +535,25 @@ static noinline void __init copy_user_test(void) } pr_info("out-of-bounds in copy_from_user()\n"); - unused = copy_from_user(kmem, usermem, size + 1); + unused = copy_from_user(kmem, usermem, size + 1 + OOB_TAG_OFF); pr_info("out-of-bounds in copy_to_user()\n"); - unused = copy_to_user(usermem, kmem, size + 1); + unused = copy_to_user(usermem, kmem, size + 1 + OOB_TAG_OFF); pr_info("out-of-bounds in __copy_from_user()\n"); - unused = __copy_from_user(kmem, usermem, size + 1); + unused = __copy_from_user(kmem, usermem, size + 1 + OOB_TAG_OFF); pr_info("out-of-bounds in __copy_to_user()\n"); - unused = __copy_to_user(usermem, kmem, size + 1); + unused = __copy_to_user(usermem, kmem, size + 1 + OOB_TAG_OFF); pr_info("out-of-bounds in __copy_from_user_inatomic()\n"); - unused = __copy_from_user_inatomic(kmem, usermem, size + 1); + unused = __copy_from_user_inatomic(kmem, usermem, size + 1 + OOB_TAG_OFF); pr_info("out-of-bounds in __copy_to_user_inatomic()\n"); - unused = __copy_to_user_inatomic(usermem, kmem, size + 1); + unused = __copy_to_user_inatomic(usermem, kmem, size + 1 + OOB_TAG_OFF); pr_info("out-of-bounds in strncpy_from_user()\n"); - unused = strncpy_from_user(kmem, usermem, size + 1); + unused = strncpy_from_user(kmem, usermem, size + 1 + OOB_TAG_OFF); vm_munmap((unsigned long)usermem, PAGE_SIZE); kfree(kmem); -- GitLab From 8dcc1d34661d58a7889fb06517c8738d1412d1bc Mon Sep 17 00:00:00 2001 From: Andrey Konovalov Date: Thu, 6 Aug 2020 23:24:57 -0700 Subject: [PATCH 1445/1476] kasan: don't tag stacks allocated with pagealloc Patch series "kasan: support stack instrumentation for tag-based mode", v2. This patch (of 5): Prepare Software Tag-Based KASAN for stack tagging support. With Tag-Based KASAN when kernel stacks are allocated via pagealloc (which happens when CONFIG_VMAP_STACK is not enabled), they get tagged. KASAN instrumentation doesn't expect the sp register to be tagged, and this leads to false-positive reports. Fix by resetting the tag of kernel stack pointers after allocation. Signed-off-by: Andrey Konovalov Signed-off-by: Andrew Morton Cc: Andrey Ryabinin Cc: Alexander Potapenko Cc: Dmitry Vyukov Cc: Marco Elver Cc: Walter Wu Cc: Elena Petrova Cc: Vincenzo Frascino Cc: Catalin Marinas Cc: Ard Biesheuvel Link: http://lkml.kernel.org/r/cover.1596199677.git.andreyknvl@google.com Link: http://lkml.kernel.org/r/cover.1596544734.git.andreyknvl@google.com Link: http://lkml.kernel.org/r/12d8c678869268dd0884b01271ab592f30792abf.1596544734.git.andreyknvl@google.com Link: http://lkml.kernel.org/r/01c678b877755bcf29009176592402cdf6f2cb15.1596199677.git.andreyknvl@google.com Link: https://bugzilla.kernel.org/show_bug.cgi?id=203497 Signed-off-by: Linus Torvalds --- kernel/fork.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/kernel/fork.c b/kernel/fork.c index c7b4ce9d2647c..35e9894d394c2 100644 --- a/kernel/fork.c +++ b/kernel/fork.c @@ -261,7 +261,7 @@ static unsigned long *alloc_thread_stack_node(struct task_struct *tsk, int node) THREAD_SIZE_ORDER); if (likely(page)) { - tsk->stack = page_address(page); + tsk->stack = kasan_reset_tag(page_address(page)); return tsk->stack; } return NULL; @@ -302,6 +302,7 @@ static unsigned long *alloc_thread_stack_node(struct task_struct *tsk, { unsigned long *stack; stack = kmem_cache_alloc_node(thread_stack_cache, THREADINFO_GFP, node); + stack = kasan_reset_tag(stack); tsk->stack = stack; return stack; } -- GitLab From 2c547f9da0539ad1f7ef7f08c8c82036d61b011a Mon Sep 17 00:00:00 2001 From: Andrey Konovalov Date: Thu, 6 Aug 2020 23:25:01 -0700 Subject: [PATCH 1446/1476] efi: provide empty efi_enter_virtual_mode implementation When CONFIG_EFI is not enabled, we might get an undefined reference to efi_enter_virtual_mode() error, if this efi_enabled() call isn't inlined into start_kernel(). This happens in particular, if start_kernel() is annodated with __no_sanitize_address. Reported-by: kernel test robot Signed-off-by: Andrey Konovalov Signed-off-by: Andrew Morton Acked-by: Ard Biesheuvel Cc: Alexander Potapenko Cc: Andrey Ryabinin Cc: Catalin Marinas Cc: Dmitry Vyukov Cc: Elena Petrova Cc: Marco Elver Cc: Vincenzo Frascino Cc: Walter Wu Link: http://lkml.kernel.org/r/6514652d3a32d3ed33d6eb5c91d0af63bf0d1a0c.1596544734.git.andreyknvl@google.com Signed-off-by: Linus Torvalds --- include/linux/efi.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/include/linux/efi.h b/include/linux/efi.h index 05c47f857383e..73db1ae04cef8 100644 --- a/include/linux/efi.h +++ b/include/linux/efi.h @@ -606,7 +606,11 @@ extern void *efi_get_pal_addr (void); extern void efi_map_pal_code (void); extern void efi_memmap_walk (efi_freemem_callback_t callback, void *arg); extern void efi_gettimeofday (struct timespec64 *ts); +#ifdef CONFIG_EFI extern void efi_enter_virtual_mode (void); /* switch EFI to virtual mode, if possible */ +#else +static inline void efi_enter_virtual_mode (void) {} +#endif #ifdef CONFIG_X86 extern efi_status_t efi_query_variable_store(u32 attributes, unsigned long size, -- GitLab From f9409d58e972cada2c524b7f1e54631bb8fa176f Mon Sep 17 00:00:00 2001 From: Andrey Konovalov Date: Thu, 6 Aug 2020 23:25:05 -0700 Subject: [PATCH 1447/1476] kasan, arm64: don't instrument functions that enable kasan This patch prepares Software Tag-Based KASAN for stack tagging support. With stack tagging enabled, KASAN tags stack variable in each function in its prologue. In start_kernel() stack variables get tagged before KASAN is enabled via setup_arch()->kasan_init(). As the result the tags for start_kernel()'s stack variables end up in the temporary shadow memory. Later when KASAN gets enabled, switched to normal shadow, and starts checking tags, this leads to false-positive reports, as proper tags are missing in normal shadow. Disable KASAN instrumentation for start_kernel(). Also disable it for arm64's setup_arch() as a precaution (it doesn't have any stack variables right now). [andreyknvl@google.com: reorder attributes for start_kernel()] Link: http://lkml.kernel.org/r/26fb6165a17abcf61222eda5184c030fb6b133d1.1596544734.git.andreyknvl@google.com Signed-off-by: Andrey Konovalov Signed-off-by: Andrew Morton Acked-by: Catalin Marinas [arm64] Cc: Alexander Potapenko Cc: Andrey Ryabinin Cc: Dmitry Vyukov Cc: Elena Petrova Cc: Marco Elver Cc: Vincenzo Frascino Cc: Walter Wu Cc: Ard Biesheuvel Link: http://lkml.kernel.org/r/55d432671a92e931ab8234b03dc36b14d4c21bfb.1596199677.git.andreyknvl@google.com Signed-off-by: Linus Torvalds --- arch/arm64/kernel/setup.c | 2 +- init/main.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/arm64/kernel/setup.c b/arch/arm64/kernel/setup.c index c793276ec7ad9..87e81d29e6fbe 100644 --- a/arch/arm64/kernel/setup.c +++ b/arch/arm64/kernel/setup.c @@ -276,7 +276,7 @@ arch_initcall(reserve_memblock_reserved_regions); u64 __cpu_logical_map[NR_CPUS] = { [0 ... NR_CPUS-1] = INVALID_HWID }; -void __init setup_arch(char **cmdline_p) +void __init __no_sanitize_address setup_arch(char **cmdline_p) { init_mm.start_code = (unsigned long) _text; init_mm.end_code = (unsigned long) _etext; diff --git a/init/main.c b/init/main.c index 15bd0efff3dfa..92d25796ae9d2 100644 --- a/init/main.c +++ b/init/main.c @@ -829,7 +829,7 @@ void __init __weak arch_call_rest_init(void) rest_init(); } -asmlinkage __visible void __init start_kernel(void) +asmlinkage __visible void __init __no_sanitize_address start_kernel(void) { char *command_line; char *after_dashes; -- GitLab From cae9dc35ed9ff82a99754e51d57ff6c332e1f7e4 Mon Sep 17 00:00:00 2001 From: Andrey Konovalov Date: Thu, 6 Aug 2020 23:25:08 -0700 Subject: [PATCH 1448/1476] kasan: allow enabling stack tagging for tag-based mode Use CONFIG_KASAN_STACK to enable stack tagging. Note, that HWASAN short granules [1] are disabled. Supporting those will require more kernel changes. [1] https://clang.llvm.org/docs/HardwareAssistedAddressSanitizerDesign.html Signed-off-by: Andrey Konovalov Signed-off-by: Andrew Morton Cc: Alexander Potapenko Cc: Andrey Ryabinin Cc: Catalin Marinas Cc: Dmitry Vyukov Cc: Elena Petrova Cc: Marco Elver Cc: Vincenzo Frascino Cc: Walter Wu Cc: Ard Biesheuvel Link: http://lkml.kernel.org/r/e7febb907b539c3730780df587ce0b38dc558c3d.1596199677.git.andreyknvl@google.com Link: http://lkml.kernel.org/r/99f7d90a4237431bf5988599fb41358e92876eb0.1596544734.git.andreyknvl@google.com Signed-off-by: Linus Torvalds --- scripts/Makefile.kasan | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/scripts/Makefile.kasan b/scripts/Makefile.kasan index 03757cc60e06c..f4beee1b00130 100644 --- a/scripts/Makefile.kasan +++ b/scripts/Makefile.kasan @@ -44,7 +44,8 @@ else endif CFLAGS_KASAN := -fsanitize=kernel-hwaddress \ - -mllvm -hwasan-instrument-stack=0 \ + -mllvm -hwasan-instrument-stack=$(CONFIG_KASAN_STACK) \ + -mllvm -hwasan-use-short-granules=0 \ $(instrumentation_flags) endif # CONFIG_KASAN_SW_TAGS -- GitLab From 51dcc81c282dc401dfd8460a7e59546bc1b30e32 Mon Sep 17 00:00:00 2001 From: Andrey Konovalov Date: Thu, 6 Aug 2020 23:25:12 -0700 Subject: [PATCH 1449/1476] kasan: adjust kasan_stack_oob for tag-based mode Use OOB_TAG_OFF as access offset to land the access into the next granule. Suggested-by: Walter Wu Signed-off-by: Andrey Konovalov Signed-off-by: Andrew Morton Cc: Alexander Potapenko Cc: Andrey Ryabinin Cc: Catalin Marinas Cc: Dmitry Vyukov Cc: Elena Petrova Cc: Marco Elver Cc: Vincenzo Frascino Cc: Ard Biesheuvel Link: http://lkml.kernel.org/r/403b259f1de49a7a3694531c851ac28326a586a8.1596199677.git.andreyknvl@google.com Link: http://lkml.kernel.org/r/3063ab1411e92bce36061a96e25b651212e70ba6.1596544734.git.andreyknvl@google.com Signed-off-by: Linus Torvalds --- lib/test_kasan.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/test_kasan.c b/lib/test_kasan.c index f362f26629381..53e953bb1d1da 100644 --- a/lib/test_kasan.c +++ b/lib/test_kasan.c @@ -488,7 +488,7 @@ static noinline void __init kasan_global_oob(void) static noinline void __init kasan_stack_oob(void) { char stack_array[10]; - volatile int i = 0; + volatile int i = OOB_TAG_OFF; char *p = &stack_array[ARRAY_SIZE(stack_array) + i]; pr_info("out-of-bounds on stack\n"); -- GitLab From deba04872be44e1c005364c276495a3b425dfbe6 Mon Sep 17 00:00:00 2001 From: Vlastimil Babka Date: Thu, 6 Aug 2020 23:25:16 -0700 Subject: [PATCH 1450/1476] mm, page_alloc: use unlikely() in task_capc() Hugh noted that task_capc() could use unlikely(), as most of the time there is no capture in progress and we are in page freeing hot path. Indeed adding unlikely() produces assembly that better matches the assumption and moves all the tests away from the hot path. I have also noticed that we don't need to test for cc->direct_compaction as the only place we set current->task_capture is compact_zone_order() which also always sets cc->direct_compaction true. Suggested-by: Hugh Dickins Signed-off-by: Vlastimil Babka Signed-off-by: Andrew Morton Acked-by: Hugh Dickins Acked-by: Mel Gorman Cc: Alex Shi Cc: Li Wang Link: http://lkml.kernel.org/r/4a24f7af-3aa5-6e80-4ae6-8f253b562039@suse.cz Signed-off-by: Linus Torvalds --- mm/page_alloc.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/mm/page_alloc.c b/mm/page_alloc.c index f49de9e97bf24..ce2409ee2144b 100644 --- a/mm/page_alloc.c +++ b/mm/page_alloc.c @@ -813,11 +813,10 @@ static inline struct capture_control *task_capc(struct zone *zone) { struct capture_control *capc = current->capture_control; - return capc && + return unlikely(capc) && !(current->flags & PF_KTHREAD) && !capc->page && - capc->cc->zone == zone && - capc->cc->direct_compaction ? capc : NULL; + capc->cc->zone == zone ? capc : NULL; } static inline bool -- GitLab From f27ce0e14088b23f8d54ae4a44f70307ec420e64 Mon Sep 17 00:00:00 2001 From: Jaewon Kim Date: Thu, 6 Aug 2020 23:25:20 -0700 Subject: [PATCH 1451/1476] page_alloc: consider highatomic reserve in watermark fast zone_watermark_fast was introduced by commit 48ee5f3696f6 ("mm, page_alloc: shortcut watermark checks for order-0 pages"). The commit simply checks if free pages is bigger than watermark without additional calculation such like reducing watermark. It considered free cma pages but it did not consider highatomic reserved. This may incur exhaustion of free pages except high order atomic free pages. Assume that reserved_highatomic pageblock is bigger than watermark min, and there are only few free pages except high order atomic free. Because zone_watermark_fast passes the allocation without considering high order atomic free, normal reclaimable allocation like GFP_HIGHUSER will consume all the free pages. Then finally order-0 atomic allocation may fail on allocation. This means watermark min is not protected against non-atomic allocation. The order-0 atomic allocation with ALLOC_HARDER unwantedly can be failed. Additionally the __GFP_MEMALLOC allocation with ALLOC_NO_WATERMARKS also can be failed. To avoid the problem, zone_watermark_fast should consider highatomic reserve. If the actual size of high atomic free is counted accurately like cma free, we may use it. On this patch just use nr_reserved_highatomic. Additionally introduce __zone_watermark_unusable_free to factor out common parts between zone_watermark_fast and __zone_watermark_ok. This is an example of ALLOC_HARDER allocation failure using v4.19 based kernel. Binder:9343_3: page allocation failure: order:0, mode:0x480020(GFP_ATOMIC), nodemask=(null) Call trace: [] dump_stack+0xb8/0xf0 [] warn_alloc+0xd8/0x12c [] __alloc_pages_nodemask+0x120c/0x1250 [] new_slab+0x128/0x604 [] ___slab_alloc+0x508/0x670 [] __kmalloc+0x2f8/0x310 [] context_struct_to_string+0x104/0x1cc [] security_sid_to_context_core+0x74/0x144 [] security_sid_to_context+0x10/0x18 [] selinux_secid_to_secctx+0x20/0x28 [] security_secid_to_secctx+0x3c/0x70 [] binder_transaction+0xe68/0x454c Mem-Info: active_anon:102061 inactive_anon:81551 isolated_anon:0 active_file:59102 inactive_file:68924 isolated_file:64 unevictable:611 dirty:63 writeback:0 unstable:0 slab_reclaimable:13324 slab_unreclaimable:44354 mapped:83015 shmem:4858 pagetables:26316 bounce:0 free:2727 free_pcp:1035 free_cma:178 Node 0 active_anon:408244kB inactive_anon:326204kB active_file:236408kB inactive_file:275696kB unevictable:2444kB isolated(anon):0kB isolated(file):256kB mapped:332060kB dirty:252kB writeback:0kB shmem:19432kB writeback_tmp:0kB unstable:0kB all_unreclaimable? no Normal free:10908kB min:6192kB low:44388kB high:47060kB active_anon:409160kB inactive_anon:325924kB active_file:235820kB inactive_file:276628kB unevictable:2444kB writepending:252kB present:3076096kB managed:2673676kB mlocked:2444kB kernel_stack:62512kB pagetables:105264kB bounce:0kB free_pcp:4140kB local_pcp:40kB free_cma:712kB lowmem_reserve[]: 0 0 Normal: 505*4kB (H) 357*8kB (H) 201*16kB (H) 65*32kB (H) 1*64kB (H) 0*128kB 0*256kB 0*512kB 0*1024kB 0*2048kB 0*4096kB = 10236kB 138826 total pagecache pages 5460 pages in swap cache Swap cache stats: add 8273090, delete 8267506, find 1004381/4060142 This is an example of ALLOC_NO_WATERMARKS allocation failure using v4.14 based kernel. kswapd0: page allocation failure: order:0, mode:0x140000a(GFP_NOIO|__GFP_HIGHMEM|__GFP_MOVABLE), nodemask=(null) kswapd0 cpuset=/ mems_allowed=0 CPU: 4 PID: 1221 Comm: kswapd0 Not tainted 4.14.113-18770262-userdebug #1 Call trace: [<0000000000000000>] dump_backtrace+0x0/0x248 [<0000000000000000>] show_stack+0x18/0x20 [<0000000000000000>] __dump_stack+0x20/0x28 [<0000000000000000>] dump_stack+0x68/0x90 [<0000000000000000>] warn_alloc+0x104/0x198 [<0000000000000000>] __alloc_pages_nodemask+0xdc0/0xdf0 [<0000000000000000>] zs_malloc+0x148/0x3d0 [<0000000000000000>] zram_bvec_rw+0x410/0x798 [<0000000000000000>] zram_rw_page+0x88/0xdc [<0000000000000000>] bdev_write_page+0x70/0xbc [<0000000000000000>] __swap_writepage+0x58/0x37c [<0000000000000000>] swap_writepage+0x40/0x4c [<0000000000000000>] shrink_page_list+0xc30/0xf48 [<0000000000000000>] shrink_inactive_list+0x2b0/0x61c [<0000000000000000>] shrink_node_memcg+0x23c/0x618 [<0000000000000000>] shrink_node+0x1c8/0x304 [<0000000000000000>] kswapd+0x680/0x7c4 [<0000000000000000>] kthread+0x110/0x120 [<0000000000000000>] ret_from_fork+0x10/0x18 Mem-Info: active_anon:111826 inactive_anon:65557 isolated_anon:0\x0a active_file:44260 inactive_file:83422 isolated_file:0\x0a unevictable:4158 dirty:117 writeback:0 unstable:0\x0a slab_reclaimable:13943 slab_unreclaimable:43315\x0a mapped:102511 shmem:3299 pagetables:19566 bounce:0\x0a free:3510 free_pcp:553 free_cma:0 Node 0 active_anon:447304kB inactive_anon:262228kB active_file:177040kB inactive_file:333688kB unevictable:16632kB isolated(anon):0kB isolated(file):0kB mapped:410044kB d irty:468kB writeback:0kB shmem:13196kB writeback_tmp:0kB unstable:0kB all_unreclaimable? no Normal free:14040kB min:7440kB low:94500kB high:98136kB reserved_highatomic:32768KB active_anon:447336kB inactive_anon:261668kB active_file:177572kB inactive_file:333768k B unevictable:16632kB writepending:480kB present:4081664kB managed:3637088kB mlocked:16632kB kernel_stack:47072kB pagetables:78264kB bounce:0kB free_pcp:2280kB local_pcp:720kB free_cma:0kB [ 4738.329607] lowmem_reserve[]: 0 0 Normal: 860*4kB (H) 453*8kB (H) 180*16kB (H) 26*32kB (H) 34*64kB (H) 6*128kB (H) 2*256kB (H) 0*512kB 0*1024kB 0*2048kB 0*4096kB = 14232kB This is trace log which shows GFP_HIGHUSER consumes free pages right before ALLOC_NO_WATERMARKS. <...>-22275 [006] .... 889.213383: mm_page_alloc: page=00000000d2be5665 pfn=970744 order=0 migratetype=0 nr_free=3650 gfp_flags=GFP_HIGHUSER|__GFP_ZERO <...>-22275 [006] .... 889.213385: mm_page_alloc: page=000000004b2335c2 pfn=970745 order=0 migratetype=0 nr_free=3650 gfp_flags=GFP_HIGHUSER|__GFP_ZERO <...>-22275 [006] .... 889.213387: mm_page_alloc: page=00000000017272e1 pfn=970278 order=0 migratetype=0 nr_free=3650 gfp_flags=GFP_HIGHUSER|__GFP_ZERO <...>-22275 [006] .... 889.213389: mm_page_alloc: page=00000000c4be79fb pfn=970279 order=0 migratetype=0 nr_free=3650 gfp_flags=GFP_HIGHUSER|__GFP_ZERO <...>-22275 [006] .... 889.213391: mm_page_alloc: page=00000000f8a51d4f pfn=970260 order=0 migratetype=0 nr_free=3650 gfp_flags=GFP_HIGHUSER|__GFP_ZERO <...>-22275 [006] .... 889.213393: mm_page_alloc: page=000000006ba8f5ac pfn=970261 order=0 migratetype=0 nr_free=3650 gfp_flags=GFP_HIGHUSER|__GFP_ZERO <...>-22275 [006] .... 889.213395: mm_page_alloc: page=00000000819f1cd3 pfn=970196 order=0 migratetype=0 nr_free=3650 gfp_flags=GFP_HIGHUSER|__GFP_ZERO <...>-22275 [006] .... 889.213396: mm_page_alloc: page=00000000f6b72a64 pfn=970197 order=0 migratetype=0 nr_free=3650 gfp_flags=GFP_HIGHUSER|__GFP_ZERO kswapd0-1207 [005] ...1 889.213398: mm_page_alloc: page= (null) pfn=0 order=0 migratetype=1 nr_free=3650 gfp_flags=GFP_NOWAIT|__GFP_HIGHMEM|__GFP_NOWARN|__GFP_MOVABLE [jaewon31.kim@samsung.com: remove redundant code for high-order] Link: http://lkml.kernel.org/r/20200623035242.27232-1-jaewon31.kim@samsung.com Reported-by: Yong-Taek Lee Suggested-by: Minchan Kim Signed-off-by: Jaewon Kim Signed-off-by: Andrew Morton Reviewed-by: Baoquan He Acked-by: Vlastimil Babka Acked-by: Mel Gorman Cc: Johannes Weiner Cc: Yong-Taek Lee Cc: Michal Hocko Link: http://lkml.kernel.org/r/20200619235958.11283-1-jaewon31.kim@samsung.com Signed-off-by: Linus Torvalds --- mm/page_alloc.c | 66 +++++++++++++++++++++++++++---------------------- 1 file changed, 36 insertions(+), 30 deletions(-) diff --git a/mm/page_alloc.c b/mm/page_alloc.c index ce2409ee2144b..1b2f7b3bba342 100644 --- a/mm/page_alloc.c +++ b/mm/page_alloc.c @@ -3486,6 +3486,29 @@ static noinline bool should_fail_alloc_page(gfp_t gfp_mask, unsigned int order) } ALLOW_ERROR_INJECTION(should_fail_alloc_page, TRUE); +static inline long __zone_watermark_unusable_free(struct zone *z, + unsigned int order, unsigned int alloc_flags) +{ + const bool alloc_harder = (alloc_flags & (ALLOC_HARDER|ALLOC_OOM)); + long unusable_free = (1 << order) - 1; + + /* + * If the caller does not have rights to ALLOC_HARDER then subtract + * the high-atomic reserves. This will over-estimate the size of the + * atomic reserve but it avoids a search. + */ + if (likely(!alloc_harder)) + unusable_free += z->nr_reserved_highatomic; + +#ifdef CONFIG_CMA + /* If allocation can't use CMA areas don't use free CMA pages */ + if (!(alloc_flags & ALLOC_CMA)) + unusable_free += zone_page_state(z, NR_FREE_CMA_PAGES); +#endif + + return unusable_free; +} + /* * Return true if free base pages are above 'mark'. For high-order checks it * will return true of the order-0 watermark is reached and there is at least @@ -3501,19 +3524,12 @@ bool __zone_watermark_ok(struct zone *z, unsigned int order, unsigned long mark, const bool alloc_harder = (alloc_flags & (ALLOC_HARDER|ALLOC_OOM)); /* free_pages may go negative - that's OK */ - free_pages -= (1 << order) - 1; + free_pages -= __zone_watermark_unusable_free(z, order, alloc_flags); if (alloc_flags & ALLOC_HIGH) min -= min / 2; - /* - * If the caller does not have rights to ALLOC_HARDER then subtract - * the high-atomic reserves. This will over-estimate the size of the - * atomic reserve but it avoids a search. - */ - if (likely(!alloc_harder)) { - free_pages -= z->nr_reserved_highatomic; - } else { + if (unlikely(alloc_harder)) { /* * OOM victims can try even harder than normal ALLOC_HARDER * users on the grounds that it's definitely going to be in @@ -3526,13 +3542,6 @@ bool __zone_watermark_ok(struct zone *z, unsigned int order, unsigned long mark, min -= min / 4; } - -#ifdef CONFIG_CMA - /* If allocation can't use CMA areas don't use free CMA pages */ - if (!(alloc_flags & ALLOC_CMA)) - free_pages -= zone_page_state(z, NR_FREE_CMA_PAGES); -#endif - /* * Check watermarks for an order-0 allocation request. If these * are not met, then a high-order request also cannot go ahead @@ -3581,25 +3590,22 @@ static inline bool zone_watermark_fast(struct zone *z, unsigned int order, unsigned long mark, int highest_zoneidx, unsigned int alloc_flags) { - long free_pages = zone_page_state(z, NR_FREE_PAGES); - long cma_pages = 0; + long free_pages; -#ifdef CONFIG_CMA - /* If allocation can't use CMA areas don't use free CMA pages */ - if (!(alloc_flags & ALLOC_CMA)) - cma_pages = zone_page_state(z, NR_FREE_CMA_PAGES); -#endif + free_pages = zone_page_state(z, NR_FREE_PAGES); /* * Fast check for order-0 only. If this fails then the reserves - * need to be calculated. There is a corner case where the check - * passes but only the high-order atomic reserve are free. If - * the caller is !atomic then it'll uselessly search the free - * list. That corner case is then slower but it is harmless. + * need to be calculated. */ - if (!order && (free_pages - cma_pages) > - mark + z->lowmem_reserve[highest_zoneidx]) - return true; + if (!order) { + long fast_free; + + fast_free = free_pages; + fast_free -= __zone_watermark_unusable_free(z, 0, alloc_flags); + if (fast_free > mark + z->lowmem_reserve[highest_zoneidx]) + return true; + } return __zone_watermark_ok(z, order, mark, highest_zoneidx, alloc_flags, free_pages); -- GitLab From f80b08fc44536a311a9f3182e50f318b79076425 Mon Sep 17 00:00:00 2001 From: Charan Teja Reddy Date: Thu, 6 Aug 2020 23:25:24 -0700 Subject: [PATCH 1452/1476] mm, page_alloc: skip ->waternark_boost for atomic order-0 allocations When boosting is enabled, it is observed that rate of atomic order-0 allocation failures are high due to the fact that free levels in the system are checked with ->watermark_boost offset. This is not a problem for sleepable allocations but for atomic allocations which looks like regression. This problem is seen frequently on system setup of Android kernel running on Snapdragon hardware with 4GB RAM size. When no extfrag event occurred in the system, ->watermark_boost factor is zero, thus the watermark configurations in the system are: _watermark = ( [WMARK_MIN] = 1272, --> ~5MB [WMARK_LOW] = 9067, --> ~36MB [WMARK_HIGH] = 9385), --> ~38MB watermark_boost = 0 After launching some memory hungry applications in Android which can cause extfrag events in the system to an extent that ->watermark_boost can be set to max i.e. default boost factor makes it to 150% of high watermark. _watermark = ( [WMARK_MIN] = 1272, --> ~5MB [WMARK_LOW] = 9067, --> ~36MB [WMARK_HIGH] = 9385), --> ~38MB watermark_boost = 14077, -->~57MB With default system configuration, for an atomic order-0 allocation to succeed, having free memory of ~2MB will suffice. But boosting makes the min_wmark to ~61MB thus for an atomic order-0 allocation to be successful system should have minimum of ~23MB of free memory(from calculations of zone_watermark_ok(), min = 3/4(min/2)). But failures are observed despite system is having ~20MB of free memory. In the testing, this is reproducible as early as first 300secs since boot and with furtherlowram configurations(<2GB) it is observed as early as first 150secs since boot. These failures can be avoided by excluding the ->watermark_boost in watermark caluculations for atomic order-0 allocations. [akpm@linux-foundation.org: fix comment grammar, reflow comment] [charante@codeaurora.org: fix suggested by Mel Gorman] Link: http://lkml.kernel.org/r/31556793-57b1-1c21-1a9d-22674d9bd938@codeaurora.org Signed-off-by: Charan Teja Reddy Signed-off-by: Andrew Morton Acked-by: Vlastimil Babka Cc: Vinayak Menon Cc: Mel Gorman Link: http://lkml.kernel.org/r/1589882284-21010-1-git-send-email-charante@codeaurora.org Signed-off-by: Linus Torvalds --- mm/page_alloc.c | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/mm/page_alloc.c b/mm/page_alloc.c index 1b2f7b3bba342..0fb5c97ac94cb 100644 --- a/mm/page_alloc.c +++ b/mm/page_alloc.c @@ -3588,7 +3588,7 @@ bool zone_watermark_ok(struct zone *z, unsigned int order, unsigned long mark, static inline bool zone_watermark_fast(struct zone *z, unsigned int order, unsigned long mark, int highest_zoneidx, - unsigned int alloc_flags) + unsigned int alloc_flags, gfp_t gfp_mask) { long free_pages; @@ -3607,8 +3607,23 @@ static inline bool zone_watermark_fast(struct zone *z, unsigned int order, return true; } - return __zone_watermark_ok(z, order, mark, highest_zoneidx, alloc_flags, - free_pages); + if (__zone_watermark_ok(z, order, mark, highest_zoneidx, alloc_flags, + free_pages)) + return true; + /* + * Ignore watermark boosting for GFP_ATOMIC order-0 allocations + * when checking the min watermark. The min watermark is the + * point where boosting is ignored so that kswapd is woken up + * when below the low watermark. + */ + if (unlikely(!order && (gfp_mask & __GFP_ATOMIC) && z->watermark_boost + && ((alloc_flags & ALLOC_WMARK_MASK) == WMARK_MIN))) { + mark = z->_watermark[WMARK_MIN]; + return __zone_watermark_ok(z, order, mark, highest_zoneidx, + alloc_flags, free_pages); + } + + return false; } bool zone_watermark_ok_safe(struct zone *z, unsigned int order, @@ -3752,7 +3767,8 @@ retry: mark = wmark_pages(zone, alloc_flags & ALLOC_WMARK_MASK); if (!zone_watermark_fast(zone, order, mark, - ac->highest_zoneidx, alloc_flags)) { + ac->highest_zoneidx, alloc_flags, + gfp_mask)) { int ret; #ifdef CONFIG_DEFERRED_STRUCT_PAGE_INIT -- GitLab From 0a18e60788d6a39436e8b5e91001b790043fc29c Mon Sep 17 00:00:00 2001 From: David Hildenbrand Date: Thu, 6 Aug 2020 23:25:27 -0700 Subject: [PATCH 1453/1476] mm: remove vm_total_pages The global variable "vm_total_pages" is a relic from older days. There is only a single user that reads the variable - build_all_zonelists() - and the first thing it does is update it. Use a local variable in build_all_zonelists() instead and remove the global variable. Signed-off-by: David Hildenbrand Signed-off-by: Andrew Morton Reviewed-by: Wei Yang Reviewed-by: Pankaj Gupta Reviewed-by: Mike Rapoport Acked-by: Michal Hocko Cc: Johannes Weiner Cc: Huang Ying Cc: Minchan Kim Link: http://lkml.kernel.org/r/20200619132410.23859-2-david@redhat.com Signed-off-by: Linus Torvalds --- include/linux/swap.h | 1 - mm/memory_hotplug.c | 3 --- mm/page-writeback.c | 6 ++---- mm/page_alloc.c | 2 ++ mm/vmscan.c | 5 ----- 5 files changed, 4 insertions(+), 13 deletions(-) diff --git a/include/linux/swap.h b/include/linux/swap.h index 5b3216ba39a99..4ab236692e05b 100644 --- a/include/linux/swap.h +++ b/include/linux/swap.h @@ -372,7 +372,6 @@ extern unsigned long mem_cgroup_shrink_node(struct mem_cgroup *mem, extern unsigned long shrink_all_memory(unsigned long nr_pages); extern int vm_swappiness; extern int remove_mapping(struct address_space *mapping, struct page *page); -extern unsigned long vm_total_pages; extern unsigned long reclaim_pages(struct list_head *page_list); #ifdef CONFIG_NUMA diff --git a/mm/memory_hotplug.c b/mm/memory_hotplug.c index da374cd3d45b3..be3c62e3fb95c 100644 --- a/mm/memory_hotplug.c +++ b/mm/memory_hotplug.c @@ -844,8 +844,6 @@ int __ref online_pages(unsigned long pfn, unsigned long nr_pages, kswapd_run(nid); kcompactd_run(nid); - vm_total_pages = nr_free_pagecache_pages(); - writeback_set_ratelimit(); memory_notify(MEM_ONLINE, &arg); @@ -1595,7 +1593,6 @@ static int __ref __offline_pages(unsigned long start_pfn, kcompactd_stop(node); } - vm_total_pages = nr_free_pagecache_pages(); writeback_set_ratelimit(); memory_notify(MEM_OFFLINE, &arg); diff --git a/mm/page-writeback.c b/mm/page-writeback.c index 28b3e7a675657..4e4ddd67b71e5 100644 --- a/mm/page-writeback.c +++ b/mm/page-writeback.c @@ -2076,13 +2076,11 @@ static int page_writeback_cpu_online(unsigned int cpu) * Called early on to tune the page writeback dirty limits. * * We used to scale dirty pages according to how total memory - * related to pages that could be allocated for buffers (by - * comparing nr_free_buffer_pages() to vm_total_pages. + * related to pages that could be allocated for buffers. * * However, that was when we used "dirty_ratio" to scale with * all memory, and we don't do that any more. "dirty_ratio" - * is now applied to total non-HIGHPAGE memory (by subtracting - * totalhigh_pages from vm_total_pages), and as such we can't + * is now applied to total non-HIGHPAGE memory, and as such we can't * get into the old insane situation any more where we had * large amounts of dirty pages compared to a small amount of * non-HIGHMEM memory. diff --git a/mm/page_alloc.c b/mm/page_alloc.c index 0fb5c97ac94cb..20184e2a8cfe3 100644 --- a/mm/page_alloc.c +++ b/mm/page_alloc.c @@ -5912,6 +5912,8 @@ build_all_zonelists_init(void) */ void __ref build_all_zonelists(pg_data_t *pgdat) { + unsigned long vm_total_pages; + if (system_state == SYSTEM_BOOTING) { build_all_zonelists_init(); } else { diff --git a/mm/vmscan.c b/mm/vmscan.c index 23156c252e0a1..d4d7cd1d24c1c 100644 --- a/mm/vmscan.c +++ b/mm/vmscan.c @@ -170,11 +170,6 @@ struct scan_control { * From 0 .. 200. Higher means more swappy. */ int vm_swappiness = 60; -/* - * The total number of pages which are beyond the high watermark within all - * zones. - */ -unsigned long vm_total_pages; static void set_task_reclaim_state(struct task_struct *task, struct reclaim_state *rs) -- GitLab From 56b9413bcb369f1329a438c7b61d235b4123e794 Mon Sep 17 00:00:00 2001 From: David Hildenbrand Date: Thu, 6 Aug 2020 23:25:30 -0700 Subject: [PATCH 1454/1476] mm/page_alloc: remove nr_free_pagecache_pages() nr_free_pagecache_pages() isn't used outside page_alloc.c anymore - and the name does not really help to understand what's going on. Let's open-code it instead and add a comment. Signed-off-by: David Hildenbrand Signed-off-by: Andrew Morton Reviewed-by: Wei Yang Reviewed-by: Pankaj Gupta Reviewed-by: Mike Rapoport Acked-by: Michal Hocko Cc: Johannes Weiner Cc: Minchan Kim Cc: Huang Ying Link: http://lkml.kernel.org/r/20200619132410.23859-3-david@redhat.com Signed-off-by: Linus Torvalds --- include/linux/swap.h | 1 - mm/page_alloc.c | 16 ++-------------- 2 files changed, 2 insertions(+), 15 deletions(-) diff --git a/include/linux/swap.h b/include/linux/swap.h index 4ab236692e05b..7eb59bc552a5b 100644 --- a/include/linux/swap.h +++ b/include/linux/swap.h @@ -328,7 +328,6 @@ void workingset_update_node(struct xa_node *node); /* linux/mm/page_alloc.c */ extern unsigned long totalreserve_pages; extern unsigned long nr_free_buffer_pages(void); -extern unsigned long nr_free_pagecache_pages(void); /* Definition of global_zone_page_state not available yet */ #define nr_free_pages() global_zone_page_state(NR_FREE_PAGES) diff --git a/mm/page_alloc.c b/mm/page_alloc.c index 20184e2a8cfe3..ba09a3aeb76b0 100644 --- a/mm/page_alloc.c +++ b/mm/page_alloc.c @@ -5186,19 +5186,6 @@ unsigned long nr_free_buffer_pages(void) } EXPORT_SYMBOL_GPL(nr_free_buffer_pages); -/** - * nr_free_pagecache_pages - count number of pages beyond high watermark - * - * nr_free_pagecache_pages() counts the number of pages which are beyond the - * high watermark within all zones. - * - * Return: number of pages beyond high watermark within all zones. - */ -unsigned long nr_free_pagecache_pages(void) -{ - return nr_free_zone_pages(gfp_zone(GFP_HIGHUSER_MOVABLE)); -} - static inline void show_node(struct zone *zone) { if (IS_ENABLED(CONFIG_NUMA)) @@ -5920,7 +5907,8 @@ void __ref build_all_zonelists(pg_data_t *pgdat) __build_all_zonelists(pgdat); /* cpuset refresh routine should be here */ } - vm_total_pages = nr_free_pagecache_pages(); + /* Get the number of free pages beyond high watermark in all zones. */ + vm_total_pages = nr_free_zone_pages(gfp_zone(GFP_HIGHUSER_MOVABLE)); /* * Disable grouping by mobility if the number of pages in the * system is too low to allow the mechanism to work. It would be -- GitLab From 93146d98ce73fa416be3ffa147247a2454645ebc Mon Sep 17 00:00:00 2001 From: David Hildenbrand Date: Thu, 6 Aug 2020 23:25:35 -0700 Subject: [PATCH 1455/1476] mm/memory_hotplug: document why shuffle_zone() is relevant It's not completely obvious why we have to shuffle the complete zone - introduced in commit e900a918b098 ("mm: shuffle initial free memory to improve memory-side-cache utilization") - because some sort of shuffling is already performed when onlining pages via __free_one_page(), placing MAX_ORDER-1 pages either to the head or the tail of the freelist. Let's document why we have to shuffle the complete zone when exposing larger, contiguous physical memory areas to the buddy. Signed-off-by: David Hildenbrand Signed-off-by: Andrew Morton Acked-by: Dan Williams Acked-by: Michal Hocko Cc: Alexander Duyck Cc: Dan Williams Cc: Michal Hocko Link: http://lkml.kernel.org/r/20200624094741.9918-3-david@redhat.com Signed-off-by: Linus Torvalds --- mm/memory_hotplug.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/mm/memory_hotplug.c b/mm/memory_hotplug.c index be3c62e3fb95c..ac6961abaa103 100644 --- a/mm/memory_hotplug.c +++ b/mm/memory_hotplug.c @@ -831,6 +831,14 @@ int __ref online_pages(unsigned long pfn, unsigned long nr_pages, zone->zone_pgdat->node_present_pages += onlined_pages; pgdat_resize_unlock(zone->zone_pgdat, &flags); + /* + * When exposing larger, physically contiguous memory areas to the + * buddy, shuffling in the buddy (when freeing onlined pages, putting + * them either to the head or the tail of the freelist) is only helpful + * for maintaining the shuffle, but not for creating the initial + * shuffle. Shuffle the whole zone to make sure the just onlined pages + * are properly distributed across the whole freelist. + */ shuffle_zone(zone); node_states_set_node(nid, &arg); -- GitLab From 839195352d8235072dcaac054b2e26b2dd2213e5 Mon Sep 17 00:00:00 2001 From: David Hildenbrand Date: Thu, 6 Aug 2020 23:25:38 -0700 Subject: [PATCH 1456/1476] mm/shuffle: remove dynamic reconfiguration Commit e900a918b098 ("mm: shuffle initial free memory to improve memory-side-cache utilization") promised "autodetection of a memory-side-cache (to be added in a follow-on patch)" over a year ago. The original series included patches [1], however, they were dropped during review [2] to be followed-up later. Due to lack of platforms that publish an HMAT, autodetection is currently not implemented. However, manual activation is actively used [3]. Let's simplify for now and re-add when really (ever?) needed. [1] https://lkml.kernel.org/r/154510700291.1941238.817190985966612531.stgit@dwillia2-desk3.amr.corp.intel.com [2] https://lkml.kernel.org/r/154690326478.676627.103843791978176914.stgit@dwillia2-desk3.amr.corp.intel.com [3] https://lkml.kernel.org/r/CAPcyv4irwGUU2x+c6b4L=KbB1dnasNKaaZd6oSpYjL9kfsnROQ@mail.gmail.com Signed-off-by: David Hildenbrand Signed-off-by: Andrew Morton Reviewed-by: Wei Yang Acked-by: Dan Williams Acked-by: Michal Hocko Cc: Johannes Weiner Cc: Michal Hocko Cc: Minchan Kim Cc: Huang Ying Cc: Wei Yang Cc: Mel Gorman Cc: Dan Williams Link: http://lkml.kernel.org/r/20200624094741.9918-4-david@redhat.com Signed-off-by: Linus Torvalds --- mm/shuffle.c | 28 ++-------------------------- mm/shuffle.h | 17 ----------------- 2 files changed, 2 insertions(+), 43 deletions(-) diff --git a/mm/shuffle.c b/mm/shuffle.c index dd13ab851b3ee..9b5cd4b004b0f 100644 --- a/mm/shuffle.c +++ b/mm/shuffle.c @@ -10,33 +10,11 @@ #include "shuffle.h" DEFINE_STATIC_KEY_FALSE(page_alloc_shuffle_key); -static unsigned long shuffle_state __ro_after_init; - -/* - * Depending on the architecture, module parameter parsing may run - * before, or after the cache detection. SHUFFLE_FORCE_DISABLE prevents, - * or reverts the enabling of the shuffle implementation. SHUFFLE_ENABLE - * attempts to turn on the implementation, but aborts if it finds - * SHUFFLE_FORCE_DISABLE already set. - */ -__meminit void page_alloc_shuffle(enum mm_shuffle_ctl ctl) -{ - if (ctl == SHUFFLE_FORCE_DISABLE) - set_bit(SHUFFLE_FORCE_DISABLE, &shuffle_state); - - if (test_bit(SHUFFLE_FORCE_DISABLE, &shuffle_state)) { - if (test_and_clear_bit(SHUFFLE_ENABLE, &shuffle_state)) - static_branch_disable(&page_alloc_shuffle_key); - } else if (ctl == SHUFFLE_ENABLE - && !test_and_set_bit(SHUFFLE_ENABLE, &shuffle_state)) - static_branch_enable(&page_alloc_shuffle_key); -} static bool shuffle_param; static int shuffle_show(char *buffer, const struct kernel_param *kp) { - return sprintf(buffer, "%c\n", test_bit(SHUFFLE_ENABLE, &shuffle_state) - ? 'Y' : 'N'); + return sprintf(buffer, "%c\n", shuffle_param ? 'Y' : 'N'); } static __meminit int shuffle_store(const char *val, @@ -47,9 +25,7 @@ static __meminit int shuffle_store(const char *val, if (rc < 0) return rc; if (shuffle_param) - page_alloc_shuffle(SHUFFLE_ENABLE); - else - page_alloc_shuffle(SHUFFLE_FORCE_DISABLE); + static_branch_enable(&page_alloc_shuffle_key); return 0; } module_param_call(shuffle, shuffle_store, shuffle_show, &shuffle_param, 0400); diff --git a/mm/shuffle.h b/mm/shuffle.h index 4d79f03b6658f..71b784f0b7c3e 100644 --- a/mm/shuffle.h +++ b/mm/shuffle.h @@ -4,23 +4,10 @@ #define _MM_SHUFFLE_H #include -/* - * SHUFFLE_ENABLE is called from the command line enabling path, or by - * platform-firmware enabling that indicates the presence of a - * direct-mapped memory-side-cache. SHUFFLE_FORCE_DISABLE is called from - * the command line path and overrides any previous or future - * SHUFFLE_ENABLE. - */ -enum mm_shuffle_ctl { - SHUFFLE_ENABLE, - SHUFFLE_FORCE_DISABLE, -}; - #define SHUFFLE_ORDER (MAX_ORDER-1) #ifdef CONFIG_SHUFFLE_PAGE_ALLOCATOR DECLARE_STATIC_KEY_FALSE(page_alloc_shuffle_key); -extern void page_alloc_shuffle(enum mm_shuffle_ctl ctl); extern void __shuffle_free_memory(pg_data_t *pgdat); extern bool shuffle_pick_tail(void); static inline void shuffle_free_memory(pg_data_t *pgdat) @@ -58,10 +45,6 @@ static inline void shuffle_zone(struct zone *z) { } -static inline void page_alloc_shuffle(enum mm_shuffle_ctl ctl) -{ -} - static inline bool is_shuffle_order(int order) { return false; -- GitLab From d38ac97f8a7c4519ba141bbd7c2f7a8da8c9ff8d Mon Sep 17 00:00:00 2001 From: Wei Yang Date: Thu, 6 Aug 2020 23:25:41 -0700 Subject: [PATCH 1457/1476] mm/page_alloc.c: replace the definition of NR_MIGRATETYPE_BITS with PB_migratetype_bits We already have the definition of PB_migratetype_bits and current NR_MIGRATETYPE_BITS looks like a cyclic definition. Just use PB_migratetype_bits is enough. Signed-off-by: Wei Yang Signed-off-by: Andrew Morton Cc: Mel Gorman Link: http://lkml.kernel.org/r/20200623124201.8199-1-richard.weiyang@linux.alibaba.com Signed-off-by: Linus Torvalds --- include/linux/mmzone.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/include/linux/mmzone.h b/include/linux/mmzone.h index 2eef8afd3a0f6..f509ede317b5e 100644 --- a/include/linux/mmzone.h +++ b/include/linux/mmzone.h @@ -88,8 +88,7 @@ static inline bool is_migrate_movable(int mt) extern int page_group_by_mobility_disabled; -#define NR_MIGRATETYPE_BITS (PB_migrate_end - PB_migrate + 1) -#define MIGRATETYPE_MASK ((1UL << NR_MIGRATETYPE_BITS) - 1) +#define MIGRATETYPE_MASK ((1UL << PB_migratetype_bits) - 1) #define get_pageblock_migratetype(page) \ get_pfnblock_flags_mask(page, page_to_pfn(page), \ -- GitLab From 399b795b7a80bf703aae56fa0b66fd1949ca1fd9 Mon Sep 17 00:00:00 2001 From: Wei Yang Date: Thu, 6 Aug 2020 23:25:44 -0700 Subject: [PATCH 1458/1476] mm/page_alloc.c: extract the common part in pfn_to_bitidx() The return value calculation is the same both for SPARSEMEM or not. Just take it out. Signed-off-by: Wei Yang Signed-off-by: Andrew Morton Cc: Mel Gorman Link: http://lkml.kernel.org/r/20200623124201.8199-2-richard.weiyang@linux.alibaba.com Signed-off-by: Linus Torvalds --- mm/page_alloc.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/mm/page_alloc.c b/mm/page_alloc.c index ba09a3aeb76b0..6c524fb76b317 100644 --- a/mm/page_alloc.c +++ b/mm/page_alloc.c @@ -459,11 +459,10 @@ static inline int pfn_to_bitidx(struct page *page, unsigned long pfn) { #ifdef CONFIG_SPARSEMEM pfn &= (PAGES_PER_SECTION-1); - return (pfn >> pageblock_order) * NR_PAGEBLOCK_BITS; #else pfn = pfn - round_down(page_zone(page)->zone_start_pfn, pageblock_nr_pages); - return (pfn >> pageblock_order) * NR_PAGEBLOCK_BITS; #endif /* CONFIG_SPARSEMEM */ + return (pfn >> pageblock_order) * NR_PAGEBLOCK_BITS; } /** -- GitLab From d93d5ab9ca01e24efc6add60371b0d5684b5c146 Mon Sep 17 00:00:00 2001 From: Wei Yang Date: Thu, 6 Aug 2020 23:25:48 -0700 Subject: [PATCH 1459/1476] mm/page_alloc.c: simplify pageblock bitmap access Due to commit e58469bafd05 ("mm: page_alloc: use word-based accesses for get/set pageblock bitmaps"), pageblock bitmap is accessed with word-based access. This operation could be simplified a little. Intuitively, if we want to get a bit range [start_idx, end_idx] in a word, we can do like this: mask = (1 << (end_bitidx - start_bitidx + 1)) - 1; ret = (word >> start_idx) & mask; And also if we want to set a bit range [start_idx, end_idx] with flags, we can do the same by just shift start_bitidx. By doing so we reduce some instructions for these two helper functions: Before Patched set_pfnblock_flags_mask 209 198(-5%) get_pfnblock_flags_mask 101 87(-13%) Since the syntax is changed a little, we need to check the whole 4-bit migrate_type instead of part of it. Signed-off-by: Wei Yang Signed-off-by: Andrew Morton Cc: Mel Gorman Link: http://lkml.kernel.org/r/20200623124201.8199-3-richard.weiyang@linux.alibaba.com Signed-off-by: Linus Torvalds --- include/linux/pageblock-flags.h | 22 +++++++--------------- mm/page_alloc.c | 13 ++++++------- 2 files changed, 13 insertions(+), 22 deletions(-) diff --git a/include/linux/pageblock-flags.h b/include/linux/pageblock-flags.h index c066fec5b74bf..6556e44744090 100644 --- a/include/linux/pageblock-flags.h +++ b/include/linux/pageblock-flags.h @@ -66,25 +66,17 @@ void set_pfnblock_flags_mask(struct page *page, unsigned long mask); /* Declarations for getting and setting flags. See mm/page_alloc.c */ -#define get_pageblock_flags_group(page, start_bitidx, end_bitidx) \ - get_pfnblock_flags_mask(page, page_to_pfn(page), \ - end_bitidx, \ - (1 << (end_bitidx - start_bitidx + 1)) - 1) -#define set_pageblock_flags_group(page, flags, start_bitidx, end_bitidx) \ - set_pfnblock_flags_mask(page, flags, page_to_pfn(page), \ - end_bitidx, \ - (1 << (end_bitidx - start_bitidx + 1)) - 1) - #ifdef CONFIG_COMPACTION #define get_pageblock_skip(page) \ - get_pageblock_flags_group(page, PB_migrate_skip, \ - PB_migrate_skip) + get_pfnblock_flags_mask(page, page_to_pfn(page), \ + PB_migrate_skip, (1 << (PB_migrate_skip))) #define clear_pageblock_skip(page) \ - set_pageblock_flags_group(page, 0, PB_migrate_skip, \ - PB_migrate_skip) + set_pfnblock_flags_mask(page, 0, page_to_pfn(page), \ + PB_migrate_skip, (1 << PB_migrate_skip)) #define set_pageblock_skip(page) \ - set_pageblock_flags_group(page, 1, PB_migrate_skip, \ - PB_migrate_skip) + set_pfnblock_flags_mask(page, (1 << PB_migrate_skip), \ + page_to_pfn(page), \ + PB_migrate_skip, (1 << PB_migrate_skip)) #else static inline bool get_pageblock_skip(struct page *page) { diff --git a/mm/page_alloc.c b/mm/page_alloc.c index 6c524fb76b317..e1fdca9ef4e96 100644 --- a/mm/page_alloc.c +++ b/mm/page_alloc.c @@ -489,8 +489,7 @@ static __always_inline unsigned long __get_pfnblock_flags_mask(struct page *page bitidx &= (BITS_PER_LONG-1); word = bitmap[word_bitidx]; - bitidx += end_bitidx; - return (word >> (BITS_PER_LONG - bitidx - 1)) & mask; + return (word >> bitidx) & mask; } unsigned long get_pfnblock_flags_mask(struct page *page, unsigned long pfn, @@ -532,9 +531,8 @@ void set_pfnblock_flags_mask(struct page *page, unsigned long flags, VM_BUG_ON_PAGE(!zone_spans_pfn(page_zone(page), pfn), page); - bitidx += end_bitidx; - mask <<= (BITS_PER_LONG - bitidx - 1); - flags <<= (BITS_PER_LONG - bitidx - 1); + mask <<= bitidx; + flags <<= bitidx; word = READ_ONCE(bitmap[word_bitidx]); for (;;) { @@ -551,8 +549,9 @@ void set_pageblock_migratetype(struct page *page, int migratetype) migratetype < MIGRATE_PCPTYPES)) migratetype = MIGRATE_UNMOVABLE; - set_pageblock_flags_group(page, (unsigned long)migratetype, - PB_migrate, PB_migrate_end); + set_pfnblock_flags_mask(page, (unsigned long)migratetype, + page_to_pfn(page), PB_migrate_end, + MIGRATETYPE_MASK); } #ifdef CONFIG_DEBUG_VM -- GitLab From 535b81e209219e03f815379746bfd1eeb82d68e5 Mon Sep 17 00:00:00 2001 From: Wei Yang Date: Thu, 6 Aug 2020 23:25:51 -0700 Subject: [PATCH 1460/1476] mm/page_alloc.c: remove unnecessary end_bitidx for [set|get]_pfnblock_flags_mask() After previous cleanup, the end_bitidx is not necessary any more. Signed-off-by: Wei Yang Signed-off-by: Andrew Morton Cc: Mel Gorman Link: http://lkml.kernel.org/r/20200623124201.8199-4-richard.weiyang@linux.alibaba.com Signed-off-by: Linus Torvalds --- include/linux/mmzone.h | 3 +-- include/linux/pageblock-flags.h | 8 +++----- mm/page_alloc.c | 15 +++++---------- 3 files changed, 9 insertions(+), 17 deletions(-) diff --git a/include/linux/mmzone.h b/include/linux/mmzone.h index f509ede317b5e..635a96cd9b1f7 100644 --- a/include/linux/mmzone.h +++ b/include/linux/mmzone.h @@ -91,8 +91,7 @@ extern int page_group_by_mobility_disabled; #define MIGRATETYPE_MASK ((1UL << PB_migratetype_bits) - 1) #define get_pageblock_migratetype(page) \ - get_pfnblock_flags_mask(page, page_to_pfn(page), \ - PB_migrate_end, MIGRATETYPE_MASK) + get_pfnblock_flags_mask(page, page_to_pfn(page), MIGRATETYPE_MASK) struct free_area { struct list_head free_list[MIGRATE_TYPES]; diff --git a/include/linux/pageblock-flags.h b/include/linux/pageblock-flags.h index 6556e44744090..fff52ad370c11 100644 --- a/include/linux/pageblock-flags.h +++ b/include/linux/pageblock-flags.h @@ -56,27 +56,25 @@ struct page; unsigned long get_pfnblock_flags_mask(struct page *page, unsigned long pfn, - unsigned long end_bitidx, unsigned long mask); void set_pfnblock_flags_mask(struct page *page, unsigned long flags, unsigned long pfn, - unsigned long end_bitidx, unsigned long mask); /* Declarations for getting and setting flags. See mm/page_alloc.c */ #ifdef CONFIG_COMPACTION #define get_pageblock_skip(page) \ get_pfnblock_flags_mask(page, page_to_pfn(page), \ - PB_migrate_skip, (1 << (PB_migrate_skip))) + (1 << (PB_migrate_skip))) #define clear_pageblock_skip(page) \ set_pfnblock_flags_mask(page, 0, page_to_pfn(page), \ - PB_migrate_skip, (1 << PB_migrate_skip)) + (1 << PB_migrate_skip)) #define set_pageblock_skip(page) \ set_pfnblock_flags_mask(page, (1 << PB_migrate_skip), \ page_to_pfn(page), \ - PB_migrate_skip, (1 << PB_migrate_skip)) + (1 << PB_migrate_skip)) #else static inline bool get_pageblock_skip(struct page *page) { diff --git a/mm/page_alloc.c b/mm/page_alloc.c index e1fdca9ef4e96..53c89de89e46f 100644 --- a/mm/page_alloc.c +++ b/mm/page_alloc.c @@ -469,14 +469,13 @@ static inline int pfn_to_bitidx(struct page *page, unsigned long pfn) * get_pfnblock_flags_mask - Return the requested group of flags for the pageblock_nr_pages block of pages * @page: The page within the block of interest * @pfn: The target page frame number - * @end_bitidx: The last bit of interest to retrieve * @mask: mask of bits that the caller is interested in * * Return: pageblock_bits flags */ -static __always_inline unsigned long __get_pfnblock_flags_mask(struct page *page, +static __always_inline +unsigned long __get_pfnblock_flags_mask(struct page *page, unsigned long pfn, - unsigned long end_bitidx, unsigned long mask) { unsigned long *bitmap; @@ -493,15 +492,14 @@ static __always_inline unsigned long __get_pfnblock_flags_mask(struct page *page } unsigned long get_pfnblock_flags_mask(struct page *page, unsigned long pfn, - unsigned long end_bitidx, unsigned long mask) { - return __get_pfnblock_flags_mask(page, pfn, end_bitidx, mask); + return __get_pfnblock_flags_mask(page, pfn, mask); } static __always_inline int get_pfnblock_migratetype(struct page *page, unsigned long pfn) { - return __get_pfnblock_flags_mask(page, pfn, PB_migrate_end, MIGRATETYPE_MASK); + return __get_pfnblock_flags_mask(page, pfn, MIGRATETYPE_MASK); } /** @@ -509,12 +507,10 @@ static __always_inline int get_pfnblock_migratetype(struct page *page, unsigned * @page: The page within the block of interest * @flags: The flags to set * @pfn: The target page frame number - * @end_bitidx: The last bit of interest * @mask: mask of bits that the caller is interested in */ void set_pfnblock_flags_mask(struct page *page, unsigned long flags, unsigned long pfn, - unsigned long end_bitidx, unsigned long mask) { unsigned long *bitmap; @@ -550,8 +546,7 @@ void set_pageblock_migratetype(struct page *page, int migratetype) migratetype = MIGRATE_UNMOVABLE; set_pfnblock_flags_mask(page, (unsigned long)migratetype, - page_to_pfn(page), PB_migrate_end, - MIGRATETYPE_MASK); + page_to_pfn(page), MIGRATETYPE_MASK); } #ifdef CONFIG_DEBUG_VM -- GitLab From 9e15afa5a87a3bf969a3b33c3dadfb8b46df42c0 Mon Sep 17 00:00:00 2001 From: Qian Cai Date: Thu, 6 Aug 2020 23:25:54 -0700 Subject: [PATCH 1461/1476] mm/page_alloc: silence a KASAN false positive kernel_init_free_pages() will use memset() on s390 to clear all pages from kmalloc_order() which will override KASAN redzones because a redzone was setup from the end of the allocation size to the end of the last page. Silence it by not reporting it there. An example of the report is, BUG: KASAN: slab-out-of-bounds in __free_pages_ok Write of size 4096 at addr 000000014beaa000 Call Trace: show_stack+0x152/0x210 dump_stack+0x1f8/0x248 print_address_description.isra.13+0x5e/0x4d0 kasan_report+0x130/0x178 check_memory_region+0x190/0x218 memset+0x34/0x60 __free_pages_ok+0x894/0x12f0 kfree+0x4f2/0x5e0 unpack_to_rootfs+0x60e/0x650 populate_rootfs+0x56/0x358 do_one_initcall+0x1f4/0xa20 kernel_init_freeable+0x758/0x7e8 kernel_init+0x1c/0x170 ret_from_fork+0x24/0x28 Memory state around the buggy address: 000000014bea9f00: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 000000014bea9f80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 >000000014beaa000: 03 fe fe fe fe fe fe fe fe fe fe fe fe fe fe fe ^ 000000014beaa080: fe fe fe fe fe fe fe fe fe fe fe fe fe fe fe fe 000000014beaa100: fe fe fe fe fe fe fe fe fe fe fe fe fe fe Fixes: 6471384af2a6 ("mm: security: introduce init_on_alloc=1 and init_on_free=1 boot options") Signed-off-by: Qian Cai Signed-off-by: Andrew Morton Tested-by: Vasily Gorbik Acked-by: Vasily Gorbik Cc: Dmitry Vyukov Cc: Christian Borntraeger Cc: Alexander Potapenko Cc: Kees Cook Cc: Heiko Carstens Link: http://lkml.kernel.org/r/20200610052154.5180-1-cai@lca.pw Signed-off-by: Linus Torvalds --- mm/page_alloc.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/mm/page_alloc.c b/mm/page_alloc.c index 53c89de89e46f..9a39009b15625 100644 --- a/mm/page_alloc.c +++ b/mm/page_alloc.c @@ -1156,8 +1156,11 @@ static void kernel_init_free_pages(struct page *page, int numpages) { int i; + /* s390's use of memset() could override KASAN redzones. */ + kasan_disable_current(); for (i = 0; i < numpages; i++) clear_highpage(page + i); + kasan_enable_current(); } static __always_inline bool free_pages_prepare(struct page *page, -- GitLab From da4156639985a9cdba9727d7f39b69a6b4268dad Mon Sep 17 00:00:00 2001 From: Wei Yang Date: Thu, 6 Aug 2020 23:25:58 -0700 Subject: [PATCH 1462/1476] mm/page_alloc: fallbacks at most has 3 elements MIGRAGE_TYPES is used to be the mark of end and there are at most 3 elements for the one dimension array. Reduce to 3 to save little memory. Signed-off-by: Wei Yang Signed-off-by: Andrew Morton Reviewed-by: David Hildenbrand Link: http://lkml.kernel.org/r/20200625231022.18784-1-richard.weiyang@linux.alibaba.com Signed-off-by: Linus Torvalds --- mm/page_alloc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mm/page_alloc.c b/mm/page_alloc.c index 9a39009b15625..2e49b184233cc 100644 --- a/mm/page_alloc.c +++ b/mm/page_alloc.c @@ -2268,7 +2268,7 @@ struct page *__rmqueue_smallest(struct zone *zone, unsigned int order, * This array describes the order lists are fallen back to when * the free lists for the desirable migrate type are depleted */ -static int fallbacks[MIGRATE_TYPES][4] = { +static int fallbacks[MIGRATE_TYPES][3] = { [MIGRATE_UNMOVABLE] = { MIGRATE_RECLAIMABLE, MIGRATE_MOVABLE, MIGRATE_TYPES }, [MIGRATE_MOVABLE] = { MIGRATE_RECLAIMABLE, MIGRATE_UNMOVABLE, MIGRATE_TYPES }, [MIGRATE_RECLAIMABLE] = { MIGRATE_UNMOVABLE, MIGRATE_MOVABLE, MIGRATE_TYPES }, -- GitLab From 182f3d7a02fddd9e4b1181c2ba710a930e05ddbb Mon Sep 17 00:00:00 2001 From: Muchun Song Date: Thu, 6 Aug 2020 23:26:01 -0700 Subject: [PATCH 1463/1476] mm/page_alloc.c: skip setting nodemask when we are in interrupt When we are in the interrupt context, it is irrelevant to the current task context. If we use current task's mems_allowed, we can be fair to alloc pages in the fast path and fall back to slow path memory allocation when the current node(which is the current task mems_allowed) does not have enough memory to allocate. In this case, it slows down the memory allocation speed of interrupt context. So we can skip setting the nodemask to allow any node to allocate memory, so that fast path allocation can success. Signed-off-by: Muchun Song Signed-off-by: Andrew Morton Reviewed-by: Pekka Enberg Cc: David Hildenbrand Link: http://lkml.kernel.org/r/20200706025921.53683-1-songmuchun@bytedance.com Signed-off-by: Linus Torvalds --- mm/page_alloc.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/mm/page_alloc.c b/mm/page_alloc.c index 2e49b184233cc..9f9e15a502aec 100644 --- a/mm/page_alloc.c +++ b/mm/page_alloc.c @@ -4788,7 +4788,11 @@ static inline bool prepare_alloc_pages(gfp_t gfp_mask, unsigned int order, if (cpusets_enabled()) { *alloc_mask |= __GFP_HARDWALL; - if (!ac->nodemask) + /* + * When we are in the interrupt context, it is irrelevant + * to the current task context. It means that any node ok. + */ + if (!in_interrupt() && !ac->nodemask) ac->nodemask = &cpuset_current_mems_allowed; else *alloc_flags |= ALLOC_CPUSET; -- GitLab From 8510e69c8efef82f2b37ea3e8ea19a27122c533e Mon Sep 17 00:00:00 2001 From: Joonsoo Kim Date: Thu, 6 Aug 2020 23:26:04 -0700 Subject: [PATCH 1464/1476] mm/page_alloc: fix memalloc_nocma_{save/restore} APIs Currently, memalloc_nocma_{save/restore} API that prevents CMA area in page allocation is implemented by using current_gfp_context(). However, there are two problems of this implementation. First, this doesn't work for allocation fastpath. In the fastpath, original gfp_mask is used since current_gfp_context() is introduced in order to control reclaim and it is on slowpath. So, CMA area can be allocated through the allocation fastpath even if memalloc_nocma_{save/restore} APIs are used. Currently, there is just one user for these APIs and it has a fallback method to prevent actual problem. Second, clearing __GFP_MOVABLE in current_gfp_context() has a side effect to exclude the memory on the ZONE_MOVABLE for allocation target. To fix these problems, this patch changes the implementation to exclude CMA area in page allocation. Main point of this change is using the alloc_flags. alloc_flags is mainly used to control allocation so it fits for excluding CMA area in allocation. Fixes: d7fefcc8de91 (mm/cma: add PF flag to force non cma alloc) Signed-off-by: Joonsoo Kim Signed-off-by: Andrew Morton Reviewed-by: Vlastimil Babka Cc: Christoph Hellwig Cc: Roman Gushchin Cc: Mike Kravetz Cc: Naoya Horiguchi Cc: Michal Hocko Cc: "Aneesh Kumar K . V" Link: http://lkml.kernel.org/r/1595468942-29687-1-git-send-email-iamjoonsoo.kim@lge.com Signed-off-by: Linus Torvalds --- include/linux/sched/mm.h | 8 +------- mm/page_alloc.c | 31 +++++++++++++++++++++---------- 2 files changed, 22 insertions(+), 17 deletions(-) diff --git a/include/linux/sched/mm.h b/include/linux/sched/mm.h index 6be66f52a2ad1..85023ddc2dc25 100644 --- a/include/linux/sched/mm.h +++ b/include/linux/sched/mm.h @@ -175,12 +175,10 @@ static inline bool in_vfork(struct task_struct *tsk) * Applies per-task gfp context to the given allocation flags. * PF_MEMALLOC_NOIO implies GFP_NOIO * PF_MEMALLOC_NOFS implies GFP_NOFS - * PF_MEMALLOC_NOCMA implies no allocation from CMA region. */ static inline gfp_t current_gfp_context(gfp_t flags) { - if (unlikely(current->flags & - (PF_MEMALLOC_NOIO | PF_MEMALLOC_NOFS | PF_MEMALLOC_NOCMA))) { + if (unlikely(current->flags & (PF_MEMALLOC_NOIO | PF_MEMALLOC_NOFS))) { /* * NOIO implies both NOIO and NOFS and it is a weaker context * so always make sure it makes precedence @@ -189,10 +187,6 @@ static inline gfp_t current_gfp_context(gfp_t flags) flags &= ~(__GFP_IO | __GFP_FS); else if (current->flags & PF_MEMALLOC_NOFS) flags &= ~__GFP_FS; -#ifdef CONFIG_CMA - if (current->flags & PF_MEMALLOC_NOCMA) - flags &= ~__GFP_MOVABLE; -#endif } return flags; } diff --git a/mm/page_alloc.c b/mm/page_alloc.c index 9f9e15a502aec..167732f4d1247 100644 --- a/mm/page_alloc.c +++ b/mm/page_alloc.c @@ -2785,7 +2785,7 @@ __rmqueue(struct zone *zone, unsigned int order, int migratetype, * allocating from CMA when over half of the zone's free memory * is in the CMA area. */ - if (migratetype == MIGRATE_MOVABLE && + if (alloc_flags & ALLOC_CMA && zone_page_state(zone, NR_FREE_CMA_PAGES) > zone_page_state(zone, NR_FREE_PAGES) / 2) { page = __rmqueue_cma_fallback(zone, order); @@ -2796,7 +2796,7 @@ __rmqueue(struct zone *zone, unsigned int order, int migratetype, retry: page = __rmqueue_smallest(zone, order, migratetype); if (unlikely(!page)) { - if (migratetype == MIGRATE_MOVABLE) + if (alloc_flags & ALLOC_CMA) page = __rmqueue_cma_fallback(zone, order); if (!page && __rmqueue_fallback(zone, order, migratetype, @@ -3687,6 +3687,20 @@ alloc_flags_nofragment(struct zone *zone, gfp_t gfp_mask) return alloc_flags; } +static inline unsigned int current_alloc_flags(gfp_t gfp_mask, + unsigned int alloc_flags) +{ +#ifdef CONFIG_CMA + unsigned int pflags = current->flags; + + if (!(pflags & PF_MEMALLOC_NOCMA) && + gfp_migratetype(gfp_mask) == MIGRATE_MOVABLE) + alloc_flags |= ALLOC_CMA; + +#endif + return alloc_flags; +} + /* * get_page_from_freelist goes through the zonelist trying to allocate * a page. @@ -4333,10 +4347,8 @@ gfp_to_alloc_flags(gfp_t gfp_mask) } else if (unlikely(rt_task(current)) && !in_interrupt()) alloc_flags |= ALLOC_HARDER; -#ifdef CONFIG_CMA - if (gfp_migratetype(gfp_mask) == MIGRATE_MOVABLE) - alloc_flags |= ALLOC_CMA; -#endif + alloc_flags = current_alloc_flags(gfp_mask, alloc_flags); + return alloc_flags; } @@ -4637,7 +4649,7 @@ retry: reserve_flags = __gfp_pfmemalloc_flags(gfp_mask); if (reserve_flags) - alloc_flags = reserve_flags; + alloc_flags = current_alloc_flags(gfp_mask, reserve_flags); /* * Reset the nodemask and zonelist iterators if memory policies can be @@ -4714,7 +4726,7 @@ retry: /* Avoid allocations with no watermarks from looping endlessly */ if (tsk_is_oom_victim(current) && - (alloc_flags == ALLOC_OOM || + (alloc_flags & ALLOC_OOM || (gfp_mask & __GFP_NOMEMALLOC))) goto nopage; @@ -4806,8 +4818,7 @@ static inline bool prepare_alloc_pages(gfp_t gfp_mask, unsigned int order, if (should_fail_alloc_page(gfp_mask, order)) return false; - if (IS_ENABLED(CONFIG_CMA) && ac->migratetype == MIGRATE_MOVABLE) - *alloc_flags |= ALLOC_CMA; + *alloc_flags = current_alloc_flags(gfp_mask, *alloc_flags); return true; } -- GitLab From 42742d9bde2a8e11ec932cb5821f720a40a7c2a9 Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Thu, 6 Aug 2020 23:26:08 -0700 Subject: [PATCH 1465/1476] mm: thp: replace HTTP links with HTTPS ones Rationale: Reduces attack surface on kernel devs opening the links for MITM as HTTPS traffic is much harder to manipulate. Deterministic algorithm: For each file: If not .svg: For each line: If doesn't contain `xmlns`: For each link, `http://[^# ]*(?:\w|/)`: If neither `gnu\.org/license`, nor `mozilla\.org/MPL`: If both the HTTP and HTTPS versions return 200 OK and serve the same content: Replace HTTP with HTTPS. [akpm@linux-foundation.org: fix amd.com URL, per Vlastimil] Signed-off-by: Alexander A. Klimov Signed-off-by: Andrew Morton Reviewed-by: Andrew Morton Cc: Vlastimil Babka Link: http://lkml.kernel.org/r/20200713164345.36088-1-grandmaster@al2klimov.de Signed-off-by: Linus Torvalds --- mm/huge_memory.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mm/huge_memory.c b/mm/huge_memory.c index 462a7dbd63502..206f52b36ffb9 100644 --- a/mm/huge_memory.c +++ b/mm/huge_memory.c @@ -2063,8 +2063,8 @@ static void __split_huge_pmd_locked(struct vm_area_struct *vma, pmd_t *pmd, * free), userland could trigger a small page size TLB miss on the * small sized TLB while the hugepage TLB entry is still established in * the huge TLB. Some CPU doesn't like that. - * See http://support.amd.com/us/Processor_TechDocs/41322.pdf, Erratum - * 383 on page 93. Intel should be safe but is also warns that it's + * See http://support.amd.com/TechDocs/41322_10h_Rev_Gd.pdf, Erratum + * 383 on page 105. Intel should be safe but is also warns that it's * only safe if the permission and cache attributes of the two entries * loaded in the two TLB is identical (which should be the case here). * But it is generally safer to never allow small and huge TLB entries -- GitLab From 75802ca66354a39ab8e35822747cd08b3384a99a Mon Sep 17 00:00:00 2001 From: Peter Xu Date: Thu, 6 Aug 2020 23:26:11 -0700 Subject: [PATCH 1466/1476] mm/hugetlb: fix calculation of adjust_range_if_pmd_sharing_possible This is found by code observation only. Firstly, the worst case scenario should assume the whole range was covered by pmd sharing. The old algorithm might not work as expected for ranges like (1g-2m, 1g+2m), where the adjusted range should be (0, 1g+2m) but the expected range should be (0, 2g). Since at it, remove the loop since it should not be required. With that, the new code should be faster too when the invalidating range is huge. Mike said: : With range (1g-2m, 1g+2m) within a vma (0, 2g) the existing code will only : adjust to (0, 1g+2m) which is incorrect. : : We should cc stable. The original reason for adjusting the range was to : prevent data corruption (getting wrong page). Since the range is not : always adjusted correctly, the potential for corruption still exists. : : However, I am fairly confident that adjust_range_if_pmd_sharing_possible : is only gong to be called in two cases: : : 1) for a single page : 2) for range == entire vma : : In those cases, the current code should produce the correct results. : : To be safe, let's just cc stable. Fixes: 017b1660df89 ("mm: migration: fix migration of huge PMD shared pages") Signed-off-by: Peter Xu Signed-off-by: Andrew Morton Reviewed-by: Mike Kravetz Cc: Andrea Arcangeli Cc: Matthew Wilcox Cc: Link: http://lkml.kernel.org/r/20200730201636.74778-1-peterx@redhat.com Signed-off-by: Linus Torvalds --- mm/hugetlb.c | 24 ++++++++++-------------- 1 file changed, 10 insertions(+), 14 deletions(-) diff --git a/mm/hugetlb.c b/mm/hugetlb.c index 27556d4d49fee..e52c878940bb0 100644 --- a/mm/hugetlb.c +++ b/mm/hugetlb.c @@ -5314,25 +5314,21 @@ static bool vma_shareable(struct vm_area_struct *vma, unsigned long addr) void adjust_range_if_pmd_sharing_possible(struct vm_area_struct *vma, unsigned long *start, unsigned long *end) { - unsigned long check_addr; + unsigned long a_start, a_end; if (!(vma->vm_flags & VM_MAYSHARE)) return; - for (check_addr = *start; check_addr < *end; check_addr += PUD_SIZE) { - unsigned long a_start = check_addr & PUD_MASK; - unsigned long a_end = a_start + PUD_SIZE; + /* Extend the range to be PUD aligned for a worst case scenario */ + a_start = ALIGN_DOWN(*start, PUD_SIZE); + a_end = ALIGN(*end, PUD_SIZE); - /* - * If sharing is possible, adjust start/end if necessary. - */ - if (range_in_vma(vma, a_start, a_end)) { - if (a_start < *start) - *start = a_start; - if (a_end > *end) - *end = a_end; - } - } + /* + * Intersect the range with the vma range, since pmd sharing won't be + * across vma after all + */ + *start = max(vma->vm_start, a_start); + *end = min(vma->vm_end, a_end); } /* -- GitLab From 723a80dafed5c95889d48baab9aa433a6ffa0b4e Mon Sep 17 00:00:00 2001 From: Hugh Dickins Date: Thu, 6 Aug 2020 23:26:15 -0700 Subject: [PATCH 1467/1476] khugepaged: collapse_pte_mapped_thp() flush the right range pmdp_collapse_flush() should be given the start address at which the huge page is mapped, haddr: it was given addr, which at that point has been used as a local variable, incremented to the end address of the extent. Found by source inspection while chasing a hugepage locking bug, which I then could not explain by this. At first I thought this was very bad; then saw that all of the page translations that were not flushed would actually still point to the right pages afterwards, so harmless; then realized that I know nothing of how different architectures and models cache intermediate paging structures, so maybe it matters after all - particularly since the page table concerned is immediately freed. Much easier to fix than to think about. Fixes: 27e1f8273113 ("khugepaged: enable collapse pmd for pte-mapped THP") Signed-off-by: Hugh Dickins Signed-off-by: Andrew Morton Acked-by: Kirill A. Shutemov Cc: Andrea Arcangeli Cc: Mike Kravetz Cc: Song Liu Cc: [5.4+] Link: http://lkml.kernel.org/r/alpine.LSU.2.11.2008021204390.27773@eggly.anvils Signed-off-by: Linus Torvalds --- mm/khugepaged.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mm/khugepaged.c b/mm/khugepaged.c index 700f5160f3e4d..28aae7f45c63f 100644 --- a/mm/khugepaged.c +++ b/mm/khugepaged.c @@ -1502,7 +1502,7 @@ void collapse_pte_mapped_thp(struct mm_struct *mm, unsigned long addr) /* step 4: collapse pmd */ ptl = pmd_lock(vma->vm_mm, pmd); - _pmd = pmdp_collapse_flush(vma, addr, pmd); + _pmd = pmdp_collapse_flush(vma, haddr, pmd); spin_unlock(ptl); mm_dec_nr_ptes(mm); pte_free(mm, pmd_pgtable(_pmd)); -- GitLab From 119a5fc16105b2b9383a6e2a7800b2ef861b2975 Mon Sep 17 00:00:00 2001 From: Hugh Dickins Date: Thu, 6 Aug 2020 23:26:18 -0700 Subject: [PATCH 1468/1476] khugepaged: collapse_pte_mapped_thp() protect the pmd lock When retract_page_tables() removes a page table to make way for a huge pmd, it holds huge page lock, i_mmap_lock_write, mmap_write_trylock and pmd lock; but when collapse_pte_mapped_thp() does the same (to handle the case when the original mmap_write_trylock had failed), only mmap_write_trylock and pmd lock are held. That's not enough. One machine has twice crashed under load, with "BUG: spinlock bad magic" and GPF on 6b6b6b6b6b6b6b6b. Examining the second crash, page_vma_mapped_walk_done()'s spin_unlock of pvmw->ptl (serving page_referenced() on a file THP, that had found a page table at *pmd) discovers that the page table page and its lock have already been freed by the time it comes to unlock. Follow the example of retract_page_tables(), but we only need one of huge page lock or i_mmap_lock_write to secure against this: because it's the narrower lock, and because it simplifies collapse_pte_mapped_thp() to know the hpage earlier, choose to rely on huge page lock here. Fixes: 27e1f8273113 ("khugepaged: enable collapse pmd for pte-mapped THP") Signed-off-by: Hugh Dickins Signed-off-by: Andrew Morton Acked-by: Kirill A. Shutemov Cc: Andrea Arcangeli Cc: Mike Kravetz Cc: Song Liu Cc: [5.4+] Link: http://lkml.kernel.org/r/alpine.LSU.2.11.2008021213070.27773@eggly.anvils Signed-off-by: Linus Torvalds --- mm/khugepaged.c | 44 +++++++++++++++++++------------------------- 1 file changed, 19 insertions(+), 25 deletions(-) diff --git a/mm/khugepaged.c b/mm/khugepaged.c index 28aae7f45c63f..a9aca9b71d6fa 100644 --- a/mm/khugepaged.c +++ b/mm/khugepaged.c @@ -1412,7 +1412,7 @@ void collapse_pte_mapped_thp(struct mm_struct *mm, unsigned long addr) { unsigned long haddr = addr & HPAGE_PMD_MASK; struct vm_area_struct *vma = find_vma(mm, haddr); - struct page *hpage = NULL; + struct page *hpage; pte_t *start_pte, *pte; pmd_t *pmd, _pmd; spinlock_t *ptl; @@ -1432,9 +1432,17 @@ void collapse_pte_mapped_thp(struct mm_struct *mm, unsigned long addr) if (!hugepage_vma_check(vma, vma->vm_flags | VM_HUGEPAGE)) return; + hpage = find_lock_page(vma->vm_file->f_mapping, + linear_page_index(vma, haddr)); + if (!hpage) + return; + + if (!PageHead(hpage)) + goto drop_hpage; + pmd = mm_find_pmd(mm, haddr); if (!pmd) - return; + goto drop_hpage; start_pte = pte_offset_map_lock(mm, pmd, haddr, &ptl); @@ -1453,30 +1461,11 @@ void collapse_pte_mapped_thp(struct mm_struct *mm, unsigned long addr) page = vm_normal_page(vma, addr, *pte); - if (!page || !PageCompound(page)) - goto abort; - - if (!hpage) { - hpage = compound_head(page); - /* - * The mapping of the THP should not change. - * - * Note that uprobe, debugger, or MAP_PRIVATE may - * change the page table, but the new page will - * not pass PageCompound() check. - */ - if (WARN_ON(hpage->mapping != vma->vm_file->f_mapping)) - goto abort; - } - /* - * Confirm the page maps to the correct subpage. - * - * Note that uprobe, debugger, or MAP_PRIVATE may change - * the page table, but the new page will not pass - * PageCompound() check. + * Note that uprobe, debugger, or MAP_PRIVATE may change the + * page table, but the new page will not be a subpage of hpage. */ - if (WARN_ON(hpage + i != page)) + if (hpage + i != page) goto abort; count++; } @@ -1495,7 +1484,7 @@ void collapse_pte_mapped_thp(struct mm_struct *mm, unsigned long addr) pte_unmap_unlock(start_pte, ptl); /* step 3: set proper refcount and mm_counters. */ - if (hpage) { + if (count) { page_ref_sub(hpage, count); add_mm_counter(vma->vm_mm, mm_counter_file(hpage), -count); } @@ -1506,10 +1495,15 @@ void collapse_pte_mapped_thp(struct mm_struct *mm, unsigned long addr) spin_unlock(ptl); mm_dec_nr_ptes(mm); pte_free(mm, pmd_pgtable(_pmd)); + +drop_hpage: + unlock_page(hpage); + put_page(hpage); return; abort: pte_unmap_unlock(start_pte, ptl); + goto drop_hpage; } static int khugepaged_collapse_pte_mapped_thps(struct mm_slot *mm_slot) -- GitLab From 18e77600f7a1ed69f8ce46c9e11cad0985712dfa Mon Sep 17 00:00:00 2001 From: Hugh Dickins Date: Thu, 6 Aug 2020 23:26:22 -0700 Subject: [PATCH 1469/1476] khugepaged: retract_page_tables() remember to test exit Only once have I seen this scenario (and forgot even to notice what forced the eventual crash): a sequence of "BUG: Bad page map" alerts from vm_normal_page(), from zap_pte_range() servicing exit_mmap(); pmd:00000000, pte values corresponding to data in physical page 0. The pte mappings being zapped in this case were supposed to be from a huge page of ext4 text (but could as well have been shmem): my belief is that it was racing with collapse_file()'s retract_page_tables(), found *pmd pointing to a page table, locked it, but *pmd had become 0 by the time start_pte was decided. In most cases, that possibility is excluded by holding mmap lock; but exit_mmap() proceeds without mmap lock. Most of what's run by khugepaged checks khugepaged_test_exit() after acquiring mmap lock: khugepaged_collapse_pte_mapped_thps() and hugepage_vma_revalidate() do so, for example. But retract_page_tables() did not: fix that. The fix is for retract_page_tables() to check khugepaged_test_exit(), after acquiring mmap lock, before doing anything to the page table. Getting the mmap lock serializes with __mmput(), which briefly takes and drops it in __khugepaged_exit(); then the khugepaged_test_exit() check on mm_users makes sure we don't touch the page table once exit_mmap() might reach it, since exit_mmap() will be proceeding without mmap lock, not expecting anyone to be racing with it. Fixes: f3f0e1d2150b ("khugepaged: add support of collapse for tmpfs/shmem pages") Signed-off-by: Hugh Dickins Signed-off-by: Andrew Morton Acked-by: Kirill A. Shutemov Cc: Andrea Arcangeli Cc: Mike Kravetz Cc: Song Liu Cc: [4.8+] Link: http://lkml.kernel.org/r/alpine.LSU.2.11.2008021215400.27773@eggly.anvils Signed-off-by: Linus Torvalds --- mm/khugepaged.c | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/mm/khugepaged.c b/mm/khugepaged.c index a9aca9b71d6fa..ac04b332a373a 100644 --- a/mm/khugepaged.c +++ b/mm/khugepaged.c @@ -1532,6 +1532,7 @@ out: static void retract_page_tables(struct address_space *mapping, pgoff_t pgoff) { struct vm_area_struct *vma; + struct mm_struct *mm; unsigned long addr; pmd_t *pmd, _pmd; @@ -1560,7 +1561,8 @@ static void retract_page_tables(struct address_space *mapping, pgoff_t pgoff) continue; if (vma->vm_end < addr + HPAGE_PMD_SIZE) continue; - pmd = mm_find_pmd(vma->vm_mm, addr); + mm = vma->vm_mm; + pmd = mm_find_pmd(mm, addr); if (!pmd) continue; /* @@ -1570,17 +1572,19 @@ static void retract_page_tables(struct address_space *mapping, pgoff_t pgoff) * mmap_lock while holding page lock. Fault path does it in * reverse order. Trylock is a way to avoid deadlock. */ - if (mmap_write_trylock(vma->vm_mm)) { - spinlock_t *ptl = pmd_lock(vma->vm_mm, pmd); - /* assume page table is clear */ - _pmd = pmdp_collapse_flush(vma, addr, pmd); - spin_unlock(ptl); - mmap_write_unlock(vma->vm_mm); - mm_dec_nr_ptes(vma->vm_mm); - pte_free(vma->vm_mm, pmd_pgtable(_pmd)); + if (mmap_write_trylock(mm)) { + if (!khugepaged_test_exit(mm)) { + spinlock_t *ptl = pmd_lock(mm, pmd); + /* assume page table is clear */ + _pmd = pmdp_collapse_flush(vma, addr, pmd); + spin_unlock(ptl); + mm_dec_nr_ptes(mm); + pte_free(mm, pmd_pgtable(_pmd)); + } + mmap_write_unlock(mm); } else { /* Try again later */ - khugepaged_add_pte_mapped_thp(vma->vm_mm, addr); + khugepaged_add_pte_mapped_thp(mm, addr); } } i_mmap_unlock_write(mapping); -- GitLab From bbe98f9cadff58cdd6a4acaeba0efa8565dabe65 Mon Sep 17 00:00:00 2001 From: Hugh Dickins Date: Thu, 6 Aug 2020 23:26:25 -0700 Subject: [PATCH 1470/1476] khugepaged: khugepaged_test_exit() check mmget_still_valid() Move collapse_huge_page()'s mmget_still_valid() check into khugepaged_test_exit() itself. collapse_huge_page() is used for anon THP only, and earned its mmget_still_valid() check because it inserts a huge pmd entry in place of the page table's pmd entry; whereas collapse_file()'s retract_page_tables() or collapse_pte_mapped_thp() merely clears the page table's pmd entry. But core dumping without mmap lock must have been as open to mistaking a racily cleared pmd entry for a page table at physical page 0, as exit_mmap() was. And we certainly have no interest in mapping as a THP once dumping core. Fixes: 59ea6d06cfa9 ("coredump: fix race condition between collapse_huge_page() and core dumping") Signed-off-by: Hugh Dickins Signed-off-by: Andrew Morton Cc: Andrea Arcangeli Cc: Song Liu Cc: Mike Kravetz Cc: Kirill A. Shutemov Cc: [4.8+] Link: http://lkml.kernel.org/r/alpine.LSU.2.11.2008021217020.27773@eggly.anvils Signed-off-by: Linus Torvalds --- mm/khugepaged.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/mm/khugepaged.c b/mm/khugepaged.c index ac04b332a373a..b52bd46ad146d 100644 --- a/mm/khugepaged.c +++ b/mm/khugepaged.c @@ -431,7 +431,7 @@ static void insert_to_mm_slots_hash(struct mm_struct *mm, static inline int khugepaged_test_exit(struct mm_struct *mm) { - return atomic_read(&mm->mm_users) == 0; + return atomic_read(&mm->mm_users) == 0 || !mmget_still_valid(mm); } static bool hugepage_vma_check(struct vm_area_struct *vma, @@ -1100,9 +1100,6 @@ static void collapse_huge_page(struct mm_struct *mm, * handled by the anon_vma lock + PG_lock. */ mmap_write_lock(mm); - result = SCAN_ANY_PROCESS; - if (!mmget_still_valid(mm)) - goto out; result = hugepage_vma_revalidate(mm, address, &vma); if (result) goto out; -- GitLab From 238c30468f46b16d7c22df175da11109346e769c Mon Sep 17 00:00:00 2001 From: dylan-meiners Date: Thu, 6 Aug 2020 23:26:29 -0700 Subject: [PATCH 1471/1476] mm/vmscan.c: fix typo Change "optizimation" to "optimization". Signed-off-by: dylan-meiners Signed-off-by: Andrew Morton Reviewed-by: David Hildenbrand Link: http://lkml.kernel.org/r/20200609185144.10049-1-spacct.spacct@gmail.com Signed-off-by: Linus Torvalds --- mm/vmscan.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mm/vmscan.c b/mm/vmscan.c index d4d7cd1d24c1c..dbabf812a28ac 100644 --- a/mm/vmscan.c +++ b/mm/vmscan.c @@ -910,7 +910,7 @@ static int __remove_mapping(struct address_space *mapping, struct page *page, * order to detect refaults, thus thrashing, later on. * * But don't store shadows in an address space that is - * already exiting. This is not just an optizimation, + * already exiting. This is not just an optimization, * inode reclaim needs to empty out the radix tree or * the nodes are lost. Don't plant shadows behind its * back. -- GitLab From 912c05720f00d039103d356a59c37dc7c3995e01 Mon Sep 17 00:00:00 2001 From: Shakeel Butt Date: Thu, 6 Aug 2020 23:26:32 -0700 Subject: [PATCH 1472/1476] mm: vmscan: consistent update to pgrefill The vmstat pgrefill is useful together with pgscan and pgsteal stats to measure the reclaim efficiency. However vmstat's pgrefill is not updated consistently at system level. It gets updated for both global and memcg reclaim however pgscan and pgsteal are updated for only global reclaim. So, update pgrefill only for global reclaim. If someone is interested in the stats representing both system level as well as memcg level reclaim, then consult the root memcg's memory.stat instead of /proc/vmstat. Signed-off-by: Shakeel Butt Signed-off-by: Andrew Morton Acked-by: Yafang Shao Acked-by: Roman Gushchin Acked-by: Chris Down Cc: Johannes Weiner Cc: Michal Hocko Link: http://lkml.kernel.org/r/20200711011459.1159929-1-shakeelb@google.com Signed-off-by: Linus Torvalds --- mm/vmscan.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/mm/vmscan.c b/mm/vmscan.c index dbabf812a28ac..72da290b171bf 100644 --- a/mm/vmscan.c +++ b/mm/vmscan.c @@ -2030,7 +2030,8 @@ static void shrink_active_list(unsigned long nr_to_scan, __mod_node_page_state(pgdat, NR_ISOLATED_ANON + file, nr_taken); - __count_vm_events(PGREFILL, nr_scanned); + if (!cgroup_reclaim(sc)) + __count_vm_events(PGREFILL, nr_scanned); __count_memcg_events(lruvec_memcg(lruvec), PGREFILL, nr_scanned); spin_unlock_irq(&pgdat->lru_lock); -- GitLab From 16b89f695313d91c99bdaf6898f28a51d0af1b17 Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Fri, 7 Aug 2020 10:53:54 -0700 Subject: [PATCH 1473/1476] net/scm: Fix typo in SCM_RIGHTS compat refactoring When refactoring the SCM_RIGHTS code, I accidentally mis-merged my native/compat diffs, which entirely broke using SCM_RIGHTS in compat mode. Use the correct helper. Reported-by: Christian Zigotzky Link: https://lists.ozlabs.org/pipermail/linuxppc-dev/2020-August/216156.html Reported-by: "Alex Xu (Hello71)" Link: https://lore.kernel.org/lkml/1596812929.lz7fuo8r2w.none@localhost/ Suggested-by: Thadeu Lima de Souza Cascardo Fixes: c0029de50982 ("net/scm: Regularize compat handling of scm_detach_fds()") Tested-by: Alex Xu (Hello71) Acked-by: Thadeu Lima de Souza Cascardo Signed-off-by: Kees Cook --- net/compat.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/compat.c b/net/compat.c index 703acb51c698a..95ce707a30a31 100644 --- a/net/compat.c +++ b/net/compat.c @@ -294,7 +294,7 @@ void scm_detach_fds_compat(struct msghdr *msg, struct scm_cookie *scm) (struct compat_cmsghdr __user *)msg->msg_control; unsigned int o_flags = (msg->msg_flags & MSG_CMSG_CLOEXEC) ? O_CLOEXEC : 0; int fdmax = min_t(int, scm_max_fds_compat(msg), scm->fp->count); - int __user *cmsg_data = CMSG_USER_DATA(cm); + int __user *cmsg_data = CMSG_COMPAT_DATA(cm); int err = 0, i; for (i = 0; i < fdmax; i++) { -- GitLab From 38ce2a9e33db61a3041840310077072d6210ead4 Mon Sep 17 00:00:00 2001 From: "Steven Rostedt (VMware)" Date: Thu, 6 Aug 2020 12:46:49 -0400 Subject: [PATCH 1474/1476] tracing: Add trace_array_init_printk() to initialize instance trace_printk() buffers As trace_array_printk() used with not global instances will not add noise to the main buffer, they are OK to have in the kernel (unlike trace_printk()). This require the subsystem to create their own tracing instance, and the trace_array_printk() only writes into those instances. Add trace_array_init_printk() to initialize the trace_printk() buffers without printing out the WARNING message. Reported-by: Sean Paul Reviewed-by: Sean Paul Signed-off-by: Steven Rostedt (VMware) --- include/linux/trace.h | 1 + kernel/trace/trace.c | 44 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 45 insertions(+) diff --git a/include/linux/trace.h b/include/linux/trace.h index 7fd86d3c691f5..36d255d66f88f 100644 --- a/include/linux/trace.h +++ b/include/linux/trace.h @@ -29,6 +29,7 @@ struct trace_array; void trace_printk_init_buffers(void); int trace_array_printk(struct trace_array *tr, unsigned long ip, const char *fmt, ...); +int trace_array_init_printk(struct trace_array *tr); void trace_array_put(struct trace_array *tr); struct trace_array *trace_array_get_by_name(const char *name); int trace_array_destroy(struct trace_array *tr); diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c index 06c0feae5ff98..c5f822736261c 100644 --- a/kernel/trace/trace.c +++ b/kernel/trace/trace.c @@ -3129,6 +3129,9 @@ static int alloc_percpu_trace_buffer(void) { struct trace_buffer_struct *buffers; + if (trace_percpu_buffer) + return 0; + buffers = alloc_percpu(struct trace_buffer_struct); if (MEM_FAIL(!buffers, "Could not allocate percpu trace_printk buffer")) return -ENOMEM; @@ -3331,6 +3334,26 @@ int trace_array_vprintk(struct trace_array *tr, return __trace_array_vprintk(tr->array_buffer.buffer, ip, fmt, args); } +/** + * trace_array_printk - Print a message to a specific instance + * @tr: The instance trace_array descriptor + * @ip: The instruction pointer that this is called from. + * @fmt: The format to print (printf format) + * + * If a subsystem sets up its own instance, they have the right to + * printk strings into their tracing instance buffer using this + * function. Note, this function will not write into the top level + * buffer (use trace_printk() for that), as writing into the top level + * buffer should only have events that can be individually disabled. + * trace_printk() is only used for debugging a kernel, and should not + * be ever encorporated in normal use. + * + * trace_array_printk() can be used, as it will not add noise to the + * top level tracing buffer. + * + * Note, trace_array_init_printk() must be called on @tr before this + * can be used. + */ __printf(3, 0) int trace_array_printk(struct trace_array *tr, unsigned long ip, const char *fmt, ...) @@ -3355,6 +3378,27 @@ int trace_array_printk(struct trace_array *tr, } EXPORT_SYMBOL_GPL(trace_array_printk); +/** + * trace_array_init_printk - Initialize buffers for trace_array_printk() + * @tr: The trace array to initialize the buffers for + * + * As trace_array_printk() only writes into instances, they are OK to + * have in the kernel (unlike trace_printk()). This needs to be called + * before trace_array_printk() can be used on a trace_array. + */ +int trace_array_init_printk(struct trace_array *tr) +{ + if (!tr) + return -ENOENT; + + /* This is only allowed for created instances */ + if (tr == &global_trace) + return -EINVAL; + + return alloc_percpu_trace_buffer(); +} +EXPORT_SYMBOL_GPL(trace_array_init_printk); + __printf(3, 4) int trace_array_printk_buf(struct trace_buffer *buffer, unsigned long ip, const char *fmt, ...) -- GitLab From 25ccd24ffd9119c452d711efa2604a7a0c35956e Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Thu, 6 Aug 2020 16:07:10 +0200 Subject: [PATCH 1475/1476] fs: fix a struct path leak in path_umount Make sure we also put the dentry and vfsmnt in the illegal flags and !may_umount cases. Fixes: 41525f56e256 ("fs: refactor ksys_umount") Reported-by: Vikas Kumar Signed-off-by: Christoph Hellwig Signed-off-by: Al Viro --- fs/namespace.c | 32 ++++++++++++++++++-------------- 1 file changed, 18 insertions(+), 14 deletions(-) diff --git a/fs/namespace.c b/fs/namespace.c index a7301790abb21..1c74a46367df4 100644 --- a/fs/namespace.c +++ b/fs/namespace.c @@ -1706,34 +1706,38 @@ static inline bool may_mandlock(void) } #endif -int path_umount(struct path *path, int flags) +static int can_umount(const struct path *path, int flags) { - struct mount *mnt; - int retval; + struct mount *mnt = real_mount(path->mnt); if (flags & ~(MNT_FORCE | MNT_DETACH | MNT_EXPIRE | UMOUNT_NOFOLLOW)) return -EINVAL; if (!may_mount()) return -EPERM; - - mnt = real_mount(path->mnt); - retval = -EINVAL; if (path->dentry != path->mnt->mnt_root) - goto dput_and_out; + return -EINVAL; if (!check_mnt(mnt)) - goto dput_and_out; + return -EINVAL; if (mnt->mnt.mnt_flags & MNT_LOCKED) /* Check optimistically */ - goto dput_and_out; - retval = -EPERM; + return -EINVAL; if (flags & MNT_FORCE && !capable(CAP_SYS_ADMIN)) - goto dput_and_out; + return -EPERM; + return 0; +} + +int path_umount(struct path *path, int flags) +{ + struct mount *mnt = real_mount(path->mnt); + int ret; + + ret = can_umount(path, flags); + if (!ret) + ret = do_umount(mnt, flags); - retval = do_umount(mnt, flags); -dput_and_out: /* we mustn't call path_put() as that would clear mnt_expiry_mark */ dput(path->dentry); mntput_no_expire(mnt); - return retval; + return ret; } static int ksys_umount(char __user *name, int flags) -- GitLab From 7b9de97711225559af213dc52b6ea883ef1ea7a8 Mon Sep 17 00:00:00 2001 From: Michael Ellerman Date: Sat, 8 Aug 2020 09:12:03 +1000 Subject: [PATCH 1476/1476] powerpc/ptrace: Fix build error in pkey_get() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The merge resolution in commit 25d8d4eecace left ret no longer used, leading to: arch/powerpc/kernel/ptrace/ptrace-view.c: In function ‘pkey_get’: arch/powerpc/kernel/ptrace/ptrace-view.c:473:6: error: unused variable ‘ret’ 473 | int ret; Fix it by removing ret. Fixes: 25d8d4eecace ("Merge tag 'powerpc-5.9-1' of git://git.kernel.org/pub/scm/linux/kernel/git/powerpc/linux") Signed-off-by: Michael Ellerman Signed-off-by: Linus Torvalds --- arch/powerpc/kernel/ptrace/ptrace-view.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/arch/powerpc/kernel/ptrace/ptrace-view.c b/arch/powerpc/kernel/ptrace/ptrace-view.c index 19823a250aa02..7e6478e7ed074 100644 --- a/arch/powerpc/kernel/ptrace/ptrace-view.c +++ b/arch/powerpc/kernel/ptrace/ptrace-view.c @@ -470,8 +470,6 @@ static int pkey_active(struct task_struct *target, const struct user_regset *reg static int pkey_get(struct task_struct *target, const struct user_regset *regset, struct membuf to) { - int ret; - BUILD_BUG_ON(TSO(amr) + sizeof(unsigned long) != TSO(iamr)); if (!arch_pkeys_enabled()) -- GitLab